Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
64791cc
first minimally working version of scip interface, thanks Mark Turner…
tias Sep 17, 2023
0d9f2f4
update transform
IgnaceBleukx Sep 18, 2023
e5574b3
update indicator constraints
IgnaceBleukx Sep 18, 2023
aeaf02e
leftover from gurobi
IgnaceBleukx Sep 18, 2023
74ace09
scip does not support min/max/abs
IgnaceBleukx Sep 18, 2023
701731e
update tests and SolverLookup
IgnaceBleukx Sep 18, 2023
94a66dd
add False
IgnaceBleukx Sep 18, 2023
2b430c9
reduce default verbosity
IgnaceBleukx Sep 18, 2023
4d0d7f8
solveAll
IgnaceBleukx Sep 18, 2023
925e4f0
special cardinality constraints
IgnaceBleukx Sep 18, 2023
6dedf8f
update init
Wout4 Sep 19, 2023
15295c0
refactor status translation
IgnaceBleukx Oct 3, 2023
eae98e6
minor
IgnaceBleukx Oct 3, 2023
c3a528e
proper objective value retrieving
IgnaceBleukx Oct 3, 2023
75a54a2
remove leftover mul and div restrictions from Gurobi
IgnaceBleukx Oct 3, 2023
0b57056
remove import
IgnaceBleukx Oct 3, 2023
d6e10f6
refactor indicator constraints
IgnaceBleukx Oct 3, 2023
96c863e
Merge branch 'master' into scip2
tias Feb 5, 2026
fbb08ca
putting an Agent to work...
tias Feb 6, 2026
1907399
Merge remote-tracking branch 'origin/master' into scip2
tias Mar 16, 2026
46dc7fc
scip with linear_decompose etc
tias Mar 17, 2026
bbed8a3
test solvers: sol limit for all and smaller cumulative
tias Mar 17, 2026
230456b
Merge branch 'master' into scip2
tias Mar 25, 2026
91a7540
update tests
tias Mar 25, 2026
8c6ae15
add scip requirements and ci
IgnaceBleukx Mar 25, 2026
4e74c9d
fix error raising in supported check
IgnaceBleukx Mar 25, 2026
3258d1d
pyscipopt is typed
IgnaceBleukx Mar 25, 2026
8af18da
re-add pyscopopt to mypy.ini
IgnaceBleukx Mar 25, 2026
14947f1
Merge remote-tracking branch 'origin/master' into scip2
tias Apr 14, 2026
acee739
Merge remote-tracking branch 'origin/master' into scip2
tias Apr 14, 2026
17c7093
scip: fix imports
tias Apr 14, 2026
c463c02
scip: div fix
tias Apr 15, 2026
f94d80e
doc updates
tias Apr 15, 2026
4c49fec
Process some of the smaller comments
hbierlee Apr 17, 2026
57492fc
Merge branch 'master' into scip2
hbierlee Apr 17, 2026
078292e
Some additions to test_solverinterface to be split out
hbierlee Apr 21, 2026
a51999a
Process remaining review comments
hbierlee Apr 21, 2026
e992028
Remove unused reified abs case
hbierlee Apr 21, 2026
b1b92cc
Actually use the SCIP != case
hbierlee Apr 21, 2026
b6f88fd
Remove != (or rather Disjunction) support
hbierlee Apr 21, 2026
cc92701
Remove dead `sub` support
hbierlee Apr 21, 2026
32a537b
Refactor _make_numexpr
hbierlee Apr 21, 2026
21d446d
Merge branch 'master' into scip2
hbierlee Apr 21, 2026
8e18fcd
Remove changes to `test_solverinterface`
hbierlee Apr 21, 2026
b563c9f
Polish
hbierlee Apr 21, 2026
0aad31f
Remove duplicated docstrings
hbierlee Apr 21, 2026
6d2882a
Polish PR
hbierlee Apr 21, 2026
1f794b6
Polish
hbierlee Apr 21, 2026
e6bb864
typo/consistency
tias Apr 22, 2026
cf1d2fa
tweak
tias Apr 22, 2026
42814a8
too quick...
tias Apr 22, 2026
b86b077
More explicit has_objective check
hbierlee Apr 22, 2026
4b74e8c
Rm superfluous test
hbierlee Apr 22, 2026
8bca922
Move GlobalConstraint case
hbierlee Apr 22, 2026
e566150
Merge branch 'master' into scip2
hbierlee Apr 22, 2026
4692984
Fix syntax
hbierlee Apr 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/python-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jobs:
sudo snap install minizinc --classic
- solver: pindakaas
- solver: pumpkin
- solver: scip
uses: ./.github/workflows/test-solver.yml
with:
solver: ${{ matrix.solver }}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Install simply with `pip install cpmpy`
CPMpy can translate to a wide variety of constraint solving paradigms, including both commercial and open-source solvers.

