-
Notifications
You must be signed in to change notification settings - Fork 275
BUG: can't call getVal from GenExpr
#1148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
f87432c
8c600a2
85ee97e
135b954
ad7d4c6
5ff4144
95b3a80
7770a06
50e9c6c
239c3a3
d2ab8e2
2b145dd
9afb07f
66c2f6b
4ff2682
073ac1c
22b9f4f
449e2fd
7cd196a
3ff4658
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,8 +42,16 @@ | |
| # which should, in princple, modify the expr. However, since we do not implement __isub__, __sub__ | ||
| # gets called (I guess) and so a copy is returned. | ||
| # Modifying the expression directly would be a bug, given that the expression might be re-used by the user. </pre> | ||
| import math | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| from pyscipopt.scip cimport Variable, Solution | ||
|
|
||
| include "matrix.pxi" | ||
|
|
||
| if TYPE_CHECKING: | ||
| double = float | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For IDE
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above |
||
|
|
||
|
|
||
| def _is_number(e): | ||
| try: | ||
|
|
@@ -87,23 +95,25 @@ def _expr_richcmp(self, other, op): | |
| raise NotImplementedError("Can only support constraints with '<=', '>=', or '=='.") | ||
|
|
||
|
|
||
| class Term: | ||
| cdef class Term: | ||
Joao-Dionisio marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| '''This is a monomial term''' | ||
|
|
||
| __slots__ = ('vartuple', 'ptrtuple', 'hashval') | ||
| cdef readonly tuple vartuple | ||
| cdef readonly tuple ptrtuple | ||
| cdef int hashval | ||
|
|
||
| def __init__(self, *vartuple): | ||
| def __init__(self, *vartuple: Variable): | ||
| self.vartuple = tuple(sorted(vartuple, key=lambda v: v.ptr())) | ||
| self.ptrtuple = tuple(v.ptr() for v in self.vartuple) | ||
| self.hashval = sum(self.ptrtuple) | ||
| self.hashval = hash(self.ptrtuple) | ||
|
|
||
| def __getitem__(self, idx): | ||
| return self.vartuple[idx] | ||
|
|
||
| def __hash__(self): | ||
| return self.hashval | ||
|
|
||
| def __eq__(self, other): | ||
| def __eq__(self, other: Term): | ||
| return self.ptrtuple == other.ptrtuple | ||
|
|
||
| def __len__(self): | ||
|
|
@@ -116,6 +126,13 @@ class Term: | |
| def __repr__(self): | ||
| return 'Term(%s)' % ', '.join([str(v) for v in self.vartuple]) | ||
|
|
||
| cpdef double _evaluate(self, Solution sol): | ||
| cdef double res = 1 | ||
| cdef Variable i | ||
| for i in self.vartuple: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for my ignorance, but what is a |
||
| res *= SCIPgetSolVal(sol.scip, sol.sol, i.scip_var) | ||
| return res | ||
|
|
||
|
|
||
| CONST = Term() | ||
|
|
||
|
|
@@ -157,7 +174,7 @@ def buildGenExprObj(expr): | |
| ##@details Polynomial expressions of variables with operator overloading. \n | ||
| #See also the @ref ExprDetails "description" in the expr.pxi. | ||
| cdef class Expr: | ||
|
|
||
| def __init__(self, terms=None): | ||
| '''terms is a dict of variables to coefficients. | ||
|
|
||
|
|
@@ -318,6 +335,14 @@ cdef class Expr: | |
| else: | ||
| return max(len(v) for v in self.terms) | ||
|
|
||
| cpdef double _evaluate(self, Solution sol): | ||
| cdef double res = 0 | ||
| cdef double coef | ||
| cdef Term term | ||
| for term, coef in self.terms.items(): | ||
| res += coef * term._evaluate(sol) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this count for when there are higher degrees? |
||
| return res | ||
|
|
||
|
|
||
| cdef class ExprCons: | ||
| '''Constraints with a polynomial expressions and lower/upper bounds.''' | ||
|
|
@@ -427,10 +452,10 @@ Operator = Op() | |
| # | ||
| #See also the @ref ExprDetails "description" in the expr.pxi. | ||
| cdef class GenExpr: | ||
|
|
||
| cdef public _op | ||
| cdef public children | ||
|
|
||
|
|
||
| def __init__(self): # do we need it | ||
| ''' ''' | ||
|
|
||
|
|
@@ -625,44 +650,83 @@ cdef class SumExpr(GenExpr): | |
| def __repr__(self): | ||
| return self._op + "(" + str(self.constant) + "," + ",".join(map(lambda child : child.__repr__(), self.children)) + ")" | ||
|
|
||
| cpdef double _evaluate(self, Solution sol): | ||
| cdef double res = self.constant | ||
| cdef GenExpr child | ||
| cdef double coef | ||
| for child, coef in zip(self.children, self.coefs): | ||
| res += coef * child._evaluate(sol) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above. |
||
| return res | ||
|
|
||
|
|
||
| # Prod Expressions | ||
| cdef class ProdExpr(GenExpr): | ||
|
|
||
| cdef public constant | ||
|
|
||
| def __init__(self): | ||
| self.constant = 1.0 | ||
| self.children = [] | ||
| self._op = Operator.prod | ||
|
|
||
| def __repr__(self): | ||
| return self._op + "(" + str(self.constant) + "," + ",".join(map(lambda child : child.__repr__(), self.children)) + ")" | ||
|
|
||
| cpdef double _evaluate(self, Solution sol): | ||
| cdef double res = self.constant | ||
| cdef GenExpr child | ||
| for child in self.children: | ||
| res *= child._evaluate(sol) | ||
| return res | ||
|
|
||
|
|
||
| # Var Expressions | ||
| cdef class VarExpr(GenExpr): | ||
|
|
||
| cdef public var | ||
|
|
||
| def __init__(self, var): | ||
| self.children = [var] | ||
| self._op = Operator.varidx | ||
|
|
||
| def __repr__(self): | ||
| return self.children[0].__repr__() | ||
|
|
||
| cpdef double _evaluate(self, Solution sol): | ||
| return self.children[0]._evaluate(sol) | ||
|
|
||
|
|
||
| # Pow Expressions | ||
| cdef class PowExpr(GenExpr): | ||
|
|
||
| cdef public expo | ||
|
|
||
| def __init__(self): | ||
| self.expo = 1.0 | ||
| self.children = [] | ||
| self._op = Operator.power | ||
|
|
||
| def __repr__(self): | ||
| return self._op + "(" + self.children[0].__repr__() + "," + str(self.expo) + ")" | ||
|
|
||
| cpdef double _evaluate(self, Solution sol): | ||
| return self.children[0]._evaluate(sol) ** self.expo | ||
|
|
||
|
|
||
| # Exp, Log, Sqrt, Sin, Cos Expressions | ||
| cdef class UnaryExpr(GenExpr): | ||
| def __init__(self, op, expr): | ||
| self.children = [] | ||
| self.children.append(expr) | ||
| self._op = op | ||
|
|
||
| def __repr__(self): | ||
| return self._op + "(" + self.children[0].__repr__() + ")" | ||
|
|
||
| cpdef double _evaluate(self, Solution sol): | ||
| return getattr(math, self._op)(self.children[0]._evaluate(sol)) | ||
|
|
||
|
|
||
| # class for constant expressions | ||
| cdef class Constant(GenExpr): | ||
| cdef public number | ||
|
|
@@ -673,6 +737,10 @@ cdef class Constant(GenExpr): | |
| def __repr__(self): | ||
| return str(self.number) | ||
|
|
||
| cpdef double _evaluate(self, Solution sol): | ||
| return self.number | ||
|
|
||
|
|
||
| def exp(expr): | ||
| """returns expression with exp-function""" | ||
| if isinstance(expr, MatrixExpr): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,13 +5,16 @@ | |
|
|
||
| from typing import Optional, Tuple, Union | ||
| import numpy as np | ||
| from numpy.typing import NDArray | ||
| try: | ||
| # NumPy 2.x location | ||
| from numpy.lib.array_utils import normalize_axis_tuple | ||
| except ImportError: | ||
| # Fallback for NumPy 1.x | ||
| from numpy.core.numeric import normalize_axis_tuple | ||
|
|
||
| from pyscipopt.scip cimport Expr, Solution | ||
|
|
||
|
|
||
| def _is_number(e): | ||
| try: | ||
|
|
@@ -49,6 +52,9 @@ def _matrixexpr_richcmp(self, other, op): | |
| return res.view(MatrixExprCons) | ||
|
|
||
|
|
||
| _evaluate = np.frompyfunc(lambda expr, sol: expr._evaluate(sol), 2, 1) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This shouldn't be here, right? |
||
|
|
||
|
|
||
| class MatrixExpr(np.ndarray): | ||
|
|
||
| def sum( | ||
|
|
@@ -148,9 +154,14 @@ class MatrixExpr(np.ndarray): | |
| def __matmul__(self, other): | ||
| return super().__matmul__(other).view(MatrixExpr) | ||
|
|
||
| def _evaluate(self, Solution sol) -> NDArray[np.float64]: | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| return _evaluate(self, sol).view(np.ndarray) | ||
|
|
||
|
|
||
| class MatrixGenExpr(MatrixExpr): | ||
| pass | ||
|
|
||
|
|
||
| class MatrixExprCons(np.ndarray): | ||
|
|
||
| def __le__(self, other: Union[float, int, np.ndarray]) -> MatrixExprCons: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,10 @@ | ||
| import math | ||
|
|
||
| import pytest | ||
|
|
||
| from pyscipopt import Model, sqrt, log, exp, sin, cos | ||
| from pyscipopt.scip import Expr, GenExpr, ExprCons, Term, quicksum | ||
| from pyscipopt.scip import Expr, GenExpr, ExprCons, Term | ||
|
|
||
|
|
||
| @pytest.fixture(scope="module") | ||
| def model(): | ||
|
|
@@ -188,3 +191,21 @@ def test_rpow_constant_base(model): | |
|
|
||
| with pytest.raises(ValueError): | ||
| c = (-2)**x | ||
|
|
||
|
|
||
| def test_evaluate(): | ||
| m = Model() | ||
| x = m.addVar(lb=1, ub=1) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this please be 2, instead? Just to be safer that it works (a bit paranoid, I know). |
||
| m.optimize() | ||
|
|
||
| # test "Expr({Term(x1): 1.0, Term(): 1.0})" | ||
| assert m.getVal(x + 1) == 2 | ||
| # test "prod(1.0,sum(0.0,prod(1.0,x1)),**(sum(0.0,prod(1.0,x1)),-1))" | ||
| assert m.getVal(x / x) == 1 | ||
| # test "**(prod(1.0,**(sum(0.0,prod(1.0,x1)),-1)),2)" | ||
| assert m.getVal((1 / x) ** 2) == 1 | ||
| # test "sin(sum(0.0,prod(1.0,x1)))" | ||
| assert round(m.getVal(sin(x)), 6) == round(math.sin(1), 6) | ||
|
|
||
| with pytest.raises(TypeError): | ||
| m.getVal(1) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could let IDE know what
VariableandSolutionare.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this is only relevant for people working with stuff inside
expr.pxi, right?