PySCIPOpt 6.0.0__cp39-cp39-win_amd64.whl → 6.1.0__cp39-cp39-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 +6 -6
- 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.cp39-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 +49 -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/{.load-order-pyscipopt-6.0.0 → .load-order-pyscipopt-6.1.0} +4 -4
- pyscipopt.libs/{libscip-6dd0e0d45ccf4b75dbc52a123f9bf2e4.dll → libscip-1a4d5a21deba20278cffa088924e6323.dll} +0 -0
- pyscipopt-6.0.0.dist-info/RECORD +0 -48
- 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/scip.pxi
CHANGED
|
@@ -1068,8 +1068,8 @@ cdef class Solution:
|
|
|
1068
1068
|
"""Base class holding a pointer to corresponding SCIP_SOL."""
|
|
1069
1069
|
|
|
1070
1070
|
# We are raising an error here to avoid creating a solution without an associated model. See Issue #625
|
|
1071
|
-
def __init__(self, raise_error =
|
|
1072
|
-
if
|
|
1071
|
+
def __init__(self, raise_error = True):
|
|
1072
|
+
if raise_error:
|
|
1073
1073
|
raise ValueError("To create a solution you should use the createSol method of the Model class.")
|
|
1074
1074
|
|
|
1075
1075
|
@staticmethod
|
|
@@ -1093,35 +1093,23 @@ cdef class Solution:
|
|
|
1093
1093
|
"""
|
|
1094
1094
|
if scip == NULL:
|
|
1095
1095
|
raise Warning("cannot create Solution with SCIP* == NULL")
|
|
1096
|
-
sol = Solution(
|
|
1096
|
+
sol = Solution(raise_error=False)
|
|
1097
1097
|
sol.sol = scip_sol
|
|
1098
1098
|
sol.scip = scip
|
|
1099
1099
|
return sol
|
|
1100
1100
|
|
|
1101
|
-
def __getitem__(
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
cdef _VarArray wrapper
|
|
1111
|
-
if isinstance(expr, Variable):
|
|
1112
|
-
wrapper = _VarArray(expr)
|
|
1113
|
-
self._checkStage("SCIPgetSolVal")
|
|
1114
|
-
return SCIPgetSolVal(self.scip, self.sol, wrapper.ptr[0])
|
|
1115
|
-
return sum(self._evaluate(term)*coeff for term, coeff in expr.terms.items() if coeff != 0)
|
|
1101
|
+
def __getitem__(
|
|
1102
|
+
self,
|
|
1103
|
+
expr: Union[Expr, GenExpr, MatrixExpr],
|
|
1104
|
+
) -> Union[float, np.ndarray]:
|
|
1105
|
+
if not isinstance(expr, (Expr, GenExpr, MatrixExpr)):
|
|
1106
|
+
raise TypeError(
|
|
1107
|
+
"Argument 'expr' has incorrect type, expected 'Expr', 'GenExpr', or "
|
|
1108
|
+
f"'MatrixExpr', got {type(expr).__name__!r}"
|
|
1109
|
+
)
|
|
1116
1110
|
|
|
1117
|
-
def _evaluate(self, term):
|
|
1118
1111
|
self._checkStage("SCIPgetSolVal")
|
|
1119
|
-
|
|
1120
|
-
cdef _VarArray wrapper
|
|
1121
|
-
wrapper = _VarArray(term.vartuple)
|
|
1122
|
-
for i in range(len(term.vartuple)):
|
|
1123
|
-
result *= SCIPgetSolVal(self.scip, self.sol, wrapper.ptr[i])
|
|
1124
|
-
return result
|
|
1112
|
+
return expr._evaluate(self)
|
|
1125
1113
|
|
|
1126
1114
|
def __setitem__(self, Variable var, value):
|
|
1127
1115
|
PY_SCIP_CALL(SCIPsetSolVal(self.scip, self.sol, var.scip_var, value))
|
|
@@ -1565,6 +1553,8 @@ cdef class Variable(Expr):
|
|
|
1565
1553
|
|
|
1566
1554
|
property name:
|
|
1567
1555
|
def __get__(self):
|
|
1556
|
+
if self.scip_var == NULL:
|
|
1557
|
+
return ""
|
|
1568
1558
|
cname = bytes( SCIPvarGetName(self.scip_var) )
|
|
1569
1559
|
return cname.decode('utf-8')
|
|
1570
1560
|
|
|
@@ -1870,6 +1860,16 @@ cdef class Variable(Expr):
|
|
|
1870
1860
|
"""
|
|
1871
1861
|
return SCIPvarIsDeletable(self.scip_var)
|
|
1872
1862
|
|
|
1863
|
+
def isActive(self):
|
|
1864
|
+
"""
|
|
1865
|
+
Returns whether variable is an active (neither fixed nor aggregated) variable.
|
|
1866
|
+
|
|
1867
|
+
Returns
|
|
1868
|
+
-------
|
|
1869
|
+
boolean
|
|
1870
|
+
"""
|
|
1871
|
+
return SCIPvarIsActive(self.scip_var)
|
|
1872
|
+
|
|
1873
1873
|
def getNLocksDown(self):
|
|
1874
1874
|
"""
|
|
1875
1875
|
Returns the number of locks for rounding down.
|
|
@@ -2213,9 +2213,14 @@ cdef class Constraint:
|
|
|
2213
2213
|
|
|
2214
2214
|
property name:
|
|
2215
2215
|
def __get__(self):
|
|
2216
|
+
if self.scip_cons == NULL:
|
|
2217
|
+
return ""
|
|
2216
2218
|
cname = bytes( SCIPconsGetName(self.scip_cons) )
|
|
2217
2219
|
return cname.decode('utf-8')
|
|
2218
2220
|
|
|
2221
|
+
def ptr(self):
|
|
2222
|
+
return <size_t>(self.scip_cons)
|
|
2223
|
+
|
|
2219
2224
|
def __repr__(self):
|
|
2220
2225
|
return self.name
|
|
2221
2226
|
|
|
@@ -2780,7 +2785,7 @@ cdef class IIS:
|
|
|
2780
2785
|
##
|
|
2781
2786
|
cdef class Model:
|
|
2782
2787
|
|
|
2783
|
-
def __init__(self, problemName='model', defaultPlugins=True, Model sourceModel=None, origcopy=False, globalcopy=True, enablepricing=
|
|
2788
|
+
def __init__(self, problemName='model', defaultPlugins=True, Model sourceModel=None, origcopy=False, globalcopy=True, enablepricing=True, createscip=True, threadsafe=False):
|
|
2784
2789
|
"""
|
|
2785
2790
|
Main class holding a pointer to SCIP for managing most interactions
|
|
2786
2791
|
|
|
@@ -2797,7 +2802,7 @@ cdef class Model:
|
|
|
2797
2802
|
globalcopy : bool, optional
|
|
2798
2803
|
whether to create a global or a local copy (default True)
|
|
2799
2804
|
enablepricing : bool, optional
|
|
2800
|
-
whether to enable pricing in copy (default
|
|
2805
|
+
whether to enable pricing in copy (default True)
|
|
2801
2806
|
createscip : bool, optional
|
|
2802
2807
|
initialize the Model object and creates a SCIP instance (default True)
|
|
2803
2808
|
threadsafe : bool, optional
|
|
@@ -2813,6 +2818,7 @@ cdef class Model:
|
|
|
2813
2818
|
|
|
2814
2819
|
self._freescip = True
|
|
2815
2820
|
self._modelvars = {}
|
|
2821
|
+
self._modelconss = {}
|
|
2816
2822
|
self._generated_event_handlers_count = 0
|
|
2817
2823
|
self._benders_subproblems = [] # Keep references to Benders subproblem Models
|
|
2818
2824
|
self._iis = NULL
|
|
@@ -2909,6 +2915,16 @@ cdef class Model:
|
|
|
2909
2915
|
# Clear the references to allow Python GC to clean up the Model objects
|
|
2910
2916
|
self._benders_subproblems = []
|
|
2911
2917
|
|
|
2918
|
+
# Invalidate all variable and constraint pointers before freeing SCIP. See issue #604.
|
|
2919
|
+
if self._modelvars:
|
|
2920
|
+
for var in self._modelvars.values():
|
|
2921
|
+
(<Variable>var).scip_var = NULL
|
|
2922
|
+
self._modelvars = {}
|
|
2923
|
+
if self._modelconss:
|
|
2924
|
+
for cons in self._modelconss.values():
|
|
2925
|
+
(<Constraint>cons).scip_cons = NULL
|
|
2926
|
+
self._modelconss = {}
|
|
2927
|
+
|
|
2912
2928
|
PY_SCIP_CALL( SCIPfree(&self._scip) )
|
|
2913
2929
|
|
|
2914
2930
|
def __hash__(self):
|
|
@@ -2941,6 +2957,24 @@ cdef class Model:
|
|
|
2941
2957
|
model._benders_subproblems = [] # Initialize Benders subproblems list
|
|
2942
2958
|
return model
|
|
2943
2959
|
|
|
2960
|
+
cdef _getOrCreateCons(self, SCIP_CONS* scip_cons):
|
|
2961
|
+
"""Get existing Constraint wrapper or create and track a new one."""
|
|
2962
|
+
cdef size_t ptr = <size_t>scip_cons
|
|
2963
|
+
if ptr in self._modelconss:
|
|
2964
|
+
return self._modelconss[ptr]
|
|
2965
|
+
pyCons = Constraint.create(scip_cons)
|
|
2966
|
+
self._modelconss[ptr] = pyCons
|
|
2967
|
+
return pyCons
|
|
2968
|
+
|
|
2969
|
+
cdef _getOrCreateVar(self, SCIP_VAR* scip_var):
|
|
2970
|
+
"""Get existing Variable wrapper or create and track a new one."""
|
|
2971
|
+
cdef size_t ptr = <size_t>scip_var
|
|
2972
|
+
if ptr in self._modelvars:
|
|
2973
|
+
return self._modelvars[ptr]
|
|
2974
|
+
pyVar = Variable.create(scip_var)
|
|
2975
|
+
self._modelvars[ptr] = pyVar
|
|
2976
|
+
return pyVar
|
|
2977
|
+
|
|
2944
2978
|
@property
|
|
2945
2979
|
def _freescip(self):
|
|
2946
2980
|
"""
|
|
@@ -3048,11 +3082,20 @@ cdef class Model:
|
|
|
3048
3082
|
SCIP_STAGE_SOLVED]:
|
|
3049
3083
|
raise Warning("method cannot be called in stage %i." % self.getStage())
|
|
3050
3084
|
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
if
|
|
3055
|
-
|
|
3085
|
+
# Invalidate transformed variables. See issue #604.
|
|
3086
|
+
origvars = {ptr: var for ptr, var in self._modelvars.items() if var.isOriginal()}
|
|
3087
|
+
for ptr, var in self._modelvars.items():
|
|
3088
|
+
if ptr not in origvars:
|
|
3089
|
+
(<Variable>var).scip_var = NULL
|
|
3090
|
+
self._modelvars = origvars
|
|
3091
|
+
|
|
3092
|
+
# Invalidate transformed constraints. See issue #604.
|
|
3093
|
+
origconss = {ptr: cons for ptr, cons in self._modelconss.items() if cons.isOriginal()}
|
|
3094
|
+
for ptr, cons in self._modelconss.items():
|
|
3095
|
+
if ptr not in origconss:
|
|
3096
|
+
(<Constraint>cons).scip_cons = NULL
|
|
3097
|
+
self._modelconss = origconss
|
|
3098
|
+
|
|
3056
3099
|
PY_SCIP_CALL(SCIPfreeTransform(self._scip))
|
|
3057
3100
|
|
|
3058
3101
|
def version(self):
|
|
@@ -3361,6 +3404,16 @@ cdef class Model:
|
|
|
3361
3404
|
"""
|
|
3362
3405
|
return SCIPgetNStrongbranchLPIterations(self._scip)
|
|
3363
3406
|
|
|
3407
|
+
def getPrimalDualIntegral(self):
|
|
3408
|
+
"""
|
|
3409
|
+
Recomputes and returns the primal dual gap stored in the stats
|
|
3410
|
+
|
|
3411
|
+
Returns
|
|
3412
|
+
------
|
|
3413
|
+
float
|
|
3414
|
+
"""
|
|
3415
|
+
return SCIPgetPrimalDualIntegral(self._scip)
|
|
3416
|
+
|
|
3364
3417
|
def cutoffNode(self, Node node):
|
|
3365
3418
|
"""
|
|
3366
3419
|
marks node and whole subtree to be cut off from the branch and bound tree.
|
|
@@ -3569,6 +3622,62 @@ cdef class Model:
|
|
|
3569
3622
|
"""
|
|
3570
3623
|
return SCIPisFeasIntegral(self._scip, value)
|
|
3571
3624
|
|
|
3625
|
+
def isIntegral(self, value):
|
|
3626
|
+
"""
|
|
3627
|
+
Returns whether value is integral within epsilon tolerance.
|
|
3628
|
+
|
|
3629
|
+
Parameters
|
|
3630
|
+
----------
|
|
3631
|
+
value : float
|
|
3632
|
+
value to check
|
|
3633
|
+
|
|
3634
|
+
Returns
|
|
3635
|
+
-------
|
|
3636
|
+
bool
|
|
3637
|
+
|
|
3638
|
+
"""
|
|
3639
|
+
return SCIPisIntegral(self._scip, value)
|
|
3640
|
+
|
|
3641
|
+
def adjustedVarLb(self, Variable var, lb):
|
|
3642
|
+
"""
|
|
3643
|
+
Returns the adjusted (i.e. rounded, if the given variable is of integral type) lower bound value;
|
|
3644
|
+
does not change the bounds of the variable.
|
|
3645
|
+
|
|
3646
|
+
Parameters
|
|
3647
|
+
----------
|
|
3648
|
+
var : Variable
|
|
3649
|
+
variable for which the bound is adjusted
|
|
3650
|
+
lb : float
|
|
3651
|
+
lower bound value to adjust
|
|
3652
|
+
|
|
3653
|
+
Returns
|
|
3654
|
+
-------
|
|
3655
|
+
float
|
|
3656
|
+
adjusted lower bound
|
|
3657
|
+
|
|
3658
|
+
"""
|
|
3659
|
+
return SCIPadjustedVarLb(self._scip, var.scip_var, lb)
|
|
3660
|
+
|
|
3661
|
+
def adjustedVarUb(self, Variable var, ub):
|
|
3662
|
+
"""
|
|
3663
|
+
Returns the adjusted (i.e. rounded, if the given variable is of integral type) upper bound value;
|
|
3664
|
+
does not change the bounds of the variable.
|
|
3665
|
+
|
|
3666
|
+
Parameters
|
|
3667
|
+
----------
|
|
3668
|
+
var : Variable
|
|
3669
|
+
variable for which the bound is adjusted
|
|
3670
|
+
ub : float
|
|
3671
|
+
upper bound value to adjust
|
|
3672
|
+
|
|
3673
|
+
Returns
|
|
3674
|
+
-------
|
|
3675
|
+
float
|
|
3676
|
+
adjusted upper bound
|
|
3677
|
+
|
|
3678
|
+
"""
|
|
3679
|
+
return SCIPadjustedVarUb(self._scip, var.scip_var, ub)
|
|
3680
|
+
|
|
3572
3681
|
def isEQ(self, val1, val2):
|
|
3573
3682
|
"""
|
|
3574
3683
|
Checks, if values are in range of epsilon.
|
|
@@ -3981,6 +4090,17 @@ cdef class Model:
|
|
|
3981
4090
|
"""
|
|
3982
4091
|
PY_SCIP_CALL(SCIPsetObjIntegral(self._scip))
|
|
3983
4092
|
|
|
4093
|
+
def isObjIntegral(self):
|
|
4094
|
+
"""
|
|
4095
|
+
Returns whether the objective function is integral.
|
|
4096
|
+
|
|
4097
|
+
Returns
|
|
4098
|
+
-------
|
|
4099
|
+
bool
|
|
4100
|
+
|
|
4101
|
+
"""
|
|
4102
|
+
return SCIPisObjIntegral(self._scip)
|
|
4103
|
+
|
|
3984
4104
|
def getLocalEstimate(self, original = False):
|
|
3985
4105
|
"""
|
|
3986
4106
|
Gets estimate of best primal solution w.r.t. original or transformed problem contained in current subtree.
|
|
@@ -4238,11 +4358,7 @@ cdef class Model:
|
|
|
4238
4358
|
else:
|
|
4239
4359
|
PY_SCIP_CALL(SCIPaddVar(self._scip, scip_var))
|
|
4240
4360
|
|
|
4241
|
-
pyVar =
|
|
4242
|
-
|
|
4243
|
-
# store variable in the model to avoid creating new python variable objects in getVars()
|
|
4244
|
-
assert not pyVar.ptr() in self._modelvars
|
|
4245
|
-
self._modelvars[pyVar.ptr()] = pyVar
|
|
4361
|
+
pyVar = self._getOrCreateVar(scip_var)
|
|
4246
4362
|
|
|
4247
4363
|
#setting the variable data
|
|
4248
4364
|
SCIPvarSetData(scip_var, <SCIP_VARDATA*>pyVar)
|
|
@@ -4372,8 +4488,7 @@ cdef class Model:
|
|
|
4372
4488
|
"""
|
|
4373
4489
|
cdef SCIP_VAR* _tvar
|
|
4374
4490
|
PY_SCIP_CALL(SCIPgetTransformedVar(self._scip, var.scip_var, &_tvar))
|
|
4375
|
-
|
|
4376
|
-
return Variable.create(_tvar)
|
|
4491
|
+
return self._getOrCreateVar(_tvar)
|
|
4377
4492
|
|
|
4378
4493
|
def addVarLocks(self, Variable var, int nlocksdown, int nlocksup):
|
|
4379
4494
|
"""
|
|
@@ -4449,11 +4564,72 @@ cdef class Model:
|
|
|
4449
4564
|
|
|
4450
4565
|
"""
|
|
4451
4566
|
cdef SCIP_Bool deleted
|
|
4452
|
-
|
|
4453
|
-
del self._modelvars[var.ptr()]
|
|
4567
|
+
del self._modelvars[var.ptr()]
|
|
4454
4568
|
PY_SCIP_CALL(SCIPdelVar(self._scip, var.scip_var, &deleted))
|
|
4569
|
+
# Invalidate pointer after deletion. See issue #604.
|
|
4570
|
+
var.scip_var = NULL
|
|
4455
4571
|
return deleted
|
|
4456
4572
|
|
|
4573
|
+
def aggregateVars(self, Variable varx, Variable vary, coefx=1.0, coefy=-1.0, rhs=0.0):
|
|
4574
|
+
"""
|
|
4575
|
+
Aggregate two variables by adding an aggregation constraint.
|
|
4576
|
+
|
|
4577
|
+
The aggregation is defined by the linear equation:
|
|
4578
|
+
|
|
4579
|
+
coefx * varx + coefy * vary = rhs
|
|
4580
|
+
|
|
4581
|
+
After aggregation, varx becomes a redundant variable and vary remains active.
|
|
4582
|
+
The aggregation effectively substitutes varx with: (rhs - coefy * vary) / coefx
|
|
4583
|
+
|
|
4584
|
+
This method can only be called during presolving.
|
|
4585
|
+
|
|
4586
|
+
Parameters
|
|
4587
|
+
----------
|
|
4588
|
+
varx : Variable
|
|
4589
|
+
variable to be aggregated (will become redundant)
|
|
4590
|
+
vary : Variable
|
|
4591
|
+
variable to aggregate with (will remain active)
|
|
4592
|
+
coefx : float, optional
|
|
4593
|
+
coefficient for varx in the aggregation equation (default: 1.0)
|
|
4594
|
+
coefy : float, optional
|
|
4595
|
+
coefficient for vary in the aggregation equation (default: -1.0)
|
|
4596
|
+
rhs : float, optional
|
|
4597
|
+
right-hand side of the aggregation equation (default: 0.0)
|
|
4598
|
+
|
|
4599
|
+
Returns
|
|
4600
|
+
-------
|
|
4601
|
+
infeasible : bool
|
|
4602
|
+
whether the aggregation is infeasible (e.g., bounds are incompatible)
|
|
4603
|
+
redundant : bool
|
|
4604
|
+
whether the aggregation makes varx redundant
|
|
4605
|
+
aggregated : bool
|
|
4606
|
+
whether the aggregation was actually performed
|
|
4607
|
+
|
|
4608
|
+
Examples
|
|
4609
|
+
--------
|
|
4610
|
+
To express x = y (i.e., 1*x + (-1)*y = 0):
|
|
4611
|
+
|
|
4612
|
+
infeas, redun, aggr = model.aggregateVars(x, y, 1.0, -1.0, 0.0)
|
|
4613
|
+
|
|
4614
|
+
To express x = 5 - y (i.e., 1*x + 1*y = 5):
|
|
4615
|
+
|
|
4616
|
+
infeas, redun, aggr = model.aggregateVars(x, y, 1.0, 1.0, 5.0)
|
|
4617
|
+
|
|
4618
|
+
"""
|
|
4619
|
+
cdef SCIP_Bool infeasible
|
|
4620
|
+
cdef SCIP_Bool redundant
|
|
4621
|
+
cdef SCIP_Bool aggregated
|
|
4622
|
+
PY_SCIP_CALL(SCIPaggregateVars(self._scip,
|
|
4623
|
+
varx.scip_var,
|
|
4624
|
+
vary.scip_var,
|
|
4625
|
+
coefx,
|
|
4626
|
+
coefy,
|
|
4627
|
+
rhs,
|
|
4628
|
+
&infeasible,
|
|
4629
|
+
&redundant,
|
|
4630
|
+
&aggregated))
|
|
4631
|
+
return infeasible, redundant, aggregated
|
|
4632
|
+
|
|
4457
4633
|
def tightenVarLb(self, Variable var, lb, force=False):
|
|
4458
4634
|
"""
|
|
4459
4635
|
Tighten the lower bound in preprocessing or current node, if the bound is tighter.
|
|
@@ -4729,17 +4905,7 @@ cdef class Model:
|
|
|
4729
4905
|
nvars = SCIPgetNOrigVars(self._scip)
|
|
4730
4906
|
|
|
4731
4907
|
for i in range(nvars):
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
# check whether the corresponding variable exists already
|
|
4735
|
-
if ptr in self._modelvars:
|
|
4736
|
-
vars.append(self._modelvars[ptr])
|
|
4737
|
-
else:
|
|
4738
|
-
# create a new variable
|
|
4739
|
-
var = Variable.create(_vars[i])
|
|
4740
|
-
assert var.ptr() == ptr
|
|
4741
|
-
self._modelvars[ptr] = var
|
|
4742
|
-
vars.append(var)
|
|
4908
|
+
vars.append(self._getOrCreateVar(_vars[i]))
|
|
4743
4909
|
|
|
4744
4910
|
return vars
|
|
4745
4911
|
|
|
@@ -6013,7 +6179,7 @@ cdef class Model:
|
|
|
6013
6179
|
Parameters
|
|
6014
6180
|
----------
|
|
6015
6181
|
cons : ExprCons
|
|
6016
|
-
|
|
6182
|
+
the constraint expression to add to the model (e.g., x + y <= 5)
|
|
6017
6183
|
name : str, optional
|
|
6018
6184
|
the name of the constraint, generic name if empty (Default value = "")
|
|
6019
6185
|
initial : bool, optional
|
|
@@ -6063,7 +6229,7 @@ cdef class Model:
|
|
|
6063
6229
|
scip_cons = (<Constraint>pycons_initial).scip_cons
|
|
6064
6230
|
|
|
6065
6231
|
PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
|
|
6066
|
-
pycons =
|
|
6232
|
+
pycons = self._getOrCreateCons(scip_cons)
|
|
6067
6233
|
PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
|
|
6068
6234
|
|
|
6069
6235
|
return pycons
|
|
@@ -6385,7 +6551,7 @@ cdef class Model:
|
|
|
6385
6551
|
PY_SCIP_CALL(SCIPaddConsElemDisjunction(self._scip,disj_cons, (<Constraint>pycons).scip_cons))
|
|
6386
6552
|
PY_SCIP_CALL(SCIPreleaseCons(self._scip, &(<Constraint>pycons).scip_cons))
|
|
6387
6553
|
PY_SCIP_CALL(SCIPaddCons(self._scip, disj_cons))
|
|
6388
|
-
PyCons =
|
|
6554
|
+
PyCons = self._getOrCreateCons(disj_cons)
|
|
6389
6555
|
PY_SCIP_CALL(SCIPreleaseCons(self._scip, &disj_cons))
|
|
6390
6556
|
|
|
6391
6557
|
return PyCons
|
|
@@ -6471,20 +6637,11 @@ cdef class Model:
|
|
|
6471
6637
|
|
|
6472
6638
|
vars = []
|
|
6473
6639
|
for i in range(nvars):
|
|
6474
|
-
|
|
6475
|
-
# check whether the corresponding variable exists already
|
|
6476
|
-
if ptr in self._modelvars:
|
|
6477
|
-
vars.append(self._modelvars[ptr])
|
|
6478
|
-
else:
|
|
6479
|
-
# create a new variable
|
|
6480
|
-
var = Variable.create(_vars[i])
|
|
6481
|
-
assert var.ptr() == ptr
|
|
6482
|
-
self._modelvars[ptr] = var
|
|
6483
|
-
vars.append(var)
|
|
6640
|
+
vars.append(self._getOrCreateVar(_vars[i]))
|
|
6484
6641
|
|
|
6485
6642
|
free(_vars)
|
|
6486
6643
|
return vars
|
|
6487
|
-
|
|
6644
|
+
|
|
6488
6645
|
def getConsVals(self, Constraint constraint):
|
|
6489
6646
|
"""
|
|
6490
6647
|
Returns the value array of an arbitrary SCIP constraint that can be represented as a single linear constraint.
|
|
@@ -6560,16 +6717,7 @@ cdef class Model:
|
|
|
6560
6717
|
|
|
6561
6718
|
vars = []
|
|
6562
6719
|
for i in range(nvars):
|
|
6563
|
-
|
|
6564
|
-
# check whether the corresponding variable exists already
|
|
6565
|
-
if ptr in self._modelvars:
|
|
6566
|
-
vars.append(self._modelvars[ptr])
|
|
6567
|
-
else:
|
|
6568
|
-
# create a new variable
|
|
6569
|
-
var = Variable.create(_vars[i])
|
|
6570
|
-
assert var.ptr() == ptr
|
|
6571
|
-
self._modelvars[ptr] = var
|
|
6572
|
-
vars.append(var)
|
|
6720
|
+
vars.append(self._getOrCreateVar(_vars[i]))
|
|
6573
6721
|
|
|
6574
6722
|
return vars
|
|
6575
6723
|
|
|
@@ -6587,22 +6735,10 @@ cdef class Model:
|
|
|
6587
6735
|
Variable
|
|
6588
6736
|
|
|
6589
6737
|
"""
|
|
6590
|
-
|
|
6591
6738
|
cdef SCIP_VAR* _resultant
|
|
6592
6739
|
|
|
6593
6740
|
_resultant = SCIPgetResultantAnd(self._scip, and_cons.scip_cons)
|
|
6594
|
-
|
|
6595
|
-
ptr = <size_t>(_resultant)
|
|
6596
|
-
# check whether the corresponding variable exists already
|
|
6597
|
-
if ptr not in self._modelvars:
|
|
6598
|
-
# create a new variable
|
|
6599
|
-
resultant = Variable.create(_resultant)
|
|
6600
|
-
assert resultant.ptr() == ptr
|
|
6601
|
-
self._modelvars[ptr] = resultant
|
|
6602
|
-
else:
|
|
6603
|
-
resultant = self._modelvars[ptr]
|
|
6604
|
-
|
|
6605
|
-
return resultant
|
|
6741
|
+
return self._getOrCreateVar(_resultant)
|
|
6606
6742
|
|
|
6607
6743
|
def isAndConsSorted(self, Constraint and_cons):
|
|
6608
6744
|
"""
|
|
@@ -6691,7 +6827,10 @@ cdef class Model:
|
|
|
6691
6827
|
else:
|
|
6692
6828
|
raise NotImplementedError("Adding coefficients to %s constraints is not implemented." % constype)
|
|
6693
6829
|
|
|
6694
|
-
def addConsNode(self, Node node,
|
|
6830
|
+
def addConsNode(self, Node node, ExprCons cons, Node validnode=None, name='',
|
|
6831
|
+
initial=True, separate=True, enforce=True, check=True,
|
|
6832
|
+
propagate=True, local=True, dynamic=False, removable=True,
|
|
6833
|
+
stickingatnode=True):
|
|
6695
6834
|
"""
|
|
6696
6835
|
Add a constraint to the given node.
|
|
6697
6836
|
|
|
@@ -6699,35 +6838,120 @@ cdef class Model:
|
|
|
6699
6838
|
----------
|
|
6700
6839
|
node : Node
|
|
6701
6840
|
node at which the constraint will be added
|
|
6702
|
-
cons :
|
|
6703
|
-
the constraint to add to the node
|
|
6841
|
+
cons : ExprCons
|
|
6842
|
+
the constraint expression to add to the node (e.g., x + y <= 5)
|
|
6704
6843
|
validnode : Node or None, optional
|
|
6705
6844
|
more global node where cons is also valid. (Default=None)
|
|
6845
|
+
name : str, optional
|
|
6846
|
+
name of the constraint (Default value = '')
|
|
6847
|
+
initial : bool, optional
|
|
6848
|
+
should the LP relaxation of constraint be in the initial LP? (Default value = True)
|
|
6849
|
+
separate : bool, optional
|
|
6850
|
+
should the constraint be separated during LP processing? (Default value = True)
|
|
6851
|
+
enforce : bool, optional
|
|
6852
|
+
should the constraint be enforced during node processing? (Default value = True)
|
|
6853
|
+
check : bool, optional
|
|
6854
|
+
should the constraint be checked for feasibility? (Default value = True)
|
|
6855
|
+
propagate : bool, optional
|
|
6856
|
+
should the constraint be propagated during node processing? (Default value = True)
|
|
6857
|
+
local : bool, optional
|
|
6858
|
+
is the constraint only valid locally? (Default value = True)
|
|
6859
|
+
dynamic : bool, optional
|
|
6860
|
+
is the constraint subject to aging? (Default value = False)
|
|
6861
|
+
removable : bool, optional
|
|
6862
|
+
should the relaxation be removed from the LP due to aging or cleanup? (Default value = True)
|
|
6863
|
+
stickingatnode : bool, optional
|
|
6864
|
+
should the constraint always be kept at the node where it was added? (Default value = True)
|
|
6865
|
+
|
|
6866
|
+
Returns
|
|
6867
|
+
-------
|
|
6868
|
+
Constraint
|
|
6869
|
+
The added Constraint object.
|
|
6706
6870
|
|
|
6707
6871
|
"""
|
|
6872
|
+
assert isinstance(cons, ExprCons), "given constraint is not ExprCons but %s" % cons.__class__.__name__
|
|
6873
|
+
|
|
6874
|
+
cdef SCIP_CONS* scip_cons
|
|
6875
|
+
|
|
6876
|
+
kwargs = dict(name=name, initial=initial, separate=separate,
|
|
6877
|
+
enforce=enforce, check=check, propagate=propagate,
|
|
6878
|
+
local=local, modifiable=False, dynamic=dynamic,
|
|
6879
|
+
removable=removable, stickingatnode=stickingatnode)
|
|
6880
|
+
pycons_initial = self.createConsFromExpr(cons, **kwargs)
|
|
6881
|
+
scip_cons = (<Constraint>pycons_initial).scip_cons
|
|
6882
|
+
|
|
6708
6883
|
if isinstance(validnode, Node):
|
|
6709
|
-
PY_SCIP_CALL(SCIPaddConsNode(self._scip, node.scip_node,
|
|
6884
|
+
PY_SCIP_CALL(SCIPaddConsNode(self._scip, node.scip_node, scip_cons, validnode.scip_node))
|
|
6710
6885
|
else:
|
|
6711
|
-
PY_SCIP_CALL(SCIPaddConsNode(self._scip, node.scip_node,
|
|
6712
|
-
|
|
6886
|
+
PY_SCIP_CALL(SCIPaddConsNode(self._scip, node.scip_node, scip_cons, NULL))
|
|
6887
|
+
|
|
6888
|
+
pycons = Constraint.create(scip_cons)
|
|
6889
|
+
pycons.data = (<Constraint>pycons_initial).data
|
|
6890
|
+
PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
|
|
6713
6891
|
|
|
6714
|
-
|
|
6892
|
+
return pycons
|
|
6893
|
+
|
|
6894
|
+
def addConsLocal(self, ExprCons cons, Node validnode=None, name='',
|
|
6895
|
+
initial=True, separate=True, enforce=True, check=True,
|
|
6896
|
+
propagate=True, local=True, dynamic=False, removable=True,
|
|
6897
|
+
stickingatnode=True):
|
|
6715
6898
|
"""
|
|
6716
6899
|
Add a constraint to the current node.
|
|
6717
6900
|
|
|
6718
6901
|
Parameters
|
|
6719
6902
|
----------
|
|
6720
|
-
cons :
|
|
6721
|
-
the constraint to add to the current node
|
|
6903
|
+
cons : ExprCons
|
|
6904
|
+
the constraint expression to add to the current node (e.g., x + y <= 5)
|
|
6722
6905
|
validnode : Node or None, optional
|
|
6723
6906
|
more global node where cons is also valid. (Default=None)
|
|
6907
|
+
name : str, optional
|
|
6908
|
+
name of the constraint (Default value = '')
|
|
6909
|
+
initial : bool, optional
|
|
6910
|
+
should the LP relaxation of constraint be in the initial LP? (Default value = True)
|
|
6911
|
+
separate : bool, optional
|
|
6912
|
+
should the constraint be separated during LP processing? (Default value = True)
|
|
6913
|
+
enforce : bool, optional
|
|
6914
|
+
should the constraint be enforced during node processing? (Default value = True)
|
|
6915
|
+
check : bool, optional
|
|
6916
|
+
should the constraint be checked for feasibility? (Default value = True)
|
|
6917
|
+
propagate : bool, optional
|
|
6918
|
+
should the constraint be propagated during node processing? (Default value = True)
|
|
6919
|
+
local : bool, optional
|
|
6920
|
+
is the constraint only valid locally? (Default value = True)
|
|
6921
|
+
dynamic : bool, optional
|
|
6922
|
+
is the constraint subject to aging? (Default value = False)
|
|
6923
|
+
removable : bool, optional
|
|
6924
|
+
should the relaxation be removed from the LP due to aging or cleanup? (Default value = True)
|
|
6925
|
+
stickingatnode : bool, optional
|
|
6926
|
+
should the constraint always be kept at the node where it was added? (Default value = True)
|
|
6927
|
+
|
|
6928
|
+
Returns
|
|
6929
|
+
-------
|
|
6930
|
+
Constraint
|
|
6931
|
+
The added Constraint object.
|
|
6724
6932
|
|
|
6725
6933
|
"""
|
|
6934
|
+
assert isinstance(cons, ExprCons), "given constraint is not ExprCons but %s" % cons.__class__.__name__
|
|
6935
|
+
|
|
6936
|
+
cdef SCIP_CONS* scip_cons
|
|
6937
|
+
|
|
6938
|
+
kwargs = dict(name=name, initial=initial, separate=separate,
|
|
6939
|
+
enforce=enforce, check=check, propagate=propagate,
|
|
6940
|
+
local=local, modifiable=False, dynamic=dynamic,
|
|
6941
|
+
removable=removable, stickingatnode=stickingatnode)
|
|
6942
|
+
pycons_initial = self.createConsFromExpr(cons, **kwargs)
|
|
6943
|
+
scip_cons = (<Constraint>pycons_initial).scip_cons
|
|
6944
|
+
|
|
6726
6945
|
if isinstance(validnode, Node):
|
|
6727
|
-
PY_SCIP_CALL(SCIPaddConsLocal(self._scip,
|
|
6946
|
+
PY_SCIP_CALL(SCIPaddConsLocal(self._scip, scip_cons, validnode.scip_node))
|
|
6728
6947
|
else:
|
|
6729
|
-
PY_SCIP_CALL(SCIPaddConsLocal(self._scip,
|
|
6730
|
-
|
|
6948
|
+
PY_SCIP_CALL(SCIPaddConsLocal(self._scip, scip_cons, NULL))
|
|
6949
|
+
|
|
6950
|
+
pycons = Constraint.create(scip_cons)
|
|
6951
|
+
pycons.data = (<Constraint>pycons_initial).data
|
|
6952
|
+
PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
|
|
6953
|
+
|
|
6954
|
+
return pycons
|
|
6731
6955
|
|
|
6732
6956
|
def addConsKnapsack(self, vars, weights, capacity, name="",
|
|
6733
6957
|
initial=True, separate=True, enforce=True, check=True,
|
|
@@ -6794,9 +7018,9 @@ cdef class Model:
|
|
|
6794
7018
|
|
|
6795
7019
|
PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
|
|
6796
7020
|
|
|
6797
|
-
pyCons =
|
|
7021
|
+
pyCons = self._getOrCreateCons(scip_cons)
|
|
6798
7022
|
PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
|
|
6799
|
-
|
|
7023
|
+
|
|
6800
7024
|
return pyCons
|
|
6801
7025
|
|
|
6802
7026
|
def addConsSOS1(self, vars, weights=None, name="",
|
|
@@ -6862,7 +7086,7 @@ cdef class Model:
|
|
|
6862
7086
|
|
|
6863
7087
|
PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
|
|
6864
7088
|
|
|
6865
|
-
return
|
|
7089
|
+
return self._getOrCreateCons(scip_cons)
|
|
6866
7090
|
|
|
6867
7091
|
def addConsSOS2(self, vars, weights=None, name="",
|
|
6868
7092
|
initial=True, separate=True, enforce=True, check=True,
|
|
@@ -6927,7 +7151,7 @@ cdef class Model:
|
|
|
6927
7151
|
|
|
6928
7152
|
PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
|
|
6929
7153
|
|
|
6930
|
-
return
|
|
7154
|
+
return self._getOrCreateCons(scip_cons)
|
|
6931
7155
|
|
|
6932
7156
|
def addConsAnd(self, vars, resvar, name="",
|
|
6933
7157
|
initial=True, separate=True, enforce=True, check=True,
|
|
@@ -6988,7 +7212,7 @@ cdef class Model:
|
|
|
6988
7212
|
initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode))
|
|
6989
7213
|
|
|
6990
7214
|
PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
|
|
6991
|
-
pyCons =
|
|
7215
|
+
pyCons = self._getOrCreateCons(scip_cons)
|
|
6992
7216
|
PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
|
|
6993
7217
|
|
|
6994
7218
|
return pyCons
|
|
@@ -7052,7 +7276,7 @@ cdef class Model:
|
|
|
7052
7276
|
initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode))
|
|
7053
7277
|
|
|
7054
7278
|
PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
|
|
7055
|
-
pyCons =
|
|
7279
|
+
pyCons = self._getOrCreateCons(scip_cons)
|
|
7056
7280
|
PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
|
|
7057
7281
|
|
|
7058
7282
|
return pyCons
|
|
@@ -7115,7 +7339,7 @@ cdef class Model:
|
|
|
7115
7339
|
initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode))
|
|
7116
7340
|
|
|
7117
7341
|
PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
|
|
7118
|
-
pyCons =
|
|
7342
|
+
pyCons = self._getOrCreateCons(scip_cons)
|
|
7119
7343
|
PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
|
|
7120
7344
|
|
|
7121
7345
|
return pyCons
|
|
@@ -7200,7 +7424,7 @@ cdef class Model:
|
|
|
7200
7424
|
PY_SCIP_CALL(SCIPaddVarCardinality(self._scip, scip_cons, scip_var, indvar, <SCIP_Real>weights[i]))
|
|
7201
7425
|
|
|
7202
7426
|
PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
|
|
7203
|
-
pyCons =
|
|
7427
|
+
pyCons = self._getOrCreateCons(scip_cons)
|
|
7204
7428
|
|
|
7205
7429
|
PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
|
|
7206
7430
|
|
|
@@ -7293,7 +7517,7 @@ cdef class Model:
|
|
|
7293
7517
|
PY_SCIP_CALL(SCIPaddVarIndicator(self._scip, scip_cons, wrapper.ptr[0], <SCIP_Real>coeff))
|
|
7294
7518
|
|
|
7295
7519
|
PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
|
|
7296
|
-
pyCons =
|
|
7520
|
+
pyCons = self._getOrCreateCons(scip_cons)
|
|
7297
7521
|
|
|
7298
7522
|
PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
|
|
7299
7523
|
|
|
@@ -7483,7 +7707,7 @@ cdef class Model:
|
|
|
7483
7707
|
cdef SCIP_CONS* lincons = SCIPgetLinearConsIndicator(cons.scip_cons)
|
|
7484
7708
|
if lincons == NULL:
|
|
7485
7709
|
return None
|
|
7486
|
-
return
|
|
7710
|
+
return self._getOrCreateCons(lincons)
|
|
7487
7711
|
|
|
7488
7712
|
def getSlackVarIndicator(self, Constraint cons):
|
|
7489
7713
|
"""
|
|
@@ -7501,7 +7725,7 @@ cdef class Model:
|
|
|
7501
7725
|
|
|
7502
7726
|
"""
|
|
7503
7727
|
cdef SCIP_VAR* var = SCIPgetSlackVarIndicator(cons.scip_cons)
|
|
7504
|
-
return
|
|
7728
|
+
return self._getOrCreateVar(var)
|
|
7505
7729
|
|
|
7506
7730
|
def addPyCons(self, Constraint cons):
|
|
7507
7731
|
"""
|
|
@@ -7945,7 +8169,7 @@ cdef class Model:
|
|
|
7945
8169
|
"""
|
|
7946
8170
|
cdef SCIP_CONS* transcons
|
|
7947
8171
|
PY_SCIP_CALL(SCIPgetTransformedCons(self._scip, cons.scip_cons, &transcons))
|
|
7948
|
-
return
|
|
8172
|
+
return self._getOrCreateCons(transcons)
|
|
7949
8173
|
|
|
7950
8174
|
def isNLPConstructed(self):
|
|
7951
8175
|
"""
|
|
@@ -8085,8 +8309,16 @@ cdef class Model:
|
|
|
8085
8309
|
Returns
|
|
8086
8310
|
-------
|
|
8087
8311
|
bilinterms : list of tuple
|
|
8312
|
+
Triples ``(var1, var2, coef)`` for terms of the form
|
|
8313
|
+
``coef * var1 * var2`` with ``var1 != var2``.
|
|
8088
8314
|
quadterms : list of tuple
|
|
8315
|
+
Triples ``(var, sqrcoef, lincoef)`` for variables that appear in
|
|
8316
|
+
quadratic or bilinear terms. ``sqrcoef`` is the coefficient of
|
|
8317
|
+
``var**2``, and ``lincoef`` is the linear coefficient of ``var``
|
|
8318
|
+
if it also appears linearly.
|
|
8089
8319
|
linterms : list of tuple
|
|
8320
|
+
Pairs ``(var, coef)`` for purely linear variables, i.e.,
|
|
8321
|
+
variables that do not participate in any quadratic or bilinear term.
|
|
8090
8322
|
|
|
8091
8323
|
"""
|
|
8092
8324
|
cdef SCIP_EXPR* expr
|
|
@@ -8105,6 +8337,7 @@ cdef class Model:
|
|
|
8105
8337
|
cdef int nbilinterms
|
|
8106
8338
|
|
|
8107
8339
|
# quadratic terms
|
|
8340
|
+
cdef SCIP_EXPR* quadexpr
|
|
8108
8341
|
cdef SCIP_EXPR* sqrexpr
|
|
8109
8342
|
cdef SCIP_Real sqrcoef
|
|
8110
8343
|
cdef int nquadterms
|
|
@@ -8117,33 +8350,49 @@ cdef class Model:
|
|
|
8117
8350
|
assert self.checkQuadraticNonlinear(cons), "constraint is not quadratic"
|
|
8118
8351
|
|
|
8119
8352
|
expr = SCIPgetExprNonlinear(cons.scip_cons)
|
|
8120
|
-
SCIPexprGetQuadraticData(expr, NULL, &nlinvars, &linexprs, &lincoefs,
|
|
8353
|
+
SCIPexprGetQuadraticData(expr, NULL, &nlinvars, &linexprs, &lincoefs,
|
|
8354
|
+
&nquadterms, &nbilinterms, NULL, NULL)
|
|
8121
8355
|
|
|
8122
8356
|
linterms = []
|
|
8123
8357
|
bilinterms = []
|
|
8124
|
-
quadterms = []
|
|
8125
8358
|
|
|
8359
|
+
# Purely linear terms (variables not in any quadratic/bilinear term)
|
|
8126
8360
|
for termidx in range(nlinvars):
|
|
8127
|
-
var =
|
|
8361
|
+
var = self._getOrCreateVar(SCIPgetVarExprVar(linexprs[termidx]))
|
|
8128
8362
|
linterms.append((var, lincoefs[termidx]))
|
|
8129
8363
|
|
|
8364
|
+
# Collect quadratic terms in a dict so we can merge entries for the same variable.
|
|
8365
|
+
quaddict = {} # var.ptr() -> [var, sqrcoef, lincoef]
|
|
8366
|
+
|
|
8130
8367
|
for termidx in range(nbilinterms):
|
|
8131
8368
|
SCIPexprGetQuadraticBilinTerm(expr, termidx, &bilinterm1, &bilinterm2, &bilincoef, NULL, NULL)
|
|
8132
8369
|
scipvar1 = SCIPgetVarExprVar(bilinterm1)
|
|
8133
8370
|
scipvar2 = SCIPgetVarExprVar(bilinterm2)
|
|
8134
|
-
var1 =
|
|
8135
|
-
var2 =
|
|
8371
|
+
var1 = self._getOrCreateVar(scipvar1)
|
|
8372
|
+
var2 = self._getOrCreateVar(scipvar2)
|
|
8136
8373
|
if scipvar1 != scipvar2:
|
|
8137
|
-
bilinterms.append((var1,var2,bilincoef))
|
|
8374
|
+
bilinterms.append((var1, var2, bilincoef))
|
|
8138
8375
|
else:
|
|
8139
|
-
|
|
8140
|
-
|
|
8376
|
+
# Squared term reported as bilinear var*var
|
|
8377
|
+
key = var1.ptr()
|
|
8378
|
+
if key in quaddict:
|
|
8379
|
+
quaddict[key][1] += bilincoef
|
|
8380
|
+
else: # TODO: SCIP handles expr like x**2 appropriately, but PySCIPOpt requires this. Need to investigate why.
|
|
8381
|
+
quaddict[key] = [var1, bilincoef, 0.0]
|
|
8382
|
+
|
|
8383
|
+
# Also collect linear coefficients from the quadratic terms
|
|
8141
8384
|
for termidx in range(nquadterms):
|
|
8142
|
-
SCIPexprGetQuadraticQuadTerm(expr, termidx,
|
|
8143
|
-
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
|
|
8385
|
+
SCIPexprGetQuadraticQuadTerm(expr, termidx, &quadexpr, &lincoef, &sqrcoef, NULL, NULL, &sqrexpr)
|
|
8386
|
+
scipvar1 = SCIPgetVarExprVar(quadexpr)
|
|
8387
|
+
var = self._getOrCreateVar(scipvar1)
|
|
8388
|
+
key = var.ptr()
|
|
8389
|
+
if key in quaddict:
|
|
8390
|
+
quaddict[key][1] += sqrcoef
|
|
8391
|
+
quaddict[key][2] += lincoef
|
|
8392
|
+
else:
|
|
8393
|
+
quaddict[key] = [var, sqrcoef, lincoef]
|
|
8394
|
+
|
|
8395
|
+
quadterms = [tuple(entry) for entry in quaddict.values()]
|
|
8147
8396
|
|
|
8148
8397
|
return (bilinterms, quadterms, linterms)
|
|
8149
8398
|
|
|
@@ -8202,7 +8451,7 @@ cdef class Model:
|
|
|
8202
8451
|
conss = SCIPgetOrigConss(self._scip)
|
|
8203
8452
|
nconss = SCIPgetNOrigConss(self._scip)
|
|
8204
8453
|
|
|
8205
|
-
return [
|
|
8454
|
+
return [self._getOrCreateCons(conss[i]) for i in range(nconss)]
|
|
8206
8455
|
|
|
8207
8456
|
def getNConss(self, transformed=True):
|
|
8208
8457
|
"""
|
|
@@ -8233,7 +8482,10 @@ cdef class Model:
|
|
|
8233
8482
|
constraint to be deleted
|
|
8234
8483
|
|
|
8235
8484
|
"""
|
|
8485
|
+
del self._modelconss[cons.ptr()]
|
|
8236
8486
|
PY_SCIP_CALL(SCIPdelCons(self._scip, cons.scip_cons))
|
|
8487
|
+
# Remove from tracking and invalidate pointer. See issue #604.
|
|
8488
|
+
cons.scip_cons = NULL
|
|
8237
8489
|
|
|
8238
8490
|
def delConsLocal(self, Constraint cons):
|
|
8239
8491
|
"""
|
|
@@ -8833,11 +9085,8 @@ cdef class Model:
|
|
|
8833
9085
|
PY_SCIP_CALL(SCIPgetBendersSubproblemVar(self._scip, _benders, var.scip_var, &_mappedvar, probnumber))
|
|
8834
9086
|
|
|
8835
9087
|
if _mappedvar == NULL:
|
|
8836
|
-
|
|
8837
|
-
|
|
8838
|
-
mappedvar = Variable.create(_mappedvar)
|
|
8839
|
-
|
|
8840
|
-
return mappedvar
|
|
9088
|
+
return None
|
|
9089
|
+
return self._getOrCreateVar(_mappedvar)
|
|
8841
9090
|
|
|
8842
9091
|
def getBendersAuxiliaryVar(self, probnumber, Benders benders = None):
|
|
8843
9092
|
"""
|
|
@@ -8864,9 +9113,7 @@ cdef class Model:
|
|
|
8864
9113
|
_benders = benders._benders
|
|
8865
9114
|
|
|
8866
9115
|
_auxvar = SCIPbendersGetAuxiliaryVar(_benders, probnumber)
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
return auxvar
|
|
9116
|
+
return self._getOrCreateVar(_auxvar)
|
|
8870
9117
|
|
|
8871
9118
|
def checkBendersSubproblemOptimality(self, Solution solution, probnumber, Benders benders = None):
|
|
8872
9119
|
"""
|
|
@@ -9659,7 +9906,7 @@ cdef class Model:
|
|
|
9659
9906
|
PY_SCIP_CALL(SCIPgetLPBranchCands(self._scip, &lpcands, &lpcandssol, &lpcandsfrac,
|
|
9660
9907
|
&nlpcands, &npriolpcands, &nfracimplvars))
|
|
9661
9908
|
|
|
9662
|
-
return ([
|
|
9909
|
+
return ([self._getOrCreateVar(lpcands[i]) for i in range(nlpcands)], [lpcandssol[i] for i in range(nlpcands)],
|
|
9663
9910
|
[lpcandsfrac[i] for i in range(nlpcands)], nlpcands, npriolpcands, nfracimplvars)
|
|
9664
9911
|
|
|
9665
9912
|
def getNLPBranchCands(self):
|
|
@@ -9696,7 +9943,7 @@ cdef class Model:
|
|
|
9696
9943
|
|
|
9697
9944
|
PY_SCIP_CALL(SCIPgetPseudoBranchCands(self._scip, &pseudocands, &npseudocands, &npriopseudocands))
|
|
9698
9945
|
|
|
9699
|
-
return ([
|
|
9946
|
+
return ([self._getOrCreateVar(pseudocands[i]) for i in range(npseudocands)], npseudocands, npriopseudocands)
|
|
9700
9947
|
|
|
9701
9948
|
def branchVar(self, Variable variable):
|
|
9702
9949
|
"""
|
|
@@ -10671,7 +10918,10 @@ cdef class Model:
|
|
|
10671
10918
|
Solution or None
|
|
10672
10919
|
|
|
10673
10920
|
"""
|
|
10674
|
-
|
|
10921
|
+
cdef SCIP_SOL* _sol = SCIPgetBestSol(self._scip)
|
|
10922
|
+
if _sol == NULL:
|
|
10923
|
+
return None
|
|
10924
|
+
self._bestSol = Solution.create(self._scip, _sol)
|
|
10675
10925
|
return self._bestSol
|
|
10676
10926
|
|
|
10677
10927
|
def getSolObjVal(self, Solution sol, original=True):
|
|
@@ -10714,6 +10964,8 @@ cdef class Model:
|
|
|
10714
10964
|
float
|
|
10715
10965
|
|
|
10716
10966
|
"""
|
|
10967
|
+
if sol is None or sol.sol == NULL:
|
|
10968
|
+
raise ValueError("Cannot get solution time: solution is None or NULL")
|
|
10717
10969
|
return SCIPgetSolTime(self._scip, sol.sol)
|
|
10718
10970
|
|
|
10719
10971
|
def getObjVal(self, original=True):
|
|
@@ -10747,20 +10999,26 @@ cdef class Model:
|
|
|
10747
10999
|
|
|
10748
11000
|
return self.getSolObjVal(self._bestSol, original)
|
|
10749
11001
|
|
|
10750
|
-
def getSolVal(
|
|
11002
|
+
def getSolVal(
|
|
11003
|
+
self,
|
|
11004
|
+
Solution sol,
|
|
11005
|
+
expr: Union[Expr, GenExpr, MatrixExpr],
|
|
11006
|
+
) -> Union[float, np.ndarray]:
|
|
10751
11007
|
"""
|
|
10752
|
-
Retrieve value of given variable or expression in the given solution
|
|
10753
|
-
the LP/pseudo solution if sol == None
|
|
11008
|
+
Retrieve value of given variable or expression in the given solution.
|
|
10754
11009
|
|
|
10755
11010
|
Parameters
|
|
10756
11011
|
----------
|
|
10757
11012
|
sol : Solution
|
|
10758
|
-
|
|
10759
|
-
|
|
11013
|
+
Solution to query the value from. If None, the current LP/pseudo solution is
|
|
11014
|
+
used.
|
|
11015
|
+
|
|
11016
|
+
expr : Expr, GenExpr, MatrixExpr
|
|
11017
|
+
Expression to query the value of.
|
|
10760
11018
|
|
|
10761
11019
|
Returns
|
|
10762
11020
|
-------
|
|
10763
|
-
float
|
|
11021
|
+
float or np.ndarray
|
|
10764
11022
|
|
|
10765
11023
|
Notes
|
|
10766
11024
|
-----
|
|
@@ -10768,46 +11026,39 @@ cdef class Model:
|
|
|
10768
11026
|
|
|
10769
11027
|
"""
|
|
10770
11028
|
# no need to create a NULL solution wrapper in case we have a variable
|
|
10771
|
-
|
|
10772
|
-
if sol == None and isinstance(expr, Variable):
|
|
10773
|
-
wrapper = _VarArray(expr)
|
|
10774
|
-
return SCIPgetSolVal(self._scip, NULL, wrapper.ptr[0])
|
|
10775
|
-
if sol == None:
|
|
10776
|
-
sol = Solution.create(self._scip, NULL)
|
|
10777
|
-
return sol[expr]
|
|
11029
|
+
return (sol or Solution.create(self._scip, NULL))[expr]
|
|
10778
11030
|
|
|
10779
|
-
def getVal(self, expr: Union[Expr, MatrixExpr]
|
|
11031
|
+
def getVal(self, expr: Union[Expr, GenExpr, MatrixExpr]) -> Union[float, np.ndarray]:
|
|
10780
11032
|
"""
|
|
10781
11033
|
Retrieve the value of the given variable or expression in the best known solution.
|
|
10782
11034
|
Can only be called after solving is completed.
|
|
10783
11035
|
|
|
10784
11036
|
Parameters
|
|
10785
11037
|
----------
|
|
10786
|
-
expr : Expr
|
|
10787
|
-
|
|
11038
|
+
expr : Expr, GenExpr or MatrixExpr
|
|
11039
|
+
Expression to query the value of.
|
|
10788
11040
|
|
|
10789
11041
|
Returns
|
|
10790
11042
|
-------
|
|
10791
|
-
float
|
|
11043
|
+
float or np.ndarray
|
|
10792
11044
|
|
|
10793
11045
|
Notes
|
|
10794
11046
|
-----
|
|
10795
11047
|
A variable is also an expression.
|
|
10796
11048
|
|
|
10797
11049
|
"""
|
|
10798
|
-
|
|
10799
|
-
|
|
10800
|
-
if not stage_check or self._bestSol.sol == NULL and SCIPgetStage(self._scip) != SCIP_STAGE_SOLVING:
|
|
11050
|
+
if SCIPgetStage(self._scip) in {SCIP_STAGE_INIT, SCIP_STAGE_FREE}:
|
|
10801
11051
|
raise Warning("Method cannot be called in stage ", self.getStage())
|
|
10802
11052
|
|
|
10803
|
-
|
|
10804
|
-
|
|
10805
|
-
|
|
10806
|
-
|
|
10807
|
-
else:
|
|
10808
|
-
result = self.getSolVal(self._bestSol, expr)
|
|
11053
|
+
# Ensure _bestSol is up-to-date (cheap pointer comparison)
|
|
11054
|
+
cdef SCIP_SOL* current_best_sol = SCIPgetBestSol(self._scip)
|
|
11055
|
+
if self._bestSol is None or self._bestSol.sol != current_best_sol:
|
|
11056
|
+
self._bestSol = Solution.create(self._scip, current_best_sol)
|
|
10809
11057
|
|
|
10810
|
-
|
|
11058
|
+
if self._bestSol.sol == NULL and SCIPgetStage(self._scip) != SCIP_STAGE_SOLVING:
|
|
11059
|
+
raise Warning("No solution available")
|
|
11060
|
+
|
|
11061
|
+
return self._bestSol[expr]
|
|
10811
11062
|
|
|
10812
11063
|
def hasPrimalRay(self):
|
|
10813
11064
|
"""
|
|
@@ -11623,12 +11874,12 @@ cdef class Model:
|
|
|
11623
11874
|
|
|
11624
11875
|
def chgReoptObjective(self, coeffs, sense = 'minimize'):
|
|
11625
11876
|
"""
|
|
11626
|
-
|
|
11877
|
+
Change the objective function for reoptimization.
|
|
11627
11878
|
|
|
11628
11879
|
Parameters
|
|
11629
11880
|
----------
|
|
11630
|
-
coeffs :
|
|
11631
|
-
the coefficients
|
|
11881
|
+
coeffs : Expr
|
|
11882
|
+
the coefficients as a linear expression
|
|
11632
11883
|
sense : str
|
|
11633
11884
|
the objective sense (Default value = 'minimize')
|
|
11634
11885
|
|
|
@@ -11637,7 +11888,6 @@ cdef class Model:
|
|
|
11637
11888
|
cdef int nvars
|
|
11638
11889
|
cdef SCIP_Real* _coeffs
|
|
11639
11890
|
cdef SCIP_OBJSENSE objsense
|
|
11640
|
-
cdef SCIP_Real coef
|
|
11641
11891
|
cdef int i
|
|
11642
11892
|
cdef _VarArray wrapper
|
|
11643
11893
|
|
|
@@ -11655,24 +11905,27 @@ cdef class Model:
|
|
|
11655
11905
|
if coeffs[CONST] != 0.0:
|
|
11656
11906
|
raise ValueError("Constant offsets in objective are not supported!")
|
|
11657
11907
|
|
|
11658
|
-
|
|
11659
|
-
nvars = SCIPgetNOrigVars(self._scip)
|
|
11660
|
-
_coeffs = <SCIP_Real*> malloc(nvars * sizeof(SCIP_Real))
|
|
11908
|
+
nvars = len(coeffs.terms) - (CONST in coeffs.terms)
|
|
11661
11909
|
|
|
11662
|
-
|
|
11663
|
-
|
|
11910
|
+
if nvars == 0:
|
|
11911
|
+
PY_SCIP_CALL(SCIPchgReoptObjective(self._scip, objsense, NULL, NULL, 0))
|
|
11912
|
+
return
|
|
11913
|
+
|
|
11914
|
+
_coeffs = <SCIP_Real*> malloc(nvars * sizeof(SCIP_Real))
|
|
11915
|
+
vars = <SCIP_VAR**> malloc(nvars * sizeof(SCIP_VAR*))
|
|
11664
11916
|
|
|
11917
|
+
i = 0
|
|
11665
11918
|
for term, coef in coeffs.terms.items():
|
|
11666
11919
|
# avoid CONST term of Expr
|
|
11667
11920
|
if term != CONST:
|
|
11668
|
-
|
|
11669
|
-
|
|
11670
|
-
|
|
11671
|
-
|
|
11672
|
-
_coeffs[i] = coef
|
|
11921
|
+
wrapper = _VarArray(term[0])
|
|
11922
|
+
vars[i] = wrapper.ptr[0]
|
|
11923
|
+
_coeffs[i] = coef
|
|
11924
|
+
i += 1
|
|
11673
11925
|
|
|
11674
|
-
PY_SCIP_CALL(SCIPchgReoptObjective(self._scip, objsense, vars,
|
|
11926
|
+
PY_SCIP_CALL(SCIPchgReoptObjective(self._scip, objsense, vars, _coeffs, nvars))
|
|
11675
11927
|
|
|
11928
|
+
free(vars)
|
|
11676
11929
|
free(_coeffs)
|
|
11677
11930
|
|
|
11678
11931
|
def chgVarBranchPriority(self, Variable var, priority):
|