From 6874ae46dfc85851ff93501393be4cc1088bffb9 Mon Sep 17 00:00:00 2001 From: 40% Date: Mon, 6 Apr 2026 19:58:05 +0800 Subject: [PATCH 01/17] Consolidate operator overloads into ExprLike Move common operator dunder methods (__neg__, __radd__, __sub__, __rsub__, __rmul__, __richcmp__) into the base ExprLike class and remove their duplicate implementations from Expr and GenExpr. This consolidates operator behavior across expression types, forwarding rich comparisons to _expr_richcmp and reducing code duplication for negation, arithmetic and reflected operations. --- src/pyscipopt/expr.pxi | 57 +++++++++++++----------------------------- 1 file changed, 18 insertions(+), 39 deletions(-) diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi index b26b87997..5c4d15e18 100644 --- a/src/pyscipopt/expr.pxi +++ b/src/pyscipopt/expr.pxi @@ -279,6 +279,24 @@ cdef class ExprLike: def cos(self) -> GenExpr: return UnaryExpr(Operator.cos, buildGenExprObj(self)) + def __neg__(self): + return self * -1.0 + + def __radd__(self, other): + return self + other + + def __sub__(self, other): + return self + (-other) + + def __rsub__(self, other): + return (-self) + other + + def __rmul__(self, other): + return self * other + + def __richcmp__(self, other, int op): + return _expr_richcmp(self, other, op) + ##@details Polynomial expressions of variables with operator overloading. \n #See also the @ref ExprDetails "description" in the expr.pxi. @@ -411,25 +429,6 @@ cdef class Expr(ExprLike): else: raise TypeError(f"Unsupported base type {type(other)} for exponentiation.") - def __neg__(self): - return Expr({v:-c for v,c in self.terms.items()}) - - def __sub__(self, other): - return self + (-other) - - def __radd__(self, other): - return self.__add__(other) - - def __rmul__(self, other): - return self.__mul__(other) - - def __rsub__(self, other): - return -1.0 * self + other - - def __richcmp__(self, other, op): - '''turn it into a constraint''' - return _expr_richcmp(self, other, op) - def normalize(self): '''remove terms with coefficient of 0''' self.terms = {t:c for (t,c) in self.terms.items() if c != 0.0} @@ -488,7 +487,6 @@ cdef class ExprCons: if not self._rhs is None: self._rhs -= c - def __richcmp__(self, other, op): '''turn it into a constraint''' if op == 1: # <= @@ -720,25 +718,6 @@ cdef class GenExpr(ExprLike): otherexpr = buildGenExprObj(other) return otherexpr.__truediv__(self) - def __neg__(self): - return -1.0 * self - - def __sub__(self, other): - return self + (-other) - - def __radd__(self, other): - return self.__add__(other) - - def __rmul__(self, other): - return self.__mul__(other) - - def __rsub__(self, other): - return -1.0 * self + other - - def __richcmp__(self, other, op): - '''turn it into a constraint''' - return _expr_richcmp(self, other, op) - def degree(self): '''Note: none of these expressions should be polynomial''' return float('inf') From d8f2cceabf281428032fae775b861a5c52b75262 Mon Sep 17 00:00:00 2001 From: 40% Date: Mon, 6 Apr 2026 20:01:21 +0800 Subject: [PATCH 02/17] Consolidate ExprLike operator methods Move and centralize operator overloads for ExprLike: __radd__, __sub__, __rsub__, __rmul__, __neg__ and __richcmp__ are added earlier in the class and the duplicate implementations later in the file were removed. This refactor cleans up the class definition and avoids duplicated method definitions. --- src/pyscipopt/expr.pxi | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi index 5c4d15e18..4c22ebaf2 100644 --- a/src/pyscipopt/expr.pxi +++ b/src/pyscipopt/expr.pxi @@ -261,6 +261,24 @@ cdef class ExprLike: return NotImplemented + def __radd__(self, other): + return self + other + + def __sub__(self, other): + return self + (-other) + + def __rsub__(self, other): + return (-self) + other + + def __rmul__(self, other): + return self * other + + def __richcmp__(self, other, int op): + return _expr_richcmp(self, other, op) + + def __neg__(self): + return self * -1.0 + def __abs__(self) -> GenExpr: return UnaryExpr(Operator.fabs, buildGenExprObj(self)) @@ -279,24 +297,6 @@ cdef class ExprLike: def cos(self) -> GenExpr: return UnaryExpr(Operator.cos, buildGenExprObj(self)) - def __neg__(self): - return self * -1.0 - - def __radd__(self, other): - return self + other - - def __sub__(self, other): - return self + (-other) - - def __rsub__(self, other): - return (-self) + other - - def __rmul__(self, other): - return self * other - - def __richcmp__(self, other, int op): - return _expr_richcmp(self, other, op) - ##@details Polynomial expressions of variables with operator overloading. \n #See also the @ref ExprDetails "description" in the expr.pxi. From d140fb7ccbd70ea2c5a0743c8e1003a3c21b4e84 Mon Sep 17 00:00:00 2001 From: 40% Date: Mon, 6 Apr 2026 20:02:24 +0800 Subject: [PATCH 03/17] Move __rtruediv__ to ExprLike base class Consolidate reflected true-division handling by adding __rtruediv__ to the ExprLike base class and removing duplicate implementations from Expr and GenExpr. The reflected division now uniformly uses buildGenExprObj(other) / self, reducing code duplication and ensuring consistent behavior across expression types. --- src/pyscipopt/expr.pxi | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi index 4c22ebaf2..e9ff02f42 100644 --- a/src/pyscipopt/expr.pxi +++ b/src/pyscipopt/expr.pxi @@ -273,6 +273,9 @@ cdef class ExprLike: def __rmul__(self, other): return self * other + def __rtruediv__(self, other): + return buildGenExprObj(other) / self + def __richcmp__(self, other, int op): return _expr_richcmp(self, other, op) @@ -401,10 +404,6 @@ cdef class Expr(ExprLike): selfexpr = buildGenExprObj(self) return selfexpr.__truediv__(other) - def __rtruediv__(self, other): - ''' other / self ''' - return buildGenExprObj(other) / self - def __pow__(self, other, modulo): if float(other).is_integer() and other >= 0: exp = int(other) @@ -713,11 +712,6 @@ cdef class GenExpr(ExprLike): raise ZeroDivisionError("cannot divide by 0") return self * divisor**(-1) - def __rtruediv__(self, other): - ''' other / self ''' - otherexpr = buildGenExprObj(other) - return otherexpr.__truediv__(self) - def degree(self): '''Note: none of these expressions should be polynomial''' return float('inf') From 69989448fdd39edb02a10e7f08b42d95f47e64e7 Mon Sep 17 00:00:00 2001 From: 40% Date: Mon, 6 Apr 2026 20:05:21 +0800 Subject: [PATCH 04/17] Update changelog: move magic methods to ExprLike Document a refactor that moves several dunder methods (__radd__, __sub__, __rsub__, __rmul__, __richcmp__, __neg__, __rtruediv__) into the ExprLike base class to centralize operator behavior. This change only updates CHANGELOG.md to record the API/internal restructuring. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9e1944cf..5ad4a3255 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ### Changed - Speed up `constant * Expr` via C-level API - Speed up `Term.__eq__` via the C-level API +- Move `__radd__`, `__sub__`, `__rsub__`, `__rmul__`, `__richcmp__`, `__neg__`, and `__rtruediv__` to `ExprLike` base class ### Removed - Removed outdated warning about Make build system incompatibility - Removed `Term.ptrtuple` to optimize `Term` memory usage From 9d2d2512795c7b730d58e93ca2b9b53b86ebe5d1 Mon Sep 17 00:00:00 2001 From: 40% Date: Mon, 6 Apr 2026 20:05:53 +0800 Subject: [PATCH 05/17] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ad4a3255..a8fcdcf62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ ### Changed - Speed up `constant * Expr` via C-level API - Speed up `Term.__eq__` via the C-level API -- Move `__radd__`, `__sub__`, `__rsub__`, `__rmul__`, `__richcmp__`, `__neg__`, and `__rtruediv__` to `ExprLike` base class +- Move magic methods (`__radd__`, `__sub__`, `__rsub__`, `__rmul__`, `__richcmp__`, `__neg__`, and `__rtruediv__`) to `ExprLike` base class ### Removed - Removed outdated warning about Make build system incompatibility - Removed `Term.ptrtuple` to optimize `Term` memory usage From 043b032af450be6fbcdd60b873b00636eb0b0e5b Mon Sep 17 00:00:00 2001 From: 40% Date: Tue, 7 Apr 2026 20:49:12 +0800 Subject: [PATCH 06/17] Mark reflected dunder methods positional-only Add the positional-only marker ('/') to several operator method signatures in ExprLike to prevent passing the operand as a keyword and to align with CPython semantics. Affected methods: __radd__, __sub__, __rsub__, __rmul__, and __rtruediv__. This is a signature-level change only and should not alter runtime behavior. --- src/pyscipopt/expr.pxi | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi index e9ff02f42..83ef4b5c9 100644 --- a/src/pyscipopt/expr.pxi +++ b/src/pyscipopt/expr.pxi @@ -261,19 +261,19 @@ cdef class ExprLike: return NotImplemented - def __radd__(self, other): + def __radd__(self, other, /): return self + other - def __sub__(self, other): + def __sub__(self, other, /): return self + (-other) - def __rsub__(self, other): + def __rsub__(self, other, /): return (-self) + other - def __rmul__(self, other): + def __rmul__(self, other, /): return self * other - def __rtruediv__(self, other): + def __rtruediv__(self, other, /): return buildGenExprObj(other) / self def __richcmp__(self, other, int op): From 11d18017c72b773ab462e5013a7bf4ae0452e638 Mon Sep 17 00:00:00 2001 From: 40% Date: Tue, 7 Apr 2026 20:54:21 +0800 Subject: [PATCH 07/17] Consolidate arithmetic dunders into ExprLike Update src/pyscipopt/scip.pyi: add missing arithmetic/operator dunder declarations (__radd__, __sub__, __rsub__, __rmul__, __rtruediv__, __neg__) to the ExprLike base stub and remove redundant/operator declarations from Expr and GenExpr. This consolidates common operator signatures in the base protocol, reduces duplication, and improves typing/stub consistency. --- src/pyscipopt/scip.pyi | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index 741e35ff9..2db1eba6c 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -333,6 +333,12 @@ class ExprLike: *args: Incomplete, **kwargs: Incomplete, ) -> Incomplete: ... + def __radd__(self, other: Incomplete, /) -> Incomplete: ... + def __sub__(self, other: Incomplete, /) -> Incomplete: ... + def __rsub__(self, other: Incomplete, /) -> Incomplete: ... + def __rmul__(self, other: Incomplete, /) -> Incomplete: ... + def __rtruediv__(self, other: Incomplete, /) -> Incomplete: ... + def __neg__(self) -> Incomplete: ... def __abs__(self) -> GenExpr: ... def exp(self) -> GenExpr: ... def log(self) -> GenExpr: ... @@ -340,13 +346,13 @@ class ExprLike: def sin(self) -> GenExpr: ... def cos(self) -> GenExpr: ... + @disjoint_base class Expr(ExprLike): terms: Incomplete def __init__(self, terms: Incomplete = ...) -> None: ... def degree(self) -> Incomplete: ... def normalize(self) -> Incomplete: ... - def __abs__(self) -> GenExpr: ... def __add__(self, other: Incomplete, /) -> Incomplete: ... def __eq__(self, other: object, /) -> bool: ... def __ge__(self, other: object, /) -> bool: ... @@ -358,14 +364,8 @@ class Expr(ExprLike): def __lt__(self, other: object, /) -> bool: ... def __mul__(self, other: Incomplete, /) -> Incomplete: ... def __ne__(self, other: object, /) -> bool: ... - def __neg__(self) -> Incomplete: ... def __pow__(self, other: Incomplete, modulo: Incomplete = ..., /) -> Incomplete: ... - def __radd__(self, other: Incomplete, /) -> Incomplete: ... - def __rmul__(self, other: Incomplete, /) -> Incomplete: ... def __rpow__(self, other: Incomplete, /) -> Incomplete: ... - def __rsub__(self, other: Incomplete, /) -> Incomplete: ... - def __rtruediv__(self, other: Incomplete, /) -> Incomplete: ... - def __sub__(self, other: Incomplete, /) -> Incomplete: ... def __truediv__(self, other: Incomplete, /) -> Incomplete: ... @disjoint_base @@ -392,7 +392,6 @@ class GenExpr(ExprLike): def __init__(self) -> None: ... def degree(self) -> Incomplete: ... def getOp(self) -> Incomplete: ... - def __abs__(self) -> GenExpr: ... def __add__(self, other: Incomplete, /) -> Incomplete: ... def __eq__(self, other: object, /) -> bool: ... def __ge__(self, other: object, /) -> bool: ... @@ -401,14 +400,8 @@ class GenExpr(ExprLike): def __lt__(self, other: object, /) -> bool: ... def __mul__(self, other: Incomplete, /) -> Incomplete: ... def __ne__(self, other: object, /) -> bool: ... - def __neg__(self) -> Incomplete: ... def __pow__(self, other: Incomplete, modulo: Incomplete = ..., /) -> Incomplete: ... - def __radd__(self, other: Incomplete, /) -> Incomplete: ... - def __rmul__(self, other: Incomplete, /) -> Incomplete: ... def __rpow__(self, other: Incomplete, /) -> Incomplete: ... - def __rsub__(self, other: Incomplete, /) -> Incomplete: ... - def __rtruediv__(self, other: Incomplete, /) -> Incomplete: ... - def __sub__(self, other: Incomplete, /) -> Incomplete: ... def __truediv__(self, other: Incomplete, /) -> Incomplete: ... @disjoint_base From b814b400c68c409cea3e3cd84046b01b883530e1 Mon Sep 17 00:00:00 2001 From: 40% Date: Tue, 7 Apr 2026 20:54:39 +0800 Subject: [PATCH 08/17] Update CHANGELOG.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8fcdcf62..dcf1aa014 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ ### Changed - Speed up `constant * Expr` via C-level API - Speed up `Term.__eq__` via the C-level API -- Move magic methods (`__radd__`, `__sub__`, `__rsub__`, `__rmul__`, `__richcmp__`, `__neg__`, and `__rtruediv__`) to `ExprLike` base class +- Move magic methods (`__radd__`, `__sub__`, `__rsub__`, `__rmul__`, `__richcmp__`, `__neg__`, and `__rtruediv__`) to `ExprLike` base class ### Removed - Removed outdated warning about Make build system incompatibility - Removed `Term.ptrtuple` to optimize `Term` memory usage From c7f6715b725519fc97e41be14cf4f3d2d260b7b5 Mon Sep 17 00:00:00 2001 From: 40% Date: Tue, 7 Apr 2026 21:03:35 +0800 Subject: [PATCH 09/17] style: format scip.pyi with ruff Delete an extraneous blank line between the end of ExprLike and the @disjoint_base decorator/Expr class in src/pyscipopt/scip.pyi to tidy file formatting. No functional changes. --- src/pyscipopt/scip.pyi | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index 2db1eba6c..c46527a62 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -346,7 +346,6 @@ class ExprLike: def sin(self) -> GenExpr: ... def cos(self) -> GenExpr: ... - @disjoint_base class Expr(ExprLike): terms: Incomplete From 7dbf94454607ba69322e3a1ac5bc8c66017013cb Mon Sep 17 00:00:00 2001 From: 40% Date: Wed, 20 May 2026 22:21:58 +0800 Subject: [PATCH 10/17] Refine operator type hints in scip.pyi Replace Incomplete with object for several ExprLike operator stubs (__radd__, __sub__, __rsub__, __rmul__, __rtruediv__, __neg__) to relax/standardize return and operand typing. Also add missing __rtruediv__ stubs to Expr and GenExpr. These changes improve the type stubs for static type checkers and better reflect Python operator semantics. --- src/pyscipopt/scip.pyi | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index 216b1cd4f..a08b498b9 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -331,12 +331,12 @@ class ExprLike: *args: Incomplete, **kwargs: Incomplete, ) -> Incomplete: ... - def __radd__(self, other: Incomplete, /) -> Incomplete: ... - def __sub__(self, other: Incomplete, /) -> Incomplete: ... - def __rsub__(self, other: Incomplete, /) -> Incomplete: ... - def __rmul__(self, other: Incomplete, /) -> Incomplete: ... - def __rtruediv__(self, other: Incomplete, /) -> Incomplete: ... - def __neg__(self) -> Incomplete: ... + def __radd__(self, other: object, /) -> object: ... + def __sub__(self, other: object, /) -> object: ... + def __rsub__(self, other: object, /) -> object: ... + def __rmul__(self, other: object, /) -> object: ... + def __rtruediv__(self, other: object, /) -> object: ... + def __neg__(self) -> object: ... def __abs__(self) -> GenExpr: ... def exp(self) -> GenExpr: ... def log(self) -> GenExpr: ... @@ -364,6 +364,7 @@ class Expr(ExprLike): def __pow__(self, other: Incomplete, modulo: Incomplete = ..., /) -> Incomplete: ... def __rpow__(self, other: Incomplete, /) -> Incomplete: ... def __truediv__(self, other: Incomplete, /) -> Incomplete: ... + def __rtruediv__(self, other: object, /) -> object: ... @disjoint_base class ExprCons: @@ -400,6 +401,7 @@ class GenExpr(ExprLike): def __pow__(self, other: Incomplete, modulo: Incomplete = ..., /) -> Incomplete: ... def __rpow__(self, other: Incomplete, /) -> Incomplete: ... def __truediv__(self, other: Incomplete, /) -> Incomplete: ... + def __rtruediv__(self, other: object, /) -> object: ... @disjoint_base class Heur: From 8e8bf59198dc8acee05a3c1ffd39ec3240c5a5e3 Mon Sep 17 00:00:00 2001 From: 40% Date: Wed, 20 May 2026 22:24:35 +0800 Subject: [PATCH 11/17] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3008b26b3..88cac2a9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added ### Fixed ### Changed +- Move magic methods (`__radd__`, `__sub__`, `__rsub__`, `__rmul__`, `__richcmp__`, `__neg__`, and `__rtruediv__`) to `ExprLike` base class ### Removed ## 6.2.1 - 2026.05.16 @@ -31,7 +32,6 @@ - Return NotImplemented for `Expr` and `GenExpr` operators if they can't handle input types in the calculation - Speed up `constant * Expr` via C-level API - Speed up `Term.__eq__` via the C-level API -- Move magic methods (`__radd__`, `__sub__`, `__rsub__`, `__rmul__`, `__richcmp__`, `__neg__`, and `__rtruediv__`) to `ExprLike` base class ### Removed - Removed outdated warning about Make build system incompatibility - Removed `Term.ptrtuple` to optimize `Term` memory usage From 6ca96c9f375e452d937e2c3136b29307ee119457 Mon Sep 17 00:00:00 2001 From: 40% Date: Sat, 23 May 2026 12:01:03 +0800 Subject: [PATCH 12/17] Make ExprLike.__neg__ positional-only Add the positional-only marker (/) to the ExprLike.__neg__ signature in src/pyscipopt/expr.pxi. This enforces that the unary negation method cannot be called with keyword arguments and aligns the signature with Python/Cython expectations, avoiding potential warnings or misuse. --- src/pyscipopt/expr.pxi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi index 5985d2eb8..8c952700f 100644 --- a/src/pyscipopt/expr.pxi +++ b/src/pyscipopt/expr.pxi @@ -274,7 +274,7 @@ cdef class ExprLike: def __richcmp__(self, other, int op): return _expr_richcmp(self, other, op) - def __neg__(self): + def __neg__(self, /): return self * -1.0 def __abs__(self) -> GenExpr: From 823b94c969608a6915c5c07bee51a1e05778fc39 Mon Sep 17 00:00:00 2001 From: 40% Date: Sat, 23 May 2026 12:01:12 +0800 Subject: [PATCH 13/17] Refine ExprLike operator return types Update type stubs in src/pyscipopt/scip.pyi to change return types of several ExprLike dunder operators from object to Incomplete for improved typing precision. Affected methods: __radd__, __sub__, __rsub__, __rmul__, __rtruediv__, and __neg__. This is a type-only change with no runtime behavior modifications. --- src/pyscipopt/scip.pyi | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index a08b498b9..663ac911a 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -331,12 +331,12 @@ class ExprLike: *args: Incomplete, **kwargs: Incomplete, ) -> Incomplete: ... - def __radd__(self, other: object, /) -> object: ... - def __sub__(self, other: object, /) -> object: ... - def __rsub__(self, other: object, /) -> object: ... - def __rmul__(self, other: object, /) -> object: ... - def __rtruediv__(self, other: object, /) -> object: ... - def __neg__(self) -> object: ... + def __radd__(self, other: object, /) -> Incomplete: ... + def __sub__(self, other: object, /) -> Incomplete: ... + def __rsub__(self, other: object, /) -> Incomplete: ... + def __rmul__(self, other: object, /) -> Incomplete: ... + def __rtruediv__(self, other: object, /) -> Incomplete: ... + def __neg__(self) -> Incomplete: ... def __abs__(self) -> GenExpr: ... def exp(self) -> GenExpr: ... def log(self) -> GenExpr: ... From ec64ba2ce92e42cd90e37098261bfe76a05deb65 Mon Sep 17 00:00:00 2001 From: 40% Date: Sat, 23 May 2026 12:03:17 +0800 Subject: [PATCH 14/17] Annotate __rtruediv__ return type as GenExpr Add explicit GenExpr return annotations for __rtruediv__ across implementation and typing stub. Updated src/pyscipopt/expr.pxi to annotate ExprLike, Expr and GenExpr __rtruediv__ methods with -> GenExpr, and updated src/pyscipopt/scip.pyi to change the stub return types from object to GenExpr for Expr.__rtruediv__ and GenExpr.__rtruediv__. This improves static typing consistency between the Cython implementation and the Python stubs. --- src/pyscipopt/expr.pxi | 6 +++--- src/pyscipopt/scip.pyi | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi index 120dc0efd..1c625b375 100644 --- a/src/pyscipopt/expr.pxi +++ b/src/pyscipopt/expr.pxi @@ -269,7 +269,7 @@ cdef class ExprLike: def __rmul__(self, other, /): return self * other - def __rtruediv__(self, other, /): + def __rtruediv__(self, other, /) -> GenExpr: return buildGenExprObj(other) / self def __richcmp__(self, other, int op): @@ -379,7 +379,7 @@ cdef class Expr(ExprLike): return 1.0 / other * self return buildGenExprObj(self) / other - def __rtruediv__(self, other, /): + def __rtruediv__(self, other, /) -> GenExpr: if not _is_expr_compatible(other): return NotImplemented return super().__rtruediv__(other) @@ -690,7 +690,7 @@ cdef class GenExpr(ExprLike): raise ZeroDivisionError("cannot divide by 0") return self * divisor**(-1) - def __rtruediv__(self, other, /): + def __rtruediv__(self, other, /) -> GenExpr: if not _is_genexpr_compatible(other): return NotImplemented return super().__rtruediv__(other) diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index 663ac911a..f3edc3d79 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -364,7 +364,7 @@ class Expr(ExprLike): def __pow__(self, other: Incomplete, modulo: Incomplete = ..., /) -> Incomplete: ... def __rpow__(self, other: Incomplete, /) -> Incomplete: ... def __truediv__(self, other: Incomplete, /) -> Incomplete: ... - def __rtruediv__(self, other: object, /) -> object: ... + def __rtruediv__(self, other: object, /) -> GenExpr: ... @disjoint_base class ExprCons: @@ -401,7 +401,7 @@ class GenExpr(ExprLike): def __pow__(self, other: Incomplete, modulo: Incomplete = ..., /) -> Incomplete: ... def __rpow__(self, other: Incomplete, /) -> Incomplete: ... def __truediv__(self, other: Incomplete, /) -> Incomplete: ... - def __rtruediv__(self, other: object, /) -> object: ... + def __rtruediv__(self, other: object, /) -> GenExpr: ... @disjoint_base class Heur: From 9fdca93602710851377d77b7909249260ae219ac Mon Sep 17 00:00:00 2001 From: 40% Date: Sat, 23 May 2026 12:11:57 +0800 Subject: [PATCH 15/17] Annotate __neg__ return type Update negation operator type hints to return Union[Expr, GenExpr]. - src/pyscipopt/expr.pxi: add a Cython return annotation for __neg__. - src/pyscipopt/scip.pyi: change __neg__ from Incomplete to Union[Expr, GenExpr]. These changes improve static typing and IDE support by accurately describing the result of unary negation on expression-like objects. --- src/pyscipopt/expr.pxi | 2 +- src/pyscipopt/scip.pyi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi index 1c625b375..62f0c880d 100644 --- a/src/pyscipopt/expr.pxi +++ b/src/pyscipopt/expr.pxi @@ -275,7 +275,7 @@ cdef class ExprLike: def __richcmp__(self, other, int op): return _expr_richcmp(self, other, op) - def __neg__(self, /): + def __neg__(self, /) -> Union[Expr, GenExpr]: return self * -1.0 def __abs__(self) -> GenExpr: diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index f3edc3d79..49a5a03f9 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -336,7 +336,7 @@ class ExprLike: def __rsub__(self, other: object, /) -> Incomplete: ... def __rmul__(self, other: object, /) -> Incomplete: ... def __rtruediv__(self, other: object, /) -> Incomplete: ... - def __neg__(self) -> Incomplete: ... + def __neg__(self) -> Union[Expr, GenExpr]: ... def __abs__(self) -> GenExpr: ... def exp(self) -> GenExpr: ... def log(self) -> GenExpr: ... From 61cea57d28fb7a41f11ab2d3fb5cfc6fdffcf5d1 Mon Sep 17 00:00:00 2001 From: 40% Date: Sat, 23 May 2026 12:12:37 +0800 Subject: [PATCH 16/17] Unify __rtruediv__ typing in stubs Update typing in src/pyscipopt/scip.pyi: change ExprLike.__rtruediv__ return type from Incomplete to GenExpr and remove duplicate __rtruediv__ declarations from Expr and GenExpr. This consolidates the right-division signature in the base ExprLike class to avoid conflicting or redundant stub definitions and improve type consistency. --- src/pyscipopt/scip.pyi | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index 49a5a03f9..e765c9268 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -335,7 +335,7 @@ class ExprLike: def __sub__(self, other: object, /) -> Incomplete: ... def __rsub__(self, other: object, /) -> Incomplete: ... def __rmul__(self, other: object, /) -> Incomplete: ... - def __rtruediv__(self, other: object, /) -> Incomplete: ... + def __rtruediv__(self, other: object, /) -> GenExpr: ... def __neg__(self) -> Union[Expr, GenExpr]: ... def __abs__(self) -> GenExpr: ... def exp(self) -> GenExpr: ... @@ -364,7 +364,6 @@ class Expr(ExprLike): def __pow__(self, other: Incomplete, modulo: Incomplete = ..., /) -> Incomplete: ... def __rpow__(self, other: Incomplete, /) -> Incomplete: ... def __truediv__(self, other: Incomplete, /) -> Incomplete: ... - def __rtruediv__(self, other: object, /) -> GenExpr: ... @disjoint_base class ExprCons: @@ -401,7 +400,6 @@ class GenExpr(ExprLike): def __pow__(self, other: Incomplete, modulo: Incomplete = ..., /) -> Incomplete: ... def __rpow__(self, other: Incomplete, /) -> Incomplete: ... def __truediv__(self, other: Incomplete, /) -> Incomplete: ... - def __rtruediv__(self, other: object, /) -> GenExpr: ... @disjoint_base class Heur: From aa6ae866a5f0830548737ed8d9b33d7c52b92162 Mon Sep 17 00:00:00 2001 From: 40% Date: Sat, 23 May 2026 12:38:05 +0800 Subject: [PATCH 17/17] Mark __neg__ as positional-only in scip.pyi Update the typing stub for ExprLike.__neg__ in src/pyscipopt/scip.pyi to include the positional-only marker (/). This clarifies that __neg__ accepts no keyword arguments and returns Union[Expr, GenExpr]; no runtime behavior changes. --- src/pyscipopt/scip.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index da3a3e29b..ad576048d 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -336,7 +336,7 @@ class ExprLike: def __rsub__(self, other: object, /) -> Incomplete: ... def __rmul__(self, other: object, /) -> Incomplete: ... def __rtruediv__(self, other: object, /) -> GenExpr: ... - def __neg__(self) -> Union[Expr, GenExpr]: ... + def __neg__(self, /) -> Union[Expr, GenExpr]: ... def __abs__(self) -> GenExpr: ... def exp(self) -> GenExpr: ... def log(self) -> GenExpr: ...