Skip to content

Commit

Permalink
update trans_simplify
Browse files Browse the repository at this point in the history
  • Loading branch information
Wout4 committed May 27, 2024
1 parent 63249ac commit 6d94b95
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 40 deletions.
56 changes: 17 additions & 39 deletions cpmpy/transformations/normalize.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import numpy as np

from ..expressions.core import BoolVal, Expression, Comparison, Operator
from ..expressions.utils import eval_comparison, is_false_cst, is_true_cst, is_boolexpr, is_num, is_bool
from ..expressions.utils import eval_comparison, is_false_cst, is_true_cst, is_boolexpr, is_num
from ..expressions.variables import NDVarArray
from ..exceptions import NotSupportedError
from ..expressions.globalconstraints import GlobalConstraint
Expand Down Expand Up @@ -40,18 +40,6 @@ def unravel(lst, append):
return newlist


def needs_simplify(expr):
if hasattr(expr, 'args'):
args = set()
for arg in expr.args:
if is_bool(arg):
return True # boolean constants can be simplified away
args.add(is_boolexpr(arg))
return len(args) > 1 # mixed types should be simplified -> constant ints?
else:
return False


def simplify_boolean(lst_of_expr, num_context=False):
"""
removes boolean constants from all CPMpy expressions
Expand All @@ -60,27 +48,21 @@ def simplify_boolean(lst_of_expr, num_context=False):
"""
from .negation import recurse_negation # avoid circular import
newlist = []

for expr in lst_of_expr:

if isinstance(expr, bool):
# not sure if BoolVal creation should happen here or at construction time
newlist.append(expr if num_context else BoolVal(expr))
# not sure if this should happen here or at construction time
expr = BoolVal(expr)

elif isinstance(expr, BoolVal):
if isinstance(expr, BoolVal):
newlist.append(int(expr.value()) if num_context else expr)

# Detect branch in expression tree where no boolean constants will follow
# Thomas > it causes a [... <= -(BoolVal(True))] not to be detected TODO
# elif not needs_simplify(expr): # not expr.has_nested_boolean_constants():
# newlist.append(expr)

elif isinstance(expr, Operator):
args = simplify_boolean(expr.args, num_context=not expr.is_bool())

if expr.name == "or":
if any(is_true_cst(arg) for arg in args): # expr | True -> True
newlist.append(1 if num_context else BoolVal(True)) # Why a boolval and not just True?
if any(is_true_cst(arg) for arg in args):
newlist.append(1 if num_context else BoolVal(True))
else:
filtered_args = [arg for arg in args if not isinstance(arg, BoolVal)]
if len(filtered_args):
Expand Down Expand Up @@ -152,15 +134,15 @@ def simplify_boolean(lst_of_expr, num_context=False):
if name == "==" or name == "<=":
newlist.append(recurse_negation(lhs))
if name == "<":
newlist.append(BoolVal(False))
newlist.append(0 if num_context else BoolVal(False))
if name == ">=":
newlist.append(BoolVal(True))
newlist.append(1 if num_context else BoolVal(True))
elif 0 < rhs < 1:
# a floating point value
if name == "==":
newlist.append(BoolVal(False))
newlist.append(0 if num_context else BoolVal(False))
if name == "!=":
newlist.append(BoolVal(True))
newlist.append(1 if num_context else BoolVal(True))
if name == "<" or name == "<=":
newlist.append(recurse_negation(lhs))
if name == ">" or name == ">=":
Expand All @@ -171,21 +153,17 @@ def simplify_boolean(lst_of_expr, num_context=False):
if name == "!=" or name == "<":
newlist.append(recurse_negation(lhs))
if name == ">":
newlist.append(BoolVal(False))
newlist.append(0 if num_context else BoolVal(False))
if name == "<=":
newlist.append(BoolVal(True))
newlist.append(1 if num_context else BoolVal(True))
elif rhs > 1:
newlist.append(BoolVal(name in {"!=", "<", "<="})) # all other operators evaluate to False
else:
newlist.append(eval_comparison(name, lhs, rhs))


#elif isinstance(expr, GlobalConstraint):
# TODO: how to determine boolean or numerical context?
# not sure this is needed at all... maybe in a XOR? but could damage it?
#if any(needs_simplify(a) for a in expr.args):
# expr = copy.copy(expr)
# expr.update_args(simplify_boolean(expr.args))
elif isinstance(expr, GlobalConstraint):
expr = copy.copy(expr)
expr.args = simplify_boolean(expr.args) # TODO: how to determine boolean or numerical context?
newlist.append(expr)
else: # variables/constants/direct constraints
newlist.append(expr)
return newlist
2 changes: 1 addition & 1 deletion tests/test_trans_simplify.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,6 @@ def test_nested_boolval(self):
bv = cp.boolvar(name="bv")
x = cp.intvar(0, 3, name="x")
cons = (x == 2) == (bv == 4)
self.assertEquals(str(self.transform(cons)), "[x != 2]")
self.assertEqual(str(self.transform(cons)), "[x != 2]")
self.assertTrue(cp.Model(cons).solve())

0 comments on commit 6d94b95

Please sign in to comment.