From c3ba9e93db1b65634255dac43d5dab56edb9b750 Mon Sep 17 00:00:00 2001 From: Justin Okamoto Date: Fri, 23 Jan 2026 21:21:12 +0000 Subject: [PATCH] Add `getPrimalDualIntegral()` from C API * Expose SCIPgetPrimalDualIntegral * Add test * Update CHANGELOG.md --- CHANGELOG.md | 1 + src/pyscipopt/scip.pxd | 1 + src/pyscipopt/scip.pxi | 10 ++++++++++ src/pyscipopt/scip.pyi | 1 + tests/test_statistics.py | 7 +++++++ 5 files changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cfed00f5..0bfca7d74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Added pre-commit hook for automatic stub regeneration (see .pre-commit-config.yaml) - Wrapped isObjIntegral() and test - Added structured_optimization_trace recipe for structured optimization progress tracking +- Added methods: getPrimalDualIntegral() ### Fixed - getBestSol() now returns None for infeasible problems instead of a Solution with NULL pointer - all fundamental callbacks now raise an error if not implemented diff --git a/src/pyscipopt/scip.pxd b/src/pyscipopt/scip.pxd index 7b24dc03d..d86bae9c8 100644 --- a/src/pyscipopt/scip.pxd +++ b/src/pyscipopt/scip.pxd @@ -1473,6 +1473,7 @@ cdef extern from "scip/scip.h": int SCIPgetPlungeDepth(SCIP* scip) SCIP_Longint SCIPgetNNodeLPIterations(SCIP* scip) SCIP_Longint SCIPgetNStrongbranchLPIterations(SCIP* scip) + SCIP_Real SCIPgetPrimalDualIntegral(SCIP* scip) # Parameter Functions SCIP_RETCODE SCIPsetBoolParam(SCIP* scip, char* name, SCIP_Bool value) diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index ac49eff98..213d70675 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -3385,6 +3385,16 @@ cdef class Model: """ return SCIPgetNStrongbranchLPIterations(self._scip) + def getPrimalDualIntegral(self): + """ + Recomputes and returns the primal dual gap stored in the stats + + Returns + ------ + float + """ + return SCIPgetPrimalDualIntegral(self._scip) + def cutoffNode(self, Node node): """ marks node and whole subtree to be cut off from the branch and bound tree. diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index 61c4ba773..4bbe3e911 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -1129,6 +1129,7 @@ class Model: def getNSols(self) -> Incomplete: ... def getNSolsFound(self) -> Incomplete: ... def getNStrongbranchLPIterations(self) -> Incomplete: ... + def getPrimalDualIntegral(self) -> Incomplete: ... def getNTotalNodes(self) -> Incomplete: ... def getNVars(self, transformed: Incomplete = ...) -> Incomplete: ... def getNVarsAnd(self, and_cons: Incomplete) -> Incomplete: ... diff --git a/tests/test_statistics.py b/tests/test_statistics.py index fb4194547..fe030a385 100644 --- a/tests/test_statistics.py +++ b/tests/test_statistics.py @@ -12,3 +12,10 @@ def test_statistics_json(): 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() + + assert isinstance(primal_dual_integral, float)