diff --git a/CHANGELOG.md b/CHANGELOG.md index 7464e9a8f..b51215e88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,11 @@ ### Added ### Fixed ### Changed +<<<<<<< ExprLike +- Move magic methods (`__radd__`, `__sub__`, `__rsub__`, `__rmul__`, `__richcmp__`, `__neg__`, and `__rtruediv__`) to `ExprLike` base class +======= - Speed up `Expr.__add__` and `Expr.__iadd__` via the C-level API +>>>>>>> master ### Removed ## 6.2.1 - 2026.05.16 diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi index bb25fdb29..62f0c880d 100644 --- a/src/pyscipopt/expr.pxi +++ b/src/pyscipopt/expr.pxi @@ -257,6 +257,27 @@ 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 __rtruediv__(self, other, /) -> GenExpr: + return buildGenExprObj(other) / self + + def __richcmp__(self, other, int op): + return _expr_richcmp(self, other, op) + + def __neg__(self, /) -> Union[Expr, GenExpr]: + return self * -1.0 + def __abs__(self) -> GenExpr: return UnaryExpr(Operator.fabs, buildGenExprObj(self)) @@ -358,11 +379,10 @@ cdef class Expr(ExprLike): return 1.0 / other * self return buildGenExprObj(self) / other - def __rtruediv__(self, other): - ''' other / self ''' + def __rtruediv__(self, other, /) -> GenExpr: if not _is_expr_compatible(other): return NotImplemented - return buildGenExprObj(other) / self + return super().__rtruediv__(other) def __pow__(self, other, modulo): if float(other).is_integer() and other >= 0: @@ -387,25 +407,6 @@ cdef class Expr(ExprLike): raise ValueError("Base of a**x must be positive, as expression is reformulated to scip.exp(x * scip.log(a)); got %g" % base) return (self * Constant(base).log()).exp() - 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, int 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} @@ -464,7 +465,6 @@ cdef class ExprCons: if not self._rhs is None: self._rhs -= c - def __richcmp__(self, other, op): '''turn it into a constraint''' if not _is_number(other): @@ -690,30 +690,10 @@ cdef class GenExpr(ExprLike): raise ZeroDivisionError("cannot divide by 0") return self * divisor**(-1) - def __rtruediv__(self, other): - ''' other / self ''' + def __rtruediv__(self, other, /) -> GenExpr: if not _is_genexpr_compatible(other): return NotImplemented - return buildGenExprObj(other) / 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, int op): - '''turn it into a constraint''' - return _expr_richcmp(self, other, op) + return super().__rtruediv__(other) def degree(self): '''Note: none of these expressions should be polynomial''' diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index ee82c00d9..ad576048d 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -331,6 +331,12 @@ class ExprLike: *args: Incomplete, **kwargs: Incomplete, ) -> Incomplete: ... + 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, /) -> GenExpr: ... + def __neg__(self, /) -> Union[Expr, GenExpr]: ... def __abs__(self) -> GenExpr: ... def exp(self) -> GenExpr: ... def log(self) -> GenExpr: ... @@ -344,7 +350,6 @@ class Expr(ExprLike): 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: ... @@ -356,14 +361,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 @@ -390,7 +389,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: ... @@ -399,14 +397,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