Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

## Unreleased
### Added
- Added methods: `getNNodesLeft()`, `getNRuns()`, `getNReoptRuns()`, `addNNodes()` with tests
- Added the following methods with tests: `getNNodesLeft()`, `getNRuns()`, `getNReoptRuns()`, `addNNodes()`, `getDeterministicTime()`, `getAvgDualbound()`, `getMaxTotalDepth()`, `getNBacktracks()`,\
`getFocusNode()`, `getAvgLowerbound()`, `getFirstPrimalBound()`, `getLowerboundRoot()`, `getUpperbound()`, `getNObjlimLeaves()`
- Added `addConsCumulative()` for SCIP cumulative constraints (#1222)
- `Expr` and `GenExpr` support `__pos__` magic method like `+Expr` or `+GenExpr`
- Added type annotations to most methods on the `Model` class
Expand Down
10 changes: 10 additions & 0 deletions src/pyscipopt/scip.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,7 @@ cdef extern from "scip/scip.h":
SCIP_Real SCIPgetSolvingTime(SCIP* scip)
SCIP_Real SCIPgetReadingTime(SCIP* scip)
SCIP_Real SCIPgetPresolvingTime(SCIP* scip)
SCIP_Real SCIPgetDeterministicTime(SCIP* scip)
SCIP_STAGE SCIPgetStage(SCIP* scip)
SCIP_RETCODE SCIPsetProbName(SCIP* scip, char* name)
const char* SCIPgetProbName(SCIP* scip)
Expand Down Expand Up @@ -749,6 +750,7 @@ cdef extern from "scip/scip.h":
SCIP_RETCODE SCIPpresolve(SCIP* scip)

# Node Methods
SCIP_NODE* SCIPgetFocusNode(SCIP* scip)
SCIP_NODE* SCIPgetCurrentNode(SCIP* scip)
SCIP_NODE* SCIPnodeGetParent(SCIP_NODE* node)
SCIP_Longint SCIPnodeGetNumber(SCIP_NODE* node)
Expand Down Expand Up @@ -969,6 +971,7 @@ cdef extern from "scip/scip.h":
SCIP_RETCODE SCIPprintBestTransSol(SCIP* scip, FILE* outfile, SCIP_Bool printzeros)
SCIP_RETCODE SCIPprintSol(SCIP* scip, SCIP_SOL* sol, FILE* outfile, SCIP_Bool printzeros)
SCIP_RETCODE SCIPprintTransSol(SCIP* scip, SCIP_SOL* sol, FILE* outfile, SCIP_Bool printzeros)
SCIP_Real SCIPgetFirstPrimalBound(SCIP* scip)
SCIP_Real SCIPgetPrimalbound(SCIP* scip)
SCIP_Real SCIPgetGap(SCIP* scip)
int SCIPgetDepth(SCIP* scip)
Expand Down Expand Up @@ -1480,12 +1483,19 @@ cdef extern from "scip/scip.h":
SCIP_Longint SCIPgetNTotalNodes(SCIP* scip)
SCIP_Longint SCIPgetNFeasibleLeaves(SCIP* scip)
SCIP_Longint SCIPgetNInfeasibleLeaves(SCIP* scip)
SCIP_Longint SCIPgetNObjlimLeaves(SCIP* scip)
SCIP_Longint SCIPgetNLPs(SCIP* scip)
SCIP_Longint SCIPgetNLPIterations(SCIP* scip)
int SCIPgetNSepaRounds(SCIP* scip)
SCIP_Real SCIPgetAvgLowerbound(SCIP* scip)
SCIP_Real SCIPgetAvgDualbound(SCIP* scip)
SCIP_Real SCIPgetLowerbound(SCIP* scip)
SCIP_Real SCIPgetLowerboundRoot(SCIP* scip)
SCIP_Real SCIPgetCutoffbound(SCIP* scip)
SCIP_Real SCIPgetUpperbound(SCIP* scip)
int SCIPgetMaxDepth(SCIP* scip)
int SCIPgetMaxTotalDepth(SCIP* scip)
SCIP_Longint SCIPgetNBacktracks(SCIP* scip)
int SCIPgetPlungeDepth(SCIP* scip)
SCIP_Longint SCIPgetNNodeLPIterations(SCIP* scip)
SCIP_Longint SCIPgetNStrongbranchLPIterations(SCIP* scip)
Expand Down
110 changes: 110 additions & 0 deletions src/pyscipopt/scip.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -3285,6 +3285,28 @@ cdef class Model:
"""
return SCIPgetPresolvingTime(self._scip)

def getDeterministicTime(self):
Comment thread
adamj34 marked this conversation as resolved.
"""
Computes a deterministic measure of time from statistics.

Returns
-------
float

"""
return SCIPgetDeterministicTime(self._scip)

def getFirstPrimalBound(self):
"""
Gets the primal bound of the very first solution in the original space.

Returns
-------
float

"""
return SCIPgetFirstPrimalBound(self._scip)

def getNLPIterations(self):
"""
Returns the total number of LP iterations so far.
Expand Down Expand Up @@ -3373,6 +3395,17 @@ cdef class Model:
"""
return SCIPgetNInfeasibleLeaves(self._scip)

def getNObjlimLeaves(self):
"""
Gets number of processed leaf nodes that hit LP objective limit.

Returns
-------
int

"""
return SCIPgetNObjlimLeaves(self._scip)

def getNLeaves(self):
"""
Gets number of leaves in the tree.
Expand Down Expand Up @@ -3417,6 +3450,18 @@ cdef class Model:
"""
return SCIPgetNSiblings(self._scip)

def getFocusNode(self):
"""
Gets focus node in the tree.
If we are in probing/diving mode this method returns the node in the tree where the probing/diving mode was started.

Returns
-------
Node

"""
return Node.create(SCIPgetFocusNode(self._scip))

def getCurrentNode(self):
"""
Retrieve current node.
Expand Down Expand Up @@ -3462,6 +3507,28 @@ cdef class Model:
"""
return SCIPgetMaxDepth(self._scip)

def getMaxTotalDepth(self):
"""
Gets maximal depth of all processed nodes over all branch and bound runs.

Returns
-------
int

"""
return SCIPgetMaxTotalDepth(self._scip)

def getNBacktracks(self):
"""
Gets total number of backtracks, i.e., number of times the new node was selected from the leaves queue.

Returns
-------
int

"""
return SCIPgetNBacktracks(self._scip)

def getPlungeDepth(self):
"""
Gets current plunging depth (successive selections of child/sibling nodes).
Expand All @@ -3473,6 +3540,28 @@ cdef class Model:
"""
return SCIPgetPlungeDepth(self._scip)

def getAvgLowerbound(self):
"""
Gets average lower (dual) bound of all unprocessed nodes in transformed problem.

Returns
-------
float

"""
return SCIPgetAvgLowerbound(self._scip)

def getAvgDualbound(self):
"""
Gets average dual bound of all unprocessed nodes for original problem.

Returns
-------
float

"""
return SCIPgetAvgDualbound(self._scip)

def getLowerbound(self):
"""
Gets global lower (dual) bound of the transformed problem.
Expand All @@ -3495,6 +3584,16 @@ cdef class Model:
"""
return SCIPgetCutoffbound(self._scip)

def getUpperbound(self):
"""
Gets global upper (primal) bound in transformed problem (objective value of best solution or user objective limit).

Returns
-------
float
"""
return SCIPgetUpperbound(self._scip)

def getNNodeLPIterations(self):
"""
Gets number of LP iterations used for solving node relaxations so far.
Expand Down Expand Up @@ -11506,6 +11605,17 @@ cdef class Model:
"""
return SCIPgetDualboundRoot(self._scip)

def getLowerboundRoot(self):
"""
Gets lower (dual) bound in transformed problem of the root node.

Returns
-------
float

"""
return SCIPgetLowerboundRoot(self._scip)

def writeName(self, Variable var):
"""
Write the name of the variable to the std out.
Expand Down
10 changes: 10 additions & 0 deletions src/pyscipopt/scip.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,8 @@ class Model:
list[list[float]],
dict[str, dict[str, int]],
]: ...
def getAvgLowerbound(self) -> float: ...
def getAvgDualbound(self) -> float: ...
def getBranchScoreMultiple(self, var: Variable, gains: list[float]) -> float: ...
def getCapacityKnapsack(self, cons: Constraint) -> int: ...
def getChildren(self) -> list[Node]: ...
Expand All @@ -1205,17 +1207,22 @@ class Model:
def getConsVals(self, constraint: Constraint) -> list[float] | None: ...
def getConsVars(self, constraint: Constraint) -> list[Variable] | None: ...
def getConss(self, transformed: bool = True) -> list[Constraint]: ...
def getFocusNode(self) -> Node | None: ...
def getCurrentNode(self) -> Node | None: ...
def getCutEfficacy(self, cut: Row, sol: Solution | None = None) -> float: ...
def getCutLPSolCutoffDistance(self, cut: Row, sol: Solution) -> float: ...
def getCutoffbound(self) -> float: ...
def getUpperbound(self) -> float: ...
def getDepth(self) -> int: ...
def getDeterministicTime(self) -> float: ...
def getFirstPrimalBound(self) -> float: ...
def getDualMultiplier(self, cons: Constraint) -> float: ...
def getDualSolVal(
self, cons: Constraint, boundconstraint: bool = False
) -> float: ...
def getDualbound(self) -> float: ...
def getDualboundRoot(self) -> float: ...
def getLowerboundRoot(self) -> float: ...
def getDualfarkasKnapsack(self, cons: Constraint) -> float: ...
def getDualfarkasLinear(self, cons: Constraint) -> float: ...
def getDualsolKnapsack(self, cons: Constraint) -> float: ...
Expand All @@ -1240,6 +1247,8 @@ class Model:
def getLowerbound(self) -> float: ...
def getMajorVersion(self) -> int: ...
def getMaxDepth(self) -> int: ...
def getMaxTotalDepth(self) -> int: ...
def getNBacktracks(self) -> int: ...
def getMinorVersion(self) -> int: ...
def getNBestSolsFound(self) -> int: ...
def getNBinVars(self) -> int: ...
Expand All @@ -1252,6 +1261,7 @@ class Model:
def getNFeasibleLeaves(self) -> int: ...
def getNImplVars(self) -> int: ...
def getNInfeasibleLeaves(self) -> int: ...
def getNObjlimLeaves(self) -> int: ...
def getNIntVars(self) -> int: ...
def getNLPBranchCands(self) -> int: ...
def getNLPCols(self) -> int: ...
Expand Down
37 changes: 36 additions & 1 deletion tests/test_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,39 @@ def test_tree_methods():

m.optimize()

assert m.getNSols() == 0
assert m.getNSols() == 0

class ProbingNodeChecker(Eventhdlr):
def eventinit(self):
self.model.catchEvent(SCIP_EVENTTYPE.NODEFOCUSED, self)

def eventexec(self, event):
m = self.model
focus = m.getFocusNode()
current = m.getCurrentNode()

assert isinstance(focus, scip.Node)
assert isinstance(current, scip.Node)

# focus and current node should be the same before probing
assert focus.getNumber() == current.getNumber()

m.startProbing()
m.newProbingNode()
m.newProbingNode()

# after starting probing, the focus node should remain the same, but the current node should change
assert m.getProbingDepth() == 2
assert m.getFocusNode().getNumber() == focus.getNumber()
assert m.getCurrentNode().getNumber() != current.getNumber()

m.endProbing()

return {'result': SCIP_RESULT.SUCCESS}

def test_getFocusNode_and_getCurrentNode():

m = random_mip_1(small=True)

m.includeEventhdlr(ProbingNodeChecker(), "Probing Node Checker", "test if getFocusNode and getCurrentNode work correctly")
m.optimize()
Loading
Loading