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

type-check in operator constructor #297

Merged
merged 8 commits into from
May 22, 2023
Merged

type-check in operator constructor #297

merged 8 commits into from
May 22, 2023

Conversation

Wout4
Copy link
Collaborator

@Wout4 Wout4 commented May 15, 2023

No description provided.

@Wout4 Wout4 requested a review from IgnaceBleukx May 15, 2023 12:34
Copy link
Collaborator

@IgnaceBleukx IgnaceBleukx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, will certainly simplify some edge cases.
Instead of an assert, maybe we should make a "CPMpyTypeError"? Or probably Python has a "TypeError" itself?
To me an "assert" is more a sanity check for the developers that everything works as it should.

Also, a typerror can be catched in Bigtest if any of those models create invalid constraints

cpmpy/expressions/globalconstraints.py Outdated Show resolved Hide resolved
@@ -437,6 +459,10 @@ class Element(GlobalConstraint):
"""

def __init__(self, arr, idx):
flatargs = flatlist(arr)
assert not is_boolexpr(idx), "index cannot be a boolexpression: {}".format(idx)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"boolean expression"?

@IgnaceBleukx
Copy link
Collaborator

Good to merge!

@Wout4
Copy link
Collaborator Author

Wout4 commented May 16, 2023

will add some tests for completeness :)

Wout4 added 4 commits May 19, 2023 10:48
Inverse now works when arguments are not ndvararray
added tests for the typechecks in global constraints
Copy link
Collaborator

@tias tias left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

late review, I saw it was already merged... still, would be good to improve (the current version might have a bit of an unnecessary runtime overhead)

@@ -401,6 +401,11 @@ def __init__(self, name, arg_list):
# sanity checks
assert (name in Operator.allowed), "Operator {} not allowed".format(name)
arity, is_bool = Operator.allowed[name]
if is_bool:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for being late to review...

you don't need to go into the children if each constructor checks their direct children (can have overhead on deeply nested expressions)

so smth like if is_bool: assert (all(is_boolexpr(a) for a in arg_list), "..."
or not asserts but raises? also fine...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand, just remove the flatlist?
the code is only checking the direct children, the flatlist call just handles cases like wsum were the args consist of 2 list, in stead of one. It can be removed however since there are no such cases for boolean operators

@@ -172,7 +172,10 @@ class AllDifferent(GlobalConstraint):
"""All arguments have a different (distinct) value
"""
def __init__(self, *args):
super().__init__("alldifferent", flatlist(args))
flatargs = flatlist(args)
if not (all(is_boolexpr(arg) for arg in flatargs) or not any(is_boolexpr(arg) for arg in flatargs)):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are looping twice over the args, and on the flat ones even; this surely can trigger (e.g. a boolexpr with an intexpr inside...)
is_boolarg = [is_boolexpr(a) for a in args]; and then on that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, flatlist is not calling flatten, or recursively giving back all children, it just unnests iterables and gives back a list. (since the Alldifferent constructor allows things like AllDifferent([1,2,[1,3]]) and is_ boolexpr on a list will always return false so doing [is_boolexpr(a) for a in args] would run into issues that way

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but certainly it could be more efficient than looping twice, maybe a custom function that checks the type of the first element and then checks if the other elements are of the same type?

@@ -331,8 +347,8 @@ def value(self):
# a little helper:
class IfThenElse(GlobalConstraint):
def __init__(self, condition, if_true, if_false):
assert all([is_boolexpr(condition), is_boolexpr(if_true), is_boolexpr(if_false)]), \
"only boolean expression allowed in IfThenElse"
if not all([is_boolexpr(condition), is_boolexpr(if_true), is_boolexpr(if_false)]):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't do that inside if's, its an additional function call..

should be if not is_boolexpr(cond) and is_bool...
or even better if not is_boolex(cond) or not is_boolexp(if_true)...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants