Modelling Tricks#
Scalar and Pairwise Tricks#
These use threshold literals directly.
IntVar == IntVar: threshold-wise equivalence, linear in domain width.IntVar != IntVar: overlap-value forbidding with compact clauses (no aux variables).IntVar <=, <, >=, > IntVar: direct threshold implications and strict variants (no PB encoder).Full Boolean reification:
b == (x <= y)(and<, >=, >) compiles to two conditional native branches.IntVar.distance_at_most(other, D):|X-Y| <= Dvia shifted threshold implications, zero aux vars.IntVar.forbid_value(v): forbids one value using one small clause.IntVar.forbid_interval(start, end): removes a contiguous block using one jump implication.IntVar.in_range(start, end): returns an indicator literal for inclusive range membership.IntVar // const: floor-division by a positive constant via threshold remapping(q >= m) <-> (x >= m*d).
Aggregates and Prefix Patterns#
These avoid generic arithmetic encodings.
IntVector.max()/IntVector.min()(lazy wrappers) andModel.max(...)/Model.min(...)(eager): threshold-wise OR/AND.IntVector.upper_bound()/lower_bound(): one-sided aggregate bounds, cheaper than exactmax/minwhen only one direction is needed.IntVector.running_max()/running_min(): cumulative-fold helpers for prefix extrema.
Pseudo Boolean (PB) Tricks#
PBExprscalar multiplication: expressions like2 * (x + y + 1)stay lazy.Compiler GCD normalization: coefficients and bounds are reduced before dispatch [1] and merges repeated terms (e.g.
a + b + 2*b->a + 3*b).PB/Card compare compile cache: comparator are cached, so repeated equivalent PB/Card constraints reuse the same clauses.
PB Objective Tricks#
Objective lowering avoids unnecessary proxy variables.
Direct
PBExprobjective lowering:model.obj[w] += pbexprlowers to weighted soft unit clauses plus an objective offset.IntVarobjective lowering:model.obj[w] += xlowers to threshold soft units (ladder bits), linear in domain width, plus an offset.IntVar.piecewise(...)objective use: minimize step costs directly without proxy integers.
Piecewise and Step Function#
IntVar.piecewise(base_value, steps) maps an integer to a step function as a
lazy PBExpr using threshold deltas:
Properties:
zero new variables and zero clauses
works for monotonic and non-monotonic step functions
negative deltas are handled by PB normalization
composes into constraints and objectives
Compiler Fast Paths#
The compiler recognizes structured forms and emits ladder clauses.
Univariate fast path:
a*X OP C(OPin<=,<,>=,>,==), compiled to boundary literals or small clause groups without PB/Card.Univariate + boolean (Big-M) fast path:
a*X + w*b OP Csplits into two conditional univariate branches; avoids generic PB for common indicator constraints.Unified bivariate fast path:
a*X + b*Y OP Ccompiled with a threshold cliff tracer and zero helper variables (supersedes older offset/scaled special cases).Trivariate sum fast path:
X + Y <= ZandX + Y < Zare compiled directly with ladder-threshold implications (binary/ternary clauses), avoiding generic PB/Card.Bool-sum to IntVar channeling fast path:
\[X + c_1 \; OP \; (b_1 + \dots + b_n) + c_2\]for
OP in {==, <=, >=, <, >}(unit boolean coefficients), compiled with a sequential counter and directional ladder channeling.Specialized offset/scaled relations are handled by the unified bivariate path keeping the compiler simpler and less leak-prone.
Collection and Table Tricks#
CP conveniences compiled into CNF/PB-friendly forms.
EnumVar.is_in(...): fast subset membership as a CNF clause over existing choice literals.Vector.is_in(rows): allowed-combinations table constraint via row selectors + exactly-one + conditional row implications.IntVector[IntVar]element access: variable-index branch gating(idx=i) -> (vals_i OP rhs)forOP in {==, !=, <=, <, >=, >}.EnumVector.all_different(backend="bipartite"): column-wise AMO on existing enum literals.IntVector.all_different(backend="bipartite"): exact-value indicator channeling + value-column AMOs.
Matrix and Scheduling Tricks#
NumPy matrix indexing and slicing:
grid[r, c],grid[:, j],grid[r0:r1, c0:c1].flatten()for clean CP subset constraints.IntervalVarfixed-duration weld:end == start + durationis encoded by tying ladder thresholds directly, avoiding generic PB/Card equality for this relation.