1

I want to eliminate linear equality constraints on integral variables in a pyomo model by substitution. For instance, I wish to transform the model

enter image description here

by substituting

enter image description here ( * )

to

enter image description here

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]])
  • It may be better to leave that to the solver. – Erwin Kalvelagen Feb 28 '18 at 12:00
  • If my concern was to solve the model, you certainly would be right. However, I am testing a high-level algorithm for solving mixed-integer optimization problems which only works if equalty constraints on integral variables are eliminated from the model. – Christoph Neumann Feb 28 '18 at 12:21
  • 1
    Short answer yes. There is a `substitute` capability for expressions, and a way to detect linear equality constraints, but nothing yet that does exactly what you want, to my knowledge. – Qi Chen Feb 28 '18 at 22:22
  • I know how to detect linear equality constraints so I would be quite happy if you could demonstrate how to use the `substitute` capability for expressions. Anyway, thanks for your comment. – Christoph Neumann Mar 01 '18 at 10:24

1 Answers1

1

What you are describing is generally referred to as "variable aggregation." As you indicate, there are four basic steps:

  1. Identify the linear equality equations you want to remove
  2. Compute the substitution map
  3. Deactivate the equality constraints that you want to remove
  4. 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:

  1. Setting the constraint with this syntax should work with recent Pyomo releases (I tested back through 5.1)
  2. 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.
  3. Pyomo 5.5 will have a new expression system that will likely have a different mechanism for manipulating / substituting variables.
jsiirola
  • 2,259
  • 2
  • 9
  • 12
  • Thanks heaps for this explanation! I am still struggling with "automating" step 3 and 4 (in fact, only the last line of your example causes trouble). I think the main reason for my failure is that I manipulate a reference and not the model-object itself. I use some line like `constraints = list(m.component_objects(Constraint, active=True))` and then manipulate constraints from this list. Can you point out how to properly manipulate the constraints from the model? Also, it would be beneficial to actually delete the corresponding eliminated constraints (i.e. with `del m.c`). – Christoph Neumann Apr 18 '18 at 09:18
  • 1
    There is no reason to delete the constraints, simply deactivating them will prevent them from being sent to the solver. As for manipulating all constraints in a general way, something like `for c in m.componsnt_data_objects(Constraint, active=True): c.set_value((c.lower, clone_expression(c.body), c.upper))` would probably come close to doing what you want. The trick is to use `component_data_objects`, as that takes care of "unrolling" both scalar and indexed constraints. – jsiirola Apr 18 '18 at 14:01
  • Thanks again.For me, deactivated constraints have a meaning. Without going into to much depth- we do something similar to the extended cutting plane method for convex MINLPs: We deactivate nonlinear constraints, pass the linear model to a solver and then check if the nonlinear constraints are satisfied at some minimizer of the linear model. If not, we add a cutting plane. However, explicitly using the nonlinearity, deactivating equality-constraints suffices. As I need to compute the gradient of violated constraint functions, deleting eliminated variables seems beneficial. Is this possible? – Christoph Neumann Apr 19 '18 at 15:36
  • 1
    Yes: you should be able to delete components with either `del` or `block.del_component()`. You will want to be careful with indexed constraints, as deleting the component removes *all* the constraints. If you only want to remove one you can do something akin to `block.c[i] = Constraint.Skip` (but that only works on very recent versions of Pyomo). Instead of deactivating NL constraints, when I do things like this, I frequently clone the entire model instead of doing everything in place, e.g. the GDP cutting planes https://github.com/Pyomo/pyomo/blob/master/pyomo/gdp/plugins/cuttingplane.py – jsiirola Apr 19 '18 at 17:13