Summary sheet
More extensive user documentation in Modeling and solving with CPMpy.
import cpmpy as cp
Model class
model = cp.Model()– Create aModel.model += constraint– Add a constraint (anExpression) to the model.model.maximize(obj)ormodel.minimize(obj)– Set the objective (anExpression).model.solve()– Solve the model with the default solver, returns True/False.model.solveAll()– Solve and enumerate all solutions, returns number of solutions.model.status()– Get the status of the last solver run.model.objective_value()– Get the objective value obtained during the last solver run.
Solvers
Solvers have the same API as Model. Solvers are instantiated throught the static cp.SolverLookup class:
cp.SolverLookup.solvernames()– List all installed solvers (including subsolvers).cp.SolverLookup.get(solvername, model=None)– Initialize a specific solver.
Decision Variables
Decision variables are NumPy-like objects: shape=None|1 creates one variable, shape=4 creates a vector of 4 variables, shape=(2,3) creates a matrix of 2x3 variables, etc.
Name is optional too, indices are automatically added to the name so each variable has a unique name.
x = cp.boolvar(shape=4, name="x")– Create four Boolean decision variables.x = cp.intvar(lb, ub)– Create one integer decision variable with domain[lb, ub](inclusive).x.value()– Get the value ofxobtained during the last solver run.
Core Expressions
You can apply the following standard Python operators on CPMpy expressions, which creates the corresponding Core Expression object:
Comparison:
==,!=,<,<=,>,>=Arithmetic:
+,-,*,//(integer division),%(modulo),**(power)Logical:
&(and),|(or),~(not),^(xor)Logical implication:
x.implies(y)
Logical operators only work on Boolean variables/constraints, numeric operators work on both integer and Boolean variables/expressions.
CPMpy overwrites the following Python built-ins, they allow vectorized operations:
You can index CPMpy expressions with an integer decision variable: x[y], which will create an Element expression object.
To index non-CPMpy arrays, wrap them with cpm_array(): cpm_array([1,2,3])[y].
Global Functions
Global functions are numeric functions that some solvers support natively (through a solver-specific global constraint). CPMpy automatically rewrites the global function as needed to work with any solver.
Computes the minimum value of the arguments |
|
Computes the maximum value of the arguments |
|
Computes the absolute value of the argument |
|
The Element(Arr, Idx) global function allows indexing into an array with a decision variable. |
|
The Count global function represents the number of occurrences of a value in an array |
|
The Among global function counts how many variables in an array take values that are in a given set of values. |
|
The NValue global function counts the number of distinct values in an array. |
|
The NValueExcept global function counts the number of distinct values in an array, excluding a specified value. |
Global Constraints
Global constraints are constraints (Boolean functions) that some solvers support natively. All global constraints can be reified (implication, equivalence) and used in other expressions, which CPMpy will handle.
All arguments have a different (distinct) value |
|
All nonzero arguments have a distinct value |
|
All arguments except those equal to a value in n have a distinct value. |
|
All arguments have the same value |
|
All arguments except those equal to a value in n have the same value. |
|
The sequence of variables form a circuit, where x[i] = j means that j is the successor of i. |
|
Inverse (aka channeling / assignment) constraint. |
|
The values of the variables in 'array' correspond to a row in 'table' |
|
Extension of the Table constraint where the table matrix may contain wildcards (STAR), meaning there are no restrictions for the corresponding variable in that tuple. |
|
The values of the variables in 'array' do not correspond to any row in 'table' |
|
The IfThenElse constraint, defining a conditional expression of the form: if condition then if_true else if_false where condition, if_true and if_false are all boolean expressions. |
|
The "InDomain" constraint, defining non-interval domains for an expression |
|
The |
|
Global cumulative constraint. Used for resource aware scheduling. Ensures that the capacity of the resource is never exceeded and enforces: duration >= 0 demand >= 0 start + duration == end. |
|
Constraint enforcing some values have precedence over others. |
|
Global no-overlap constraint. Used for scheduling problems Ensures no tasks overlap and enforces: duration >= 0 start + duration == end. |
|
The number of occurrences of each value vals[i] in the list of variables vars must be equal to occ[i]. |
|
The "Increasing" constraint, the expressions will have increasing (not strictly) values |
|
The "Decreasing" constraint, the expressions will have decreasing (not strictly) values |
|
The "IncreasingStrict" constraint, the expressions will have increasing (strictly) values |
|
The "DecreasingStrict" constraint, the expressions will have decreasing (strictly) values |
|
Given lists X,Y, enforcing that X is lexicographically less than Y. |
|
Given lists X,Y, enforcing that X is lexicographically less than Y (or equal). |
|
Given a matrix X, |
|
Given a matrix X, LexChainLessEq enforces that all rows are lexicographically ordered. |
|
A |
Guidelines and tips
Do not
from cpmpy import *, the implicit overloading of any/all and sum may break or slow down other libraries.Explicitly use CPMpy versions of built-in functions (
cp.sum,cp.all, etc.).Use global constraints/global functions where possible, some solvers will be much faster.
Stick to integer constants; floats and fractional numbers are not supported.
For maintainability, use logical code organization and comments to explain your constraints.
Toy example
import cpmpy as cp
# Decision Variables
b = cp.boolvar()
x1, x2, x3 = x = cp.intvar(1, 10, shape=3)
# Constraints
model = cp.Model()
model += (x[0] == 1)
model += cp.AllDifferent(x)
model += cp.Count(x, 9) == 1
model += b.implies(x[1] + x[2] > 5)
# Objective
model.maximize(cp.sum(x) + 100 * b)
# Solving
solved = model.solve()
if solved:
print("Solution found:")
print('b:', b.value(), ' x:', x.value().tolist())
else:
print("No solution found.")