@@ -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