Skip to content

Commit 5f65812

Browse files
Add deactivatePricer (#973)
* Add deactivatePricer * Add setModifiable * remove forgotten function call * Add getSiblings, getNSiblings * Leaves * getChildren * Store scip_pricer instead of pricer_name
1 parent a8daa5c commit 5f65812

File tree

6 files changed

+199
-4
lines changed

6 files changed

+199
-4
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44
### Added
5+
- Wrapped SCIPgetChildren and added getChildren and test (also test getOpenNodes)
6+
- Wrapped SCIPgetLeaves, SCIPgetNLeaves, and added getLeaves, getNLeaves and test
7+
- Wrapped SCIPgetSiblings, SCIPgetNSiblings, and added getSiblings, getNSiblings and test
8+
- Wrapped SCIPdeactivatePricer, SCIPsetConsModifiable, and added deactivatePricer, setModifiable and test
59
- Added getLinearConsIndicator
610
- Added SCIP_LPPARAM, setIntParam, setRealParam, getIntParam, getRealParam, isOptimal, getObjVal, getRedcost for lpi
711
- Added isFeasPositive

src/pyscipopt/pricer.pxi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#@brief Base class of the Pricers Plugin
33
cdef class Pricer:
44
cdef public Model model
5+
cdef SCIP_PRICER* scip_pricer
56

67
def pricerfree(self):
78
'''calls destructor and frees memory of variable pricer '''

src/pyscipopt/scip.pxd

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,7 @@ cdef extern from "scip/scip.h":
875875
SCIP_RETCODE SCIPsetConsChecked(SCIP *scip, SCIP_CONS *cons, SCIP_Bool check)
876876
SCIP_RETCODE SCIPsetConsRemovable(SCIP *scip, SCIP_CONS *cons, SCIP_Bool removable)
877877
SCIP_RETCODE SCIPsetConsInitial(SCIP *scip, SCIP_CONS *cons, SCIP_Bool initial)
878+
SCIP_RETCODE SCIPsetConsModifiable(SCIP *scip, SCIP_CONS *cons, SCIP_Bool modifiable)
878879
SCIP_RETCODE SCIPsetConsEnforced(SCIP *scip, SCIP_CONS *cons, SCIP_Bool enforce)
879880

880881
# Primal Solution Methods
@@ -998,6 +999,7 @@ cdef extern from "scip/scip.h":
998999
SCIP_PRICERDATA* pricerdata)
9991000
SCIP_PRICER* SCIPfindPricer(SCIP* scip, const char* name)
10001001
SCIP_RETCODE SCIPactivatePricer(SCIP* scip, SCIP_PRICER* pricer)
1002+
SCIP_RETCODE SCIPdeactivatePricer(SCIP* scip, SCIP_PRICER* pricer)
10011003
SCIP_PRICERDATA* SCIPpricerGetData(SCIP_PRICER* pricer)
10021004

10031005
# Constraint handler plugin
@@ -1934,10 +1936,13 @@ cdef extern from "scip/pub_lp.h":
19341936

19351937
cdef extern from "scip/scip_tree.h":
19361938
SCIP_RETCODE SCIPgetOpenNodesData(SCIP* scip, SCIP_NODE*** leaves, SCIP_NODE*** children, SCIP_NODE*** siblings, int* nleaves, int* nchildren, int* nsiblings)
1937-
SCIP_Longint SCIPgetNLeaves(SCIP* scip)
1939+
SCIP_RETCODE SCIPgetChildren(SCIP* scip, SCIP_NODE*** children, int* nchildren)
19381940
SCIP_Longint SCIPgetNChildren(SCIP* scip)
1939-
SCIP_Longint SCIPgetNSiblings(SCIP* scip)
19401941
SCIP_NODE* SCIPgetBestChild(SCIP* scip)
1942+
SCIP_RETCODE SCIPgetSiblings(SCIP* scip, SCIP_NODE*** siblings, int* nsiblings)
1943+
SCIP_RETCODE SCIPgetNSiblings(SCIP* scip)
1944+
SCIP_RETCODE SCIPgetLeaves(SCIP* scip, SCIP_NODE*** leaves, int* nleaves)
1945+
SCIP_Longint SCIPgetNLeaves(SCIP* scip)
19411946
SCIP_NODE* SCIPgetBestSibling(SCIP* scip)
19421947
SCIP_NODE* SCIPgetBestLeaf(SCIP* scip)
19431948
SCIP_NODE* SCIPgetPrioChild(SCIP* scip)

