How can I do elementwise operation on parameter vectors in AMPL - ampl

In MATLAB, if I have two vectors a=[a_1,..,a_n], b=[b_1,..,b_n], I can obtain another vector c = [a_1/b_1,..,a_n/b_n] by a./b. How can I achieve this in AMPL?

You can use something like this:
set S := {1,2,3};
param p{S};
var x{S};
var y{i in S} = x[i]*p[i];
or alternately:
set S := {1,2,3};
param p{S};
var x{S};
var y{S};
s.t. c1{i in S}: y[i] = x[i]*p[i];
However, your ability to do this may be limited by the constraints supported by your solver, e.g. if you define a relationship that implies a nonlinear constraint while using a nonlinear solver.

Related

Set the objective of an optimizer as the standard deviation of the input (Nonlinear optimization using pymo)

I am trying to use pymo for a single objective nonlinear optimization problem.
The objective function is to minimize the variance (or standard deviation) of the input variables following certain constraints (which I was able to do in Excel).
Following is a code example of what I am trying to do
model = pyo.ConcreteModel()
# declare decision variables
model.x1 = pyo.Var(domain=pyo.NonNegativeReals)
model.x2 = pyo.Var(domain=pyo.NonNegativeReals)
model.x3 = pyo.Var(domain=pyo.NonNegativeReals)
model.x4 = pyo.Var(domain=pyo.NonNegativeReals)
# declare objective
from statistics import stdev
model.variance = pyo.Objective(
expr = stdev([model.x1, model.x2, model.x3, model.x4]),
sense = pyo.minimize)
# declare constraints
model.max_charging = pyo.Constraint(expr = model.x1 + model.x2 + model.x3 + model.x4 >= 500)
model.max_x1 = pyo.Constraint(expr = model.x1 <= 300)
model.max_x2 = pyo.Constraint(expr = model.x2 <= 200)
model.max_x3 = pyo.Constraint(expr = model.x3 <= 100)
model.max_x4 = pyo.Constraint(expr = model.x4 <= 200)
# solve
pyo.SolverFactory('glpk').solve(model).write()
#print
print("energy_price = ", model.variance())
print(f'Variables = [{model.x1()},{model.x2()},{model.x3()},{model.x4()}]')
The error I get is TypeError: can't convert type 'ScalarVar' to numerator/denominator
The problem seems to be caused by using the stdev function from statistics.
My assumption is that the models variables x1-x4 are yet to have been assigned a value and that is the main issue. However, I am not sure how to approach this?
First: stdev is nonlinear. So why even try to solve this with a linear solver?
Pyomo does not know about the statistics package. You'll have to program the standard deviation using elementary operations, use an external function approach, or use an approximation (like minimizing the range).
So I managed to solve this issue and I am including the solution below. But first, there are a couple of points I would like to highlight
As #Erwin Kalvelagen mentioned, 'stdev' is nonlinear so a linear solver like 'glpk' would always result in an error. For my problem, 'ipopt' worked fine but be careful as it can perform poorly in some cases.
Also, as #Erwin Kalvelagen mentioned, Pyomo does not know about the statistics package. So When you try to use a function from that package (e.g., 'stdev', 'variance', etc.), it will try to evaluate the model variables before the solver assigns them any value and that will cause an error.
A pyomo objective function needs an expression. The code sample below shows how to dynamically generate an expression for the variance without using an external package. The code is also agnostic to the number of model variables you have.
Using either the variance or the standard deviation will serve the same purpose for my project. I opted for using the variance to avoid calculating its square root (as the standard deviation is the square root of the variance).
Variability Objective Function
import pyomo.environ as pyo
def variabilityRule(model):
#Calculate mean of all model variables
for index in model.x:
mean += model.x[index]
mean = mean/len(model.x)
#Calculate the difference between each model variable and the mean
obj_exp = ((model.x[1])-mean) * ((model.x[1])-mean)
for i in range(2, len(model.x)+1): #note that pyomo variables start from 1 not zero
obj_exp += ((model.x[i])-mean) * ((model.x[i])-mean)
#Divide by number of items
obj_exp = (obj_exp/(len(model.x)))
return obj_exp
model = pyo.ConcreteModel()
model.objective = pyo.Objective(
rule = variabilityRule,
sense = pyo.maximize)
EDIT: Standard Deviation Calculation
You can use the same approach to calculate the standard deviation as I found out. Just multiply the final expression (`obj_exp`) by power 0.5
obj_exp = obj_exp ** 0.5
...
P.S. If you are interested, this is how I dynamically generated my model variables based on an input array
model.x = pyo.VarList(domain=pyo.NonNegativeReals)
for i in range(0, len(input_array)):
model.x.add()

In AMPL, how to refer to part of the result, and use them in multiple places

