Skip to content

Commit 3478efb

Browse files
committed
Merge remote-tracking branch 'origin/master' into realtime-trace-jsonl
2 parents 987251f + ac47132 commit 3478efb

4 files changed

Lines changed: 32 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
- Set `__array_priority__` for MatrixExpr and MatrixExprCons
2828
- changed addConsNode() and addConsLocal() to mirror addCons() and accept ExprCons instead of Constraint
2929
- Improved `chgReoptObjective()` performance
30+
- Return itself for abs to UnaryExpr(Operator.fabs)
3031
### Removed
3132

3233
## 6.0.0 - 2025.11.28

src/pyscipopt/expr.pxi

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,10 @@
4545
import math
4646
from typing import TYPE_CHECKING
4747

48-
from pyscipopt.scip cimport Variable, Solution
4948
from cpython.dict cimport PyDict_Next
49+
from cpython.object cimport Py_TYPE
5050
from cpython.ref cimport PyObject
51+
from pyscipopt.scip cimport Variable, Solution
5152

5253
import numpy as np
5354

@@ -636,6 +637,20 @@ cdef class GenExpr:
636637
'''returns operator of GenExpr'''
637638
return self._op
638639

640+
cdef GenExpr copy(self, bool copy = True):
641+
cdef object cls = <type>Py_TYPE(self)
642+
cdef GenExpr res = cls.__new__(cls)
643+
res._op = self._op
644+
res.children = self.children.copy() if copy else self.children
645+
if cls is SumExpr:
646+
(<SumExpr>res).constant = (<SumExpr>self).constant
647+
(<SumExpr>res).coefs = (<SumExpr>self).coefs.copy() if copy else (<SumExpr>self).coefs
648+
if cls is ProdExpr:
649+
(<ProdExpr>res).constant = (<ProdExpr>self).constant
650+
elif cls is PowExpr:
651+
(<PowExpr>res).expo = (<PowExpr>self).expo
652+
return res
653+
639654

640655
# Sum Expressions
641656
cdef class SumExpr(GenExpr):
@@ -725,6 +740,11 @@ cdef class UnaryExpr(GenExpr):
725740
self.children.append(expr)
726741
self._op = op
727742

743+
def __abs__(self) -> UnaryExpr:
744+
if self._op == "abs":
745+
return <UnaryExpr>self.copy()
746+
return UnaryExpr(Operator.fabs, self)
747+
728748
def __repr__(self):
729749
return self._op + "(" + self.children[0].__repr__() + ")"
730750

src/pyscipopt/scip.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ class GenExpr:
377377
def __init__(self) -> None: ...
378378
def degree(self) -> Incomplete: ...
379379
def getOp(self) -> Incomplete: ...
380-
def __abs__(self) -> Incomplete: ...
380+
def __abs__(self) -> GenExpr: ...
381381
def __add__(self, other: Incomplete) -> Incomplete: ...
382382
def __eq__(self, other: object) -> bool: ...
383383
def __ge__(self, other: object) -> bool: ...
@@ -2198,9 +2198,9 @@ class Term:
21982198
def __lt__(self, other: object) -> bool: ...
21992199
def __ne__(self, other: object) -> bool: ...
22002200

2201-
@disjoint_base
22022201
class UnaryExpr(GenExpr):
22032202
def __init__(self, *args: Incomplete, **kwargs: Incomplete) -> None: ...
2203+
def __abs__(self) -> GenExpr: ...
22042204

22052205
@disjoint_base
22062206
class VarExpr(GenExpr):

tests/test_expr.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,3 +218,11 @@ def test_getVal_with_GenExpr():
218218

219219
with pytest.raises(ZeroDivisionError):
220220
m.getVal(1 / z)
221+
222+
223+
def test_abs_abs_expr():
224+
m = Model()
225+
x = m.addVar(name="x")
226+
227+
# should print abs(x) not abs(abs(x))
228+
assert str(abs(abs(x))) == str(abs(x))

0 commit comments

Comments
 (0)