src/pyscipopt/scip.pxi

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4178,7 +4178,58 @@ cdef class Model:
41784178
41794179
"""
41804180
return Node.create(SCIPgetBestChild(self._scip))
4181+
4182+
def getChildren(self):
4183+
"""
4184+
Gets the children of the focus node.
4185+
4186+
Returns
4187+
-------
4188+
list of Nodes
4189+
4190+
"""
4191+
cdef SCIP_NODE** _children
4192+
cdef int n_children
4193+
cdef int i
4194+
4195+
PY_SCIP_CALL(SCIPgetChildren(self._scip, &_children, &n_children))
4196+
4197+
return [Node.create(_children[i]) for i in range(n_children)]
4198+
4199+
def getSiblings(self):
4200+
"""
4201+
Gets the siblings of the focus node.
41814202
4203+
Returns
4204+
-------
4205+
list of Nodes
4206+
4207+
"""
4208+
cdef SCIP_NODE** _siblings
4209+
cdef int n_siblings
4210+
cdef int i
4211+
4212+
PY_SCIP_CALL(SCIPgetSiblings(self._scip, &_siblings, &n_siblings))
4213+
4214+
return [Node.create(_siblings[i]) for i in range(n_siblings)]
4215+
4216+
def getLeaves(self):
4217+
"""
4218+
Gets the leaves of the tree along with number of leaves.
4219+
4220+
Returns
4221+
-------
4222+
list of Nodes
4223+
4224+
"""
4225+
cdef SCIP_NODE** _leaves
4226+
cdef int n_leaves
4227+
cdef int i
4228+
4229+
PY_SCIP_CALL(SCIPgetLeaves(self._scip, &_leaves, &n_leaves))
4230+
4231+
return [Node.create(_leaves[i]) for i in range(n_leaves)]
4232+
41824233
def getBestSibling(self):
41834234
"""
41844235
Gets the best sibling of the focus node w.r.t. the node selection strategy.
@@ -6410,6 +6461,18 @@ cdef class Model:
64106461
64116462
"""
64126463
PY_SCIP_CALL(SCIPsetConsInitial(self._scip, cons.scip_cons, newInit))
6464+
6465+
def setModifiable(self, Constraint cons, newMod):
6466+
"""
6467+
Set "modifiable" flag of a constraint.
6468+
6469+
Parameters
6470+
----------
6471+
cons : Constraint
6472+
newMod : bool
6473+
6474+
"""
6475+
PY_SCIP_CALL(SCIPsetConsModifiable(self._scip, cons.scip_cons, newMod))
64136476

64146477
def setRemovable(self, Constraint cons, newRem):
64156478
"""
@@ -7615,6 +7678,7 @@ cdef class Model:
76157678
PY_SCIP_CALL(SCIPactivatePricer(self._scip, scip_pricer))
76167679
pricer.model = <Model>weakref.proxy(self)
76177680
Py_INCREF(pricer)
7681+
pricer.scip_pricer = scip_pricer
76187682

