Skip to content

Commit d9e9530

Browse files
Zeroto521CopilotJoao-Dionisio
authored
Feature: Expr and GenExpr support numpy unary func like np.sin (#1170)
* Add numpy ufunc support for expression classes Introduced the ExprLike base class with __array_ufunc__ to enable numpy universal function (ufunc) support for Expr and GenExpr. Replaced standalone exp, log, sqrt, sin, and cos functions with numpy equivalents and mapped them for ufunc dispatch. This change improves interoperability with numpy and simplifies the codebase. * Introduce ExprLike base class for expressions Added a new ExprLike base class and made Expr inherit from it. This refactoring prepares the codebase for future extensions or polymorphism involving expression-like objects. * Fix unary ufunc mapping in ExprLike class Corrects the method call to use the first argument in the unary ufunc mapping, ensuring the correct object is used when applying numpy universal functions. * Add tests for unary operations and numpy compatibility Introduces tests for unary operations such as abs, sin, and sqrt, including their numpy equivalents, to ensure correct string representations and compatibility with numpy functions. * Update CHANGELOG.md * Add colon to ExprLike class definition Added a missing colon to the ExprLike class definition in scip.pxd to conform with Python/Cython syntax. * Fix test expectations for variable names in unary ops Updated test assertions in test_unary to expect variable names 'x', 'y', and 'z' instead of 'x1'. This aligns the tests with the current string representation of expressions. * Fix expected output format in test_unary Updated the expected string in test_unary to match the correct output format by removing commas between list elements. * Fix expected output for sin function in test_unary Corrected the expected string in test_unary to use 'sin' instead of 'abs' for the sin([x, y, z]) test case. * Add _evaluate method to Constant class Introduces a cpdef _evaluate method to the Constant class, returning the constant's value. This provides a consistent evaluation interface for expressions. * Update genexpr power tests for sqrt handling Removes the NotImplementedError expectation when raising genexpr to sqrt(2) and instead asserts the result is a GenExpr. Adds a new test to expect TypeError when raising to sqrt('2'). * Update test_unary to use two variables instead of three The test_unary function was moved and modified to test unary operations on lists containing two variables (x, y) instead of three (x, y, z). This streamlines the tests and aligns them with the current requirements. * Refactor expression classes with ExprLike base Introduced a new ExprLike base class to encapsulate common mathematical methods such as __abs__, exp, log, sqrt, sin, and cos. Updated Expr and GenExpr to inherit from ExprLike, reducing code duplication and improving maintainability. * Add __array_ufunc__ to ExprLike type stub Introduces the __array_ufunc__ method to the ExprLike class in the type stubs, enabling better compatibility with NumPy ufuncs. * Update numpy import and type annotations in stubs Changed 'import numpy' to 'import numpy as np' and updated type annotations to use 'np.ndarray' instead of 'numpy.ndarray' in the scip.pyi stub file. Also added UNARY_MAPPER as a Dict[np.ufunc, str]. * Update __array_ufunc__ type hints to use np.ufunc and str Replaces 'Incomplete' type hints with more specific types (np.ufunc and str) for the __array_ufunc__ methods in ExprLike, MatrixExpr, and MatrixExprCons classes to improve type accuracy. * Reorder and add math functions in scip.pyi Reordered the declarations of math functions and added missing entries for log, sin, and sqrt in the scip.pyi stub file to improve completeness and maintain consistency. * Reorder Operator and PATCH declarations in scip.pyi Moved the Operator type annotation below PATCH to maintain consistent ordering of variable declarations in the scip.pyi file. * Remove @disjoint_base decorator from ExprLike The @disjoint_base decorator was removed from the ExprLike class in the type stub. This may reflect a change in the class hierarchy or decorator usage. * Fix UNARY_MAPPER to use local math function references Replaces numpy function references in UNARY_MAPPER with local aliases (exp, log, sqrt, sin, cos) to ensure correct mapping and avoid issues with numpy function identity. * Update typing for UNARY_MAPPER in scip.pyi Replaces the use of Dict from typing with the built-in dict for the UNARY_MAPPER type annotation. This modernizes the type hint and removes an unused import. * Remove unused UNARY_MAPPER from type stub Deleted the UNARY_MAPPER dictionary from scip.pyi as it is no longer needed or used in the type stub. * Add return type annotations to ExprLike methods Updated the ExprLike class methods (__abs__, exp, log, sqrt, sin, cos) to specify GenExpr as their return type in both the implementation and the type stub. This improves type safety and clarity for users and tools. * Format: add blank line in GenExpr class Insert a blank line after the 'cdef class GenExpr(ExprLike):' declaration in src/pyscipopt/expr.pxi to improve readability and code formatting. * Refactor unary tests and add more unary ops Consolidate repeated expected strings into temporary `res` variables for clarity and add tests covering additional unary functions (cos, exp, log) and their numpy equivalents. Also add scalar comparisons for sqrt, exp, log, sin, cos to improve test coverage and readability. * Add array assertions for unary ops Extend tests in tests/test_expr.py to verify elementwise behavior of unary functions with array inputs. Added assert all(...) checks for sqrt, exp, log, sin, and cos comparing results to numpy equivalents to ensure these functions handle array inputs correctly. * Add test for invalid unary arcsin operation Expand test_unary in tests/test_expr.py to assert that calling np.arcsin on the symbolic x raises a TypeError. This adds coverage for invalid unary operations to ensure the code properly reports type errors for unsupported operand types. * Update CHANGELOG: numpy unary function notes Add Unreleased notes for Expr and GenExpr support for numpy unary functions (np.sin, np.cos, np.sqrt, np.exp, np.log, np.absolute) and clarify that unary functions that apply a constant now return a constant instead of a GenExpr. Remove duplicate entries of these notes from the 6.1.0 and 6.0.0 sections. * pyscipopt.sqrt(2) will return a Constant class * Expect NotImplementedError for genexpr ** sqrt(2) Update test to reflect that powering a generator expression by a non-constant expression should raise NotImplementedError. Replace the previous assertion that the operation succeeded and remove the TypeError case for sqrt("2"). Add a clarifying comment about only allowing power to constant expressions. * Dispatch numpy ufuncs for expr functions Replace per-function PyNumber_Check lambdas with a unified _dispatch_ufunc helper that vectorizes conversion of Python numbers to Constant via _to_const and np.frompyfunc. Removed the direct cimport of PyNumber_Check and updated exp/log/sqrt/sin/cos to call _dispatch_ufunc. _dispatch_ufunc also coerces ndarray results to MatrixExpr, centralizing numpy ufunc handling and improving array support for expression construction. * Update expr tests for MatrixExpr and repr Add MatrixExpr import and adjust unary-expression tests to match symbolic representations and matrix behavior. Replace numeric equality checks with string-based assertions for sqrt/exp/log/sin/cos, add a log([1,x]) repr check, and add isinstance checks for GenExpr and MatrixExpr to verify scalar vs. matrix expression types and nested/matrix shapes. * Fix ufunc dispatch for scalars update _dispatch_ufunc to distinguish iterables from scalars. For ndarray/list/tuple inputs the code applies the vectorized conversion and returns a MatrixExpr view for numpy arrays; for scalars it calls the scalar _to_const directly. This avoids incorrect wrapping of scalar inputs and ensures correct return types. * Rename _dispatch_ufunc to _wrap_ufunc * Use MatrixGenExpr for ufunc results Return MatrixGenExpr (not MatrixExpr) from _wrap_ufunc for ndarray ufunc results in expr.pxi, and update tests to import and expect MatrixGenExpr. This aligns unary ufunc behavior (e.g., sqrt on lists/arrays) with the generator matrix expression type and fixes related type assertions in tests. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Update CHANGELOG.md * Reject 'out' kwarg in __array_ufunc__ Add a guard to ExprLike.__array_ufunc__ that raises a TypeError if the numpy ufunc 'out' keyword argument is provided. Expression objects do not support writing into preallocated output arrays, so this explicit error prevents silent misuse or incorrect behavior when numpy ufuncs pass an 'out' parameter. * Test: forbid np.sin out= on Expr/Variable Add a unit test in tests/test_expr.py to assert that calling np.sin with an out= parameter on a Variable/Expr/GenExpr raises TypeError. This prevents in-place modification of expression objects via NumPy ufunc out arguments and documents the intended behavior. * Use MatrixGenExpr view for GenExpr arrays * Add docstring to _wrap_ufunc Add an explanatory docstring to the _wrap_ufunc helper describing how it handles scalars and collections: converting numeric inputs to Constant expressions, applying the ufunc element-wise via np.frompyfunc for arrays/lists/tuples, and returning a MatrixGenExpr for ndarrays or a list/tuple of GenExprs otherwise. Documentation-only change; no functional behavior modified. * Use Constant.log().exp() for pow with float base directly use inner method * Fix merge conflict artifacts in stubs and test imports * Update src/pyscipopt/scip.pyi Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Clarify _wrap_ufunc docstring returns Expand and reformat the _wrap_ufunc docstring to include a dedicated Returns section. The new text clarifies the function's return types for scalar vs. vector inputs (GenExpr/MatrixGenExpr) and explains that vectors yield an array of the same shape with the ufunc applied element-wise. Also minor formatting/line-wrap cleanup. * Replace ufunc lambdas with documented functions Replace terse lambda aliases for exp, log, sqrt, sin, and cos with full function definitions that call _wrap_ufunc. Each function now includes a detailed docstring describing parameters, behavior for scalars and vectors, and return types. This improves readability and documents expected input/outputs without changing functionality (still delegates to _wrap_ufunc and numpy ufuncs). --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: João Dionísio <57299939+Joao-Dionisio@users.noreply.github.com> Co-authored-by: Joao-Dionisio <joao.goncalves.dionisio@gmail.com>
1 parent 5329bc1 commit d9e9530

File tree

5 files changed

+303
-75
lines changed

5 files changed

+303
-75
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
## Unreleased
44
### Added
5+
- `Expr` and `GenExpr` support NumPy unary functions (`np.sin`, `np.cos`, `np.sqrt`, `np.exp`, `np.log`, `np.absolute`)
56
- Added `getBase()` and `setBase()` methods to `LP` class for getting/setting basis status
67
- Added `getMemUsed()`, `getMemTotal()`, and `getMemExternEstim()` methods
78
### Fixed
89
- Removed `Py_INCREF`/`Py_DECREF` on `Model` in `catchEvent`/`dropEvent` that caused memory leak for imbalanced usage
910
- Used `getIndex()` instead of `ptr()` for sorting nonlinear expression terms to avoid nondeterministic behavior
1011
- Fixed stubtest failures with mypy 1.20 by marking dunder method parameters as positional-only
12+
- Return `MatrixGenExpr` in `buildGenExprObj` instead of `MatrixExpr`
1113
### Changed
1214
- Speed up `constant * Expr` via C-level API
1315
- Speed up `Term.__eq__` via the C-level API

src/pyscipopt/expr.pxi

Lines changed: 215 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,16 @@
4343
# gets called (I guess) and so a copy is returned.
4444
# Modifying the expression directly would be a bug, given that the expression might be re-used by the user. </pre>
4545
import math
46-
from typing import TYPE_CHECKING
46+
from typing import TYPE_CHECKING, Literal
47+
48+
import numpy as np
4749

4850
from cpython.dict cimport PyDict_Next, PyDict_GetItem
4951
from cpython.object cimport Py_TYPE
5052
from cpython.ref cimport PyObject
5153
from cpython.tuple cimport PyTuple_GET_ITEM
5254
from pyscipopt.scip cimport Variable, Solution
5355

54-
import numpy as np
55-
5656

5757
if TYPE_CHECKING:
5858
double = float
@@ -194,6 +194,7 @@ cdef class Term:
194194

195195
CONST = Term()
196196

197+
197198
# helper function
198199
def buildGenExprObj(expr):
199200
"""helper function to generate an object of type GenExpr"""
@@ -223,16 +224,66 @@ def buildGenExprObj(expr):
223224
GenExprs = np.empty(expr.shape, dtype=object)
224225
for idx in np.ndindex(expr.shape):
225226
GenExprs[idx] = buildGenExprObj(expr[idx])
226-
return GenExprs.view(MatrixExpr)
227+
return GenExprs.view(MatrixGenExpr)
227228

228229
else:
229230
assert isinstance(expr, GenExpr)
230231
return expr
231232

233+
234+
cdef class ExprLike:
235+
236+
def __array_ufunc__(
237+
self,
238+
ufunc: np.ufunc,
239+
method: Literal["__call__", "reduce", "reduceat", "accumulate", "outer", "at"],
240+
*args,
241+
**kwargs,
242+
):
243+
if kwargs.get("out", None) is not None:
244+
raise TypeError(
245+
f"{self.__class__.__name__} doesn't support the 'out' parameter in __array_ufunc__"
246+
)
247+
248+
if method == "__call__":
249+
if ufunc is np.absolute:
250+
return args[0].__abs__()
251+
elif ufunc is np.exp:
252+
return args[0].exp()
253+
elif ufunc is np.log:
254+
return args[0].log()
255+
elif ufunc is np.sqrt:
256+
return args[0].sqrt()
257+
elif ufunc is np.sin:
258+
return args[0].sin()
259+
elif ufunc is np.cos:
260+
return args[0].cos()
261+
262+
return NotImplemented
263+
264+
def __abs__(self) -> GenExpr:
265+
return UnaryExpr(Operator.fabs, buildGenExprObj(self))
266+
267+
def exp(self) -> GenExpr:
268+
return UnaryExpr(Operator.exp, buildGenExprObj(self))
269+
270+
def log(self) -> GenExpr:
271+
return UnaryExpr(Operator.log, buildGenExprObj(self))
272+
273+
def sqrt(self) -> GenExpr:
274+
return UnaryExpr(Operator.sqrt, buildGenExprObj(self))
275+
276+
def sin(self) -> GenExpr:
277+
return UnaryExpr(Operator.sin, buildGenExprObj(self))
278+
279+
def cos(self) -> GenExpr:
280+
return UnaryExpr(Operator.cos, buildGenExprObj(self))
281+
282+
232283
##@details Polynomial expressions of variables with operator overloading. \n
233284
#See also the @ref ExprDetails "description" in the expr.pxi.
234-
cdef class Expr:
235-
285+
cdef class Expr(ExprLike):
286+
236287
def __init__(self, terms=None):
237288
'''terms is a dict of variables to coefficients.
238289
@@ -250,9 +301,6 @@ cdef class Expr:
250301
def __iter__(self):
251302
return iter(self.terms)
252303

253-
def __abs__(self):
254-
return abs(buildGenExprObj(self))
255-
256304
def __add__(self, other):
257305
left = self
258306
right = other
@@ -356,10 +404,10 @@ cdef class Expr:
356404
Note: base must be positive.
357405
"""
358406
if _is_number(other):
359-
base = float(other)
407+
base = <double>other
360408
if base <= 0.0:
361409
raise ValueError("Base of a**x must be positive, as expression is reformulated to scip.exp(x * scip.log(a)); got %g" % base)
362-
return exp(self * log(base))
410+
return (self * Constant(base).log()).exp()
363411
else:
364412
raise TypeError(f"Unsupported base type {type(other)} for exponentiation.")
365413

@@ -518,17 +566,14 @@ Operator = Op()
518566
# so expr[x] will generate an error instead of returning the coefficient of x </pre>
519567
#
520568
#See also the @ref ExprDetails "description" in the expr.pxi.
521-
cdef class GenExpr:
569+
cdef class GenExpr(ExprLike):
522570

523571
cdef public _op
524572
cdef public children
525573

526574
def __init__(self): # do we need it
527575
''' '''
528576

529-
def __abs__(self):
530-
return UnaryExpr(Operator.fabs, self)
531-
532577
def __add__(self, other):
533578
if isinstance(other, np.ndarray):
534579
return other + self
@@ -655,10 +700,10 @@ cdef class GenExpr:
655700
Note: base must be positive.
656701
"""
657702
if _is_number(other):
658-
base = float(other)
703+
base = <double>other
659704
if base <= 0.0:
660705
raise ValueError("Base of a**x must be positive, as expression is reformulated to scip.exp(x * scip.log(a)); got %g" % base)
661-
return exp(self * log(base))
706+
return (self * Constant(base).log()).exp()
662707
else:
663708
raise TypeError(f"Unsupported base type {type(other)} for exponentiation.")
664709

@@ -832,55 +877,160 @@ cdef class Constant(GenExpr):
832877
return self.number
833878

834879

835-
def exp(expr):
836-
"""returns expression with exp-function"""
837-
if isinstance(expr, MatrixExpr):
838-
unary_exprs = np.empty(shape=expr.shape, dtype=object)
839-
for idx in np.ndindex(expr.shape):
840-
unary_exprs[idx] = UnaryExpr(Operator.exp, buildGenExprObj(expr[idx]))
841-
return unary_exprs.view(MatrixGenExpr)
842-
else:
843-
return UnaryExpr(Operator.exp, buildGenExprObj(expr))
844-
845-
def log(expr):
846-
"""returns expression with log-function"""
847-
if isinstance(expr, MatrixExpr):
848-
unary_exprs = np.empty(shape=expr.shape, dtype=object)
849-
for idx in np.ndindex(expr.shape):
850-
unary_exprs[idx] = UnaryExpr(Operator.log, buildGenExprObj(expr[idx]))
851-
return unary_exprs.view(MatrixGenExpr)
852-
else:
853-
return UnaryExpr(Operator.log, buildGenExprObj(expr))
880+
def exp(x):
881+
"""
882+
returns expression with exp-function
883+
884+
Parameters
885+
----------
886+
x : Expr, GenExpr, number, np.ndarray, list, or tuple
887+
- If x is a scalar expression or number, apply the exp function directly to it.
888+
And if it's a number, convert it to a Constant expression first.
889+
- If x is a vector (np.ndarray, list, or tuple), apply the exp function
890+
element-wise using np.frompyfunc to convert each element to a Constant if it's
891+
a number, and then apply the exp function.
892+
893+
Returns
894+
-------
895+
GenExpr or MatrixGenExpr
896+
- If x is a scalar expression or number, returns the result of applying the exp
897+
function to it.
898+
- If x is a vector, returns an np.ndarray of the same shape with the exp
899+
function applied element-wise.
900+
"""
901+
return _wrap_ufunc(x, np.exp)
902+
903+
904+
def log(x):
905+
"""
906+
returns expression with log-function
907+
908+
Parameters
909+
----------
910+
x : Expr, GenExpr, number, np.ndarray, list, or tuple
911+
- If x is a scalar expression or number, apply the log function directly to it.
912+
And if it's a number, convert it to a Constant expression first.
913+
- If x is a vector (np.ndarray, list, or tuple), apply the log function
914+
element-wise using np.frompyfunc to convert each element to a Constant if it's
915+
a number, and then apply the log function.
916+
917+
Returns
918+
-------
919+
GenExpr or MatrixGenExpr
920+
- If x is a scalar expression or number, returns the result of applying the log
921+
function to it.
922+
- If x is a vector, returns an np.ndarray of the same shape with the log
923+
function applied element-wise.
924+
"""
925+
return _wrap_ufunc(x, np.log)
926+
927+
928+
def sqrt(x):
929+
"""
930+
returns expression with sqrt-function
931+
932+
Parameters
933+
----------
934+
x : Expr, GenExpr, number, np.ndarray, list, or tuple
935+
- If x is a scalar expression or number, apply the sqrt function directly to it.
936+
And if it's a number, convert it to a Constant expression first.
937+
- If x is a vector (np.ndarray, list, or tuple), apply the sqrt function
938+
element-wise using np.frompyfunc to convert each element to a Constant if it's
939+
a number, and then apply the sqrt function.
940+
941+
Returns
942+
-------
943+
GenExpr or MatrixGenExpr
944+
- If x is a scalar expression or number, returns the result of applying the sqrt
945+
function to it.
946+
- If x is a vector, returns an np.ndarray of the same shape with the sqrt
947+
function applied element-wise.
948+
"""
949+
return _wrap_ufunc(x, np.sqrt)
950+
951+
952+
def sin(x):
953+
"""
954+
returns expression with sin-function
955+
956+
Parameters
957+
----------
958+
x : Expr, GenExpr, number, np.ndarray, list, or tuple
959+
- If x is a scalar expression or number, apply the sin function directly to it.
960+
And if it's a number, convert it to a Constant expression first.
961+
- If x is a vector (np.ndarray, list, or tuple), apply the sin function
962+
element-wise using np.frompyfunc to convert each element to a Constant if it's
963+
a number, and then apply the sin function.
964+
965+
Returns
966+
-------
967+
GenExpr or MatrixGenExpr
968+
- If x is a scalar expression or number, returns the result of applying the sin
969+
function to it.
970+
- If x is a vector, returns an np.ndarray of the same shape with the sin
971+
function applied element-wise.
972+
"""
973+
return _wrap_ufunc(x, np.sin)
974+
975+
976+
def cos(x):
977+
"""
978+
returns expression with cos-function
979+
980+
Parameters
981+
----------
982+
x : Expr, GenExpr, number, np.ndarray, list, or tuple
983+
- If x is a scalar expression or number, apply the cos function directly to it.
984+
And if it's a number, convert it to a Constant expression first.
985+
- If x is a vector (np.ndarray, list, or tuple), apply the cos function
986+
element-wise using np.frompyfunc to convert each element to a Constant if it's
987+
a number, and then apply the cos function.
988+
989+
Returns
990+
-------
991+
GenExpr or MatrixGenExpr
992+
- If x is a scalar expression or number, returns the result of applying the cos
993+
function to it.
994+
- If x is a vector, returns an np.ndarray of the same shape with the cos
995+
function applied element-wise.
996+
"""
997+
return _wrap_ufunc(x, np.cos)
998+
999+
1000+
cdef inline object _to_const(object x):
1001+
return Constant(<double>x) if _is_number(x) else x
1002+
1003+
cdef object _vec_to_const = np.frompyfunc(_to_const, 1, 1)
1004+
1005+
cdef inline object _wrap_ufunc(object x, object ufunc):
1006+
"""
1007+
Apply a universal function (ufunc) to an expression or a collection of expressions.
1008+
1009+
Parameters
1010+
----------
1011+
x : Expr, GenExpr, number, np.ndarray, list, or tuple
1012+
- If x is a scalar expression or number, apply the ufunc directly to it. And if
1013+
it's a number, convert it to a Constant expression first.
1014+
- If x is a vector (np.ndarray, list, or tuple), apply the ufunc element-wise
1015+
using np.frompyfunc to convert each element to a Constant if it's a number,
1016+
and then apply the ufunc.
1017+
1018+
ufunc : np.ufunc
1019+
The universal function to be applied to x.
1020+
1021+
Returns
1022+
-------
1023+
GenExpr or MatrixGenExpr
1024+
- If x is a scalar expression or number, returns the result of applying the
1025+
ufunc to it.
1026+
- If x is a vector, returns an np.ndarray of the same shape with the ufunc
1027+
applied element-wise.
1028+
"""
1029+
if isinstance(x, (np.ndarray, list, tuple)):
1030+
res = ufunc(_vec_to_const(x))
1031+
return res.view(MatrixGenExpr) if isinstance(res, np.ndarray) else res
1032+
return ufunc(_to_const(x))
8541033

855-
def sqrt(expr):
856-
"""returns expression with sqrt-function"""
857-
if isinstance(expr, MatrixExpr):
858-
unary_exprs = np.empty(shape=expr.shape, dtype=object)
859-
for idx in np.ndindex(expr.shape):
860-
unary_exprs[idx] = UnaryExpr(Operator.sqrt, buildGenExprObj(expr[idx]))
861-
return unary_exprs.view(MatrixGenExpr)
862-
else:
863-
return UnaryExpr(Operator.sqrt, buildGenExprObj(expr))
864-
865-
def sin(expr):
866-
"""returns expression with sin-function"""
867-
if isinstance(expr, MatrixExpr):
868-
unary_exprs = np.empty(shape=expr.shape, dtype=object)
869-
for idx in np.ndindex(expr.shape):
870-
unary_exprs[idx] = UnaryExpr(Operator.sin, buildGenExprObj(expr[idx]))
871-
return unary_exprs.view(MatrixGenExpr)
872-
else:
873-
return UnaryExpr(Operator.sin, buildGenExprObj(expr))
874-
875-
def cos(expr):
876-
"""returns expression with cos-function"""
877-
if isinstance(expr, MatrixExpr):
878-
unary_exprs = np.empty(shape=expr.shape, dtype=object)
879-
for idx in np.ndindex(expr.shape):
880-
unary_exprs[idx] = UnaryExpr(Operator.cos, buildGenExprObj(expr[idx]))
881-
return unary_exprs.view(MatrixGenExpr)
882-
else:
883-
return UnaryExpr(Operator.cos, buildGenExprObj(expr))
8841034

8851035
def expr_to_nodes(expr):
8861036
'''transforms tree to an array of nodes. each node is an operator and the position of the

src/pyscipopt/scip.pxd

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2128,7 +2128,10 @@ cdef extern from "scip/scip_var.h":
21282128
cdef extern from "tpi/tpi.h":
21292129
int SCIPtpiGetNumThreads()
21302130

2131-
cdef class Expr:
2131+
cdef class ExprLike:
2132+
pass
2133+
2134+
cdef class Expr(ExprLike):
21322135
cdef public terms
21332136

21342137
cpdef double _evaluate(self, Solution sol)

0 commit comments

Comments
 (0)