* **CP Solvers**: OR-Tools (default), IBM CP Optimizer (license required), Choco, Glasgow GCS, Pumpkin, MiniZinc+solvers
* **ILP Solvers**: Gurobi (license required), CPLEX (license required)
* **ILP Solvers**: SCIP, Gurobi (license required), CPLEX (license required)
* **GO Solvers**: Hexaly (license required)
* **SMT Solvers**: Z3
* **PB Solvers**: Exact
Expand Down
3 changes: 3 additions & 0 deletions cpmpy/solvers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
cplex
hexaly
rc2
scip

=========================
List of helper submodules
Expand Down Expand Up @@ -78,6 +79,7 @@
from .cplex import CPM_cplex
from .hexaly import CPM_hexaly
from .rc2 import CPM_rc2
from .scip import CPM_scip

__all__ = [
"CPM_choco",
Expand All @@ -94,6 +96,7 @@
"CPM_pysat",
"CPM_pysdd",
"CPM_rc2",
"CPM_scip",
"CPM_z3",
"SolverLookup",
"param_combinations",
Expand Down
388 changes: 388 additions & 0 deletions cpmpy/solvers/scip.py

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions cpmpy/solvers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .ortools import CPM_ortools
from .minizinc import CPM_minizinc
from .pysat import CPM_pysat
from .scip import CPM_scip
from .z3 import CPM_z3
from .gcs import CPM_gcs
from .pysdd import CPM_pysdd
Expand Down Expand Up @@ -90,6 +91,7 @@ def base_solvers(cls):
("pindakaas", CPM_pindakaas),
("hexaly", CPM_hexaly),
("rc2", CPM_rc2),
("scip", CPM_scip),
]

@classmethod
Expand Down
2 changes: 1 addition & 1 deletion docs/installation_instructions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ CPMpy supports a multitude of solvers of different technologies to be used as ba
.. code-block:: bash
# Choose any subset of solvers to install
$ pip install cpmpy[choco, cpo, exact, gcs, gurobi, minizinc, pysat, pysdd, z3]
$ pip install cpmpy[choco, cpo, exact, gcs, gurobi, minizinc, pysat, pysdd, z3, scip]
Some solvers require additional steps (like acquiring a (aca.) license). Have a look at :ref:`this <supported-solvers>` overview.

Expand Down
2 changes: 1 addition & 1 deletion docs/solvers.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

CPMpy can be used as a declarative modeling language: you create a `Model()`, add constraints and call `solve()` on it.

The default solver is OR-Tools CP-SAT, an award winning constraint solver. But CPMpy supports multiple other solvers: more CP solvers (IBM-CPO, Choco, GCS, Pumpkin), ILP solvers (Gurobi, IBM ILOG CPLEX), SAT solvers (those in PySAT and Pindakaas), the RC2 MaxSAT solver, the Z3 SMT solver, a conflict-driven cutting-planes solver (Exact), even a knowledge compiler (PySDD), a global optimisation solver (Hexaly) and any CP solver supported by the text-based MiniZinc language.
The default solver is OR-Tools CP-SAT, an award winning constraint solver. But CPMpy supports multiple other solvers: more CP solvers (IBM-CPO, Choco, GCS, Pumpkin), ILP solvers (Gurobi, IBM ILOG CPLEX, SCIP), SAT solvers (those in PySAT and Pindakaas), the RC2 MaxSAT solver, the Z3 SMT solver, a conflict-driven cutting-planes solver (Exact), even a knowledge compiler (PySDD), a global optimisation solver (Hexaly) and any CP solver supported by the text-based MiniZinc language.

See the list of solvers known by CPMpy with:

Expand Down
5 changes: 4 additions & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ ignore_missing_imports = True
[mypy-z3.*]
ignore_missing_imports = True

[mypy-pyscipopt.*]
ignore_missing_imports = True

