Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
45 changes: 37 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
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
40 changes: 40 additions & 0 deletions tests/test_expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,3 +547,43 @@ 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
Loading