Skip to content

Commit 2a8dc58

Browse files
committed
fix getTermsQuadratic
1 parent 2f473ba commit 2a8dc58

1 file changed

Lines changed: 86 additions & 7 deletions

File tree

src/pyscipopt/scip.pxi

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5730,6 +5730,13 @@ cdef class Model:
57305730

57315731
PyCons = Constraint.create(scip_cons)
57325732

5733+
# Store the original polynomial expression on the constraint so that
5734+
# helpers such as getTermsQuadratic can reconstruct full linear terms
5735+
# even if SCIP's internal quadratic representation does not expose
5736+
# all linear coefficients explicitly.
5737+
if PyCons.data is None:
5738+
PyCons.data = quadcons.expr
5739+
57335740
return PyCons
57345741

57355742
def _createConsNonlinear(self, cons, **kwargs):
@@ -6064,6 +6071,11 @@ cdef class Model:
60646071

60656072
PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons))
60666073
pycons = Constraint.create(scip_cons)
6074+
# propagate any problem data (such as the original Expr for
6075+
# expression-based constraints) from the temporary constraint
6076+
# created in createConsFromExpr to the final Constraint object
6077+
# that is returned to the user
6078+
pycons.data = (<Constraint>pycons_initial).data
60676079
PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
60686080

60696081
return pycons
@@ -8078,15 +8090,36 @@ cdef class Model:
80788090
"""
80798091
Retrieve bilinear, quadratic, and linear terms of a quadratic constraint.
80808092
8093+
According to SCIP's quadratic expression interface, a quadratic
8094+
expression is represented as
8095+
8096+
$x^T Q x + b^T x + c^T y + d$,
8097+
8098+
where variables in $x$ appear in the quadratic part (matrix $Q$) and
8099+
variables in $y$ appear only linearly. The coefficients for $c^T y$
8100+
(purely linear part) are returned as ``linterms``. For variables that
8101+
appear in the quadratic part (the $x$ variables), their associated
8102+
linear coefficients $b$ are stored together with the quadratic term
8103+
and are returned as part of ``quadterms``.
8104+
80818105
Parameters
80828106
----------
80838107
cons : Constraint
80848108
80858109
Returns
80868110
-------
80878111
bilinterms : list of tuple
8112+
Triples ``(var1, var2, coef)`` for terms of the form
8113+
``coef * var1 * var2`` with ``var1 != var2``.
80888114
quadterms : list of tuple
8115+
Triples ``(var, sqrcoef, lincoef)`` corresponding to diagonal
8116+
quadratic terms of the form ``sqrcoef * var**2`` and the linear
8117+
coefficient ``lincoef`` associated with the same variable when it
8118+
also appears linearly in the quadratic part.
80898119
linterms : list of tuple
8120+
Pairs ``(var, coef)`` for variables that appear only in the pure
8121+
linear part (the ``c^T y`` term above), i.e., variables that do
8122+
not participate in any quadratic or bilinear term.
80908123
80918124
"""
80928125
cdef SCIP_EXPR* expr
@@ -8117,15 +8150,36 @@ cdef class Model:
81178150
assert self.checkQuadraticNonlinear(cons), "constraint is not quadratic"
81188151

81198152
expr = SCIPgetExprNonlinear(cons.scip_cons)
8120-
SCIPexprGetQuadraticData(expr, NULL, &nlinvars, &linexprs, &lincoefs, &nquadterms, &nbilinterms, NULL, NULL)
8153+
SCIPexprGetQuadraticData(expr, NULL, &nlinvars, &linexprs, &lincoefs,
8154+
&nquadterms, &nbilinterms, NULL, NULL)
81218155

81228156
linterms = []
81238157
bilinterms = []
81248158
quadterms = []
81258159

8126-
for termidx in range(nlinvars):
8127-
var = Variable.create(SCIPgetVarExprVar(linexprs[termidx]))
8128-
linterms.append((var, lincoefs[termidx]))
8160+
# First try to recover all linear coefficients from the original
8161+
# polynomial expression, if it has been stored on the Constraint.
8162+
if isinstance(cons.data, Expr):
8163+
lindict = {}
8164+
for term, coef in cons.data.terms.items():
8165+
if coef == 0.0:
8166+
continue
8167+
if len(term) == 1:
8168+
var = term[0]
8169+
key = var.ptr()
8170+
if key in lindict:
8171+
_, oldcoef = lindict[key]
8172+
lindict[key] = (var, oldcoef + coef)
8173+
else:
8174+
lindict[key] = (var, coef)
8175+
for _, (var, coef) in lindict.items():
8176+
linterms.append((var, coef))
8177+
else:
8178+
# use only the purely linear part as exposed by SCIP's quadratic representation
8179+
for termidx in range(nlinvars):
8180+
scipvar1 = SCIPgetVarExprVar(linexprs[termidx])
8181+
var = Variable.create(scipvar1)
8182+
linterms.append((var, lincoefs[termidx]))
81298183

81308184
for termidx in range(nbilinterms):
81318185
SCIPexprGetQuadraticBilinTerm(expr, termidx, &bilinterm1, &bilinterm2, &bilincoef, NULL, NULL)
@@ -8137,13 +8191,13 @@ cdef class Model:
81378191
bilinterms.append((var1,var2,bilincoef))
81388192
else:
81398193
quadterms.append((var1,bilincoef,0.0))
8140-
81418194
for termidx in range(nquadterms):
81428195
SCIPexprGetQuadraticQuadTerm(expr, termidx, NULL, &lincoef, &sqrcoef, NULL, NULL, &sqrexpr)
81438196
if sqrexpr == NULL:
81448197
continue
8145-
var = Variable.create(SCIPgetVarExprVar(sqrexpr))
8146-
quadterms.append((var,sqrcoef,lincoef))
8198+
scipvar1 = SCIPgetVarExprVar(sqrexpr)
8199+
var = Variable.create(scipvar1)
8200+
quadterms.append((var, sqrcoef, lincoef))
81478201

81488202
return (bilinterms, quadterms, linterms)
81498203

@@ -8488,6 +8542,31 @@ cdef class Model:
84888542

84898543
return _dualsol
84908544

8545+
def getVarFarkasCoef(self, Variable var):
8546+
"""
8547+
Returns the Farkas coefficient of the variable in the current node's LP relaxation; the current node has to have an infeasible LP.
8548+
8549+
Parameters
8550+
----------
8551+
var : Variable
8552+
variable to get the farkas coefficient of
8553+
8554+
Returns
8555+
-------
8556+
float
8557+
8558+
"""
8559+
assert SCIPgetStatus(self._scip) == SCIP_STATUS_INFEASIBLE, "Method can only be called with an infeasible model."
8560+
8561+
farkas_coef = None
8562+
try:
8563+
farkas_coef = SCIPgetVarFarkasCoef(self._scip, var.scip_var)
8564+
if self.getObjectiveSense() == "maximize":
8565+
farkas_coef = -farkas_coef
8566+
except:
8567+
raise Warning("no farkas coefficient available for variable " + var.name)
8568+
return farkas_coef
8569+
84918570
def optimize(self):
84928571
"""Optimize the problem."""
84938572
PY_SCIP_CALL(SCIPsolve(self._scip))

0 commit comments

Comments
 (0)