-
Notifications
You must be signed in to change notification settings - Fork 271
BUG: MatrixExpr can't be compared with Expr #1069
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
base: master
Are you sure you want to change the base?
Conversation
Updated type hints and isinstance checks in MatrixExpr comparison methods to use Expr instead of Variable. This change improves compatibility with broader expression types in matrix operations.
Updated type hints and isinstance checks in MatrixExprCons.__le__ and __ge__ methods to use Expr instead of Variable. This change improves consistency with the expected types for matrix expression constraints.
Introduces test_ranged_matrix_cons to verify correct behavior when adding a ranged matrix constraint to the model. Ensures that the matrix variable x is set to ones as expected.
Introduced a shared _matrixexpr_richcmp helper to handle rich comparison logic for MatrixExpr and MatrixExprCons, reducing code duplication and improving maintainability. Updated __le__, __ge__, and __eq__ methods to use this helper, and removed redundant code.
The __eq__ method of MatrixExprCons now raises NotImplementedError with a descriptive message instead of TypeError, clarifying that '==' comparison is not supported.
Added tests for '<=', '>=', and '==' operators in matrix constraints. Verified correct exception is raised for unsupported '==' operator.
Relocated the _is_number utility from expr.pxi to matrix.pxi for better modularity. Updated _matrixexpr_richcmp to use a local _richcmp helper for comparison operations.
Replaces usage of undefined 'shape' variable with 'self.shape' when creating the result array in _matrixexpr_richcmp, ensuring correct array dimensions.
def _is_number(e): | ||
try: | ||
f = float(e) | ||
return True | ||
except ValueError: # for malformed strings | ||
return False | ||
except TypeError: # for other types (Variable, Expr) | ||
return False | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove duplicated parts. It also appears in matrix.pxi
def _richcmp(self, other, op): | ||
if op == 1: # <= | ||
return self.__le__(other) | ||
elif op == 5: # >= | ||
return self.__ge__(other) | ||
elif op == 2: # == | ||
return self.__eq__(other) | ||
else: | ||
raise NotImplementedError("Can only support constraints with '<=', '>=', or '=='.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't use expr.pxi/_expr_richcmp
. It will cause circular imports
Removed unnecessary double .all() calls in assertions for matrix variable tests, simplifying the checks for equality with np.ones(3).
Updated assertions in test_matrix_variable.py to use m.getVal(x) and m.getVal(y) instead of direct variable comparison. This ensures the tests check the evaluated values from the model rather than the symbolic variables.
if not _is_number(other) or not isinstance(other, MatrixExpr): | ||
raise TypeError('Ranged MatrixExprCons is not well defined!') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This checking is duplicated to _expr_richcmp
Can't add with ExprCons
expr_cons_matrix[idx] = self[idx] >= other[idx] | ||
else: | ||
raise TypeError(f"Unsupported type {type(other)}") | ||
def __le__(self, other: Union[float, int, np.ndarray]) -> MatrixExprCons: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ranged ExprCons can only support numbers.
So MatrixExprCons.__ge__
can only receive a number type other
.
PySCIPOpt/src/pyscipopt/expr.pxi
Lines 334 to 356 in c681f94
def __richcmp__(self, other, op): | |
'''turn it into a constraint''' | |
if op == 1: # <= | |
if not self._rhs is None: | |
raise TypeError('ExprCons already has upper bound') | |
assert not self._lhs is None | |
if not _is_number(other): | |
raise TypeError('Ranged ExprCons is not well defined!') | |
return ExprCons(self.expr, lhs=self._lhs, rhs=float(other)) | |
elif op == 5: # >= | |
if not self._lhs is None: | |
raise TypeError('ExprCons already has lower bound') | |
assert self._lhs is None | |
assert not self._rhs is None | |
if not _is_number(other): | |
raise TypeError('Ranged ExprCons is not well defined!') | |
return ExprCons(self.expr, lhs=float(other), rhs=self._rhs) | |
else: | |
raise TypeError |
A toy demo to show that
from pyscipopt import Model
m = Model()
x = m.addVar(vtype="B", ub=0)
y = m.addVar(vtype="B", ub=0)
# (x <= 1) >= y # left is (x <= 1) (ExprCons), right is y (Variable)
# Traceback (most recent call last):
# line 6, in <module>
# (x <= 1) >= y
# File "src/pyscipopt/expr.pxi", line 352, in pyscipopt.scip.ExprCons.__richcmp__
# TypeError: Ranged ExprCons is not well defined!
y <= (x <= 1) # left is y (Variable), right is (x <= 1) (ExprCons)
# Traceback (most recent call last):
# line 12, in <module>
# y <= (x <= 1)
# File "src/pyscipopt/expr.pxi", line 287, in pyscipopt.scip.Expr.__richcmp__
# File "src/pyscipopt/expr.pxi", line 65, in pyscipopt.scip._expr_richcmp
# NotImplementedError
if not self._rhs is None: | ||
raise TypeError('ExprCons already has upper bound') | ||
assert not self._lhs is None | ||
if not self._rhs is None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use 4 spaces as the indent
This is great @Zeroto521, I just hit the same issue so thanks a lot for working on this! |
|
to fix #1061