Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increasing and decreasing global constraints #471

Merged
merged 10 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions cpmpy/expressions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
List of submodules
==================
.. autosummary::
python_builtins
:nosignatures:

variables
core
globalconstraints
globalfunctions
python_builtins
utils


Expand All @@ -21,7 +21,8 @@
# others need to be imported by the developer explicitely
from .variables import boolvar, intvar, cpm_array
from .variables import BoolVar, IntVar, cparray # Old, to be deprecated
from .globalconstraints import AllDifferent, AllDifferentExcept0, AllEqual, Circuit, Inverse, Table, Xor, Cumulative, IfThenElse, GlobalCardinalityCount, DirectConstraint, InDomain
from .globalconstraints import AllDifferent, AllDifferentExcept0, AllEqual, Circuit, Inverse, Table, Xor, Cumulative, \
IfThenElse, GlobalCardinalityCount, DirectConstraint, InDomain, Increasing, Decreasing, IncreasingStrict, DecreasingStrict
from .globalconstraints import alldifferent, allequal, circuit # Old, to be deprecated
from .globalfunctions import Maximum, Minimum, Abs, Element, Count, NValue
from .core import BoolVal
Expand Down
99 changes: 99 additions & 0 deletions cpmpy/expressions/globalconstraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,14 @@ def my_circuit_decomp(self):
Table
Xor
Cumulative
IfThenElse
GlobalCardinalityCount
DirectConstraint
InDomain
Increasing
Decreasing
IncreasingStrict
DecreasingStrict

"""
import copy
Expand Down Expand Up @@ -517,6 +524,98 @@ def value(self):
return all(decomposed).value()


class Increasing(GlobalConstraint):
"""
The "Increasing" constraint, the expressions will have increasing (not strictly) values
"""

def __init__(self, *args):
super().__init__("increasing", flatlist(args))

def decompose(self):
"""
Returns two lists of constraints:
1) the decomposition of the Increasing constraint
2) empty list of defining constraints
"""
args = self.args
return [args[i] <= args[i+1] for i in range(len(args)-1)], []

def value(self):
from .python_builtins import all
args = self.args
return all(args[i].value() <= args[i+1].value() for i in range(len(args)-1))


class Decreasing(GlobalConstraint):
"""
The "Decreasing" constraint, the expressions will have decreasing (not strictly) values
"""

def __init__(self, *args):
super().__init__("decreasing", flatlist(args))

def decompose(self):
"""
Returns two lists of constraints:
1) the decomposition of the Decreasing constraint
2) empty list of defining constraints
"""
args = self.args
return [args[i] >= args[i+1] for i in range(len(args)-1)], []

def value(self):
from .python_builtins import all
args = self.args
return all(args[i].value() >= args[i+1].value() for i in range(len(args)-1))


class IncreasingStrict(GlobalConstraint):
"""
The "IncreasingStrict" constraint, the expressions will have increasing (strictly) values
"""

def __init__(self, *args):
super().__init__("increasing_strict", flatlist(args))

def decompose(self):
"""
Returns two lists of constraints:
1) the decomposition of the IncreasingStrict constraint
2) empty list of defining constraints
"""
args = self.args
return [args[i] < args[i+1] for i in range(len(args)-1)], []

def value(self):
from .python_builtins import all
args = self.args
return all((args[i].value() < args[i+1].value()) for i in range(len(args)-1))


class DecreasingStrict(GlobalConstraint):
"""
The "DecreasingStrict" constraint, the expressions will have decreasing (strictly) values
"""

def __init__(self, *args):
super().__init__("decreasing_strict", flatlist(args))

def decompose(self):
"""
Returns two lists of constraints:
1) the decomposition of the DecreasingStrict constraint
2) empty list of defining constraints
"""
args = self.args
return [(args[i] > args[i+1]) for i in range(len(args)-1)], []

def value(self):
from .python_builtins import all
args = self.args
return all((args[i].value() > args[i+1].value()) for i in range(len(args)-1))


class DirectConstraint(Expression):
"""
A DirectConstraint will directly call a function of the underlying solver when added to a CPMpy solver
Expand Down
19 changes: 16 additions & 3 deletions cpmpy/solvers/choco.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,13 @@ def transform(self, cpm_expr):

