Return NotImplemented for Expr and GenExpr operators to simplify#1182
Open
Zeroto521 wants to merge 63 commits intoscipopt:masterfrom
Open
Return NotImplemented for Expr and GenExpr operators to simplify#1182Zeroto521 wants to merge 63 commits intoscipopt:masterfrom
NotImplemented for Expr and GenExpr operators to simplify#1182Zeroto521 wants to merge 63 commits intoscipopt:masterfrom
Conversation
Improves the _expr_richcmp function by using explicit type checks, handling numpy arrays and numbers more robustly, and leveraging Python C API comparison constants. This refactor enhances error handling and code readability, and ensures unsupported types raise clear exceptions.
Replaces repeated isinstance checks for numeric types with a shared NUMBER_TYPES tuple. This improves maintainability and consistency in type checking within _expr_richcmp and related code.
Modified __mul__ and __add__ methods in Expr and GenExpr classes to return NotImplemented when the operand is not an instance of EXPR_OP_TYPES, improving type safety and operator behavior.
Replaces the explicit type tuple with EXPR_OP_TYPES in the type check for 'other' in _expr_richcmp, raising TypeError for unsupported types. This improves maintainability and consistency in type validation.
Enhanced type validation in expression operator overloads by returning NotImplemented for unsupported types and raising TypeError in buildGenExprObj for invalid inputs. Removed special handling for numpy arrays and simplified code paths for better maintainability and clearer error reporting.
Simplified the implementation of __truediv__ and __rtruediv__ by directly using the division operator instead of calling __truediv__ explicitly on generated expression objects.
float(Expr) can't return True
Replaces manual timing with the timeit module for more accurate performance measurement in matrix operation tests. Updates assertions to require the optimized implementation to be at least 25% faster, and reduces test parameterization to n=100 for consistency.
Replaces random matrix generation with a stacked matrix of zeros and ones in test_matrix_dot_performance to provide more controlled test data.
Replaces the custom _is_number function with isinstance checks against NUMBER_TYPES for improved clarity and consistency in type checking throughout expression operations.
Simplifies type checking and arithmetic operations in Expr and GenExpr classes by removing unnecessary float conversions and reordering type checks. Also improves error handling for exponentiation and constraint comparisons.
Documented that Expr and GenExpr now return NotImplemented when they cannot handle other types in calculations.
Replaces EXPR_OP_TYPES with GENEXPR_OP_TYPES in GenExpr and related functions to distinguish between Expr and GenExpr operations. Removes special handling for GenExpr in Expr arithmetic methods, simplifying type logic and improving consistency.
Clarified the changelog note to specify that NotImplemented is returned for Expr and GenExpr operators when they can't handle input types in calculations.
Corrects error messages in Expr and GenExpr exponentiation to display the correct variable. Removes an unnecessary assertion in Model and replaces a call to _is_number with isinstance for type checking in readStatistics.
Replaces EXPR_OP_TYPES with GENEXPR_OP_TYPES in the type check to ensure correct type validation in the _expr_richcmp method.
Zeroto521
commented
Jan 31, 2026
Zeroto521
commented
Jan 31, 2026
Casts the exponent base to float in GenExpr's __pow__ method to prevent type errors when reformulating expressions using log and exp.
NotImplemented for Expr and GenExpr operatorsNotImplemented for Expr and GenExpr operators to simplify
Explicitly converts 'other' to float in the exponentiation method to avoid type errors when using non-float numeric types.
The _is_number symbol was removed from the list of incomplete stubs in scip.pyi, likely because it is no longer needed or has been implemented.
Ensures that multiplication of Expr by numeric types consistently uses float conversion, preventing potential type errors when multiplying terms.
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Member
|
After addressing Copilot's comments, this can be merged. |
Modify tests/test_expr.py to better cover comparison directions: change a genexpr >= '1' assertion to '1' >= genexpr to test the reflected comparison, and add a new assertion that genexpr <= '1' raises TypeError. Ensures both operand orders for <=/>= with a genexpr and a string are validated to raise TypeError.
Replace the previous inline PyNumber_Check heuristic with explicit runtime type inspection: obtain the object's Py_TYPE, treat builtin int/float as numbers, explicitly reject numpy arrays, Expr/GenExpr and Python lists/tuples, and fall back to PyNumber_Check otherwise. Also remove the inline qualifier. This tightens number detection to avoid misclassifying arrays or expression objects as numeric values.
In src/pyscipopt/expr.pxi, replace the explicit Cython cast `1.0 / <double>other * self` with `1.0 / other * self`. This removes the redundant C-level cast when dividing by a numeric Python object, simplifying the code and avoiding potential type/casting issues while preserving the intended behavior.
auto-merge was automatically disabled
April 5, 2026 02:07
Head branch was pushed to by a user without write access
Add an explicit type check in Model to ensure the provided expr is either an Expr or a numeric value before coercing it via Expr() + expr. If the value is neither, raise a TypeError with an informative message. This prevents silent or invalid coercions and improves error diagnostics when callers pass unsupported types.
Replace a bare except in src/pyscipopt/scip.pxi (inside readStatistics) with except (ValueError, TypeError) to avoid unintentionally catching unrelated exceptions (e.g. KeyboardInterrupt/SystemExit) while still handling float conversion failures for statistic values.
Replace Python-level type comparisons with C API checks: use PyLong_Check and PyFloat_Check to detect ints/floats instead of comparing Py_TYPE(o) to int/float. Also switch the numpy cimport to 'cimport numpy as cnp' and use cnp.PyArray_Check. Add the necessary cimports for PyFloat_Check and PyLong_Check and simplify the _is_number implementation for correctness and performance.
Generalize expression-type handling by replacing explicit (Expr, GenExpr) checks with the common ExprLike base in _is_number, ensuring all expression-like objects are treated consistently. Also update the _expr_richcmp function signature to accept ExprLike self for proper typing in Cython. Minor whitespace cleanup in the Expr class definition.
Add the 'inline' qualifier to the Cython helper _is_number in src/pyscipopt/expr.pxi to allow the compiler to inline it and reduce call overhead for frequent numeric/type checks. No behavioral changes intended.
Zeroto521
commented
Apr 6, 2026
Member
|
@Zeroto521 can you please resolve the conflicts so I merge this? |
Contributor
Author
@Joao-Dionisio, it will raise an error if I merge the master branch directly first. See details here: #1182 (comment) |
Member
|
#1203 has been merged. |
Update test assertions to match the new ordering of arguments in the string representation for np.power. The product argument order now places log(2.0) before the sum(...) term, so assertions for np.power(2, x) and np.power(a, x) were updated accordingly.
Replace strict type(a) is np.ndarray with isinstance(a, np.ndarray) in ExprLike.__call__ (src/pyscipopt/expr.pxi). This allows numpy ndarray subclasses (e.g., masked arrays or custom ndarray types) to be recognized as arrays while preserving the existing dtype kind validation and overall behavior.
Convert np.generic arguments to native Python types (using .item()) before ufunc dispatch in ExprLike. This prevents recursive __array_ufunc__ calls between np.generic and MatrixExpr (e.g., np.generic + MatrixExpr), ensuring the operation dispatches correctly.
Zeroto521
commented
Apr 24, 2026
| ### Added | ||
| ### Fixed | ||
| ### Changed | ||
| - Return NotImplemented for `Expr` and `GenExpr` operators, if they can't handle input types in the calculation |
Contributor
Author
There was a problem hiding this comment.
Should this PR be in 6.2.0 or an unreleased version?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Python prefers to use
NotImplementedto handle the unknown input types. It's clear.Here is an example.
A.__add__can only receiveinttype. Buta + bcan work. When Python callsA.__add__(B), it returnsNotImplemented. Then Python will callB.__radd__(A). If both of them areNotImplemented, Python will raise aTypeError.So we use
NotImplementedto simplifyExpr,GenExpr, andMatrixExpr.