Eliminate equality constraints in a pyomo model - python

I want to eliminate linear equality constraints on integral variables in a pyomo model by substitution. For instance, I wish to transform the model
by substituting
( * )
to
Is there a way to perfom such a substitution in a pyomo model? I will be able to obtain ( * ) by computing the solution space of the corresponding system of linear diophantine equations in the form y = const_vec + susbtitution_matrix * eta, where in our example we have
const_vec = np.array([1,0,0])
substitution_matrix = np.array([[-1,0],
[1,0],
[0,1]])

What you are describing is generally referred to as "variable aggregation." As you indicate, there are four basic steps:
Identify the linear equality equations you want to remove
Compute the substitution map
Deactivate the equality constraints that you want to remove
Substitute variables on all remaining constraints
It sounds like you have 1 and 2 under control. For 3, assuming you identified a Constraint m.c you want to deactivate, you just need to call m.c.deactivate().
For 4, you will want to generate new expressions for the remaining Constraint "body" expressions (variables only appear in the body and not in the lower/upper bounds). For current Pyomo releases (through 5.4.x), you can perform variable substitution by leveraging the clone_expression(). You need to generate a "substitution map": a dict that maps the id() of the variables you want to the new expression you want to use. For example:
from pyomo.core.base.expr import clone_expression
m = ConcreteModel()
m.y = Var([1,2,3])
m.eta = Var([1,2])
# ...
m.c = Constraint(expr=m.y[1]**2 + m.y[3]**2 <= 4)
# ...
substitution_map = {
id(m.y[1]): 1 - m.eta[1],
id(m.y[2]): m.eta[1],
id(m.y[3]): m.eta[2],
}
m.c = (m.c.lower, clone_expression(m.c.body, substitute=substitution_map), m.c.upper)
Finally, the disclaimers:
Setting the constraint with this syntax should work with recent Pyomo releases (I tested back through 5.1)
This approach technically violates one of the assumptions in the current Pyomo expression system (it generates potentially "entangled" expressions: expressions that share common sub-trees). While not "good", it shouldn't cause troubles, unless you do additional transformations / expression manipulation.
Pyomo 5.5 will have a new expression system that will likely have a different mechanism for manipulating / substituting variables.

Related

Pyomo: How to impose the constraint that the decision variables must be a multiple of a specific integer?

I am making an AbstractModel in Pyomo and I want to impose as a constraint the following:
My decision variables are Xij, defined as: model.x = Var(model.I, model.J, domain=NonNegativeIntegers)
The constraint is that Xij must be zero or a multiple of 50.
I am trying to do so by using the remainder but when making the constraint with % or something similar I obtain that % does not support using int.
How would you propose this constraint?
Example:
Example of constraint that I have tried
"Xij must be zero or a multiple of 50"
variables
y(i,j) ∈ {0,1,2,3...}
x(i,j) ≥ 0
constraint
x(i,j) = 50*y(i,j)

How to create an OR constraint in Pyomo?

