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

288 simplify boolean contstants #291

Merged
merged 31 commits into from
Jun 5, 2023
Merged

Conversation

IgnaceBleukx
Copy link
Collaborator

Implemented transformation to convert boolean constants into integers.

Related but not implemented are conversions of simple boolean expressions such as BV >= 1 to True.
It can also be done in this transformation, but then it should get a new name such as simplify_boolean or something like that.
The question is then how far do we want to go in this? In essence, we are checking if constraints posted to a solver are "satisfied by construction" and we can do this for many other expressions other than simple boolean expressions.

@IgnaceBleukx IgnaceBleukx linked an issue May 9, 2023 that may be closed by this pull request
@IgnaceBleukx IgnaceBleukx requested review from tias, Wout4 and JoD May 9, 2023 13:46
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.

for logical constraints (and, or, impl), this is currently already done in our overloadings of the python operators (at factory time).

This kind of constraint-level simplification can also be done after flatten.

I think the case we want more is the other case you are describing, where we are using a bool (const or var) as an int, in a comparison with another int, e.g. bv >= 1, bv != 0 etc.

Also, I'm not sure that your code does a full traversal of the expression tree, e.g. I dont think you walk over the args of every expressions upto their leafs (variables or constants)?

perhaps we should do the other case in a separate pull request

cpmpy/transformations/normalize.py Outdated Show resolved Hide resolved
tests/test_constraints.py Outdated Show resolved Hide resolved
@IgnaceBleukx
Copy link
Collaborator Author

Yes, I know for some operators we simpify at construction time (but for example not if you construct an Operator using its constructor directly). We yet have to converge on the question of what should be simplified at construction and what should be done later I think.

I forgot to add the case of global constraints, then it should actually traverse the entire tree line 64 and line 99

The boolean simplification will basically be a switch statement summarizing the following table:

x <0 0 1 > 1
== F ~x x F
!= T x ~x T
> T x F F
< F F ~x T
>= T T x F
<= F ~x T T

Maybe it does deserve to live in this pull request/transformation as well. I'll implement it and we can still remove it if this does not seem to be the right spot anyway.

@IgnaceBleukx
Copy link
Collaborator Author

Updated with some more tests, I think it ready to merge now.

if any(is_false_cst(arg) for arg in args):
newlist.append(1 if num_context else BoolVal(True))
else:
newlist.append(Operator("or", [arg for arg in args if not isinstance(arg, BoolVal)]))
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this would not clean a 'true' or np.true; I also think we are needlessly copying a lot of objects that do not change.

But I also don't have a solution straight away... will think about it

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 could just check for is_false_cst instead of the BoolVal here? (The is_false_cst on line 63 should be is_true_cst!) That check will also hold for np constants.

Not sure if we can do anything about the copying... Except for changing args in place but some transformation will have to copy them at least once as the users constraints should remain unchanged


elif isinstance(expr, Comparison):
lhs, rhs = simplify_boolean(expr.args, num_context=True)
if (isinstance(lhs, int) and isinstance(rhs, _BoolVarImpl)) or isinstance(lhs, BoolVal) and isinstance(rhs, _IntVarImpl):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Swap the comparison symbol too? Why swap?

Copy link
Collaborator

Choose a reason for hiding this comment

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

second case cannot happen, as BoolVal should be upcasted to int by line 97.

Copy link
Collaborator

Choose a reason for hiding this comment

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

So only case where we can do something smart is when one of the arguments is int and the other is BoolVal - Ignace.

Copy link
Collaborator

Choose a reason for hiding this comment

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

TODO: make sure comparison symbol is swapped too!

Copy link
Collaborator

Choose a reason for hiding this comment

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

TODO: note somewhere that we do not simplify comparisons between two integer expressions and between two Boolean expressions in general. We only simplify Boolean variables compared to integer values.

lhs, rhs = simplify_boolean(expr.args, num_context=True)
if (isinstance(lhs, int) and isinstance(rhs, _BoolVarImpl)) or isinstance(lhs, BoolVal) and isinstance(rhs, _IntVarImpl):
lhs, rhs = rhs, lhs
if isinstance(lhs, _BoolVarImpl) and isinstance(rhs, int):
Copy link
Collaborator

Choose a reason for hiding this comment

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

The central idea is that a comparison of a Boolean variable to an integer value can always be simplified to a simple Boolean expression. E.g., p <= 0 simplifies to ~p`.

Copy link
Collaborator

@JoD JoD 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, one issue where the comparison symbol needs to be flipped. But the general algorithm makes sense to me (now ;).

@JoD
Copy link
Collaborator

JoD commented May 22, 2023

After previous meeting: Tias suggested to not only check for Boolean variables compared to integer values, but also to Boolean expressions compared to integer values. Ignace suggested this would not be a lot of work and he would update the PR.

@IgnaceBleukx
Copy link
Collaborator Author

Adapted as Jo described above.
Also added some documentation to make it a bit more clear what is happening.
TODO still is some extra testing simplifying boolean expressions

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.

some detailed feedback

cpmpy/solvers/minizinc.py Show resolved Hide resolved
cpmpy/solvers/z3.py Show resolved Hide resolved
cpmpy/transformations/normalize.py Show resolved Hide resolved
cpmpy/transformations/normalize.py Show resolved Hide resolved
cpmpy/transformations/normalize.py Show resolved Hide resolved
cpmpy/transformations/normalize.py Outdated Show resolved Hide resolved
cpmpy/transformations/normalize.py Outdated Show resolved Hide resolved
cpmpy/transformations/normalize.py Outdated Show resolved Hide resolved
cpmpy/transformations/normalize.py Outdated Show resolved Hide resolved
cpmpy/transformations/normalize.py Outdated Show resolved Hide resolved
@JoD JoD merged commit bd404f9 into master Jun 5, 2023
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.

Simplify boolean contstants
4 participants