How to get all variables in a constraint in Pyomo? - pyomo

My question is simple. Given a Pyomo constraint, how can I easily extract the variables that appear in the constraint?
This question has been asked a few times already. I believe the Pyomo internals have been modified and the proposed solutions no longer work.
How to get variables of a constraint in Pyomo
Access all variables occurring in a pyomo constraint
Minimum working test problem:
from pyomo.environ import *
m = ConcreteModel()
m.I = Set(initialize=[i for i in range(5)])
m.x = Var(m.I,bounds=(-10,10),initialize=1.0)
m.z = Var(bounds=(-100,100), initialize=5.0)
m.con1 = Constraint(expr=m.x[0] + m.x[1] - m.x[3] >= 10)
m.con2 = Constraint(expr=m.x[0]*m.x[3] + m.x[1] >= 0)
m.con3 = Constraint(expr=m.x[4]*m.x[3] + m.x[0]*m.x[3] - m.x[4] == 0)
m.obj = Objective(expr=sum(m.x[i]**2 for i in m.I))
m.pprint()
opt = SolverFactory('ipopt')
opt.options['max_iter'] = 0
opt.solve(m, tee=True)
In this example, I would like to programmatically inspect the variables in con1.

The second link has the correct solution: Access all variables occurring in a pyomo constraint
identify_variables() still exists, but it looks like it was moved to pyomo.core.expr.visitor.
It might be worth promoting it into the pyomo.core.expr namespace.

Related

How to use a mutable Param pyomo?

I'm trying to make Param mutable with initializer zero but when I tried to read the Param in the constraint its doesn't understand what is reading, the only way that I found to read is using .value but when the problem finishes declaring the whole constraint. the solver found the first solution but when I defined the new param value, it doesn't change the value of the param. I know if I am doing bad the declared or I haven't found the correct white to do that.
As a solver im using CPLEX
This is the way i defined the Param:
model.s_value = pe.Param(mutable=True, initialize=0)
But if I use the Param as a normal Param is a constraint promo sent this message:
ValueError: Constraint 'def_constraint[0]' encountered a strict inequality expression ('>' or '<'). All constraints must be formulated using using '<=', '>=', or '=='.
I think is because when pyomo tired to read the param that sends an object like this:
pyomo.core.base.param.IndexedParam object at 0x1939C6A0
After declaring all the variable I put the solver inside a for and there I redefine the value of the Param:
model.s_value.value= new_value
Please. Someone can explain to me who can I use correctly the mutable Param and how can I iterate the model.
This is a simple example of changing the value of a mutable parameter. You didn't post a full set of executable code above, so it is difficult to figure out what is going on. If this below doesn't answer your question, request you update (edit) your post above with a minimal reproducible example.
import pyomo.environ as pyo
m = pyo.ConcreteModel()
m.x = pyo.Var(domain=pyo.NonNegativeReals)
m.p = pyo.Param(mutable=True, initialize = 10)
# the problem
m.OBJ = pyo.Objective(expr=m.x)
m.c1 = pyo.Constraint(expr=m.x >= m.p)
solver = pyo.SolverFactory('glpk')
# solve it...
results = solver.solve(m)
m.display() # x=10
# change the mutable parameter
m.p = 5
# re-solve it
results = solver.solve(m)
m.display() # x=5

How to look-up constraint by number in a Pyomo model?

We are attempted to debug a Pyomo model. Ipopt with halt_on_ampl_error True tells up Error evaluating constraint 30. Is there an easy way to programmatically look up a constraint in a Pyomo model by its number (using the numbering of Ipopt)?
You can just modify your call to solve slightly:
opt = SolverFactory('ipopt')
res = opt.solve(model, symbolic_solver_labels=True)
Then, you should see a more useful error message in the ipopt output.
Let me extend this answer to address the other part of the question regarding looking up a constraint by its number. Because Pyomo variables and constraints do not have indices, this is very solver-interface-specific. For Pynumero, you have a couple of options. Suppose you have the following ConcreteModel called m and PyomoNLP called nlp.
import pyomo.environ as pyo
from pyomo.contrib.pynumero.interfaces.pyomo_nlp import PyomoNLP
m = pyo.ConcreteModel()
m.x = pyo.Var()
m.y = pyo.Var()
m.obj = pyo.Objective(expr=m.y)
m.c1 = pyo.Constraint(expr=m.y >= m.x)
m.c2 = pyo.Constraint(expr=m.y >= -m.x)
nlp = PyomoNLP(m)
If you just want to get the index of of a few variables or constraints, you can use
var_indices = nlp.get_primal_indices([m.x, m.y])
con_indices = nlp.get_constraint_indices([m.c1, m.c2])
If you want complete maps both directions, you can use
con_to_index = dict()
index_to_con = dict()
var_to_index = pyo.ComponentMap()
index_to-var = dict()
for ndx, var in enumerate(nlp.get_pyomo_variables()):
var_to_index[var] = ndx
index_to_var[ndx] = var
for ndx, con in enumerate(nlp.get_pyomo_constraints()):
con_to_index[con] = ndx
index_to_con[ndx] = con
I will assume that you are using the PyomoNLP interface in PyNumero. If you are using the asl interface, this is a little more difficult. With the PyomoNLP interface in PyNumero, there are several methods that should be able to do what you need. Have a look at the comments in pyomo_nlp.py. Note that this stuff is pretty new and the API is subject to change.

Sparse index use for optimization with Pyomo model

I have a Pyomo model connected to a Django-created website.
My decision variable has 4 indices and I have a huge amount of constraints running on it.
Since Pyomo takes a ton of time to read in the constraints with so many variables, I want to sparse out the index set to only contain variables that actually could be 1 (i have some conditions on that)
I saw this post
Create a variable with sparse index in pyomo
and tried a for loop for all my conditions. I created a set "AllowedVariables" to later put this inside my constraints.
But Django's server takes so long to create this set while performing the system check, it never comes out.
Currently i have this model:
model = AbstractModel()
model.x = Var(model.K, model.L, model.F, model.Z, domain=Boolean)
def ObjRule(model):
# some rule, sense maximize
model.Obj = pyomo.environ.Objective(rule=ObjRule, sense=maximize)
def ARule(model,l):
maxA = sum(model.x[k,l,f,z] * for k in model.K for f in model.F
for z in model.Z and (k,l,f,z) in model.AllowedVariables)
return maxA <= 1
model.maxA = Constraint(model.L, rule=ARule)
The constraint is exemplary, I have 15 more similar ones. I currently create "AllowedVariables" this way:
AllowedVariables = []
for k in model.K:
for l in model.L:
..... check all sorts of conditions, break if not valid
AllowedVaraibles.append((k,l,f,z))
model.AllowedVariables = Set(initialize=AllowedVariables)
Using this, the Django server starts checking....and never stops
performing system checks...
Sadly, I somehow need some restriction on the variables or else the reading for the solver will take way to long since the constraints contain so many unnecessary variables that have to be 0 anyways.
Any ideas on how I can sparse my variable set?

Creating a if..then type rule (constraint) in Pyomo

I am new to Pyomo. I want to add an if..then.. type constraint to my linear programming problem. I have an abstract model and this is an example what I'd like to do:
if node j1 is receiving less than half of its water demand, the minimum flow in the link between j2 and j1 must be set to demand value in j1 (A and B are model variables, d is a known parameter).
if A(j1)<0.5 then B(j2,j1)>=d(j1)
I tried the following when I define model constraints. But since the model has not yet created the instance from its data file, it doesn't recognize j1 and j2.
def rule_(model):
term1=floor(model.A[j1]/0.5)
return (term1*model.B[j1,j2]>term1*mdoel.demand[j1])
model.rule=Constraint(rule=rule_)
If I take these lines after instantiating the model using data file, I think the constraint will not be implemented at all.
Can anyone help with this, please? Thanks.
"If/then" expressions and floor() are not linear, so they can't be inserted directly into a linear program. However, you can get the same effect by setting a binary flag and using that to activate and deactivate the constraint. Note that binary variables are also not linear, but they are commonly handled by mixed-integer solvers.
model.flag = Var(within=Binary)
def set_flag_rule(model):
# force the flag to be set if A[j1] < 0.5
return ((1 - model.flag) * 0.5 <= model.A[j1])
model.set_flag = Constraint(rule=set_flag_rule)
def rule(model):
# force B[j1, j2] to meet demand if the flag is set
return (model.B[j1,j2] >= model.flag * model.demand[j1])
model.rule=Constraint(rule=rule)

Set up model in CPLEX using Python API

In fact, I'm trying to implement the very simple model formulation:
min sum_i(y_i*f_i) + sum_i(sum_j(x_ij*c_ij))
s.t. sum_i(x_ij) = 1 for all j
x_ij <= y_i for all i,j
x_ij, y_i are binary
But I simply cannot figure out how the Python API works. They suggest creating variables like this:
model.variables.add(obj = fixedcost,
lb = [0] * num_facilities,
ub = [1] * num_facilities,
types = ["B"] * num_facilities)
# Create one binary variable for each facility/client pair. The variables
# model whether a client is served by a facility.
for c in range(num_clients):
model.variables.add(obj = cost[c],
lb = [0] * num_facilities,
ub = [1] * num_facilities,
types = ["B"] * num_facilities)
# Create corresponding indices for later use
supply = []
for c in range(num_clients):
supply.append([])
for f in range(num_facilities):
supply[c].append((c+1)*(num_facilities)+f)
# Constraint no. 1:
for c in range(num_clients):
assignment_constraint = cplex.SparsePair(ind = [supply[c][f] for f in \
range(num_facilities)],
val = [1] * num_facilities)
model.linear_constraints.add(lin_expr = [assignment_constraint],
senses = ["L"],
rhs = [1])
For this constraints I have no idea how the variables from above are refered to since it only mentions an auxiliary list of lists. Can anyone explain to me how that should work? The problem is easy, I also know how to do it in C++ but the Python API is a closed book to me.
The problem is the uncapacitated facility location problem and I want to adapt the example file facility.py
EDIT: One idea for constraint no.2 is to create one-dimensional vectors and use vector addition to create the final constraint. But that tells me that this is an unsupported operand for SparsePairs
for f in range(num_facilities):
index = [f]
value = [-1.0]
for c in range(num_clients):
open_constraint = cplex.SparsePair(ind = index, val = value) + cplex.SparsePair(ind = [supply[c][f]], val = [1.0])
model.linear_constraints.add(lin_expr=[open_constraint],
senses = ["L"],
rhs = [0])
The Python API is closer to the C Callable Library in nature than the C++/Concert API. Variables are indexed from 0 to model.variables.get_num() - 1 and can be referred to by index, for example, when creating constraints. They can also be referred to by name (the add method has an optional names argument). See the documentation for the VariablesInterface here (this is for version 12.5.1, which I believe you are using, given your previous post).
It may help to start looking at the most simple examples like lpex1.py (and read the comments). Finally, I highly recommend playing with the Python API from the interactive Python prompt (aka the REPL). You can read the help there and type things in to see what they do.
You also might want to take a look at the docplex package. This is a modeling layer built on top of the CPLEX Python API (or which can solve on the cloud if you don't have a local installation of CPLEX installed).