Short note. A compact reference, correct as far as it goes. It will be expanded into a full guide with a worked example and an interactive demonstration.
Problem
You need a constraint that only applies conditionally — e.g. “if a machine
is running (y = 1), production x can be up to 500 units; if it’s off,
x must be 0.” That’s a logical condition, and MILP solvers only understand
linear inequalities.
When to use it
Any time a constraint should switch on or off based on a binary decision, and you don’t have (or don’t want to lean on) native indicator-constraint support in your solver.
Formulation
Introduce a binary y and a large constant M that safely bounds the
variable it controls:
x <= M * y # x can only be positive when y = 1
x >= 0
y in {0, 1}
For a two-sided condition (“if y = 1, then x >= 10”):
x >= 10 - M * (1 - y)
Code sketch
import pulp
prob = pulp.LpProblem("big_m_example", pulp.LpMaximize)
x = pulp.LpVariable("x", lowBound=0)
y = pulp.LpVariable("y", cat="Binary")
M = 500 # tightest valid upper bound on x, not "infinity"
prob += x <= M * y
Pitfalls
- M too large wrecks the LP relaxation and slows the solver dramatically. Always derive the tightest valid bound from the problem data, not a round number pulled from the air.
- M too small silently cuts off feasible solutions — worse than slow, it’s wrong.
- If your solver exposes native indicator constraints (Gurobi’s
addGenConstrIndicator, CPLEX’sadd_indicator), prefer those — see the Indicator Constraints pattern. They skip the M-tuning problem entirely.