Upgrading to CPMpy 1.0
CPMpy v1.0.0 is a major release: many transformations were rewritten, expressions are typed, and a few modeling constructs changed in ways that may require small updates to your code.
This page shows the most common changes with small examples. For the complete list — including all internal changes, deprecations and widened APIs — see the full changelog on GitHub.
Note
Models saved to a .pickle file with CPMpy < 1.0.0 will no longer load (or will fail when used) in
v1.0.0. Re-run your model-building code under v1.0.0 and save again. For longer-term storage, consider
writing models to a solver-independent text format with the new cpmpy.tools.io module.
Float objectives: use FloatSum
Float coefficients are no longer allowed inside regular expressions, and Model.minimize()/Model.maximize()
only accept integer-valued expressions. Float objectives are now expressed with the objective-only
FloatSum, passed directly to a solver object:
# before (<= 0.10)
model.minimize(0.5*x + 1.5*y)
model.solve()
print(model.objective_value())
# now (1.0)
obj = cp.FloatSum([0.5, 1.5], [x, y])
s = cp.SolverLookup.get("ortools", model) # model contains only the constraints
s.minimize(obj)
s.solve()
print(obj.value()) # s.objective_value() stays None when the optimum is not integral
Float coefficients in constraints should be rescaled to integers, e.g. replace 0.5*x + 1.5*y <= 2
by x + 3*y <= 4.
Lowercase constraint aliases are gone
The functions cp.alldifferent(), cp.allequal() and cp.circuit() (deprecated since 0.9.0) are no
longer exported. Use the global constraint classes:
cp.alldifferent(x) # before, AttributeError in 1.0
cp.AllDifferent(x) # now
Inspecting expressions
If your code walks or matches expression trees, three changes matter:
x, y = cp.intvar(0, 10, shape=2)
arr = cp.intvar(0, 10, shape=(3, 3))
# 1. multiplication is a global function now, no longer an Operator
isinstance(x*y, cp.expressions.core.Operator) # False (was True)
isinstance(x*y, cp.Multiplication) # True
# 2. multi-dimensional indexing creates NDElement, and Element is 1D-only
isinstance(arr[x, y], cp.Element) # False (was True)
isinstance(arr[x, y], cp.NDElement) # True
# 3. .args is a read-only tuple instead of a list
(x + y).args # (IV0, IV1) — a tuple
(x + y).update_args([x, 3]) # explicit in-place update
Also note that variable arrays (NDVarArray, as returned by
intvar(..., shape=...)) are no longer Expression subclasses: all modeling functionality is
unchanged, but isinstance(arr, Expression) is now False and arr.args/arr.name no longer exist.
Relocated tooling
The DIMACS and XCSP3 readers moved to the new cpmpy.tools.io module (the old names still work
but raise a DeprecationWarning):
from cpmpy.tools.dimacs import read_dimacs # before
from cpmpy.tools.io import load_dimacs # now (also accepts strings and file objects)
from cpmpy.tools.xcsp3 import read_xcsp3 # before
from cpmpy.tools.io import load_xcsp3 # now
Or use the generic entry points cpmpy.tools.io.load() and cpmpy.tools.io.write(), which select the
format based on the file extension.
More
The changelog on GitHub additionally covers:
stricter global constraint constructors (e.g.
Tablerequires a rectangular integer table, andTable/Minimum/Maximumno longer flatten nested lists),minor behavior changes from bug fixes (numpy broadcasting,
value()on partially-assigned globals),changes to the transformation interfaces for advanced users,
and the new, widened APIs (intermediate-solution
display=callbacks, iterable assumptions, …).