Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
Wout4 committed Oct 3, 2024
2 parents 87c9230 + 085d919 commit 2a304a0
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 4 deletions.
2 changes: 1 addition & 1 deletion cpmpy/expressions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@
from .globalconstraints import alldifferent, allequal, circuit # Old, to be deprecated
from .globalfunctions import Maximum, Minimum, Abs, Element, Count, NValue, NValueExcept, Among
from .core import BoolVal
from .python_builtins import all, any, max, min, sum
from .python_builtins import all, any, max, min, sum, abs
27 changes: 24 additions & 3 deletions cpmpy/expressions/python_builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
max
min
sum
abs
"""
import builtins # to use the original Python-builtins

from .utils import is_false_cst, is_true_cst
from .variables import NDVarArray
from .utils import is_false_cst, is_true_cst, is_any_list
from .variables import NDVarArray, cpm_array
from .core import Expression, Operator
from .globalfunctions import Minimum, Maximum
from .globalfunctions import Minimum, Maximum, Abs
from ..exceptions import CPMpyException


# Overwriting all/any python built-ins
Expand Down Expand Up @@ -125,3 +127,22 @@ def sum(*iterable, **kwargs):

assert len(kwargs)==0, "sum over decision variables does not support keyword arguments"
return Operator("sum", iterable)


def abs(element):
"""
abs() overwrites the python built-in to support decision variables.
if the element given is not a CPMpy expression, the built-in is called
else an Absolute functional global constraint is constructed.
"""
if is_any_list(element): # compat: not allowed by builtins.abs(), but allowed by numpy.abs()
return cpm_array([abs(elem) for elem in element])

if isinstance(element, Expression):
# create global
return Abs(element)

return builtins.abs(element)


47 changes: 47 additions & 0 deletions tests/test_builtins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import unittest

import cpmpy as cp
from cpmpy.exceptions import CPMpyException

iv = cp.intvar(-8, 8, shape=5)


class TestBuiltin(unittest.TestCase):

def test_max(self):
constraints = [cp.max(iv) + 9 <= 8]
model = cp.Model(constraints)
self.assertTrue(model.solve())
self.assertTrue(cp.max(iv.value()) <= -1)

model = cp.Model(cp.max(iv).decompose_comparison('!=', 4))
self.assertTrue(model.solve())
self.assertNotEqual(str(cp.max(iv.value())), '4')

def test_min(self):
constraints = [cp.min(iv) + 9 == 8]
model = cp.Model(constraints)
self.assertTrue(model.solve())
self.assertEqual(str(cp.min(iv.value())), '-1')

model = cp.Model(cp.min(iv).decompose_comparison('==', 4))
self.assertTrue(model.solve())
self.assertEqual(str(cp.min(iv.value())), '4')

def test_abs(self):
constraints = [cp.abs(iv[0]) + 9 <= 8]
model = cp.Model(constraints)
self.assertFalse(model.solve())

#with list
constraints = [cp.abs(iv+2) <= 8, iv < 0]
model = cp.Model(constraints)
self.assertTrue(model.solve())

constraints = [cp.abs([iv[0], iv[2], iv[1], -8]) <= 8, iv < 0]
model = cp.Model(constraints)
self.assertTrue(model.solve())

model = cp.Model(cp.abs(iv[0]).decompose_comparison('!=', 4))
self.assertTrue(model.solve())
self.assertNotEqual(str(cp.abs(iv[0].value())), '4')

0 comments on commit 2a304a0

Please sign in to comment.