Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased
### Added
- Added `addConsCumulative()` for SCIP cumulative constraints (#1222)
- `Expr` and `GenExpr` support `__pos__` magic method like `+Expr` or `+GenExpr`
### Fixed
### Changed
- Move magic methods (`__radd__`, `__sub__`, `__rsub__`, `__rmul__`, `__richcmp__`, `__neg__`, and `__rtruediv__`) to `ExprLike` base class (#1204)
Expand Down
52 changes: 44 additions & 8 deletions src/pyscipopt/expr.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,9 @@ cdef class ExprLike:
def __neg__(self, /) -> Union[Expr, GenExpr]:
return self * -1.0

def __pos__(self, /) -> Union[Expr, GenExpr]:
return self.copy()

def __abs__(self) -> GenExpr:
return UnaryExpr(Operator.fabs, buildGenExprObj(self))

Expand All @@ -296,6 +299,11 @@ cdef class ExprLike:
def cos(self) -> GenExpr:
return UnaryExpr(Operator.cos, buildGenExprObj(self))

cdef ExprLike copy(self, bint copy=True):
raise NotImplementedError(
f"{self.__class__.__name__!s} need to implement copy() method"
)


##@details Polynomial expressions of variables with operator overloading. \n
#See also the @ref ExprDetails "description" in the expr.pxi.
Expand Down Expand Up @@ -435,6 +443,12 @@ cdef class Expr(ExprLike):
res += coef * term._evaluate(sol)
return res

cdef Expr copy(self, bint copy=True):
cdef object cls = <type>Py_TYPE(self)
cdef Expr res = cls.__new__(cls)
res.terms = self.terms.copy() if copy else self.terms
return res


cdef class ExprCons:
'''Constraints with a polynomial expressions and lower/upper bounds.'''
Expand Down Expand Up @@ -703,18 +717,11 @@ cdef class GenExpr(ExprLike):
'''returns operator of GenExpr'''
return self._op

cdef GenExpr copy(self, bool copy = True):
cdef GenExpr copy(self, bint copy=True):
cdef object cls = <type>Py_TYPE(self)
cdef GenExpr res = cls.__new__(cls)
res._op = self._op
res.children = self.children.copy() if copy else self.children
if cls is SumExpr:
(<SumExpr>res).constant = (<SumExpr>self).constant
(<SumExpr>res).coefs = (<SumExpr>self).coefs.copy() if copy else (<SumExpr>self).coefs
if cls is ProdExpr:
(<ProdExpr>res).constant = (<ProdExpr>self).constant
elif cls is PowExpr:
(<PowExpr>res).expo = (<PowExpr>self).expo
return res


Expand All @@ -741,6 +748,14 @@ cdef class SumExpr(GenExpr):
res += <double>coefs[i] * (<GenExpr>children[i])._evaluate(sol)
return res

cdef SumExpr copy(self, bint copy=True):
cdef SumExpr res = SumExpr.__new__(SumExpr)
res._op = self._op
res.children = self.children.copy() if copy else self.children
res.constant = self.constant
res.coefs = self.coefs.copy() if copy else self.coefs
return res


# Prod Expressions
cdef class ProdExpr(GenExpr):
Expand All @@ -765,6 +780,13 @@ cdef class ProdExpr(GenExpr):
return 0.0
return res

cdef ProdExpr copy(self, bint copy=True):
cdef ProdExpr res = ProdExpr.__new__(ProdExpr)
res._op = self._op
res.children = self.children.copy() if copy else self.children
res.constant = self.constant
return res


# Var Expressions
cdef class VarExpr(GenExpr):
Expand Down Expand Up @@ -798,6 +820,13 @@ cdef class PowExpr(GenExpr):
cpdef double _evaluate(self, Solution sol) except *:
return (<GenExpr>self.children[0])._evaluate(sol) ** self.expo

cdef PowExpr copy(self, bint copy=True):
cdef PowExpr res = PowExpr.__new__(PowExpr)
res._op = self._op
res.children = self.children.copy() if copy else self.children
res.expo = self.expo
return res


# Exp, Log, Sqrt, Sin, Cos Expressions
cdef class UnaryExpr(GenExpr):
Expand Down Expand Up @@ -832,6 +861,13 @@ cdef class Constant(GenExpr):
cpdef double _evaluate(self, Solution sol) except *:
return self.number

cdef Constant copy(self, bint copy=True):
# The copy parameter doesn't work; this is for compatibility.
cdef Constant res = Constant.__new__(Constant)
res._op = self._op
res.number = self.number
return res


def exp(x):
"""
Expand Down
3 changes: 2 additions & 1 deletion src/pyscipopt/scip.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -2151,7 +2151,8 @@ cdef extern from "tpi/tpi.h":
int SCIPtpiGetNumThreads()

cdef class ExprLike:
pass

cdef ExprLike copy(self, bint copy=*)

cdef class Expr(ExprLike):
cdef public terms
Expand Down
3 changes: 3 additions & 0 deletions src/pyscipopt/scip.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -1563,6 +1563,9 @@ cdef class Variable(Expr):
cname = bytes( SCIPvarGetName(self.scip_var) )
return cname.decode('utf-8')

cdef Variable copy(self, bint copy=True):
return self

def ptr(self):
return <size_t>(self.scip_var)

Expand Down
2 changes: 2 additions & 0 deletions src/pyscipopt/scip.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ class Eventhdlr:
def eventinit(self) -> Incomplete: ...
def eventinitsol(self) -> Incomplete: ...

@disjoint_base
class ExprLike:
def __array_ufunc__(
self,
Expand All @@ -338,6 +339,7 @@ class ExprLike:
def __rmul__(self, other: object, /) -> Incomplete: ...
def __rtruediv__(self, other: object, /) -> GenExpr: ...
def __neg__(self, /) -> Union[Expr, GenExpr]: ...
def __pos__(self, /) -> Union[Expr, GenExpr]: ...
def __abs__(self) -> GenExpr: ...
def exp(self) -> GenExpr: ...
def log(self) -> GenExpr: ...
Expand Down
47 changes: 47 additions & 0 deletions tests/test_expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,3 +547,50 @@ def test_Expr_iadd_Expr():
e1 += e2
assert str(e1) == "Expr({Term(x): -1.0, Term(): 0.0, Term(y): 1.0})"
assert str(e2) == "Expr({Term(y): 1.0, Term(): -1.0})"


def test_pos():
m = Model()
x = m.addVar(name="x")

Comment thread
Zeroto521 marked this conversation as resolved.
# test Variable
res = +x
assert str(res) == "x"
assert res is x

# test Expr
e = x + 1
res = +(x + 1)
assert str(res) == "Expr({Term(x): 1.0, Term(): 1.0})"
assert e is not res

# test SumExpr
e = sqrt(x) + 1
res = +e
assert str(res) == str(e)
assert e is not res

# test UnaryExpr
e = cos(x)
res = +e
assert str(res) == str(e)
assert e is not res

# test ProdExpr
e = x * sin(x)
res = +e
assert str(res) == str(e)
assert e is not res

# test PowExpr
e = log(x)**2
res = +e
assert str(res) == str(e)
assert e is not res

# test Constant
c = sqrt(1).children[0]
assert type(c) is not int
e = +c
assert str(e) == str(c)
assert e is not c
Loading