I'm learning AMPL to speed up a model currently in excel spreadsheet with excel solver. It basically based on the matrix multiplication result of a 1 x m variables and an m x n parameters. And it would find the variables to maximize the minimum of certain values in the result while keeping some other values in the same result satisfying a few constraints. How to do so in AMPL?
Given: P= m x n parameters
Variable: X= 1 x m variable we tried to solve
Calculate: R= X x P , result of matrix multiplication of X and P
Maximize: min(R[1..3]), the minimum value of the first 3 values in the result
Subject to: R[2]<R[4]
min(R[6..8])>20
R[5]-20>R[7]
X are all integers
I read several tutorials and look up the manual but can't find the solution to this seemingly straightforward problem. All I found is maximize a single value, which is the calculation result. And it was used only once and does not appear again in the constraint.
The usual approach for "maximize the minimum" problems in products like AMPL is to define an auxiliary variable and set linear constraints that effectively define it as the minimum, converting a nonlinear function (min) into linear rules.
For instance, suppose I have a bunch of decision variables x[i] with i ranging over an index set S, and I want to maximize the minimum over x[i]. AMPL syntax for that would be:
var x_min;
s.t. DefineMinimum{i in S}: x_min <= x[i];
maximize ObjectiveFunction: x_min;
The constraint only requires that x_min be less than or equal to the minimum of x[i]. However, since you're trying to maximize x_min and there are no other constraints on it, it should always end up exactly equal to that minimum (give or take machine-arithmetic epsilon considerations).
If you have parameters (i.e. values are known before you run the optimisation) and want to refer to their minimum, AMPL lets you do that more directly:
param p_min := min{j in IndexSet_P} p[j];
While AMPL also supports this syntax for variables, not all of the solvers used with AMPL are capable of accepting this type of constraint. For instance:
reset;
option solver gecode;
set S := {1,2,3};
var x{S} integer;
var x_min = min{s in S} x[s];
minimize OF: sum{s in S} x[s];
s.t. c1: x_min >= 5;
solve;
This will run and do what you'd expect it to do, because Gecode is programmed to recognise and deal with min-type constraints. However, if you switch the solver option to gurobi or cplex it will fail, since these only accept linear or quadratic constraints. To apply a minimum constraint with those solvers, you need to use something like the linearization trick I discussed above.

Difference of building constraints with BuildAction() vs normal way

As referred in pyomo documentation, BuildAction() is an advance topic and additionally according to the documentation, it is somewhat more efficient to build/solve? constraints with BuildAction().
An example constraint generated with BuildAction():
m.const1 = Constraint([(t, a) for t in m.TIME for a in m.AREA],
noruleinit=True)
def const1_rule(m):
for t in m.TIME:
for a in m.AREA:
lhs = (some_vars_1[t,a])
rhs = (some_vars_2[t,a])
m.const1.add((t,a), (lhs == rhs))
m.const1_build = BuildAction(rule=const1_rule)
So m.const1 = Constraint() builds the pyomo constraint without any rule via noruleinit=True.
Then m.const1_build = BuildAction() runs the function const1_rule, and in this function the constraints are added to m.const1 via .add().
An example constraint generated with normal way:
def const1_rule(m, t, a):
lhs = (some_vars_1[t,a])
rhs = (some_vars_2[t,a])
return lhs == rhs
m.const1 = Constraint(m.TIME, m.AREA, rule=const1_rule)
QUESTIONS:
Another application of BuildAction can be intialization of Pyomo model data from Python data structures, or efficient initialization of Pyomo model data from other Pyomo model data.
1) In what way it is more efficient?
2) What is the difference building the constraints with BuildAction()?
3) Should I use it, yes-no-why?
4) If BuildAction() is better, how would I take advantage of it? (for example; assume there is a different working principle of BuildAction(), and maybe because of that I don't need to create some pyomo Sets or Params.)

Defining Set Packing using MathProg

For someone with zero experience, this can be very confusing.
How do I define the Set Packing problem as seen in the wikipedia article as a MathProg program, to be later ran in the GLPK tool?
Intution alone would lead me into something like this:
var x
maximize SetPacking :
sum {s in Subsets} x
s.t. ?? //x is an integer 0 or 1
s.t. ?? //amount of x <=1
end;
But its logic is obviously wrong and I can't even finish it.
You can formulate the set packing problem in AMPL (and MathProg which is based on a subset of AMPL) as follows:
set U; # universe
set S; # subsets
set Members{S}; # members of subsets
# every set is either in the set packing or not
var x{S} binary;
# maximize the total number of subsets
maximize NumSets: sum{s in S} x[s];
# selected sets have to be pairwise disjoint
s.t. Disjoint{e in U}: sum{s in S: e in Members[s]} x[s] <= 1;
Note that you have to introduce indexed set Members with each element Members[s]representing members of subset s. This is the only difference from the algebraic formulation.

ZIMPL: Variables with two indices

i have started to use the zpl.format to solve a linear program with scip.
In my linear programm there are variables with 2 indices. Can I rebuild this in the zpl.format like var b[x,t] or something similar?
You index with a two dimension set.
Set I := { <1,2>, <2,3>, <5,7> };
var b[I];
for in I do ...