Convert constraints to linear form (cpmpy.transformations.linearize)
Transform constraints to a linear form.
This transformation is necessary for Integer Linear Programming (ILP) solvers, and also for translating to Pseudo-Boolean or CNF formats.
There are a number of components to getting a good linearisation: - Decomposing global constraints/functions in a ‘linear friendly’ way.
A common pattern is that constraints that enforce domain consistency on integer variables, that these constraints should be decomposed over a Boolean representation of the domain. This is the case for
AllDifferent,Elementand possibly others (for now, only done for AllDifferent in this module). Their default decomposition might not do it this way, in which case we want to use different decompositions.
Linearising multiplication of variables, e.g. bool*bool, bool*int and int*int. Because ‘*’ is a core Operator and not a global constraint, we need to do it with a helper function here.
Disequalities e.g. sum(X) != 5 should be rewritten as (sum(X) < 5) | (sum(X) > 5) and further flattened into implications and linearised.
Implications e.g. B -> sum(X) <= 4 should be linearised with the Big-M method.
In principle, but not actually currently implemented: - Domain encodings e.g. of A=cp.intvar(0,4) would create binary variables and constraints
like B0 -> A=0, B1 -> A=1, B2 -> A=2, etc and sum(B0..B4) = 1. However each B0 -> A=0 would require 2 Big-M constraints. Instead we can linearise the entire domain encoding with two constraints: sum(B0..4) = 1 and A = sum(B0..4 * [0,1,2,3,4]). We could even go as far as eliminate the original integer decision variable.
Module functions
Main transformations:
- linearize_constraint(): Transforms a list of constraints to a linear form.
Helper functions:
- canonical_comparison(): Canonicalizes comparison expressions by moving variables to the
left-hand side and constants to the right-hand side.
linearize_mul_comparison(): Linearizes multiplication comparisons (bool*bool | bool*int | int*int) <cmp> rhs.
Post-linearisation transformations:
- only_positive_bv(): Transforms constraints so only boolean variables appear positively
(no
NegBoolView).
only_positive_bv_wsum(): Helper function that replacesNegBoolViewin var/sum/wsum expressions with equivalent expressions using only_BoolVarImpl.only_positive_bv_wsum_const(): Same asonly_positive_bv_wsum()but returns the constant term separately.only_positive_coefficients(): Transforms constraints so only positive coefficients appear in linear constraints.
- cpmpy.transformations.linearize.canonical_comparison(lst_of_expr: Expression | List[Expression]) List[Expression][source]
Canonicalize comparison expressions.
Transforms linear expressions, or a reification thereof, into canonical form by: - moving all variables to the left-hand side - moving constants to the right-hand side
Expects the input constraints to be flat. Only apply after applying
flatten_constraint().- Parameters:
lst_of_expr – Single expression or list of CPMpy expressions to canonicalize. Can be a single
Expressionor a list.- Returns:
List of canonicalized CPMpy expressions. All variables are on the left-hand side and all constants are on the right-hand side of comparisons.
- cpmpy.transformations.linearize.linearize_constraint(lst_of_expr: List[Expression], supported: Set[str] = {}, csemap: Dict[Expression, _NumVarImpl] | None = None, implication_literal: _BoolVarImpl | NegBoolView | None = None, reified: bool = False) List[Expression][source]
Transforms all constraints to a linear form. This function assumes all constraints are in ‘flat normal form’ with only boolean variables on the lhs of an implication. Only apply after :func:’cpmpy.transformations.flatten_model.flatten_constraint()’ and :func:’cpmpy.transformations.reification.only_implies()’.
After linearize, the following constraints remain:
- Trivial:
BoolVal
- Linear comparisons:
Equality and non-strict inequality comparison where the left-hand side is a linear expression and the right-hand side is a constant:
LinExpr == ConstantLinExpr >= ConstantLinExpr <= Constant
The linear expression (LinExpr) can be: - A numeric variable (
NumVar) - A sum expression (sum([...])) - A weighted sum expression (wsum([weights], [vars]))
General comparisons or expressions, if their name is in `supported`:
Boolean expressions: -
GenExpr(whenis_bool())Numeric comparisons: -
GenExpr == Var/Constant(when GenExpr is numeric) -GenExpr <= Var/Constant(when GenExpr is numeric) -GenExpr >= Var/Constant(when GenExpr is numeric)
- Indicator constraints, if ‘->’ is in ‘supported’:
The left-hand side is always a Boolean variable or its negation, and the right-hand side can be a linear comparison or a general expression.
Linear comparisons: -
BoolVar -> LinExpr == Constant-BoolVar -> LinExpr >= Constant-BoolVar -> LinExpr <= ConstantGeneral expressions (when the expression name is in supported): -
BoolVar -> GenExpr(when GenExpr is boolean) -BoolVar -> GenExpr == Var/Constant(when GenExpr is numeric) -BoolVar -> GenExpr >= Var/Constant(when GenExpr is numeric) -BoolVar -> GenExpr <= Var/Constant(when GenExpr is numeric)
- Parameters:
lst_of_expr – List of CPMpy expressions to linearize. Must be in ‘flat normal form’ with only boolean variables on the left-hand side of implications.
supported – Set of primitive constraint that should not be linearized (e.g. ‘->’, ‘and’, ‘or’, ‘mul’, ‘alldifferent’). ‘->’ in case the solver supports implication constraints ‘and’, ‘or’ in case of e.g. a SAT solver ‘mul’ if the solver supports multiplication of two variables (otherwise it is linearized) ‘alldifferent’ if the solver supports it natively (otherwise this function uses a linear-friendly decomposition)
csemap – Optional dictionary for common subexpression elimination, mapping expressions to their flattened variable representations. Used to avoid creating duplicate variables for the same subexpression.
implication_literal – when set to, e.g. ‘b’ this means we should linearize b -> lst_of_expr. This is typically done in a recursive call, e.g. this function will call it when encountering a ‘b -> expr’ if ‘->’ is not in supported. When an implication literal is given, a ‘->’ inside it will always be linearized using Big-M even if ‘->’ is in ‘supported’.
reified – DEPRECATED, should always be False (use ‘implication_literal’ instead)
- Returns:
List of linearized CPMpy expressions. The constraints are in one of the forms described above (linear comparisons, general expressions, or indicator constraints).
- cpmpy.transformations.linearize.linearize_mul_comparison(cpm_expr: Comparison, supported: Set[str] = {}, reified: bool = False, csemap: Dict[Expression, _NumVarImpl] | None = None) Tuple[Expression, List[Expression]][source]
Linearizes multiplication comparisons (const*var | bool*bool | bool*int | int*int) <cmp> rhs.
- Parameters:
cpm_expr (Comparison) – Comparison to linearize, e.g. bool*bool <cmp> rhs or bool*int <cmp> rhs or int*int <cmp> rhs.
supported (Set[str]) – Set of constraint and variable type names that are supported by the target solver, e.g. {“sum”, “wsum”, “->”, “and”, “or”, “alldifferent”}.
reified (bool) – Whether the constraint is fully reified. When True, nested implications are linearized using Big-M method instead of indicator constraints.
csemap (Optional[Dict[Any, Any]]) – Optional dictionary for common subexpression elimination, mapping expressions to their flattened variable representations. Used to avoid creating duplicate variables for the same subexpression.
- Returns:
Tuple of (return expression, list of toplevel constraints) Warning: the return expression is either a linearized comparison, or an ‘and’ of linearized comparisons
- cpmpy.transformations.linearize.only_positive_bv(lst_of_expr: List[Expression], csemap: Dict[Any, Any] | None = None) List[Expression][source]
Replaces
ComparisoncontainingNegBoolViewwith equivalent expression using onlyBoolVar.Comparisons are expected to be linearized. Only apply after applying
linearize_constraint(cpm_expr). The resulting expression is linear if the original expression was linear.- Parameters:
lst_of_expr – List of linearized CPMpy expressions that may contain NegBoolView.
csemap – Optional dictionary for common subexpression elimination, mapping expressions to an equivalent decision variable.
- Returns:
List of CPMpy expressions where all boolean variables appear positively (no NegBoolView).
- cpmpy.transformations.linearize.only_positive_bv_wsum(expr: Expression) Expression[source]
Replaces a var/sum/wsum expression containing
NegBoolViewwith an equivalent expression using only_BoolVarImpl.It might add a constant term to the expression, e.g. 3*~b + 2*c is transfromed to 3 + -3*b + 2*c. If you want the constant separately, use
only_positive_bv_wsum_const().- Parameters:
expr – Linear expression (NumVar, sum, or wsum) that may contain NegBoolView.
- Returns:
Linear expression (NumVar, sum, or wsum) without NegBoolView. The constant term (if any) is incorporated into the expression.
- cpmpy.transformations.linearize.only_positive_bv_wsum_const(cpm_expr: Expression) Tuple[Expression, int][source]
Replaces a var/sum/wsum expression containing
NegBoolViewwith an equivalent expression using only_BoolVarImpl, and returns the constant term separately.If you want the expression where the constant term is part of the wsum returned, use
only_positive_bv_wsum().- Parameters:
cpm_expr – Linear expression (NumVar, sum, or wsum) that may contain NegBoolView.
- Returns:
pos_expr: Linear expression (NumVar, sum, or wsum) without NegBoolView.
- const: The constant term that must be added to pos_expr to make it equivalent
to the original expression.
- Return type:
Tuple of
- cpmpy.transformations.linearize.only_positive_coefficients(lst_of_expr: List[Expression]) List[Expression][source]
Replaces Boolean terms with negative coefficients in linear constraints with terms with positive coefficients by negating the literal.
This can simplify a wsum into sum when all coefficients become 1. Input expressions are expected to be canonical comparisons. Only apply after applying
canonical_comparison(cpm_expr).- Parameters:
lst_of_expr – List of canonical CPMpy expressions (comparisons) that may contain boolean variables with negative coefficients.
- Returns:
List of CPMpy expressions where all boolean variables have positive coefficients. The resulting expression is linear if the original expression was linear.