76197683
def includeConshdlr(self, Conshdlr conshdlr, name, desc, sepapriority=0,
76207684
enfopriority=0, chckpriority=0, sepafreq=-1, propfreq=-1,
@@ -7676,6 +7740,18 @@ cdef class Model:
76767740
conshdlr.name = name
76777741
Py_INCREF(conshdlr)
76787742

7743+
def deactivatePricer(self, Pricer pricer):
7744+
"""
7745+
Deactivate the given pricer.
7746+
7747+
Parameters
7748+
----------
7749+
pricer : Pricer
7750+
the pricer to deactivate
7751+
"""
7752+
cdef SCIP_PRICER* scip_pricer
7753+
PY_SCIP_CALL(SCIPdeactivatePricer(self._scip, pricer.scip_pricer))
7754+
76797755
def copyLargeNeighborhoodSearch(self, to_fix, fix_vals) -> Model:
76807756
"""
76817757
Creates a configured copy of the transformed problem and applies provided fixings intended for LNS heuristics.

tests/test_node.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from pyscipopt import SCIP_RESULT, Eventhdlr, SCIP_EVENTTYPE
1+
from pyscipopt import SCIP_RESULT, Eventhdlr, SCIP_EVENTTYPE, scip
22
from helpers.utils import random_mip_1
33

44
class cutoffEventHdlr(Eventhdlr):
@@ -18,4 +18,43 @@ def test_cutoffNode():
1818

1919
m.optimize()
2020

21+
assert m.getNSols() == 0
22+
23+
class focusEventHdlr(Eventhdlr):
24+
def eventinit(self):
25+
self.model.catchEvent(SCIP_EVENTTYPE.NODEFOCUSED, self)
26+
27+
def eventexec(self, event):
28+
assert self.model.getNSiblings() in [0,1]
29+
assert len(self.model.getSiblings()) == self.model.getNSiblings()
30+
for node in self.model.getSiblings():
31+
assert isinstance(node, scip.Node)
32+
33+
assert self.model.getNLeaves() >= 0
34+
assert len(self.model.getLeaves()) == self.model.getNLeaves()
35+
for node in self.model.getLeaves():
36+
assert isinstance(node, scip.Node)
37+
38+
assert self.model.getNChildren() >= 0
39+
assert len(self.model.getChildren()) == self.model.getNChildren()
40+
for node in self.model.getChildren():
41+
assert isinstance(node, scip.Node)
42+
43+
leaves, children, siblings = self.model.getOpenNodes()
44+
assert leaves == self.model.getLeaves()
45+
assert children == self.model.getChildren()
46+
assert siblings == self.model.getSiblings()
47+
48+
return {'result': SCIP_RESULT.SUCCESS}
49+
50+
def test_tree_methods():
51+
m = random_mip_1(disable_heur=True, disable_presolve=True, disable_sepa=True)
52+
m.setParam("limits/nodes", 10)
53+
54+
hdlr = focusEventHdlr()
55+
56+
m.includeEventhdlr(hdlr, "test", "test")
57+
58+
m.optimize()
59+
2160
assert m.getNSols() == 0

tests/test_pricer.py

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ def pricerredcost(self):
6767
self.data['patterns'].append(newPattern)
6868
self.data['var'].append(newVar)
6969

70+
if self.data["deactivate"]:
71+
# Testing deactivatePricer
72+
self.model.deactivatePricer(self)
73+
for c in self.model.getConss():
74+
self.model.setModifiable(c, False)
75+
7076
return {'result':SCIP_RESULT.SUCCESS}
7177

7278
# The initialisation function for the variable pricer to retrieve the transformed constraints of the problem
@@ -124,6 +130,7 @@ def test_cuttingstock():
124130
pricer.data['rollLength'] = rollLength
125131
pricer.data['patterns'] = patterns
126132
pricer.data['redcosts'] = []
133+
pricer.data["deactivate"] = False
127134

128135
# solve problem
129136
s.optimize()
@@ -174,4 +181,67 @@ class IncompletePricer(Pricer):
174181
model.includePricer(pricer, "", "")
175182

176183
with pytest.raises(Exception):
177-
model.optimize()
184+
model.optimize()
185+
186+
def test_deactivate_pricer():
187+
# create solver instance
188+
s = Model("CuttingStock")
189+
190+
s.setPresolve(0)
191+
s.data = {}
192+
s.data["nSols"] = 0
193+
194+
# creating a pricer
195+
pricer = CutPricer()
196+
s.includePricer(pricer, "CuttingStockPricer", "Pricer to identify new cutting stock patterns")
197+
198+
# item widths
199+
widths = [14, 31, 36, 45]
200+
# width demand
201+
demand = [211, 395, 610, 97]
202+
# roll length
203+
rollLength = 100
204+
assert len(widths) == len(demand)
205+
206+
# adding the initial variables
207+
cutPatternVars = []
208+
varNames = []
209+
varBaseName = "Pattern"
210+
patterns = []
211+
212+
for i in range(len(widths)):
213+
varNames.append(varBaseName + "_" + str(i))
214+
cutPatternVars.append(s.addVar(varNames[i], obj = 1.0))
215+
216+
# adding a linear constraint for the knapsack constraint
217+
demandCons = []
218+
for i in range(len(widths)):
219+
numWidthsPerRoll = float(int(rollLength/widths[i]))
220+
demandCons.append(s.addCons(numWidthsPerRoll*cutPatternVars[i] >= demand[i],
221+
separate = False, modifiable = True))
222+
newPattern = [0]*len(widths)
223+
newPattern[i] = numWidthsPerRoll
224+
patterns.append(newPattern)
225+
226+
# Setting the pricer_data for use in the init and redcost functions
227+
pricer.data = {}
228+
pricer.data['var'] = cutPatternVars
229+
pricer.data['cons'] = demandCons
230+
pricer.data['widths'] = widths
231+
pricer.data['demand'] = demand
232+
pricer.data['rollLength'] = rollLength
233+
pricer.data['patterns'] = patterns
234+
pricer.data['redcosts'] = []
235+
pricer.data["deactivate"] = True
236+
237+
for c in s.getConss():
238+
c.isModifiable()
239+
240+
# solve problem
241+
s.optimize()
242+
243+
for c in s.getConss():
244+
assert not c.isModifiable()
245+
246+
# the optimal solution with normal pricing
247+
assert s.isGT(s.getObjVal(), 452.25)

0 commit comments

Comments
 (0)