Efficiency considerations

The goal of these extensions is to let you write models however you think about them, relying on the MP interface to convert them to the forms required by solvers. Nevertheless, there will be situations where one choice of formulation will lead to better solver performance than another. Here we collect some examples that are relevant to the current MP implementation. Future versions may automate some of these reformulations.

Simplification of logic

Always think if your model can be simplified: certain variables, constraints or logical conditions might not be needed.

Sometimes a given constraint can be equivalently reformulated, leading to fewer logical conditions, which generally improves solving:

var Flow {PRODUCTS,ARCS} >= 0;

minimize TotalCost:
   sum {p in PRODUCTS, (i,j) in ARCS} var_cost[p,i,j] * Flow[p,i,j] +
   sum {(i,j) in ARCS}
      if exists {p in PRODUCTS} Flow[p,i,j] > 0 then fix_cost[i,j];

Each term Flow[p,i,j] > 0 is converted separately, involving a separate binary variable and implication constraint. But for a given i and j, there exists a positive Flow[p,i,j] if and only if the sum of all Flow[p,i,j] is positive. Thus an alternative formulation is given by:

minimize TotalCost:
   sum {p in PRODUCTS, (i,j) in ARCS} var_cost[p,i,j] * Flow[p,i,j] +
   sum {(i,j) in ARCS}
      if sum {p in PRODUCTS} Flow[p,i,j] > 0 then fix_cost[i,j];

By taking advantage of the solver’s ability to work with linear expressions, this form enables a substantially more concise reformulation.

Creation of common subexpressions

subject to Shipment_Limits {(i,j) in ARCS}:
   sum {p in PRODUCTS} Flow[p,i,j] = 0 or
   min_ship <= sum {p in PRODUCTS} Flow[p,i,j] <= capacity[i,j];

minimize TotalCost:
   sum {p in PRODUCTS, (i,j) in ARCS} var_cost[p,i,j] * Flow[p,i,j] +
   sum {(i,j) in ARCS}
      if sum {p in PRODUCTS} Flow[p,i,j] > 0 then fix_cost[i,j];

The constraint implies that if sum {p in PRODUCTS} Flow[p,i,j] is positive, then it must be at least equal to min_ship. Thus > 0 can be replaced by >= min_ship in the objective expression:

minimize TotalCost:
   sum {p in PRODUCTS, (i,j) in ARCS} var_cost[p,i,j] * Flow[p,i,j] +
   sum {(i,j) in ARCS}
      if sum {p in PRODUCTS} Flow[p,i,j] >= min_ship then fix_cost[i,j];

As a result of this change, sum {p in PRODUCTS} Flow[p,i,j] >= min_ship is a subexpression in both the constraint and the objective, simplifying the converson of the model.

Bounds on subexpressions

var x {1..2} <= 2, >= -2;

minimize Goldstein-Price:
   (1 + (x[1] + x[2] + 1)^2
      * (19 - 14*x[1] + 3*x[1]^2 - 14*x[2] + 6*x[1]*x[2] + 3*x[2]^2))
 * (30 + (2*x[1] - 3*x[2])^2
      * (18 - 32*x[1] + 12*x[1]^2 + 48*x[2] - 36*x[1]*x[2] + 27*x[2]^2));

Solver performance can often be improved by tightening bounds on the variables. In this example, the bounds on the variables also imply bounds on the subexpressions (x[1] + x[2] + 1)^2 and (2*x[1] - 3*x[2])^2. By defining auxiliary variables t1 and t2 equal to these subexpressions, their bounds can be communicated to the solver:

var t1 >= 0, <= 25;   subj to t1def: t1 = (x[1] + x[2] + 1)^2;
var t2 >= 0, <= 100;  subj to t2def: t2 = (2*x[1] - 3*x[2])^2;

minimize Goldstein-Price:
   (1 + t1
      * (19 - 14*x[1] + 3*x[1]^2 - 14*x[2] + 6*x[1]*x[2] + 3*x[2]^2))
 * (30 + t2
      * (18 - 32*x[1] + 12*x[1]^2 + 48*x[2] - 36*x[1]*x[2] + 27*x[2]^2));

These bounds are observed to substantially improve Gurobi’s performance in this case.