cpm_cons = toplevel_list(cpm_expr)
supported = {"min", "max", "abs", "count", "element", "alldifferent", "alldifferent_except0", "allequal",
"table", "InDomain", "cumulative", "circuit", "gcc", "inverse", "nvalue"}
supported_reified = supported # choco supports reification of any constraint
"table", "InDomain", "cumulative", "circuit", "gcc", "inverse", "nvalue", "increasing",
"decreasing","increasing_strict","decreasing_strict"}
# choco supports reification of any constraint, but has a bug in increasing and decreasing
supported_reified = {"min", "max", "abs", "count", "element", "alldifferent", "alldifferent_except0",
"allequal", "table", "InDomain", "cumulative", "circuit", "gcc", "inverse", "nvalue"}
# for when choco new release comes, fixing the bug on increasing and decreasing
#supported_reified = supported
cpm_cons = decompose_in_tree(cpm_cons, supported, supported_reified)
cpm_cons = flatten_constraint(cpm_cons) # flat normal form
cpm_cons = canonical_comparison(cpm_cons)
Expand Down Expand Up @@ -490,7 +495,7 @@ def _get_constraint(self, cpm_expr):
elif isinstance(cpm_expr, GlobalConstraint):

# many globals require all variables as arguments
if cpm_expr.name in {"alldifferent", "alldifferent_except0", "allequal", "circuit", "inverse"}:
if cpm_expr.name in {"alldifferent", "alldifferent_except0", "allequal", "circuit", "inverse","increasing","decreasing","increasing_strict","decreasing_strict"}:
chc_args = self._to_vars(cpm_expr.args)
if cpm_expr.name == 'alldifferent':
return self.chc_model.all_different(chc_args)
Expand All @@ -502,6 +507,14 @@ def _get_constraint(self, cpm_expr):
return self.chc_model.circuit(chc_args)
elif cpm_expr.name == "inverse":
return self.chc_model.inverse_channeling(*chc_args)
elif cpm_expr.name == "increasing":
return self.chc_model.increasing(chc_args,0)
elif cpm_expr.name == "decreasing":
return self.chc_model.decreasing(chc_args,0)
elif cpm_expr.name == "increasing_strict":
return self.chc_model.increasing(chc_args,1)
elif cpm_expr.name == "decreasing_strict":
return self.chc_model.decreasing(chc_args,1)

# but not all
elif cpm_expr.name == 'table':
Expand Down
6 changes: 3 additions & 3 deletions tests/test_constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
EXCLUDE_GLOBAL = {"ortools": {},
"gurobi": {},
"minizinc": {"circuit"},
"pysat": {"circuit", "element","min","max","count", "nvalue", "allequal","alldifferent","cumulative"},
"pysdd": {"circuit", "element","min","max","count", "nvalue", "allequal","alldifferent","cumulative",'xor'},
"pysat": {"circuit", "element","min","max","count", "nvalue", "allequal","alldifferent","cumulative","increasing","decreasing","increasing_strict","decreasing_strict"},
"pysdd": {"circuit", "element","min","max","count", "nvalue", "allequal","alldifferent","cumulative","xor","increasing","decreasing","increasing_strict","decreasing_strict"},
"exact": {},
"choco": {}
}
Expand Down Expand Up @@ -152,7 +152,7 @@ def global_constraints(solver):
- AllDifferent, AllEqual, Circuit, Minimum, Maximum, Element,
Xor, Cumulative, NValue, Count
"""
global_cons = [AllDifferent, AllEqual, Minimum, Maximum, NValue]
global_cons = [AllDifferent, AllEqual, Minimum, Maximum, NValue, Increasing, Decreasing, IncreasingStrict, DecreasingStrict]
for global_type in global_cons:
cons = global_type(NUM_ARGS)
if solver not in EXCLUDE_GLOBAL or cons.name not in EXCLUDE_GLOBAL[solver]:
Expand Down
Loading