diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f7124710..80bea8d07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Added `addConsCumulative()` for SCIP cumulative constraints (#1222) ### Fixed ### Changed +- Move magic methods (`__radd__`, `__sub__`, `__rsub__`, `__rmul__`, `__richcmp__`, `__neg__`, and `__rtruediv__`) to `ExprLike` base class (#1204) - Speed up `Expr.__add__` and `Expr.__iadd__` via the C-level API ### Removed 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 4b4fe6d16..86196cfc1 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -332,6 +332,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: ... @@ -345,7 +351,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: ... @@ -357,14 +362,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 @@ -391,7 +390,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: ... @@ -400,14 +398,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