flowchart LR id1[Obj 1] --- id2[Obj 2] --- id3[Obj 3]
Useful Gurobi features you may not know
Gurobi Live, Barcelona, 2023
gurobipy
import gurobipy as gp
from gurobipy import GRB
with gp.Env() as env, gp.Model(env=env) as model:
model = gp.Model("small")
x = model.addVar(vtype=GRB.BINARY, name="x")
y = model.addVar(vtype=GRB.BINARY, name="y")
z = model.addVar(vtype=GRB.BINARY, name="z")
model.setObjective(x + y + 2 * z, GRB.MAXIMIZE)
# Linear constraints
model.addConstr(x + 2 * y + 3 * z <= 4, "c0")
model.addConstr(x + y >= 1, "c1")
# Quadratic constraints
model.addConstr(x*x + y*y <= 4.0, "qc0")
model.optimize()
Example: Natural Exponential
\[ \begin{align*} \min ~& -2x + e^x \\ \text{s. t. } ~& 0 \leq x \leq 1 \end{align*} \]
Simple Constraints
Function Constraints
Static Piecewise-Linear (PWL) approximation: nonlinear function is approximated once before MIP solving starts
Cost vs. Accuracy
Accuracy controlled with constraint attributes or globally with model parameters
FuncPieces
: Number of segmentsFuncPieceLength
: Length of segmentsFuncPieceError
: Maximum deviationUnderestimate, overestimate or somewhere in between
FuncNonlinear = 1
(dynamic outer approximation)FuncNonlinear = 0
(default, static PWL approximation)What is better, static or dynamic?
Real-world optimization problems often have multiple, competing objectives
Maximize Profit
&
Minimize Late Orders
Minimize Shift Count
&
Maximize Worker Satisfaction
Minimize Cost
&
Maximize Product Durability
Maximize Profit
&
Minimize Risk
flowchart LR id1[Obj 1] --- id2[Obj 2] --- id3[Obj 3]
\[ \begin{align*} \min & ~w_1 f_1(x) + w_2 f_2(x) + w_3 f_3(x) \\ \text{s. t. } & ~x \in C \end{align*} \]
flowchart TD id1[Obj 1] --> id2[Obj 2] --> id3[Obj 3]
\[ \begin{align*} \min & ~f_1(x) \\ \text{s. t. } & ~x \in C \end{align*} \]
\[ \begin{align*} \min & ~f_2(x) \\ \text{s. t. } & ~x \in C \\ & ~f_1(x) \leq \epsilon_1 \end{align*} \]
\[ \begin{align*} \min & ~f_3(x) \\ \text{s. t. } & ~x \in C \\ & ~f_1(x) \leq \epsilon_1 \\ & ~f_2(x) \leq \epsilon_2 \end{align*} \]
model.setObjectiveN(expr, index, priority=0, weight=1,
abstol=1e-6, reltol=0, name="")
# expr (LinExpr): New alternative objective
# index (int): Index for new objective (to set parameters or
# query solution per objective)
# priority (int, optional): Objective's priority (ObjNPriority attribute)
# weight (float, optional): Objective's weight (ObjNWeight attribute)
# abstol (float, optional): Absolute tolerance used in calculating
# allowable degradation (ObjNAbsTol attribute)
# reltol (float, optional): Relative tolerance used in calculating
# allowable degradation (ObjNAbsTol attribute)
# name (string, optional): Objective's name
Workforce Scheduling:
# Set global sense for ALL objectives
model.ModelSense = GRB.MINIMIZE
# Set up primary objective: minimize total pay cost
model.setObjectiveN(gp.quicksum(pay[w] * x[w, s] for w, s in availability),
index=0, priority=2, abstol=20,
reltol=0, name='Cost')
# Set up secondary objective: minimize difference in shift length
model.setObjectiveN(maxShift - minShift, index=1, priority=1, name='Fairness')
flowchart TD id1[Obj 1] --> id2[Obj 2]
Additional constraint:
Obj 1 \(\leq\) rhs
\[ \begin{align*} \min & ~f_2(x) \\ \text{s. t. } & ~x \in C \\ & ~f_1(x) \leq \epsilon_1 \end{align*} \]
base_value = max(objbnd +|objval|*MIPGap, objbnd + MIPGapAbs, objval)
relaxed = max(ObjNRelTol*|base_value|, ObjNAbsTol)
rhs (𝝐_𝟏) = base_value + relaxed
objbnd : best bound of objective OBJ 1
objval : best solution value for objective OBJ 1
MIPGap : relative MIP gap
MIPGapAbs : absolute MIP gap
ObjNRelTol: further allowable relative degradation for OBJ 1
ObjNAbsTol: further allowable absolute degradation for OBJ 1
ModelSense
attribute
Too many objectives?
Weighted: log will be the same as for a single-objective model
Hierarchical:
--------------------------------------------------------------------
Multi-objectives: starting optimization with 3 objectives ...
--------------------------------------------------------------------
[...]
---------------------------------------------------------------------------
Multi-objectives: optimize objective 1 (Name) ...
---------------------------------------------------------------------------
---------------------------------------------------------------------------
Multi-objectives: starting optimization with 5 objectives (3 combined) ...
---------------------------------------------------------------------------
[...]
---------------------------------------------------------------------------
Multi-objectives: optimize objective 1 (weighted) ...
---------------------------------------------------------------------------
Idea: One model → solutions to multiple scenarios
Examples:
Motivation to use the Gurobi Multi-Scenario API:
Changes you can make:
It is not possible to explicitly:
GRB.INFINITY
/ -GRB.INFINITY
Phase 1: Find the best solution over all scenarios
[...]
Solving a multi-scenario model with 2 scenarios...
[...]
Nodes | Current Node | Objective Bounds | Work
Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
H 0 0 1.850017e+09 8.0000e+08 56.8% - 0s
Optimal solution found at node 131289 - now completing multiple scenarios...
Phase 2: Find the best solution for each scenario
Worst Incumbent
: The worst solution found over all scenariosBestBd
: Best possible objective value for any solution that has not yet been foundParameter settings | Behavior |
---|---|
PoolSearchMode=0 |
- Stores all solutions found in the regular optimization - No additional tree search performed |
PoolSearchMode=1 PoolSolutions=n |
- Stores n-1 additional solutions to the optimal solution - Controls how many solutions to save |
PoolSearchMode=2 PoolSolutions=n PoolGap=x |
- Stores n-1 best solutions with a MIPGap less than x% in addition to the optimal solution - Requires further tree search than PoolSearchMode=1 |
# Limit how many solutions to collect
model.setParam(GRB.Param.PoolSolutions, 100)
# Limit the search space by setting a gap for the
# worst possible solution that will be accepted
model.setParam(GRB.Param.PoolGap, 0.10)
# Do a systematic search for the k best solutions
model.setParam(GRB.Param.PoolSearchMode, 2)
Phase 1: Find one provably optimal solution (identical to MIP log except for last line)
Nodes | Current Node | Objective Bounds | Work
Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
0 0 65.87500 0 1 65.00000 65.87500 1.35% - 0s
Optimal solution found at node 0 - now completing solution pool...
Idea: build optimization models directly in terms of matrices and vectors
\[ \begin{align*} \min &~x^T Q x \\ \text{s. t. } &~Ax \geq b \\ &~x \geq 0 \end{align*} \]
Matrix variables: MVar
objects:
Building linear constraints with MVars
:
@
to build linear expressions and constraintsmodel.addConstr(A @ x == b)
A
is a Numpy ndarray
, or a Scipy.sparse matrixb
is a Numpy ndarray
Idea:
Examples:
import gurobipy as gp
def mycallback(model, where):
if where == gp.GRB.Callback.MIP:
solcnt = model.cbGet(gp.GRB.Callback.MIP_SOLCNT)
nodecnt = model.cbGet(gp.GRB.Callback.MIP_NODCNT)
if nodecnt >= 10 and solcnt >= 4:
print('Stop early')
model.terminate()
model = gp.read("data/glass4.mps")
model.optimize(mycallback)
Nodes | Current Node | Objective Bounds | Work
Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
[...]
0 2 8.0000e+08 0 77 2.0000e+09 8.0000e+08 60.0% - 0s
H 64 79 1.950016e+09 8.0000e+08 59.0% 22.5 0s
Stop early
H 74 79 1.900016e+09 8.0000e+08 57.9% 20.7 0s
[...]
Explored 79 nodes (2138 simplex iterations) in 0.11 seconds (0.08 work units)
[...]
Solution count 5: 1.90002e+09 1.95002e+09 2.00002e+09 ... 3.13336e+09
Solve interrupted
Best objective 1.900016200000e+09, best bound 8.000042222222e+08, gap 57.8949%
User-callback calls 527, time in user-callback 0.00 sec
Remarks
def mycallback(model, where):
...
if model._value == 1:
...
model._value = 1
model.optimize(mycallback)
cb
” (Model.cbGetSolution()
, Model.cbSetSolution()
, Model.cbLazy()
, …) and Model.terminate()
Gurobi Optimizer version 10.0.3 build v10.0.3rc0 (mac64[arm])
[...]
Optimize a model with 14 rows, 72 columns and 72 nonzeros
[...]
Solved in 0 iterations and 0.00 seconds (0.00 work units)
Infeasible model
Why is the model infeasible?
→ Compute an Irreducible Inconsistent Subsystem (IIS)
What changes do I need to make to recover feasibility?
→ Compute a Feasibility Relaxation: the smallest perturbation needed to recover feasibility
Given an infeasible system of constraints
Meant to be read and analyzed by a human
Computational complexity
User control to guide IIS computation: Attributes to include or exclude constraints / bounds from the IIS (IISConstrForce
, IISLBForce
, IISUBForce
, IISSOSForce
…)
Modified model minimizing the amount by which the bounds and linear constraints of the original model are violated
Violation can be measured as:
Infeasible model
\[ \begin{align*} \min &~c^T x \\ \text{s. t. } &~Ax \leq b \\ &~x \geq 0 \end{align*} \]
Feasibility relaxation
\[ \begin{align*} \min &||(s, u)||_p \\ \text{s. t. } &~Ax -s \leq b \\ &~x + u \geq 0 \\ &~s, u \geq 0 \end{align*} \]
# Simplified version
model.feasRelaxS(relaxobjtype, minrelax, vrelax, crelax)
# relaxobjtype (integer): 0-norm, 1-norm, 2-norm
# minrelax (boolean): controls type of feasibility relaxation
# vrelax (boolean): indicates whether variable bounds can be relaxed
# crelax (boolean): indicates whether constraints can be relaxed
# Full version
model.feasRelax(relaxobjtype, minrelax, vars, lbpen, ubpen, constrs, rhspen)
Start Values and Variable Hints
→ Complete or partial solutions provided by the user
Start Heuristics
→ Specialized heuristics in Gurobi to produce initial solutions
Situation:
Idea: Reduce solve times by specifying these values
Example: Planning with rolling horizon: every month plan next 12 months
→ values from prior solution of first 11 months can be used as start or hints
Start Values
Start
variable attribute (or load an .MST
MIP start file)NumStart
model attribute and StartNumber
parameterVariable Hints
VarHintVal
variable attribute. Optionally, express confidence with VarHintPri
No Relaxation Heuristic
NoRelHeurTime
/ NoRelHeurWork
parametersOther Start Heuristics (quite expensive, generally poor quality solutions)
ZeroObjNodes
- Number of nodes for running the zero objective heuristicMinRelNodes
- Number of nodes for running the minimum relaxation heuristicPumpPasses
- Number of passes of the feasibility pump heuristicGurobi Optimizer Features:
©️ Gurobi Optimization