Open hakank opened 2 years ago
Hi Hakan,
After some digging I found out this line is what causing the trouble: model += ((s_sum % 2) == (d % 2))
.
In particular, the left hand side gets evaluated to a numpy int64
object.
Because the numpy int is on the left hand side of the comparison, the __eq__
method of numpy gets called instead of our overloaded methods. As such, the expression evaluates to np._True
instead of CPMpy expression.
To fix this, you can simply swap the order of the arguments so the CPMpy expression comes first, or cast the left hand side to a Python-native int.
Regarding the line you mentioned, these are the transformations we invoke when solving using OR-Tools.
Kind regards, Ignace
Thanks for identifying the problem, Ignace.
For picking_teams.py and partition_into_subsets_of_equal_values.py it was solved by either of your methods (move constant to RHS or cast to int).
For some reason, bus_scheduling_csplib.py works when updating to master version instead of v0.9.8.
However, for linear_combinations.py I could not find the culprit. Also, it now (in master version) throws another error TypeError: The numpy boolean negative, the
-operator, is not supported, use the
~operator or the logical_not function instead.
. I'll check this more and report a new issue if I cannot fix the problem.
And now I found the problem in linear_combinations.py, It was this constraint
# ...
for i in range(1,n1):
for j in range(i+1,n1):
model += [ (x[i] + x[j] == 2) <= (sum([d==abs(i-j) for d in ds]) > 0) ]
It was fixed by casting the sum constraint to an int:
model += [ (x[i] + x[j] == 2) <= sum([d==abs(i-j) for d in ds]) > 0) ]
I hope that you regard this behavior as a bug, since having to cast (some) constants or expressions to ints (or move to RHS) is definitely a gotcha, especially since the error messages are not very clear.
Smaller example:
1 == d % 2 # Out: (d) mod 2 == 1
np.int_(1) == d % 2 # Out: True
d % 2 == np.int_(1) # Out: (d) mod 2 == 1
In case of a python int, it gives precedence to the `eq' of the CPMpy expression.
But in case of numpy arrays, both are 'custom' objects so the first one (of numpy) is chosen...
But is it a caveat or a bug... the latter would mean there exists a way to fix it, and that I don't know yet.
It seems that numpy actually has a 'one-place only hierarchy' for handing __eq__
and other comparisons/operators, namely ufuncs:
https://numpy.org/doc/stable/reference/arrays.classes.html#numpy.class.__array_ufunc__
I do not fully grasp all of it yet, but it seems their might be a fix for this issue, perhaps if we have CPMpy expressions subclass NDArrayOperatorsMixin
and implement our intended behaviour through ufuncs.
That page also suggests that for our cpmpy arrays (that subclass ndarray) we should perhaps not overwrite __eq__
but use this array-ufunc funk too.
This is to be looked at more properly at some point.
It is definitely not a feature.
The following old v0.9.7 model throws
AttributeError: 'numpy.bool_' object has no attribute 'is_bool')
when running in v9.7.8. (It's my model http://hakank.org/cpmpy/picking_teams.py )The output running the model:
Here are some other models that throws the same error:
http://hakank.org/cpmpy/bus_scheduling_csplib.py (data files in http://hakank.org/cpmpy/bus_scheduling_csplib/)
http://hakank.org/cpmpy/linear_combinations.py
http://hakank.org/cpmpy/partition_into_subsets_of_equal_values.py
For all these models, the error involves this line
so it seems that there is some issue with the reification. Has the syntax/logic for reification been changed?