Quantcast
Channel: Yet Another Math Programming Consultant
Viewing all articles
Browse latest Browse all 804

Modeling surprises

$
0
0

Here is an example where the PuLP modeling tool goes berserk.

In standard linear programming, only \(\ge\), \(=\) and \(\le\) constraints are supported. Some tools also allow \(\ne\), which for MIP models needs to be reformulated into a disjunctive constraint. Here is an attempt to do this in PuLP [1]. PuLP does not support this relational operator in its constraints, so we would expect a meaningful error message.


#-------------------------------------------# funny behavior when using a != constraint# in a PuLP model#-------------------------------------------import pulp 

model = pulp.LpProblem("notequal")
x = pulp.LpVariable("x",lowBound=0,cat=pulp.LpInteger)
y = pulp.LpVariable("y",lowBound=0,cat=pulp.LpInteger)

model += x+y     # obj
model += pulp.LpConstraint(x != y) 

print(model)
# this is an alternative debugging tool: #    model.writeLP("/tmp/notequal.lp")# note that#    model += x != y # produces a different model!!


This prints:

notequal:
MINIMIZE
1*x + 1*y + 0
SUBJECT TO
_C1:0 = -1

VARIABLES
0<= x Integer
0<= y Integer

Obviously, this is a surprise: the generated LP is just nonsensical. Surprises are bad when it comes to programming. We want reliability and predictability. It goes without saying that we are not getting that here.

The underlying issue is that operator overloading (redefining what an operator does for certain arguments) is very difficult to make bulletproof. Another example of this problem is shown in [2]. 

If a modeling tool wants to implement \(\color{darkred}x\ne \color{darkred}y\) for integer variables  \(\color{darkred}x,\color{darkred}y\) , it would need to generate something like : \(\color{darkred}x \le \color{darkred}y-1 \mathbf{\ or\ } \color{darkred}x \ge \color{darkred}y+1\). In a MIP model that could look like \[\begin{align}&\color{darkred}x\le\color{darkred}y-1+\color{darkblue}M_1\cdot\color{darkred}\delta\\&\color{darkred}x\ge\color{darkred}y+1-\color{darkblue}M_2\cdot(1-\color{darkred}\delta)\\&\color{darkred}\delta\in\{0,1\} \end{align}\] where \(\color{darkblue}M_1\), \(\color{darkblue}M_2\) are judiciously chosen large constants. If \(\color{darkred}x \in \{0,1,\dots,\color{darkblue}U_x\}\), \(\color{darkred}y \in \{0,1,\dots,\color{darkblue}U_y\}\), then the tightest values would be \(\color{darkblue}M_1 := 1+\color{darkblue}U_x\), \(\color{darkblue}M_2 := 1+\color{darkblue}U_y\). If no good upperbounds \(\color{darkblue}U\) are available, we can use either indicator constraints \[\begin{align}&\color{darkred}\delta=0\implies\color{darkred}x\le\color{darkred}y-1\\&\color{darkred}\delta=1\implies \color{darkred}x\ge \color{darkred}y+1\\&\color{darkred}\delta \in \{0,1\}\end{align}\] or SOS1 variables: \[\begin{align}&\color{darkred}x\le\color{darkred}y-1+\color{darkred}s_1\\& \color{darkred}x\ge \color{darkred}y+1 - \color{darkred}s_2 \\ & \color{darkred}s_1,\color{darkred}s_2\ge 0 \\& \{\color{darkred}s_1,\color{darkred}s_2\} \in \mathbf{SOS1} \end{align}\] depending on the capabilities of the modeling tools and solvers that are used.


Example: Minizinc


The modeling language Minizinc can do the binary-variable transformation automatically when bounds are available. My example model looks like:

var 0..5: x;
var 0..7: y;

constraint x != y;

solve minimize x+y;

It will generate a MIP model like:

\ENCODING=ISO-8859-1
\Problem name: MIPCplexWrapper

Minimize
 obj1: X_INTRODUCED_0_
Subject To
 p_lin_0: x - y + 6 X_INTRODUCED_7_ <= 5
 p_lin_1: - x + y - 8 X_INTRODUCED_7_ <= -1
 p_lin_2: x + y - X_INTRODUCED_0_  = 0
Bounds
 0<= x <= 50<= y <= 70<= X_INTRODUCED_0_ <= 120<= X_INTRODUCED_7_ <= 1
Generals
 x  y  X_INTRODUCED_0_  X_INTRODUCED_7_
End


Let's rewrite this a little bit to make it more readable. We can recognize:

\[\begin{align}\min\>&\color{darkred}z \\ & \color{darkred}x \le \color{darkred}y - 1 + \color{darkblue}M_1 \cdot (1-\color{darkred}\delta) \\ &\color{darkred}x \ge \color{darkred}y + 1 - \color{darkblue}M_2 \cdot \color{darkred}\delta \\ & \color{darkred}z = \color{darkred}x+\color{darkred}y\end{align}\]  

Notice that the values of the big-M's are the same as we calculated before: \(\color{darkblue}M_1=6\) and \(\color{darkblue}M_2=8\).

Minizinc does not employ my suggestions when no upper bounds are available. In that case, it stops with an error message:

MiniZinc: assertion failed: Variable X_INTRODUCED_10_ needs finite upper bound for a big-M constraint, current domain -infinity..infinity

The message is a bit problematic: we don't have a X_INTRODUCED_10_ in our model. It would be better to point back to the cause of the problem: the variables x and y.


Viewing all articles
Browse latest Browse all 804

Trending Articles