Skip to content

Commit

Permalink
python_builtins: follow python signature
Browse files Browse the repository at this point in the history
our 'min' and 'max' didn't allow multiple arguments, while Python's
does

in general we were hesitating whether our min/max/sum should mimic
Python's or NumPys. Now it was a mix in the middle... we were somewhat
like Python but did np.max() inside (same for sum, messing up the return
type); and we didnt yet get to implementing 'axis' like numpy either.

With this pull request, I propose that the least confusing thing for
people that do `from cpmpy import *` is to not break any existing
python code... that is, follow the Python signature of min/max/sum
(and fall back to that built-in when no decision variables are given).
  • Loading branch information
tias committed Jul 16, 2024
1 parent 0cf0618 commit 5bd8a6e
Showing 1 changed file with 32 additions and 16 deletions.
48 changes: 32 additions & 16 deletions cpmpy/expressions/python_builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
min
sum
"""
import numpy as np
import builtins # to use the original Python-builtins

from .utils import is_false_cst, is_true_cst
Expand Down Expand Up @@ -70,41 +69,58 @@ def any(iterable):
elif isinstance(elem, Expression) and elem.is_bool():
collect.append(elem)
else:
raise Exception("Non-Boolean argument '{}' to 'all'".format(elem))
raise Exception("Non-Boolean argument '{}' to 'any'".format(elem))
if len(collect) == 1:
return collect[0]
if len(collect) >= 2:
return Operator("or", collect)
return False


def max(iterable):
def max(*iterable, **kwargs):
"""
max() overwrites python built-in,
checks if all constants and computes np.max() in that case
max() overwrites the python built-in to support decision variables.
if iterable does not contain CPMpy expressions, the built-in is called
else a Maximum functional global constraint is constructed; no keyword
arguments are supported in that case
"""
if len(iterable) == 1:
iterable = tuple(iterable[0])
if not builtins.any(isinstance(elem, Expression) for elem in iterable):
return np.max(iterable)
return builtins.max(*iterable, **kwargs)

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


def min(iterable):
def min(*iterable, **kwargs):
"""
min() overwrites python built-in,
checks if all constants and computes np.min() in that case
min() overwrites the python built-in to support decision variables.
if iterable does not contain CPMpy expressions, the built-in is called
else a Minimum functional global constraint is constructed; no keyword
arguments are supported in that case
"""
if len(iterable) == 1:
iterable = tuple(iterable[0])
if not builtins.any(isinstance(elem, Expression) for elem in iterable):
return np.min(iterable)
return builtins.min(*iterable, **kwargs)

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


def sum(iterable):
def sum(iterable, **kwargs):
"""
sum() overwrites python built-in,
checks if all constants and computes np.sum() in that case
otherwise, makes a sum Operator directly on `iterable`
sum() overwrites the python built-in to support decision variables.
if iterable does not contain CPMpy expressions, the built-in is called
checks if all constants and uses built-in sum() in that case
"""
iterable = list(iterable) # Fix generator polling
iterable = tuple(iterable) # Fix generator polling
if not builtins.any(isinstance(elem, Expression) for elem in iterable):
return np.sum(iterable)
return builtins.sum(iterable, **kwargs)

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

0 comments on commit 5bd8a6e

Please sign in to comment.