# untyped pycsp3, used in tools.xcsp3
[mypy-pycsp3.*]
ignore_missing_imports = True
ignore_missing_imports = True
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def get_version(rel_path):
"pumpkin": ["pumpkin-solver>=0.3.0"], # CPMpy requires features only available from Pumpkin version >=0.3.0
"pindakaas": ["pindakaas>=0.5.0"],
"cplex": ["docplex>=2.28.240", "cplex>=20.1.0.4"],
"scip": ["pyscipopt>=6.1"]
}
solver_dependencies["all"] = list({pkg for group in solver_dependencies.values() for pkg in group})

Expand Down
44 changes: 30 additions & 14 deletions tests/test_solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from cpmpy.solvers.exact import CPM_exact
from cpmpy.solvers.choco import CPM_choco
from cpmpy.solvers.cplex import CPM_cplex
from cpmpy.solvers.scip import CPM_scip
from cpmpy import SolverLookup
from cpmpy.exceptions import MinizincNameException, NotSupportedError

Expand Down Expand Up @@ -1008,7 +1009,6 @@ def test_incremental_assumptions(self, solver):
assert s.solve(assumptions=[])

def test_vars_not_removed(self, solver):

bvs = cp.boolvar(shape=3)
m = cp.Model([cp.any(bvs) <= 2])

Expand Down Expand Up @@ -1050,13 +1050,8 @@ def test_partial_div_mod(self, solver):
m += x // y == d
m += x % y == r
sols = set()
solution_limit = None
time_limit = None
if solver == 'gurobi':
solution_limit = 15 # Gurobi does not like this model, and gets stuck finding all solutions
if solver == "hexaly":
time_limit = 5
m.solveAll(solver=solver, solution_limit=solution_limit, time_limit=time_limit, display=lambda: sols.add(tuple(argvals(vars))))
solution_limit = 15 # ILP solvers don't like this model and tend to get stuck finding all solutions
m.solveAll(solver=solver, solution_limit=solution_limit, display=lambda: sols.add(tuple(argvals(vars))))
for sol in sols:
xv, yv, dv, rv = sol
assert dv * yv + rv == xv
Expand All @@ -1082,16 +1077,16 @@ def test_status(self, solver):

# now making a tricky problem to solve
np.random.seed(0)
start = cp.intvar(0,100, shape=50)
dur = np.random.randint(1,5, size=50)
end = cp.intvar(0,100, shape=50)
demand = np.random.randint(10,15, size=50)
start = cp.intvar(0,50, shape=20)
dur = np.random.randint(1,5, size=20)
end = cp.intvar(0,50, shape=20)
demand = np.random.randint(10,15, size=20)

m += cp.Cumulative(start, dur, end,demand, 30)
m += cp.Cumulative(start, dur, end,demand, 20)
m.minimize(cp.max(end))
m.solve(solver=solver, time_limit=1)
# normally, should not be able to solve within 1s...
assert m.status().exitstatus in (ExitStatus.FEASIBLE, ExitStatus.UNKNOWN)
assert m.status().exitstatus in (ExitStatus.OPTIMAL, ExitStatus.FEASIBLE, ExitStatus.UNKNOWN)

# now trivally unsat
m += cp.sum(bv) <= 0
Expand Down Expand Up @@ -1208,3 +1203,24 @@ def test_objective_numexprs(solver, constraint):
assert constraint.value() > constraint.get_bounds()[0] # bounds are not always tight, but should be larger than lb for sure
except NotSupportedError:
pytest.skip(reason=f"{solver} does not support optimisation")


@pytest.mark.skipif(not CPM_scip.supported(), reason="Scip not installed")
def test_scip_special_cardinality():
bvs = cp.boolvar(shape=4)
sos1 = cp.sum(bvs) <= 1

model = cp.Model(sos1)
s = cp.SolverLookup.get("scip", model)
constraints = s.scip_model.getConss()
assert constraints[0].getConshdlrName() == "SOS1" # translated to native SOS1
assert s.solve()
assert bvs.value().sum() <= 1

card = cp.sum(bvs) <= 3
model = cp.Model(card)
s = cp.SolverLookup.get("scip", model)
constraints = s.scip_model.getConss()
assert constraints[0].getConshdlrName() == "cardinality" # translated to native cardinality
assert s.solve()
assert bvs.value().sum() <= 3
Loading