PySCIPOpt 6.0.0__cp312-cp312-win_amd64.whl → 6.1.0__cp312-cp312-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyscipopt/__init__.py +4 -4
- pyscipopt/_version.py +1 -1
- pyscipopt/benders.pxi +2 -4
- pyscipopt/benderscut.pxi +1 -2
- pyscipopt/conshdlr.pxi +5 -10
- pyscipopt/event.pxi +1 -2
- pyscipopt/expr.pxi +185 -42
- pyscipopt/heuristic.pxi +1 -2
- pyscipopt/iisfinder.pxi +1 -0
- pyscipopt/matrix.pxi +236 -93
- pyscipopt/presol.pxi +1 -2
- pyscipopt/propagator.pxi +2 -4
- pyscipopt/recipes/getLocalConss.py +1 -1
- pyscipopt/recipes/primal_dual_evolution.py +8 -15
- pyscipopt/recipes/structured_optimization_trace.py +37 -0
- pyscipopt/scip.cp312-win_amd64.pyd +0 -0
- pyscipopt/scip.pxd +21 -0
- pyscipopt/scip.pxi +433 -180
- pyscipopt/scip.pyi +1760 -843
- {pyscipopt-6.0.0.dist-info → pyscipopt-6.1.0.dist-info}/DELVEWHEEL +2 -2
- {pyscipopt-6.0.0.dist-info → pyscipopt-6.1.0.dist-info}/METADATA +22 -2
- pyscipopt-6.1.0.dist-info/RECORD +48 -0
- {pyscipopt-6.0.0.dist-info → pyscipopt-6.1.0.dist-info}/WHEEL +1 -1
- pyscipopt-6.1.0.dist-info/licenses/LICENSE +10 -0
- pyscipopt.libs/{libscip-6dd0e0d45ccf4b75dbc52a123f9bf2e4.dll → libscip-1a4d5a21deba20278cffa088924e6323.dll} +0 -0
- pyscipopt-6.0.0.dist-info/RECORD +0 -47
- pyscipopt-6.0.0.dist-info/licenses/LICENSE +0 -21
- {pyscipopt-6.0.0.dist-info → pyscipopt-6.1.0.dist-info}/top_level.txt +0 -0
pyscipopt/__init__.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""""" # start delvewheel patch
|
|
2
|
-
def
|
|
2
|
+
def _delvewheel_patch_1_12_0():
|
|
3
3
|
import os
|
|
4
4
|
if os.path.isdir(libs_dir := os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, 'pyscipopt.libs'))):
|
|
5
5
|
os.add_dll_directory(libs_dir)
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
del
|
|
8
|
+
_delvewheel_patch_1_12_0()
|
|
9
|
+
del _delvewheel_patch_1_12_0
|
|
10
10
|
# end delvewheel patch
|
|
11
11
|
|
|
12
12
|
from ._version import __version__
|
|
@@ -15,7 +15,7 @@ from ._version import __version__
|
|
|
15
15
|
import os
|
|
16
16
|
if hasattr(os, 'add_dll_directory'):
|
|
17
17
|
if os.getenv('SCIPOPTDIR'):
|
|
18
|
-
os.add_dll_directory(os.path.join(os.
|
|
18
|
+
os.add_dll_directory(os.path.join(os.environ['SCIPOPTDIR'].strip('"'), 'bin'))
|
|
19
19
|
|
|
20
20
|
# export user-relevant objects:
|
|
21
21
|
from pyscipopt.Multidict import multidict as multidict
|
pyscipopt/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__: str = '6.
|
|
1
|
+
__version__: str = '6.1.0'
|
pyscipopt/benders.pxi
CHANGED
|
@@ -35,8 +35,7 @@ cdef class Benders:
|
|
|
35
35
|
|
|
36
36
|
def benderscreatesub(self, probnumber):
|
|
37
37
|
'''creates the subproblems and registers it with the Benders decomposition struct '''
|
|
38
|
-
|
|
39
|
-
return {}
|
|
38
|
+
raise NotImplementedError("benderscreatesub() is a fundamental callback and should be implemented in the derived class")
|
|
40
39
|
|
|
41
40
|
def benderspresubsolve(self, solution, enfotype, checkint):
|
|
42
41
|
'''sets the pre subproblem solve callback of Benders decomposition '''
|
|
@@ -60,8 +59,7 @@ cdef class Benders:
|
|
|
60
59
|
|
|
61
60
|
def bendersgetvar(self, variable, probnumber):
|
|
62
61
|
'''Returns the corresponding master or subproblem variable for the given variable. This provides a call back for the variable mapping between the master and subproblems. '''
|
|
63
|
-
|
|
64
|
-
return {}
|
|
62
|
+
raise NotImplementedError("bendersgetvar() is a fundamental callback and should be implemented in the derived class")
|
|
65
63
|
|
|
66
64
|
# local helper functions for the interface
|
|
67
65
|
cdef Variable getPyVar(SCIP_VAR* var):
|
pyscipopt/benderscut.pxi
CHANGED
|
@@ -21,8 +21,7 @@ cdef class Benderscut:
|
|
|
21
21
|
pass
|
|
22
22
|
|
|
23
23
|
def benderscutexec(self, solution, probnumber, enfotype):
|
|
24
|
-
|
|
25
|
-
return {}
|
|
24
|
+
raise NotImplementedError("benderscutexec() is a fundamental callback and should be implemented in the derived class")
|
|
26
25
|
|
|
27
26
|
cdef SCIP_RETCODE PyBenderscutCopy (SCIP* scip, SCIP_BENDERS* benders, SCIP_BENDERSCUT* benderscut) noexcept with gil:
|
|
28
27
|
return SCIP_OKAY
|
pyscipopt/conshdlr.pxi
CHANGED
|
@@ -55,23 +55,19 @@ cdef class Conshdlr:
|
|
|
55
55
|
|
|
56
56
|
def consenfolp(self, constraints, nusefulconss, solinfeasible):
|
|
57
57
|
'''calls enforcing method of constraint handler for LP solution for all constraints added'''
|
|
58
|
-
|
|
59
|
-
return {}
|
|
58
|
+
raise NotImplementedError("consenfolp() is a fundamental callback and should be implemented in the derived class")
|
|
60
59
|
|
|
61
60
|
def consenforelax(self, solution, constraints, nusefulconss, solinfeasible):
|
|
62
61
|
'''calls enforcing method of constraint handler for a relaxation solution for all constraints added'''
|
|
63
|
-
|
|
64
|
-
return {}
|
|
62
|
+
raise NotImplementedError("consenforelax() is a fundamental callback and should be implemented in the derived class")
|
|
65
63
|
|
|
66
64
|
def consenfops(self, constraints, nusefulconss, solinfeasible, objinfeasible):
|
|
67
65
|
'''calls enforcing method of constraint handler for pseudo solution for all constraints added'''
|
|
68
|
-
|
|
69
|
-
return {}
|
|
66
|
+
raise NotImplementedError("consenfops() is a fundamental callback and should be implemented in the derived class")
|
|
70
67
|
|
|
71
68
|
def conscheck(self, constraints, solution, checkintegrality, checklprows, printreason, completely):
|
|
72
69
|
'''calls feasibility check method of constraint handler '''
|
|
73
|
-
|
|
74
|
-
return {}
|
|
70
|
+
raise NotImplementedError("conscheck() is a fundamental callback and should be implemented in the derived class")
|
|
75
71
|
|
|
76
72
|
def consprop(self, constraints, nusefulconss, nmarkedconss, proptiming):
|
|
77
73
|
'''calls propagation method of constraint handler '''
|
|
@@ -89,8 +85,7 @@ cdef class Conshdlr:
|
|
|
89
85
|
|
|
90
86
|
def conslock(self, constraint, locktype, nlockspos, nlocksneg):
|
|
91
87
|
'''variable rounding lock method of constraint handler'''
|
|
92
|
-
|
|
93
|
-
return {}
|
|
88
|
+
raise NotImplementedError("conslock() is a fundamental callback and should be implemented in the derived class")
|
|
94
89
|
|
|
95
90
|
def consactive(self, constraint):
|
|
96
91
|
'''sets activation notification method of constraint handler '''
|
pyscipopt/event.pxi
CHANGED
|
@@ -34,8 +34,7 @@ cdef class Eventhdlr:
|
|
|
34
34
|
|
|
35
35
|
def eventexec(self, event):
|
|
36
36
|
'''calls execution method of event handler '''
|
|
37
|
-
|
|
38
|
-
return {}
|
|
37
|
+
raise NotImplementedError("eventexec() is a fundamental callback and should be implemented in the derived class")
|
|
39
38
|
|
|
40
39
|
|
|
41
40
|
# local helper functions for the interface
|
pyscipopt/expr.pxi
CHANGED
|
@@ -42,7 +42,20 @@
|
|
|
42
42
|
# which should, in princple, modify the expr. However, since we do not implement __isub__, __sub__
|
|
43
43
|
# gets called (I guess) and so a copy is returned.
|
|
44
44
|
# Modifying the expression directly would be a bug, given that the expression might be re-used by the user. </pre>
|
|
45
|
-
|
|
45
|
+
import math
|
|
46
|
+
from typing import TYPE_CHECKING
|
|
47
|
+
|
|
48
|
+
from cpython.dict cimport PyDict_Next, PyDict_GetItem
|
|
49
|
+
from cpython.object cimport Py_TYPE
|
|
50
|
+
from cpython.ref cimport PyObject
|
|
51
|
+
from cpython.tuple cimport PyTuple_GET_ITEM
|
|
52
|
+
from pyscipopt.scip cimport Variable, Solution
|
|
53
|
+
|
|
54
|
+
import numpy as np
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if TYPE_CHECKING:
|
|
58
|
+
double = float
|
|
46
59
|
|
|
47
60
|
|
|
48
61
|
def _is_number(e):
|
|
@@ -61,7 +74,7 @@ def _expr_richcmp(self, other, op):
|
|
|
61
74
|
return (self - other) <= 0.0
|
|
62
75
|
elif _is_number(other):
|
|
63
76
|
return ExprCons(self, rhs=float(other))
|
|
64
|
-
elif isinstance(other,
|
|
77
|
+
elif isinstance(other, np.ndarray):
|
|
65
78
|
return _expr_richcmp(other, self, 5)
|
|
66
79
|
else:
|
|
67
80
|
raise TypeError(f"Unsupported type {type(other)}")
|
|
@@ -70,7 +83,7 @@ def _expr_richcmp(self, other, op):
|
|
|
70
83
|
return (self - other) >= 0.0
|
|
71
84
|
elif _is_number(other):
|
|
72
85
|
return ExprCons(self, lhs=float(other))
|
|
73
|
-
elif isinstance(other,
|
|
86
|
+
elif isinstance(other, np.ndarray):
|
|
74
87
|
return _expr_richcmp(other, self, 1)
|
|
75
88
|
else:
|
|
76
89
|
raise TypeError(f"Unsupported type {type(other)}")
|
|
@@ -79,7 +92,7 @@ def _expr_richcmp(self, other, op):
|
|
|
79
92
|
return (self - other) == 0.0
|
|
80
93
|
elif _is_number(other):
|
|
81
94
|
return ExprCons(self, lhs=float(other), rhs=float(other))
|
|
82
|
-
elif isinstance(other,
|
|
95
|
+
elif isinstance(other, np.ndarray):
|
|
83
96
|
return _expr_richcmp(other, self, 2)
|
|
84
97
|
else:
|
|
85
98
|
raise TypeError(f"Unsupported type {type(other)}")
|
|
@@ -87,35 +100,83 @@ def _expr_richcmp(self, other, op):
|
|
|
87
100
|
raise NotImplementedError("Can only support constraints with '<=', '>=', or '=='.")
|
|
88
101
|
|
|
89
102
|
|
|
90
|
-
class Term:
|
|
103
|
+
cdef class Term:
|
|
91
104
|
'''This is a monomial term'''
|
|
92
105
|
|
|
93
|
-
|
|
106
|
+
cdef readonly tuple vartuple
|
|
107
|
+
cdef readonly tuple ptrtuple
|
|
108
|
+
cdef Py_ssize_t hashval
|
|
94
109
|
|
|
95
|
-
def __init__(self, *vartuple):
|
|
110
|
+
def __init__(self, *vartuple: Variable):
|
|
96
111
|
self.vartuple = tuple(sorted(vartuple, key=lambda v: v.ptr()))
|
|
97
112
|
self.ptrtuple = tuple(v.ptr() for v in self.vartuple)
|
|
98
|
-
self.hashval =
|
|
113
|
+
self.hashval = <Py_ssize_t>hash(self.ptrtuple)
|
|
99
114
|
|
|
100
115
|
def __getitem__(self, idx):
|
|
101
116
|
return self.vartuple[idx]
|
|
102
117
|
|
|
103
|
-
def __hash__(self):
|
|
118
|
+
def __hash__(self) -> Py_ssize_t:
|
|
104
119
|
return self.hashval
|
|
105
120
|
|
|
106
|
-
def __eq__(self, other):
|
|
121
|
+
def __eq__(self, other: Term):
|
|
107
122
|
return self.ptrtuple == other.ptrtuple
|
|
108
123
|
|
|
109
124
|
def __len__(self):
|
|
110
125
|
return len(self.vartuple)
|
|
111
126
|
|
|
112
|
-
def
|
|
113
|
-
|
|
114
|
-
|
|
127
|
+
def __mul__(self, Term other):
|
|
128
|
+
# NOTE: This merge algorithm requires a sorted `Term.vartuple`.
|
|
129
|
+
# This should be ensured in the constructor of Term.
|
|
130
|
+
cdef int n1 = len(self)
|
|
131
|
+
cdef int n2 = len(other)
|
|
132
|
+
if n1 == 0: return other
|
|
133
|
+
if n2 == 0: return self
|
|
134
|
+
|
|
135
|
+
cdef list vartuple = [None] * (n1 + n2)
|
|
136
|
+
cdef int i = 0, j = 0, k = 0
|
|
137
|
+
cdef Variable var1, var2
|
|
138
|
+
while i < n1 and j < n2:
|
|
139
|
+
var1 = <Variable>PyTuple_GET_ITEM(self.vartuple, i)
|
|
140
|
+
var2 = <Variable>PyTuple_GET_ITEM(other.vartuple, j)
|
|
141
|
+
if var1.ptr() <= var2.ptr():
|
|
142
|
+
vartuple[k] = var1
|
|
143
|
+
i += 1
|
|
144
|
+
else:
|
|
145
|
+
vartuple[k] = var2
|
|
146
|
+
j += 1
|
|
147
|
+
k += 1
|
|
148
|
+
while i < n1:
|
|
149
|
+
vartuple[k] = <Variable>PyTuple_GET_ITEM(self.vartuple, i)
|
|
150
|
+
i += 1
|
|
151
|
+
k += 1
|
|
152
|
+
while j < n2:
|
|
153
|
+
vartuple[k] = <Variable>PyTuple_GET_ITEM(other.vartuple, j)
|
|
154
|
+
j += 1
|
|
155
|
+
k += 1
|
|
156
|
+
|
|
157
|
+
cdef Term res = Term.__new__(Term)
|
|
158
|
+
res.vartuple = tuple(vartuple)
|
|
159
|
+
res.ptrtuple = tuple(v.ptr() for v in res.vartuple)
|
|
160
|
+
res.hashval = <Py_ssize_t>hash(res.ptrtuple)
|
|
161
|
+
return res
|
|
115
162
|
|
|
116
163
|
def __repr__(self):
|
|
117
164
|
return 'Term(%s)' % ', '.join([str(v) for v in self.vartuple])
|
|
118
165
|
|
|
166
|
+
cpdef double _evaluate(self, Solution sol) except *:
|
|
167
|
+
cdef double res = 1.0
|
|
168
|
+
cdef SCIP* scip_ptr = sol.scip
|
|
169
|
+
cdef SCIP_SOL* sol_ptr = sol.sol
|
|
170
|
+
cdef int i = 0, n = len(self)
|
|
171
|
+
cdef Variable var
|
|
172
|
+
|
|
173
|
+
for i in range(n):
|
|
174
|
+
var = <Variable>self.vartuple[i]
|
|
175
|
+
res *= SCIPgetSolVal(scip_ptr, sol_ptr, var.scip_var)
|
|
176
|
+
if res == 0: # early stop
|
|
177
|
+
return 0.0
|
|
178
|
+
return res
|
|
179
|
+
|
|
119
180
|
|
|
120
181
|
CONST = Term()
|
|
121
182
|
|
|
@@ -144,7 +205,7 @@ def buildGenExprObj(expr):
|
|
|
144
205
|
sumexpr += coef * prodexpr
|
|
145
206
|
return sumexpr
|
|
146
207
|
|
|
147
|
-
elif isinstance(expr,
|
|
208
|
+
elif isinstance(expr, np.ndarray):
|
|
148
209
|
GenExprs = np.empty(expr.shape, dtype=object)
|
|
149
210
|
for idx in np.ndindex(expr.shape):
|
|
150
211
|
GenExprs[idx] = buildGenExprObj(expr[idx])
|
|
@@ -157,7 +218,7 @@ def buildGenExprObj(expr):
|
|
|
157
218
|
##@details Polynomial expressions of variables with operator overloading. \n
|
|
158
219
|
#See also the @ref ExprDetails "description" in the expr.pxi.
|
|
159
220
|
cdef class Expr:
|
|
160
|
-
|
|
221
|
+
|
|
161
222
|
def __init__(self, terms=None):
|
|
162
223
|
'''terms is a dict of variables to coefficients.
|
|
163
224
|
|
|
@@ -175,20 +236,12 @@ cdef class Expr:
|
|
|
175
236
|
def __iter__(self):
|
|
176
237
|
return iter(self.terms)
|
|
177
238
|
|
|
178
|
-
def __next__(self):
|
|
179
|
-
try: return next(self.terms)
|
|
180
|
-
except: raise StopIteration
|
|
181
|
-
|
|
182
239
|
def __abs__(self):
|
|
183
240
|
return abs(buildGenExprObj(self))
|
|
184
241
|
|
|
185
242
|
def __add__(self, other):
|
|
186
243
|
left = self
|
|
187
244
|
right = other
|
|
188
|
-
|
|
189
|
-
if _is_number(self):
|
|
190
|
-
assert isinstance(other, Expr)
|
|
191
|
-
left,right = right,left
|
|
192
245
|
terms = left.terms.copy()
|
|
193
246
|
|
|
194
247
|
if isinstance(right, Expr):
|
|
@@ -200,7 +253,7 @@ cdef class Expr:
|
|
|
200
253
|
terms[CONST] = terms.get(CONST, 0.0) + c
|
|
201
254
|
elif isinstance(right, GenExpr):
|
|
202
255
|
return buildGenExprObj(left) + right
|
|
203
|
-
elif isinstance(right,
|
|
256
|
+
elif isinstance(right, np.ndarray):
|
|
204
257
|
return right + left
|
|
205
258
|
else:
|
|
206
259
|
raise TypeError(f"Unsupported type {type(right)}")
|
|
@@ -225,22 +278,35 @@ cdef class Expr:
|
|
|
225
278
|
return self
|
|
226
279
|
|
|
227
280
|
def __mul__(self, other):
|
|
228
|
-
if isinstance(other,
|
|
281
|
+
if isinstance(other, np.ndarray):
|
|
229
282
|
return other * self
|
|
230
283
|
|
|
284
|
+
cdef dict res = {}
|
|
285
|
+
cdef Py_ssize_t pos1 = <Py_ssize_t>0, pos2 = <Py_ssize_t>0
|
|
286
|
+
cdef PyObject *k1_ptr = NULL
|
|
287
|
+
cdef PyObject *v1_ptr = NULL
|
|
288
|
+
cdef PyObject *k2_ptr = NULL
|
|
289
|
+
cdef PyObject *v2_ptr = NULL
|
|
290
|
+
cdef PyObject *old_v_ptr = NULL
|
|
291
|
+
cdef Term child
|
|
292
|
+
cdef double prod_v
|
|
293
|
+
|
|
231
294
|
if _is_number(other):
|
|
232
295
|
f = float(other)
|
|
233
296
|
return Expr({v:f*c for v,c in self.terms.items()})
|
|
234
|
-
|
|
235
|
-
f = float(self)
|
|
236
|
-
return Expr({v:f*c for v,c in other.terms.items()})
|
|
297
|
+
|
|
237
298
|
elif isinstance(other, Expr):
|
|
238
|
-
terms
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
299
|
+
while PyDict_Next(self.terms, &pos1, &k1_ptr, &v1_ptr):
|
|
300
|
+
pos2 = <Py_ssize_t>0
|
|
301
|
+
while PyDict_Next(other.terms, &pos2, &k2_ptr, &v2_ptr):
|
|
302
|
+
child = (<Term>k1_ptr) * (<Term>k2_ptr)
|
|
303
|
+
prod_v = (<double>(<object>v1_ptr)) * (<double>(<object>v2_ptr))
|
|
304
|
+
if (old_v_ptr := PyDict_GetItem(res, child)) != NULL:
|
|
305
|
+
res[child] = <double>(<object>old_v_ptr) + prod_v
|
|
306
|
+
else:
|
|
307
|
+
res[child] = prod_v
|
|
308
|
+
return Expr(res)
|
|
309
|
+
|
|
244
310
|
elif isinstance(other, GenExpr):
|
|
245
311
|
return buildGenExprObj(self) * other
|
|
246
312
|
else:
|
|
@@ -255,11 +321,7 @@ cdef class Expr:
|
|
|
255
321
|
|
|
256
322
|
def __rtruediv__(self, other):
|
|
257
323
|
''' other / self '''
|
|
258
|
-
|
|
259
|
-
f = 1.0/float(self)
|
|
260
|
-
return f * other
|
|
261
|
-
otherexpr = buildGenExprObj(other)
|
|
262
|
-
return otherexpr.__truediv__(self)
|
|
324
|
+
return buildGenExprObj(other) / self
|
|
263
325
|
|
|
264
326
|
def __pow__(self, other, modulo):
|
|
265
327
|
if float(other).is_integer() and other >= 0:
|
|
@@ -318,6 +380,20 @@ cdef class Expr:
|
|
|
318
380
|
else:
|
|
319
381
|
return max(len(v) for v in self.terms)
|
|
320
382
|
|
|
383
|
+
cpdef double _evaluate(self, Solution sol) except *:
|
|
384
|
+
cdef double res = 0
|
|
385
|
+
cdef Py_ssize_t pos = <Py_ssize_t>0
|
|
386
|
+
cdef PyObject* key_ptr
|
|
387
|
+
cdef PyObject* val_ptr
|
|
388
|
+
cdef Term term
|
|
389
|
+
cdef double coef
|
|
390
|
+
|
|
391
|
+
while PyDict_Next(self.terms, &pos, &key_ptr, &val_ptr):
|
|
392
|
+
term = <Term>key_ptr
|
|
393
|
+
coef = <double>(<object>val_ptr)
|
|
394
|
+
res += coef * term._evaluate(sol)
|
|
395
|
+
return res
|
|
396
|
+
|
|
321
397
|
|
|
322
398
|
cdef class ExprCons:
|
|
323
399
|
'''Constraints with a polynomial expressions and lower/upper bounds.'''
|
|
@@ -427,10 +503,10 @@ Operator = Op()
|
|
|
427
503
|
#
|
|
428
504
|
#See also the @ref ExprDetails "description" in the expr.pxi.
|
|
429
505
|
cdef class GenExpr:
|
|
506
|
+
|
|
430
507
|
cdef public _op
|
|
431
508
|
cdef public children
|
|
432
509
|
|
|
433
|
-
|
|
434
510
|
def __init__(self): # do we need it
|
|
435
511
|
''' '''
|
|
436
512
|
|
|
@@ -438,7 +514,7 @@ cdef class GenExpr:
|
|
|
438
514
|
return UnaryExpr(Operator.fabs, self)
|
|
439
515
|
|
|
440
516
|
def __add__(self, other):
|
|
441
|
-
if isinstance(other,
|
|
517
|
+
if isinstance(other, np.ndarray):
|
|
442
518
|
return other + self
|
|
443
519
|
|
|
444
520
|
left = buildGenExprObj(self)
|
|
@@ -496,7 +572,7 @@ cdef class GenExpr:
|
|
|
496
572
|
# return self
|
|
497
573
|
|
|
498
574
|
def __mul__(self, other):
|
|
499
|
-
if isinstance(other,
|
|
575
|
+
if isinstance(other, np.ndarray):
|
|
500
576
|
return other * self
|
|
501
577
|
|
|
502
578
|
left = buildGenExprObj(self)
|
|
@@ -610,6 +686,20 @@ cdef class GenExpr:
|
|
|
610
686
|
'''returns operator of GenExpr'''
|
|
611
687
|
return self._op
|
|
612
688
|
|
|
689
|
+
cdef GenExpr copy(self, bool copy = True):
|
|
690
|
+
cdef object cls = <type>Py_TYPE(self)
|
|
691
|
+
cdef GenExpr res = cls.__new__(cls)
|
|
692
|
+
res._op = self._op
|
|
693
|
+
res.children = self.children.copy() if copy else self.children
|
|
694
|
+
if cls is SumExpr:
|
|
695
|
+
(<SumExpr>res).constant = (<SumExpr>self).constant
|
|
696
|
+
(<SumExpr>res).coefs = (<SumExpr>self).coefs.copy() if copy else (<SumExpr>self).coefs
|
|
697
|
+
if cls is ProdExpr:
|
|
698
|
+
(<ProdExpr>res).constant = (<ProdExpr>self).constant
|
|
699
|
+
elif cls is PowExpr:
|
|
700
|
+
(<PowExpr>res).expo = (<PowExpr>self).expo
|
|
701
|
+
return res
|
|
702
|
+
|
|
613
703
|
|
|
614
704
|
# Sum Expressions
|
|
615
705
|
cdef class SumExpr(GenExpr):
|
|
@@ -625,44 +715,93 @@ cdef class SumExpr(GenExpr):
|
|
|
625
715
|
def __repr__(self):
|
|
626
716
|
return self._op + "(" + str(self.constant) + "," + ",".join(map(lambda child : child.__repr__(), self.children)) + ")"
|
|
627
717
|
|
|
718
|
+
cpdef double _evaluate(self, Solution sol) except *:
|
|
719
|
+
cdef double res = self.constant
|
|
720
|
+
cdef int i = 0, n = len(self.children)
|
|
721
|
+
cdef list children = self.children
|
|
722
|
+
cdef list coefs = self.coefs
|
|
723
|
+
for i in range(n):
|
|
724
|
+
res += <double>coefs[i] * (<GenExpr>children[i])._evaluate(sol)
|
|
725
|
+
return res
|
|
726
|
+
|
|
727
|
+
|
|
628
728
|
# Prod Expressions
|
|
629
729
|
cdef class ProdExpr(GenExpr):
|
|
730
|
+
|
|
630
731
|
cdef public constant
|
|
732
|
+
|
|
631
733
|
def __init__(self):
|
|
632
734
|
self.constant = 1.0
|
|
633
735
|
self.children = []
|
|
634
736
|
self._op = Operator.prod
|
|
737
|
+
|
|
635
738
|
def __repr__(self):
|
|
636
739
|
return self._op + "(" + str(self.constant) + "," + ",".join(map(lambda child : child.__repr__(), self.children)) + ")"
|
|
637
740
|
|
|
741
|
+
cpdef double _evaluate(self, Solution sol) except *:
|
|
742
|
+
cdef double res = self.constant
|
|
743
|
+
cdef list children = self.children
|
|
744
|
+
cdef int i = 0, n = len(children)
|
|
745
|
+
for i in range(n):
|
|
746
|
+
res *= (<GenExpr>children[i])._evaluate(sol)
|
|
747
|
+
if res == 0: # early stop
|
|
748
|
+
return 0.0
|
|
749
|
+
return res
|
|
750
|
+
|
|
751
|
+
|
|
638
752
|
# Var Expressions
|
|
639
753
|
cdef class VarExpr(GenExpr):
|
|
754
|
+
|
|
640
755
|
cdef public var
|
|
756
|
+
|
|
641
757
|
def __init__(self, var):
|
|
642
758
|
self.children = [var]
|
|
643
759
|
self._op = Operator.varidx
|
|
760
|
+
|
|
644
761
|
def __repr__(self):
|
|
645
762
|
return self.children[0].__repr__()
|
|
646
763
|
|
|
764
|
+
cpdef double _evaluate(self, Solution sol) except *:
|
|
765
|
+
return (<Expr>self.children[0])._evaluate(sol)
|
|
766
|
+
|
|
767
|
+
|
|
647
768
|
# Pow Expressions
|
|
648
769
|
cdef class PowExpr(GenExpr):
|
|
770
|
+
|
|
649
771
|
cdef public expo
|
|
772
|
+
|
|
650
773
|
def __init__(self):
|
|
651
774
|
self.expo = 1.0
|
|
652
775
|
self.children = []
|
|
653
776
|
self._op = Operator.power
|
|
777
|
+
|
|
654
778
|
def __repr__(self):
|
|
655
779
|
return self._op + "(" + self.children[0].__repr__() + "," + str(self.expo) + ")"
|
|
656
780
|
|
|
781
|
+
cpdef double _evaluate(self, Solution sol) except *:
|
|
782
|
+
return (<GenExpr>self.children[0])._evaluate(sol) ** self.expo
|
|
783
|
+
|
|
784
|
+
|
|
657
785
|
# Exp, Log, Sqrt, Sin, Cos Expressions
|
|
658
786
|
cdef class UnaryExpr(GenExpr):
|
|
659
787
|
def __init__(self, op, expr):
|
|
660
788
|
self.children = []
|
|
661
789
|
self.children.append(expr)
|
|
662
790
|
self._op = op
|
|
791
|
+
|
|
792
|
+
def __abs__(self) -> UnaryExpr:
|
|
793
|
+
if self._op == "abs":
|
|
794
|
+
return <UnaryExpr>self.copy()
|
|
795
|
+
return UnaryExpr(Operator.fabs, self)
|
|
796
|
+
|
|
663
797
|
def __repr__(self):
|
|
664
798
|
return self._op + "(" + self.children[0].__repr__() + ")"
|
|
665
799
|
|
|
800
|
+
cpdef double _evaluate(self, Solution sol) except *:
|
|
801
|
+
cdef double res = (<GenExpr>self.children[0])._evaluate(sol)
|
|
802
|
+
return math.fabs(res) if self._op == "abs" else getattr(math, self._op)(res)
|
|
803
|
+
|
|
804
|
+
|
|
666
805
|
# class for constant expressions
|
|
667
806
|
cdef class Constant(GenExpr):
|
|
668
807
|
cdef public number
|
|
@@ -673,6 +812,10 @@ cdef class Constant(GenExpr):
|
|
|
673
812
|
def __repr__(self):
|
|
674
813
|
return str(self.number)
|
|
675
814
|
|
|
815
|
+
cpdef double _evaluate(self, Solution sol) except *:
|
|
816
|
+
return self.number
|
|
817
|
+
|
|
818
|
+
|
|
676
819
|
def exp(expr):
|
|
677
820
|
"""returns expression with exp-function"""
|
|
678
821
|
if isinstance(expr, MatrixExpr):
|
pyscipopt/heuristic.pxi
CHANGED
|
@@ -26,8 +26,7 @@ cdef class Heur:
|
|
|
26
26
|
|
|
27
27
|
def heurexec(self, heurtiming, nodeinfeasible):
|
|
28
28
|
'''should the heuristic the executed at the given depth, frequency, timing,...'''
|
|
29
|
-
|
|
30
|
-
return {}
|
|
29
|
+
raise NotImplementedError("heurexec() is a fundamental callback and should be implemented in the derived class")
|
|
31
30
|
|
|
32
31
|
|
|
33
32
|
|
pyscipopt/iisfinder.pxi
CHANGED
|
@@ -33,4 +33,5 @@ cdef SCIP_RETCODE PyiisfinderExec (SCIP_IIS* iis, SCIP_IISFINDER* iisfinder, SCI
|
|
|
33
33
|
PyIIS.iis._iis = iis
|
|
34
34
|
result_dict = PyIIS.iisfinderexec()
|
|
35
35
|
assert isinstance(result_dict, dict), "iisfinderexec() must return a dictionary."
|
|
36
|
+
result[0] = result_dict.get("result", <SCIP_RESULT>result[0])
|
|
36
37
|
return SCIP_OKAY
|