From 2a8dc58a633c74d8f12ff34bdfab9ea6a4d8f895 Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Wed, 17 Dec 2025 12:12:58 +0100 Subject: [PATCH 01/11] fix getTermsQuadratic --- src/pyscipopt/scip.pxi | 93 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 86 insertions(+), 7 deletions(-) diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 14741531d..04346a79d 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -5730,6 +5730,13 @@ cdef class Model: PyCons = Constraint.create(scip_cons) + # Store the original polynomial expression on the constraint so that + # helpers such as getTermsQuadratic can reconstruct full linear terms + # even if SCIP's internal quadratic representation does not expose + # all linear coefficients explicitly. + if PyCons.data is None: + PyCons.data = quadcons.expr + return PyCons def _createConsNonlinear(self, cons, **kwargs): @@ -6064,6 +6071,11 @@ cdef class Model: PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons)) pycons = Constraint.create(scip_cons) + # propagate any problem data (such as the original Expr for + # expression-based constraints) from the temporary constraint + # created in createConsFromExpr to the final Constraint object + # that is returned to the user + pycons.data = (pycons_initial).data PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons)) return pycons @@ -8078,6 +8090,18 @@ cdef class Model: """ Retrieve bilinear, quadratic, and linear terms of a quadratic constraint. + According to SCIP's quadratic expression interface, a quadratic + expression is represented as + + $x^T Q x + b^T x + c^T y + d$, + + where variables in $x$ appear in the quadratic part (matrix $Q$) and + variables in $y$ appear only linearly. The coefficients for $c^T y$ + (purely linear part) are returned as ``linterms``. For variables that + appear in the quadratic part (the $x$ variables), their associated + linear coefficients $b$ are stored together with the quadratic term + and are returned as part of ``quadterms``. + Parameters ---------- cons : Constraint @@ -8085,8 +8109,17 @@ cdef class Model: Returns ------- bilinterms : list of tuple + Triples ``(var1, var2, coef)`` for terms of the form + ``coef * var1 * var2`` with ``var1 != var2``. quadterms : list of tuple + Triples ``(var, sqrcoef, lincoef)`` corresponding to diagonal + quadratic terms of the form ``sqrcoef * var**2`` and the linear + coefficient ``lincoef`` associated with the same variable when it + also appears linearly in the quadratic part. linterms : list of tuple + Pairs ``(var, coef)`` for variables that appear only in the pure + linear part (the ``c^T y`` term above), i.e., variables that do + not participate in any quadratic or bilinear term. """ cdef SCIP_EXPR* expr @@ -8117,15 +8150,36 @@ cdef class Model: assert self.checkQuadraticNonlinear(cons), "constraint is not quadratic" expr = SCIPgetExprNonlinear(cons.scip_cons) - SCIPexprGetQuadraticData(expr, NULL, &nlinvars, &linexprs, &lincoefs, &nquadterms, &nbilinterms, NULL, NULL) + SCIPexprGetQuadraticData(expr, NULL, &nlinvars, &linexprs, &lincoefs, + &nquadterms, &nbilinterms, NULL, NULL) linterms = [] bilinterms = [] quadterms = [] - for termidx in range(nlinvars): - var = Variable.create(SCIPgetVarExprVar(linexprs[termidx])) - linterms.append((var, lincoefs[termidx])) + # First try to recover all linear coefficients from the original + # polynomial expression, if it has been stored on the Constraint. + if isinstance(cons.data, Expr): + lindict = {} + for term, coef in cons.data.terms.items(): + if coef == 0.0: + continue + if len(term) == 1: + var = term[0] + key = var.ptr() + if key in lindict: + _, oldcoef = lindict[key] + lindict[key] = (var, oldcoef + coef) + else: + lindict[key] = (var, coef) + for _, (var, coef) in lindict.items(): + linterms.append((var, coef)) + else: + # use only the purely linear part as exposed by SCIP's quadratic representation + for termidx in range(nlinvars): + scipvar1 = SCIPgetVarExprVar(linexprs[termidx]) + var = Variable.create(scipvar1) + linterms.append((var, lincoefs[termidx])) for termidx in range(nbilinterms): SCIPexprGetQuadraticBilinTerm(expr, termidx, &bilinterm1, &bilinterm2, &bilincoef, NULL, NULL) @@ -8137,13 +8191,13 @@ cdef class Model: bilinterms.append((var1,var2,bilincoef)) else: quadterms.append((var1,bilincoef,0.0)) - for termidx in range(nquadterms): SCIPexprGetQuadraticQuadTerm(expr, termidx, NULL, &lincoef, &sqrcoef, NULL, NULL, &sqrexpr) if sqrexpr == NULL: continue - var = Variable.create(SCIPgetVarExprVar(sqrexpr)) - quadterms.append((var,sqrcoef,lincoef)) + scipvar1 = SCIPgetVarExprVar(sqrexpr) + var = Variable.create(scipvar1) + quadterms.append((var, sqrcoef, lincoef)) return (bilinterms, quadterms, linterms) @@ -8488,6 +8542,31 @@ cdef class Model: return _dualsol + def getVarFarkasCoef(self, Variable var): + """ + Returns the Farkas coefficient of the variable in the current node's LP relaxation; the current node has to have an infeasible LP. + + Parameters + ---------- + var : Variable + variable to get the farkas coefficient of + + Returns + ------- + float + + """ + assert SCIPgetStatus(self._scip) == SCIP_STATUS_INFEASIBLE, "Method can only be called with an infeasible model." + + farkas_coef = None + try: + farkas_coef = SCIPgetVarFarkasCoef(self._scip, var.scip_var) + if self.getObjectiveSense() == "maximize": + farkas_coef = -farkas_coef + except: + raise Warning("no farkas coefficient available for variable " + var.name) + return farkas_coef + def optimize(self): """Optimize the problem.""" PY_SCIP_CALL(SCIPsolve(self._scip)) From 4a7def62c108f0fcca1b36e0b3b6175c7485e057 Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Wed, 17 Dec 2025 12:13:20 +0100 Subject: [PATCH 02/11] test and changelog --- CHANGELOG.md | 2 +- src/pyscipopt/scip.pxi | 17 ++------------ tests/test_nonlinear.py | 50 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4280e772c..00d127914 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Unreleased ### Added ### Fixed -- all fundamental callbacks now raise an error if not implemented +- getTermsQuadratic() now correctly returns all linear terms ### Changed ### Removed diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 04346a79d..6d378fb56 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -8090,18 +8090,6 @@ cdef class Model: """ Retrieve bilinear, quadratic, and linear terms of a quadratic constraint. - According to SCIP's quadratic expression interface, a quadratic - expression is represented as - - $x^T Q x + b^T x + c^T y + d$, - - where variables in $x$ appear in the quadratic part (matrix $Q$) and - variables in $y$ appear only linearly. The coefficients for $c^T y$ - (purely linear part) are returned as ``linterms``. For variables that - appear in the quadratic part (the $x$ variables), their associated - linear coefficients $b$ are stored together with the quadratic term - and are returned as part of ``quadterms``. - Parameters ---------- cons : Constraint @@ -8117,9 +8105,8 @@ cdef class Model: coefficient ``lincoef`` associated with the same variable when it also appears linearly in the quadratic part. linterms : list of tuple - Pairs ``(var, coef)`` for variables that appear only in the pure - linear part (the ``c^T y`` term above), i.e., variables that do - not participate in any quadratic or bilinear term. + Pairs ``(var, coef)`` for variables that appear only in the pure, i.e., + variables that do not participate in any quadratic or bilinear term. """ cdef SCIP_EXPR* expr diff --git a/tests/test_nonlinear.py b/tests/test_nonlinear.py index 383532f2e..baa01f83a 100644 --- a/tests/test_nonlinear.py +++ b/tests/test_nonlinear.py @@ -288,6 +288,56 @@ def test_quad_coeffs(): assert linterms[0][0].name == z.name assert linterms[0][1] == 4 + +def test_quad_coeffs_mixed_linear_and_quadratic(): + """ensure getTermsQuadratic exposes all linear coefficients + + ``linterms`` contains purely linear variables (those that do not appear + in the quadratic part). For variables that also appear quadratically, + their linear coefficients are stored in the ``lincoef`` component of + ``quadterms``. + """ + scip = Model() + + var1 = scip.addVar(name="var1", vtype='C', lb=None) + var2 = scip.addVar(name="var2", vtype='C') + var3 = scip.addVar(name="var3", vtype='B') + var4 = scip.addVar(name="var4", vtype='B') + + cons = scip.addCons( + 8 * var4 + + 4 * var3 + - 5 * var2 + + 6 * var3 ** 2 + - 3 * var1 ** 2 + + 2 * var2 * var1 + + 7 * var1 * var3 + == -2 + ) + + bilinterms, quadterms, linterms = scip.getTermsQuadratic(cons) + + # Linear part: getTermsQuadratic must report all linear coefficients, + # including those of variables that also appear quadratically or + # bilinearly. + lin_only = {v.name: c for (v, c) in linterms} + assert lin_only["var4"] == 8 + assert lin_only["var3"] == 4 + assert lin_only["var2"] == -5 + + # For completeness, checking if the coefficients from reconstructing the full linear + # coefficients from both linterms and quadterms match + full_lin = {} + for v, c in linterms: + full_lin[v.name] = full_lin.get(v.name, 0.0) + c + for v, _, lincoef in quadterms: + if lincoef != 0.0: + full_lin[v.name] = full_lin.get(v.name, 0.0) + lincoef + + assert full_lin["var4"] == 8 + assert full_lin["var3"] == 4 + assert full_lin["var2"] == -5 + def test_addExprNonLinear(): m = Model() x = m.addVar("x", lb=0, ub=1, obj=10) From 34666bf0588a5262069251dcaccdb444a9971d74 Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Wed, 17 Dec 2025 12:14:11 +0100 Subject: [PATCH 03/11] add getVarFarkasCoef (no test) --- CHANGELOG.md | 1 + src/pyscipopt/scip.pxd | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00d127914..25ebd8a73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased ### Added +- Added getVarFarkasCoef() (untested) ### Fixed - getTermsQuadratic() now correctly returns all linear terms ### Changed diff --git a/src/pyscipopt/scip.pxd b/src/pyscipopt/scip.pxd index b9bffc1d6..9f3110853 100644 --- a/src/pyscipopt/scip.pxd +++ b/src/pyscipopt/scip.pxd @@ -1001,6 +1001,7 @@ cdef extern from "scip/scip.h": SCIP_Real SCIPgetDualboundRoot(SCIP* scip) SCIP_Real SCIPgetVarRedcost(SCIP* scip, SCIP_VAR* var) SCIP_RETCODE SCIPgetDualSolVal(SCIP* scip, SCIP_CONS* cons, SCIP_Real* dualsolval, SCIP_Bool* boundconstraint) + SCIP_Real SCIPgetVarFarkasCoef(SCIP* scip, SCIP_VAR* var) # Reader plugin SCIP_RETCODE SCIPincludeReader(SCIP* scip, From 92da4abccad40fe7c137b93b65f0521f9f097dfb Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Wed, 17 Dec 2025 12:18:44 +0100 Subject: [PATCH 04/11] add back deleted changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25ebd8a73..bcfe07c6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added - Added getVarFarkasCoef() (untested) ### Fixed +- all fundamental callbacks now raise an error if not implemented - getTermsQuadratic() now correctly returns all linear terms ### Changed ### Removed From 6ab6c2c7df7b43c58131c9fee60bbfc3dd6b5449 Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Wed, 17 Dec 2025 12:28:41 +0100 Subject: [PATCH 05/11] copilot suggestions --- src/pyscipopt/scip.pxi | 7 ++++--- tests/test_nonlinear.py | 8 ++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 6d378fb56..d14371055 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -8105,8 +8105,9 @@ cdef class Model: coefficient ``lincoef`` associated with the same variable when it also appears linearly in the quadratic part. linterms : list of tuple - Pairs ``(var, coef)`` for variables that appear only in the pure, i.e., - variables that do not participate in any quadratic or bilinear term. + Pairs ``(var, coef)`` for all variables with a nonzero linear + coefficient in the constraint, including variables that also + appear in quadratic or bilinear terms. """ cdef SCIP_EXPR* expr @@ -8550,7 +8551,7 @@ cdef class Model: farkas_coef = SCIPgetVarFarkasCoef(self._scip, var.scip_var) if self.getObjectiveSense() == "maximize": farkas_coef = -farkas_coef - except: + except Exception: raise Warning("no farkas coefficient available for variable " + var.name) return farkas_coef diff --git a/tests/test_nonlinear.py b/tests/test_nonlinear.py index baa01f83a..2a5434bbd 100644 --- a/tests/test_nonlinear.py +++ b/tests/test_nonlinear.py @@ -290,13 +290,7 @@ def test_quad_coeffs(): def test_quad_coeffs_mixed_linear_and_quadratic(): - """ensure getTermsQuadratic exposes all linear coefficients - ``linterms`` contains purely linear variables (those that do not appear - in the quadratic part). For variables that also appear quadratically, - their linear coefficients are stored in the ``lincoef`` component of - ``quadterms``. - """ scip = Model() var1 = scip.addVar(name="var1", vtype='C', lb=None) @@ -324,6 +318,8 @@ def test_quad_coeffs_mixed_linear_and_quadratic(): assert lin_only["var4"] == 8 assert lin_only["var3"] == 4 assert lin_only["var2"] == -5 + # var1 has no linear component and must not appear in linterms + assert "var1" not in lin_only or lin_only.get("var1", 0.0) == 0.0 # For completeness, checking if the coefficients from reconstructing the full linear # coefficients from both linterms and quadterms match From 967fb452f71dfaa8deec40e3a9c3590940fbc1a2 Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Mon, 2 Feb 2026 14:07:22 +0000 Subject: [PATCH 06/11] correct handling of purely linear and bilinear terms --- src/pyscipopt/scip.pxi | 53 ++++++++++++++++++++++++----------------- tests/test_nonlinear.py | 19 ++++++++------- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 17ff3dd07..4bc7f1936 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -5887,13 +5887,6 @@ cdef class Model: PyCons = Constraint.create(scip_cons) - # Store the original polynomial expression on the constraint so that - # helpers such as getTermsQuadratic can reconstruct full linear terms - # even if SCIP's internal quadratic representation does not expose - # all linear coefficients explicitly. - if PyCons.data is None: - PyCons.data = quadcons.expr - return PyCons def _createConsNonlinear(self, cons, **kwargs): @@ -8310,14 +8303,13 @@ cdef class Model: Triples ``(var1, var2, coef)`` for terms of the form ``coef * var1 * var2`` with ``var1 != var2``. quadterms : list of tuple - Triples ``(var, sqrcoef, lincoef)`` corresponding to diagonal - quadratic terms of the form ``sqrcoef * var**2`` and the linear - coefficient ``lincoef`` associated with the same variable when it - also appears linearly in the quadratic part. + Triples ``(var, sqrcoef, lincoef)`` for variables that appear in + quadratic or bilinear terms. ``sqrcoef`` is the coefficient of + ``var**2``, and ``lincoef`` is the linear coefficient of ``var`` + if it also appears linearly. linterms : list of tuple - Pairs ``(var, coef)`` for all variables with a nonzero linear - coefficient in the constraint, including variables that also - appear in quadratic or bilinear terms. + Pairs ``(var, coef)`` for purely linear variables, i.e., + variables that do not participate in any quadratic or bilinear term. """ cdef SCIP_EXPR* expr @@ -8336,6 +8328,7 @@ cdef class Model: cdef int nbilinterms # quadratic terms + cdef SCIP_EXPR* quadexpr cdef SCIP_EXPR* sqrexpr cdef SCIP_Real sqrcoef cdef int nquadterms @@ -8353,12 +8346,15 @@ cdef class Model: linterms = [] bilinterms = [] - quadterms = [] + # Purely linear terms (variables not in any quadratic/bilinear term) for termidx in range(nlinvars): var = self._getOrCreateVar(SCIPgetVarExprVar(linexprs[termidx])) linterms.append((var, lincoefs[termidx])) + # Collect quadratic terms in a dict so we can merge entries for the same variable. + quaddict = {} # var.ptr() -> [var, sqrcoef, lincoef] + for termidx in range(nbilinterms): SCIPexprGetQuadraticBilinTerm(expr, termidx, &bilinterm1, &bilinterm2, &bilincoef, NULL, NULL) scipvar1 = SCIPgetVarExprVar(bilinterm1) @@ -8366,15 +8362,28 @@ cdef class Model: var1 = self._getOrCreateVar(scipvar1) var2 = self._getOrCreateVar(scipvar2) if scipvar1 != scipvar2: - bilinterms.append((var1,var2,bilincoef)) + bilinterms.append((var1, var2, bilincoef)) else: - quadterms.append((var1,bilincoef,0.0)) + # Squared term reported as bilinear var*var + key = var1.ptr() + if key in quaddict: + quaddict[key][1] += bilincoef + else: + quaddict[key] = [var1, bilincoef, 0.0] + + # Also collect linear coefficients from the quadratic terms for termidx in range(nquadterms): - SCIPexprGetQuadraticQuadTerm(expr, termidx, NULL, &lincoef, &sqrcoef, NULL, NULL, &sqrexpr) - if sqrexpr == NULL: - continue - var = self._getOrCreateVar(SCIPgetVarExprVar(sqrexpr)) - quadterms.append((var,sqrcoef,lincoef)) + SCIPexprGetQuadraticQuadTerm(expr, termidx, &quadexpr, &lincoef, &sqrcoef, NULL, NULL, &sqrexpr) + scipvar1 = SCIPgetVarExprVar(quadexpr) + var = self._getOrCreateVar(scipvar1) + key = var.ptr() + if key in quaddict: + quaddict[key][1] += sqrcoef + quaddict[key][2] += lincoef + else: + quaddict[key] = [var, sqrcoef, lincoef] + + quadterms = [(entry[0], entry[1], entry[2]) for entry in quaddict.values()] return (bilinterms, quadterms, linterms) diff --git a/tests/test_nonlinear.py b/tests/test_nonlinear.py index 2a5434bbd..55fe6900c 100644 --- a/tests/test_nonlinear.py +++ b/tests/test_nonlinear.py @@ -311,18 +311,19 @@ def test_quad_coeffs_mixed_linear_and_quadratic(): bilinterms, quadterms, linterms = scip.getTermsQuadratic(cons) - # Linear part: getTermsQuadratic must report all linear coefficients, - # including those of variables that also appear quadratically or - # bilinearly. + # linterms contains only purely linear variables (not in any quadratic/bilinear term) lin_only = {v.name: c for (v, c) in linterms} assert lin_only["var4"] == 8 - assert lin_only["var3"] == 4 - assert lin_only["var2"] == -5 - # var1 has no linear component and must not appear in linterms - assert "var1" not in lin_only or lin_only.get("var1", 0.0) == 0.0 + assert len(linterms) == 1 # only var4 is purely linear - # For completeness, checking if the coefficients from reconstructing the full linear - # coefficients from both linterms and quadterms match + # quadterms contains all variables that appear in quadratic/bilinear terms, + # with both their squared coefficient and linear coefficient + quad_dict = {v.name: (sqrcoef, lincoef) for v, sqrcoef, lincoef in quadterms} + assert quad_dict["var3"] == (6.0, 4.0) # 6*var3^2 + 4*var3 + assert quad_dict["var1"] == (-3.0, 0.0) # -3*var1^2, no linear term + assert quad_dict["var2"] == (0.0, -5.0) # -5*var2, no squared term + + # Verify we can reconstruct all linear coefficients by combining linterms and quadterms full_lin = {} for v, c in linterms: full_lin[v.name] = full_lin.get(v.name, 0.0) + c From 544dc9faead9d2f1579a643bf99fdb6a1e6eadc0 Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Mon, 2 Feb 2026 14:34:32 +0000 Subject: [PATCH 07/11] add stub --- src/pyscipopt/scip.pyi | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index 097235b78..88e13c735 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -1238,6 +1238,7 @@ class Model: self, var: Incomplete, solVal: Incomplete ) -> Incomplete: ... def getVarRedcost(self, var: Incomplete) -> Incomplete: ... + def getVarFarkasCoef(self, var: Incomplete) -> Incomplete: ... def getVarStrongbranch( self, var: Incomplete, From d3ad47608ccf86dda9c06917f69a311f27d68528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Dion=C3=ADsio?= <57299939+Joao-Dionisio@users.noreply.github.com> Date: Tue, 3 Feb 2026 15:34:57 +0000 Subject: [PATCH 08/11] Apply suggestion from @Joao-Dionisio --- src/pyscipopt/scip.pxi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 4bc7f1936..bcfb4017d 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -8368,7 +8368,7 @@ cdef class Model: key = var1.ptr() if key in quaddict: quaddict[key][1] += bilincoef - else: + else: # TODO: SCIP handles expr like x**2 appropriately, but PySCIPOpt requires this. Need to investigate why. quaddict[key] = [var1, bilincoef, 0.0] # Also collect linear coefficients from the quadratic terms From 31e1e11af74212fbe0fd361520c12801e7b0a198 Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Tue, 3 Feb 2026 16:10:57 +0000 Subject: [PATCH 09/11] Remove getVarFarkasCoef --- CHANGELOG.md | 1 - src/pyscipopt/scip.pxd | 1 - src/pyscipopt/scip.pxi | 27 ++------------------------- src/pyscipopt/scip.pyi | 1 - 4 files changed, 2 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1838f5033..0d4fe9d67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,6 @@ ## Unreleased ### Added -- Added getVarFarkasCoef() (untested) - Added automated script for generating type stubs - Include parameter names in type stubs - Added pre-commit hook for automatic stub regeneration (see .pre-commit-config.yaml) diff --git a/src/pyscipopt/scip.pxd b/src/pyscipopt/scip.pxd index c8f8f30b7..2e78296e3 100644 --- a/src/pyscipopt/scip.pxd +++ b/src/pyscipopt/scip.pxd @@ -1014,7 +1014,6 @@ cdef extern from "scip/scip.h": SCIP_Real SCIPgetDualboundRoot(SCIP* scip) SCIP_Real SCIPgetVarRedcost(SCIP* scip, SCIP_VAR* var) SCIP_RETCODE SCIPgetDualSolVal(SCIP* scip, SCIP_CONS* cons, SCIP_Real* dualsolval, SCIP_Bool* boundconstraint) - SCIP_Real SCIPgetVarFarkasCoef(SCIP* scip, SCIP_VAR* var) # Reader plugin SCIP_RETCODE SCIPincludeReader(SCIP* scip, diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index da92813c6..d1d880b4c 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -8373,6 +8373,8 @@ cdef class Model: if scipvar1 != scipvar2: bilinterms.append((var1, var2, bilincoef)) else: + # DEBUG: Check if this case ever occurs + print(f"DEBUG: bilinear term with var1 == var2 detected! cons={cons.name}, var={var1.name}, coef={bilincoef}") # Squared term reported as bilinear var*var key = var1.ptr() if key in quaddict: @@ -8740,31 +8742,6 @@ cdef class Model: return _dualsol - def getVarFarkasCoef(self, Variable var): - """ - Returns the Farkas coefficient of the variable in the current node's LP relaxation; the current node has to have an infeasible LP. - - Parameters - ---------- - var : Variable - variable to get the farkas coefficient of - - Returns - ------- - float - - """ - assert SCIPgetStatus(self._scip) == SCIP_STATUS_INFEASIBLE, "Method can only be called with an infeasible model." - - farkas_coef = None - try: - farkas_coef = SCIPgetVarFarkasCoef(self._scip, var.scip_var) - if self.getObjectiveSense() == "maximize": - farkas_coef = -farkas_coef - except Exception: - raise Warning("no farkas coefficient available for variable " + var.name) - return farkas_coef - def optimize(self): """Optimize the problem.""" PY_SCIP_CALL(SCIPsolve(self._scip)) diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index 2f342a1a3..e8bbc46d5 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -1244,7 +1244,6 @@ class Model: self, var: Incomplete, solVal: Incomplete ) -> Incomplete: ... def getVarRedcost(self, var: Incomplete) -> Incomplete: ... - def getVarFarkasCoef(self, var: Incomplete) -> Incomplete: ... def getVarStrongbranch( self, var: Incomplete, From 2fece09e22defc2c6c3d108f23ccaa2b477a5867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Dion=C3=ADsio?= <57299939+Joao-Dionisio@users.noreply.github.com> Date: Tue, 3 Feb 2026 16:31:13 +0000 Subject: [PATCH 10/11] Apply suggestions from code review Co-authored-by: DominikKamp <130753997+DominikKamp@users.noreply.github.com> --- src/pyscipopt/scip.pxi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index d1d880b4c..198c31d84 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -8394,7 +8394,7 @@ cdef class Model: else: quaddict[key] = [var, sqrcoef, lincoef] - quadterms = [(entry[0], entry[1], entry[2]) for entry in quaddict.values()] + quadterms = [tuple(entry) for entry in quaddict.values()] return (bilinterms, quadterms, linterms) From f9249d1ead62bcb8d870eec694dfd9f975cfeeaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Dion=C3=ADsio?= <57299939+Joao-Dionisio@users.noreply.github.com> Date: Tue, 3 Feb 2026 16:31:31 +0000 Subject: [PATCH 11/11] Apply suggestion from @Joao-Dionisio --- src/pyscipopt/scip.pxi | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 198c31d84..9c21942bd 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -8373,8 +8373,6 @@ cdef class Model: if scipvar1 != scipvar2: bilinterms.append((var1, var2, bilincoef)) else: - # DEBUG: Check if this case ever occurs - print(f"DEBUG: bilinear term with var1 == var2 detected! cons={cons.name}, var={var1.name}, coef={bilincoef}") # Squared term reported as bilinear var*var key = var1.ptr() if key in quaddict: