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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased
### Added
- Added methods: `getNNodesLeft()`, `getNRuns()`, `getNReoptRuns()`, `addNNodes()` with tests
- 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
4 changes: 4 additions & 0 deletions src/pyscipopt/scip.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -1487,6 +1487,9 @@ cdef extern from "scip/scip.h":
SCIP_Longint SCIPgetNNodeLPIterations(SCIP* scip)
SCIP_Longint SCIPgetNStrongbranchLPIterations(SCIP* scip)
SCIP_Real SCIPgetPrimalDualIntegral(SCIP* scip)
int SCIPgetNRuns(SCIP* scip)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please order this roughly in the order in which they appear in the SCIP documentation?

int SCIPgetNReoptRuns(SCIP* scip)
void SCIPaddNNodes(SCIP* scip, SCIP_Longint nnodes)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you let me know what's the use of this method? Is it to let SCIP know that there were more nodes that were hidden from it somehow?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I'm guessing it's because of sub-SCIPs, etc. In any case, perhaps adding a line to the PySCIPOpt docstring explaining this would be nice.


# Parameter Functions
SCIP_RETCODE SCIPsetBoolParam(SCIP* scip, char* name, SCIP_Bool value)
Expand Down Expand Up @@ -2134,6 +2137,7 @@ cdef extern from "scip/scip_tree.h":
SCIP_RETCODE SCIPgetNSiblings(SCIP* scip)
SCIP_RETCODE SCIPgetLeaves(SCIP* scip, SCIP_NODE*** leaves, int* nleaves)
SCIP_Longint SCIPgetNLeaves(SCIP* scip)
SCIP_Longint SCIPgetNNodesLeft(SCIP* scip)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
SCIP_Longint SCIPgetNNodesLeft(SCIP* scip)
int SCIPgetNNodesLeft(SCIP* scip)

SCIP_NODE* SCIPgetBestSibling(SCIP* scip)
SCIP_NODE* SCIPgetBestLeaf(SCIP* scip)
SCIP_NODE* SCIPgetPrioChild(SCIP* scip)
Expand Down
44 changes: 44 additions & 0 deletions src/pyscipopt/scip.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -3296,6 +3296,39 @@ cdef class Model:
"""
return SCIPgetNLPIterations(self._scip)

def getNRuns(self):
"""
Gets number of branch and bound runs performed, including the current run

Returns
-------
int

"""
return SCIPgetNRuns(self._scip)

def getNReoptRuns(self):
"""
Gets number of reoptimization runs performed, including the current run

Returns
-------
int

"""
return SCIPgetNReoptRuns(self._scip)

def addNNodes(self, nnodes):
"""
Add given number to the number of processed nodes in current run and in all runs, including the focus node

Parameters
----------
nnodes : int

"""
SCIPaddNNodes(self._scip, nnodes)

def getNNodes(self):
"""
Gets number of processed nodes in current run, including the focus node.
Expand Down Expand Up @@ -3351,6 +3384,17 @@ cdef class Model:
"""
return SCIPgetNLeaves(self._scip)

def getNNodesLeft(self):
"""
Gets number of nodes left in the tree (children + siblings + leaves)

Returns
-------
int

"""
return SCIPgetNNodesLeft(self._scip)

def getNChildren(self):
"""
Gets number of children of focus node.
Expand Down
4 changes: 4 additions & 0 deletions src/pyscipopt/scip.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1262,9 +1262,13 @@ class Model:
def getMemTotal(self) -> int: ...
def getMemExternEstim(self) -> int: ...
def getNLeaves(self) -> int: ...
def getNNodesLeft(self) -> int: ...
def getNLimSolsFound(self) -> int: ...
def getNNlRows(self) -> int: ...
def getNNodeLPIterations(self) -> int: ...
def getNRuns(self) -> int: ...
def getNReoptRuns(self) -> int: ...
def addNNodes(self, nnodes: int) -> None: ...
def getNNodes(self) -> int: ...
def getNReaders(self) -> int: ...
def getNSepaRounds(self) -> int: ...
Expand Down
8 changes: 8 additions & 0 deletions tests/test_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ def eventexec(self, event):
assert children == self.model.getChildren()
assert siblings == self.model.getSiblings()

nodes_left = self.model.getNNodesLeft()
assert (
nodes_left
== self.model.getNLeaves()
+ self.model.getNChildren()
+ self.model.getNSiblings()
)

return {'result': SCIP_RESULT.SUCCESS}

def test_tree_methods():
Expand Down
44 changes: 36 additions & 8 deletions tests/test_statistics.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,49 @@
import os
from helpers.utils import random_mip_1
from json import load
import pytest

def test_statistics_json():
model = random_mip_1()

@pytest.fixture
def optimized_model():
model = random_mip_1(small=True) # Using small=True for speed across tests
model.optimize()
model.writeStatisticsJson("statistics.json")
return model


def test_statistics_json(optimized_model):
optimized_model.writeStatisticsJson("statistics.json")

with open("statistics.json", "r") as f:
data = load(f)
assert data["origprob"]["problem_name"] == "model"

os.remove("statistics.json")

def test_getPrimalDualIntegral():
model = random_mip_1(small=True)
model.optimize()
primal_dual_integral = model.getPrimalDualIntegral()

def test_getPrimalDualIntegral(optimized_model):
primal_dual_integral = optimized_model.getPrimalDualIntegral()

assert isinstance(primal_dual_integral, float)


def test_getNRuns(optimized_model):
n_runs = optimized_model.getNRuns()

assert isinstance(n_runs, int)
assert n_runs >= 1


def test_getNReoptRuns(optimized_model):
n_reopt_runs = optimized_model.getNReoptRuns()

assert isinstance(n_reopt_runs, int)
assert n_reopt_runs >= 0


def test_addNNodes(optimized_model):
initial_n_nodes = optimized_model.getNTotalNodes()
optimized_model.addNNodes(5)
new_n_nodes = optimized_model.getNTotalNodes()

assert new_n_nodes == initial_n_nodes + 5
Loading