diff --git a/CHANGELOG.md b/CHANGELOG.md index a603627b6..351f52ea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased ### Added +- Added cutoffNode and test ### Fixed ### Changed ### Removed diff --git a/src/pyscipopt/scip.pxd b/src/pyscipopt/scip.pxd index 8662f5d52..7f2e87f14 100644 --- a/src/pyscipopt/scip.pxd +++ b/src/pyscipopt/scip.pxd @@ -877,6 +877,7 @@ cdef extern from "scip/scip.h": SCIP_Real SCIPgetPrimalbound(SCIP* scip) SCIP_Real SCIPgetGap(SCIP* scip) int SCIPgetDepth(SCIP* scip) + SCIP_RETCODE SCIPcutoffNode(SCIP* scip, SCIP_NODE* node) SCIP_Bool SCIPhasPrimalRay(SCIP * scip) SCIP_Real SCIPgetPrimalRayVal(SCIP * scip, SCIP_VAR * var) SCIP_RETCODE SCIPaddSolFree(SCIP* scip, SCIP_SOL** sol, SCIP_Bool* stored) diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 4d22864c8..db08f94ce 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -1313,7 +1313,7 @@ cdef class Node: """ return SCIPnodeIsActive(self.scip_node) - + def isPropagatedAgain(self): """ Is the node marked to be propagated again? @@ -2335,6 +2335,16 @@ cdef class Model: """ return SCIPgetDepth(self._scip) + def cutoffNode(self, Node node): + """ + marks node and whole subtree to be cut off from the branch and bound tree. + + Parameters + ---------- + node : Node + """ + PY_SCIP_CALL( SCIPcutoffNode(self._scip, node.scip_node) ) + def infinity(self): """ Retrieve SCIP's infinity value. diff --git a/tests/helpers/utils.py b/tests/helpers/utils.py index 4166c3027..c9e63f4f4 100644 --- a/tests/helpers/utils.py +++ b/tests/helpers/utils.py @@ -1,7 +1,7 @@ from pyscipopt import Model, quicksum, SCIP_PARAMSETTING, exp, log, sqrt, sin from typing import List -def random_mip_1(disable_sepa=True, disable_huer=True, disable_presolve=True, node_lim=2000, small=False): +def random_mip_1(disable_sepa=True, disable_heur=True, disable_presolve=True, node_lim=2000, small=False): model = Model() x0 = model.addVar(lb=-2, ub=4) @@ -41,7 +41,7 @@ def random_mip_1(disable_sepa=True, disable_huer=True, disable_presolve=True, no if disable_sepa: model.setSeparating(SCIP_PARAMSETTING.OFF) - if disable_huer: + if disable_heur: model.setHeuristics(SCIP_PARAMSETTING.OFF) if disable_presolve: model.setPresolve(SCIP_PARAMSETTING.OFF) diff --git a/tests/test_heur.py b/tests/test_heur.py index a625475a7..b9d1f7fcc 100644 --- a/tests/test_heur.py +++ b/tests/test_heur.py @@ -124,7 +124,7 @@ def inner(): def test_simple_round_heur(): # create solver instance - s = random_mip_1(disable_sepa=False, disable_huer=False, node_lim=1) + s = random_mip_1(disable_sepa=False, disable_heur=False, node_lim=1) heuristic = SimpleRoundingHeuristic() s.includeHeur(heuristic, "SimpleRounding", "simple rounding heuristic implemented in python", "Y", timingmask=SCIP_HEURTIMING.DURINGLPLOOP) diff --git a/tests/test_node.py b/tests/test_node.py new file mode 100644 index 000000000..91e6139ae --- /dev/null +++ b/tests/test_node.py @@ -0,0 +1,21 @@ +from pyscipopt import SCIP_RESULT, Eventhdlr, SCIP_EVENTTYPE +from helpers.utils import random_mip_1 + +class cutoffEventHdlr(Eventhdlr): + def eventinit(self): + self.model.catchEvent(SCIP_EVENTTYPE.NODEFOCUSED, self) + + def eventexec(self, event): + self.model.cutoffNode(self.model.getCurrentNode()) + return {'result': SCIP_RESULT.SUCCESS} + +def test_cutoffNode(): + m = random_mip_1(disable_heur=True, disable_presolve=True, disable_sepa=True) + + hdlr = cutoffEventHdlr() + + m.includeEventhdlr(hdlr, "test", "test") + + m.optimize() + + assert m.getNSols() == 0 \ No newline at end of file diff --git a/tests/test_nogil.py b/tests/test_nogil.py index 90b6bdfb3..1da3af61a 100644 --- a/tests/test_nogil.py +++ b/tests/test_nogil.py @@ -6,7 +6,7 @@ def test_optimalNogil(): - ori_model = random_mip_1(disable_sepa=False, disable_huer=False, disable_presolve=False, node_lim=2000, small=True) + ori_model = random_mip_1(disable_sepa=False, disable_heur=False, disable_presolve=False, node_lim=2000, small=True) models = [Model(sourceModel=ori_model) for _ in range(N_Threads)] for i in range(N_Threads): models[i].setParam("randomization/permutationseed", i) diff --git a/tests/test_solution.py b/tests/test_solution.py index 4ab9fd5b6..240229c98 100644 --- a/tests/test_solution.py +++ b/tests/test_solution.py @@ -197,7 +197,7 @@ def test_getSols(): def test_getOrigin_retrasform(): - m = random_mip_1(disable_sepa=False, disable_huer=False, disable_presolve=False, small=True) + m = random_mip_1(disable_sepa=False, disable_heur=False, disable_presolve=False, small=True) m.optimize() sol = m.getBestSol() @@ -208,11 +208,11 @@ def test_getOrigin_retrasform(): def test_translate(): - m = random_mip_1(disable_sepa=False, disable_huer=False, disable_presolve=False, small=True) + m = random_mip_1(disable_sepa=False, disable_heur=False, disable_presolve=False, small=True) m.optimize() sol = m.getBestSol() - m1 = random_mip_1(disable_sepa=False, disable_huer=False, disable_presolve=False, small=True) + m1 = random_mip_1(disable_sepa=False, disable_heur=False, disable_presolve=False, small=True) sol1 = sol.translate(m1) assert m1.addSol(sol1) == True assert m1.getNSols() == 1 diff --git a/tests/test_strong_branching.py b/tests/test_strong_branching.py index fc538df75..350eda290 100644 --- a/tests/test_strong_branching.py +++ b/tests/test_strong_branching.py @@ -140,7 +140,7 @@ def branchexeclp(self, allowaddcons): def test_strong_branching(): - scip = random_mip_1(disable_presolve=False, disable_huer=False, small=True, node_lim=500) + scip = random_mip_1(disable_presolve=False, disable_heur=False, small=True, node_lim=500) strong_branch_rule = StrongBranchingRule(scip, idempotent=False) scip.includeBranchrule(strong_branch_rule, "strong branch rule", "custom strong branching rule", @@ -155,7 +155,7 @@ def test_strong_branching(): def test_strong_branching_idempotent(): - scip = random_mip_1(disable_presolve=False, disable_huer=False, small=True, node_lim=500) + scip = random_mip_1(disable_presolve=False, disable_heur=False, small=True, node_lim=500) strong_branch_rule = StrongBranchingRule(scip, idempotent=True) scip.includeBranchrule(strong_branch_rule, "strong branch rule", "custom strong branching rule", @@ -170,7 +170,7 @@ def test_strong_branching_idempotent(): def test_dummy_feature_selector(): - scip = random_mip_1(disable_presolve=False, disable_huer=False, small=True, node_lim=300) + scip = random_mip_1(disable_presolve=False, disable_heur=False, small=True, node_lim=300) feature_dummy_branch_rule = FeatureSelectorBranchingRule(scip) scip.includeBranchrule(feature_dummy_branch_rule, "dummy branch rule", "custom feature creation branching rule",