I am trying to build a MIP model in Pyomo and having trouble creating an Or constraint. An OR constraint r = or{x1, ..., xn} states that the binary resultant variable r should be 1 if and only if any of the operand variables x1, ..., xn is equal to 1. I failed no such function that can create OR constraint in Pyomo, so I use my own code
m = ConcreteModel()
m.r = Var(within=Binary)
m.x1 = Var(within=Binary)
m.x2 = Var(within=Binary)
m.or_constraint = Constraint(expr=m.r==min(sum(m.x1, m.x2), 1)
Then ran the code and got the error msg that variables m.x1 and m.x2 should be initialized. I initialized them with 1 and found that m.or_constraint degraded to force m.r equal to 1. In other words, m.or_constraint just used m.x1 and m.x2 initial value to build the constraint and never updated in the process of solving the MIP problem.
I tried different expressions in Pyomo to create this constraint, like call a rule function in the constraint definition. However, every time I got the same result.
Could you direct me to create OR constraint in Pyomo?
The relation
y = x(1) or x(2) or ... or x(n) ( same as y = max{x(i)} )
y, x(i) ∈ {0,1} ( all binary variables )
can be formulated as a set of n+1 linear inequalities
y <= sum(i, x(i))
y >= x(i) for all i
It is also possible to write this as just two constraints:
y <= sum(i,x(i))
y >= sum(i,x(i))/n
The first version is tighter, however. That is the one I typically use.
Note: the first version is so tight that we even can relax y to be continuous between 0 and 1. Whether this is advantageous for the solver, requires a bit of experimentation. For the second version, it is required that y is binary.
Actually, there is another way to create a 'or' statement by using Pyomo with Binary(only for two constraints).
Obj: Min(OF)=f(x)
s.t. x<=A or x<=B
We can add a binary Number(Y) to form this model.M is a number which great enough(100000).
Obj: Min(OF)=f(x,y)
s.t. X<= A+M*y
X<=B+(1-Y)M
Y=0,1

What is the best way to display numeric and symbolic expressions in python?

I need to produce calculation reports that detail step by step calculations, showing the formulas that are used and then showing how the results are achieved.
I have looked at using sympy to display symbolic equations. The problem is that a sympy symbol is stored as a variable, and therefore I cannot also store the numerical value of that symbol.
For example, for the formula σ=My/I , I need to show the value of each symbol, then the symbolic formula, then the formula with values substituted in, and finally the resolution of the formula.
M=100
y= 25
I=5
σ=My/I
σ=100*25/5
σ=5000
I’m new to programming and this is something I’m struggling with. I’ve thought of perhaps building my own class but not sure how to make the distinction the different forms. In the example above, σ is at one point a numerical value, one half of an symbolic expression, and also one half of a numerical expression.
Hopefully the following helps. This produces more or less what you want. You cannot get your fifth line of workings easily as you'll see in the code.
from sympy import *
# define all variables needed
# trying to keep things clear that symbols are different from their numeric values
M_label, y_label, l_label = ("M", "y", "l")
M_symbol, y_symbol, l_symbol = symbols(f"{M_label} {y_label} {l_label}", real=True)
M_value, y_value, l_value = (100, 25, 5)
# define the dictionary whose keys are string names
# and whose values are a tuple of symbols and numerical values
symbols_values = {M_label: (M_symbol, M_value),
y_label: (y_symbol, y_value),
l_label: (l_symbol, l_value)}
for name, symbol_value in symbols_values.items():
print(f"{name} = {symbol_value[1]}") # an f-string or formatted string
sigma = M_symbol * y_symbol / l_symbol
print(f"sigma = {sigma}")
# option 1
# changes `/5` to 5**(-1) since this is exactly how sympy views division
# credit for UnevaluatedExpr
# https://stackoverflow.com/questions/49842196/substitute-in-sympy-wihout-evaluating-or-simplifying-the-expression
sigma_substituted = sigma\
.subs(M_symbol, UnevaluatedExpr(M_value))\
.subs(y_symbol, UnevaluatedExpr(y_value))\
.subs(l_symbol, UnevaluatedExpr(l_value))
print(f"sigma = {sigma_substituted}")
# option 2
# using string substitution
# note this could replace words like `log`, `cos` or `exp` to something completely different
# this is why it is unadvised. The code above is far better for that purpose
sigma_substituted = str(sigma)\
.replace(M_label, str(M_value))\
.replace(y_label, str(y_value))\
.replace(l_label, str(l_value))
print(f"sigma = {sigma_substituted}")
sigma_simplified = sigma\
.subs(M_symbol, M_value)\
.subs(y_symbol, y_value)\
.subs(l_symbol, l_value)
print(f"sigma = {sigma_simplified}")
Also note that if you wanted to change the symbols_values dictionary to keys being the symbols and values being the numerical values, you will have a hard time or seemingly buggy experience using the keys. That is because if you have x1 = Symbol("x") and x2 = Symbol("x"), SymPy sometimes treats the above as 2 completely different variables even though they are defined the same way. It is far easier to use strings as keys.
If you begin to use more variables and choose to work this way, I suggest using lists and for loops instead of writing the same code over and over.

How to add condition to gurobi

I have variables whose values change every hour during the day (24 values):
plants = ['Plant1', 'Plant2']
users = ['user1', 'user2']
time_steps = range(0,24)
p_gen = model.addVars(plants, time_steps, name="pow_gen")
tot_consume = model.addVars(users, time_steps, name="total_demand")
p_grid = model.addVars(time_steps, lb = -GRB.INFINITY, name="exch_pow")
I want to implement something like this:
If ((quicksum(p_gen[t] for pp in plants) - quicksum(tot_d[u,t] for u in users) )>= p_grid[t] for t in time_steps)
model.addConstrs(A)
model.addConstrs(B)
else:
model.addConstrs(C)
My problem is that Gurobi does not understand the variables which depend on the time. I want to implement if the condition, so it depends on the condition the program, will you different addConstr.
How to implement this condition in Gurobi?
Linear Programming doesn't work like this.
You have constraints and your model must fulfill them, otherwise your model is infeasible.
You can't put constraints based on constraints conditions, if anything you can put constraints based on boolean conditions (like a setting, a value...) or you can put boolean constraints.
You can, however, have two models at the same time, with the same vars and constraints before the if / else branches.
You can resolve the first model, get the value you need with the x attribute (just call variable.x to get its value), and with that value you can select which constraints add to second model, and then resolve it.

How to stack variables together in cvxpy?

I want to solve an optimisation problem using cvxpy.
Suppose I want to use log_sum_exp function to build a constraint like this:
m >= log(1 + exp(m+z))
The syntax of cvxpy allows me to create a vector variable x = [z,m] of dimension 2 and apply matrix multiplication to create a vector of expressions 0, z+m:
import cvxpy
x = cvxpy.Variable(2)
coeff = np.array([
[0,0],
[1,1]
])
constraints = [ x[1] >= cvxpy.log_sum_exp(coeff * x)]
When coding like this, I lose some part of the logic because I want different names for different parts of my variable array. Is there some way to use the log_sum_exp transform more explicitly, like
z = cvxpy.Variable()
m = cvxpy.Variable()
constraints = [ m >= cvxpy.log_sum_exp([0, m+z]) ]
?
I couldn't find any hints in the official docs. Thanks!
As sascha pointed out, one of the manual pages
Functions in CVXPY
contains the answer. In particular, I can give an example of using log_sum_exp without matrix multiplication. Note that it is not possible to construct a correct problem within DCP (disciplined convex programming) framework using only operators exp and log because you will obtain concave function applied to convex one, which is considered as undefined behaviour. One should use a built-in constructor instead.
If you want to encode the constraint
F0 >= log( exp(F1) + exp(F2) + ... + exp(Fn) )
where F1, F2, ..., Fn are some convex expressions, and F0 is a concave expression,
then instead of introducing slack variables, one can type
import cvxpy
... # Define variables and functions
constraints = [
...,
something >= cvxpy.log_sum_exp(
cvxpy.vstack(
F1,
F2,
...,
Fn
)
)
]
... # Solve the optimisation problem
Note that vstack can be used both in multiple-argument style:
cvxpy.vstack(z, u)
and in list-style (but not tuples)
cvxpy.vstack([z,u])

Categories

Resources