From d8669bf3509f3cee6f6ef06b1dff83e7cbeb5f6c Mon Sep 17 00:00:00 2001 From: Dimos Tsouros Date: Fri, 3 May 2024 13:58:24 +0200 Subject: [PATCH 1/9] increasing and decreasing --- cpmpy/expressions/globalconstraints.py | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/cpmpy/expressions/globalconstraints.py b/cpmpy/expressions/globalconstraints.py index cb792bd89..856146710 100644 --- a/cpmpy/expressions/globalconstraints.py +++ b/cpmpy/expressions/globalconstraints.py @@ -517,6 +517,46 @@ 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 + """ + return [var1 <= var2 for var1, var2 in all_pairs(self.args)], [] + + def value(self): + from .python_builtins import all + return all(var1.value() <= var2.value() for var1, var2 in all_pairs(self.args)) + +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 + """ + return [var1 >= var2 for var1, var2 in all_pairs(self.args)], [] + + def value(self): + from .python_builtins import all + return all(var1.value() >= var2.value() for var1, var2 in all_pairs(self.args)) + class DirectConstraint(Expression): """ A DirectConstraint will directly call a function of the underlying solver when added to a CPMpy solver From 6d62627abb6ef942c0c8d62bc22b0e7c8a2ba753 Mon Sep 17 00:00:00 2001 From: Dimos Tsouros Date: Fri, 3 May 2024 14:06:34 +0200 Subject: [PATCH 2/9] update tests --- tests/test_constraints.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_constraints.py b/tests/test_constraints.py index 40b7e1590..edc903644 100644 --- a/tests/test_constraints.py +++ b/tests/test_constraints.py @@ -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"}, + "pysdd": {"circuit", "element","min","max","count", "nvalue", "allequal","alldifferent","cumulative","xor","increasing","decreasing"}, "exact": {}, "choco": {} } @@ -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] 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]: From 3ad3b772d74efb31020c2b0aad5c7b73a0ff6625 Mon Sep 17 00:00:00 2001 From: Dimos Tsouros Date: Fri, 3 May 2024 14:12:46 +0200 Subject: [PATCH 3/9] strict increasing and decreasing --- cpmpy/expressions/globalconstraints.py | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/cpmpy/expressions/globalconstraints.py b/cpmpy/expressions/globalconstraints.py index 856146710..bc0b8db5e 100644 --- a/cpmpy/expressions/globalconstraints.py +++ b/cpmpy/expressions/globalconstraints.py @@ -537,6 +537,7 @@ def value(self): from .python_builtins import all return all(var1.value() <= var2.value() for var1, var2 in all_pairs(self.args)) + class Decreasing(GlobalConstraint): """ The "Decreasing" constraint, the expressions will have decreasing (not strictly) values @@ -557,6 +558,49 @@ def value(self): from .python_builtins import all return all(var1.value() >= var2.value() for var1, var2 in all_pairs(self.args)) + +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 + """ + return [var1 < var2 for var1, var2 in all_pairs(self.args)], [] + + def value(self): + from .python_builtins import all + return all(var1.value() < var2.value() for var1, var2 in all_pairs(self.args)) + + +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 + """ + return [var1 > var2 for var1, var2 in all_pairs(self.args)], [] + + def value(self): + from .python_builtins import all + return all(var1.value() > var2.value() for var1, var2 in all_pairs(self.args)) + + class DirectConstraint(Expression): """ A DirectConstraint will directly call a function of the underlying solver when added to a CPMpy solver From 4d32445aeaaac059c33082b8f9dea63334af21c6 Mon Sep 17 00:00:00 2001 From: Dimos Tsouros Date: Fri, 3 May 2024 14:12:55 +0200 Subject: [PATCH 4/9] update tests --- tests/test_constraints.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_constraints.py b/tests/test_constraints.py index edc903644..53a7a2669 100644 --- a/tests/test_constraints.py +++ b/tests/test_constraints.py @@ -14,8 +14,8 @@ EXCLUDE_GLOBAL = {"ortools": {}, "gurobi": {}, "minizinc": {"circuit"}, - "pysat": {"circuit", "element","min","max","count", "nvalue", "allequal","alldifferent","cumulative","increasing","decreasing"}, - "pysdd": {"circuit", "element","min","max","count", "nvalue", "allequal","alldifferent","cumulative","xor","increasing","decreasing"}, + "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": {} } @@ -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, Increasing, Decreasing] + 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]: From bbede46bdb109ad091c0807305229f2129b39e04 Mon Sep 17 00:00:00 2001 From: Dimos Tsouros Date: Fri, 3 May 2024 15:53:45 +0200 Subject: [PATCH 5/9] use them in choco sovlver --- cpmpy/solvers/choco.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/cpmpy/solvers/choco.py b/cpmpy/solvers/choco.py index f161b1360..8009ba038 100644 --- a/cpmpy/solvers/choco.py +++ b/cpmpy/solvers/choco.py @@ -308,8 +308,11 @@ 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"} 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) @@ -485,7 +488,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) @@ -497,6 +500,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': From 7390f15d2930f5074fd5429a3cfdca2f1117a45f Mon Sep 17 00:00:00 2001 From: Dimos Tsouros Date: Fri, 10 May 2024 10:36:21 +0200 Subject: [PATCH 6/9] Update choco.py --- cpmpy/solvers/choco.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpmpy/solvers/choco.py b/cpmpy/solvers/choco.py index 8009ba038..7f2cc1777 100644 --- a/cpmpy/solvers/choco.py +++ b/cpmpy/solvers/choco.py @@ -313,6 +313,8 @@ def transform(self, cpm_expr): # 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) From 81a3b7fd458b2211cacb73517197e48910642230 Mon Sep 17 00:00:00 2001 From: Dimos Tsouros Date: Fri, 10 May 2024 12:05:31 +0200 Subject: [PATCH 7/9] only consecutive pairs, not all pairs --- cpmpy/expressions/globalconstraints.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/cpmpy/expressions/globalconstraints.py b/cpmpy/expressions/globalconstraints.py index bc0b8db5e..bcd2877d0 100644 --- a/cpmpy/expressions/globalconstraints.py +++ b/cpmpy/expressions/globalconstraints.py @@ -531,11 +531,13 @@ def decompose(self): 1) the decomposition of the Increasing constraint 2) empty list of defining constraints """ - return [var1 <= var2 for var1, var2 in all_pairs(self.args)], [] + args = self.args + return [(args[i] <= args[i+1]) for i in range(len(args)-1)], [] def value(self): from .python_builtins import all - return all(var1.value() <= var2.value() for var1, var2 in all_pairs(self.args)) + args = self.args + return all((args[i] <= args[i+1]) for i in range(len(args)-1)) class Decreasing(GlobalConstraint): @@ -552,11 +554,13 @@ def decompose(self): 1) the decomposition of the Decreasing constraint 2) empty list of defining constraints """ - return [var1 >= var2 for var1, var2 in all_pairs(self.args)], [] + args = self.args + return [(args[i] >= args[i+1]) for i in range(len(args)-1)], [] def value(self): from .python_builtins import all - return all(var1.value() >= var2.value() for var1, var2 in all_pairs(self.args)) + args = self.args + return all((args[i] >= args[i+1]) for i in range(len(args)-1)) class IncreasingStrict(GlobalConstraint): @@ -573,11 +577,13 @@ def decompose(self): 1) the decomposition of the IncreasingStrict constraint 2) empty list of defining constraints """ - return [var1 < var2 for var1, var2 in all_pairs(self.args)], [] + args = self.args + return [(args[i] < args[i+1]) for i in range(len(args)-1)], [] def value(self): from .python_builtins import all - return all(var1.value() < var2.value() for var1, var2 in all_pairs(self.args)) + args = self.args + return all((args[i] < args[i+1]) for i in range(len(args)-1)) class DecreasingStrict(GlobalConstraint): @@ -594,11 +600,13 @@ def decompose(self): 1) the decomposition of the DecreasingStrict constraint 2) empty list of defining constraints """ - return [var1 > var2 for var1, var2 in all_pairs(self.args)], [] + args = self.args + return [(args[i] > args[i+1]) for i in range(len(args)-1)], [] def value(self): from .python_builtins import all - return all(var1.value() > var2.value() for var1, var2 in all_pairs(self.args)) + args = self.args + return all((args[i] > args[i+1]) for i in range(len(args)-1)) class DirectConstraint(Expression): From b38bcaef31cb799ee948427d2da5af7cd88fb902 Mon Sep 17 00:00:00 2001 From: Dimos Tsouros Date: Fri, 10 May 2024 12:12:37 +0200 Subject: [PATCH 8/9] fix .value() --- cpmpy/expressions/globalconstraints.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cpmpy/expressions/globalconstraints.py b/cpmpy/expressions/globalconstraints.py index bcd2877d0..09f02d08f 100644 --- a/cpmpy/expressions/globalconstraints.py +++ b/cpmpy/expressions/globalconstraints.py @@ -532,12 +532,12 @@ def decompose(self): 2) empty list of defining constraints """ args = self.args - return [(args[i] <= args[i+1]) for i in range(len(args)-1)], [] + 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] <= args[i+1]) for i in range(len(args)-1)) + return all(args[i].value() <= args[i+1].value() for i in range(len(args)-1)) class Decreasing(GlobalConstraint): @@ -555,12 +555,12 @@ def decompose(self): 2) empty list of defining constraints """ args = self.args - return [(args[i] >= args[i+1]) for i in range(len(args)-1)], [] + 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] >= args[i+1]) for i in range(len(args)-1)) + return all(args[i].value() >= args[i+1].value() for i in range(len(args)-1)) class IncreasingStrict(GlobalConstraint): @@ -578,12 +578,12 @@ def decompose(self): 2) empty list of defining constraints """ args = self.args - return [(args[i] < args[i+1]) for i in range(len(args)-1)], [] + 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] < args[i+1]) for i in range(len(args)-1)) + return all((args[i].value() < args[i+1].value()) for i in range(len(args)-1)) class DecreasingStrict(GlobalConstraint): @@ -606,7 +606,7 @@ def decompose(self): def value(self): from .python_builtins import all args = self.args - return all((args[i] > args[i+1]) for i in range(len(args)-1)) + return all((args[i].value() > args[i+1].value()) for i in range(len(args)-1)) class DirectConstraint(Expression): From 533e712dc1a88cc99c2bc4e3f12ee7e47fd59b60 Mon Sep 17 00:00:00 2001 From: Dimos Tsouros Date: Fri, 10 May 2024 13:14:56 +0200 Subject: [PATCH 9/9] updating init file + list of globals --- cpmpy/expressions/__init__.py | 5 +++-- cpmpy/expressions/globalconstraints.py | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/cpmpy/expressions/__init__.py b/cpmpy/expressions/__init__.py index e6859d70a..bd71085cf 100644 --- a/cpmpy/expressions/__init__.py +++ b/cpmpy/expressions/__init__.py @@ -5,13 +5,13 @@ List of submodules ================== .. autosummary:: + python_builtins :nosignatures: variables core globalconstraints globalfunctions - python_builtins utils @@ -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 diff --git a/cpmpy/expressions/globalconstraints.py b/cpmpy/expressions/globalconstraints.py index 09f02d08f..61e7965a6 100644 --- a/cpmpy/expressions/globalconstraints.py +++ b/cpmpy/expressions/globalconstraints.py @@ -104,7 +104,14 @@ def my_circuit_decomp(self): Table Xor Cumulative + IfThenElse GlobalCardinalityCount + DirectConstraint + InDomain + Increasing + Decreasing + IncreasingStrict + DecreasingStrict """ import copy