Skip to content

Commit

Permalink
Native solver unified access (#505)
Browse files Browse the repository at this point in the history
* Unified access to native solver

* docs: native solver attribute
  • Loading branch information
ThomSerg authored Jul 18, 2024
1 parent 26bc446 commit cdd80f4
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 2 deletions.
7 changes: 7 additions & 0 deletions cpmpy/solvers/choco.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ def __init__(self, cpm_model=None, subsolver=None):
# initialise everything else and post the constraints/objective
super().__init__(name="choco", cpm_model=cpm_model)

@property
def native_model(self):
"""
Returns the solver's underlying native model (for direct solver access).
"""
return self.chc_model

def solve(self, time_limit=None, **kwargs):
"""
Call the Choco solver
Expand Down
6 changes: 6 additions & 0 deletions cpmpy/solvers/exact.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ def __init__(self, cpm_model=None, subsolver=None):

# initialise everything else and post the constraints/objective
super().__init__(name="exact", cpm_model=cpm_model)
@property
def native_model(self):
"""
Returns the solver's underlying native model (for direct solver access).
"""
return self.xct_solver

def _fillObjAndVars(self):
if not self.xct_solver.hasSolution():
Expand Down
7 changes: 7 additions & 0 deletions cpmpy/solvers/gurobi.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ def __init__(self, cpm_model=None, subsolver=None):
# it is sufficient to implement __add__() and minimize/maximize() below
super().__init__(name="gurobi", cpm_model=cpm_model)

@property
def native_model(self):
"""
Returns the solver's underlying native model (for direct solver access).
"""
return self.grb_model


def solve(self, time_limit=None, solution_callback=None, **kwargs):
"""
Expand Down
8 changes: 8 additions & 0 deletions cpmpy/solvers/minizinc.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,14 @@ def __init__(self, cpm_model=None, subsolver=None):
# initialise everything else and post the constraints/objective
super().__init__(name="minizinc:"+subsolver, cpm_model=cpm_model)

@property
def native_model(self):
"""
Returns the solver's underlying native model (for direct solver access).
"""
return self.mzn_model


def _pre_solve(self, time_limit=None, **kwargs):
""" shared by solve() and solveAll() """
import minizinc
Expand Down
6 changes: 6 additions & 0 deletions cpmpy/solvers/ortools.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ def __init__(self, cpm_model=None, subsolver=None):

# initialise everything else and post the constraints/objective
super().__init__(name="ortools", cpm_model=cpm_model)
@property
def native_model(self):
"""
Returns the solver's underlying native model (for direct solver access).
"""
return self.ort_model


def solve(self, time_limit=None, assumptions=None, solution_callback=None, **kwargs):
Expand Down
7 changes: 7 additions & 0 deletions cpmpy/solvers/pysat.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ def __init__(self, cpm_model=None, subsolver=None):
# initialise everything else and post the constraints/objective
super().__init__(name="pysat:"+subsolver, cpm_model=cpm_model)

@property
def native_model(self):
"""
Returns the solver's underlying native model (for direct solver access).
"""
return self.pysat_solver


def solve(self, time_limit=None, assumptions=None):
"""
Expand Down
7 changes: 7 additions & 0 deletions cpmpy/solvers/solver_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ def __init__(self, name="dummy", cpm_model=None, subsolver=None):
else:
self.maximize(cpm_model.objective_)

@property
def native_model(self):
"""
Returns the solver's underlying native model (for direct solver access).
"""
raise NotImplementedError("Solver does not support direct solver access. Look at the solver's API for alternative native objects to access directly.")

# instead of overloading minimize/maximize, better just overload 'objective()'
def minimize(self, expr):
"""
Expand Down
7 changes: 7 additions & 0 deletions cpmpy/solvers/z3.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ def __init__(self, cpm_model=None, subsolver="sat"):
# initialise everything else and post the constraints/objective
super().__init__(name="z3", cpm_model=cpm_model)

@property
def native_model(self):
"""
Returns the solver's underlying native model (for direct solver access).
"""
return self.z3_solver


def solve(self, time_limit=None, assumptions=[], **kwargs):
"""
Expand Down
4 changes: 2 additions & 2 deletions docs/modeling.md
Original file line number Diff line number Diff line change
Expand Up @@ -697,10 +697,10 @@ iv = cp.intvar(1,9, shape=3)
s = cp.SolverLookup.get("ortools")

s += AllDifferent(iv) # the traditional way, equivalent to:
s.ort_model.AddAllDifferent(s.solver_vars(iv)) # directly calling the API, has to be with native variables
s.native_model.AddAllDifferent(s.solver_vars(iv)) # directly calling the API (OR-Tools' python library), has to be with native variables

This comment has been minimized.

Copy link
@tias

tias Jul 18, 2024

Collaborator

should be s.native_model().AddAll...?

```

observe how we first map the CPMpy variables to native variables by calling `s.solver_vars()`, and then give these to the native solver API directly. This is in fact what happens behind the scenes when posting a DirectConstraint, or any CPMpy constraint.
Observe how we first map the CPMpy variables to native variables by calling `s.solver_vars()`, and then give these to the native solver API directly (in the case of OR-Tools, the `native_model` property returns a `CpModel` instance). This is in fact what happens behind the scenes when posting a DirectConstraint, or any CPMpy constraint. Consult [the solver API documentation](api/solvers.html) for more information on the available solver specific objects which can be accessed directly.

While directly calling the solver offers a lot of freedom, it is a bit more cumbersome as you have to map the variables manually each time. Also, you no longer have a declarative model that you can pass along, print or inspect. In contrast, a `DirectConstraint` is a CPMpy expression so it can be part of a model like any other CPMpy constraint. Note that it can only be used as top-level (non-nested, non-reified) constraint.

Expand Down

0 comments on commit cdd80f4

Please sign in to comment.