1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /*                                                                           */
3 /*                  This file is part of the program and library             */
4 /*         SCIP --- Solving Constraint Integer Programs                      */
5 /*                                                                           */
6 /*    Copyright (C) 2002-2021 Konrad-Zuse-Zentrum                            */
7 /*                            fuer Informationstechnik Berlin                */
8 /*                                                                           */
9 /*  SCIP is distributed under the terms of the ZIB Academic License.         */
10 /*                                                                           */
11 /*  You should have received a copy of the ZIB Academic License              */
12 /*  along with SCIP; see the file COPYING. If not visit scipopt.org.         */
13 /*                                                                           */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file   cons_quadratic.c
17  * @ingroup DEFPLUGINS_CONS
18  * @brief  constraint handler for quadratic constraints \f$\textrm{lhs} \leq \sum_{i,j=1}^n a_{i,j} x_i x_j + \sum_{i=1}^n b_i x_i \leq \textrm{rhs}\f$
19  * @author Stefan Vigerske
20  * @author Benjamin Mueller
21  * @author Felipe Serrano
22  *
23  * @todo SCIP might fix linear variables on +/- infinity; remove them in presolve and take care later
24  * @todo round constraint sides to integers if all coefficients and variables are (impl.) integer
25  * @todo constraints in one variable should be replaced by linear or bounddisjunction constraint
26  * @todo check if some quadratic terms appear in several constraints and try to simplify (e.g., nous1)
27  * @todo skip separation in enfolp if for current LP (check LP id) was already separated
28  * @todo watch unbounded variables to enable/disable propagation
29  * @todo sort order in bilinvar1/bilinvar2 such that the var which is involved in more terms is in bilinvar1, and use this info propagate and AddLinearReform
30  * @todo underestimate for multivariate concave quadratic terms as in cons_nonlinear
31  */
32 
33 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
34 
35 #define SCIP_PRIVATE_ROWPREP
36 
37 #include "blockmemshell/memory.h"
38 #include <ctype.h>
39 #include "nlpi/nlpi.h"
40 #include "nlpi/nlpi_ipopt.h"
41 #include "nlpi/pub_expr.h"
42 #include "nlpi/type_expr.h"
43 #include "scip/cons_and.h"
44 #include "scip/cons_bounddisjunction.h"
45 #include "scip/cons_linear.h"
46 #include "scip/cons_nonlinear.h"
47 #include "scip/cons_quadratic.h"
48 #include "scip/cons_varbound.h"
49 #include "scip/debug.h"
50 #include "scip/heur_subnlp.h"
51 #include "scip/heur_trysol.h"
52 #include "scip/intervalarith.h"
53 #include "scip/pub_cons.h"
54 #include "scip/pub_event.h"
55 #include "scip/pub_heur.h"
56 #include "scip/pub_lp.h"
57 #include "scip/pub_message.h"
58 #include "scip/pub_misc.h"
59 #include "scip/pub_misc_sort.h"
60 #include "scip/pub_nlp.h"
61 #include "scip/pub_sol.h"
62 #include "scip/pub_tree.h"
63 #include "scip/pub_var.h"
64 #include "scip/scip_branch.h"
65 #include "scip/scip_cons.h"
66 #include "scip/scip_copy.h"
67 #include "scip/scip_cut.h"
68 #include "scip/scip_event.h"
69 #include "scip/scip_general.h"
70 #include "scip/scip_heur.h"
71 #include "scip/scip_lp.h"
72 #include "scip/scip_mem.h"
73 #include "scip/scip_message.h"
74 #include "scip/scip_nlp.h"
75 #include "scip/scip_nonlinear.h"
76 #include "scip/scip_numerics.h"
77 #include "scip/scip_param.h"
78 #include "scip/scip_prob.h"
79 #include "scip/scip_probing.h"
80 #include "scip/scip_sepa.h"
81 #include "scip/scip_sol.h"
82 #include "scip/scip_solve.h"
83 #include "scip/scip_solvingstats.h"
84 #include "scip/scip_tree.h"
85 #include "scip/scip_var.h"
86 #include <string.h>
87 
88 /* Inform compiler that this code accesses the floating-point environment, so that
89  * certain optimizations should be omitted (http://www.cplusplus.com/reference/cfenv/FENV_ACCESS/).
90  * Not supported by Clang (gives warning) and GCC (silently), at the moment.
91  */
92 #if defined(__INTEL_COMPILER) || defined(_MSC_VER)
93 #pragma fenv_access (on)
94 #elif defined __GNUC__
95 #pragma STDC FENV_ACCESS ON
96 #endif
97 
98 /* constraint handler properties */
99 #define CONSHDLR_NAME          "quadratic"
100 #define CONSHDLR_DESC          "quadratic constraints of the form lhs <= b' x + x' A x <= rhs"
101 #define CONSHDLR_SEPAPRIORITY        10 /**< priority of the constraint handler for separation */
102 #define CONSHDLR_ENFOPRIORITY       -50 /**< priority of the constraint handler for constraint enforcing */
103 #define CONSHDLR_CHECKPRIORITY -4000000 /**< priority of the constraint handler for checking feasibility */
104 #define CONSHDLR_SEPAFREQ             1 /**< frequency for separating cuts; zero means to separate only in the root node */
105 #define CONSHDLR_PROPFREQ             1 /**< frequency for propagating domains; zero means only preprocessing propagation */
106 #define CONSHDLR_EAGERFREQ          100 /**< frequency for using all instead of only the useful constraints in separation,
107                                          *   propagation and enforcement, -1 for no eager evaluations, 0 for first only */
108 #define CONSHDLR_MAXPREROUNDS        -1 /**< maximal number of presolving rounds the constraint handler participates in (-1: no limit) */
109 #define CONSHDLR_DELAYSEPA        FALSE /**< should separation method be delayed, if other separators found cuts? */
110 #define CONSHDLR_DELAYPROP        FALSE /**< should propagation method be delayed, if other propagators found reductions? */
111 #define CONSHDLR_NEEDSCONS         TRUE /**< should the constraint handler be skipped, if no constraints are available? */
112 
113 #define CONSHDLR_PROP_TIMING             SCIP_PROPTIMING_BEFORELP /**< propagation timing mask of the constraint handler */
114 #define CONSHDLR_PRESOLTIMING            SCIP_PRESOLTIMING_ALWAYS /**< presolving timing of the constraint handler (fast, medium, or exhaustive) */
115 
116 #define MAXDNOM                 10000LL /**< maximal denominator for simple rational fixed values */
117 #define NONLINCONSUPGD_PRIORITY   40000 /**< priority of upgrading nonlinear constraints */
118 #define INITLPMAXVARVAL          1000.0 /**< maximal absolute value of variable for still generating a linearization cut at that point in initlp */
119 
120 /* Activating this define enables reformulation of bilinear terms x*y with implications from x to y into linear terms.
121  * However, implications are not enforced by SCIP. Thus, if, e.g., the used implication was derived from this constraint and we then reformulate the constraint,
122  * then the implication may not be enforced in a solution.
123  * This issue need to be fixed before this feature can be enabled.
124  */
125 /* #define CHECKIMPLINBILINEAR */
126 
127 /* enable new propagation for bivariate quadratic terms */
128 #define PROPBILINNEW
129 
130 /* epsilon for differentiating between a boundary and interior point */
131 #define INTERIOR_EPS 1e-1
132 
133 /* scaling factor for gauge function */
134 #define GAUGESCALE 0.99999
135 
136 #define ROWPREP_SCALEUP_VIOLNONZERO    (10.0*SCIPepsilon(scip))    /**< minimal violation for considering up-scaling of rowprep (we want to avoid upscaling very small violations) */
137 #define ROWPREP_SCALEUP_MINVIOLFACTOR  2.0                         /**< scale up will target a violation of ~MINVIOLFACTOR*minviol, where minviol is given by caller */
138 #define ROWPREP_SCALEUP_MAXMINCOEF     (1.0 / SCIPfeastol(scip))   /**< scale up only if min. coef is below this number (before scaling) */
139 #define ROWPREP_SCALEUP_MAXMAXCOEF     SCIPgetHugeValue(scip)      /**< scale up only if max. coef will not exceed this number by scaling */
140 #define ROWPREP_SCALEUP_MAXSIDE        SCIPgetHugeValue(scip)      /**< scale up only if side will not exceed this number by scaling */
141 #define ROWPREP_SCALEDOWN_MINMAXCOEF   (1.0 / SCIPfeastol(scip))   /**< scale down if max. coef is at least this number (before scaling) */
142 #define ROWPREP_SCALEDOWN_MINCOEF      SCIPfeastol(scip)           /**< scale down only if min. coef does not drop below this number by scaling */
143 
144 /*
145  * Data structures
146  */
147 
148 /** eventdata for variable bound change events in quadratic constraints */
149 struct SCIP_QuadVarEventData
150 {
151    SCIP_CONS*            cons;               /**< the constraint */
152    int                   varidx;             /**< the index of the variable which bound change is caught, positive for linear variables, negative for quadratic variables */
153    int                   filterpos;          /**< position of eventdata in SCIP's event filter */
154 };
155 
156 /** Data of a quadratic constraint. */
157 struct SCIP_ConsData
158 {
159    SCIP_Real             lhs;                /**< left hand side of constraint */
160    SCIP_Real             rhs;                /**< right hand side of constraint */
161 
162    int                   nlinvars;           /**< number of linear variables */
163    int                   linvarssize;        /**< length of linear variable arrays */
164    SCIP_VAR**            linvars;            /**< linear variables */
165    SCIP_Real*            lincoefs;           /**< coefficients of linear variables */
166    SCIP_QUADVAREVENTDATA** lineventdata;       /**< eventdata for bound change of linear variable */
167 
168    int                   nquadvars;          /**< number of variables in quadratic terms */
169    int                   quadvarssize;       /**< length of quadratic variable terms arrays */
170    SCIP_QUADVARTERM*     quadvarterms;       /**< array with quadratic variable terms */
171 
172    int                   nbilinterms;        /**< number of bilinear terms */
173    int                   bilintermssize;     /**< length of bilinear term arrays */
174    SCIP_BILINTERM*       bilinterms;         /**< bilinear terms array */
175    int*                  bilintermsidx;      /**< unique index of each bilinear term xy in the bilinestimators array of the constraint handler data */
176 
177    SCIP_NLROW*           nlrow;              /**< a nonlinear row representation of this constraint */
178 
179    unsigned int          linvarssorted:1;    /**< are the linear variables already sorted? */
180    unsigned int          linvarsmerged:1;    /**< are equal linear variables already merged? */
181    unsigned int          quadvarssorted:1;   /**< are the quadratic variables already sorted? */
182    unsigned int          quadvarsmerged:1;   /**< are equal quadratic variables already merged? */
183    unsigned int          bilinsorted:1;      /**< are the bilinear terms already sorted? */
184    unsigned int          bilinmerged:1;      /**< are equal bilinear terms (and bilinear terms with zero coefficient) already merged? */
185 
186    unsigned int          isconvex:1;         /**< is quadratic function is convex ? */
187    unsigned int          isconcave:1;        /**< is quadratic function is concave ? */
188    unsigned int          iscurvchecked:1;    /**< is quadratic function checked on convexity or concavity ? */
189    unsigned int          isremovedfixings:1; /**< did we removed fixed/aggr/multiaggr variables ? */
190    unsigned int          ispropagated:1;     /**< was the constraint propagated with respect to the current bounds ? */
191    unsigned int          ispresolved:1;      /**< did we checked for possibilities of upgrading or implicit integer variables ? */
192    unsigned int          initialmerge:1;     /**< did we perform an initial merge and clean in presolving yet ? */
193 #ifdef CHECKIMPLINBILINEAR
194    unsigned int          isimpladded:1;      /**< has there been an implication added for a binary variable in a bilinear term? */
195 #endif
196    unsigned int          isgaugeavailable:1; /**< is the gauge function computed? */
197    unsigned int          isedavailable:1;    /**< is the eigen decomposition of A available? */
198 
199    SCIP_Real             minlinactivity;     /**< sum of minimal activities of all linear terms with finite minimal activity */
200    SCIP_Real             maxlinactivity;     /**< sum of maximal activities of all linear terms with finite maximal activity */
201    int                   minlinactivityinf;  /**< number of linear terms with infinite minimal activity */
202    int                   maxlinactivityinf;  /**< number of linear terms with infinity maximal activity */
203    SCIP_INTERVAL         quadactivitybounds; /**< bounds on the activity of the quadratic term, if up to date, otherwise empty interval */
204    SCIP_Real             activity;           /**< activity of quadratic function w.r.t. current solution */
205    SCIP_Real             lhsviol;            /**< violation of lower bound by current solution (used temporarily inside constraint handler) */
206    SCIP_Real             rhsviol;            /**< violation of lower bound by current solution (used temporarily inside constraint handler) */
207 
208    int                   linvar_maydecrease; /**< index of a variable in linvars that may be decreased without making any other constraint infeasible, or -1 if none */
209    int                   linvar_mayincrease; /**< index of a variable in linvars that may be increased without making any other constraint infeasible, or -1 if none */
210 
211    SCIP_VAR**            sepaquadvars;       /**< variables corresponding to quadvarterms to use in separation, only available in solving stage */
212    int*                  sepabilinvar2pos;   /**< position of second variable in bilinear terms to use in separation, only available in solving stage */
213    SCIP_Real             lincoefsmin;        /**< minimal absolute value of coefficients in linear part, only available in solving stage */
214    SCIP_Real             lincoefsmax;        /**< maximal absolute value of coefficients in linear part, only available in solving stage */
215 
216    SCIP_Real*            factorleft;         /**< coefficients of left factor if constraint function is factorable */
217    SCIP_Real*            factorright;        /**< coefficients of right factor if constraint function is factorable */
218 
219    SCIP_Real*            gaugecoefs;         /**< coefficients of the gauge function */
220    SCIP_Real             gaugeconst;         /**< constant of the gauge function */
221    SCIP_Real*            interiorpoint;      /**< interior point of the region defined by the convex function */
222    SCIP_Real             interiorpointval;   /**< function value at interior point */
223 
224    SCIP_Real*            eigenvalues;        /**< eigenvalues of A */
225    SCIP_Real*            eigenvectors;       /**< orthonormal eigenvectors of A; if A = P D P^T, then eigenvectors is P^T */
226    SCIP_Real*            bp;                 /**< stores b * P where b are the linear coefficients of the quadratic vars */
227    SCIP_Real             maxnonconvexity;    /**< nonconvexity measure: estimate on largest absolute value of nonconvex eigenvalues */
228 
229    SCIP_Bool             isdisaggregated;    /**< has the constraint already been disaggregated? if might happen that more disaggreation would be potentially
230                                                   possible, but we reached the maximum number of sparsity components during presolveDisaggregate() */
231 };
232 
233 /** quadratic constraint update method */
234 struct SCIP_QuadConsUpgrade
235 {
236    SCIP_DECL_QUADCONSUPGD((*quadconsupgd));  /**< method to call for upgrading quadratic constraint */
237    int                   priority;           /**< priority of upgrading method */
238    SCIP_Bool             active;             /**< is upgrading enabled */
239 };
240 typedef struct SCIP_QuadConsUpgrade SCIP_QUADCONSUPGRADE; /**< quadratic constraint update method */
241 
242 /** structure to store everything needed for using linear inequalities to improve upon the McCormick relaxation */
243 struct BilinearEstimator
244 {
245    SCIP_VAR*             x;                 /**< first variable */
246    SCIP_VAR*             y;                 /**< second variable */
247    SCIP_Real             inequnderest[6];   /**< at most two inequalities that can be used to underestimate xy; stored as (xcoef,ycoef,constant) with xcoef x <= ycoef y + constant */
248    SCIP_Real             ineqoverest[6];    /**< at most two inequalities that can be used to overestimate xy; stored as (xcoef,ycoef,constant) with xcoef x <= ycoef y + constant */
249    SCIP_Real             maxnonconvexity;   /**< estimate on largest absolute value of nonconvex eigenvalues of all quadratic constraint containing xy */
250    int                   ninequnderest;     /**< total number of inequalities for underestimating xy */
251    int                   nineqoverest;      /**< total number of inequalities for overestimating xy */
252    int                   nunderest;         /**< number of constraints that require to underestimate xy */
253    int                   noverest;          /**< number of constraints that require to overestimate xy */
254 
255    SCIP_Real             lastimprfac;       /**< last achieved improvement factor */
256 };
257 typedef struct BilinearEstimator BILINESTIMATOR;
258 
259 /** constraint handler data */
260 struct SCIP_ConshdlrData
261 {
262    int                   replacebinaryprodlength; /**< length of linear term which when multiplied with a binary variable is replaced by an auxiliary variable and an equivalent linear formulation */
263    int                   empathy4and;        /**< how much empathy we have for using the AND constraint handler: 0 avoid always; 1 use sometimes; 2 use as often as possible */
264    SCIP_Bool             binreforminitial;   /**< whether to make constraints added due to replacing products with binary variables initial */
265    SCIP_Bool             binreformbinaryonly;/**< whether to consider only binary variables when reformulating products with binary variables */
266    SCIP_Real             binreformmaxcoef;   /**< factor on 1/feastol to limit coefficients and coef range in linear constraints created by binary reformulation */
267    SCIP_Real             cutmaxrange;        /**< maximal range (maximal coef / minimal coef) of a cut in order to be added to LP */
268    SCIP_Bool             linearizeheursol;   /**< whether linearizations of convex quadratic constraints should be added to cutpool when some heuristics finds a new solution */
269    SCIP_Bool             checkcurvature;     /**< whether functions should be checked for convexity/concavity */
270    SCIP_Bool             checkfactorable;    /**< whether functions should be checked to be factorable */
271    char                  checkquadvarlocks;  /**< whether quadratic variables contained in a single constraint should be forced to be at their lower or upper bounds ('d'isable, change 't'ype, add 'b'ound disjunction) */
272    SCIP_Bool             linfeasshift;       /**< whether to make solutions in check feasible if possible */
273    int                   maxdisaggrsize;     /**< maximum number of components when disaggregating a quadratic constraint (<= 1: off) */
274    char                  disaggrmergemethod; /**< method on merging blocks in disaggregation */
275    int                   maxproprounds;      /**< limit on number of propagation rounds for a single constraint within one round of SCIP propagation during solve */
276    int                   maxproproundspresolve; /**< limit on number of propagation rounds for a single constraint within one presolving round */
277    SCIP_Real             sepanlpmincont;     /**< minimal required fraction of continuous variables in problem to use solution of NLP relaxation in root for separation */
278    SCIP_Bool             enfocutsremovable;  /**< are cuts added during enforcement removable from the LP in the same node? */
279    SCIP_Bool             gaugecuts;          /**< should convex quadratics generated strong cuts via gauge function? */
280    SCIP_Bool             projectedcuts;      /**< should convex quadratics generated strong cuts via projections? */
281    char                  interiorcomputation;/**< how the interior point should be computed: 'a'ny point per constraint, 'm'ost interior per constraint */
282    char                  branchscoring;      /**< method to use to compute score of branching candidates */
283    int                   enfolplimit;        /**< maximum number of enforcement round before declaring the LP relaxation
284                                               * infeasible (-1: no limit); WARNING: if this parameter is not set to -1,
285                                               * SCIP might declare sub-optimal solutions optimal or feasible instances
286                                               * infeasible; thus, the result returned by SCIP might be incorrect!
287                                               */
288    SCIP_HEUR*            subnlpheur;         /**< a pointer to the subnlp heuristic, if available */
289    SCIP_HEUR*            trysolheur;         /**< a pointer to the trysol heuristic, if available */
290    SCIP_EVENTHDLR*       eventhdlr;          /**< our handler for variable bound change events */
291    int                   newsoleventfilterpos; /**< filter position of new solution event handler, if caught */
292    SCIP_Bool             sepanlp;            /**< where linearization of the NLP relaxation solution added? */
293    SCIP_NODE*            lastenfonode;       /**< the node for which enforcement was called the last time (and some constraint was violated) */
294    int                   nenforounds;        /**< counter on number of enforcement rounds for the current node */
295    SCIP_QUADCONSUPGRADE** quadconsupgrades;  /**< quadratic constraint upgrade methods for specializing quadratic constraints */
296    int                   quadconsupgradessize; /**< size of quadconsupgrade array */
297    int                   nquadconsupgrades;  /**< number of quadratic constraint upgrade methods */
298 
299    BILINESTIMATOR*       bilinestimators;    /**< array containing all required information for using stronger estimators for each bilinear term in all quadratic constraints */
300    int                   nbilinterms;        /**< number of bilinear terms in all quadratic constraints */
301 
302    SCIP_Bool             usebilinineqbranch; /**< should linear inequalities be considered when computing the branching scores for bilinear terms? */
303    SCIP_Bool             storedbilinearterms; /**< did we already try to store all bilinear terms? */
304 
305    SCIP_Real             minscorebilinterms; /**< minimal required score in order to use linear inequalities for tighter bilinear relaxations */
306    SCIP_Real             mincurvcollectbilinterms;/**< minimal curvature of constraints to be considered when returning bilinear terms to other plugins */
307    int                   bilinineqmaxseparounds; /**< maximum number of separation rounds to use linear inequalities for the bilinear term relaxation in a local node */
308 };
309 
310 
311 /*
312  * local methods for managing quadratic constraint update methods
313  */
314 
315 
316 /** checks whether a quadratic constraint upgrade method has already be registered */
317 static
conshdlrdataHasUpgrade(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_DECL_QUADCONSUPGD ((* quadconsupgd)),const char * conshdlrname)318 SCIP_Bool conshdlrdataHasUpgrade(
319    SCIP*                 scip,               /**< SCIP data structure */
320    SCIP_CONSHDLRDATA*    conshdlrdata,       /**< constraint handler data */
321    SCIP_DECL_QUADCONSUPGD((*quadconsupgd)),  /**< method to call for upgrading quadratic constraint */
322    const char*           conshdlrname        /**< name of the constraint handler */
323    )
324 {
325    int i;
326 
327    assert(scip != NULL);
328    assert(conshdlrdata != NULL);
329    assert(quadconsupgd != NULL);
330    assert(conshdlrname != NULL);
331 
332    for( i = conshdlrdata->nquadconsupgrades - 1; i >= 0; --i )
333    {
334       if( conshdlrdata->quadconsupgrades[i]->quadconsupgd == quadconsupgd )
335       {
336          SCIPwarningMessage(scip, "Try to add already known upgrade message for constraint handler <%s>.\n", conshdlrname);
337          return TRUE;
338       }
339    }
340 
341    return FALSE;
342 }
343 
344 /*
345  * Local methods
346  */
347 
348 /** translate from one value of infinity to another
349  *
350  *  if val is >= infty1, then give infty2, else give val
351  */
352 #define infty2infty(infty1, infty2, val) ((val) >= (infty1) ? (infty2) : (val))
353 
354 /** catches variable bound change events on a linear variable in a quadratic constraint */
355 static
catchLinearVarEvents(SCIP * scip,SCIP_EVENTHDLR * eventhdlr,SCIP_CONS * cons,int linvarpos)356 SCIP_RETCODE catchLinearVarEvents(
357    SCIP*                 scip,               /**< SCIP data structure */
358    SCIP_EVENTHDLR*       eventhdlr,          /**< event handler */
359    SCIP_CONS*            cons,               /**< constraint for which to catch bound change events */
360    int                   linvarpos           /**< position of variable in linear variables array */
361    )
362 {
363    SCIP_CONSDATA*  consdata;
364    SCIP_QUADVAREVENTDATA* eventdata;
365    SCIP_EVENTTYPE  eventtype;
366 
367    assert(scip != NULL);
368    assert(eventhdlr != NULL);
369    assert(cons != NULL);
370 
371    consdata = SCIPconsGetData(cons);
372    assert(consdata != NULL);
373 
374    assert(linvarpos >= 0);
375    assert(linvarpos < consdata->nlinvars);
376    assert(consdata->lineventdata != NULL);
377 
378    SCIP_CALL( SCIPallocBlockMemory(scip, &eventdata) );
379 
380    eventdata->cons = cons;
381    eventdata->varidx = linvarpos;
382 
383    eventtype = SCIP_EVENTTYPE_VARFIXED | SCIP_EVENTTYPE_GBDCHANGED;
384    if( !SCIPisInfinity(scip, consdata->rhs) )
385    {
386       /* if right hand side is finite, then a tightening in the lower bound of coef*linvar is of interest
387        * since we also want to keep activities in consdata up-to-date, we also need to know when the corresponding bound is relaxed */
388       if( consdata->lincoefs[linvarpos] > 0.0 )
389          eventtype |= SCIP_EVENTTYPE_LBCHANGED;
390       else
391          eventtype |= SCIP_EVENTTYPE_UBCHANGED;
392    }
393    if( !SCIPisInfinity(scip, -consdata->lhs) )
394    {
395       /* if left hand side is finite, then a tightening in the upper bound of coef*linvar is of interest
396        * since we also want to keep activities in consdata up-to-date, we also need to know when the corresponding bound is relaxed */
397       if( consdata->lincoefs[linvarpos] > 0.0 )
398          eventtype |= SCIP_EVENTTYPE_UBCHANGED;
399       else
400          eventtype |= SCIP_EVENTTYPE_LBCHANGED;
401    }
402 
403    SCIP_CALL( SCIPcatchVarEvent(scip, consdata->linvars[linvarpos], eventtype, eventhdlr, (SCIP_EVENTDATA*)eventdata, &eventdata->filterpos) );
404 
405    consdata->lineventdata[linvarpos] = eventdata;
406 
407    /* invalidate activity information
408     * NOTE: It could happen that a constraint gets temporary deactivated and some variable bounds change. In this case
409     *       we do not recognize those bound changes with the variable events and thus we have to recompute the activities.
410     */
411    consdata->minlinactivity = SCIP_INVALID;
412    consdata->maxlinactivity = SCIP_INVALID;
413    consdata->minlinactivityinf = -1;
414    consdata->maxlinactivityinf = -1;
415 
416    return SCIP_OKAY;
417 }
418 
419 /** drops variable bound change events on a linear variable in a quadratic constraint */
420 static
dropLinearVarEvents(SCIP * scip,SCIP_EVENTHDLR * eventhdlr,SCIP_CONS * cons,int linvarpos)421 SCIP_RETCODE dropLinearVarEvents(
422    SCIP*                 scip,               /**< SCIP data structure */
423    SCIP_EVENTHDLR*       eventhdlr,          /**< event handler */
424    SCIP_CONS*            cons,               /**< constraint for which to catch bound change events */
425    int                   linvarpos           /**< position of variable in linear variables array */
426    )
427 {
428    SCIP_CONSDATA* consdata;
429    SCIP_EVENTTYPE  eventtype;
430 
431    assert(scip != NULL);
432    assert(eventhdlr != NULL);
433    assert(cons != NULL);
434 
435    consdata = SCIPconsGetData(cons);
436    assert(consdata != NULL);
437 
438    assert(linvarpos >= 0);
439    assert(linvarpos < consdata->nlinvars);
440    assert(consdata->lineventdata != NULL);
441    assert(consdata->lineventdata[linvarpos] != NULL);
442    assert(consdata->lineventdata[linvarpos]->cons == cons);
443    assert(consdata->lineventdata[linvarpos]->varidx == linvarpos);
444    assert(consdata->lineventdata[linvarpos]->filterpos >= 0);
445 
446    eventtype = SCIP_EVENTTYPE_VARFIXED | SCIP_EVENTTYPE_GBDCHANGED;
447    if( !SCIPisInfinity(scip, consdata->rhs) )
448    {
449       /* if right hand side is finite, then a tightening in the lower bound of coef*linvar is of interest
450        * since we also want to keep activities in consdata up-to-date, we also need to know when the corresponding bound is relaxed */
451       if( consdata->lincoefs[linvarpos] > 0.0 )
452          eventtype |= SCIP_EVENTTYPE_LBCHANGED;
453       else
454          eventtype |= SCIP_EVENTTYPE_UBCHANGED;
455    }
456    if( !SCIPisInfinity(scip, -consdata->lhs) )
457    {
458       /* if left hand side is finite, then a tightening in the upper bound of coef*linvar is of interest
459        * since we also want to keep activities in consdata up-to-date, we also need to know when the corresponding bound is relaxed */
460       if( consdata->lincoefs[linvarpos] > 0.0 )
461          eventtype |= SCIP_EVENTTYPE_UBCHANGED;
462       else
463          eventtype |= SCIP_EVENTTYPE_LBCHANGED;
464    }
465 
466    SCIP_CALL( SCIPdropVarEvent(scip, consdata->linvars[linvarpos], eventtype, eventhdlr, (SCIP_EVENTDATA*)consdata->lineventdata[linvarpos], consdata->lineventdata[linvarpos]->filterpos) );
467 
468    SCIPfreeBlockMemory(scip, &consdata->lineventdata[linvarpos]);  /*lint !e866 */
469 
470    return SCIP_OKAY;
471 }
472 
473 /** catches variable bound change events on a quadratic variable in a quadratic constraint */
474 static
catchQuadVarEvents(SCIP * scip,SCIP_EVENTHDLR * eventhdlr,SCIP_CONS * cons,int quadvarpos)475 SCIP_RETCODE catchQuadVarEvents(
476    SCIP*                 scip,               /**< SCIP data structure */
477    SCIP_EVENTHDLR*       eventhdlr,          /**< event handler */
478    SCIP_CONS*            cons,               /**< constraint for which to catch bound change events */
479    int                   quadvarpos          /**< position of variable in quadratic variables array */
480    )
481 {
482    SCIP_CONSDATA* consdata;
483    SCIP_QUADVAREVENTDATA* eventdata;
484    SCIP_EVENTTYPE eventtype;
485 
486    assert(scip != NULL);
487    assert(eventhdlr != NULL);
488    assert(cons != NULL);
489 
490    consdata = SCIPconsGetData(cons);
491    assert(consdata != NULL);
492 
493    assert(quadvarpos >= 0);
494    assert(quadvarpos < consdata->nquadvars);
495    assert(consdata->quadvarterms[quadvarpos].eventdata == NULL);
496 
497    SCIP_CALL( SCIPallocBlockMemory(scip, &eventdata) );
498 
499    eventtype = SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED | SCIP_EVENTTYPE_GBDCHANGED;
500 #ifdef CHECKIMPLINBILINEAR
501    eventtype |= SCIP_EVENTTYPE_IMPLADDED;
502 #endif
503    eventdata->cons = cons;
504    eventdata->varidx   = -quadvarpos-1;
505    SCIP_CALL( SCIPcatchVarEvent(scip, consdata->quadvarterms[quadvarpos].var, eventtype, eventhdlr, (SCIP_EVENTDATA*)eventdata, &eventdata->filterpos) );
506 
507    consdata->quadvarterms[quadvarpos].eventdata = eventdata;
508 
509    /* invalidate activity information
510     * NOTE: It could happen that a constraint gets temporary deactivated and some variable bounds change. In this case
511     *       we do not recognize those bound changes with the variable events and thus we have to recompute the activities.
512     */
513    SCIPintervalSetEmpty(&consdata->quadactivitybounds);
514 
515    return SCIP_OKAY;
516 }
517 
518 /** catches variable bound change events on a quadratic variable in a quadratic constraint */
519 static
dropQuadVarEvents(SCIP * scip,SCIP_EVENTHDLR * eventhdlr,SCIP_CONS * cons,int quadvarpos)520 SCIP_RETCODE dropQuadVarEvents(
521    SCIP*                 scip,               /**< SCIP data structure */
522    SCIP_EVENTHDLR*       eventhdlr,          /**< event handler */
523    SCIP_CONS*            cons,               /**< constraint for which to catch bound change events */
524    int                   quadvarpos          /**< position of variable in quadratic variables array */
525    )
526 {
527    SCIP_CONSDATA* consdata;
528    SCIP_EVENTTYPE eventtype;
529 
530    assert(scip != NULL);
531    assert(eventhdlr != NULL);
532    assert(cons != NULL);
533 
534    consdata = SCIPconsGetData(cons);
535    assert(consdata != NULL);
536 
537    assert(quadvarpos >= 0);
538    assert(quadvarpos < consdata->nquadvars);
539    assert(consdata->quadvarterms[quadvarpos].eventdata != NULL);
540    assert(consdata->quadvarterms[quadvarpos].eventdata->cons == cons);
541    assert(consdata->quadvarterms[quadvarpos].eventdata->varidx == -quadvarpos-1);
542    assert(consdata->quadvarterms[quadvarpos].eventdata->filterpos >= 0);
543 
544    eventtype = SCIP_EVENTTYPE_BOUNDCHANGED | SCIP_EVENTTYPE_VARFIXED | SCIP_EVENTTYPE_GBDCHANGED;
545 #ifdef CHECKIMPLINBILINEAR
546    eventtype |= SCIP_EVENTTYPE_IMPLADDED;
547 #endif
548 
549    SCIP_CALL( SCIPdropVarEvent(scip, consdata->quadvarterms[quadvarpos].var, eventtype, eventhdlr, (SCIP_EVENTDATA*)consdata->quadvarterms[quadvarpos].eventdata, consdata->quadvarterms[quadvarpos].eventdata->filterpos) );
550 
551    SCIPfreeBlockMemory(scip, &consdata->quadvarterms[quadvarpos].eventdata);
552 
553    return SCIP_OKAY;
554 }
555 
556 /** catch variable events */
557 static
catchVarEvents(SCIP * scip,SCIP_EVENTHDLR * eventhdlr,SCIP_CONS * cons)558 SCIP_RETCODE catchVarEvents(
559    SCIP*                 scip,               /**< SCIP data structure */
560    SCIP_EVENTHDLR*       eventhdlr,          /**< event handler */
561    SCIP_CONS*            cons                /**< constraint for which to catch bound change events */
562    )
563 {
564    SCIP_CONSDATA* consdata;
565    SCIP_VAR* var;
566    int i;
567 
568    assert(scip != NULL);
569    assert(cons != NULL);
570    assert(eventhdlr != NULL);
571 
572    consdata = SCIPconsGetData(cons);
573    assert(consdata != NULL);
574    assert(consdata->lineventdata == NULL);
575 
576    /* we will update isremovedfixings, so reset it to TRUE first */
577    consdata->isremovedfixings = TRUE;
578 
579    SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->lineventdata, consdata->linvarssize) );
580    for( i = 0; i < consdata->nlinvars; ++i )
581    {
582       SCIP_CALL( catchLinearVarEvents(scip, eventhdlr, cons, i) );
583 
584       var = consdata->linvars[i];
585       consdata->isremovedfixings = consdata->isremovedfixings && SCIPvarIsActive(var)
586          && !SCIPisEQ(scip, SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var));
587    }
588 
589    for( i = 0; i < consdata->nquadvars; ++i )
590    {
591       assert(consdata->quadvarterms[i].eventdata == NULL);
592 
593       SCIP_CALL( catchQuadVarEvents(scip, eventhdlr, cons, i) );
594 
595       var = consdata->quadvarterms[i].var;
596       consdata->isremovedfixings = consdata->isremovedfixings && SCIPvarIsActive(var)
597          && !SCIPisEQ(scip, SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var));
598    }
599 
600    consdata->ispropagated = FALSE;
601 
602    return SCIP_OKAY;
603 }
604 
605 /** drop variable events */
606 static
dropVarEvents(SCIP * scip,SCIP_EVENTHDLR * eventhdlr,SCIP_CONS * cons)607 SCIP_RETCODE dropVarEvents(
608    SCIP*                 scip,               /**< SCIP data structure */
609    SCIP_EVENTHDLR*       eventhdlr,          /**< event handler */
610    SCIP_CONS*            cons                /**< constraint for which to drop bound change events */
611    )
612 {
613    SCIP_CONSDATA* consdata;
614    int i;
615 
616    assert(scip != NULL);
617    assert(eventhdlr != NULL);
618    assert(cons != NULL);
619 
620    consdata = SCIPconsGetData(cons);
621    assert(consdata != NULL);
622 
623    if( consdata->lineventdata != NULL )
624    {
625       for( i = 0; i < consdata->nlinvars; ++i )
626       {
627          if( consdata->lineventdata[i] != NULL )
628          {
629             SCIP_CALL( dropLinearVarEvents(scip, eventhdlr, cons, i) );
630          }
631       }
632       SCIPfreeBlockMemoryArray(scip, &consdata->lineventdata, consdata->linvarssize);
633    }
634 
635    for( i = 0; i < consdata->nquadvars; ++i )
636    {
637       if( consdata->quadvarterms[i].eventdata != NULL )
638       {
639          SCIP_CALL( dropQuadVarEvents(scip, eventhdlr, cons, i) );
640       }
641    }
642 
643    return SCIP_OKAY;
644 }
645 
646 /** locks a linear variable in a constraint */
647 static
lockLinearVariable(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real coef)648 SCIP_RETCODE lockLinearVariable(
649    SCIP*                 scip,               /**< SCIP data structure */
650    SCIP_CONS*            cons,               /**< constraint where to lock a variable */
651    SCIP_VAR*             var,                /**< variable to lock */
652    SCIP_Real             coef                /**< coefficient of variable in constraint */
653    )
654 {
655    SCIP_CONSDATA* consdata;
656 
657    assert(scip != NULL);
658    assert(cons != NULL);
659    assert(var != NULL);
660    assert(coef != 0.0);
661 
662    consdata = SCIPconsGetData(cons);
663    assert(consdata != NULL);
664 
665    if( coef > 0.0 )
666    {
667       SCIP_CALL( SCIPlockVarCons(scip, var, cons, !SCIPisInfinity(scip, -consdata->lhs), !SCIPisInfinity(scip,  consdata->rhs)) );
668    }
669    else
670    {
671       SCIP_CALL( SCIPlockVarCons(scip, var, cons, !SCIPisInfinity(scip,  consdata->rhs), !SCIPisInfinity(scip, -consdata->lhs)) );
672    }
673 
674    return SCIP_OKAY;
675 }
676 
677 /** unlocks a linear variable in a constraint */
678 static
unlockLinearVariable(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real coef)679 SCIP_RETCODE unlockLinearVariable(
680    SCIP*                 scip,               /**< SCIP data structure */
681    SCIP_CONS*            cons,               /**< constraint where to unlock a variable */
682    SCIP_VAR*             var,                /**< variable to unlock */
683    SCIP_Real             coef                /**< coefficient of variable in constraint */
684    )
685 {
686    SCIP_CONSDATA* consdata;
687 
688    assert(scip != NULL);
689    assert(cons != NULL);
690    assert(var != NULL);
691    assert(coef != 0.0);
692 
693    consdata = SCIPconsGetData(cons);
694    assert(consdata != NULL);
695 
696    if( coef > 0.0 )
697    {
698       SCIP_CALL( SCIPunlockVarCons(scip, var, cons, !SCIPisInfinity(scip, -consdata->lhs), !SCIPisInfinity(scip,  consdata->rhs)) );
699    }
700    else
701    {
702       SCIP_CALL( SCIPunlockVarCons(scip, var, cons, !SCIPisInfinity(scip,  consdata->rhs), !SCIPisInfinity(scip, -consdata->lhs)) );
703    }
704 
705    return SCIP_OKAY;
706 }
707 
708 /** locks a quadratic variable in a constraint */
709 static
lockQuadraticVariable(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var)710 SCIP_RETCODE lockQuadraticVariable(
711    SCIP*                 scip,               /**< SCIP data structure */
712    SCIP_CONS*            cons,               /**< constraint where to lock a variable */
713    SCIP_VAR*             var                 /**< variable to lock */
714    )
715 {
716    SCIP_CALL( SCIPlockVarCons(scip, var, cons, TRUE, TRUE) );
717 
718    return SCIP_OKAY;
719 }
720 
721 /** unlocks a quadratic variable in a constraint */
722 static
unlockQuadraticVariable(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var)723 SCIP_RETCODE unlockQuadraticVariable(
724    SCIP*                 scip,               /**< SCIP data structure */
725    SCIP_CONS*            cons,               /**< constraint where to unlock a variable */
726    SCIP_VAR*             var                 /**< variable to unlock */
727    )
728 {
729    SCIP_CALL( SCIPunlockVarCons(scip, var, cons, TRUE, TRUE) );
730 
731    return SCIP_OKAY;
732 }
733 
734 /** computes the minimal and maximal activity for the linear part in a constraint data
735  *
736  *  Only sums up terms that contribute finite values.
737  *  Gives the number of terms that contribute infinite values.
738  *  Only computes those activities where the corresponding side of the constraint is finite.
739  */
740 static
consdataUpdateLinearActivity(SCIP * scip,SCIP_CONSDATA * consdata,SCIP_Real intervalinfty)741 void consdataUpdateLinearActivity(
742    SCIP*                 scip,               /**< SCIP data structure */
743    SCIP_CONSDATA*        consdata,           /**< constraint data */
744    SCIP_Real             intervalinfty       /**< infinity value used in interval operations */
745    )
746 {  /*lint --e{666}*/
747    SCIP_ROUNDMODE prevroundmode;
748    int       i;
749    SCIP_Real bnd;
750 
751    assert(scip != NULL);
752    assert(consdata != NULL);
753 
754    /* if variable bounds are not strictly consistent, then the activity update methods may yield inconsistent activities
755     * in this case, we also recompute the activities
756     */
757    if( consdata->minlinactivity != SCIP_INVALID && consdata->maxlinactivity != SCIP_INVALID &&  /*lint !e777 */
758       (consdata->minlinactivityinf > 0 || consdata->maxlinactivityinf > 0 || consdata->minlinactivity <= consdata->maxlinactivity) )
759    {
760       /* activities should be up-to-date */
761       assert(consdata->minlinactivityinf >= 0);
762       assert(consdata->maxlinactivityinf >= 0);
763       return;
764    }
765 
766    consdata->minlinactivityinf = 0;
767    consdata->maxlinactivityinf = 0;
768 
769    /* if lhs is -infinite, then we do not compute a maximal activity, so we set it to  infinity
770     * if rhs is  infinite, then we do not compute a minimal activity, so we set it to -infinity
771     */
772    consdata->minlinactivity = SCIPisInfinity(scip,  consdata->rhs) ? -intervalinfty : 0.0;
773    consdata->maxlinactivity = SCIPisInfinity(scip, -consdata->lhs) ?  intervalinfty : 0.0;
774 
775    if( consdata->nlinvars == 0 )
776       return;
777 
778    /* if the activities computed here should be still up-to-date after bound changes,
779     * variable events need to be caught */
780    assert(consdata->lineventdata != NULL);
781 
782    prevroundmode = SCIPintervalGetRoundingMode();
783 
784    if( !SCIPisInfinity(scip,  consdata->rhs) )
785    {
786       /* compute minimal activity only if there is a finite right hand side */
787       SCIPintervalSetRoundingModeDownwards();
788 
789       for( i = 0; i < consdata->nlinvars; ++i )
790       {
791          assert(consdata->lineventdata[i] != NULL);
792          if( consdata->lincoefs[i] >= 0.0 )
793          {
794             bnd = MIN(SCIPvarGetLbLocal(consdata->linvars[i]), SCIPvarGetUbLocal(consdata->linvars[i]));
795             if( SCIPisInfinity(scip, -bnd) )
796             {
797                ++consdata->minlinactivityinf;
798                continue;
799             }
800             assert(!SCIPisInfinity(scip, bnd)); /* do not like variables that are fixed at +infinity */
801          }
802          else
803          {
804             bnd = MAX(SCIPvarGetLbLocal(consdata->linvars[i]), SCIPvarGetUbLocal(consdata->linvars[i]));
805             if( SCIPisInfinity(scip,  bnd) )
806             {
807                ++consdata->minlinactivityinf;
808                continue;
809             }
810             assert(!SCIPisInfinity(scip, -bnd)); /* do not like variables that are fixed at -infinity */
811          }
812          consdata->minlinactivity += consdata->lincoefs[i] * bnd;
813       }
814    }
815 
816    if( !SCIPisInfinity(scip, -consdata->lhs) )
817    {
818       /* compute maximal activity only if there is a finite left hand side */
819       SCIPintervalSetRoundingModeUpwards();
820 
821       for( i = 0; i < consdata->nlinvars; ++i )
822       {
823          assert(consdata->lineventdata[i] != NULL);
824          if( consdata->lincoefs[i] >= 0.0 )
825          {
826             bnd = MAX(SCIPvarGetLbLocal(consdata->linvars[i]), SCIPvarGetUbLocal(consdata->linvars[i]));
827             if( SCIPisInfinity(scip,  bnd) )
828             {
829                ++consdata->maxlinactivityinf;
830                continue;
831             }
832             assert(!SCIPisInfinity(scip, -bnd)); /* do not like variables that are fixed at -infinity */
833          }
834          else
835          {
836             bnd = MIN(SCIPvarGetLbLocal(consdata->linvars[i]), SCIPvarGetUbLocal(consdata->linvars[i]));
837             if( SCIPisInfinity(scip, -bnd) )
838             {
839                ++consdata->maxlinactivityinf;
840                continue;
841             }
842             assert(!SCIPisInfinity(scip, bnd)); /* do not like variables that are fixed at +infinity */
843          }
844          consdata->maxlinactivity += consdata->lincoefs[i] * bnd;
845       }
846    }
847 
848    SCIPintervalSetRoundingMode(prevroundmode);
849 
850    assert(consdata->minlinactivityinf > 0 || consdata->maxlinactivityinf > 0 || consdata->minlinactivity <= consdata->maxlinactivity);
851 }
852 
853 /** update the linear activities after a change in the lower bound of a variable */
854 static
consdataUpdateLinearActivityLbChange(SCIP * scip,SCIP_CONSDATA * consdata,SCIP_Real coef,SCIP_Real oldbnd,SCIP_Real newbnd)855 void consdataUpdateLinearActivityLbChange(
856    SCIP*                 scip,               /**< SCIP data structure */
857    SCIP_CONSDATA*        consdata,           /**< constraint data */
858    SCIP_Real             coef,               /**< coefficient of variable in constraint */
859    SCIP_Real             oldbnd,             /**< previous lower bound of variable */
860    SCIP_Real             newbnd              /**< new lower bound of variable */
861    )
862 {
863    SCIP_ROUNDMODE prevroundmode;
864 
865    assert(scip != NULL);
866    assert(consdata != NULL);
867    /* we can't deal with lower bounds at infinity */
868    assert(!SCIPisInfinity(scip, oldbnd));
869    assert(!SCIPisInfinity(scip, newbnd));
870 
871    /* @todo since we check the linear activity for consistency later anyway, we may skip changing the rounding mode here */
872 
873    /* assume lhs <= a*x + y <= rhs, then the following bound changes can be deduced:
874     * a > 0:  y <= rhs - a*lb(x),  y >= lhs - a*ub(x)
875     * a < 0:  y <= rhs - a*ub(x),  y >= lhs - a*lb(x)
876     */
877 
878    if( coef > 0.0 )
879    {
880       /* we should only be called if rhs is finite */
881       assert(!SCIPisInfinity(scip, consdata->rhs));
882 
883       /* we have no min activities computed so far, so cannot update */
884       if( consdata->minlinactivity == SCIP_INVALID )  /*lint !e777 */
885          return;
886 
887       prevroundmode = SCIPintervalGetRoundingMode();
888       SCIPintervalSetRoundingModeDownwards();
889 
890       /* update min activity */
891       if( SCIPisInfinity(scip, -oldbnd) )
892       {
893          --consdata->minlinactivityinf;
894          assert(consdata->minlinactivityinf >= 0);
895       }
896       else
897       {
898          SCIP_Real minuscoef;
899          minuscoef = -coef;
900          consdata->minlinactivity += minuscoef * oldbnd;
901       }
902 
903       if( SCIPisInfinity(scip, -newbnd) )
904       {
905          ++consdata->minlinactivityinf;
906       }
907       else
908       {
909          consdata->minlinactivity += coef * newbnd;
910       }
911 
912       SCIPintervalSetRoundingMode(prevroundmode);
913    }
914    else
915    {
916       /* we should only be called if lhs is finite */
917       assert(!SCIPisInfinity(scip, -consdata->lhs));
918 
919       /* we have no max activities computed so far, so cannot update */
920       if( consdata->maxlinactivity == SCIP_INVALID )  /*lint !e777 */
921          return;
922 
923       prevroundmode = SCIPintervalGetRoundingMode();
924       SCIPintervalSetRoundingModeUpwards();
925 
926       /* update max activity */
927       if( SCIPisInfinity(scip, -oldbnd) )
928       {
929          --consdata->maxlinactivityinf;
930          assert(consdata->maxlinactivityinf >= 0);
931       }
932       else
933       {
934          SCIP_Real minuscoef;
935          minuscoef = -coef;
936          consdata->maxlinactivity += minuscoef * oldbnd;
937       }
938 
939       if( SCIPisInfinity(scip, -newbnd) )
940       {
941          ++consdata->maxlinactivityinf;
942       }
943       else
944       {
945          consdata->maxlinactivity += coef * newbnd;
946       }
947 
948       SCIPintervalSetRoundingMode(prevroundmode);
949    }
950 }
951 
952 /** update the linear activities after a change in the upper bound of a variable */
953 static
consdataUpdateLinearActivityUbChange(SCIP * scip,SCIP_CONSDATA * consdata,SCIP_Real coef,SCIP_Real oldbnd,SCIP_Real newbnd)954 void consdataUpdateLinearActivityUbChange(
955    SCIP*                 scip,               /**< SCIP data structure */
956    SCIP_CONSDATA*        consdata,           /**< constraint data */
957    SCIP_Real             coef,               /**< coefficient of variable in constraint */
958    SCIP_Real             oldbnd,             /**< previous lower bound of variable */
959    SCIP_Real             newbnd              /**< new lower bound of variable */
960    )
961 {
962    SCIP_ROUNDMODE prevroundmode;
963 
964    assert(scip != NULL);
965    assert(consdata != NULL);
966    /* we can't deal with upper bounds at -infinity */
967    assert(!SCIPisInfinity(scip, -oldbnd));
968    assert(!SCIPisInfinity(scip, -newbnd));
969 
970    /* @todo since we check the linear activity for consistency later anyway, we may skip changing the rounding mode here */
971 
972    /* assume lhs <= a*x + y <= rhs, then the following bound changes can be deduced:
973     * a > 0:  y <= rhs - a*lb(x),  y >= lhs - a*ub(x)
974     * a < 0:  y <= rhs - a*ub(x),  y >= lhs - a*lb(x)
975     */
976 
977    if( coef > 0.0 )
978    {
979       /* we should only be called if lhs is finite */
980       assert(!SCIPisInfinity(scip, -consdata->lhs));
981 
982       /* we have no max activities computed so far, so cannot update */
983       if( consdata->maxlinactivity == SCIP_INVALID )  /*lint !e777 */
984          return;
985 
986       prevroundmode = SCIPintervalGetRoundingMode();
987       SCIPintervalSetRoundingModeUpwards();
988 
989       /* update max activity */
990       if( SCIPisInfinity(scip, oldbnd) )
991       {
992          --consdata->maxlinactivityinf;
993          assert(consdata->maxlinactivityinf >= 0);
994       }
995       else
996       {
997          SCIP_Real minuscoef;
998          minuscoef = -coef;
999          consdata->maxlinactivity += minuscoef * oldbnd;
1000       }
1001 
1002       if( SCIPisInfinity(scip, newbnd) )
1003       {
1004          ++consdata->maxlinactivityinf;
1005       }
1006       else
1007       {
1008          consdata->maxlinactivity += coef * newbnd;
1009       }
1010 
1011       SCIPintervalSetRoundingMode(prevroundmode);
1012    }
1013    else
1014    {
1015       /* we should only be called if rhs is finite */
1016       assert(!SCIPisInfinity(scip, consdata->rhs));
1017 
1018       /* we have no min activities computed so far, so cannot update */
1019       if( consdata->minlinactivity == SCIP_INVALID )  /*lint !e777 */
1020          return;
1021 
1022       prevroundmode = SCIPintervalGetRoundingMode();
1023       SCIPintervalSetRoundingModeDownwards();
1024 
1025       /* update min activity */
1026       if( SCIPisInfinity(scip, oldbnd) )
1027       {
1028          --consdata->minlinactivityinf;
1029          assert(consdata->minlinactivityinf >= 0);
1030       }
1031       else
1032       {
1033          SCIP_Real minuscoef;
1034          minuscoef = -coef;
1035          consdata->minlinactivity += minuscoef * oldbnd;
1036       }
1037 
1038       if( SCIPisInfinity(scip, newbnd) )
1039       {
1040          ++consdata->minlinactivityinf;
1041       }
1042       else
1043       {
1044          consdata->minlinactivity += coef * newbnd;
1045       }
1046 
1047       SCIPintervalSetRoundingMode(prevroundmode);
1048    }
1049 }
1050 
1051 /** returns whether a quadratic variable domain can be reduced to its lower or upper bound; this is the case if the
1052  *  quadratic variable is in just one single quadratic constraint and (sqrcoef > 0 and LHS = -infinity), or
1053  *  (sqrcoef < 0 and RHS = +infinity) hold
1054  */
1055 static
hasQuadvarHpProperty(SCIP * scip,SCIP_CONSDATA * consdata,int idx)1056 SCIP_Bool hasQuadvarHpProperty(
1057    SCIP*                 scip,               /**< SCIP data structure */
1058    SCIP_CONSDATA*        consdata,           /**< constraint data */
1059    int                   idx                 /**< index of quadratic variable */
1060    )
1061 {
1062    SCIP_VAR* var;
1063    SCIP_Real quadcoef;
1064    SCIP_Bool haslhs;
1065    SCIP_Bool hasrhs;
1066 
1067    assert(scip != NULL);
1068    assert(consdata != NULL);
1069    assert(idx >= 0 && idx < consdata->nquadvars);
1070 
1071    var = consdata->quadvarterms[idx].var;
1072    assert(var != NULL);
1073 
1074    quadcoef = consdata->quadvarterms[idx].sqrcoef;
1075    haslhs = !SCIPisInfinity(scip, -consdata->lhs);
1076    hasrhs = !SCIPisInfinity(scip, consdata->rhs);
1077 
1078    return SCIPvarGetNLocksDownType(var, SCIP_LOCKTYPE_MODEL) == 1
1079       && SCIPvarGetNLocksUpType(var, SCIP_LOCKTYPE_MODEL) == 1 && SCIPisZero(scip, SCIPvarGetObj(var))
1080       && SCIPvarGetType(var) != SCIP_VARTYPE_BINARY && ((quadcoef < 0.0 && !haslhs) || (quadcoef > 0.0 && !hasrhs));
1081 }
1082 
1083 /** processes variable fixing or bound change event */
1084 static
SCIP_DECL_EVENTEXEC(processVarEvent)1085 SCIP_DECL_EVENTEXEC(processVarEvent)
1086 {
1087    SCIP_CONS* cons;
1088    SCIP_CONSDATA* consdata;
1089    SCIP_EVENTTYPE eventtype;
1090    int varidx;
1091 
1092    assert(scip != NULL);
1093    assert(event != NULL);
1094    assert(eventdata != NULL);
1095    assert(eventhdlr != NULL);
1096 
1097    cons = ((SCIP_QUADVAREVENTDATA*)eventdata)->cons;
1098    assert(cons != NULL);
1099    consdata = SCIPconsGetData(cons);
1100    assert(consdata != NULL);
1101 
1102    varidx = ((SCIP_QUADVAREVENTDATA*)eventdata)->varidx;
1103    assert(varidx <  0 ||  varidx   < consdata->nlinvars);
1104    assert(varidx >= 0 || -varidx-1 < consdata->nquadvars);
1105 
1106    eventtype = SCIPeventGetType(event);
1107 
1108    /* process local bound changes */
1109    if( eventtype & SCIP_EVENTTYPE_BOUNDCHANGED )
1110    {
1111       if( varidx < 0 )
1112       {
1113          /* mark activity bounds for quad term as not up to date anymore */
1114          SCIPintervalSetEmpty(&consdata->quadactivitybounds);
1115       }
1116       else
1117       {
1118          /* update activity bounds for linear terms */
1119          if( eventtype & SCIP_EVENTTYPE_LBCHANGED )
1120             consdataUpdateLinearActivityLbChange(scip, consdata, consdata->lincoefs[varidx], SCIPeventGetOldbound(event), SCIPeventGetNewbound(event));
1121          else
1122             consdataUpdateLinearActivityUbChange(scip, consdata, consdata->lincoefs[varidx], SCIPeventGetOldbound(event), SCIPeventGetNewbound(event));
1123       }
1124 
1125       if( eventtype & SCIP_EVENTTYPE_BOUNDTIGHTENED )
1126       {
1127          SCIP_CALL( SCIPmarkConsPropagate(scip, cons) );
1128          consdata->ispropagated = FALSE;
1129       }
1130    }
1131 
1132    /* process global bound changes */
1133    if( eventtype & SCIP_EVENTTYPE_GBDCHANGED )
1134    {
1135       SCIP_VAR* var;
1136 
1137       var = varidx < 0 ? consdata->quadvarterms[-varidx-1].var : consdata->linvars[varidx];
1138       assert(var != NULL);
1139 
1140       if( varidx < 0 )
1141       {
1142          SCIP_QUADVARTERM* quadvarterm;
1143 
1144          quadvarterm = &consdata->quadvarterms[-varidx-1];
1145 
1146          /* if an integer variable x with a x^2 is tightened to [0,1], then we can replace the x^2 by x, which is done in mergeAndCleanQuadVarTerms()
1147           * we currently do this only if the binary variable does not show up in any bilinear terms
1148           */
1149          if( SCIPgetStage(scip) < SCIP_STAGE_SOLVING && SCIPvarGetType(var) == SCIP_VARTYPE_INTEGER && SCIPvarIsBinary(var) &&
1150             quadvarterm->sqrcoef != 0.0 && quadvarterm->nadjbilin == 0 )
1151          {
1152             consdata->quadvarsmerged = FALSE;
1153             consdata->initialmerge = FALSE;
1154          }
1155       }
1156 
1157       if( SCIPisEQ(scip, SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var)) )
1158          consdata->isremovedfixings = FALSE;
1159    }
1160 
1161    /* process variable fixing event */
1162    if( eventtype & SCIP_EVENTTYPE_VARFIXED )
1163    {
1164       consdata->isremovedfixings = FALSE;
1165    }
1166 
1167 #ifdef CHECKIMPLINBILINEAR
1168    if( eventtype & SCIP_EVENTTYPE_IMPLADDED )
1169    {
1170       assert(varidx < 0); /* we catch impladded events only for quadratic variables */
1171       /* if variable is binary (quite likely if an implication has been added) and occurs in a bilinear term, then mark that we should check implications */
1172       if( SCIPvarIsBinary(SCIPeventGetVar(event)) && consdata->quadvarterms[-varidx-1].nadjbilin > 0 )
1173          consdata->isimpladded = TRUE;
1174    }
1175 #endif
1176 
1177    return SCIP_OKAY;
1178 }
1179 
1180 /** ensures, that linear vars and coefs arrays can store at least num entries */
1181 static
consdataEnsureLinearVarsSize(SCIP * scip,SCIP_CONSDATA * consdata,int num)1182 SCIP_RETCODE consdataEnsureLinearVarsSize(
1183    SCIP*                 scip,               /**< SCIP data structure */
1184    SCIP_CONSDATA*        consdata,           /**< quadratic constraint data */
1185    int                   num                 /**< minimum number of entries to store */
1186    )
1187 {
1188    assert(scip != NULL);
1189    assert(consdata != NULL);
1190    assert(consdata->nlinvars <= consdata->linvarssize);
1191 
1192    if( num > consdata->linvarssize )
1193    {
1194       int newsize;
1195 
1196       newsize = SCIPcalcMemGrowSize(scip, num);
1197       SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->linvars,  consdata->linvarssize, newsize) );
1198       SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->lincoefs, consdata->linvarssize, newsize) );
1199       if( consdata->lineventdata != NULL )
1200       {
1201          SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->lineventdata, consdata->linvarssize, newsize) );
1202       }
1203       consdata->linvarssize = newsize;
1204    }
1205    assert(num <= consdata->linvarssize);
1206 
1207    return SCIP_OKAY;
1208 }
1209 
1210 /** ensures, that quadratic variable terms array can store at least num entries */
1211 static
consdataEnsureQuadVarTermsSize(SCIP * scip,SCIP_CONSDATA * consdata,int num)1212 SCIP_RETCODE consdataEnsureQuadVarTermsSize(
1213    SCIP*                 scip,               /**< SCIP data structure */
1214    SCIP_CONSDATA*        consdata,           /**< quadratic constraint data */
1215    int                   num                 /**< minimum number of entries to store */
1216    )
1217 {
1218    assert(scip != NULL);
1219    assert(consdata != NULL);
1220    assert(consdata->nquadvars <= consdata->quadvarssize);
1221 
1222    if( num > consdata->quadvarssize )
1223    {
1224       int newsize;
1225 
1226       newsize = SCIPcalcMemGrowSize(scip, num);
1227       SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->quadvarterms, consdata->quadvarssize, newsize) );
1228       consdata->quadvarssize = newsize;
1229    }
1230    assert(num <= consdata->quadvarssize);
1231 
1232    return SCIP_OKAY;
1233 }
1234 
1235 /** ensures, that adjacency array can store at least num entries */
1236 static
consdataEnsureAdjBilinSize(SCIP * scip,SCIP_QUADVARTERM * quadvarterm,int num)1237 SCIP_RETCODE consdataEnsureAdjBilinSize(
1238    SCIP*                 scip,               /**< SCIP data structure */
1239    SCIP_QUADVARTERM*     quadvarterm,        /**< quadratic variable term */
1240    int                   num                 /**< minimum number of entries to store */
1241    )
1242 {
1243    assert(scip != NULL);
1244    assert(quadvarterm != NULL);
1245    assert(quadvarterm->nadjbilin <= quadvarterm->adjbilinsize);
1246 
1247    if( num > quadvarterm->adjbilinsize )
1248    {
1249       int newsize;
1250 
1251       newsize = SCIPcalcMemGrowSize(scip, num);
1252       SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &quadvarterm->adjbilin, quadvarterm->adjbilinsize, newsize) );
1253       quadvarterm->adjbilinsize = newsize;
1254    }
1255    assert(num <= quadvarterm->adjbilinsize);
1256 
1257    return SCIP_OKAY;
1258 }
1259 
1260 /** ensures, that bilinear term arrays can store at least num entries */
1261 static
consdataEnsureBilinSize(SCIP * scip,SCIP_CONSDATA * consdata,int num)1262 SCIP_RETCODE consdataEnsureBilinSize(
1263    SCIP*                 scip,               /**< SCIP data structure */
1264    SCIP_CONSDATA*        consdata,           /**< quadratic constraint data */
1265    int                   num                 /**< minimum number of entries to store */
1266    )
1267 {
1268    assert(scip != NULL);
1269    assert(consdata != NULL);
1270    assert(consdata->nbilinterms <= consdata->bilintermssize);
1271 
1272    if( num > consdata->bilintermssize )
1273    {
1274       int newsize;
1275 
1276       newsize = SCIPcalcMemGrowSize(scip, num);
1277       SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &consdata->bilinterms, consdata->bilintermssize, newsize) );
1278       consdata->bilintermssize = newsize;
1279    }
1280    assert(num <= consdata->bilintermssize);
1281 
1282    return SCIP_OKAY;
1283 }
1284 
1285 /** creates empty constraint data structure */
1286 static
consdataCreateEmpty(SCIP * scip,SCIP_CONSDATA ** consdata)1287 SCIP_RETCODE consdataCreateEmpty(
1288    SCIP*                 scip,               /**< SCIP data structure */
1289    SCIP_CONSDATA**       consdata            /**< a buffer to store pointer to new constraint data */
1290    )
1291 {
1292    assert(scip != NULL);
1293    assert(consdata != NULL);
1294 
1295    SCIP_CALL( SCIPallocBlockMemory(scip, consdata) );
1296    BMSclearMemory(*consdata);
1297 
1298    (*consdata)->lhs = -SCIPinfinity(scip);
1299    (*consdata)->rhs =  SCIPinfinity(scip);
1300 
1301    (*consdata)->linvarssorted  = TRUE;
1302    (*consdata)->linvarsmerged  = TRUE;
1303    (*consdata)->quadvarssorted = TRUE;
1304    (*consdata)->quadvarsmerged = TRUE;
1305    (*consdata)->bilinsorted    = TRUE;
1306    (*consdata)->bilinmerged    = TRUE;
1307 
1308    (*consdata)->isremovedfixings = TRUE;
1309    (*consdata)->ispropagated     = TRUE;
1310    (*consdata)->initialmerge     = FALSE;
1311 
1312    (*consdata)->linvar_maydecrease = -1;
1313    (*consdata)->linvar_mayincrease = -1;
1314 
1315    (*consdata)->minlinactivity = SCIP_INVALID;
1316    (*consdata)->maxlinactivity = SCIP_INVALID;
1317    (*consdata)->minlinactivityinf = -1;
1318    (*consdata)->maxlinactivityinf = -1;
1319 
1320    (*consdata)->isgaugeavailable = FALSE;
1321    (*consdata)->isedavailable = FALSE;
1322 
1323    return SCIP_OKAY;
1324 }
1325 
1326 /** creates constraint data structure */
1327 static
consdataCreate(SCIP * scip,SCIP_CONSDATA ** consdata,SCIP_Real lhs,SCIP_Real rhs,int nlinvars,SCIP_VAR ** linvars,SCIP_Real * lincoefs,int nquadvars,SCIP_QUADVARTERM * quadvarterms,int nbilinterms,SCIP_BILINTERM * bilinterms,SCIP_Bool capturevars)1328 SCIP_RETCODE consdataCreate(
1329    SCIP*                 scip,               /**< SCIP data structure */
1330    SCIP_CONSDATA**       consdata,           /**< a buffer to store pointer to new constraint data */
1331    SCIP_Real             lhs,                /**< left hand side of constraint */
1332    SCIP_Real             rhs,                /**< right hand side of constraint */
1333    int                   nlinvars,           /**< number of linear variables */
1334    SCIP_VAR**            linvars,            /**< array of linear variables */
1335    SCIP_Real*            lincoefs,           /**< array of coefficients of linear variables */
1336    int                   nquadvars,          /**< number of quadratic variables */
1337    SCIP_QUADVARTERM*     quadvarterms,       /**< array of quadratic variable terms */
1338    int                   nbilinterms,        /**< number of bilinear terms */
1339    SCIP_BILINTERM*       bilinterms,         /**< array of bilinear terms */
1340    SCIP_Bool             capturevars         /**< whether we should capture variables */
1341    )
1342 {
1343    int i;
1344 
1345    assert(scip != NULL);
1346    assert(consdata != NULL);
1347 
1348    assert(nlinvars == 0 || linvars  != NULL);
1349    assert(nlinvars == 0 || lincoefs != NULL);
1350    assert(nquadvars == 0 || quadvarterms != NULL);
1351    assert(nbilinterms == 0 || bilinterms != NULL);
1352 
1353    SCIP_CALL( SCIPallocBlockMemory(scip, consdata) );
1354    BMSclearMemory(*consdata);
1355 
1356    (*consdata)->minlinactivity = SCIP_INVALID;
1357    (*consdata)->maxlinactivity = SCIP_INVALID;
1358    (*consdata)->minlinactivityinf = -1;
1359    (*consdata)->maxlinactivityinf = -1;
1360 
1361    (*consdata)->lhs = lhs;
1362    (*consdata)->rhs = rhs;
1363 
1364    if( nlinvars > 0 )
1365    {
1366       SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->linvars,  linvars,  nlinvars) );
1367       SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->lincoefs, lincoefs, nlinvars) );
1368       (*consdata)->nlinvars = nlinvars;
1369       (*consdata)->linvarssize = nlinvars;
1370 
1371       if( capturevars )
1372          for( i = 0; i < nlinvars; ++i )
1373          {
1374             SCIP_CALL( SCIPcaptureVar(scip, linvars[i]) );
1375          }
1376    }
1377    else
1378    {
1379       (*consdata)->linvarssorted = TRUE;
1380       (*consdata)->linvarsmerged = TRUE;
1381       (*consdata)->minlinactivity = 0.0;
1382       (*consdata)->maxlinactivity = 0.0;
1383       (*consdata)->minlinactivityinf = 0;
1384       (*consdata)->maxlinactivityinf = 0;
1385    }
1386 
1387    if( nquadvars > 0 )
1388    {
1389       SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->quadvarterms, quadvarterms, nquadvars) );
1390 
1391       for( i = 0; i < nquadvars; ++i )
1392       {
1393          (*consdata)->quadvarterms[i].eventdata = NULL;
1394          if( quadvarterms[i].nadjbilin )
1395          {
1396             SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->quadvarterms[i].adjbilin, quadvarterms[i].adjbilin, quadvarterms[i].nadjbilin) );
1397             (*consdata)->quadvarterms[i].adjbilinsize = quadvarterms[i].nadjbilin;
1398          }
1399          else
1400          {
1401             assert((*consdata)->quadvarterms[i].nadjbilin == 0);
1402             (*consdata)->quadvarterms[i].adjbilin = NULL;
1403             (*consdata)->quadvarterms[i].adjbilinsize = 0;
1404          }
1405          if( capturevars )
1406          {
1407             SCIP_CALL( SCIPcaptureVar(scip, quadvarterms[i].var) );
1408          }
1409       }
1410 
1411       (*consdata)->nquadvars = nquadvars;
1412       (*consdata)->quadvarssize = nquadvars;
1413       SCIPintervalSetEmpty(&(*consdata)->quadactivitybounds);
1414    }
1415    else
1416    {
1417       (*consdata)->quadvarssorted = TRUE;
1418       (*consdata)->quadvarsmerged = TRUE;
1419       SCIPintervalSet(&(*consdata)->quadactivitybounds, 0.0);
1420    }
1421 
1422    if( nbilinterms > 0 )
1423    {
1424       SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*consdata)->bilinterms, bilinterms, nbilinterms) );
1425       (*consdata)->nbilinterms = nbilinterms;
1426       (*consdata)->bilintermssize = nbilinterms;
1427    }
1428    else
1429    {
1430       (*consdata)->bilinsorted = TRUE;
1431       (*consdata)->bilinmerged = TRUE;
1432    }
1433 
1434    (*consdata)->linvar_maydecrease = -1;
1435    (*consdata)->linvar_mayincrease = -1;
1436 
1437    (*consdata)->activity = SCIP_INVALID;
1438    (*consdata)->lhsviol  = SCIPisInfinity(scip, -lhs) ? 0.0 : SCIP_INVALID;
1439    (*consdata)->rhsviol  = SCIPisInfinity(scip,  rhs) ? 0.0 : SCIP_INVALID;
1440 
1441    (*consdata)->isgaugeavailable = FALSE;
1442 
1443    return SCIP_OKAY;
1444 }
1445 
1446 /** frees constraint data structure */
1447 static
consdataFree(SCIP * scip,SCIP_CONSDATA ** consdata)1448 SCIP_RETCODE consdataFree(
1449    SCIP*                 scip,               /**< SCIP data structure */
1450    SCIP_CONSDATA**       consdata            /**< pointer to constraint data to free */
1451    )
1452 {
1453    int i;
1454 
1455    assert(scip != NULL);
1456    assert(consdata != NULL);
1457    assert(*consdata != NULL);
1458 
1459    /* free sepa arrays, may exists if constraint is deleted in solving stage */
1460    SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->sepaquadvars,     (*consdata)->nquadvars);
1461    SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->sepabilinvar2pos, (*consdata)->nbilinterms);
1462 
1463    /* release linear variables and free linear part */
1464    if( (*consdata)->linvarssize > 0 )
1465    {
1466       for( i = 0; i < (*consdata)->nlinvars; ++i )
1467       {
1468          assert((*consdata)->lineventdata == NULL || (*consdata)->lineventdata[i] == NULL); /* variable events should have been dropped earlier */
1469          SCIP_CALL( SCIPreleaseVar(scip, &(*consdata)->linvars[i]) );
1470       }
1471       SCIPfreeBlockMemoryArray(scip, &(*consdata)->linvars,  (*consdata)->linvarssize);
1472       SCIPfreeBlockMemoryArray(scip, &(*consdata)->lincoefs, (*consdata)->linvarssize);
1473       SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->lineventdata, (*consdata)->linvarssize);
1474    }
1475    assert((*consdata)->linvars == NULL);
1476    assert((*consdata)->lincoefs == NULL);
1477    assert((*consdata)->lineventdata == NULL);
1478 
1479    /* release quadratic variables and free quadratic variable term part */
1480    for( i = 0; i < (*consdata)->nquadvars; ++i )
1481    {
1482       assert((*consdata)->quadvarterms[i].eventdata == NULL); /* variable events should have been dropped earlier */
1483       SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->quadvarterms[i].adjbilin, (*consdata)->quadvarterms[i].adjbilinsize);
1484       SCIP_CALL( SCIPreleaseVar(scip, &(*consdata)->quadvarterms[i].var) );
1485    }
1486    SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->quadvarterms, (*consdata)->quadvarssize);
1487 
1488    /* free bilinear terms */
1489    SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->bilinterms, (*consdata)->bilintermssize);
1490 
1491    /* free nonlinear row representation */
1492    if( (*consdata)->nlrow != NULL )
1493    {
1494       SCIP_CALL( SCIPreleaseNlRow(scip, &(*consdata)->nlrow) );
1495    }
1496 
1497    /* free interior point information, may exists if constraint is deleted in solving stage */
1498    SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->interiorpoint, (*consdata)->nquadvars);
1499    SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->gaugecoefs, (*consdata)->nquadvars);
1500 
1501    /* free eigen decomposition information */
1502    SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->eigenvalues, (*consdata)->nquadvars);
1503    if( (*consdata)->eigenvectors != NULL )  /* explicit check on NULL to avoid runtime warning if nquadvars^2 > int_max */
1504       SCIPfreeBlockMemoryArray(scip, &(*consdata)->eigenvectors, (int)((*consdata)->nquadvars*(*consdata)->nquadvars));
1505    SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->bp, (*consdata)->nquadvars);
1506 
1507    /* free unique indices of bilinear terms array */
1508    SCIPfreeBlockMemoryArrayNull(scip, &(*consdata)->bilintermsidx, (*consdata)->nbilinterms);
1509 
1510    SCIPfreeBlockMemory(scip, consdata);
1511    *consdata = NULL;
1512 
1513    return SCIP_OKAY;
1514 }
1515 
1516 /** sorts linear part of constraint data */
1517 static
consdataSortLinearVars(SCIP_CONSDATA * consdata)1518 void consdataSortLinearVars(
1519    SCIP_CONSDATA*        consdata            /**< quadratic constraint data */
1520    )
1521 {
1522    assert(consdata != NULL);
1523 
1524    if( consdata->linvarssorted )
1525       return;
1526 
1527    if( consdata->nlinvars <= 1 )
1528    {
1529       consdata->linvarssorted = TRUE;
1530       return;
1531    }
1532 
1533    if( consdata->lineventdata == NULL )
1534    {
1535       SCIPsortPtrReal((void**)consdata->linvars, consdata->lincoefs, SCIPvarComp, consdata->nlinvars);
1536    }
1537    else
1538    {
1539       int i;
1540 
1541       SCIPsortPtrPtrReal((void**)consdata->linvars, (void**)consdata->lineventdata, consdata->lincoefs, SCIPvarComp, consdata->nlinvars);
1542 
1543       /* update variable indices in event data */
1544       for( i = 0; i < consdata->nlinvars; ++i )
1545          if( consdata->lineventdata[i] != NULL )
1546             consdata->lineventdata[i]->varidx = i;
1547    }
1548 
1549    consdata->linvarssorted = TRUE;
1550 }
1551 
1552 #ifdef SCIP_DISABLED_CODE /* no-one needs this routine currently */
1553 /** returns the position of variable in the linear coefficients array of a constraint, or -1 if not found */
1554 static
consdataFindLinearVar(SCIP_CONSDATA * consdata,SCIP_VAR * var)1555 int consdataFindLinearVar(
1556    SCIP_CONSDATA*        consdata,           /**< quadratic constraint data */
1557    SCIP_VAR*             var                 /**< variable to search for */
1558    )
1559 {
1560    int pos;
1561 
1562    assert(consdata != NULL);
1563    assert(var != NULL);
1564 
1565    if( consdata->nlinvars == 0 )
1566       return -1;
1567 
1568    consdataSortLinearVars(consdata);
1569 
1570    if( !SCIPsortedvecFindPtr((void**)consdata->linvars, SCIPvarComp, (void*)var, consdata->nlinvars, &pos) )
1571       pos = -1;
1572 
1573    return pos;
1574 }
1575 #endif
1576 
1577 /** index comparison method for quadratic variable terms: compares two indices of the quadratic variable set in the quadratic constraint */
1578 static
SCIP_DECL_SORTINDCOMP(quadVarTermComp)1579 SCIP_DECL_SORTINDCOMP(quadVarTermComp)
1580 {  /*lint --e{715}*/
1581    SCIP_CONSDATA* consdata = (SCIP_CONSDATA*)dataptr;
1582 
1583    assert(consdata != NULL);
1584    assert(0 <= ind1 && ind1 < consdata->nquadvars);
1585    assert(0 <= ind2 && ind2 < consdata->nquadvars);
1586 
1587    return SCIPvarCompare(consdata->quadvarterms[ind1].var, consdata->quadvarterms[ind2].var);
1588 }
1589 
1590 /** sorting of quadratic variable terms */
1591 static
consdataSortQuadVarTerms(SCIP * scip,SCIP_CONSDATA * consdata)1592 SCIP_RETCODE consdataSortQuadVarTerms(
1593    SCIP*                 scip,               /**< SCIP data structure */
1594    SCIP_CONSDATA*        consdata            /**< quadratic constraint data */
1595    )
1596 {
1597    int* perm;
1598    int  i;
1599    int  nexti;
1600    int  v;
1601    SCIP_QUADVARTERM quadterm;
1602 
1603    assert(scip != NULL);
1604    assert(consdata != NULL);
1605 
1606    if( consdata->quadvarssorted )
1607       return SCIP_OKAY;
1608 
1609    if( consdata->nquadvars == 0 )
1610    {
1611       consdata->quadvarssorted = TRUE;
1612       return SCIP_OKAY;
1613    }
1614 
1615    /* get temporary memory to store the sorted permutation */
1616    SCIP_CALL( SCIPallocBufferArray(scip, &perm, consdata->nquadvars) );
1617 
1618    /* call bubble sort */
1619    SCIPsort(perm, quadVarTermComp, (void*)consdata, consdata->nquadvars);
1620 
1621    /* permute the quadratic variable terms according to the resulting permutation */
1622    for( v = 0; v < consdata->nquadvars; ++v )
1623    {
1624       if( perm[v] != v )
1625       {
1626          quadterm = consdata->quadvarterms[v];
1627 
1628          i = v;
1629          do
1630          {
1631             assert(0 <= perm[i] && perm[i] < consdata->nquadvars);
1632             assert(perm[i] != i);
1633             consdata->quadvarterms[i] = consdata->quadvarterms[perm[i]];
1634             if( consdata->quadvarterms[i].eventdata != NULL )
1635             {
1636                consdata->quadvarterms[i].eventdata->varidx = -i-1;
1637             }
1638             nexti = perm[i];
1639             perm[i] = i;
1640             i = nexti;
1641          }
1642          while( perm[i] != v );
1643          consdata->quadvarterms[i] = quadterm;
1644          if( consdata->quadvarterms[i].eventdata != NULL )
1645          {
1646             consdata->quadvarterms[i].eventdata->varidx = -i-1;
1647          }
1648          perm[i] = i;
1649       }
1650    }
1651    consdata->quadvarssorted = TRUE;
1652 
1653    /* free temporary memory */
1654    SCIPfreeBufferArray(scip, &perm);
1655 
1656    return SCIP_OKAY;
1657 }
1658 
1659 /** returns the position of variable in the quadratic variable terms array of a constraint, or -1 if not found */
1660 static
consdataFindQuadVarTerm(SCIP * scip,SCIP_CONSDATA * consdata,SCIP_VAR * var,int * pos)1661 SCIP_RETCODE consdataFindQuadVarTerm(
1662    SCIP*                 scip,               /**< SCIP data structure */
1663    SCIP_CONSDATA*        consdata,           /**< quadratic constraint data */
1664    SCIP_VAR*             var,                /**< variable to search for */
1665    int*                  pos                 /**< buffer where to store position of var in quadvarterms array, or -1 if not found */
1666    )
1667 {
1668    int left;
1669    int right;
1670    int cmpres;
1671 
1672    assert(consdata != NULL);
1673    assert(var != NULL);
1674    assert(pos != NULL);
1675 
1676    if( consdata->nquadvars == 0 )
1677    {
1678       *pos = -1;
1679       return SCIP_OKAY;
1680    }
1681 
1682    SCIP_CALL( consdataSortQuadVarTerms(scip, consdata) );
1683 
1684    left = 0;
1685    right = consdata->nquadvars - 1;
1686    while( left <= right )
1687    {
1688       int middle;
1689 
1690       middle = (left+right)/2;
1691       assert(0 <= middle && middle < consdata->nquadvars);
1692 
1693       cmpres = SCIPvarCompare(var, consdata->quadvarterms[middle].var);
1694 
1695       if( cmpres < 0 )
1696          right = middle - 1;
1697       else if( cmpres > 0 )
1698          left  = middle + 1;
1699       else
1700       {
1701          *pos = middle;
1702          return SCIP_OKAY;
1703       }
1704    }
1705    assert(left == right+1);
1706 
1707    *pos = -1;
1708 
1709    return SCIP_OKAY;
1710 }
1711 
1712 /** index comparison method for bilinear terms: compares two index pairs of the bilinear term set in the quadratic constraint */
1713 static
SCIP_DECL_SORTINDCOMP(bilinTermComp)1714 SCIP_DECL_SORTINDCOMP(bilinTermComp)
1715 {  /*lint --e{715}*/
1716    SCIP_CONSDATA* consdata = (SCIP_CONSDATA*)dataptr;
1717    int var1cmp;
1718 
1719    assert(consdata != NULL);
1720    assert(0 <= ind1 && ind1 < consdata->nbilinterms);
1721    assert(0 <= ind2 && ind2 < consdata->nbilinterms);
1722 
1723    var1cmp = SCIPvarCompare(consdata->bilinterms[ind1].var1, consdata->bilinterms[ind2].var1);
1724    if( var1cmp != 0 )
1725       return var1cmp;
1726 
1727    return SCIPvarCompare(consdata->bilinterms[ind1].var2, consdata->bilinterms[ind2].var2);
1728 }
1729 
1730 #ifndef NDEBUG
1731 /** checks if all bilinear terms are sorted correctly */
1732 static
consdataCheckBilinTermsSort(SCIP_CONSDATA * consdata)1733 SCIP_Bool consdataCheckBilinTermsSort(
1734    SCIP_CONSDATA* consdata
1735    )
1736 {
1737    int i;
1738 
1739    assert(consdata != NULL);
1740 
1741    /* nothing to check if the bilinear terms have not been sorted yet */
1742    if( !consdata->bilinsorted )
1743       return TRUE;
1744 
1745    for( i = 0; i < consdata->nbilinterms - 1; ++i )
1746    {
1747       if( bilinTermComp(consdata, i, i+1) > 0 )
1748          return FALSE;
1749    }
1750    return TRUE;
1751 }
1752 #endif
1753 
1754 /** sorting of bilinear terms */
1755 static
consdataSortBilinTerms(SCIP * scip,SCIP_CONSDATA * consdata)1756 SCIP_RETCODE consdataSortBilinTerms(
1757    SCIP*                 scip,               /**< SCIP data structure */
1758    SCIP_CONSDATA*        consdata            /**< quadratic constraint data */
1759    )
1760 {
1761    int* perm;
1762    int* invperm;
1763    int  i;
1764    int  nexti;
1765    int  v;
1766    SCIP_BILINTERM bilinterm;
1767 
1768    assert(scip != NULL);
1769    assert(consdata != NULL);
1770 
1771    if( consdata->bilinsorted )
1772       return SCIP_OKAY;
1773 
1774    if( consdata->nbilinterms == 0 )
1775    {
1776       consdata->bilinsorted = TRUE;
1777       return SCIP_OKAY;
1778    }
1779 
1780    /* get temporary memory to store the sorted permutation and the inverse permutation */
1781    SCIP_CALL( SCIPallocBufferArray(scip, &perm,    consdata->nbilinterms) );
1782    SCIP_CALL( SCIPallocBufferArray(scip, &invperm, consdata->nbilinterms) );
1783 
1784    /* call bubble sort */
1785    SCIPsort(perm, bilinTermComp, (void*)consdata, consdata->nbilinterms);
1786 
1787    /* compute inverted permutation */
1788    for( v = 0; v < consdata->nbilinterms; ++v )
1789    {
1790       assert(0 <= perm[v] && perm[v] < consdata->nbilinterms);
1791       invperm[perm[v]] = v;
1792    }
1793 
1794    /* permute the bilinear terms according to the resulting permutation */
1795    for( v = 0; v < consdata->nbilinterms; ++v )
1796    {
1797       if( perm[v] != v )
1798       {
1799          bilinterm = consdata->bilinterms[v];
1800 
1801          i = v;
1802          do
1803          {
1804             assert(0 <= perm[i] && perm[i] < consdata->nbilinterms);
1805             assert(perm[i] != i);
1806             consdata->bilinterms[i] = consdata->bilinterms[perm[i]];
1807             nexti = perm[i];
1808             perm[i] = i;
1809             i = nexti;
1810          }
1811          while( perm[i] != v );
1812          consdata->bilinterms[i] = bilinterm;
1813          perm[i] = i;
1814       }
1815    }
1816 
1817    /* update the adjacency information in the quadratic variable terms */
1818    for( v = 0; v < consdata->nquadvars; ++v )
1819       for( i = 0; i < consdata->quadvarterms[v].nadjbilin; ++i )
1820          consdata->quadvarterms[v].adjbilin[i] = invperm[consdata->quadvarterms[v].adjbilin[i]];
1821 
1822    consdata->bilinsorted = TRUE;
1823    assert(consdataCheckBilinTermsSort(consdata));
1824 
1825    /* free temporary memory */
1826    SCIPfreeBufferArray(scip, &invperm);
1827    SCIPfreeBufferArray(scip, &perm);
1828 
1829    return SCIP_OKAY;
1830 }
1831 
1832 /** moves a linear variable from one position to another */
1833 static
consdataMoveLinearVar(SCIP_CONSDATA * consdata,int oldpos,int newpos)1834 void consdataMoveLinearVar(
1835    SCIP_CONSDATA*        consdata,           /**< constraint data */
1836    int                   oldpos,             /**< position of variable that shall be moved */
1837    int                   newpos              /**< new position of variable */
1838    )
1839 {
1840    assert(consdata != NULL);
1841    assert(oldpos >= 0);
1842    assert(oldpos < consdata->nlinvars);
1843    assert(newpos >= 0);
1844    assert(newpos < consdata->linvarssize);
1845 
1846    if( newpos == oldpos )
1847       return;
1848 
1849    consdata->linvars [newpos] = consdata->linvars [oldpos];
1850    consdata->lincoefs[newpos] = consdata->lincoefs[oldpos];
1851 
1852    if( consdata->lineventdata != NULL )
1853    {
1854       assert(newpos >= consdata->nlinvars || consdata->lineventdata[newpos] == NULL);
1855 
1856       consdata->lineventdata[newpos] = consdata->lineventdata[oldpos];
1857       consdata->lineventdata[newpos]->varidx = newpos;
1858 
1859       consdata->lineventdata[oldpos] = NULL;
1860    }
1861 
1862    consdata->linvarssorted = FALSE;
1863 }
1864 
1865 /** moves a quadratic variable from one position to another */
1866 static
consdataMoveQuadVarTerm(SCIP_CONSDATA * consdata,int oldpos,int newpos)1867 void consdataMoveQuadVarTerm(
1868    SCIP_CONSDATA*        consdata,           /**< constraint data */
1869    int                   oldpos,             /**< position of variable that shall be moved */
1870    int                   newpos              /**< new position of variable */
1871    )
1872 {
1873    assert(consdata != NULL);
1874    assert(oldpos >= 0);
1875    assert(oldpos < consdata->nquadvars);
1876    assert(newpos >= 0);
1877    assert(newpos < consdata->quadvarssize);
1878 
1879    if( newpos == oldpos )
1880       return;
1881 
1882    assert(newpos >= consdata->nquadvars || consdata->quadvarterms[newpos].eventdata == NULL);
1883 
1884    consdata->quadvarterms[newpos] = consdata->quadvarterms[oldpos];
1885 
1886    if( consdata->quadvarterms[newpos].eventdata != NULL )
1887    {
1888       consdata->quadvarterms[newpos].eventdata->varidx = -newpos-1;
1889       consdata->quadvarterms[oldpos].eventdata = NULL;
1890    }
1891 
1892    consdata->quadvarssorted = FALSE;
1893 }
1894 
1895 /** adds linear coefficient in quadratic constraint */
1896 static
addLinearCoef(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real coef)1897 SCIP_RETCODE addLinearCoef(
1898    SCIP*                 scip,               /**< SCIP data structure */
1899    SCIP_CONS*            cons,               /**< quadratic constraint */
1900    SCIP_VAR*             var,                /**< variable of constraint entry */
1901    SCIP_Real             coef                /**< coefficient of constraint entry */
1902    )
1903 {
1904    SCIP_CONSDATA* consdata;
1905    SCIP_Bool transformed;
1906 
1907    assert(scip != NULL);
1908    assert(cons != NULL);
1909    assert(var  != NULL);
1910 
1911    /* ignore coefficient if it is nearly zero */
1912    if( SCIPisZero(scip, coef) )
1913       return SCIP_OKAY;
1914 
1915    consdata = SCIPconsGetData(cons);
1916    assert(consdata != NULL);
1917 
1918    /* are we in the transformed problem? */
1919    transformed = SCIPconsIsTransformed(cons);
1920 
1921    /* always use transformed variables in transformed constraints */
1922    if( transformed )
1923    {
1924       SCIP_CALL( SCIPgetTransformedVar(scip, var, &var) );
1925    }
1926    assert(var != NULL);
1927    assert(transformed == SCIPvarIsTransformed(var));
1928 
1929    SCIP_CALL( consdataEnsureLinearVarsSize(scip, consdata, consdata->nlinvars+1) );
1930    consdata->linvars [consdata->nlinvars] = var;
1931    consdata->lincoefs[consdata->nlinvars] = coef;
1932 
1933    ++consdata->nlinvars;
1934 
1935    /* catch variable events */
1936    if( SCIPconsIsEnabled(cons) )
1937    {
1938       SCIP_CONSHDLR* conshdlr;
1939       SCIP_CONSHDLRDATA* conshdlrdata;
1940 
1941       /* get event handler */
1942       conshdlr = SCIPconsGetHdlr(cons);
1943       conshdlrdata = SCIPconshdlrGetData(conshdlr);
1944       assert(conshdlrdata != NULL);
1945       assert(conshdlrdata->eventhdlr != NULL);
1946 
1947       assert(consdata->lineventdata != NULL);
1948       consdata->lineventdata[consdata->nlinvars-1] = NULL;
1949 
1950       /* catch bound change events of variable */
1951       SCIP_CALL( catchLinearVarEvents(scip, conshdlrdata->eventhdlr, cons, consdata->nlinvars-1) );
1952    }
1953 
1954    /* invalidate activity information */
1955    consdata->activity = SCIP_INVALID;
1956    consdata->minlinactivity = SCIP_INVALID;
1957    consdata->maxlinactivity = SCIP_INVALID;
1958    consdata->minlinactivityinf = -1;
1959    consdata->maxlinactivityinf = -1;
1960 
1961    /* invalidate nonlinear row */
1962    if( consdata->nlrow != NULL )
1963    {
1964       SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
1965    }
1966 
1967    /* install rounding locks for new variable */
1968    SCIP_CALL( lockLinearVariable(scip, cons, var, coef) );
1969 
1970    /* capture new variable */
1971    SCIP_CALL( SCIPcaptureVar(scip, var) );
1972 
1973    consdata->ispropagated = FALSE;
1974    consdata->ispresolved = FALSE;
1975    consdata->isremovedfixings = consdata->isremovedfixings && SCIPvarIsActive(var)
1976       && !SCIPisEQ(scip, SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var));
1977    if( consdata->nlinvars == 1 )
1978       consdata->linvarssorted = TRUE;
1979    else
1980       consdata->linvarssorted = consdata->linvarssorted && (SCIPvarCompare(consdata->linvars[consdata->nlinvars-2], consdata->linvars[consdata->nlinvars-1]) == -1);
1981    /* always set too FALSE since the new linear variable should be checked if already existing as quad var term */
1982    consdata->linvarsmerged = FALSE;
1983 
1984    return SCIP_OKAY;
1985 }
1986 
1987 /** deletes linear coefficient at given position from quadratic constraint data */
1988 static
delLinearCoefPos(SCIP * scip,SCIP_CONS * cons,int pos)1989 SCIP_RETCODE delLinearCoefPos(
1990    SCIP*                 scip,               /**< SCIP data structure */
1991    SCIP_CONS*            cons,               /**< quadratic constraint */
1992    int                   pos                 /**< position of coefficient to delete */
1993    )
1994 {
1995    SCIP_CONSDATA* consdata;
1996    SCIP_VAR* var;
1997    SCIP_Real coef;
1998 
1999    assert(scip != NULL);
2000    assert(cons != NULL);
2001 
2002    consdata = SCIPconsGetData(cons);
2003    assert(consdata != NULL);
2004    assert(0 <= pos && pos < consdata->nlinvars);
2005 
2006    var  = consdata->linvars[pos];
2007    coef = consdata->lincoefs[pos];
2008    assert(var != NULL);
2009 
2010    /* remove rounding locks for deleted variable */
2011    SCIP_CALL( unlockLinearVariable(scip, cons, var, coef) );
2012 
2013    /* if we catch variable events, drop the events on the variable */
2014    if( consdata->lineventdata != NULL )
2015    {
2016       SCIP_CONSHDLR* conshdlr;
2017       SCIP_CONSHDLRDATA* conshdlrdata;
2018 
2019       /* get event handler */
2020       conshdlr = SCIPconsGetHdlr(cons);
2021       conshdlrdata = SCIPconshdlrGetData(conshdlr);
2022       assert(conshdlrdata != NULL);
2023       assert(conshdlrdata->eventhdlr != NULL);
2024 
2025       /* drop bound change events of variable */
2026       SCIP_CALL( dropLinearVarEvents(scip, conshdlrdata->eventhdlr, cons, pos) );
2027    }
2028 
2029    /* release variable */
2030    SCIP_CALL( SCIPreleaseVar(scip, &consdata->linvars[pos]) );
2031 
2032    /* move the last variable to the free slot */
2033    consdataMoveLinearVar(consdata, consdata->nlinvars-1, pos);
2034 
2035    --consdata->nlinvars;
2036 
2037    /* invalidate activity */
2038    consdata->activity = SCIP_INVALID;
2039    consdata->minlinactivity = SCIP_INVALID;
2040    consdata->maxlinactivity = SCIP_INVALID;
2041    consdata->minlinactivityinf = -1;
2042    consdata->maxlinactivityinf = -1;
2043 
2044    /* invalidate nonlinear row */
2045    if( consdata->nlrow != NULL )
2046    {
2047       SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
2048    }
2049 
2050    consdata->ispropagated = FALSE;
2051    consdata->ispresolved  = FALSE;
2052 
2053    return SCIP_OKAY;
2054 }
2055 
2056 /** changes linear coefficient value at given position of quadratic constraint */
2057 static
chgLinearCoefPos(SCIP * scip,SCIP_CONS * cons,int pos,SCIP_Real newcoef)2058 SCIP_RETCODE chgLinearCoefPos(
2059    SCIP*                 scip,               /**< SCIP data structure */
2060    SCIP_CONS*            cons,               /**< quadratic constraint */
2061    int                   pos,                /**< position of linear coefficient to change */
2062    SCIP_Real             newcoef             /**< new value of linear coefficient */
2063    )
2064 {
2065    SCIP_CONSHDLR* conshdlr;
2066    SCIP_CONSHDLRDATA* conshdlrdata;
2067    SCIP_CONSDATA* consdata;
2068    SCIP_VAR* var;
2069    SCIP_Real coef;
2070 
2071    assert(scip != NULL);
2072    assert(cons != NULL);
2073    assert(!SCIPconsIsLockedType(cons, SCIP_LOCKTYPE_CONFLICT));
2074    assert(!SCIPisZero(scip, newcoef));
2075 
2076    conshdlrdata = NULL;
2077 
2078    consdata = SCIPconsGetData(cons);
2079    assert(consdata != NULL);
2080    assert(0 <= pos);
2081    assert(pos < consdata->nlinvars);
2082    assert(!SCIPisZero(scip, newcoef));
2083 
2084    var = consdata->linvars[pos];
2085    coef = consdata->lincoefs[pos];
2086    assert(var != NULL);
2087    assert(SCIPconsIsTransformed(cons) == SCIPvarIsTransformed(var));
2088 
2089    /* invalidate activity */
2090    consdata->activity = SCIP_INVALID;
2091    consdata->minlinactivity = SCIP_INVALID;
2092    consdata->maxlinactivity = SCIP_INVALID;
2093    consdata->minlinactivityinf = -1;
2094    consdata->maxlinactivityinf = -1;
2095 
2096    /* invalidate nonlinear row */
2097    if( consdata->nlrow != NULL )
2098    {
2099       SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
2100    }
2101 
2102    /* if necessary, remove the rounding locks and event catching of the variable */
2103    if( newcoef * coef < 0.0 )
2104    {
2105       if( SCIPconsIsLocked(cons) )
2106       {
2107          assert(SCIPconsIsTransformed(cons));
2108 
2109          /* remove rounding locks for variable with old coefficient */
2110          SCIP_CALL( unlockLinearVariable(scip, cons, var, coef) );
2111       }
2112 
2113       if( consdata->lineventdata[pos] != NULL )
2114       {
2115          /* get event handler */
2116          conshdlr = SCIPconsGetHdlr(cons);
2117          conshdlrdata = SCIPconshdlrGetData(conshdlr);
2118          assert(conshdlrdata != NULL);
2119          assert(conshdlrdata->eventhdlr != NULL);
2120 
2121          /* drop bound change events of variable */
2122          SCIP_CALL( dropLinearVarEvents(scip, conshdlrdata->eventhdlr, cons, pos) );
2123       }
2124    }
2125 
2126    /* change the coefficient */
2127    consdata->lincoefs[pos] = newcoef;
2128 
2129    /* if necessary, install the rounding locks and event catching of the variable again */
2130    if( newcoef * coef < 0.0 )
2131    {
2132       if( SCIPconsIsLocked(cons) )
2133       {
2134          /* install rounding locks for variable with new coefficient */
2135          SCIP_CALL( lockLinearVariable(scip, cons, var, newcoef) );
2136       }
2137 
2138       if( conshdlrdata != NULL )
2139       {
2140          assert(SCIPconsIsEnabled(cons));
2141 
2142          /* catch bound change events of variable */
2143          SCIP_CALL( catchLinearVarEvents(scip, conshdlrdata->eventhdlr, cons, pos) );
2144       }
2145    }
2146 
2147    consdata->ispropagated = FALSE;
2148    consdata->ispresolved = FALSE;
2149 
2150    return SCIP_OKAY;
2151 }
2152 
2153 /** adds quadratic variable term to quadratic constraint */
2154 static
addQuadVarTerm(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real lincoef,SCIP_Real sqrcoef)2155 SCIP_RETCODE addQuadVarTerm(
2156    SCIP*                 scip,               /**< SCIP data structure */
2157    SCIP_CONS*            cons,               /**< quadratic constraint */
2158    SCIP_VAR*             var,                /**< variable to add */
2159    SCIP_Real             lincoef,            /**< linear coefficient of variable */
2160    SCIP_Real             sqrcoef             /**< square coefficient of variable */
2161    )
2162 {
2163    SCIP_CONSDATA* consdata;
2164    SCIP_Bool transformed;
2165    SCIP_QUADVARTERM* quadvarterm;
2166 
2167    assert(scip != NULL);
2168    assert(cons != NULL);
2169    assert(var  != NULL);
2170 
2171    consdata = SCIPconsGetData(cons);
2172    assert(consdata != NULL);
2173 
2174    /* are we in the transformed problem? */
2175    transformed = SCIPconsIsTransformed(cons);
2176 
2177    /* always use transformed variables in transformed constraints */
2178    if( transformed )
2179    {
2180       SCIP_CALL( SCIPgetTransformedVar(scip, var, &var) );
2181    }
2182    assert(var != NULL);
2183    assert(transformed == SCIPvarIsTransformed(var));
2184 
2185    SCIP_CALL( consdataEnsureQuadVarTermsSize(scip, consdata, consdata->nquadvars+1) );
2186 
2187    quadvarterm = &consdata->quadvarterms[consdata->nquadvars];
2188    quadvarterm->var       = var;
2189    quadvarterm->lincoef   = lincoef;
2190    quadvarterm->sqrcoef   = sqrcoef;
2191    quadvarterm->adjbilinsize = 0;
2192    quadvarterm->nadjbilin = 0;
2193    quadvarterm->adjbilin  = NULL;
2194    quadvarterm->eventdata = NULL;
2195 
2196    ++consdata->nquadvars;
2197 
2198    /* capture variable */
2199    SCIP_CALL( SCIPcaptureVar(scip, var) );
2200 
2201    /* catch variable events, if we do so */
2202    if( SCIPconsIsEnabled(cons) )
2203    {
2204       SCIP_CONSHDLR* conshdlr;
2205       SCIP_CONSHDLRDATA* conshdlrdata;
2206 
2207       /* get event handler */
2208       conshdlr = SCIPconsGetHdlr(cons);
2209       conshdlrdata = SCIPconshdlrGetData(conshdlr);
2210       assert(conshdlrdata != NULL);
2211       assert(conshdlrdata->eventhdlr != NULL);
2212 
2213       /* catch bound change events of variable */
2214       SCIP_CALL( catchQuadVarEvents(scip, conshdlrdata->eventhdlr, cons, consdata->nquadvars-1) );
2215    }
2216 
2217    /* invalidate activity information */
2218    consdata->activity = SCIP_INVALID;
2219    SCIPintervalSetEmpty(&consdata->quadactivitybounds);
2220 
2221    /* invalidate nonlinear row */
2222    if( consdata->nlrow != NULL )
2223    {
2224       SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
2225    }
2226 
2227    /* install rounding locks for new variable */
2228    SCIP_CALL( lockQuadraticVariable(scip, cons, var) );
2229 
2230    consdata->ispropagated = FALSE;
2231    consdata->ispresolved  = FALSE;
2232    consdata->isremovedfixings = consdata->isremovedfixings && SCIPvarIsActive(var)
2233       && !SCIPisEQ(scip, SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var));
2234    if( consdata->nquadvars == 1 )
2235       consdata->quadvarssorted = TRUE;
2236    else
2237       consdata->quadvarssorted = consdata->quadvarssorted &&
2238          (SCIPvarCompare(consdata->quadvarterms[consdata->nquadvars-2].var, consdata->quadvarterms[consdata->nquadvars-1].var) == -1);
2239    /* also set to FALSE if nquadvars == 1, since the new variable should be checked for linearity and other stuff in mergeAndClean ... */
2240    consdata->quadvarsmerged = FALSE;
2241 
2242    consdata->iscurvchecked = FALSE;
2243 
2244    return SCIP_OKAY;
2245 }
2246 
2247 /** deletes quadratic variable term at given position from quadratic constraint data */
2248 static
delQuadVarTermPos(SCIP * scip,SCIP_CONS * cons,int pos)2249 SCIP_RETCODE delQuadVarTermPos(
2250    SCIP*                 scip,               /**< SCIP data structure */
2251    SCIP_CONS*            cons,               /**< quadratic constraint */
2252    int                   pos                 /**< position of term to delete */
2253    )
2254 {
2255    SCIP_CONSDATA* consdata;
2256    SCIP_VAR* var;
2257 
2258    assert(scip != NULL);
2259    assert(cons != NULL);
2260 
2261    consdata = SCIPconsGetData(cons);
2262    assert(consdata != NULL);
2263    assert(0 <= pos && pos < consdata->nquadvars);
2264 
2265    var = consdata->quadvarterms[pos].var;
2266    assert(var != NULL);
2267    assert(consdata->quadvarterms[pos].nadjbilin == 0);
2268 
2269    /* remove rounding locks for deleted variable */
2270    SCIP_CALL( unlockQuadraticVariable(scip, cons, var) );
2271 
2272    /* if we catch variable events, drop the events on the variable */
2273    if( consdata->quadvarterms[pos].eventdata != NULL )
2274    {
2275       SCIP_CONSHDLR* conshdlr;
2276       SCIP_CONSHDLRDATA* conshdlrdata;
2277 
2278       /* get event handler */
2279       conshdlr = SCIPconsGetHdlr(cons);
2280       conshdlrdata = SCIPconshdlrGetData(conshdlr);
2281       assert(conshdlrdata != NULL);
2282       assert(conshdlrdata->eventhdlr != NULL);
2283 
2284       /* drop bound change events of variable */
2285       SCIP_CALL( dropQuadVarEvents(scip, conshdlrdata->eventhdlr, cons, pos) );
2286    }
2287 
2288    /* release variable */
2289    SCIP_CALL( SCIPreleaseVar(scip, &consdata->quadvarterms[pos].var) );
2290 
2291    /* free adjacency array */
2292    SCIPfreeBlockMemoryArrayNull(scip, &consdata->quadvarterms[pos].adjbilin, consdata->quadvarterms[pos].adjbilinsize);
2293 
2294    /* move the last variable term to the free slot */
2295    consdataMoveQuadVarTerm(consdata, consdata->nquadvars-1, pos);
2296 
2297    --consdata->nquadvars;
2298 
2299    /* invalidate activity */
2300    consdata->activity = SCIP_INVALID;
2301    SCIPintervalSetEmpty(&consdata->quadactivitybounds);
2302 
2303    /* invalidate nonlinear row */
2304    if( consdata->nlrow != NULL )
2305    {
2306       SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
2307    }
2308 
2309    consdata->ispropagated  = FALSE;
2310    consdata->ispresolved   = FALSE;
2311    consdata->iscurvchecked = FALSE;
2312 
2313    return SCIP_OKAY;
2314 }
2315 
2316 /** replace variable in quadratic variable term at given position of quadratic constraint data
2317  *
2318  * Allows to replace x by coef*y+offset, thereby maintaining linear and square coefficients and bilinear terms.
2319  */
2320 static
replaceQuadVarTermPos(SCIP * scip,SCIP_CONS * cons,int pos,SCIP_VAR * var,SCIP_Real coef,SCIP_Real offset)2321 SCIP_RETCODE replaceQuadVarTermPos(
2322    SCIP*                 scip,               /**< SCIP data structure */
2323    SCIP_CONS*            cons,               /**< quadratic constraint */
2324    int                   pos,                /**< position of term to replace */
2325    SCIP_VAR*             var,                /**< new variable */
2326    SCIP_Real             coef,               /**< linear coefficient of new variable */
2327    SCIP_Real             offset              /**< offset of new variable */
2328    )
2329 {
2330    SCIP_CONSDATA* consdata;
2331    SCIP_QUADVARTERM* quadvarterm;
2332    SCIP_EVENTHDLR* eventhdlr;
2333    SCIP_BILINTERM* bilinterm;
2334    SCIP_Real constant;
2335 
2336    int i;
2337    SCIP_VAR* var2;
2338 
2339    consdata = SCIPconsGetData(cons);
2340    assert(consdata != NULL);
2341    assert(pos >= 0);
2342    assert(pos <  consdata->nquadvars);
2343 
2344    quadvarterm = &consdata->quadvarterms[pos];
2345 
2346    /* remove rounding locks for old variable */
2347    SCIP_CALL( unlockQuadraticVariable(scip, cons, quadvarterm->var) );
2348 
2349    /* if we catch variable events, drop the events on the old variable */
2350    if( quadvarterm->eventdata != NULL )
2351    {
2352       SCIP_CONSHDLR* conshdlr;
2353       SCIP_CONSHDLRDATA* conshdlrdata;
2354 
2355       /* get event handler */
2356       conshdlr = SCIPconsGetHdlr(cons);
2357       conshdlrdata = SCIPconshdlrGetData(conshdlr);
2358       assert(conshdlrdata != NULL);
2359       assert(conshdlrdata->eventhdlr != NULL);
2360 
2361       eventhdlr = conshdlrdata->eventhdlr;
2362 
2363       /* drop bound change events of variable */
2364       SCIP_CALL( dropQuadVarEvents(scip, eventhdlr, cons, pos) );
2365    }
2366    else
2367    {
2368       eventhdlr = NULL;
2369    }
2370 
2371    /* compute constant and put into lhs/rhs */
2372    constant = quadvarterm->lincoef * offset + quadvarterm->sqrcoef * offset * offset;
2373    if( constant != 0.0 )
2374    {
2375       /* maintain constant part */
2376       if( !SCIPisInfinity(scip, -consdata->lhs) )
2377          consdata->lhs -= constant;
2378       if( !SCIPisInfinity(scip,  consdata->rhs) )
2379          consdata->rhs -= constant;
2380    }
2381 
2382    /* update linear and square coefficient */
2383    quadvarterm->lincoef *= coef;
2384    quadvarterm->lincoef += 2.0 * quadvarterm->sqrcoef * coef * offset;
2385    quadvarterm->sqrcoef *= coef * coef;
2386 
2387    /* update bilinear terms */
2388    for( i = 0; i < quadvarterm->nadjbilin; ++i )
2389    {
2390       bilinterm = &consdata->bilinterms[quadvarterm->adjbilin[i]];
2391 
2392       if( bilinterm->var1 == quadvarterm->var )
2393       {
2394          bilinterm->var1 = var;
2395          var2 = bilinterm->var2;
2396       }
2397       else
2398       {
2399          assert(bilinterm->var2 == quadvarterm->var);
2400          bilinterm->var2 = var;
2401          var2 = bilinterm->var1;
2402       }
2403 
2404       if( var == var2 )
2405       {
2406          /* looks like we actually have a square term here */
2407          quadvarterm->lincoef += bilinterm->coef * offset;
2408          quadvarterm->sqrcoef += bilinterm->coef * coef;
2409          /* deleting bilinear terms is expensive, since it requires updating adjacency information
2410           * thus, for now we just set the coefficient to 0.0 and clear in later when the bilinear terms are merged */
2411          bilinterm->coef = 0.0;
2412          continue;
2413       }
2414 
2415       /* swap var1 and var2 if they are in wrong order */
2416       if( SCIPvarCompare(bilinterm->var1, bilinterm->var2) > 0 )
2417       {
2418          SCIP_VAR* tmp;
2419          tmp = bilinterm->var1;
2420          bilinterm->var1 = bilinterm->var2;
2421          bilinterm->var2 = tmp;
2422       }
2423       assert(SCIPvarCompare(bilinterm->var1, bilinterm->var2) == -1);
2424 
2425       if( offset != 0.0 )
2426       {
2427          /* need to find var2 and add offset*bilinterm->coef to linear coefficient */
2428          int var2pos;
2429 
2430          var2pos = 0;
2431          while( consdata->quadvarterms[var2pos].var != var2 )
2432          {
2433             ++var2pos;
2434             assert(var2pos < consdata->nquadvars);
2435          }
2436 
2437          consdata->quadvarterms[var2pos].lincoef += bilinterm->coef * offset;
2438       }
2439 
2440       bilinterm->coef *= coef;
2441    }
2442 
2443    /* release old variable */
2444    SCIP_CALL( SCIPreleaseVar(scip, &quadvarterm->var) );
2445 
2446    /* set new variable */
2447    quadvarterm->var = var;
2448 
2449    /* capture new variable */
2450    SCIP_CALL( SCIPcaptureVar(scip, quadvarterm->var) );
2451 
2452    /* catch variable events, if we do so */
2453    if( eventhdlr != NULL )
2454    {
2455       assert(SCIPconsIsEnabled(cons));
2456 
2457       /* catch bound change events of variable */
2458       SCIP_CALL( catchQuadVarEvents(scip, eventhdlr, cons, pos) );
2459    }
2460 
2461    /* invalidate activity information */
2462    consdata->activity = SCIP_INVALID;
2463    SCIPintervalSetEmpty(&consdata->quadactivitybounds);
2464 
2465    /* invalidate nonlinear row */
2466    if( consdata->nlrow != NULL )
2467    {
2468       SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
2469    }
2470 
2471    /* install rounding locks for new variable */
2472    SCIP_CALL( lockQuadraticVariable(scip, cons, var) );
2473 
2474    consdata->isremovedfixings = consdata->isremovedfixings && SCIPvarIsActive(var)
2475       && !SCIPisEQ(scip, SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var));
2476    consdata->quadvarssorted = (consdata->nquadvars == 1);
2477    consdata->quadvarsmerged = FALSE;
2478    consdata->bilinsorted &= (quadvarterm->nadjbilin == 0);  /*lint !e514*/
2479    consdata->bilinmerged &= (quadvarterm->nadjbilin == 0);  /*lint !e514*/
2480 
2481    consdata->ispropagated  = FALSE;
2482    consdata->ispresolved   = FALSE;
2483    consdata->iscurvchecked = FALSE;
2484 
2485    return SCIP_OKAY;
2486 }
2487 
2488 /** adds a bilinear term to quadratic constraint */
2489 static
addBilinearTerm(SCIP * scip,SCIP_CONS * cons,int var1pos,int var2pos,SCIP_Real coef)2490 SCIP_RETCODE addBilinearTerm(
2491    SCIP*                 scip,               /**< SCIP data structure */
2492    SCIP_CONS*            cons,               /**< quadratic constraint */
2493    int                   var1pos,            /**< position of first variable in quadratic variables array */
2494    int                   var2pos,            /**< position of second variable in quadratic variables array */
2495    SCIP_Real             coef                /**< coefficient of bilinear term */
2496    )
2497 {
2498    SCIP_CONSDATA* consdata;
2499    SCIP_BILINTERM* bilinterm;
2500 
2501    assert(scip != NULL);
2502    assert(cons != NULL);
2503 
2504    if( var1pos == var2pos )
2505    {
2506       SCIPerrorMessage("tried to add bilinear term where both variables are the same\n");
2507       return SCIP_INVALIDDATA;
2508    }
2509 
2510    consdata = SCIPconsGetData(cons);
2511    assert(consdata != NULL);
2512 
2513    /* check if the bilinear terms are sorted (disabled for big constraints as becoming expensive) */
2514    assert(consdata->nbilinterms > 10 || consdataCheckBilinTermsSort(consdata));
2515 
2516    assert(var1pos >= 0);
2517    assert(var1pos < consdata->nquadvars);
2518    assert(var2pos >= 0);
2519    assert(var2pos < consdata->nquadvars);
2520 
2521    SCIP_CALL( consdataEnsureBilinSize(scip, consdata, consdata->nbilinterms + 1) );
2522 
2523    bilinterm = &consdata->bilinterms[consdata->nbilinterms];
2524    if( SCIPvarCompare(consdata->quadvarterms[var1pos].var, consdata->quadvarterms[var2pos].var) < 0 )
2525    {
2526       bilinterm->var1 = consdata->quadvarterms[var1pos].var;
2527       bilinterm->var2 = consdata->quadvarterms[var2pos].var;
2528    }
2529    else
2530    {
2531       bilinterm->var1 = consdata->quadvarterms[var2pos].var;
2532       bilinterm->var2 = consdata->quadvarterms[var1pos].var;
2533    }
2534    bilinterm->coef = coef;
2535 
2536    if( bilinterm->var1 == bilinterm->var2 )
2537    {
2538       SCIPerrorMessage("tried to add bilinear term where both variables are the same, but appear at different positions in quadvarterms array\n");
2539       return SCIP_INVALIDDATA;
2540    }
2541    assert(SCIPvarCompare(bilinterm->var1, bilinterm->var2) == -1);
2542 
2543    SCIP_CALL( consdataEnsureAdjBilinSize(scip, &consdata->quadvarterms[var1pos], consdata->quadvarterms[var1pos].nadjbilin + 1) );
2544    SCIP_CALL( consdataEnsureAdjBilinSize(scip, &consdata->quadvarterms[var2pos], consdata->quadvarterms[var2pos].nadjbilin + 1) );
2545 
2546    consdata->quadvarterms[var1pos].adjbilin[consdata->quadvarterms[var1pos].nadjbilin] = consdata->nbilinterms;
2547    consdata->quadvarterms[var2pos].adjbilin[consdata->quadvarterms[var2pos].nadjbilin] = consdata->nbilinterms;
2548    ++consdata->quadvarterms[var1pos].nadjbilin;
2549    ++consdata->quadvarterms[var2pos].nadjbilin;
2550 
2551    ++consdata->nbilinterms;
2552 
2553    /* invalidate activity information */
2554    consdata->activity = SCIP_INVALID;
2555    SCIPintervalSetEmpty(&consdata->quadactivitybounds);
2556 
2557    /* invalidate nonlinear row */
2558    if( consdata->nlrow != NULL )
2559    {
2560       SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
2561    }
2562 
2563    consdata->ispropagated = FALSE;
2564    consdata->ispresolved  = FALSE;
2565    if( consdata->nbilinterms == 1 )
2566    {
2567       consdata->bilinsorted = TRUE;
2568 
2569       /* we have to take care of the bilinear term in mergeAndCleanBilinearTerms() if the coefficient is zero */
2570       consdata->bilinmerged = !SCIPisZero(scip, consdata->bilinterms[0].coef);
2571    }
2572    else
2573    {
2574       consdata->bilinsorted = consdata->bilinsorted
2575          && (bilinTermComp(consdata, consdata->nbilinterms-2, consdata->nbilinterms-1) <= 0);
2576       consdata->bilinmerged = FALSE;
2577    }
2578 
2579    consdata->iscurvchecked = FALSE;
2580 
2581    /* check if the bilinear terms are sorted (disabled as expensive if big constraint) */
2582    assert(consdata->nbilinterms > 10 || consdataCheckBilinTermsSort(consdata));
2583 
2584    return SCIP_OKAY;
2585 }
2586 
2587 /** removes a set of bilinear terms and updates adjacency information in quad var terms
2588  *
2589  * Note: this function sorts the given array termposs.
2590  */
2591 static
removeBilinearTermsPos(SCIP * scip,SCIP_CONS * cons,int nterms,int * termposs)2592 SCIP_RETCODE removeBilinearTermsPos(
2593    SCIP*                 scip,               /**< SCIP data structure */
2594    SCIP_CONS*            cons,               /**< quadratic constraint */
2595    int                   nterms,             /**< number of terms to delete */
2596    int*                  termposs            /**< indices of terms to delete */
2597    )
2598 {
2599    SCIP_CONSDATA* consdata;
2600    int* newpos;
2601    int i;
2602    int j;
2603    int offset;
2604 
2605    assert(scip != NULL);
2606    assert(cons != NULL);
2607    assert(nterms == 0 || termposs != NULL);
2608 
2609    if( nterms == 0 )
2610       return SCIP_OKAY;
2611 
2612    consdata = SCIPconsGetData(cons);
2613    assert(consdata != NULL);
2614 
2615    SCIPsortInt(termposs, nterms);
2616 
2617    SCIP_CALL( SCIPallocBufferArray(scip, &newpos, consdata->nbilinterms) );
2618 
2619    i = 0;
2620    offset = 0;
2621    for( j = 0; j < consdata->nbilinterms; ++j )
2622    {
2623       /* if j'th term is deleted, increase offset and continue */
2624       if( i < nterms && j == termposs[i] )
2625       {
2626          ++offset;
2627          ++i;
2628          newpos[j] = -1;
2629          continue;
2630       }
2631 
2632       /* otherwise, move it forward and remember new position */
2633       if( offset > 0 )
2634          consdata->bilinterms[j-offset] = consdata->bilinterms[j];
2635       newpos[j] = j - offset;
2636    }
2637    assert(offset == nterms);
2638 
2639    /* update adjacency and activity information in quad var terms */
2640    for( i = 0; i < consdata->nquadvars; ++i )
2641    {
2642       offset = 0;
2643       for( j = 0; j < consdata->quadvarterms[i].nadjbilin; ++j )
2644       {
2645          assert(consdata->quadvarterms[i].adjbilin[j] < consdata->nbilinterms);
2646          if( newpos[consdata->quadvarterms[i].adjbilin[j]] == -1 )
2647          {
2648             /* corresponding bilinear term was deleted, thus increase offset */
2649             ++offset;
2650          }
2651          else
2652          {
2653             /* update index of j'th bilinear term and store at position j-offset */
2654             consdata->quadvarterms[i].adjbilin[j-offset] = newpos[consdata->quadvarterms[i].adjbilin[j]];
2655          }
2656       }
2657       consdata->quadvarterms[i].nadjbilin -= offset;
2658       /* some bilinear term was removed, so invalidate activity bounds */
2659    }
2660 
2661    consdata->nbilinterms -= nterms;
2662 
2663    SCIPfreeBufferArray(scip, &newpos);
2664 
2665    /* some quad vars may be linear now */
2666    consdata->quadvarsmerged = FALSE;
2667 
2668    consdata->ispropagated  = FALSE;
2669    consdata->ispresolved   = FALSE;
2670    consdata->iscurvchecked = FALSE;
2671 
2672    /* invalidate activity */
2673    consdata->activity = SCIP_INVALID;
2674    SCIPintervalSetEmpty(&consdata->quadactivitybounds);
2675 
2676    /* invalidate nonlinear row */
2677    if( consdata->nlrow != NULL )
2678    {
2679       SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
2680    }
2681 
2682    return SCIP_OKAY;
2683 }
2684 
2685 /** changes side of constraint and allow to change between finite and infinite
2686  *
2687  * takes care of updating events and locks of linear variables
2688  */
2689 static
chgSideQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_EVENTHDLR * eventhdlr,SCIP_SIDETYPE side,SCIP_Real sideval)2690 SCIP_RETCODE chgSideQuadratic(
2691    SCIP*                 scip,               /**< SCIP data structure */
2692    SCIP_CONS*            cons,               /**< constraint */
2693    SCIP_EVENTHDLR*       eventhdlr,          /**< event handler */
2694    SCIP_SIDETYPE         side,               /**< which side to change */
2695    SCIP_Real             sideval             /**< new value for side */
2696    )
2697 {
2698    SCIP_CONSDATA* consdata;
2699    int i;
2700 
2701    assert(scip != NULL);
2702    assert(cons != NULL);
2703    assert(!SCIPisInfinity(scip, side == SCIP_SIDETYPE_LEFT ? sideval : -sideval));
2704 
2705    consdata = SCIPconsGetData(cons);
2706    assert(consdata != NULL);
2707 
2708    /* if remaining finite or remaining infinite, then can just update the value */
2709    if( side == SCIP_SIDETYPE_LEFT )
2710    {
2711       if( SCIPisInfinity(scip, -consdata->lhs) == SCIPisInfinity(scip, -sideval) )
2712       {
2713          consdata->lhs = sideval;
2714          return SCIP_OKAY;
2715       }
2716    }
2717    else
2718    {
2719       if( SCIPisInfinity(scip, consdata->rhs) == SCIPisInfinity(scip, sideval) )
2720       {
2721          consdata->rhs = sideval;
2722          return SCIP_OKAY;
2723       }
2724    }
2725 
2726    /* catched boundchange events and locks for linear variables depends on whether side is finite, so first drop all */
2727    for( i = 0; i < consdata->nlinvars; ++i )
2728    {
2729       if( consdata->lineventdata != NULL && consdata->lineventdata[i] != NULL )
2730       {
2731          assert(SCIPconsIsEnabled(cons));
2732 
2733          SCIP_CALL( dropLinearVarEvents(scip, eventhdlr, cons, i) );
2734       }
2735 
2736       if( SCIPconsIsLocked(cons) )
2737       {
2738          assert(SCIPconsIsTransformed(cons));
2739 
2740          /* remove rounding locks for variable with old side */
2741          SCIP_CALL( unlockLinearVariable(scip, cons, consdata->linvars[i], consdata->lincoefs[i]) );
2742       }
2743    }
2744 
2745    if( side == SCIP_SIDETYPE_LEFT )
2746       consdata->lhs = sideval;
2747    else
2748       consdata->rhs = sideval;
2749 
2750    /* catch boundchange events and locks on variables again */
2751    for( i = 0; i < consdata->nlinvars; ++i )
2752    {
2753       if( consdata->lineventdata != NULL )
2754       {
2755          SCIP_CALL( catchLinearVarEvents(scip, eventhdlr, cons, i) );
2756       }
2757 
2758       if( SCIPconsIsLocked(cons) )
2759       {
2760          /* add rounding locks for variable with new side */
2761          SCIP_CALL( lockLinearVariable(scip, cons, consdata->linvars[i], consdata->lincoefs[i]) );
2762       }
2763    }
2764 
2765    return SCIP_OKAY;
2766 }
2767 
2768 /** merges quad var terms that correspond to the same variable and does additional cleanup
2769  *
2770  *  If a quadratic variable terms is actually linear, makes a linear term out of it
2771  *  also replaces squares of binary variables by the binary variables, i.e., adds sqrcoef to lincoef.
2772  */
2773 static
mergeAndCleanQuadVarTerms(SCIP * scip,SCIP_CONS * cons)2774 SCIP_RETCODE mergeAndCleanQuadVarTerms(
2775    SCIP*                 scip,               /**< SCIP data structure */
2776    SCIP_CONS*            cons                /**< quadratic constraint */
2777    )
2778 {
2779    SCIP_QUADVARTERM* quadvarterm;
2780    SCIP_CONSDATA* consdata;
2781    int i;
2782    int j;
2783 
2784    assert(scip != NULL);
2785    assert(cons != NULL);
2786 
2787    consdata = SCIPconsGetData(cons);
2788 
2789    if( consdata->quadvarsmerged )
2790       return SCIP_OKAY;
2791 
2792    if( consdata->nquadvars == 0 )
2793    {
2794       consdata->quadvarsmerged = TRUE;
2795       return SCIP_OKAY;
2796    }
2797 
2798    i = 0;
2799    while( i < consdata->nquadvars )
2800    {
2801       /* make sure quad var terms are sorted (do this in every round, since we may move variables around) */
2802       SCIP_CALL( consdataSortQuadVarTerms(scip, consdata) );
2803 
2804       quadvarterm = &consdata->quadvarterms[i];
2805 
2806       for( j = i+1; j < consdata->nquadvars && consdata->quadvarterms[j].var == quadvarterm->var; ++j )
2807       {
2808          /* add quad var term j to current term i */
2809          quadvarterm->lincoef += consdata->quadvarterms[j].lincoef;
2810          quadvarterm->sqrcoef += consdata->quadvarterms[j].sqrcoef;
2811          if( consdata->quadvarterms[j].nadjbilin > 0 )
2812          {
2813             /* move adjacency information from j to i */
2814             SCIP_CALL( consdataEnsureAdjBilinSize(scip, quadvarterm, quadvarterm->nadjbilin + consdata->quadvarterms[j].nadjbilin) );
2815             BMScopyMemoryArray(&quadvarterm->adjbilin[quadvarterm->nadjbilin], consdata->quadvarterms[j].adjbilin, consdata->quadvarterms[j].nadjbilin);  /*lint !e866*/
2816             quadvarterm->nadjbilin += consdata->quadvarterms[j].nadjbilin;
2817             consdata->quadvarterms[j].nadjbilin = 0;
2818          }
2819          consdata->quadvarterms[j].lincoef = 0.0;
2820          consdata->quadvarterms[j].sqrcoef = 0.0;
2821          /* mark that activity information in quadvarterm is not up to date anymore */
2822       }
2823 
2824       /* remove quad var terms i+1..j-1 backwards */
2825       for( j = j-1; j > i; --j )
2826       {
2827          SCIP_CALL( delQuadVarTermPos(scip, cons, j) );
2828       }
2829 
2830       /* for binary variables, x^2 = x
2831        * however, we may destroy convexity of a quadratic term that involves also bilinear terms
2832        * thus, we do this step only if the variable does not appear in any bilinear term */
2833       if( quadvarterm->sqrcoef != 0.0 && SCIPvarIsBinary(quadvarterm->var) && quadvarterm->nadjbilin == 0 )
2834       {
2835          SCIPdebugMsg(scip, "replace square of binary variable by itself: <%s>^2 --> <%s>\n", SCIPvarGetName(quadvarterm->var), SCIPvarGetName(quadvarterm->var));
2836          quadvarterm->lincoef += quadvarterm->sqrcoef;
2837          quadvarterm->sqrcoef = 0.0;
2838 
2839          /* invalidate nonlinear row */
2840          if( consdata->nlrow != NULL )
2841          {
2842             SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
2843          }
2844       }
2845 
2846       /* if its 0.0 or linear, get rid of it */
2847       if( SCIPisZero(scip, quadvarterm->sqrcoef) && quadvarterm->nadjbilin == 0 )
2848       {
2849          if( !SCIPisZero(scip, quadvarterm->lincoef) )
2850          {
2851             /* seem to be a linear term now, thus add as linear term */
2852             SCIP_CALL( addLinearCoef(scip, cons, quadvarterm->var, quadvarterm->lincoef) );
2853          }
2854          /* remove term at pos i */
2855          SCIP_CALL( delQuadVarTermPos(scip, cons, i) );
2856       }
2857       else
2858       {
2859          ++i;
2860       }
2861    }
2862 
2863    consdata->quadvarsmerged = TRUE;
2864    SCIPintervalSetEmpty(&consdata->quadactivitybounds);
2865 
2866    return SCIP_OKAY;
2867 }
2868 
2869 /** merges entries with same linear variable into one entry and cleans up entries with coefficient 0.0 */
2870 static
mergeAndCleanLinearVars(SCIP * scip,SCIP_CONS * cons)2871 SCIP_RETCODE mergeAndCleanLinearVars(
2872    SCIP*                 scip,               /**< SCIP data structure */
2873    SCIP_CONS*            cons                /**< quadratic constraint */
2874    )
2875 {
2876    SCIP_CONSDATA* consdata;
2877    SCIP_Real newcoef;
2878    int i;
2879    int j;
2880    int qvarpos;
2881 
2882    assert(scip != NULL);
2883    assert(cons != NULL);
2884 
2885    consdata = SCIPconsGetData(cons);
2886 
2887    if( consdata->linvarsmerged )
2888       return SCIP_OKAY;
2889 
2890    if( consdata->nlinvars == 0 )
2891    {
2892       consdata->linvarsmerged = TRUE;
2893       return SCIP_OKAY;
2894    }
2895 
2896    i = 0;
2897    while( i < consdata->nlinvars )
2898    {
2899       /* make sure linear variables are sorted (do this in every round, since we may move variables around) */
2900       consdataSortLinearVars(consdata);
2901 
2902       /* sum up coefficients that correspond to variable i */
2903       newcoef = consdata->lincoefs[i];
2904       for( j = i+1; j < consdata->nlinvars && consdata->linvars[i] == consdata->linvars[j]; ++j )
2905          newcoef += consdata->lincoefs[j];
2906       /* delete the additional variables in backward order */
2907       for( j = j-1; j > i; --j )
2908       {
2909          SCIP_CALL( delLinearCoefPos(scip, cons, j) );
2910       }
2911 
2912       /* check if there is already a quadratic variable term with this variable */
2913       SCIP_CALL( consdataFindQuadVarTerm(scip, consdata, consdata->linvars[i], &qvarpos) );
2914       if( qvarpos >= 0)
2915       {
2916          /* add newcoef to linear coefficient of quadratic variable and mark linear variable as to delete */
2917          assert(qvarpos < consdata->nquadvars);
2918          assert(consdata->quadvarterms[qvarpos].var == consdata->linvars[i]);
2919          consdata->quadvarterms[qvarpos].lincoef += newcoef;
2920          newcoef = 0.0;
2921          SCIPintervalSetEmpty(&consdata->quadactivitybounds);
2922       }
2923 
2924       /* delete also entry at position i, if it became zero (or was zero before) */
2925       if( SCIPisZero(scip, newcoef) )
2926       {
2927          SCIP_CALL( delLinearCoefPos(scip, cons, i) );
2928       }
2929       else
2930       {
2931          SCIP_CALL( chgLinearCoefPos(scip, cons, i, newcoef) );
2932          ++i;
2933       }
2934    }
2935 
2936    consdata->linvarsmerged = TRUE;
2937 
2938    return SCIP_OKAY;
2939 }
2940 
2941 /** merges bilinear terms with same variables into a single term, removes bilinear terms with coefficient 0.0 */
2942 static
mergeAndCleanBilinearTerms(SCIP * scip,SCIP_CONS * cons)2943 SCIP_RETCODE mergeAndCleanBilinearTerms(
2944    SCIP*                 scip,               /**< SCIP data structure */
2945    SCIP_CONS*            cons                /**< quadratic constraint */
2946    )
2947 {
2948    SCIP_CONSDATA* consdata;
2949    SCIP_BILINTERM* bilinterm;
2950    int i;
2951    int j;
2952    int* todelete;
2953    int ntodelete;
2954 
2955    assert(scip != NULL);
2956    assert(cons != NULL);
2957 
2958    consdata = SCIPconsGetData(cons);
2959 
2960    /* check if the bilinear terms are sorted */
2961    assert(consdataCheckBilinTermsSort(consdata));
2962 
2963    if( consdata->bilinmerged )
2964       return SCIP_OKAY;
2965 
2966    if( consdata->nbilinterms == 0 )
2967    {
2968       consdata->bilinmerged = TRUE;
2969       return SCIP_OKAY;
2970    }
2971 
2972    /* alloc memory for array of terms that need to be deleted finally */
2973    ntodelete = 0;
2974    SCIP_CALL( SCIPallocBufferArray(scip, &todelete, consdata->nbilinterms) );
2975 
2976    /* make sure bilinear terms are sorted */
2977    SCIP_CALL( consdataSortBilinTerms(scip, consdata) );
2978 
2979    i = 0;
2980    while( i < consdata->nbilinterms )
2981    {
2982       bilinterm = &consdata->bilinterms[i];
2983 
2984       /* sum up coefficients that correspond to same variables as term i */
2985       for( j = i+1; j < consdata->nbilinterms && bilinterm->var1 == consdata->bilinterms[j].var1 && bilinterm->var2 == consdata->bilinterms[j].var2; ++j )
2986       {
2987          bilinterm->coef += consdata->bilinterms[j].coef;
2988          todelete[ntodelete++] = j;
2989       }
2990 
2991       /* delete also entry at position i, if it became zero (or was zero before) */
2992       if( SCIPisZero(scip, bilinterm->coef) )
2993       {
2994          todelete[ntodelete++] = i;
2995       }
2996 
2997       /* continue with term after the current series */
2998       i = j;
2999    }
3000 
3001    /* delete bilinear terms */
3002    SCIP_CALL( removeBilinearTermsPos(scip, cons, ntodelete, todelete) );
3003 
3004    SCIPfreeBufferArray(scip, &todelete);
3005 
3006    consdata->bilinmerged = TRUE;
3007 
3008    /* check if the bilinear terms are sorted */
3009    assert(consdataCheckBilinTermsSort(consdata));
3010 
3011    return SCIP_OKAY;
3012 }
3013 
3014 /** removes fixes (or aggregated) variables from a quadratic constraint */
3015 static
removeFixedVariables(SCIP * scip,SCIP_EVENTHDLR * eventhdlr,SCIP_CONS * cons)3016 SCIP_RETCODE removeFixedVariables(
3017    SCIP*                 scip,               /**< SCIP data structure */
3018    SCIP_EVENTHDLR*       eventhdlr,          /**< event handler for variable bound changes */
3019    SCIP_CONS*            cons                /**< quadratic constraint */
3020    )
3021 {
3022    SCIP_CONSDATA* consdata;
3023    SCIP_BILINTERM* bilinterm;
3024    SCIP_Real bilincoef;
3025    SCIP_Real coef;
3026    SCIP_Real offset;
3027    SCIP_VAR* var;
3028    SCIP_VAR* var2;
3029    int var2pos;
3030    int i;
3031    int j;
3032    int k;
3033 
3034    SCIP_Bool have_change;
3035 
3036    assert(scip != NULL);
3037    assert(cons != NULL);
3038 
3039    consdata = SCIPconsGetData(cons);
3040 
3041    have_change = FALSE;
3042    i = 0;
3043    while( i < consdata->nlinvars )
3044    {
3045       var = consdata->linvars[i];
3046 
3047       if( SCIPvarIsActive(var) && !SCIPisEQ(scip, SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var)) )
3048       {
3049          ++i;
3050          continue;
3051       }
3052 
3053       have_change = TRUE;
3054 
3055       coef = consdata->lincoefs[i];
3056       offset = 0.0;
3057 
3058       if( SCIPisEQ(scip, SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var)) )
3059       {
3060          offset = coef * (SCIPvarGetLbGlobal(var) + SCIPvarGetUbGlobal(var)) / 2.0;
3061          coef = 0.0;
3062       }
3063       else
3064       {
3065          SCIP_CALL( SCIPgetProbvarSum(scip, &var, &coef, &offset) );
3066       }
3067 
3068       SCIPdebugMsg(scip, "  linear term %g*<%s> is replaced by %g * <%s> + %g\n", consdata->lincoefs[i], SCIPvarGetName(consdata->linvars[i]),
3069          coef, SCIPvarGetName(var), offset);
3070 
3071       /* delete previous variable (this will move another variable to position i) */
3072       SCIP_CALL( delLinearCoefPos(scip, cons, i) );
3073 
3074       /* put constant part into bounds */
3075       if( offset != 0.0 )
3076       {
3077          if( !SCIPisInfinity(scip, -consdata->lhs) )
3078          {
3079             SCIP_CALL( chgSideQuadratic(scip, cons, eventhdlr, SCIP_SIDETYPE_LEFT, consdata->lhs - offset) );
3080          }
3081          if( !SCIPisInfinity(scip,  consdata->rhs) )
3082          {
3083             SCIP_CALL( chgSideQuadratic(scip, cons, eventhdlr, SCIP_SIDETYPE_RIGHT, consdata->rhs - offset) );
3084          }
3085       }
3086 
3087       /* nothing left to do if variable had been fixed */
3088       if( coef == 0.0 )
3089          continue;
3090 
3091       /* if GetProbvar gave a linear variable, just add it
3092        * if it's a multilinear variable, add it's disaggregated variables */
3093       if( SCIPvarIsActive(var) )
3094       {
3095          SCIP_CALL( addLinearCoef(scip, cons, var, coef) );
3096       }
3097       else
3098       {
3099          int        naggrs;
3100          SCIP_VAR** aggrvars;
3101          SCIP_Real* aggrscalars;
3102          SCIP_Real  aggrconstant;
3103 
3104          assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
3105 
3106          naggrs = SCIPvarGetMultaggrNVars(var);
3107          aggrvars = SCIPvarGetMultaggrVars(var);
3108          aggrscalars = SCIPvarGetMultaggrScalars(var);
3109          aggrconstant = SCIPvarGetMultaggrConstant(var);
3110 
3111          SCIP_CALL( consdataEnsureLinearVarsSize(scip, consdata, consdata->nlinvars + naggrs) );
3112 
3113          for( j = 0; j < naggrs; ++j )
3114          {
3115             SCIP_CALL( addLinearCoef(scip, cons, aggrvars[j], coef * aggrscalars[j]) );
3116          }
3117 
3118          if( aggrconstant != 0.0 )
3119          {
3120             if( !SCIPisInfinity(scip, -consdata->lhs) )
3121                consdata->lhs -= coef * aggrconstant;
3122             if( !SCIPisInfinity(scip,  consdata->rhs) )
3123                consdata->rhs -= coef * aggrconstant;
3124          }
3125       }
3126    }
3127 
3128    i = 0;
3129    while( i < consdata->nquadvars )
3130    {
3131       var = consdata->quadvarterms[i].var;
3132 
3133       if( SCIPvarIsActive(var) && !SCIPisEQ(scip, SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var)) )
3134       {
3135          ++i;
3136          continue;
3137       }
3138 
3139       have_change = TRUE;
3140 
3141       coef   = 1.0;
3142       offset = 0.0;
3143 
3144       if( !SCIPisEQ(scip, SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var)) )
3145       {
3146          SCIP_CALL( SCIPgetProbvarSum(scip, &var, &coef, &offset) );
3147       }
3148       else
3149       {
3150          coef = 0.0;
3151          offset = (SCIPvarGetLbGlobal(var) + SCIPvarGetUbGlobal(var)) / 2.0;
3152       }
3153 
3154       SCIPdebugMsg(scip, "  quadratic variable <%s> with status %d is replaced by %g * <%s> + %g\n", SCIPvarGetName(consdata->quadvarterms[i].var),
3155          SCIPvarGetStatus(consdata->quadvarterms[i].var), coef, SCIPvarGetName(var), offset);
3156 
3157       /* handle fixed variable */
3158       if( coef == 0.0 )
3159       {
3160          /* if not fixed to 0.0, add to linear coefs of vars in bilinear terms, and deal with linear and square term as constant */
3161          if( offset != 0.0 )
3162          {
3163             for( j = 0; j < consdata->quadvarterms[i].nadjbilin; ++j )
3164             {
3165                bilinterm = &consdata->bilinterms[consdata->quadvarterms[i].adjbilin[j]];
3166 
3167                var2 = bilinterm->var1 == consdata->quadvarterms[i].var ? bilinterm->var2 : bilinterm->var1;
3168                assert(var2 != consdata->quadvarterms[i].var);
3169 
3170                var2pos = 0;
3171                while( consdata->quadvarterms[var2pos].var != var2 )
3172                {
3173                   ++var2pos;
3174                   assert(var2pos < consdata->nquadvars);
3175                }
3176                consdata->quadvarterms[var2pos].lincoef += bilinterm->coef * offset;
3177             }
3178 
3179             offset = consdata->quadvarterms[i].lincoef * offset + consdata->quadvarterms[i].sqrcoef * offset * offset;
3180             if( !SCIPisInfinity(scip, -consdata->lhs) )
3181                consdata->lhs -= offset;
3182             if( !SCIPisInfinity(scip,  consdata->rhs) )
3183                consdata->rhs -= offset;
3184          }
3185 
3186          /* remove bilinear terms */
3187          SCIP_CALL( removeBilinearTermsPos(scip, cons, consdata->quadvarterms[i].nadjbilin, consdata->quadvarterms[i].adjbilin) );
3188 
3189          /* delete quad. var term i */
3190          SCIP_CALL( delQuadVarTermPos(scip, cons, i) );
3191 
3192          continue;
3193       }
3194 
3195       assert(var != NULL);
3196 
3197       /* if GetProbvar gave an active variable, replace the quad var term so that it uses the new variable */
3198       if( SCIPvarIsActive(var) )
3199       {
3200          /* replace x by coef*y+offset */
3201          SCIP_CALL( replaceQuadVarTermPos(scip, cons, i, var, coef, offset) );
3202 
3203          continue;
3204       }
3205       else
3206       {
3207          /* if GetProbVar gave a multi-aggregated variable, add new quad var terms and new bilinear terms
3208           * x is replaced by coef * (sum_i a_ix_i + b) + offset
3209           * lcoef * x + scoef * x^2 + bcoef * x * y ->
3210           *   (b*coef + offset) * (lcoef + (b*coef + offset) * scoef)
3211           * + sum_i a_i*coef * (lcoef + 2 (b*coef + offset) * scoef) x_i
3212           * + sum_i (a_i*coef)^2 * scoef * x_i^2
3213           * + 2 sum_{i,j, i<j} (a_i a_j coef^2 scoef) x_i x_j
3214           * + bcoef * (b*coef + offset + coef * sum_i a_ix_i) y
3215           */
3216          int        naggrs;
3217          SCIP_VAR** aggrvars;     /* x_i */
3218          SCIP_Real* aggrscalars;  /* a_i */
3219          SCIP_Real  aggrconstant; /* b */
3220          int nquadtermsold;
3221 
3222          SCIP_Real lcoef;
3223          SCIP_Real scoef;
3224 
3225          assert(SCIPvarGetStatus(var) == SCIP_VARSTATUS_MULTAGGR);
3226 
3227          naggrs = SCIPvarGetMultaggrNVars(var);
3228          aggrvars = SCIPvarGetMultaggrVars(var);
3229          aggrscalars = SCIPvarGetMultaggrScalars(var);
3230          aggrconstant = SCIPvarGetMultaggrConstant(var);
3231 
3232          lcoef = consdata->quadvarterms[i].lincoef;
3233          scoef = consdata->quadvarterms[i].sqrcoef;
3234 
3235          nquadtermsold = consdata->nquadvars;
3236 
3237          SCIP_CALL( consdataEnsureQuadVarTermsSize(scip, consdata, consdata->nquadvars + naggrs) );
3238 
3239          /* take care of constant part */
3240          if( aggrconstant != 0.0 || offset != 0.0 )
3241          {
3242             SCIP_Real constant;
3243             constant = (aggrconstant * coef + offset) * (lcoef + (aggrconstant * coef + offset) * scoef);
3244             if( !SCIPisInfinity(scip, -consdata->lhs) )
3245                consdata->lhs -= constant;
3246             if( !SCIPisInfinity(scip,  consdata->rhs) )
3247                consdata->rhs -= constant;
3248          }
3249 
3250          /* add x_i's with linear and square coefficients */
3251          for( j = 0; j < naggrs; ++j )
3252          {
3253             SCIP_CALL( addQuadVarTerm(scip, cons, aggrvars[j],
3254                   coef * aggrscalars[j] * (lcoef + 2.0 * scoef * (coef * aggrconstant + offset)),
3255                   coef * coef * aggrscalars[j] * aggrscalars[j] * scoef) );
3256          }
3257 
3258          /* ensure space for bilinear terms */
3259          SCIP_CALL( consdataEnsureBilinSize(scip, consdata, consdata->nquadvars + (scoef != 0.0 ? (naggrs * (naggrs-1))/2 : 0) + consdata->quadvarterms[j].nadjbilin * naggrs) );
3260 
3261          /* add x_j*x_k's */
3262          if( scoef != 0.0 )
3263          {
3264             for( j = 0; j < naggrs; ++j )
3265                for( k = 0; k < j; ++k )
3266                {
3267                   assert(aggrvars[j] != aggrvars[k]);
3268                   SCIP_CALL( addBilinearTerm(scip, cons, nquadtermsold + j, nquadtermsold + k,
3269                         2.0 * aggrscalars[j] * aggrscalars[k] * coef * coef * scoef) );
3270                }
3271          }
3272 
3273          /* add x_i*y's */
3274          for( k = 0; k < consdata->quadvarterms[i].nadjbilin; ++k )
3275          {
3276             bilinterm = &consdata->bilinterms[consdata->quadvarterms[i].adjbilin[k]];
3277             bilincoef = bilinterm->coef;   /* copy coef, as bilinterm pointer may become invalid by realloc in addBilinearTerm() below */
3278             var2 = (bilinterm->var1 == consdata->quadvarterms[i].var) ? bilinterm->var2 : bilinterm->var1;
3279             assert(var2 != consdata->quadvarterms[i].var);
3280 
3281             /* this is not efficient, but we cannot sort the quadratic terms here, since we currently iterate over them */
3282             var2pos = 0;
3283             while( consdata->quadvarterms[var2pos].var != var2 )
3284             {
3285                ++var2pos;
3286                assert(var2pos < consdata->nquadvars);
3287             }
3288 
3289             for( j = 0; j < naggrs; ++j )
3290             {
3291                if( aggrvars[j] == var2 )
3292                { /* x_i == y, so we have a square term here */
3293                   consdata->quadvarterms[var2pos].sqrcoef += bilincoef * coef * aggrscalars[j];
3294                }
3295                else
3296                { /* x_i != y, so we need to add a bilinear term here */
3297                   SCIP_CALL( addBilinearTerm(scip, cons, nquadtermsold + j, var2pos, bilincoef * coef * aggrscalars[j]) );
3298                }
3299             }
3300 
3301             consdata->quadvarterms[var2pos].lincoef += bilincoef * (aggrconstant * coef + offset);
3302          }
3303 
3304          /* remove bilinear terms */
3305          SCIP_CALL( removeBilinearTermsPos(scip, cons, consdata->quadvarterms[i].nadjbilin, consdata->quadvarterms[i].adjbilin) );
3306 
3307          /* delete quad. var term i */
3308          SCIP_CALL( delQuadVarTermPos(scip, cons, i) );
3309       }
3310    }
3311 
3312    consdata->isremovedfixings = TRUE;
3313 
3314    SCIPdebugMsg(scip, "removed fixations from <%s>\n  -> ", SCIPconsGetName(cons));
3315    SCIPdebugPrintCons(scip, cons, NULL);
3316 
3317 #ifndef NDEBUG
3318    for( i = 0; i < consdata->nlinvars; ++i )
3319       assert(SCIPvarIsActive(consdata->linvars[i]));
3320 
3321    for( i = 0; i < consdata->nquadvars; ++i )
3322       assert(SCIPvarIsActive(consdata->quadvarterms[i].var));
3323 #endif
3324 
3325    if( !have_change )
3326       return SCIP_OKAY;
3327 
3328    /* some quadratic variable may have been replaced by an already existing linear variable
3329     * in this case, we want the linear variable to be removed, which happens in mergeAndCleanLinearVars
3330     */
3331    consdata->linvarsmerged = FALSE;
3332 
3333    SCIP_CALL( mergeAndCleanBilinearTerms(scip, cons) );
3334    SCIP_CALL( mergeAndCleanQuadVarTerms(scip, cons) );
3335    SCIP_CALL( mergeAndCleanLinearVars(scip, cons) );
3336 
3337 #ifndef NDEBUG
3338    for( i = 0; i < consdata->nbilinterms; ++i )
3339    {
3340       assert(consdata->bilinterms[i].var1 != consdata->bilinterms[i].var2);
3341       assert(consdata->bilinterms[i].coef != 0.0);
3342       assert(SCIPvarCompare(consdata->bilinterms[i].var1, consdata->bilinterms[i].var2) < 0);
3343    }
3344 #endif
3345 
3346    return SCIP_OKAY;
3347 }
3348 
3349 /** create a nonlinear row representation of the constraint and stores them in consdata */
3350 static
createNlRow(SCIP * scip,SCIP_CONS * cons)3351 SCIP_RETCODE createNlRow(
3352    SCIP*                 scip,               /**< SCIP data structure */
3353    SCIP_CONS*            cons                /**< quadratic constraint */
3354    )
3355 {
3356    SCIP_CONSDATA* consdata;
3357    int        nquadvars;     /* number of variables in quadratic terms */
3358    SCIP_VAR** quadvars;      /* variables in quadratic terms */
3359    int        nquadelems;    /* number of quadratic elements (square and bilinear terms) */
3360    SCIP_QUADELEM* quadelems; /* quadratic elements (square and bilinear terms) */
3361    int        nquadlinterms; /* number of linear terms using variables that are in quadratic terms */
3362    SCIP_VAR** quadlinvars;   /* variables of linear terms using variables that are in quadratic terms */
3363    SCIP_Real* quadlincoefs;  /* coefficients of linear terms using variables that are in quadratic terms */
3364    int i;
3365    int idx1;
3366    int idx2;
3367    int lincnt;
3368    int elcnt;
3369    SCIP_VAR* lastvar;
3370    int lastvaridx;
3371    SCIP_EXPRCURV curvature;
3372 
3373    assert(scip != NULL);
3374    assert(cons != NULL);
3375 
3376    consdata = SCIPconsGetData(cons);
3377    assert(consdata != NULL);
3378 
3379    if( consdata->nlrow != NULL )
3380    {
3381       SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
3382    }
3383 
3384    nquadvars = consdata->nquadvars;
3385    nquadelems = consdata->nbilinterms;
3386    nquadlinterms = 0;
3387    for( i = 0; i < nquadvars; ++i )
3388    {
3389       if( consdata->quadvarterms[i].sqrcoef != 0.0 )
3390          ++nquadelems;
3391       if( !SCIPisZero(scip, consdata->quadvarterms[i].lincoef) )
3392          ++nquadlinterms;
3393    }
3394 
3395    SCIP_CALL( SCIPallocBufferArray(scip, &quadvars,  nquadvars) );
3396    SCIP_CALL( SCIPallocBufferArray(scip, &quadelems, nquadelems) );
3397    SCIP_CALL( SCIPallocBufferArray(scip, &quadlinvars,  nquadlinterms) );
3398    SCIP_CALL( SCIPallocBufferArray(scip, &quadlincoefs, nquadlinterms) );
3399 
3400    lincnt = 0;
3401    elcnt = 0;
3402    for( i = 0; i < nquadvars; ++i )
3403    {
3404       quadvars[i] = consdata->quadvarterms[i].var;
3405 
3406       if( consdata->quadvarterms[i].sqrcoef != 0.0 )
3407       {
3408          assert(elcnt < nquadelems);
3409          quadelems[elcnt].idx1 = i;
3410          quadelems[elcnt].idx2 = i;
3411          quadelems[elcnt].coef = consdata->quadvarterms[i].sqrcoef;
3412          ++elcnt;
3413       }
3414 
3415       if( !SCIPisZero(scip, consdata->quadvarterms[i].lincoef) )
3416       {
3417          assert(lincnt < nquadlinterms);
3418          quadlinvars [lincnt] = consdata->quadvarterms[i].var;
3419          quadlincoefs[lincnt] = consdata->quadvarterms[i].lincoef;
3420          ++lincnt;
3421       }
3422    }
3423    assert(lincnt == nquadlinterms);
3424 
3425    /* bilinear terms are sorted first by first variable, then by second variable
3426     * thus, it makes sense to remember the index of the previous first variable for the case a series of bilinear terms with the same first var appears */
3427    lastvar = NULL;
3428    lastvaridx = -1;
3429    for( i = 0; i < consdata->nbilinterms; ++i )
3430    {
3431       if( lastvar == consdata->bilinterms[i].var1 )
3432       {
3433          assert(lastvaridx >= 0);
3434          assert(consdata->quadvarterms[lastvaridx].var == consdata->bilinterms[i].var1);
3435       }
3436       else
3437       {
3438          lastvar = consdata->bilinterms[i].var1;
3439          SCIP_CALL( consdataFindQuadVarTerm(scip, consdata, lastvar, &lastvaridx) );
3440       }
3441       idx1 = lastvaridx;
3442 
3443       SCIP_CALL( consdataFindQuadVarTerm(scip, consdata, consdata->bilinterms[i].var2, &idx2) );
3444 
3445       assert(elcnt < nquadelems);
3446       quadelems[elcnt].idx1 = MIN(idx1, idx2);
3447       quadelems[elcnt].idx2 = MAX(idx1, idx2);
3448       quadelems[elcnt].coef = consdata->bilinterms[i].coef;
3449       ++elcnt;
3450    }
3451    assert(elcnt == nquadelems);
3452 
3453    /* set curvature for the nonlinear row */
3454    if( consdata->isconcave && consdata->isconvex )
3455    {
3456       assert(consdata->nbilinterms == 0 && consdata->nquadvars == 0);
3457       curvature = SCIP_EXPRCURV_LINEAR;
3458    }
3459    else if( consdata->isconcave )
3460       curvature = SCIP_EXPRCURV_CONCAVE;
3461    else if( consdata->isconvex )
3462       curvature = SCIP_EXPRCURV_CONVEX;
3463    else
3464       curvature = SCIP_EXPRCURV_UNKNOWN;
3465 
3466    SCIP_CALL( SCIPcreateNlRow(scip, &consdata->nlrow, SCIPconsGetName(cons), 0.0,
3467          consdata->nlinvars, consdata->linvars, consdata->lincoefs,
3468          nquadvars, quadvars, nquadelems, quadelems,
3469          NULL, consdata->lhs, consdata->rhs,
3470          curvature) );
3471 
3472    SCIP_CALL( SCIPaddLinearCoefsToNlRow(scip, consdata->nlrow, nquadlinterms, quadlinvars, quadlincoefs) );
3473 
3474    SCIPfreeBufferArray(scip, &quadlincoefs);
3475    SCIPfreeBufferArray(scip, &quadlinvars);
3476    SCIPfreeBufferArray(scip, &quadelems);
3477    SCIPfreeBufferArray(scip, &quadvars);
3478 
3479    return SCIP_OKAY;
3480 }
3481 
3482 /** solve constraint as presolving */
3483 static
presolveSolve(SCIP * scip,SCIP_CONS * cons,SCIP_RESULT * result,SCIP_Bool * redundant,int * naggrvars)3484 SCIP_RETCODE presolveSolve(
3485    SCIP*                 scip,               /**< SCIP data structure */
3486    SCIP_CONS*            cons,               /**< constraint */
3487    SCIP_RESULT*          result,             /**< to store result of solve: cutoff, success, or do-not-find */
3488    SCIP_Bool*            redundant,          /**< to store whether constraint is redundant now (should be deleted) */
3489    int*                  naggrvars           /**< counter on number of variable aggregations */
3490    )
3491 {
3492    SCIP_CONSDATA* consdata;
3493 
3494    assert(scip != NULL);
3495    assert(cons != NULL);
3496    assert(result != NULL);
3497    assert(redundant != NULL);
3498 
3499    *result = SCIP_DIDNOTFIND;
3500    *redundant = FALSE;
3501 
3502    consdata = SCIPconsGetData(cons);
3503    assert(consdata != NULL);
3504 
3505    /* if constraint is an equality with two variables, at least one of them binary,
3506     * and linear after fixing the binary, then we can aggregate the variables */
3507    if( SCIPisEQ(scip, consdata->lhs, consdata->rhs) && consdata->nlinvars == 0 && consdata->nquadvars == 2 &&
3508       ((SCIPvarIsBinary(consdata->quadvarterms[0].var) && consdata->quadvarterms[1].sqrcoef == 0.0) ||
3509        (SCIPvarIsBinary(consdata->quadvarterms[1].var) && consdata->quadvarterms[0].sqrcoef == 0.0)) )
3510    {
3511       SCIP_Bool infeasible;
3512       SCIP_Bool aggregated;
3513       SCIP_Real a;
3514       SCIP_Real b;
3515       SCIP_Real c;
3516       SCIP_VAR* x;
3517       SCIP_VAR* y;
3518       int binvaridx;
3519 
3520       /* constraint is a*(x+x^2) + b*y + c*x*y = rhs, with x binary variable
3521        * x = 0 -> b*y == rhs
3522        * x = 1 -> (b+c)*y == rhs - a
3523        *
3524        * if b != 0 and b+c != 0, then y = (rhs-a)/(b+c) * x + rhs/b * (1-x) = ((rhs-a)/(b+c) - rhs/b) * x + rhs/b
3525        */
3526 
3527       binvaridx = (SCIPvarIsBinary(consdata->quadvarterms[0].var) && consdata->quadvarterms[1].sqrcoef == 0.0) ? 0 : 1;
3528 
3529       x = consdata->quadvarterms[binvaridx].var;
3530       a = consdata->quadvarterms[binvaridx].sqrcoef + consdata->quadvarterms[binvaridx].lincoef;
3531 
3532       y = consdata->quadvarterms[1-binvaridx].var;
3533       b = consdata->quadvarterms[1-binvaridx].lincoef;
3534 
3535       assert(consdata->nbilinterms <= 1);  /* should actually be 1, since constraint is otherwise linear */
3536       c = (consdata->nbilinterms == 1) ? consdata->bilinterms[0].coef : 0.0;
3537 
3538       if( !SCIPisZero(scip, b) && !SCIPisZero(scip, b+c) )
3539       {
3540          SCIPdebugMsg(scip, "<%s> = 0 -> %g*<%s> = %g  and  <%s> = 1 -> %g*<%s> = %g\n", SCIPvarGetName(x), b, SCIPvarGetName(y), consdata->rhs,
3541             SCIPvarGetName(x), b+c, SCIPvarGetName(y), consdata->rhs - a);
3542          SCIPdebugMsg(scip, "=> attempt aggregation <%s> = %g*<%s> + %g\n", SCIPvarGetName(y), (consdata->rhs-a)/(b+c) - consdata->rhs/b,
3543             SCIPvarGetName(x), consdata->rhs/b);
3544 
3545          SCIP_CALL( SCIPaggregateVars(scip, x, y, (consdata->rhs-a)/(b+c) - consdata->rhs/b, -1.0, -consdata->rhs/b, &infeasible, redundant, &aggregated) );
3546          if( infeasible )
3547             *result = SCIP_CUTOFF;
3548          else if( *redundant || aggregated )
3549          {
3550             /* aggregated (or were already aggregated), so constraint is now redundant */
3551             *result = SCIP_SUCCESS;
3552             *redundant = TRUE;
3553 
3554             if( aggregated )
3555                ++*naggrvars;
3556          }
3557       }
3558 
3559       /* @todo if b is 0 or b+c is 0, or lhs != rhs, then could replace by varbound constraint */
3560    }
3561 
3562    return SCIP_OKAY;
3563 }
3564 
3565 
3566 /** reformulates products of binary variables as AND constraint
3567  *
3568  *  For a product x*y, with x and y binary variables, the product is replaced by a new auxiliary variable z and the constraint z = {x and y} is added.
3569  */
3570 static
presolveTryAddAND(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,int * naddconss)3571 SCIP_RETCODE presolveTryAddAND(
3572    SCIP*                 scip,               /**< SCIP data structure */
3573    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
3574    SCIP_CONS*            cons,               /**< constraint */
3575    int*                  naddconss           /**< buffer where to add the number of AND constraints added */
3576    )
3577 {
3578    SCIP_CONSHDLRDATA* conshdlrdata;
3579    SCIP_CONSDATA*     consdata;
3580    char               name[SCIP_MAXSTRLEN];
3581    SCIP_VAR*          vars[2];
3582    SCIP_VAR*          auxvar;
3583    SCIP_CONS*         andcons;
3584    int                i;
3585    int                ntodelete;
3586    int*               todelete;
3587 
3588    assert(scip != NULL);
3589    assert(conshdlr != NULL);
3590    assert(cons != NULL);
3591    assert(naddconss != NULL);
3592 
3593    conshdlrdata = SCIPconshdlrGetData(conshdlr);
3594    assert(conshdlrdata != NULL);
3595 
3596    /* if no binary variables, then we will find nothing to reformulate here
3597     * (note that this does not count in integer variables with {0,1} bounds...)
3598     */
3599    if( SCIPgetNBinVars(scip) == 0 )
3600       return SCIP_OKAY;
3601 
3602    /* if user does not like AND very much, then return */
3603    if( conshdlrdata->empathy4and < 2 )
3604       return SCIP_OKAY;
3605 
3606    consdata = SCIPconsGetData(cons);
3607    assert(consdata != NULL);
3608 
3609    if( consdata->nbilinterms == 0 )
3610       return SCIP_OKAY;
3611 
3612    /* get array to store indices of bilinear terms that shall be deleted */
3613    SCIP_CALL( SCIPallocBufferArray(scip, &todelete, consdata->nbilinterms) );
3614    ntodelete = 0;
3615 
3616    for( i = 0; i < consdata->nbilinterms; ++i )
3617    {
3618       vars[0] = consdata->bilinterms[i].var1;
3619       if( !SCIPvarIsBinary(vars[0]) )
3620          continue;
3621 
3622       vars[1] = consdata->bilinterms[i].var2;
3623       if( !SCIPvarIsBinary(vars[1]) )
3624          continue;
3625 
3626       /* create auxiliary variable */
3627       (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "prod%s_%s_%s", SCIPvarGetName(vars[0]), SCIPvarGetName(vars[1]), SCIPconsGetName(cons));
3628       SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, 0.0, 1.0, 0.0, SCIP_VARTYPE_BINARY,
3629             SCIPvarIsInitial(vars[0]) || SCIPvarIsInitial(vars[1]), SCIPvarIsRemovable(vars[0]) && SCIPvarIsRemovable(vars[1]), NULL, NULL, NULL, NULL, NULL) );
3630       SCIP_CALL( SCIPaddVar(scip, auxvar) );
3631 #ifdef WITH_DEBUG_SOLUTION
3632       if( SCIPdebugIsMainscip(scip) )
3633       {
3634          SCIP_Real var0val;
3635          SCIP_Real var1val;
3636          SCIP_CALL( SCIPdebugGetSolVal(scip, vars[0], &var0val) );
3637          SCIP_CALL( SCIPdebugGetSolVal(scip, vars[1], &var1val) );
3638          SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, var0val * var1val) );
3639       }
3640 #endif
3641 
3642       /* create AND-constraint auxvar = x and y, need to be enforced as not redundant */
3643       (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "%sAND%s", SCIPvarGetName(vars[0]), SCIPvarGetName(vars[1]));
3644       SCIP_CALL( SCIPcreateConsAnd(scip, &andcons, name, auxvar, 2, vars,
3645             SCIPconsIsInitial(cons) && conshdlrdata->binreforminitial,
3646             SCIPconsIsSeparated(cons), TRUE, TRUE,
3647             SCIPconsIsPropagated(cons),  SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons),
3648             SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
3649       SCIP_CALL( SCIPaddCons(scip, andcons) );
3650       SCIPdebugMsg(scip, "added AND constraint: ");
3651       SCIPdebugPrintCons(scip, andcons, NULL);
3652       SCIP_CALL( SCIPreleaseCons(scip, &andcons) );
3653       ++*naddconss;
3654 
3655       /* add bilincoef * auxvar to linear terms */
3656       SCIP_CALL( addLinearCoef(scip, cons, auxvar, consdata->bilinterms[i].coef) );
3657       SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
3658 
3659       /* remember that we have to delete this bilinear term */
3660       assert(ntodelete < consdata->nbilinterms);
3661       todelete[ntodelete++] = i;
3662    }
3663 
3664    /* remove bilinear terms that have been replaced */
3665    SCIP_CALL( removeBilinearTermsPos(scip, cons, ntodelete, todelete) );
3666    SCIPfreeBufferArray(scip, &todelete);
3667 
3668    return SCIP_OKAY;
3669 }
3670 
3671 /** gets bounds of variable y if x takes a certain value; checks whether x = xval has implications on y */
3672 static
getImpliedBounds(SCIP * scip,SCIP_VAR * x,SCIP_Bool xval,SCIP_VAR * y,SCIP_INTERVAL * resultant)3673 SCIP_RETCODE getImpliedBounds(
3674    SCIP*                 scip,               /**< SCIP data structure */
3675    SCIP_VAR*             x,                  /**< variable which implications to check */
3676    SCIP_Bool             xval,               /**< value of x to check for (TRUE for 1, FALSE for 0) */
3677    SCIP_VAR*             y,                  /**< variable to check if bounds can be reduced */
3678    SCIP_INTERVAL*        resultant           /**< buffer to store bounds on y */
3679    )
3680 {
3681    SCIP_VAR** implvars;
3682    SCIP_BOUNDTYPE* impltypes;
3683    SCIP_Real* implbounds;
3684    int nimpls;
3685    int pos;
3686 
3687    assert(scip != NULL);
3688    assert(x != NULL);
3689    assert(y != NULL);
3690    assert(resultant != NULL);
3691 
3692    SCIPintervalSetBounds(resultant, MIN(SCIPvarGetLbGlobal(y), SCIPvarGetUbGlobal(y)), MAX(SCIPvarGetLbGlobal(y), SCIPvarGetUbGlobal(y)));  /*lint !e666 */
3693 
3694    if( !SCIPvarIsBinary(x) || !SCIPvarIsActive(x) )
3695       return SCIP_OKAY;
3696 
3697    /* check in cliques for binary to binary implications */
3698    if( SCIPvarIsBinary(y) )
3699    {
3700       resultant->inf = MAX(resultant->inf, MIN(resultant->sup, 0.0));
3701       resultant->sup = MIN(resultant->sup, MAX(resultant->inf, 1.0));
3702 
3703       if( SCIPhaveVarsCommonClique(scip, x, xval, y, TRUE, FALSE) )
3704       {
3705          resultant->sup = MIN(resultant->sup, MAX(resultant->inf, 0.0));
3706       }
3707       else if( SCIPhaveVarsCommonClique(scip, x, xval, y, FALSE, FALSE) )
3708       {
3709          resultant->inf = MAX(resultant->inf, MIN(resultant->sup, 1.0));
3710       }
3711 
3712       return SCIP_OKAY;
3713    }
3714 
3715    /* analyze implications for x = xval */
3716    nimpls = SCIPvarGetNImpls(x, xval);
3717    if( nimpls == 0 )
3718       return SCIP_OKAY;
3719 
3720    implvars   = SCIPvarGetImplVars  (x, xval);
3721    impltypes  = SCIPvarGetImplTypes (x, xval);
3722    implbounds = SCIPvarGetImplBounds(x, xval);
3723 
3724    assert(implvars != NULL);
3725    assert(impltypes != NULL);
3726    assert(implbounds != NULL);
3727 
3728    /* find implications */
3729    if( !SCIPsortedvecFindPtr((void**)implvars, SCIPvarComp, (void*)y, nimpls, &pos) )
3730       return SCIP_OKAY;
3731 
3732    /* if there are several implications on y, go to the first one */
3733    while( pos > 0 && implvars[pos-1] == y )
3734       --pos;
3735 
3736    /* update implied lower and upper bounds on y
3737     * but make sure that resultant will not be empty, due to tolerances
3738     */
3739    while( pos < nimpls && implvars[pos] == y )
3740    {
3741       if( impltypes[pos] == SCIP_BOUNDTYPE_LOWER )
3742          resultant->inf = MAX(resultant->inf, MIN(resultant->sup, implbounds[pos]));
3743       else
3744          resultant->sup = MIN(resultant->sup, MAX(resultant->inf, implbounds[pos]));
3745       ++pos;
3746    }
3747 
3748    assert(resultant->sup >= resultant->inf);
3749 
3750    return SCIP_OKAY;
3751 }
3752 
3753 /** Reformulates products of binary times bounded continuous variables as system of linear inequalities (plus auxiliary variable).
3754  *
3755  *  For a product x*y, with y a binary variable and x a continuous variable with finite bounds,
3756  *  an auxiliary variable z and the inequalities \f$ x^L y \leq z \leq x^U y \f$ and \f$ x - (1-y) x^U \leq z \leq x - (1-y) x^L \f$ are added.
3757  *
3758  *  If x is a linear term consisting of more than one variable, it is split up in groups of linear terms of length at most maxnrvar.
3759  *  For each product of linear term of length at most maxnrvar with y, an auxiliary z and linear inequalities are added.
3760  *
3761  *  If y is a binary variable, the AND constraint \f$ z = x \wedge y \f$ may be added instead of linear constraints.
3762  */
3763 static
presolveTryAddLinearReform(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,int * naddconss)3764 SCIP_RETCODE presolveTryAddLinearReform(
3765    SCIP*                 scip,               /**< SCIP data structure */
3766    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
3767    SCIP_CONS*            cons,               /**< constraint */
3768    int*                  naddconss           /**< buffer where to add the number of auxiliary constraints added */
3769    )
3770 {  /*lint --e{666} */
3771    SCIP_CONSHDLRDATA* conshdlrdata;
3772    SCIP_CONSDATA*     consdata;
3773    SCIP_VAR**         xvars;
3774    SCIP_Real*         xcoef;
3775    SCIP_INTERVAL      xbndszero;
3776    SCIP_INTERVAL      xbndsone;
3777    SCIP_INTERVAL      act0;
3778    SCIP_INTERVAL      act1;
3779    int                nxvars;
3780    SCIP_VAR*          y;
3781    SCIP_VAR*          bvar;
3782    char               name[SCIP_MAXSTRLEN];
3783    int                nbilinterms;
3784    SCIP_VAR*          auxvar;
3785    SCIP_CONS*         auxcons;
3786    int                i;
3787    int                j;
3788    int                k;
3789    int                bilinidx;
3790    SCIP_Real          bilincoef;
3791    SCIP_Real          mincoef;
3792    SCIP_Real          maxcoef;
3793    int*               todelete;
3794    int                ntodelete;
3795    int                maxnrvar;
3796    SCIP_Bool          integral;
3797    SCIP_Longint       gcd;
3798    SCIP_Bool          auxvarinitial;
3799    SCIP_Bool          auxvarremovable;
3800 
3801    assert(scip != NULL);
3802    assert(conshdlr != NULL);
3803    assert(cons != NULL);
3804    assert(naddconss != NULL);
3805 
3806    /* if no binary variables, then we will find nothing to reformulate here
3807     * (note that this does not count in integer variables with {0,1} bounds...)
3808     */
3809    if( SCIPgetNBinVars(scip) == 0 )
3810       return SCIP_OKAY;
3811 
3812    conshdlrdata = SCIPconshdlrGetData(conshdlr);
3813    assert(conshdlrdata != NULL);
3814 
3815    maxnrvar = conshdlrdata->replacebinaryprodlength;
3816    if( maxnrvar == 0 )
3817       return SCIP_OKAY;
3818 
3819    consdata = SCIPconsGetData(cons);
3820    assert(consdata != NULL);
3821 
3822    xvars = NULL;
3823    xcoef = NULL;
3824    todelete = NULL;
3825    gcd = 0;
3826 
3827    for( i = 0; i < consdata->nquadvars; ++i )
3828    {
3829       y = consdata->quadvarterms[i].var;
3830       if( !SCIPvarIsBinary(y) )
3831          continue;
3832 
3833       nbilinterms = consdata->quadvarterms[i].nadjbilin;
3834       if( nbilinterms == 0 )
3835          continue;
3836 
3837       SCIP_CALL( SCIPreallocBufferArray(scip, &xvars, MIN(maxnrvar, nbilinterms)+2) ); /* add 2 for later use when creating linear constraints */
3838       SCIP_CALL( SCIPreallocBufferArray(scip, &xcoef, MIN(maxnrvar, nbilinterms)+2) );
3839 
3840       /* alloc array to store indices of bilinear terms that shall be deleted */
3841       SCIP_CALL( SCIPreallocBufferArray(scip, &todelete, nbilinterms) );
3842       ntodelete = 0;
3843 
3844       auxvarinitial = SCIPvarIsInitial(y);
3845       auxvarremovable = SCIPvarIsRemovable(y);
3846 
3847       /* setup a list of bounded variables x_i with coefficients a_i that are multiplied with binary y: y*(sum_i a_i*x_i)
3848        * and compute range of sum_i a_i*x_i for the cases y = 0 and y = 1
3849        * we may need several rounds if maxnrvar < nbilinterms
3850        */
3851       j = 0;
3852       do
3853       {
3854          nxvars = 0;
3855          SCIPintervalSet(&xbndszero, 0.0);
3856          SCIPintervalSet(&xbndsone, 0.0);
3857 
3858          mincoef = SCIPinfinity(scip);
3859          maxcoef = 0.0;
3860          integral = TRUE;
3861 
3862          /* collect at most maxnrvar variables for x term */
3863          for( ; j < nbilinterms && nxvars < maxnrvar; ++j )
3864          {
3865             bilinidx = consdata->quadvarterms[i].adjbilin[j];
3866             assert(bilinidx >= 0);
3867             assert(bilinidx < consdata->nbilinterms);
3868 
3869             bvar = consdata->bilinterms[bilinidx].var1;
3870             if( bvar == y )
3871                bvar = consdata->bilinterms[bilinidx].var2;
3872             assert(bvar != y);
3873 
3874             /* skip products with unbounded variables */
3875             if( SCIPisInfinity(scip, -SCIPvarGetLbGlobal(bvar)) || SCIPisInfinity(scip, SCIPvarGetUbGlobal(bvar)) )
3876             {
3877                SCIPdebugMsg(scip, "skip reform of <%s><%s> due to unbounded second variable [%g,%g]\n",
3878                   SCIPvarGetName(y), SCIPvarGetName(bvar), SCIPvarGetLbGlobal(bvar), SCIPvarGetUbGlobal(bvar));
3879                continue;
3880             }
3881 
3882             /* skip products with non-binary variables if binreformbinaryonly is set */
3883             if( conshdlrdata->binreformbinaryonly && !SCIPvarIsBinary(bvar) )
3884             {
3885                SCIPdebugMsg(scip, "skip reform of <%s><%s> because second variable is not binary\n",
3886                   SCIPvarGetName(y), SCIPvarGetName(bvar));
3887                continue;
3888             }
3889 
3890             bilincoef = consdata->bilinterms[bilinidx].coef;
3891             assert(bilincoef != 0.0);
3892 
3893             /* get activity of bilincoef * x if y = 0 */
3894             SCIP_CALL( getImpliedBounds(scip, y, FALSE, bvar, &act0) );
3895             SCIPintervalMulScalar(SCIPinfinity(scip), &act0, act0, bilincoef);
3896 
3897             /* get activity of bilincoef * x if y = 1 */
3898             SCIP_CALL( getImpliedBounds(scip, y,  TRUE, bvar, &act1) );
3899             SCIPintervalMulScalar(SCIPinfinity(scip), &act1, act1, bilincoef);
3900 
3901             /* skip products that give rise to very large coefficients (big big-M's) */
3902             if( SCIPfeastol(scip) * REALABS(act0.inf) >= conshdlrdata->binreformmaxcoef || SCIPfeastol(scip) * REALABS(act0.sup) >= conshdlrdata->binreformmaxcoef )
3903             {
3904                SCIPdebugMsg(scip, "skip reform of %g<%s><%s> due to huge activity [%g,%g] for <%s> = 0.0\n",
3905                   bilincoef, SCIPvarGetName(y), SCIPvarGetName(bvar), SCIPintervalGetInf(act0), SCIPintervalGetSup(act0), SCIPvarGetName(y));
3906                continue;
3907             }
3908             if( SCIPfeastol(scip) * REALABS(act1.inf) >= conshdlrdata->binreformmaxcoef || SCIPfeastol(scip) * REALABS(act1.sup) >= conshdlrdata->binreformmaxcoef )
3909             {
3910                SCIPdebugMsg(scip, "skip reform of %g<%s><%s> due to huge activity [%g,%g] for <%s> = 1.0\n",
3911                   bilincoef, SCIPvarGetName(y), SCIPvarGetName(bvar), SCIPintervalGetInf(act1), SCIPintervalGetSup(act1), SCIPvarGetName(y));
3912                continue;
3913             }
3914             if( !SCIPisZero(scip, MIN(REALABS(act0.inf), REALABS(act0.sup))) &&
3915                SCIPfeastol(scip) * MAX(REALABS(act0.inf), REALABS(act0.sup)) / MIN(REALABS(act0.inf), REALABS(act0.sup)) >= conshdlrdata->binreformmaxcoef )
3916             {
3917                SCIPdebugMsg(scip, "skip reform of %g<%s><%s> due to huge activity ratio %g for <%s> = 0.0\n", bilincoef, SCIPvarGetName(y), SCIPvarGetName(bvar),
3918                   MAX(REALABS(act0.inf), REALABS(act0.sup)) / MIN(REALABS(act0.inf), REALABS(act0.sup)), SCIPvarGetName(y));
3919                continue;
3920             }
3921             if( !SCIPisZero(scip, MIN(REALABS(act1.inf), REALABS(act1.sup))) &&
3922                SCIPfeastol(scip) * MAX(REALABS(act1.inf), REALABS(act1.sup)) / MIN(REALABS(act1.inf), REALABS(act1.sup)) >= conshdlrdata->binreformmaxcoef )
3923             {
3924                SCIPdebugMsg(scip, "skip reform of %g<%s><%s> due to huge activity ratio %g for <%s> = 0.0\n", bilincoef, SCIPvarGetName(y), SCIPvarGetName(bvar),
3925                   MAX(REALABS(act1.inf), REALABS(act1.sup)) / MIN(REALABS(act1.inf), REALABS(act1.sup)), SCIPvarGetName(y));
3926                continue;
3927             }
3928 
3929             /* add bvar to x term */
3930             xvars[nxvars] = bvar;
3931             xcoef[nxvars] = bilincoef;
3932             ++nxvars;
3933 
3934             /* update bounds on x term */
3935             SCIPintervalAdd(SCIPinfinity(scip), &xbndszero, xbndszero, act0);
3936             SCIPintervalAdd(SCIPinfinity(scip), &xbndsone,  xbndsone,  act1);
3937 
3938             if( REALABS(bilincoef) < mincoef )
3939                mincoef = ABS(bilincoef);
3940             if( REALABS(bilincoef) > maxcoef )
3941                maxcoef = ABS(bilincoef);
3942 
3943             /* update whether all coefficients will be integral and if so, compute their gcd */
3944             integral &= (SCIPvarGetType(bvar) < SCIP_VARTYPE_CONTINUOUS) && SCIPisIntegral(scip, bilincoef);  /*lint !e514 */
3945             if( integral )
3946             {
3947                if( nxvars == 1 )
3948                   gcd = (SCIP_Longint)SCIPround(scip, REALABS(bilincoef));
3949                else
3950                   gcd = SCIPcalcGreComDiv(gcd, (SCIP_Longint)SCIPround(scip, REALABS(bilincoef)));
3951             }
3952 
3953             /* if bvar is initial, then also the auxiliary variable should be initial
3954              * if bvar is not removable, then also the auxiliary variable should not be removable
3955              */
3956             auxvarinitial |= SCIPvarIsInitial(bvar);
3957             auxvarremovable &= SCIPvarIsRemovable(bvar);
3958 
3959             /* remember that we have to remove this bilinear term later */
3960             assert(ntodelete < nbilinterms);
3961             todelete[ntodelete++] = bilinidx;
3962          }
3963 
3964          if( nxvars == 0 ) /* all (remaining) x_j seem to be unbounded */
3965             break;
3966 
3967          assert(!SCIPisInfinity(scip, -SCIPintervalGetInf(xbndszero)));
3968          assert(!SCIPisInfinity(scip,  SCIPintervalGetSup(xbndszero)));
3969          assert(!SCIPisInfinity(scip, -SCIPintervalGetInf(xbndsone)));
3970          assert(!SCIPisInfinity(scip,  SCIPintervalGetSup(xbndsone)));
3971 
3972 #ifdef SCIP_DEBUG
3973          if( SCIPintervalGetInf(xbndszero) != SCIPintervalGetInf(xbndsone) || /*lint !e777*/
3974             +SCIPintervalGetSup(xbndszero) != SCIPintervalGetSup(xbndsone) ) /*lint !e777*/
3975          {
3976             SCIPdebugMsg(scip, "got different bounds for y = 0: [%g, %g] and y = 1: [%g, %g]\n", xbndszero.inf, xbndszero.sup, xbndsone.inf, xbndsone.sup);
3977          }
3978 #endif
3979 
3980          if( nxvars == 1 && conshdlrdata->empathy4and >= 1 && SCIPvarIsBinary(xvars[0]) )
3981          {
3982             /* product of two binary variables, replace by auxvar and AND constraint */
3983             /* add auxiliary variable z */
3984             (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "prod%s_%s_%s", SCIPvarGetName(y), SCIPvarGetName(xvars[0]), SCIPconsGetName(cons));
3985             SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, 0.0, 1.0, 0.0, SCIP_VARTYPE_IMPLINT,
3986                   auxvarinitial, auxvarremovable, NULL, NULL, NULL, NULL, NULL) );
3987             SCIP_CALL( SCIPaddVar(scip, auxvar) );
3988 
3989 #ifdef WITH_DEBUG_SOLUTION
3990             if( SCIPdebugIsMainscip(scip) )
3991             {
3992                SCIP_Real var0val;
3993                SCIP_Real var1val;
3994                SCIP_CALL( SCIPdebugGetSolVal(scip, xvars[0], &var0val) );
3995                SCIP_CALL( SCIPdebugGetSolVal(scip, y, &var1val) );
3996                SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, var0val * var1val) );
3997             }
3998 #endif
3999 
4000             /* add constraint z = x and y; need to be enforced, as it is not redundant w.r.t. existing constraints */
4001             xvars[1] = y;
4002             (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "%sAND%s_%s", SCIPvarGetName(y), SCIPvarGetName(xvars[0]), SCIPconsGetName(cons));
4003             SCIP_CALL( SCIPcreateConsAnd(scip, &auxcons, name, auxvar, 2, xvars,
4004                   SCIPconsIsInitial(cons) && conshdlrdata->binreforminitial,
4005                   SCIPconsIsSeparated(cons), TRUE, TRUE,
4006                   SCIPconsIsPropagated(cons),  SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons),
4007                   SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
4008             SCIP_CALL( SCIPaddCons(scip, auxcons) );
4009             SCIPdebugMsg(scip, "added AND constraint: ");
4010             SCIPdebugPrintCons(scip, auxcons, NULL);
4011             SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
4012             ++*naddconss;
4013 
4014             /* add linear term coef*auxvar */
4015             SCIP_CALL( addLinearCoef(scip, cons, auxvar, xcoef[0]) );
4016 
4017             /* forget about auxvar */
4018             SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
4019          }
4020          else
4021          {
4022             /* product of binary variable with more than one binary or with continuous variables or with binary and user
4023              * did not like AND -> replace by auxvar and linear constraints */
4024             SCIP_Real scale;
4025 
4026             /* scale auxiliary constraint by some nice value,
4027              * if all coefficients are integral, take a value that preserves integrality (-> gcd), so we can make the auxiliary variable impl. integer
4028              */
4029             if( integral )
4030             {
4031                scale = (SCIP_Real)gcd;
4032                assert(scale >= 1.0);
4033             }
4034             else if( nxvars == 1 )
4035             {
4036                /* scaling by the only coefficient gives auxiliary variable = x * y, which thus will be implicit integral provided y is not continuous */
4037                assert(mincoef == maxcoef);  /*lint !e777 */
4038                scale = mincoef;
4039                integral = SCIPvarGetType(xvars[0]) < SCIP_VARTYPE_CONTINUOUS;
4040             }
4041             else
4042             {
4043                scale = 1.0;
4044                if( maxcoef < 0.5 )
4045                   scale = maxcoef;
4046                if( mincoef > 2.0 )
4047                   scale = mincoef;
4048                if( scale != 1.0 )
4049                   scale = SCIPselectSimpleValue(scale / 2.0, 1.5 * scale, MAXDNOM);
4050             }
4051             assert(scale > 0.0);
4052             assert(!SCIPisInfinity(scip, scale));
4053 
4054             /* if x-term is always negative for y = 1, negate scale so we get a positive auxiliary variable; maybe this is better sometimes? */
4055             if( !SCIPisPositive(scip, SCIPintervalGetSup(xbndsone)) )
4056                scale = -scale;
4057 
4058             SCIPdebugMsg(scip, "binary reformulation using scale %g, nxvars = %d, integral = %u\n", scale, nxvars, integral);
4059             if( scale != 1.0 )
4060             {
4061                SCIPintervalDivScalar(SCIPinfinity(scip), &xbndszero, xbndszero, scale);
4062                SCIPintervalDivScalar(SCIPinfinity(scip), &xbndsone,  xbndsone, scale);
4063                for( k = 0; k < nxvars; ++k )
4064                   xcoef[k] /= scale;
4065             }
4066 
4067             /* add auxiliary variable z */
4068             if( nxvars == 1 )
4069                (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "prod%s_%s_%s", SCIPvarGetName(y), SCIPvarGetName(xvars[0]), SCIPconsGetName(cons));
4070             else
4071                (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "prod%s_%s_more_%s", SCIPvarGetName(y), SCIPvarGetName(xvars[0]), SCIPconsGetName(cons));
4072             SCIP_CALL( SCIPcreateVar(scip, &auxvar, name, MIN(0., SCIPintervalGetInf(xbndsone)), MAX(0., SCIPintervalGetSup(xbndsone)),
4073                   0.0, integral ? SCIP_VARTYPE_IMPLINT : SCIP_VARTYPE_CONTINUOUS,
4074                   auxvarinitial, auxvarremovable, NULL, NULL, NULL, NULL, NULL) );
4075             SCIP_CALL( SCIPaddVar(scip, auxvar) );
4076 
4077             /* compute value of auxvar in debug solution */
4078 #ifdef WITH_DEBUG_SOLUTION
4079             if( SCIPdebugIsMainscip(scip) )
4080             {
4081                SCIP_Real debugval;
4082                SCIP_Real varval;
4083 
4084                SCIP_CALL( SCIPdebugGetSolVal(scip, y, &varval) );
4085                if( SCIPisZero(scip, varval) )
4086                {
4087                   SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, 0.0) );
4088                }
4089                else
4090                {
4091                   assert(SCIPisEQ(scip, varval, 1.0));
4092 
4093                   debugval = 0.0;
4094                   for( k = 0; k < nxvars; ++k )
4095                   {
4096                      SCIP_CALL( SCIPdebugGetSolVal(scip, xvars[k], &varval) );
4097                      debugval += xcoef[k] * varval;
4098                   }
4099                   SCIP_CALL( SCIPdebugAddSolVal(scip, auxvar, debugval) );
4100                }
4101             }
4102 #endif
4103 
4104             /* add auxiliary constraints
4105              * it seems to be advantageous to make the varbound constraints initial and the linear constraints not initial
4106              * maybe because it is more likely that a binary variable takes value 0 instead of 1, and thus the varbound constraints
4107              * are more often active, compared to the linear constraints added below
4108              * also, the varbound constraints are more sparse than the linear cons
4109              */
4110             if( SCIPisNegative(scip, SCIPintervalGetInf(xbndsone)) )
4111             {
4112                /* add 0 <= z - xbndsone.inf * y constraint (as varbound constraint), need to be enforced as not redundant */
4113                if( nxvars == 1 )
4114                   (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "linreform%s*%s_%s_1", SCIPvarGetName(y), SCIPvarGetName(xvars[0]), SCIPconsGetName(cons));
4115                else
4116                   (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "linreform%s*%s*more_%s_1", SCIPvarGetName(y), SCIPvarGetName(xvars[0]), SCIPconsGetName(cons));
4117                SCIP_CALL( SCIPcreateConsVarbound(scip, &auxcons, name, auxvar, y, -SCIPintervalGetInf(xbndsone), 0.0, SCIPinfinity(scip),
4118                      SCIPconsIsInitial(cons) /*&& conshdlrdata->binreforminitial*/,
4119                      SCIPconsIsSeparated(cons), TRUE, TRUE,
4120                      SCIPconsIsPropagated(cons),  SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons),
4121                      SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
4122                SCIP_CALL( SCIPaddCons(scip, auxcons) );
4123                SCIPdebugMsg(scip, "added varbound constraint: ");
4124                SCIPdebugPrintCons(scip, auxcons, NULL);
4125                SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
4126                ++*naddconss;
4127             }
4128             if( SCIPisPositive(scip, SCIPintervalGetSup(xbndsone)) )
4129             {
4130                /* add z - xbndsone.sup * y <= 0 constraint (as varbound constraint), need to be enforced as not redundant */
4131                if( nxvars == 1 )
4132                   (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "linreform%s*%s_%s_2", SCIPvarGetName(y), SCIPvarGetName(xvars[0]), SCIPconsGetName(cons));
4133                else
4134                   (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "linreform%s*%s*more_%s_2", SCIPvarGetName(y), SCIPvarGetName(xvars[0]), SCIPconsGetName(cons));
4135                SCIP_CALL( SCIPcreateConsVarbound(scip, &auxcons, name, auxvar, y, -SCIPintervalGetSup(xbndsone), -SCIPinfinity(scip), 0.0,
4136                      SCIPconsIsInitial(cons) /*&& conshdlrdata->binreforminitial*/,
4137                      SCIPconsIsSeparated(cons), TRUE, TRUE,
4138                      SCIPconsIsPropagated(cons),  SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons),
4139                      SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
4140                SCIP_CALL( SCIPaddCons(scip, auxcons) );
4141                SCIPdebugMsg(scip, "added varbound constraint: ");
4142                SCIPdebugPrintCons(scip, auxcons, NULL);
4143                SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
4144                ++*naddconss;
4145             }
4146 
4147             /* add xbndszero.inf <= sum_i a_i*x_i + xbndszero.inf * y - z constraint, need to be enforced as not redundant */
4148             xvars[nxvars]   = y;
4149             xvars[nxvars+1] = auxvar;
4150             xcoef[nxvars]   = SCIPintervalGetInf(xbndszero);
4151             xcoef[nxvars+1] = -1;
4152 
4153             if( nxvars == 1 )
4154                (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "linreform%s*%s_%s_3", SCIPvarGetName(y), SCIPvarGetName(xvars[0]), SCIPconsGetName(cons));
4155             else
4156                (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "linreform%s*%s*more_%s_3", SCIPvarGetName(y), SCIPvarGetName(xvars[0]), SCIPconsGetName(cons));
4157             SCIP_CALL( SCIPcreateConsLinear(scip, &auxcons, name, nxvars+2, xvars, xcoef, SCIPintervalGetInf(xbndszero), SCIPinfinity(scip),
4158                   SCIPconsIsInitial(cons) && conshdlrdata->binreforminitial,
4159                   SCIPconsIsSeparated(cons), TRUE, TRUE,
4160                   SCIPconsIsPropagated(cons),  SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons),
4161                   SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
4162             SCIP_CALL( SCIPaddCons(scip, auxcons) );
4163             SCIPdebugMsg(scip, "added linear constraint: ");
4164             SCIPdebugPrintCons(scip, auxcons, NULL);
4165             SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
4166             ++*naddconss;
4167 
4168             /* add sum_i a_i*x_i + xbndszero.sup * y - z <= xbndszero.sup constraint, need to be enforced as not redundant */
4169             xcoef[nxvars] = SCIPintervalGetSup(xbndszero);
4170 
4171             if( nxvars == 1 )
4172                (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "linreform%s*%s_%s_4", SCIPvarGetName(y), SCIPvarGetName(xvars[0]), SCIPconsGetName(cons));
4173             else
4174                (void)SCIPsnprintf(name, SCIP_MAXSTRLEN, "linreform%s*%s*more_%s_4", SCIPvarGetName(y), SCIPvarGetName(xvars[0]), SCIPconsGetName(cons));
4175             SCIP_CALL( SCIPcreateConsLinear(scip, &auxcons, name, nxvars+2, xvars, xcoef, -SCIPinfinity(scip), SCIPintervalGetSup(xbndszero),
4176                   SCIPconsIsInitial(cons) && conshdlrdata->binreforminitial,
4177                   SCIPconsIsSeparated(cons), TRUE, TRUE,
4178                   SCIPconsIsPropagated(cons),  SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons),
4179                   SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons), SCIPconsIsStickingAtNode(cons)) );
4180             SCIP_CALL( SCIPaddCons(scip, auxcons) );
4181             SCIPdebugMsg(scip, "added linear constraint: ");
4182             SCIPdebugPrintCons(scip, auxcons, NULL);
4183             SCIP_CALL( SCIPreleaseCons(scip, &auxcons) );
4184             ++*naddconss;
4185 
4186             /* add linear term scale*auxvar to this constraint */
4187             SCIP_CALL( addLinearCoef(scip, cons, auxvar, scale) );
4188 
4189             /* forget about auxvar */
4190             SCIP_CALL( SCIPreleaseVar(scip, &auxvar) );
4191          }
4192       }
4193       while( j < nbilinterms );
4194 
4195       /* remove bilinear terms that have been replaced */
4196       SCIP_CALL( removeBilinearTermsPos(scip, cons, ntodelete, todelete) );
4197    }
4198    SCIPdebugMsg(scip, "resulting quadratic constraint: ");
4199    SCIPdebugPrintCons(scip, cons, NULL);
4200 
4201    SCIPfreeBufferArrayNull(scip, &todelete);
4202    SCIPfreeBufferArrayNull(scip, &xcoef);
4203    SCIPfreeBufferArrayNull(scip, &xvars);
4204 
4205    return SCIP_OKAY;
4206 }
4207 
4208 /** tries to automatically convert a quadratic constraint (or a part of it) into a more specific and more specialized constraint */
4209 static
presolveUpgrade(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_Bool * upgraded,int * nupgdconss,int * naddconss,SCIP_PRESOLTIMING presoltiming)4210 SCIP_RETCODE presolveUpgrade(
4211    SCIP*                 scip,               /**< SCIP data structure */
4212    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler data structure */
4213    SCIP_CONS*            cons,               /**< source constraint to try to convert */
4214    SCIP_Bool*            upgraded,           /**< buffer to store whether constraint was upgraded */
4215    int*                  nupgdconss,         /**< buffer to increase if constraint was upgraded */
4216    int*                  naddconss,          /**< buffer to increase with number of additional constraints created during upgrade */
4217    SCIP_PRESOLTIMING     presoltiming        /**< current presolving timing */
4218    )
4219 {
4220    SCIP_CONSHDLRDATA* conshdlrdata;
4221    SCIP_CONSDATA* consdata;
4222    SCIP_VAR* var;
4223    SCIP_Real lincoef;
4224    SCIP_Real quadcoef;
4225    SCIP_Real lb;
4226    SCIP_Real ub;
4227    int nbinlin;
4228    int nbinquad;
4229    int nintlin;
4230    int nintquad;
4231    int nimpllin;
4232    int nimplquad;
4233    int ncontlin;
4234    int ncontquad;
4235    SCIP_Bool integral;
4236    int i;
4237    int j;
4238    SCIP_CONS** upgdconss;
4239    int upgdconsssize;
4240    int nupgdconss_;
4241 
4242    assert(scip != NULL);
4243    assert(conshdlr != NULL);
4244    assert(cons != NULL);
4245    assert(!SCIPconsIsModifiable(cons));
4246    assert(upgraded   != NULL);
4247    assert(nupgdconss != NULL);
4248    assert(naddconss  != NULL);
4249 
4250    *upgraded = FALSE;
4251 
4252    nupgdconss_ = 0;
4253 
4254    conshdlrdata = SCIPconshdlrGetData(conshdlr);
4255    assert(conshdlrdata != NULL);
4256 
4257    /* if there are no upgrade methods, we can also stop */
4258    if( conshdlrdata->nquadconsupgrades == 0 )
4259       return SCIP_OKAY;
4260 
4261    upgdconsssize = 2;
4262    SCIP_CALL( SCIPallocBufferArray(scip, &upgdconss, upgdconsssize) );
4263 
4264    consdata = SCIPconsGetData(cons);
4265    assert(consdata != NULL);
4266 
4267    /* calculate some statistics on quadratic constraint */
4268    nbinlin   = 0;
4269    nbinquad  = 0;
4270    nintlin   = 0;
4271    nintquad  = 0;
4272    nimpllin  = 0;
4273    nimplquad = 0;
4274    ncontlin  = 0;
4275    ncontquad = 0;
4276    integral  = TRUE;
4277    for( i = 0; i < consdata->nlinvars; ++i )
4278    {
4279       var = consdata->linvars[i];
4280       lincoef = consdata->lincoefs[i];
4281       lb  = SCIPvarGetLbLocal(var);
4282       ub  = SCIPvarGetUbLocal(var);
4283       assert(!SCIPisZero(scip, lincoef));
4284 
4285       switch( SCIPvarGetType(var) )
4286       {
4287       case SCIP_VARTYPE_BINARY:
4288          if( !SCIPisZero(scip, lb) || !SCIPisZero(scip, ub) )
4289             integral = integral && SCIPisIntegral(scip, lincoef);
4290          nbinlin++;
4291          break;
4292       case SCIP_VARTYPE_INTEGER:
4293          if( !SCIPisZero(scip, lb) || !SCIPisZero(scip, ub) )
4294             integral = integral && SCIPisIntegral(scip, lincoef);
4295          nintlin++;
4296          break;
4297       case SCIP_VARTYPE_IMPLINT:
4298          if( !SCIPisZero(scip, lb) || !SCIPisZero(scip, ub) )
4299             integral = integral && SCIPisIntegral(scip, lincoef);
4300          nimpllin++;
4301          break;
4302       case SCIP_VARTYPE_CONTINUOUS:
4303          integral = integral && SCIPisRelEQ(scip, lb, ub) && SCIPisIntegral(scip, lincoef * lb);
4304          ncontlin++;
4305          break;
4306       default:
4307          SCIPerrorMessage("unknown variable type\n");
4308          return SCIP_INVALIDDATA;
4309       }
4310    }
4311 
4312    for( i = 0; i < consdata->nquadvars; ++i )
4313    {
4314       var = consdata->quadvarterms[i].var;
4315       lincoef  = consdata->quadvarterms[i].lincoef;
4316       quadcoef = consdata->quadvarterms[i].sqrcoef;
4317       lb  = SCIPvarGetLbLocal(var);
4318       ub  = SCIPvarGetUbLocal(var);
4319 
4320       switch( SCIPvarGetType(var) )
4321       {
4322       case SCIP_VARTYPE_BINARY:
4323          if( !SCIPisZero(scip, lb) || !SCIPisZero(scip, ub) )
4324             integral = integral && SCIPisIntegral(scip, lincoef) && SCIPisIntegral(scip, quadcoef);
4325          nbinquad++;
4326          break;
4327       case SCIP_VARTYPE_INTEGER:
4328          if( !SCIPisZero(scip, lb) || !SCIPisZero(scip, ub) )
4329             integral = integral && SCIPisIntegral(scip, lincoef) && SCIPisIntegral(scip, quadcoef);
4330          nintquad++;
4331          break;
4332       case SCIP_VARTYPE_IMPLINT:
4333          if( !SCIPisZero(scip, lb) || !SCIPisZero(scip, ub) )
4334             integral = integral && SCIPisIntegral(scip, lincoef) && SCIPisIntegral(scip, quadcoef);
4335          nimplquad++;
4336          break;
4337       case SCIP_VARTYPE_CONTINUOUS:
4338          integral = integral && SCIPisRelEQ(scip, lb, ub) && SCIPisIntegral(scip, lincoef * lb + quadcoef * lb * lb);
4339          ncontquad++;
4340          break;
4341       default:
4342          SCIPerrorMessage("unknown variable type\n");
4343          return SCIP_INVALIDDATA;
4344       }
4345    }
4346 
4347    if( integral )
4348    {
4349       for( i = 0; i < consdata->nbilinterms && integral; ++i )
4350       {
4351          if( SCIPvarGetType(consdata->bilinterms[i].var1) < SCIP_VARTYPE_CONTINUOUS && SCIPvarGetType(consdata->bilinterms[i].var2) < SCIP_VARTYPE_CONTINUOUS )
4352             integral = integral && SCIPisIntegral(scip, consdata->bilinterms[i].coef);
4353          else
4354             integral = FALSE;
4355       }
4356    }
4357 
4358    /* call the upgrading methods */
4359 
4360    SCIPdebugMsg(scip, "upgrading quadratic constraint <%s> (%d upgrade methods):\n",
4361       SCIPconsGetName(cons), conshdlrdata->nquadconsupgrades);
4362    SCIPdebugMsg(scip, " binlin=%d binquad=%d intlin=%d intquad=%d impllin=%d implquad=%d contlin=%d contquad=%d integral=%u\n",
4363       nbinlin, nbinquad, nintlin, nintquad, nimpllin, nimplquad, ncontlin, ncontquad, integral);
4364    SCIPdebugPrintCons(scip, cons, NULL);
4365 
4366    /* try all upgrading methods in priority order in case the upgrading step is enable  */
4367    for( i = 0; i < conshdlrdata->nquadconsupgrades; ++i )
4368    {
4369       if( !conshdlrdata->quadconsupgrades[i]->active )
4370          continue;
4371 
4372       SCIP_CALL( conshdlrdata->quadconsupgrades[i]->quadconsupgd(scip, cons,
4373             nbinlin, nbinquad, nintlin, nintquad, nimpllin, nimplquad, ncontlin, ncontquad, integral,
4374             &nupgdconss_, upgdconss, upgdconsssize, presoltiming) );
4375 
4376       while( nupgdconss_ < 0 )
4377       {
4378          /* upgrade function requires more memory: resize upgdconss and call again */
4379          assert(-nupgdconss_ > upgdconsssize);
4380          upgdconsssize = -nupgdconss_;
4381          SCIP_CALL( SCIPreallocBufferArray(scip, &upgdconss, -nupgdconss_) );
4382 
4383          SCIP_CALL( conshdlrdata->quadconsupgrades[i]->quadconsupgd(scip, cons,
4384                nbinlin, nbinquad, nintlin, nintquad, nimpllin, nimplquad, ncontlin, ncontquad, integral,
4385                &nupgdconss_, upgdconss, upgdconsssize, presoltiming) );
4386 
4387          assert(nupgdconss_ != 0);
4388       }
4389 
4390       if( nupgdconss_ > 0 )
4391       {
4392          /* got upgrade */
4393          SCIPdebugPrintCons(scip, cons, NULL);
4394          SCIPdebugMsg(scip, " -> upgraded to %d constraints:\n", nupgdconss_);
4395 
4396          /* add the upgraded constraints to the problem and forget them */
4397          for( j = 0; j < nupgdconss_; ++j )
4398          {
4399             SCIPdebugMsgPrint(scip, "\t");
4400             SCIPdebugPrintCons(scip, upgdconss[j], NULL);
4401 
4402             SCIP_CALL( SCIPaddCons(scip, upgdconss[j]) );      /*lint !e613*/
4403             SCIP_CALL( SCIPreleaseCons(scip, &upgdconss[j]) ); /*lint !e613*/
4404          }
4405 
4406          /* count the first upgrade constraint as constraint upgrade and the remaining ones as added constraints */
4407          *nupgdconss += 1;
4408          *naddconss += nupgdconss_ - 1;
4409          *upgraded = TRUE;
4410 
4411          /* delete upgraded constraint */
4412          SCIPdebugMsg(scip, "delete constraint <%s> after upgrade\n", SCIPconsGetName(cons));
4413          SCIP_CALL( SCIPdelCons(scip, cons) );
4414 
4415          break;
4416       }
4417    }
4418 
4419    SCIPfreeBufferArray(scip, &upgdconss);
4420 
4421    return SCIP_OKAY;
4422 }
4423 
4424 /** helper function for presolveDisaggregate */
4425 static
presolveDisaggregateMarkComponent(SCIP * scip,SCIP_CONSDATA * consdata,int quadvaridx,SCIP_HASHMAP * var2component,int componentnr,int * componentsize)4426 SCIP_RETCODE presolveDisaggregateMarkComponent(
4427    SCIP*                 scip,               /**< SCIP data structure */
4428    SCIP_CONSDATA*        consdata,           /**< constraint data */
4429    int                   quadvaridx,         /**< index of quadratic variable to mark */
4430    SCIP_HASHMAP*         var2component,      /**< variables to components mapping */
4431    int                   componentnr,        /**< the component number to mark to */
4432    int*                  componentsize       /**< buffer to store size of component (incremented by 1) */
4433    )
4434 {
4435    SCIP_QUADVARTERM* quadvarterm;
4436    SCIP_VAR* othervar;
4437    int othervaridx;
4438    int i;
4439 
4440    assert(consdata != NULL);
4441    assert(quadvaridx >= 0);
4442    assert(quadvaridx < consdata->nquadvars);
4443    assert(var2component != NULL);
4444    assert(componentnr >= 0);
4445 
4446    quadvarterm = &consdata->quadvarterms[quadvaridx];
4447 
4448    if( SCIPhashmapExists(var2component, quadvarterm->var) )
4449    {
4450       /* if we saw the variable before, then it should have the same component number */
4451       assert(SCIPhashmapGetImageInt(var2component, quadvarterm->var) == componentnr);
4452       return SCIP_OKAY;
4453    }
4454 
4455    /* assign component number to variable */
4456    SCIP_CALL( SCIPhashmapInsertInt(var2component, quadvarterm->var, componentnr) );
4457    ++*componentsize;
4458 
4459    /* assign same component number to all variables this variable is multiplied with */
4460    for( i = 0; i < quadvarterm->nadjbilin; ++i )
4461    {
4462       othervar = consdata->bilinterms[quadvarterm->adjbilin[i]].var1 == quadvarterm->var ?
4463          consdata->bilinterms[quadvarterm->adjbilin[i]].var2 : consdata->bilinterms[quadvarterm->adjbilin[i]].var1;
4464       SCIP_CALL( consdataFindQuadVarTerm(scip, consdata, othervar, &othervaridx) );
4465       assert(othervaridx >= 0);
4466       SCIP_CALL( presolveDisaggregateMarkComponent(scip, consdata, othervaridx, var2component, componentnr, componentsize) );
4467    }
4468 
4469    return SCIP_OKAY;
4470 }
4471 
4472 /** merges components in variables connectivity graph */
4473 static
presolveDisaggregateMergeComponents(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_HASHMAP * var2component,int nvars,int * ncomponents,int * componentssize)4474 SCIP_RETCODE presolveDisaggregateMergeComponents(
4475    SCIP*                 scip,               /**< SCIP data structure */
4476    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler data structure */
4477    SCIP_HASHMAP*         var2component,      /**< variables to component mapping */
4478    int                   nvars,              /**< number of variables */
4479    int*                  ncomponents,        /**< number of components */
4480    int*                  componentssize      /**< size of components */
4481    )
4482 {
4483    SCIP_CONSHDLRDATA* conshdlrdata;
4484    SCIP_HASHMAPENTRY* entry;
4485    int maxncomponents;
4486    int* oldcompidx;
4487    int* newcompidx;
4488    int i;
4489    int oldcomponent;
4490    int newcomponent;
4491 
4492    assert(scip != NULL);
4493    assert(conshdlr != NULL);
4494    assert(var2component != NULL);
4495    assert(ncomponents != NULL);
4496    assert(componentssize != NULL);
4497 
4498    conshdlrdata = SCIPconshdlrGetData(conshdlr);
4499    assert(conshdlrdata != NULL);
4500 
4501    maxncomponents = conshdlrdata->maxdisaggrsize;
4502    assert(maxncomponents > 0);
4503 
4504    /* if already not too many components, then nothing to do */
4505    if( *ncomponents <= maxncomponents )
4506       return SCIP_OKAY;
4507 
4508    /*
4509    printf("component sizes before:");
4510    for( i = 0; i < *ncomponents; ++i )
4511       printf(" %d", componentssize[i]);
4512    printf("\n");
4513    */
4514 
4515    SCIP_CALL( SCIPallocBufferArray(scip, &oldcompidx, *ncomponents) );
4516    SCIP_CALL( SCIPallocBufferArray(scip, &newcompidx, *ncomponents) );
4517 
4518    for( i = 0; i < *ncomponents; ++i )
4519       oldcompidx[i] = i;
4520 
4521    switch( conshdlrdata->disaggrmergemethod )
4522    {
4523       case 's' :
4524          /* sort components by size, increasing order */
4525          SCIPsortIntInt(componentssize, oldcompidx, *ncomponents);
4526          break;
4527       case 'b' :
4528       case 'm' :
4529          /* sort components by size, decreasing order */
4530          SCIPsortDownIntInt(componentssize, oldcompidx, *ncomponents);
4531          break;
4532       default :
4533          SCIPerrorMessage("invalid value for constraints/quadratic/disaggrmergemethod parameter");
4534          return SCIP_PARAMETERWRONGVAL;
4535    }
4536 
4537    SCIPdebugMsg(scip, "%-30s: % 4d components of size % 4d to % 4d, median: % 4d\n", SCIPgetProbName(scip), *ncomponents, componentssize[0], componentssize[*ncomponents-1], componentssize[*ncomponents/2]);
4538 
4539    if( conshdlrdata->disaggrmergemethod == 'm' )
4540    {
4541       SCIP_Real targetsize;
4542       int count = 0;
4543 
4544       /* a minimal component size we should reach to have all components roughly the same size */
4545       targetsize = nvars / maxncomponents;  /*lint !e653*/
4546       for( i = 0; i < *ncomponents; ++i )
4547       {
4548          newcompidx[oldcompidx[i]] = i;
4549          count += componentssize[i];
4550 
4551          /* fill with small components until we reach targetsize
4552           * Since targetsize might be fractional, we also add another component if
4553           * the number of variables remaining (=nvars-count) is larger than
4554           * what we expect to put into the remaining components (=targetsize * (maxncomponents - i-1)).
4555           * Thus, from time to time, a component is made larger than the targetsize to avoid
4556           * having to add much into the last component.
4557           */
4558          while( i < *ncomponents-1 && (componentssize[i] + componentssize[*ncomponents-1] <= targetsize ||
4559             nvars - count > targetsize * (maxncomponents - i)) )
4560          {
4561             /* map last (=smallest) component to component i */
4562             newcompidx[oldcompidx[*ncomponents-1]] = i;
4563 
4564             /* increase size of component i accordingly */
4565             componentssize[i] += componentssize[*ncomponents-1];
4566             count += componentssize[*ncomponents-1];
4567 
4568             /* forget about last component */
4569             --*ncomponents;
4570          }
4571       }
4572       assert(count == nvars);
4573    }
4574    else
4575    {
4576       /* get inverse permutation */
4577       for( i = 0; i < *ncomponents; ++i )
4578          newcompidx[oldcompidx[i]] = i;
4579    }
4580 
4581    /* assign new component numbers to variables, cutting off at maxncomponents */
4582    for( i = 0; i < SCIPhashmapGetNEntries(var2component); ++i )
4583    {
4584       entry = SCIPhashmapGetEntry(var2component, i);
4585       if( entry == NULL )
4586          continue;
4587 
4588       oldcomponent = (int)(size_t)SCIPhashmapEntryGetImage(entry);
4589 
4590       newcomponent = newcompidx[oldcomponent];
4591       if( newcomponent >= maxncomponents )
4592       {
4593          newcomponent = maxncomponents-1;
4594          ++componentssize[maxncomponents-1];
4595       }
4596 
4597       SCIPhashmapEntrySetImage(entry, (void*)(size_t)newcomponent); /*lint !e571*/
4598    }
4599    if( *ncomponents > maxncomponents )
4600       *ncomponents = maxncomponents;
4601 
4602    /*
4603    printf("component sizes after :");
4604    for( i = 0; i < *ncomponents; ++i )
4605       printf(" %d", componentssize[i]);
4606    printf("\n");
4607    */
4608 
4609    SCIPfreeBufferArray(scip, &newcompidx);
4610    SCIPfreeBufferArray(scip, &oldcompidx);
4611 
4612    return SCIP_OKAY;
4613 }
4614 
4615 /** compute the next highest power of 2 for a 32-bit argument
4616  *
4617  * Source: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
4618  *
4619  * @note Returns 0 for v=0.
4620  */
4621 static
nextPowerOf2(unsigned int v)4622 unsigned int nextPowerOf2(
4623    unsigned int          v                   /**< input */
4624    )
4625 {
4626    v--;
4627    v |= v >> 1;
4628    v |= v >> 2;
4629    v |= v >> 4;
4630    v |= v >> 8;
4631    v |= v >> 16;
4632    v++;
4633 
4634    return v;
4635 }
4636 
4637 
4638 /** for quadratic constraints that consists of a sum of quadratic terms, disaggregates the sum into a set of constraints by introducing auxiliary variables
4639  *
4640  * Assume the quadratic constraint can be written in the form
4641  *   lhs <= b'x + sum_{k=1..p} q_k(x_k) <= rhs
4642  * where x_k denotes a subset of the variables in x and these subsets are pairwise disjunct
4643  * and q_k(.) is a quadratic form.
4644  * p is selected as large as possible, but to be <= conshdlrdata->maxdisaggrsize.
4645  *
4646  * Without additional scaling, the constraint is disaggregated into
4647  *   lhs <= b'x + sum_k c_k z_k <= rhs
4648  *   c_k z_k ~ q_k(x)
4649  * where "~" is either "<=", "==", or ">=", depending on whether lhs or rhs are infinite.
4650  * Further, c_k is chosen to be the maximal absolute value of the coefficients of the quadratic terms in q_k(x).
4651  * This is done to ensure that z_k takes values with a similar magnitute as the variables in x_k (better for separation).
4652  *
4653  * However, a solution of this disaggregated system can violate the original constraint by (p+1)*epsilon
4654  * (assuming unscaled violations are used, which is the default).
4655  * Therefore, all constraints are scaled by p+1:
4656  *   (p+1)*lhs <= (p+1)*b'x + (p+1) * sum_k c_k z_k <= (p+1) * rhs
4657  *   (p+1)*c_k z_k ~ (p+1)*q_k(x)
4658  */
4659 static
presolveDisaggregate(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,int * naddconss)4660 SCIP_RETCODE presolveDisaggregate(
4661    SCIP*                 scip,               /**< SCIP data structure */
4662    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler data structure */
4663    SCIP_CONS*            cons,               /**< source constraint to try to convert */
4664    int*                  naddconss           /**< pointer to counter of added constraints */
4665    )
4666 {
4667    SCIP_CONSDATA* consdata;
4668    SCIP_HASHMAP* var2component;
4669    int* componentssize;
4670    int ncomponents;
4671    int i;
4672    int comp;
4673    SCIP_CONS** auxconss;
4674    SCIP_VAR** auxvars;
4675    SCIP_Real* auxcoefs;
4676 #ifdef WITH_DEBUG_SOLUTION
4677    SCIP_Real* auxsolvals; /* value of auxiliary variable in debug solution */
4678 #endif
4679    SCIP_Real scale;
4680    char name[SCIP_MAXSTRLEN];
4681 
4682    assert(scip != NULL);
4683    assert(conshdlr != NULL);
4684    assert(cons != NULL);
4685    assert(naddconss != NULL);
4686 
4687    consdata = SCIPconsGetData(cons);
4688    assert(consdata != NULL);
4689 
4690    /* skip if constraint has been already disaggregated */
4691    if( consdata->isdisaggregated )
4692       return SCIP_OKAY;
4693 
4694    consdata->isdisaggregated = TRUE;
4695 
4696    /* make sure there are no quadratic variables without coefficients */
4697    SCIP_CALL( mergeAndCleanBilinearTerms(scip, cons) );
4698    SCIP_CALL( mergeAndCleanQuadVarTerms(scip, cons) );
4699 
4700    if( consdata->nquadvars <= 1 )
4701       return SCIP_OKAY;
4702 
4703    /* sort quadratic variable terms here, so we can later search in it without reordering the array */
4704    SCIP_CALL( consdataSortQuadVarTerms(scip, consdata) );
4705 
4706    /* check how many quadratic terms with non-overlapping variables we have
4707     * in other words, the number of components in the sparsity graph of the quadratic term matrix
4708     */
4709    ncomponents = 0;
4710    SCIP_CALL( SCIPhashmapCreate(&var2component, SCIPblkmem(scip), consdata->nquadvars) );
4711    SCIP_CALL( SCIPallocBufferArray(scip, &componentssize, consdata->nquadvars) );
4712    for( i = 0; i < consdata->nquadvars; ++i )
4713    {
4714       /* if variable was marked already, skip it */
4715       if( SCIPhashmapExists(var2component, (void*)consdata->quadvarterms[i].var) )
4716          continue;
4717 
4718       /* start a new component with variable i */
4719       componentssize[ncomponents] = 0;
4720       SCIP_CALL( presolveDisaggregateMarkComponent(scip, consdata, i, var2component, ncomponents, componentssize + ncomponents) );
4721       ++ncomponents;
4722    }
4723 
4724    assert(ncomponents >= 1);
4725 
4726    /* if there is only one component, we cannot disaggregate
4727     * @todo we could still split the constraint into several while keeping the number of variables sharing several constraints as small as possible
4728     */
4729    if( ncomponents == 1 )
4730    {
4731       SCIPhashmapFree(&var2component);
4732       SCIPfreeBufferArray(scip, &componentssize);
4733       return SCIP_OKAY;
4734    }
4735 
4736    /* merge some components, if necessary */
4737    SCIP_CALL( presolveDisaggregateMergeComponents(scip, conshdlr, var2component, consdata->nquadvars, &ncomponents, componentssize) );
4738 
4739    SCIPfreeBufferArray(scip, &componentssize);
4740 
4741    /* scale all new constraints (ncomponents+1 many) by ncomponents+1 (or its next power of 2), so violations sum up to at most epsilon */
4742    scale = nextPowerOf2((unsigned int)ncomponents + 1);
4743 
4744    SCIP_CALL( SCIPallocBufferArray(scip, &auxconss, ncomponents) );
4745    SCIP_CALL( SCIPallocBufferArray(scip, &auxvars,  ncomponents) );
4746    SCIP_CALL( SCIPallocBufferArray(scip, &auxcoefs, ncomponents) );
4747 #ifdef WITH_DEBUG_SOLUTION
4748    SCIP_CALL( SCIPallocClearBufferArray(scip, &auxsolvals, ncomponents) );
4749 #endif
4750 
4751    /* create auxiliary variables and empty constraints for each component */
4752    for( comp = 0; comp < ncomponents; ++comp )
4753    {
4754       (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "%s_comp%d", SCIPconsGetName(cons), comp);
4755 
4756       SCIP_CALL( SCIPcreateVar(scip, &auxvars[comp], name, -SCIPinfinity(scip), SCIPinfinity(scip), 0.0,
4757             SCIP_VARTYPE_CONTINUOUS, SCIPconsIsInitial(cons), FALSE, NULL, NULL, NULL, NULL, NULL) );
4758 
4759       SCIP_CALL( SCIPcreateConsQuadratic2(scip, &auxconss[comp], name, 0, NULL, NULL, 0, NULL, 0, NULL,
4760             (SCIPisInfinity(scip, -consdata->lhs) ? -SCIPinfinity(scip) : 0.0),
4761             (SCIPisInfinity(scip,  consdata->rhs) ?  SCIPinfinity(scip) : 0.0),
4762             SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), TRUE,
4763             TRUE, SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons), SCIPconsIsModifiable(cons),
4764             SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons)) );
4765 
4766       auxcoefs[comp] = SCIPinfinity(scip);
4767    }
4768 
4769    /* add quadratic variables to each component constraint
4770     * delete adjacency information */
4771    for( i = 0; i < consdata->nquadvars; ++i )
4772    {
4773       assert(SCIPhashmapExists(var2component, consdata->quadvarterms[i].var));
4774 
4775       comp = SCIPhashmapGetImageInt(var2component, consdata->quadvarterms[i].var);
4776       assert(comp >= 0);
4777       assert(comp < ncomponents);
4778 
4779       /* add variable term to corresponding constraint */
4780       SCIP_CALL( SCIPaddQuadVarQuadratic(scip, auxconss[comp], consdata->quadvarterms[i].var, scale * consdata->quadvarterms[i].lincoef, scale * consdata->quadvarterms[i].sqrcoef) );
4781 
4782       /* reduce coefficient of aux variable */
4783       if( !SCIPisZero(scip, consdata->quadvarterms[i].lincoef) && ABS(consdata->quadvarterms[i].lincoef) < auxcoefs[comp] )
4784          auxcoefs[comp] = REALABS(consdata->quadvarterms[i].lincoef);
4785       if( !SCIPisZero(scip, consdata->quadvarterms[i].sqrcoef) && ABS(consdata->quadvarterms[i].sqrcoef) < auxcoefs[comp] )
4786          auxcoefs[comp] = REALABS(consdata->quadvarterms[i].sqrcoef);
4787 
4788       SCIPfreeBlockMemoryArray(scip, &consdata->quadvarterms[i].adjbilin, consdata->quadvarterms[i].adjbilinsize);
4789       consdata->quadvarterms[i].nadjbilin = 0;
4790       consdata->quadvarterms[i].adjbilinsize = 0;
4791 
4792 #ifdef WITH_DEBUG_SOLUTION
4793       if( SCIPdebugIsMainscip(scip) )
4794       {
4795          SCIP_Real debugvarval;
4796 
4797          SCIP_CALL( SCIPdebugGetSolVal(scip, consdata->quadvarterms[i].var, &debugvarval) );
4798          auxsolvals[comp] += consdata->quadvarterms[i].lincoef * debugvarval + consdata->quadvarterms[i].sqrcoef * debugvarval * debugvarval;
4799       }
4800 #endif
4801    }
4802 
4803    /* add bilinear terms to each component constraint */
4804    for( i = 0; i < consdata->nbilinterms; ++i )
4805    {
4806       assert(SCIPhashmapExists(var2component, consdata->bilinterms[i].var1));
4807       assert(SCIPhashmapExists(var2component, consdata->bilinterms[i].var2));
4808 
4809       comp = SCIPhashmapGetImageInt(var2component, consdata->bilinterms[i].var1);
4810       assert(comp == SCIPhashmapGetImageInt(var2component, consdata->bilinterms[i].var2));
4811       assert(!SCIPisZero(scip, consdata->bilinterms[i].coef));
4812 
4813       SCIP_CALL( SCIPaddBilinTermQuadratic(scip, auxconss[comp],
4814             consdata->bilinterms[i].var1, consdata->bilinterms[i].var2, scale * consdata->bilinterms[i].coef) );
4815 
4816       if( ABS(consdata->bilinterms[i].coef) < auxcoefs[comp] )
4817          auxcoefs[comp] = ABS(consdata->bilinterms[i].coef);
4818 
4819 #ifdef WITH_DEBUG_SOLUTION
4820       if( SCIPdebugIsMainscip(scip) )
4821       {
4822          SCIP_Real debugvarval1;
4823          SCIP_Real debugvarval2;
4824 
4825          SCIP_CALL( SCIPdebugGetSolVal(scip, consdata->bilinterms[i].var1, &debugvarval1) );
4826          SCIP_CALL( SCIPdebugGetSolVal(scip, consdata->bilinterms[i].var2, &debugvarval2) );
4827          auxsolvals[comp] += consdata->bilinterms[i].coef * debugvarval1 * debugvarval2;
4828       }
4829 #endif
4830    }
4831 
4832    /* forget about bilinear terms in cons */
4833    SCIPfreeBlockMemoryArray(scip, &consdata->bilinterms, consdata->bilintermssize);
4834    consdata->nbilinterms = 0;
4835    consdata->bilintermssize = 0;
4836 
4837    /* remove quadratic variable terms from cons */
4838    for( i = consdata->nquadvars - 1; i >= 0; --i )
4839    {
4840       SCIP_CALL( delQuadVarTermPos(scip, cons, i) );
4841    }
4842    assert(consdata->nquadvars == 0);
4843 
4844    /* scale remaining linear variables and sides by scale */
4845    for( i = 0; i < consdata->nlinvars; ++i )
4846    {
4847       SCIP_CALL( chgLinearCoefPos(scip, cons, i, scale * consdata->lincoefs[i]) );
4848    }
4849    if( !SCIPisInfinity(scip, -consdata->lhs) )
4850    {
4851       consdata->lhs *= scale;
4852       assert(!SCIPisInfinity(scip, -consdata->lhs) );
4853    }
4854    if( !SCIPisInfinity(scip, consdata->rhs) )
4855    {
4856       consdata->rhs *= scale;
4857       assert(!SCIPisInfinity(scip, consdata->rhs) );
4858    }
4859 
4860    /* add auxiliary variables to auxiliary constraints
4861     * add aux vars and constraints to SCIP
4862     * add aux vars to this constraint
4863     * set value of aux vars in debug solution, if any
4864     */
4865    SCIPdebugMsg(scip, "add %d constraints for disaggregation of quadratic constraint <%s>\n", ncomponents, SCIPconsGetName(cons));
4866    SCIP_CALL( consdataEnsureLinearVarsSize(scip, consdata, consdata->nlinvars + ncomponents) );
4867    for( comp = 0; comp < ncomponents; ++comp )
4868    {
4869       SCIP_CONSDATA* auxconsdata;
4870 
4871       SCIP_CALL( SCIPaddLinearVarQuadratic(scip, auxconss[comp], auxvars[comp], -scale * auxcoefs[comp]) );
4872 
4873       SCIP_CALL( SCIPaddVar(scip, auxvars[comp]) );
4874 
4875       SCIP_CALL( SCIPaddCons(scip, auxconss[comp]) );
4876       SCIPdebugPrintCons(scip, auxconss[comp], NULL);
4877 
4878       SCIP_CALL( addLinearCoef(scip, cons, auxvars[comp], scale * auxcoefs[comp]) );
4879 
4880       /* mark that the constraint should not further be disaggregated */
4881       auxconsdata = SCIPconsGetData(auxconss[comp]);
4882       assert(auxconsdata != NULL);
4883       auxconsdata->isdisaggregated = TRUE;
4884 
4885 #ifdef WITH_DEBUG_SOLUTION
4886       if( SCIPdebugIsMainscip(scip) )
4887       {
4888          /* auxvar should take value from auxsolvals in debug solution, but we also scaled auxvar by auxcoefs[comp] */
4889          SCIP_CALL( SCIPdebugAddSolVal(scip, auxvars[comp], auxsolvals[comp] / auxcoefs[comp]) );
4890       }
4891 #endif
4892 
4893       SCIP_CALL( SCIPreleaseCons(scip, &auxconss[comp]) );
4894       SCIP_CALL( SCIPreleaseVar(scip, &auxvars[comp]) );
4895    }
4896    *naddconss += ncomponents;
4897 
4898    SCIPdebugPrintCons(scip, cons, NULL);
4899 
4900    SCIPfreeBufferArray(scip, &auxconss);
4901    SCIPfreeBufferArray(scip, &auxvars);
4902    SCIPfreeBufferArray(scip, &auxcoefs);
4903 #ifdef WITH_DEBUG_SOLUTION
4904    SCIPfreeBufferArray(scip, &auxsolvals);
4905 #endif
4906    SCIPhashmapFree(&var2component);
4907 
4908    return SCIP_OKAY;
4909 }
4910 
4911 #ifdef CHECKIMPLINBILINEAR
4912 /** checks if there are bilinear terms x*y with a binary variable x and an implication x = {0,1} -> y = 0
4913  *
4914  *  In this case, the bilinear term can be removed (x=0 case) or replaced by y (x=1 case).
4915  */
4916 static
presolveApplyImplications(SCIP * scip,SCIP_CONS * cons,int * nbilinremoved)4917 SCIP_RETCODE presolveApplyImplications(
4918    SCIP*                 scip,               /**< SCIP data structure */
4919    SCIP_CONS*            cons,               /**< quadratic constraint */
4920    int*                  nbilinremoved       /**< buffer to store number of removed bilinear terms */
4921    )
4922 {
4923    SCIP_CONSDATA* consdata;
4924    SCIP_VAR* x;
4925    SCIP_VAR* y;
4926    SCIP_INTERVAL implbnds;
4927    int i;
4928    int j;
4929    int k;
4930 
4931    assert(scip != NULL);
4932    assert(cons != NULL);
4933    assert(nbilinremoved != NULL);
4934 
4935    *nbilinremoved = 0;
4936 
4937    consdata = SCIPconsGetData(cons);
4938    assert(consdata != NULL);
4939 
4940    SCIPdebugMsg(scip, "apply implications in <%s>\n", SCIPconsGetName(cons));
4941 
4942    /* sort quadvarterms in case we need to search */
4943    SCIP_CALL( consdataSortQuadVarTerms(scip, consdata) );
4944 
4945    for( i = 0; i < consdata->nquadvars; ++i )
4946    {
4947       x = consdata->quadvarterms[i].var;
4948       assert(x != NULL);
4949 
4950       if( consdata->quadvarterms[i].nadjbilin == 0 )
4951          continue;
4952 
4953       if( !SCIPvarIsBinary(x) )
4954          continue;
4955 
4956       if( !SCIPvarIsActive(x) )
4957          continue;
4958 
4959       if( SCIPvarGetNImpls(x, TRUE) == 0 && SCIPvarGetNImpls(x, FALSE) == 0 )
4960          continue;
4961 
4962       for( j = 0; j < consdata->quadvarterms[i].nadjbilin; ++j )
4963       {
4964          k = consdata->quadvarterms[i].adjbilin[j];
4965          assert(k >= 0);
4966          assert(k < consdata->nbilinterms);
4967 
4968          if( consdata->bilinterms[k].coef == 0.0 )
4969             continue;
4970 
4971          y = consdata->bilinterms[k].var1 == x ? consdata->bilinterms[k].var2 : consdata->bilinterms[k].var1;
4972          assert(x != y);
4973 
4974          SCIP_CALL( getImpliedBounds(scip, x, TRUE, y, &implbnds) );
4975          if( SCIPisZero(scip, implbnds.inf) && SCIPisZero(scip, implbnds.sup) )
4976          {
4977             /* if x = 1 implies y = 0, then we can remove the bilinear term x*y, since it is always 0
4978              * we only set the coefficient to 0.0 here and mark the bilinterms as not merged */
4979             SCIPdebugMsg(scip, "remove bilinear term %g<%s><%s> from <%s> due to implication\n", consdata->bilinterms[k].coef, SCIPvarGetName(x), SCIPvarGetName(y), SCIPconsGetName(cons));
4980             consdata->bilinterms[k].coef = 0.0;
4981             consdata->bilinmerged = FALSE;
4982             ++*nbilinremoved;
4983             continue;
4984          }
4985 
4986          SCIP_CALL( getImpliedBounds(scip, x, FALSE, y, &implbnds) );
4987          if( SCIPisZero(scip, implbnds.inf) && SCIPisZero(scip, implbnds.sup) )
4988          {
4989             /* if x = 0 implies y = 0, then we can replace the bilinear term x*y by y
4990              * we only move the coefficient to the linear coef of y here and mark the bilinterms as not merged */
4991             SCIPdebugMsg(scip, "replace bilinear term %g<%s><%s> by %g<%s> in <%s> due to implication\n", consdata->bilinterms[k].coef, SCIPvarGetName(x), SCIPvarGetName(y), consdata->bilinterms[k].coef, SCIPvarGetName(y), SCIPconsGetName(cons));
4992             assert(consdata->quadvarssorted);
4993             SCIP_CALL( SCIPaddQuadVarLinearCoefQuadratic(scip, cons, y, consdata->bilinterms[k].coef) );
4994             consdata->bilinterms[k].coef = 0.0;
4995             consdata->bilinmerged = FALSE;
4996             ++*nbilinremoved;
4997          }
4998       }
4999    }
5000 
5001    if( *nbilinremoved > 0 )
5002    {
5003       SCIP_CALL( mergeAndCleanBilinearTerms(scip, cons) );
5004 
5005       /* invalidate nonlinear row */
5006       if( consdata->nlrow != NULL )
5007       {
5008          SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
5009       }
5010 
5011       consdata->ispropagated  = FALSE;
5012       consdata->ispresolved   = FALSE;
5013       consdata->iscurvchecked = FALSE;
5014    }
5015 
5016    consdata->isimpladded = FALSE;
5017 
5018    return SCIP_OKAY;
5019 }
5020 #endif
5021 
5022 /** checks a quadratic constraint for convexity and/or concavity without checking multivariate functions */
5023 static
checkCurvatureEasy(SCIP * scip,SCIP_CONS * cons,SCIP_HASHMAP * assumevarfixed,SCIP_Bool * determined,SCIP_Bool checkmultivariate,SCIP_Bool * isconvex,SCIP_Bool * isconcave,SCIP_Real * maxnonconvexity)5024 void checkCurvatureEasy(
5025    SCIP*                 scip,               /**< SCIP data structure */
5026    SCIP_CONS*            cons,               /**< quadratic constraint */
5027    SCIP_HASHMAP*         assumevarfixed,     /**< variables to be assumed to be fixed, or NULL */
5028    SCIP_Bool*            determined,         /**< pointer to store whether the curvature could be determined */
5029    SCIP_Bool             checkmultivariate,  /**< whether curvature will be checked later on for multivariate functions */
5030    SCIP_Bool*            isconvex,           /**< buffer to store whether found convex */
5031    SCIP_Bool*            isconcave,          /**< buffer to store whether found concave */
5032    SCIP_Real*            maxnonconvexity     /**< buffer to store "maximal nonconvexity" */
5033    )
5034 {
5035    SCIP_CONSDATA* consdata;
5036    int nquadvars;
5037 
5038    assert(scip != NULL);
5039    assert(cons != NULL);
5040    assert(determined != NULL);
5041    assert(isconvex != NULL);
5042    assert(isconcave != NULL);
5043    assert(maxnonconvexity != NULL);
5044 
5045    consdata = SCIPconsGetData(cons);
5046    assert(consdata != NULL);
5047 
5048    nquadvars = consdata->nquadvars;
5049    *determined = TRUE;
5050 
5051    SCIPdebugMsg(scip, "Checking curvature of constraint <%s> without multivariate functions\n", SCIPconsGetName(cons));
5052 
5053    *maxnonconvexity = 0.0;
5054    if( nquadvars == 1 )
5055    {
5056       assert(consdata->nbilinterms == 0);
5057       if( assumevarfixed == NULL || !SCIPhashmapExists(assumevarfixed, (void*)consdata->quadvarterms[0].var) )
5058       {
5059          *isconvex      = !SCIPisNegative(scip, consdata->quadvarterms[0].sqrcoef);
5060          *isconcave     = !SCIPisPositive(scip, consdata->quadvarterms[0].sqrcoef);
5061 
5062          if( !SCIPisInfinity(scip, -consdata->lhs) && consdata->quadvarterms[0].sqrcoef > 0.0 )
5063             *maxnonconvexity =  consdata->quadvarterms[0].sqrcoef;
5064          if( !SCIPisInfinity(scip,  consdata->rhs) && consdata->quadvarterms[0].sqrcoef < 0.0 )
5065             *maxnonconvexity = -consdata->quadvarterms[0].sqrcoef;
5066       }
5067       else
5068       {
5069          /* only variable should be assumed to be fixed, so we are linear */
5070          *isconvex = TRUE;
5071          *isconcave = TRUE;
5072       }
5073    }
5074    else if( nquadvars == 0 )
5075    {
5076       *isconvex = TRUE;
5077       *isconcave = TRUE;
5078    }
5079    else if( consdata->nbilinterms == 0 )
5080    {
5081       int v;
5082 
5083       *isconvex = TRUE;
5084       *isconcave = TRUE;
5085 
5086       for( v = nquadvars - 1; v >= 0; --v )
5087       {
5088          /* skip assumed-to-be-fixed variables */
5089          if( assumevarfixed != NULL && SCIPhashmapExists(assumevarfixed, (void*)consdata->quadvarterms[v].var) )
5090             continue;
5091 
5092          *isconvex  = *isconvex  && !SCIPisNegative(scip, consdata->quadvarterms[v].sqrcoef);
5093          *isconcave = *isconcave && !SCIPisPositive(scip, consdata->quadvarterms[v].sqrcoef);
5094 
5095          if( !SCIPisInfinity(scip, -consdata->lhs) &&  consdata->quadvarterms[v].sqrcoef > consdata->maxnonconvexity )
5096             *maxnonconvexity =  consdata->quadvarterms[0].sqrcoef;
5097          if( !SCIPisInfinity(scip,  consdata->rhs) && -consdata->quadvarterms[v].sqrcoef > consdata->maxnonconvexity )
5098             *maxnonconvexity = -consdata->quadvarterms[0].sqrcoef;
5099       }
5100    }
5101    else if( !checkmultivariate )
5102    {
5103       *isconvex  = FALSE;
5104       *isconcave = FALSE;
5105       *maxnonconvexity = SCIPinfinity(scip);
5106    }
5107    else
5108    {
5109       *isconvex  = FALSE;
5110       *isconcave = FALSE;
5111       *determined = FALSE;
5112    }
5113 }
5114 
5115 /** checks a quadratic constraint for convexity and/or concavity while checking multivariate functions */
5116 static
checkCurvatureExpensive(SCIP * scip,SCIP_CONS * cons,SCIP_HASHMAP * assumevarfixed,SCIP_Bool * isconvex,SCIP_Bool * isconcave,SCIP_Real * maxnonconvexity)5117 SCIP_RETCODE checkCurvatureExpensive(
5118    SCIP*                 scip,               /**< SCIP data structure */
5119    SCIP_CONS*            cons,               /**< quadratic constraint */
5120    SCIP_HASHMAP*         assumevarfixed,     /**< variables to be assumed to be fixed, or NULL */
5121    SCIP_Bool*            isconvex,           /**< buffer to store whether found convex */
5122    SCIP_Bool*            isconcave,          /**< buffer to store whether found concave */
5123    SCIP_Real*            maxnonconvexity     /**< buffer to store "maximal nonconvexity" */
5124    )
5125 {
5126    SCIP_CONSDATA* consdata;
5127    double*        matrix;
5128    SCIP_HASHMAP*  var2index;
5129    int            i;
5130    int            n;
5131    int            nn;
5132    int            row;
5133    int            col;
5134    double*        alleigval;
5135 
5136    assert(scip != NULL);
5137    assert(cons != NULL);
5138    assert(isconvex != NULL);
5139    assert(isconcave != NULL);
5140    assert(maxnonconvexity != NULL);
5141 
5142    consdata = SCIPconsGetData(cons);
5143    assert(consdata != NULL);
5144 
5145    n = consdata->nquadvars;
5146 
5147    SCIPdebugMsg(scip, "Checking curvature of constraint <%s> with multivariate functions\n", SCIPconsGetName(cons));
5148 
5149    if( n == 2 && assumevarfixed == NULL )
5150    {
5151       SCIP_Real tracehalf;
5152       SCIP_Real discriminantroot;
5153 
5154       /* compute eigenvalues by hand */
5155       assert(consdata->nbilinterms == 1);
5156       *isconvex =
5157          consdata->quadvarterms[0].sqrcoef >= 0 &&
5158          consdata->quadvarterms[1].sqrcoef >= 0 &&
5159          4 * consdata->quadvarterms[0].sqrcoef * consdata->quadvarterms[1].sqrcoef >= consdata->bilinterms[0].coef * consdata->bilinterms[0].coef;
5160       *isconcave =
5161          consdata->quadvarterms[0].sqrcoef <= 0 &&
5162          consdata->quadvarterms[1].sqrcoef <= 0 &&
5163          4 * consdata->quadvarterms[0].sqrcoef * consdata->quadvarterms[1].sqrcoef >= consdata->bilinterms[0].coef * consdata->bilinterms[0].coef;
5164 
5165       /* store largest eigenvalue causing nonconvexity according to sides */
5166       tracehalf = (consdata->quadvarterms[0].sqrcoef + consdata->quadvarterms[1].sqrcoef) / 2.0;
5167       discriminantroot = consdata->quadvarterms[0].sqrcoef * consdata->quadvarterms[1].sqrcoef - SQR(consdata->bilinterms[0].coef / 2.0);
5168       discriminantroot = SQR(tracehalf) - discriminantroot;
5169       assert(!SCIPisNegative(scip, discriminantroot));
5170       discriminantroot = SQRT(MAX(0.0, discriminantroot));
5171 
5172       *maxnonconvexity = 0.0;
5173       if( !SCIPisInfinity(scip, -consdata->lhs) )
5174          *maxnonconvexity = MAX(*maxnonconvexity, tracehalf + discriminantroot);
5175       if( !SCIPisInfinity(scip, consdata->rhs) )
5176          *maxnonconvexity = MAX(*maxnonconvexity, discriminantroot - tracehalf);
5177 
5178       return SCIP_OKAY;
5179    }
5180 
5181    /* do not check curvature if n is too large */
5182    nn = n * n;
5183    if( nn < 0 || (unsigned) (int) nn > UINT_MAX / sizeof(SCIP_Real) )
5184    {
5185       SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, "cons_quadratic - n is too large to check the curvature\n");
5186       *isconvex = FALSE;
5187       *isconcave = FALSE;
5188       *maxnonconvexity = SCIPinfinity(scip);
5189       return SCIP_OKAY;
5190    }
5191 
5192    /* lower triangular of quadratic term matrix */
5193    SCIP_CALL( SCIPallocBufferArray(scip, &matrix, nn) );
5194    BMSclearMemoryArray(matrix, nn);
5195 
5196    *isconvex  = TRUE;
5197    *isconcave = TRUE;
5198    *maxnonconvexity = 0.0;
5199 
5200    SCIP_CALL( SCIPhashmapCreate(&var2index, SCIPblkmem(scip), n) );
5201    for( i = 0; i < n; ++i )
5202    {
5203       /* skip variables that we assume are fixed -> 0 row/col in coef matrix */
5204       if( assumevarfixed != NULL && SCIPhashmapExists(assumevarfixed, (void*)consdata->quadvarterms[i].var) )
5205          continue;
5206 
5207       if( consdata->quadvarterms[i].nadjbilin > 0 )
5208       {
5209          SCIP_CALL( SCIPhashmapInsertInt(var2index, consdata->quadvarterms[i].var, i) );
5210          matrix[i*n + i] = consdata->quadvarterms[i].sqrcoef;
5211       }
5212       else
5213       {
5214          /* if pure square term, then update maximal nonconvex eigenvalue, as it will not be considered in lapack call below */
5215          if( !SCIPisInfinity(scip, -consdata->lhs) && consdata->quadvarterms[i].sqrcoef > *maxnonconvexity )
5216             *maxnonconvexity = consdata->quadvarterms[i].sqrcoef;
5217          if( !SCIPisInfinity(scip, consdata->rhs) && -consdata->quadvarterms[i].sqrcoef > *maxnonconvexity )
5218             *maxnonconvexity = -consdata->quadvarterms[i].sqrcoef;
5219       }
5220       /* nonzero elements on diagonal tell a lot about convexity/concavity */
5221       if( SCIPisNegative(scip, consdata->quadvarterms[i].sqrcoef) )
5222          *isconvex  = FALSE;
5223       if( SCIPisPositive(scip, consdata->quadvarterms[i].sqrcoef) )
5224          *isconcave = FALSE;
5225    }
5226 
5227    /* skip lapack call, if we know already that we are indefinite
5228     * NOTE: this will leave out updating consdata->maxnonconvexity, so that it only provides a lower bound in this case
5229     */
5230    if( !*isconvex && !*isconcave )
5231    {
5232       SCIPfreeBufferArray(scip, &matrix);
5233       SCIPhashmapFree(&var2index);
5234       /* make sure that maxnonconvexity is strictly different from zero if nonconvex
5235        * TODO one could think about doing some eigenvalue estimation here (Gershgorin)
5236        */
5237       *maxnonconvexity = MAX(1000.0, *maxnonconvexity);
5238       return SCIP_OKAY;
5239    }
5240 
5241    if( SCIPisIpoptAvailableIpopt() )
5242    {
5243       for( i = 0; i < consdata->nbilinterms; ++i )
5244       {
5245          /* skip variables that we assume are fixed -> 0 row/col in coef matrix */
5246          if( assumevarfixed != NULL && (SCIPhashmapExists(assumevarfixed, (void*)consdata->bilinterms[i].var1) || SCIPhashmapExists(assumevarfixed, (void*)consdata->bilinterms[i].var2)) )
5247             continue;
5248 
5249          assert(SCIPhashmapExists(var2index, consdata->bilinterms[i].var1));
5250          assert(SCIPhashmapExists(var2index, consdata->bilinterms[i].var2));
5251          row = SCIPhashmapGetImageInt(var2index, consdata->bilinterms[i].var1);
5252          col = SCIPhashmapGetImageInt(var2index, consdata->bilinterms[i].var2);
5253          if( row < col )
5254             matrix[row * n + col] = consdata->bilinterms[i].coef/2;
5255          else
5256             matrix[col * n + row] = consdata->bilinterms[i].coef/2;
5257       }
5258 
5259       SCIP_CALL( SCIPallocBufferArray(scip, &alleigval, n) );
5260       /* @todo Can we compute only min and max eigen value?
5261        * @todo Can we estimate the numerical error?
5262        * @todo Trying a cholesky factorization may be much faster.
5263        */
5264       if( LapackDsyev(FALSE, n, matrix, alleigval) != SCIP_OKAY )
5265       {
5266          SCIPwarningMessage(scip, "Failed to compute eigenvalues of quadratic coefficient matrix of constraint %s. Assuming matrix is indefinite.\n", SCIPconsGetName(cons));
5267          *isconvex = FALSE;
5268          *isconcave = FALSE;
5269       }
5270       else
5271       {
5272          /* deconvexification reformulates a stricly convex quadratic function in binaries such that it becomes not-strictly convex
5273           * by adding the -lambda*(x^2-x) terms for lambda the smallest eigenvalue of the matrix
5274           * the result is still a convex form "but less so" (ref. papers by Guignard et.al.), but with hopefully tighter value for the continuous relaxation
5275           */
5276 #ifdef DECONVEXIFY
5277          SCIP_Bool allbinary;
5278          printf("cons <%s>[%g,%g] spectrum = [%g,%g]\n", SCIPconsGetName(cons), consdata->lhs, consdata->rhs, alleigval[0], alleigval[n-1]);
5279 #endif
5280          *isconvex  &= !SCIPisNegative(scip, alleigval[0]);   /*lint !e514*/
5281          *isconcave &= !SCIPisPositive(scip, alleigval[n-1]); /*lint !e514*/
5282 #ifdef DECONVEXIFY
5283          for( i = 0; i < consdata->nquadvars; ++i )
5284             if( !SCIPvarIsBinary(consdata->quadvarterms[i].var) )
5285                break;
5286          allbinary = i == consdata->nquadvars;
5287 
5288          if( !SCIPisInfinity(scip, consdata->rhs) && alleigval[0] > 0.1 && allbinary )
5289          {
5290             printf("deconvexify cons <%s> by shifting hessian by %g\n", SCIPconsGetName(cons), alleigval[0]);
5291             for( i = 0; i < consdata->nquadvars; ++i )
5292             {
5293                consdata->quadvarterms[i].sqrcoef -= alleigval[0];
5294                consdata->quadvarterms[i].lincoef += alleigval[0];
5295             }
5296          }
5297 
5298          if( !SCIPisInfinity(scip, consdata->lhs) && alleigval[n-1] < -0.1 && allbinary )
5299          {
5300             printf("deconcavify cons <%s> by shifting hessian by %g\n", SCIPconsGetName(cons), alleigval[n-1]);
5301             for( i = 0; i < consdata->nquadvars; ++i )
5302             {
5303                consdata->quadvarterms[i].sqrcoef -= alleigval[n-1];
5304                consdata->quadvarterms[i].lincoef += alleigval[n-1];
5305             }
5306          }
5307 #endif
5308       }
5309 
5310       /* update largest eigenvalue causing nonconvexity according to sides */
5311       if( !SCIPisInfinity(scip, -consdata->lhs) )
5312          *maxnonconvexity = MAX(*maxnonconvexity, alleigval[n-1]);
5313       if( !SCIPisInfinity(scip, consdata->rhs) )
5314          *maxnonconvexity = MAX(*maxnonconvexity, -alleigval[0]);
5315 
5316       SCIPfreeBufferArray(scip, &alleigval);
5317    }
5318    else
5319    {
5320       *isconvex = FALSE;
5321       *isconcave = FALSE;
5322       *maxnonconvexity = SCIPinfinity(scip);
5323    }
5324 
5325    SCIPhashmapFree(&var2index);
5326    SCIPfreeBufferArray(scip, &matrix);
5327 
5328    return SCIP_OKAY;
5329 }
5330 
5331 
5332 /** checks a quadratic constraint for convexity and/or concavity */
5333 static
checkCurvature(SCIP * scip,SCIP_CONS * cons,SCIP_Bool checkmultivariate)5334 SCIP_RETCODE checkCurvature(
5335    SCIP*                 scip,               /**< SCIP data structure */
5336    SCIP_CONS*            cons,               /**< quadratic constraint */
5337    SCIP_Bool             checkmultivariate   /**< whether curvature should also be checked for multivariate functions */
5338    )
5339 {
5340    SCIP_Bool determined;
5341    SCIP_Bool isconvex;
5342    SCIP_Bool isconcave;
5343    SCIP_CONSDATA* consdata;
5344 
5345    consdata = SCIPconsGetData(cons);
5346    assert(consdata != NULL);
5347 
5348    if( consdata->iscurvchecked )
5349       return SCIP_OKAY;
5350 
5351    checkCurvatureEasy(scip, cons, NULL, &determined, checkmultivariate, &isconvex, &isconcave,
5352       &consdata->maxnonconvexity);
5353    if( !determined && checkmultivariate )
5354    {
5355       SCIP_CALL( checkCurvatureExpensive(scip, cons, NULL, &isconvex, &isconcave,
5356             &consdata->maxnonconvexity) );
5357    }
5358 
5359    consdata->isconvex = isconvex;
5360    consdata->isconcave = isconcave;
5361    consdata->iscurvchecked = TRUE;
5362 
5363    return SCIP_OKAY;
5364 }
5365 
5366 /** check whether indefinite constraint function is factorable and store corresponding coefficients */
5367 static
checkFactorable(SCIP * scip,SCIP_CONS * cons)5368 SCIP_RETCODE checkFactorable(
5369    SCIP*                 scip,               /**< SCIP data structure */
5370    SCIP_CONS*            cons                /**< constraint */
5371    )
5372 {
5373    SCIP_BILINTERM* bilinterm;
5374    SCIP_CONSDATA* consdata;
5375    SCIP_Real* a;
5376    SCIP_Real* eigvals;
5377    SCIP_Real sigma1;
5378    SCIP_Real sigma2;
5379    SCIP_Bool success;
5380    int n;
5381    int i;
5382    int idx1;
5383    int idx2;
5384    int posidx;
5385    int negidx;
5386 
5387    assert(scip != NULL);
5388    assert(cons != NULL);
5389 
5390    consdata = SCIPconsGetData(cons);
5391    assert(consdata != NULL);
5392    assert(consdata->factorleft == NULL);
5393    assert(consdata->factorright == NULL);
5394 
5395    /* we don't need this if there are no bilinear terms */
5396    if( consdata->nbilinterms == 0 )
5397       return SCIP_OKAY;
5398 
5399    /* write constraint as lhs <= linear + x'^T A x' <= rhs where x' = (x,1) and
5400     * A = ( Q     b/2 )
5401     *     ( b^T/2  0  )
5402     * compute an eigenvalue factorization of A and check if there are one positive and one negative eigenvalue
5403     * if so, then let sigma1^2 and -sigma2^2 be these eigenvalues and v1 and v2 be the first two rows of the inverse eigenvector matrix
5404     * thus, x'^T A x' = sigma1^2 (v1^T x')^2 - sigma2^2 (v2^T x')^2
5405     *                 = (sigma1 (v1^T x') - sigma2 (v2^T x')) * (sigma1 (v1^T x') + sigma2 (v2^T x'))
5406     * we then store sigma1 v1^T - sigma2 v2^T as left factor coef, and sigma1 v1^T + sigma2 v2^T as right factor coef
5407     */
5408 
5409    /* if we already know that there are only positive or only negative eigenvalues, then don't try */
5410    if( consdata->iscurvchecked && (consdata->isconvex || consdata->isconcave) )
5411       return SCIP_OKAY;
5412 
5413    n = consdata->nquadvars + 1;
5414 
5415    /* @todo handle case n=3 explicitly */
5416 
5417    /* skip too large matrices */
5418    if( n > 50 )
5419       return SCIP_OKAY;
5420 
5421    /* need routine to compute eigenvalues/eigenvectors */
5422    if( !SCIPisIpoptAvailableIpopt() )
5423       return SCIP_OKAY;
5424 
5425    SCIP_CALL( consdataSortQuadVarTerms(scip, consdata) );
5426 
5427    SCIP_CALL( SCIPallocBufferArray(scip, &a, n*n) );
5428    BMSclearMemoryArray(a, n*n);
5429 
5430    /* set lower triangular entries of A corresponding to bilinear terms */
5431    for( i = 0; i < consdata->nbilinterms; ++i )
5432    {
5433       bilinterm = &consdata->bilinterms[i];
5434 
5435       SCIP_CALL( consdataFindQuadVarTerm(scip, consdata, bilinterm->var1, &idx1) );
5436       SCIP_CALL( consdataFindQuadVarTerm(scip, consdata, bilinterm->var2, &idx2) );
5437       assert(idx1 >= 0);
5438       assert(idx2 >= 0);
5439       assert(idx1 != idx2);
5440 
5441       a[MIN(idx1,idx2) * n + MAX(idx1,idx2)] = bilinterm->coef / 2.0;
5442    }
5443 
5444    /* set lower triangular entries of A corresponding to square and linear terms */
5445    for( i = 0; i < consdata->nquadvars; ++i )
5446    {
5447       a[i*n + i]   = consdata->quadvarterms[i].sqrcoef;
5448       a[i*n + n-1] = consdata->quadvarterms[i].lincoef / 2.0;
5449    }
5450 
5451    SCIP_CALL( SCIPallocBufferArray(scip, &eigvals, n) );
5452    if( LapackDsyev(TRUE, n, a, eigvals) != SCIP_OKAY )
5453    {
5454       SCIPdebugMsg(scip, "Failed to compute eigenvalues and eigenvectors of augmented quadratic form matrix for constraint <%s>.\n", SCIPconsGetName(cons));
5455       goto CLEANUP;
5456    }
5457 
5458    /* check if there is exactly one positive and one negative eigenvalue */
5459    posidx = -1;
5460    negidx = -1;
5461    for( i = 0; i < n; ++i )
5462    {
5463       if( SCIPisPositive(scip, eigvals[i]) )
5464       {
5465          if( posidx == -1 )
5466             posidx = i;
5467          else
5468             break;
5469       }
5470       else if( SCIPisNegative(scip, eigvals[i]) )
5471       {
5472          if( negidx == -1 )
5473             negidx = i;
5474          else
5475             break;
5476       }
5477    }
5478    if( i < n || posidx == -1 || negidx == -1 )
5479    {
5480       SCIPdebugMsg(scip, "Augmented quadratic form of constraint <%s> is not factorable.\n", SCIPconsGetName(cons));
5481       goto CLEANUP;
5482    }
5483    assert(SCIPisPositive(scip, eigvals[posidx]));
5484    assert(SCIPisNegative(scip, eigvals[negidx]));
5485 
5486    /* compute factorleft and factorright */
5487    SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->factorleft,  consdata->nquadvars + 1) );
5488    SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->factorright, consdata->nquadvars + 1) );
5489 
5490    /* eigenvectors are stored in a, inverse eigenvector matrix is transposed of a
5491     * it seems that v1 and v2 are at &a[posidx*n] and &a[negidx*n]
5492     */
5493    sigma1 = sqrt( eigvals[posidx]);
5494    sigma2 = sqrt(-eigvals[negidx]);
5495    for( i = 0; i < n; ++i )
5496    {
5497       consdata->factorleft[i]  = sigma1 * a[posidx * n + i] - sigma2 * a[negidx * n + i];
5498       consdata->factorright[i] = sigma1 * a[posidx * n + i] + sigma2 * a[negidx * n + i];
5499       /* set almost-zero elements to zero */
5500       if( SCIPisZero(scip, consdata->factorleft[i]) )
5501          consdata->factorleft[i] = 0.0;
5502       if( SCIPisZero(scip, consdata->factorright[i]) )
5503          consdata->factorright[i] = 0.0;
5504    }
5505 
5506 #ifdef SCIP_DEBUG
5507    SCIPdebugMsg(scip, "constraint <%s> has factorable quadratic form: (%g", SCIPconsGetName(cons), consdata->factorleft[n-1]);
5508    for( i = 0; i < consdata->nquadvars; ++i )
5509    {
5510       if( consdata->factorleft[i] != 0.0 )
5511          SCIPdebugMsgPrint(scip, " %+g<%s>", consdata->factorleft[i], SCIPvarGetName(consdata->quadvarterms[i].var));
5512    }
5513    SCIPdebugMsgPrint(scip, ") * (%g", consdata->factorright[n-1]);
5514    for( i = 0; i < consdata->nquadvars; ++i )
5515    {
5516       if( consdata->factorright[i] != 0.0 )
5517          SCIPdebugMsgPrint(scip, " %+g<%s>", consdata->factorright[i], SCIPvarGetName(consdata->quadvarterms[i].var));
5518    }
5519    SCIPdebugMsgPrint(scip, ")\n");
5520 #endif
5521 
5522    /* check whether factorleft * factorright^T is matrix of augmented quadratic form
5523     * we check here only the nonzero entries from the quadratic form
5524     */
5525    success = TRUE;
5526 
5527    /* check bilinear terms */
5528    for( i = 0; i < consdata->nbilinterms; ++i )
5529    {
5530       bilinterm = &consdata->bilinterms[i];
5531 
5532       SCIP_CALL( consdataFindQuadVarTerm(scip, consdata, bilinterm->var1, &idx1) );
5533       SCIP_CALL( consdataFindQuadVarTerm(scip, consdata, bilinterm->var2, &idx2) );
5534 
5535       if( !SCIPisRelEQ(scip, consdata->factorleft[idx1] * consdata->factorright[idx2] + consdata->factorleft[idx2] * consdata->factorright[idx1], bilinterm->coef) )
5536       {
5537          success = FALSE;
5538          break;
5539       }
5540    }
5541 
5542    /* set lower triangular entries of A corresponding to square and linear terms */
5543    for( i = 0; i < consdata->nquadvars; ++i )
5544    {
5545       if( !SCIPisRelEQ(scip, consdata->factorleft[i] * consdata->factorright[i], consdata->quadvarterms[i].sqrcoef) )
5546       {
5547          success = FALSE;
5548          break;
5549       }
5550 
5551       if( !SCIPisRelEQ(scip, consdata->factorleft[n-1] * consdata->factorright[i] + consdata->factorleft[i] * consdata->factorright[n-1], consdata->quadvarterms[i].lincoef) )
5552       {
5553          success = FALSE;
5554          break;
5555       }
5556    }
5557 
5558    if( !success )
5559    {
5560       SCIPdebugMsg(scip, "Factorization not accurate enough. Dropping it.\n");
5561       SCIPfreeBlockMemoryArray(scip, &consdata->factorleft,  consdata->nquadvars + 1);
5562       SCIPfreeBlockMemoryArray(scip, &consdata->factorright, consdata->nquadvars + 1);
5563    }
5564 
5565  CLEANUP:
5566    SCIPfreeBufferArray(scip, &eigvals);
5567    SCIPfreeBufferArray(scip, &a);
5568 
5569    return SCIP_OKAY;
5570 }
5571 
5572 /** computes activity and violation of a constraint
5573  *
5574  * If solution violates bounds by more than feastol, the violation is still computed, but *solviolbounds is set to TRUE
5575  */
5576 static
computeViolation(SCIP * scip,SCIP_CONS * cons,SCIP_SOL * sol,SCIP_Bool * solviolbounds)5577 SCIP_RETCODE computeViolation(
5578    SCIP*                 scip,               /**< SCIP data structure */
5579    SCIP_CONS*            cons,               /**< constraint */
5580    SCIP_SOL*             sol,                /**< solution or NULL if LP solution should be used */
5581    SCIP_Bool*            solviolbounds       /**< buffer to store whether quadratic variables in solution are outside their bounds by more than feastol */
5582    )
5583 {  /*lint --e{666}*/
5584    SCIP_CONSDATA* consdata;
5585    SCIP_Real varval;
5586    SCIP_Real varval2;
5587    SCIP_Real absviol;
5588    SCIP_Real relviol;
5589    SCIP_VAR* var;
5590    SCIP_VAR* var2;
5591    int i;
5592    int j;
5593 
5594    assert(scip != NULL);
5595    assert(cons != NULL);
5596    assert(solviolbounds != NULL);
5597 
5598    consdata = SCIPconsGetData(cons);
5599    assert(consdata != NULL);
5600 
5601    *solviolbounds = FALSE;
5602    consdata->activity = 0.0;
5603    consdata->lhsviol = 0.0;
5604    consdata->rhsviol = 0.0;
5605 
5606    for( i = 0; i < consdata->nlinvars; ++i )
5607    {
5608       SCIP_Real activity;
5609 
5610       var = consdata->linvars[i];
5611       varval = SCIPgetSolVal(scip, sol, var);
5612       activity = consdata->lincoefs[i] * varval;
5613 
5614       /* the contribution of a variable with |varval| = +inf is +inf when activity > 0.0, -inf when activity < 0.0, and
5615        * 0.0 otherwise
5616        */
5617       if( SCIPisInfinity(scip, REALABS(varval)) )
5618       {
5619          if( activity > 0.0 && !SCIPisInfinity(scip, consdata->rhs) )
5620          {
5621             consdata->activity = SCIPinfinity(scip);
5622             consdata->rhsviol = SCIPinfinity(scip);
5623             return SCIP_OKAY;
5624          }
5625 
5626          if( activity < 0.0 && !SCIPisInfinity(scip, -consdata->lhs) )
5627          {
5628             consdata->activity = -SCIPinfinity(scip);
5629             consdata->lhsviol = SCIPinfinity(scip);
5630             return SCIP_OKAY;
5631          }
5632       }
5633 
5634       consdata->activity += activity;
5635    }
5636 
5637    for( j = 0; j < consdata->nquadvars; ++j )
5638    {
5639       SCIP_Real activity;
5640 
5641       var = consdata->quadvarterms[j].var;
5642       varval = SCIPgetSolVal(scip, sol, var);
5643       activity = (consdata->quadvarterms[j].lincoef + consdata->quadvarterms[j].sqrcoef * varval) * varval;
5644 
5645       /* the contribution of a variable with |varval| = +inf is +inf when activity > 0.0, -inf when activity < 0.0, and
5646        * 0.0 otherwise
5647        */
5648       if( SCIPisInfinity(scip, REALABS(varval)) )
5649       {
5650          if( activity > 0.0 && !SCIPisInfinity(scip, consdata->rhs) )
5651          {
5652             consdata->activity = SCIPinfinity(scip);
5653             consdata->rhsviol = SCIPinfinity(scip);
5654             return SCIP_OKAY;
5655          }
5656 
5657          if( activity < 0.0 && !SCIPisInfinity(scip, -consdata->lhs) )
5658          {
5659             consdata->activity = -SCIPinfinity(scip);
5660             consdata->lhsviol = SCIPinfinity(scip);
5661             return SCIP_OKAY;
5662          }
5663       }
5664 
5665       /* project onto local box, in case the LP solution is slightly outside the bounds (which is not our job to enforce) */
5666       if( sol == NULL )
5667       {
5668          /* with non-initial columns, variables can shortly be a column variable before entering the LP and have value 0.0 in this case, which might violated the variable bounds */
5669          if( (!SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) && !SCIPisFeasGE(scip, varval, SCIPvarGetLbLocal(var))) ||
5670              (!SCIPisInfinity(scip,  SCIPvarGetUbLocal(var)) && !SCIPisFeasLE(scip, varval, SCIPvarGetUbLocal(var))) )
5671             *solviolbounds = TRUE;
5672          else
5673          {
5674             varval = MAX(SCIPvarGetLbLocal(var), MIN(SCIPvarGetUbLocal(var), varval));
5675             activity = (consdata->quadvarterms[j].lincoef + consdata->quadvarterms[j].sqrcoef * varval) * varval;
5676          }
5677       }
5678 
5679       consdata->activity += activity;
5680    }
5681 
5682    for( j = 0; j < consdata->nbilinterms; ++j )
5683    {
5684       SCIP_Real activity;
5685 
5686       var = consdata->bilinterms[j].var1;
5687       var2 = consdata->bilinterms[j].var2;
5688       varval = SCIPgetSolVal(scip, sol, var);
5689       varval2 = SCIPgetSolVal(scip, sol, var2);
5690 
5691       /* project onto local box, in case the LP solution is slightly outside the bounds (which is not our job to enforce) */
5692       if( sol == NULL )
5693       {
5694          /* with non-initial columns, variables can shortly be a column variable before entering the LP and have value 0.0 in this case, which might violated the variable bounds */
5695          if( (!SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) && !SCIPisFeasGE(scip, varval, SCIPvarGetLbLocal(var))) ||
5696              (!SCIPisInfinity(scip,  SCIPvarGetUbLocal(var)) && !SCIPisFeasLE(scip, varval, SCIPvarGetUbLocal(var))) )
5697             *solviolbounds = TRUE;
5698          else
5699             varval = MAX(SCIPvarGetLbLocal(var), MIN(SCIPvarGetUbLocal(var), varval));
5700 
5701          /* with non-initial columns, variables can shortly be a column variable before entering the LP and have value 0.0 in this case, which might violated the variable bounds */
5702          if( (!SCIPisInfinity(scip, -SCIPvarGetLbLocal(var2)) && !SCIPisFeasGE(scip, varval2, SCIPvarGetLbLocal(var2))) ||
5703              (!SCIPisInfinity(scip,  SCIPvarGetUbLocal(var2)) && !SCIPisFeasLE(scip, varval2, SCIPvarGetUbLocal(var2))) )
5704             *solviolbounds = TRUE;
5705          else
5706             varval2 = MAX(SCIPvarGetLbLocal(var2), MIN(SCIPvarGetUbLocal(var2), varval2));
5707       }
5708 
5709       activity = consdata->bilinterms[j].coef * varval * varval2;
5710 
5711       /* consider var*var2 as a new variable and handle it as it would appear linearly */
5712       if( SCIPisInfinity(scip, REALABS(varval*varval2)) )
5713       {
5714          if( activity > 0.0 && !SCIPisInfinity(scip, consdata->rhs) )
5715          {
5716             consdata->activity = SCIPinfinity(scip);
5717             consdata->rhsviol = SCIPinfinity(scip);
5718             return SCIP_OKAY;
5719          }
5720 
5721          if( activity < 0.0 && !SCIPisInfinity(scip, -consdata->lhs) )
5722          {
5723             consdata->activity = -SCIPinfinity(scip);
5724             consdata->lhsviol = SCIPinfinity(scip);
5725             return SCIP_OKAY;
5726          }
5727       }
5728 
5729       consdata->activity += activity;
5730    }
5731 
5732    absviol = 0.0;
5733    relviol = 0.0;
5734    /* compute absolute violation left hand side */
5735    if( consdata->activity < consdata->lhs && !SCIPisInfinity(scip, -consdata->lhs) )
5736    {
5737       consdata->lhsviol = consdata->lhs - consdata->activity;
5738       absviol = consdata->lhsviol;
5739       relviol = SCIPrelDiff(consdata->lhs, consdata->activity);
5740    }
5741    else
5742       consdata->lhsviol = 0.0;
5743 
5744    /* compute absolute violation right hand side */
5745    if( consdata->activity > consdata->rhs && !SCIPisInfinity(scip,  consdata->rhs) )
5746    {
5747       consdata->rhsviol = consdata->activity - consdata->rhs;
5748       absviol = consdata->rhsviol;
5749       relviol = SCIPrelDiff(consdata->activity, consdata->rhs);
5750    }
5751    else
5752       consdata->rhsviol = 0.0;
5753 
5754    /* update absolute and relative violation of the solution */
5755    if( sol != NULL )
5756       SCIPupdateSolConsViolation(scip, sol, absviol, relviol);
5757 
5758    return SCIP_OKAY;
5759 }
5760 
5761 /** computes violation of a set of constraints */
5762 static
computeViolations(SCIP * scip,SCIP_CONS ** conss,int nconss,SCIP_SOL * sol,SCIP_Bool * solviolbounds,SCIP_CONS ** maxviolcon)5763 SCIP_RETCODE computeViolations(
5764    SCIP*                 scip,               /**< SCIP data structure */
5765    SCIP_CONS**           conss,              /**< constraints */
5766    int                   nconss,             /**< number of constraints */
5767    SCIP_SOL*             sol,                /**< solution or NULL if LP solution should be used */
5768    SCIP_Bool*            solviolbounds,      /**< buffer to store whether quadratic variables in solution are outside their bounds by more than feastol in some constraint */
5769    SCIP_CONS**           maxviolcon          /**< buffer to store constraint with largest violation, or NULL if solution is feasible */
5770    )
5771 {
5772    SCIP_CONSDATA* consdata;
5773    SCIP_Real      viol;
5774    SCIP_Real      maxviol;
5775    SCIP_Bool      solviolbounds1;
5776    int            c;
5777 
5778    assert(scip != NULL);
5779    assert(conss != NULL || nconss == 0);
5780    assert(solviolbounds != NULL);
5781    assert(maxviolcon != NULL);
5782 
5783    *solviolbounds = FALSE;
5784    *maxviolcon = NULL;
5785 
5786    maxviol = 0.0;
5787 
5788    for( c = 0; c < nconss; ++c )
5789    {
5790       assert(conss != NULL);
5791       assert(conss[c] != NULL);
5792 
5793       SCIP_CALL( computeViolation(scip, conss[c], sol, &solviolbounds1) );
5794       *solviolbounds |= solviolbounds1;
5795 
5796       consdata = SCIPconsGetData(conss[c]);
5797       assert(consdata != NULL);
5798 
5799       viol = MAX(consdata->lhsviol, consdata->rhsviol);
5800       if( viol > maxviol && SCIPisGT(scip, viol, SCIPfeastol(scip)) )
5801       {
5802          maxviol = viol;
5803          *maxviolcon = conss[c];
5804       }
5805    }
5806 
5807    return SCIP_OKAY;
5808 }
5809 
5810 
5811 /** index comparison method for bilinear terms */
5812 static
SCIP_DECL_SORTINDCOMP(bilinTermComp2)5813 SCIP_DECL_SORTINDCOMP(bilinTermComp2)
5814 {  /*lint --e{715}*/
5815    SCIP_BILINTERM* bilinterms = (SCIP_BILINTERM*)dataptr;
5816    int var1cmp;
5817 
5818    assert(bilinterms != NULL);
5819 
5820    var1cmp = SCIPvarCompare(bilinterms[ind1].var1, bilinterms[ind2].var1);
5821    if( var1cmp != 0 )
5822       return var1cmp;
5823 
5824    return SCIPvarCompare(bilinterms[ind1].var2, bilinterms[ind2].var2);
5825 }
5826 
5827 /** volume comparison method for bilinear terms; prioritizes bilinear products with a larger volume */
5828 static
SCIP_DECL_SORTINDCOMP(bilinTermCompVolume)5829 SCIP_DECL_SORTINDCOMP(bilinTermCompVolume)
5830 {  /*lint --e{715}*/
5831    SCIP_BILINTERM* bilinterms = (SCIP_BILINTERM*)dataptr;
5832    SCIP_Real vol1;
5833    SCIP_Real vol2;
5834 
5835    assert(bilinterms != NULL);
5836 
5837    vol1 = (SCIPvarGetUbLocal(bilinterms[ind1].var1) - SCIPvarGetLbLocal(bilinterms[ind1].var1))
5838       * (SCIPvarGetUbLocal(bilinterms[ind1].var2) - SCIPvarGetLbLocal(bilinterms[ind1].var2));
5839    vol2 = (SCIPvarGetUbLocal(bilinterms[ind2].var1) - SCIPvarGetLbLocal(bilinterms[ind2].var1))
5840       * (SCIPvarGetUbLocal(bilinterms[ind2].var2) - SCIPvarGetLbLocal(bilinterms[ind2].var2));
5841 
5842    if( vol1 > vol2 )
5843       return -1;
5844    else if( vol1 < vol2 )
5845       return 1;
5846    return bilinTermComp2(dataptr, ind1, ind2);
5847 }
5848 
5849 /** helper function to sort all bilinear terms in the constraint handler data */
5850 static
sortAllBilinTerms(SCIP * scip,SCIP_BILINTERM * bilinterms,int nbilinterms,SCIP_CONS ** bilinconss,int * bilinposs)5851 SCIP_RETCODE sortAllBilinTerms(
5852    SCIP*                 scip,               /**< SCIP data structure */
5853    SCIP_BILINTERM*       bilinterms,         /**< array containing all bilinear terms */
5854    int                   nbilinterms,        /**< total number of bilinear terms */
5855    SCIP_CONS**           bilinconss,         /**< array for mapping each term to its constraint */
5856    int*                  bilinposs           /**< array for mapping each term to its position in the corresponding
5857                                               *   bilinconss constraint */
5858    )
5859 {
5860    int* perm;
5861    int  i;
5862    int  nexti;
5863    int  v;
5864    SCIP_BILINTERM bilinterm;
5865    SCIP_CONS* bilincons;
5866    int bilinpos;
5867 
5868    assert(scip != NULL);
5869    assert(bilinterms != NULL);
5870    assert(nbilinterms > 0);
5871    assert(bilinconss != NULL);
5872    assert(bilinposs != NULL);
5873 
5874    /* get temporary memory to store the sorted permutation and the inverse permutation */
5875    SCIP_CALL( SCIPallocBufferArray(scip, &perm, nbilinterms) );
5876 
5877    /* call quicksort */
5878    SCIPsort(perm, bilinTermCompVolume, (void*)bilinterms, nbilinterms);
5879 
5880    /* permute the bilinear terms according to the resulting permutation */
5881    for( v = 0; v < nbilinterms; ++v )
5882    {
5883       if( perm[v] != v )
5884       {
5885          bilinterm = bilinterms[v];
5886          bilincons = bilinconss[v];
5887          bilinpos = bilinposs[v];
5888 
5889          i = v;
5890          do
5891          {
5892             assert(0 <= perm[i] && perm[i] < nbilinterms);
5893             assert(perm[i] != i);
5894 
5895             bilinterms[i] = bilinterms[perm[i]];
5896             bilinconss[i] = bilinconss[perm[i]];
5897             bilinposs[i] = bilinposs[perm[i]];
5898 
5899             nexti = perm[i];
5900             perm[i] = i;
5901             i = nexti;
5902          }
5903          while( perm[i] != v );
5904          bilinterms[i] = bilinterm;
5905          bilinconss[i] = bilincons;
5906          bilinposs[i] = bilinpos;
5907          perm[i] = i;
5908       }
5909    }
5910 
5911    /* free temporary memory */
5912    SCIPfreeBufferArray(scip, &perm);
5913 
5914    return SCIP_OKAY;
5915 }
5916 
5917 /** stores all bilinear terms in the quadratic constraint handler data; in addition, for each bilinear term we store
5918  *  the number of nonconvex constraints that require to over- or underestimate this term, which only depends on the
5919  *  lhs, rhs, and the bilinear coefficient
5920  */
5921 static
storeAllBilinearTerms(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_CONS ** conss,int nconss)5922 SCIP_RETCODE storeAllBilinearTerms(
5923    SCIP*                 scip,               /**< SCIP data structure */
5924    SCIP_CONSHDLRDATA*    conshdlrdata,       /**< constraint handler data */
5925    SCIP_CONS**           conss,              /**< constraints to process */
5926    int                   nconss              /**< number of constraints */
5927    )
5928 {
5929    SCIP_BILINTERM* bilinterms;
5930    SCIP_CONS** bilincons;
5931    int* bilinpos;
5932    int nbilinterms;
5933    int pos;
5934    int c;
5935    int i;
5936 
5937    assert(scip != NULL);
5938    assert(conshdlrdata != NULL);
5939    assert(conss != NULL);
5940 
5941    /* check for all cases for which we don't want to spend time for collecting all bilinear terms */
5942    if( nconss == 0 || conshdlrdata->storedbilinearterms || SCIPgetSubscipDepth(scip) != 0 || SCIPgetDepth(scip) >= 1
5943       || SCIPinProbing(scip) || SCIPinDive(scip) )
5944       return SCIP_OKAY;
5945 
5946    assert(conshdlrdata->bilinestimators == NULL);
5947    assert(conshdlrdata->nbilinterms == 0);
5948 
5949    conshdlrdata->storedbilinearterms = TRUE;
5950    nbilinterms = 0;
5951 
5952    /* count the number of bilinear terms (including duplicates) */
5953    for( c = 0; c < nconss; ++c )
5954    {
5955       SCIP_CONSDATA* consdata = SCIPconsGetData(conss[c]);
5956       assert(consdata != NULL);
5957       nbilinterms += consdata->nbilinterms;
5958    }
5959 
5960    /* no bilinear terms available -> stop */
5961    if( nbilinterms == 0 )
5962       return SCIP_OKAY;
5963 
5964    /* allocate temporary memory for sorting all bilinear terms (including duplicates) */
5965    SCIP_CALL( SCIPallocBufferArray(scip, &bilinterms, nbilinterms) );
5966    SCIP_CALL( SCIPallocBufferArray(scip, &bilincons, nbilinterms) );
5967    SCIP_CALL( SCIPallocBufferArray(scip, &bilinpos, nbilinterms) );
5968 
5969    /* copy all bilinear terms; note that we need separate entries for x*y and y*x */
5970    pos = 0;
5971    for( c = 0; c < nconss; ++c )
5972    {
5973       SCIP_CONSDATA* consdata = SCIPconsGetData(conss[c]);
5974 
5975       /* allocate memory to store the later computed indices of each bilinear term in the bilinterms array of the
5976        * constraint handler data
5977        */
5978       if( consdata->nbilinterms > 0 )
5979       {
5980          SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->bilintermsidx, consdata->nbilinterms) );
5981       }
5982 
5983       for( i = 0; i < consdata->nbilinterms; ++i )
5984       {
5985          assert(consdata->bilinterms != NULL);
5986          assert(consdata->bilinterms[i].var1 != consdata->bilinterms[i].var2);
5987 
5988          /* add xy */
5989          bilinterms[pos] = consdata->bilinterms[i];
5990          bilincons[pos] = conss[c];
5991          bilinpos[pos] = i;
5992          ++pos;
5993 
5994          /* invalidate bilinear term index */
5995          assert(consdata->bilintermsidx != NULL);
5996          consdata->bilintermsidx[i] = -1;
5997       }
5998    }
5999    assert(pos == nbilinterms);
6000 
6001    /* sorts all bilinear terms (including duplicates) */
6002    SCIP_CALL( sortAllBilinTerms(scip, bilinterms, nbilinterms, bilincons, bilinpos) );
6003 
6004    /* count the number of bilinear terms without duplicates */
6005    conshdlrdata->nbilinterms = nbilinterms;
6006    for( i = 0; i < nbilinterms - 1; ++i )
6007    {
6008       assert(bilinTermCompVolume((void*)bilinterms, i, i+1) != 0 || bilinTermComp2((void*)bilinterms, i, i+1) <= 0);
6009 
6010       if( bilinTermComp2((void*)bilinterms, i, i+1) == 0 )
6011          --(conshdlrdata->nbilinterms);
6012    }
6013    assert(conshdlrdata->nbilinterms <= nbilinterms && conshdlrdata->nbilinterms > 0);
6014 
6015    /* store all information for each bilinear term into the constraint handler data */
6016    SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &conshdlrdata->bilinestimators, conshdlrdata->nbilinterms) );
6017 
6018    /* filter duplicates and update entries in the corresponding constraint datas */
6019    pos = 0;
6020    for( i = 0; i < nbilinterms; ++i )
6021    {
6022       SCIP_CONSDATA* consdata = SCIPconsGetData(bilincons[i]);
6023       SCIP_VAR* x;
6024       SCIP_Bool haslhs = !SCIPisInfinity(scip, -consdata->lhs);
6025       SCIP_Bool hasrhs = !SCIPisInfinity(scip, consdata->rhs);
6026 
6027       assert(consdata != NULL);
6028       assert(bilinpos[i] >= 0 && bilinpos[i] < consdata->nbilinterms);
6029 
6030       /* check for a new bilinear term */
6031       if( i == 0 || bilinTermComp2((void*)bilinterms, i-1, i) != 0 )
6032       {
6033          conshdlrdata->bilinestimators[pos].x = bilinterms[i].var1;
6034          conshdlrdata->bilinestimators[pos].y = bilinterms[i].var2;
6035          conshdlrdata->bilinestimators[pos].lastimprfac = 0.0;
6036          conshdlrdata->bilinestimators[pos].maxnonconvexity = 0.0;
6037          ++pos;
6038       }
6039 
6040       /* store whether under- or overestimation is needed for each bilinear term; note that we do not consider convex
6041        * constraints because they will not be used in separated generateCutNonConvex(), which is the only function that
6042        * uses a term-wise relaxation
6043        */
6044       if( SCIPisPositive(scip, bilinterms[i].coef) )
6045       {
6046          conshdlrdata->bilinestimators[pos-1].nunderest += (hasrhs && !consdata->isconvex) ? 1 : 0;
6047          conshdlrdata->bilinestimators[pos-1].noverest += (haslhs && !consdata->isconcave) ? 1 : 0;
6048          conshdlrdata->bilinestimators[pos-1].maxnonconvexity = MAX(conshdlrdata->bilinestimators[pos-1].maxnonconvexity, consdata->maxnonconvexity);
6049       }
6050       else
6051       {
6052          assert(SCIPisNegative(scip, bilinterms[i].coef));
6053          conshdlrdata->bilinestimators[pos-1].nunderest += (haslhs && !consdata->isconcave) ? 1 : 0;
6054          conshdlrdata->bilinestimators[pos-1].noverest += (hasrhs && !consdata->isconvex) ? 1 : 0;
6055          conshdlrdata->bilinestimators[pos-1].maxnonconvexity = MAX(conshdlrdata->bilinestimators[pos-1].maxnonconvexity, consdata->maxnonconvexity);
6056       }
6057 
6058       /* update index of bilinear term in the constraint data */
6059       x = consdata->bilinterms[bilinpos[i]].var1;
6060 
6061       assert(pos > 0);
6062       if( x == conshdlrdata->bilinestimators[pos-1].x )
6063       {
6064          assert(consdata->bilinterms[bilinpos[i]].var2 == conshdlrdata->bilinestimators[pos-1].y);
6065          consdata->bilintermsidx[bilinpos[i]] = pos-1;
6066       }
6067    }
6068    assert(pos == conshdlrdata->nbilinterms);
6069 
6070 #ifndef NDEBUG
6071    /* check whether
6072     * - all bilintermsidx entries have been set
6073     * - variables in bilinear terms of each constraint data and the constraint handler data match
6074     */
6075    for( c = 0; c < nconss; ++c )
6076    {
6077       SCIP_CONSDATA* consdata = SCIPconsGetData(conss[c]);
6078       assert(consdata != NULL);
6079 
6080       for( i = 0; i < consdata->nbilinterms; ++i )
6081       {
6082          SCIP_VAR* x = consdata->bilinterms[i].var1;
6083          SCIP_VAR* y = consdata->bilinterms[i].var2;
6084          int idx = consdata->bilintermsidx[i];
6085 
6086          assert(idx >= 0 && idx < conshdlrdata->nbilinterms);
6087          assert(x == conshdlrdata->bilinestimators[idx].x);
6088          assert(y == conshdlrdata->bilinestimators[idx].y);
6089 
6090          /* at least one direction is important if the constraint is not convex */
6091          if( !SCIPisInfinity(scip, consdata->rhs) && !consdata->isconvex )
6092             assert(conshdlrdata->bilinestimators[idx].nunderest + conshdlrdata->bilinestimators[idx].noverest > 0);
6093          if( !SCIPisInfinity(scip, -consdata->lhs) && !consdata->isconcave )
6094             assert(conshdlrdata->bilinestimators[idx].nunderest + conshdlrdata->bilinestimators[idx].noverest > 0);
6095       }
6096    }
6097 #endif
6098 
6099    /* free memory */
6100    SCIPfreeBufferArray(scip, &bilinpos);
6101    SCIPfreeBufferArray(scip, &bilincons);
6102    SCIPfreeBufferArray(scip, &bilinterms);
6103 
6104    return SCIP_OKAY;
6105 }
6106 
6107 /** frees memory allocated in storeAllBilinearTerms() */
6108 static
freeAllBilinearTerms(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_CONS ** conss,int nconss)6109 SCIP_RETCODE freeAllBilinearTerms(
6110    SCIP*                 scip,               /**< SCIP data structure */
6111    SCIP_CONSHDLRDATA*    conshdlrdata,       /**< constraint handler data */
6112    SCIP_CONS**           conss,              /**< constraints to process */
6113    int                   nconss              /**< number of constraints */
6114 
6115    )
6116 {
6117    int c;
6118 
6119    assert(conshdlrdata != NULL);
6120 
6121    for( c = 0; c < nconss; ++c )
6122    {
6123       SCIP_CONSDATA* consdata = SCIPconsGetData(conss[c]); /*lint !e613*/
6124       assert(consdata != NULL);
6125 
6126       SCIPfreeBlockMemoryArrayNull(scip, &consdata->bilintermsidx, consdata->nbilinterms);
6127    }
6128 
6129    SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->bilinestimators, conshdlrdata->nbilinterms);
6130 
6131    conshdlrdata->nbilinterms = 0;
6132    conshdlrdata->storedbilinearterms = FALSE;
6133 
6134    return SCIP_OKAY;
6135 }
6136 
6137 /** tries to compute cut for multleft * <coefleft, x'> * multright <= rhs / (multright * <coefright, x'>) where x'=(x,1) */
6138 static
generateCutFactorableDo(SCIP * scip,SCIP_CONS * cons,SCIP_Real * ref,SCIP_Real multleft,SCIP_Real * coefleft,SCIP_Real multright,SCIP_Real * coefright,SCIP_Real rightminactivity,SCIP_Real rightmaxactivity,SCIP_Real rhs,SCIP_ROWPREP * rowprep,SCIP_Bool * success)6139 SCIP_RETCODE generateCutFactorableDo(
6140    SCIP*                 scip,               /**< SCIP data structure */
6141    SCIP_CONS*            cons,               /**< constraint */
6142    SCIP_Real*            ref,                /**< reference solution where to generate the cut */
6143    SCIP_Real             multleft,           /**< multiplicator on lhs */
6144    SCIP_Real*            coefleft,           /**< coefficient for factor on lhs */
6145    SCIP_Real             multright,          /**< multiplicator on both sides */
6146    SCIP_Real*            coefright,          /**< coefficient for factor that goes to rhs */
6147    SCIP_Real             rightminactivity,   /**< minimal activity of <coefright, x> */
6148    SCIP_Real             rightmaxactivity,   /**< maximal activity of <coefright, x> */
6149    SCIP_Real             rhs,                /**< denominator on rhs */
6150    SCIP_ROWPREP*         rowprep,            /**< rowprep to store cut coefs and constant */
6151    SCIP_Bool*            success             /**< buffer to indicate whether a cut was successfully computed */
6152    )
6153 {
6154    SCIP_CONSDATA* consdata;
6155    SCIP_Real constant;
6156    SCIP_Real coef;
6157    int i;
6158 
6159    assert(rowprep != NULL);
6160    assert(rightminactivity * multright > 0.0);
6161    assert(rightmaxactivity * multright > 0.0);
6162    assert(multright == 1.0 || multright == -1.0);
6163 
6164    consdata = SCIPconsGetData(cons);
6165    assert(consdata != NULL);
6166 
6167    rowprep->sidetype = SCIP_SIDETYPE_RIGHT;
6168 
6169    if( rhs > 0.0 )
6170    {
6171       /* if rhs > 0.0, then rhs / (multright * <coefright, x'>) is convex, thus need secant:
6172        *  1 / multright*<coefright, x'> <= 1/minact + 1/maxact - 1/(minact * maxact) multright*<coefright, x'>
6173        *  where [minact, maxact] = multright * [rightminactivity, rightmaxactivity]
6174        *
6175        * assuming multright is either -1 or 1, and substituting gives
6176        *  multright/rightminactivity + multright/rightmaxactivity  - multright/(rightminactivity * rightmaxactivity) *<coefright, x'>
6177        *
6178        * multiplying by rhs, gives the estimate
6179        *  rhs / (multright * <coefright, x'>) <= rhs * multright * (1/rightminactivity + 1/rightmaxactivity - 1/(rightminactivity * rightmaxactivity) * <coefright, x'>)
6180        */
6181 
6182       /* cannot do if unbounded */
6183       if( SCIPisInfinity(scip, rightmaxactivity * multright) )
6184       {
6185          *success = FALSE;
6186          return SCIP_OKAY;
6187       }
6188 
6189       assert(SCIPisFeasLE(scip, rightminactivity, rightmaxactivity));
6190 
6191       constant = multleft * multright * coefleft[consdata->nquadvars];
6192       constant -= rhs * multright * (1.0 / rightminactivity + 1.0 / rightmaxactivity);
6193       constant += rhs * multright * coefright[consdata->nquadvars] / (rightminactivity * rightmaxactivity);
6194 
6195       SCIPaddRowprepConstant(rowprep, constant);
6196 
6197       for( i = 0; i < consdata->nquadvars; ++i )
6198       {
6199          coef = multleft * multright * coefleft[i];
6200          coef += rhs * multright / (rightminactivity * rightmaxactivity) * coefright[i];
6201          SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, consdata->quadvarterms[i].var, coef) );
6202       }
6203 
6204       (void) SCIPsnprintf(rowprep->name, SCIP_MAXSTRLEN, "%s_factorablesecant_%" SCIP_LONGINT_FORMAT, SCIPconsGetName(cons), SCIPgetNLPs(scip));
6205 
6206       rowprep->local = TRUE;
6207    }
6208    else
6209    {
6210       SCIP_Real refvalue;
6211 
6212       /* if rhs < 0.0, then rhs / (multright * <coefright, x'>) is convex, thus need linearization:
6213        *     rhs / (multright * <coefright, x'>)
6214        *  <= rhs / (multright * <coefright, ref'>) - rhs / (multright * <coefright, ref'>)^2 * (multright * <coefright, x'> - multright * <coefright, ref'>)
6215        *   = 2*rhs / (multright * <coefright, ref'>) - rhs / (multright * <coefright, ref'>)^2 * (multright * <coefright, x'>)
6216        *
6217        *  where ref' = (ref, 1)
6218        */
6219 
6220       /* compute <coefright, ref'> */
6221       refvalue = coefright[consdata->nquadvars];
6222       for( i = 0; i < consdata->nquadvars; ++i )
6223          refvalue += coefright[i] * ref[i];
6224 
6225       /* should not happen, since we checked activity of <coefright,x> before, and assume ref within bounds */
6226       assert(!SCIPisZero(scip, refvalue));
6227 
6228       constant  = multleft * multright * coefleft[consdata->nquadvars];
6229       constant -= 2.0 * rhs / (multright * refvalue);
6230       constant += rhs / (refvalue * refvalue) * multright * coefright[consdata->nquadvars];
6231 
6232       SCIPaddRowprepConstant(rowprep, constant);
6233 
6234       for( i = 0; i < consdata->nquadvars; ++i )
6235       {
6236          coef = multleft * multright * coefleft[i];
6237          coef += rhs / (refvalue * refvalue) * multright * coefright[i];
6238          SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, consdata->quadvarterms[i].var, coef) );
6239       }
6240 
6241       (void) SCIPsnprintf(rowprep->name, SCIP_MAXSTRLEN, "%s_factorablelinearization_%" SCIP_LONGINT_FORMAT, SCIPconsGetName(cons), SCIPgetNLPs(scip));
6242    }
6243 
6244    *success = TRUE;
6245 
6246    return SCIP_OKAY;
6247 }
6248 
6249 /** tries to generate a cut if constraint quadratic function is factorable and there are no linear variables
6250  * (ax+b)(cx+d) <= rhs and cx+d >= 0 -> (ax+b) <= rhs / (cx+d), where the right hand side is concave and can be linearized
6251  */
6252 static
generateCutFactorable(SCIP * scip,SCIP_CONS * cons,SCIP_SIDETYPE violside,SCIP_Real * ref,SCIP_ROWPREP * rowprep,SCIP_Bool * success)6253 SCIP_RETCODE generateCutFactorable(
6254    SCIP*                 scip,               /**< SCIP data structure */
6255    SCIP_CONS*            cons,               /**< constraint */
6256    SCIP_SIDETYPE         violside,           /**< for which side a cut should be generated */
6257    SCIP_Real*            ref,                /**< reference solution where to generate the cut */
6258    SCIP_ROWPREP*         rowprep,            /**< data structure to store cut coefficients */
6259    SCIP_Bool*            success             /**< buffer to indicate whether a cut was successfully computed */
6260    )
6261 {
6262    SCIP_CONSDATA* consdata;
6263    SCIP_Real leftminactivity;
6264    SCIP_Real leftmaxactivity;
6265    SCIP_Real rightminactivity;
6266    SCIP_Real rightmaxactivity;
6267    SCIP_Real leftminactivityglobal;
6268    SCIP_Real leftmaxactivityglobal;
6269    SCIP_Real rightminactivityglobal;
6270    SCIP_Real rightmaxactivityglobal;
6271    SCIP_Real multleft;
6272    SCIP_Real multright;
6273    SCIP_Real rhs;
6274    int i;
6275 
6276    assert(scip != NULL);
6277    assert(cons != NULL);
6278    assert(ref  != NULL);
6279    assert(rowprep != NULL);
6280    assert(success != NULL);
6281 
6282    consdata = SCIPconsGetData(cons);
6283    assert(consdata != NULL);
6284    assert(consdata->nlinvars == 0);
6285    assert(consdata->factorleft != NULL);
6286    assert(consdata->factorright != NULL);
6287 
6288    *success = FALSE;
6289 
6290    leftminactivityglobal  = leftminactivity  = consdata->factorleft[consdata->nquadvars];
6291    leftmaxactivityglobal  = leftmaxactivity  = consdata->factorleft[consdata->nquadvars];
6292    rightminactivityglobal = rightminactivity = consdata->factorright[consdata->nquadvars];
6293    rightmaxactivityglobal = rightmaxactivity = consdata->factorright[consdata->nquadvars];
6294    for( i = 0; i < consdata->nquadvars; ++i )
6295    {
6296       if( !SCIPisInfinity(scip, -leftminactivity) )
6297       {
6298          if( consdata->factorleft[i] > 0.0 )
6299          {
6300             if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(consdata->quadvarterms[i].var)) )
6301                leftminactivity = -SCIPinfinity(scip);
6302             else
6303                leftminactivity += consdata->factorleft[i] * SCIPvarGetLbLocal(consdata->quadvarterms[i].var);
6304          }
6305          else if( consdata->factorleft[i] < 0.0 )
6306          {
6307             if( SCIPisInfinity(scip, SCIPvarGetUbLocal(consdata->quadvarterms[i].var)) )
6308                leftminactivity = -SCIPinfinity(scip);
6309             else
6310                leftminactivity += consdata->factorleft[i] * SCIPvarGetUbLocal(consdata->quadvarterms[i].var);
6311          }
6312       }
6313       if( !SCIPisInfinity(scip, leftmaxactivity) )
6314       {
6315          if( consdata->factorleft[i] > 0.0 )
6316          {
6317             if( SCIPisInfinity(scip, SCIPvarGetUbLocal(consdata->quadvarterms[i].var)) )
6318                leftmaxactivity = SCIPinfinity(scip);
6319             else
6320                leftmaxactivity += consdata->factorleft[i] * SCIPvarGetUbLocal(consdata->quadvarterms[i].var);
6321          }
6322          else if( consdata->factorleft[i] < 0.0 )
6323          {
6324             if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(consdata->quadvarterms[i].var)) )
6325                leftmaxactivity = SCIPinfinity(scip);
6326             else
6327                leftmaxactivity += consdata->factorleft[i] * SCIPvarGetLbLocal(consdata->quadvarterms[i].var);
6328          }
6329       }
6330 
6331       if( !SCIPisInfinity(scip, -rightminactivity) )
6332       {
6333          if( consdata->factorright[i] > 0.0 )
6334          {
6335             if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(consdata->quadvarterms[i].var)) )
6336                rightminactivity = -SCIPinfinity(scip);
6337             else
6338                rightminactivity += consdata->factorright[i] * SCIPvarGetLbLocal(consdata->quadvarterms[i].var);
6339          }
6340          else if( consdata->factorright[i] < 0.0 )
6341          {
6342             if( SCIPisInfinity(scip, SCIPvarGetUbLocal(consdata->quadvarterms[i].var)) )
6343                rightminactivity = -SCIPinfinity(scip);
6344             else
6345                rightminactivity += consdata->factorright[i] * SCIPvarGetUbLocal(consdata->quadvarterms[i].var);
6346          }
6347       }
6348       if( !SCIPisInfinity(scip, rightmaxactivity) )
6349       {
6350          if( consdata->factorright[i] > 0.0 )
6351          {
6352             if( SCIPisInfinity(scip, SCIPvarGetUbLocal(consdata->quadvarterms[i].var)) )
6353                rightmaxactivity = SCIPinfinity(scip);
6354             else
6355                rightmaxactivity += consdata->factorright[i] * SCIPvarGetUbLocal(consdata->quadvarterms[i].var);
6356          }
6357          else if( consdata->factorright[i] < 0.0 )
6358          {
6359             if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(consdata->quadvarterms[i].var)) )
6360                rightmaxactivity = SCIPinfinity(scip);
6361             else
6362                rightmaxactivity += consdata->factorright[i] * SCIPvarGetLbLocal(consdata->quadvarterms[i].var);
6363          }
6364       }
6365 
6366       if( SCIPgetDepth(scip) > 0 )
6367       {
6368          if( !SCIPisInfinity(scip, -leftminactivityglobal) )
6369          {
6370             if( consdata->factorleft[i] > 0.0 )
6371             {
6372                if( SCIPisInfinity(scip, -SCIPvarGetLbGlobal(consdata->quadvarterms[i].var)) )
6373                   leftminactivityglobal = -SCIPinfinity(scip);
6374                else
6375                   leftminactivityglobal += consdata->factorleft[i] * SCIPvarGetLbGlobal(consdata->quadvarterms[i].var);
6376             }
6377             else if( consdata->factorleft[i] < 0.0 )
6378             {
6379                if( SCIPisInfinity(scip, SCIPvarGetUbGlobal(consdata->quadvarterms[i].var)) )
6380                   leftminactivityglobal = -SCIPinfinity(scip);
6381                else
6382                   leftminactivityglobal += consdata->factorleft[i] * SCIPvarGetUbGlobal(consdata->quadvarterms[i].var);
6383             }
6384          }
6385          if( !SCIPisInfinity(scip, leftmaxactivityglobal) )
6386          {
6387             if( consdata->factorleft[i] > 0.0 )
6388             {
6389                if( SCIPisInfinity(scip, SCIPvarGetUbGlobal(consdata->quadvarterms[i].var)) )
6390                   leftmaxactivityglobal = SCIPinfinity(scip);
6391                else
6392                   leftmaxactivityglobal += consdata->factorleft[i] * SCIPvarGetUbGlobal(consdata->quadvarterms[i].var);
6393             }
6394             else if( consdata->factorleft[i] < 0.0 )
6395             {
6396                if( SCIPisInfinity(scip, -SCIPvarGetLbGlobal(consdata->quadvarterms[i].var)) )
6397                   leftmaxactivityglobal = SCIPinfinity(scip);
6398                else
6399                   leftmaxactivityglobal += consdata->factorleft[i] * SCIPvarGetLbGlobal(consdata->quadvarterms[i].var);
6400             }
6401          }
6402 
6403          if( !SCIPisInfinity(scip, -rightminactivityglobal) )
6404          {
6405             if( consdata->factorright[i] > 0.0 )
6406             {
6407                if( SCIPisInfinity(scip, -SCIPvarGetLbGlobal(consdata->quadvarterms[i].var)) )
6408                   rightminactivityglobal = -SCIPinfinity(scip);
6409                else
6410                   rightminactivityglobal += consdata->factorright[i] * SCIPvarGetLbGlobal(consdata->quadvarterms[i].var);
6411             }
6412             else if( consdata->factorright[i] < 0.0 )
6413             {
6414                if( SCIPisInfinity(scip, SCIPvarGetUbGlobal(consdata->quadvarterms[i].var)) )
6415                   rightminactivityglobal = -SCIPinfinity(scip);
6416                else
6417                   rightminactivityglobal += consdata->factorright[i] * SCIPvarGetUbGlobal(consdata->quadvarterms[i].var);
6418             }
6419          }
6420          if( !SCIPisInfinity(scip, rightmaxactivityglobal) )
6421          {
6422             if( consdata->factorright[i] > 0.0 )
6423             {
6424                if( SCIPisInfinity(scip, SCIPvarGetUbGlobal(consdata->quadvarterms[i].var)) )
6425                   rightmaxactivityglobal = SCIPinfinity(scip);
6426                else
6427                   rightmaxactivityglobal += consdata->factorright[i] * SCIPvarGetUbGlobal(consdata->quadvarterms[i].var);
6428             }
6429             else if( consdata->factorright[i] < 0.0 )
6430             {
6431                if( SCIPisInfinity(scip, -SCIPvarGetLbGlobal(consdata->quadvarterms[i].var)) )
6432                   rightmaxactivityglobal = SCIPinfinity(scip);
6433                else
6434                   rightmaxactivityglobal += consdata->factorright[i] * SCIPvarGetLbGlobal(consdata->quadvarterms[i].var);
6435             }
6436          }
6437       }
6438    }
6439 
6440    /* write violated constraints as multleft * factorleft * factorright <= rhs */
6441    if( violside == SCIP_SIDETYPE_RIGHT )
6442    {
6443       rhs = consdata->rhs;
6444       multleft = 1.0;
6445    }
6446    else
6447    {
6448       rhs = -consdata->lhs;
6449       multleft = -1.0;
6450    }
6451 
6452    if( SCIPisZero(scip, rhs) )
6453    {
6454       /* @todo do something for rhs == 0.0? */
6455       return SCIP_OKAY;
6456    }
6457 
6458    if( !SCIPisFeasPositive(scip, leftminactivity) && !SCIPisFeasNegative(scip, leftmaxactivity) )
6459    {
6460       /* left factor has 0 within activity bounds, or is very close, at least */
6461       if( !SCIPisFeasPositive(scip, rightminactivity) && !SCIPisFeasNegative(scip, rightmaxactivity) )
6462       {
6463          /* right factor also has 0 within activity bounds, or is very close, at least
6464           * -> cannot separate
6465           */
6466          return SCIP_OKAY;
6467       }
6468 
6469       /* write violated constraint as multleft * factorleft * multright * (multright * factorright) <= rhs
6470        *   such that multright * factorright > 0.0
6471        */
6472       if( rightminactivity < 0.0 )
6473          multright = -1.0;
6474       else
6475          multright =  1.0;
6476 
6477       /* generate cut for multleft * factorleft * multright <= rhs / (factorright * multright) */
6478       SCIP_CALL( generateCutFactorableDo(scip, cons, ref, multleft, consdata->factorleft, multright, consdata->factorright, rightminactivity, rightmaxactivity, rhs, rowprep, success) );
6479 
6480       /* if right factor has 0 within global activity bounds, then the added linearization is not globally valid */
6481       if( rhs < 0.0 && SCIPgetDepth(scip) > 0 && rightminactivityglobal < 0.0 && rightmaxactivityglobal > 0.0 )
6482          rowprep->local = TRUE;
6483    }
6484    else if( !SCIPisFeasPositive(scip, rightminactivity) && !SCIPisFeasNegative(scip, rightmaxactivity) )
6485    {
6486       /* left factor is bounded away from 0
6487        * right factor has 0 within activity bounds, or is very close, at least
6488        * -> so divide by left factor
6489        */
6490 
6491       /* write violated constraint as multleft * factorright * multright * (multright * factorleft) <= rhs
6492        *   such that multright * factorleft > 0.0
6493        */
6494       if( leftminactivity < 0.0 )
6495          multright = -1.0;
6496       else
6497          multright =  1.0;
6498 
6499       /* generate cut for multleft * factorright * multright <= rhs / (factorleft * multright) */
6500       SCIP_CALL( generateCutFactorableDo(scip, cons, ref, multleft, consdata->factorright, multright, consdata->factorleft, leftminactivity, leftmaxactivity, rhs, rowprep, success) );
6501 
6502       /* if left factor has 0 within global activity bounds, then the added linearization is not globally valid */
6503       if( rhs < 0.0 && SCIPgetDepth(scip) > 0 && leftminactivityglobal < 0.0 && leftmaxactivityglobal > 0.0 )
6504          rowprep->local = TRUE;
6505    }
6506    else if( SCIPisInfinity(scip, -leftminactivity) || SCIPisInfinity(scip, leftmaxactivity) ||
6507       (!SCIPisInfinity(scip, -rightminactivity) && !SCIPisInfinity(scip, rightmaxactivity) && rightmaxactivity - rightminactivity < leftmaxactivity - leftminactivity) )
6508    {
6509       /* both factors are bounded away from 0, but the right one has a smaller activity range, so divide by that one */
6510 
6511       /* write violated constraint as multleft * factorleft * multright * (multright * factorright) <= rhs
6512        *   such that multright * factorright > 0.0
6513        */
6514       if( rightminactivity < 0.0 )
6515          multright = -1.0;
6516       else
6517          multright =  1.0;
6518 
6519       /* generate cut for multleft * factorleft * multright <= rhs / (factorright * multright) */
6520       SCIP_CALL( generateCutFactorableDo(scip, cons, ref, multleft, consdata->factorleft, multright, consdata->factorright, rightminactivity, rightmaxactivity, rhs, rowprep, success) );
6521 
6522       /* if right factor has 0 within global activity bounds, then the added linearization is not globally valid */
6523       if( rhs < 0.0 && SCIPgetDepth(scip) > 0 && rightminactivityglobal < 0.0 && rightmaxactivityglobal > 0.0 )
6524          rowprep->local = TRUE;
6525    }
6526    else
6527    {
6528       /* both factors are bounded away from 0, but the left one has a smaller activity range, so divide by that one */
6529 
6530       /* write violated constraint as multleft * factorright * multright * (multright * factorleft) <= rhs
6531        *   such that multright * factorleft > 0.0
6532        */
6533       if( leftminactivity < 0.0 )
6534          multright = -1.0;
6535       else
6536          multright =  1.0;
6537 
6538       /* generate cut for multleft * factorright * multright <= rhs / (factorleft * multright) */
6539       SCIP_CALL( generateCutFactorableDo(scip, cons, ref, multleft, consdata->factorright, multright, consdata->factorleft, leftminactivity, leftmaxactivity, rhs, rowprep, success) );
6540 
6541       /* if left factor has 0 within global activity bounds, then the added linearization is not globally valid */
6542       if( rhs < 0.0 && SCIPgetDepth(scip) > 0 && leftminactivityglobal < 0.0 && leftmaxactivityglobal > 0.0 )
6543          rowprep->local = TRUE;
6544    }
6545 
6546    return SCIP_OKAY;
6547 }
6548 
6549 /* finds intersections of a parametric line (x,y) = (x0,y0) + t [(x1,y1) - (x0,y0)] on curves x*y = wl and x*y = wu;
6550  * returns TRUE if unsuccessful and FALSE otherwise
6551  */
6552 static
generateCutLTIfindIntersection(SCIP * scip,SCIP_Real x0,SCIP_Real y0_,SCIP_Real x1,SCIP_Real y1_,SCIP_Real wl,SCIP_Real wu,SCIP_Real * xl,SCIP_Real * yl,SCIP_Real * xu,SCIP_Real * yu)6553 SCIP_Bool generateCutLTIfindIntersection(
6554    SCIP*                 scip,
6555    SCIP_Real             x0,
6556    SCIP_Real             y0_,
6557    SCIP_Real             x1,
6558    SCIP_Real             y1_,
6559    SCIP_Real             wl,
6560    SCIP_Real             wu,
6561    SCIP_Real*            xl,
6562    SCIP_Real*            yl,
6563    SCIP_Real*            xu,
6564    SCIP_Real*            yu
6565    )
6566 {
6567    SCIP_Real a;
6568    SCIP_Real b;
6569    SCIP_Real c;
6570    SCIP_Real tl;
6571    SCIP_Real tu;
6572 
6573    /* The parametric line is of the form
6574     *
6575     *  x = x0 + t (x1-x0)
6576     *  y = y0 + t (y1-y0)
6577     *
6578     * and for that to satisfy xy = wl and xy = wu we must have
6579     *
6580     * x0 y0 + t [x0 (y1-y0) + y0 (x1-x0)] + t^2 (x1-x0) (y1-y0) = wl
6581     *                                                           = wu
6582     *
6583     * or a t^2 + b t + c - wl = 0 for proper values of a,b,c.
6584     *    a t^2 + b t + c - wu = 0
6585     *
6586     * Because of the way this procedure will be used, one of the two
6587     * solutions found we must always use the minimum nonnegative one
6588     */
6589 
6590    a = (x1 - x0) * (y1_ - y0_);
6591    c = x0 * y0_;
6592    b = x0 * y1_ + y0_ * x1 - 2.0 * c;
6593 
6594    tl = 0.0;
6595    tu = 0.0;
6596 
6597    if( !SCIPisZero(scip, (SCIP_Real)a) )
6598    {
6599       if( wl != SCIP_INVALID )  /*lint !e777 */
6600       {
6601          SCIP_Real tl1;
6602          SCIP_Real tl2;
6603          SCIP_Real denom;
6604          SCIP_Real q;
6605 
6606          if( b * b - 4.0 * a * (c - wl) < 0.0 )
6607          {
6608             SCIPdebugMsg(scip, "probable numerical difficulties, give up\n");
6609             return TRUE;
6610          }
6611 
6612          denom = sqrt(b * b - 4.0 * a * (c - wl));
6613          q = -0.5 * (b + COPYSIGN(denom, b));
6614          tl1 = q / a;
6615          tl2 = (c - wl) / q;
6616 
6617          /* choose the smallest non-negative root */
6618          tl = (tl1 >= 0.0 && (tl2 < 0.0 || tl1 < tl2)) ? tl1 : tl2;
6619       }
6620 
6621       if( wu != SCIP_INVALID )  /*lint !e777 */
6622       {
6623          SCIP_Real tu1;
6624          SCIP_Real tu2;
6625          SCIP_Real denom;
6626          SCIP_Real q;
6627 
6628          if( b * b - 4.0 * a * (c - wu) < 0.0 )
6629          {
6630             SCIPdebugMsg(scip, "probable numerical difficulties, give up\n");
6631             return TRUE;
6632          }
6633 
6634          denom = sqrt(b * b - 4.0 * a * (c - wu));
6635          q = -0.5 * (b + COPYSIGN(denom, b));
6636          tu1 = q / a;
6637          tu2 = (c - wu) / q;
6638 
6639          /* choose the smallest non-negative root */
6640          tu = (tu1 >= 0.0 && (tu2 < 0.0 || tu1 < tu2)) ? tu1 : tu2;
6641       }
6642    }
6643    else if( !SCIPisZero(scip, (SCIP_Real)b) )
6644    {
6645       if( wl != SCIP_INVALID )  /*lint !e777 */
6646          tl = (wl - c) / b;
6647       if( wu != SCIP_INVALID )  /*lint !e777 */
6648          tu = (wu - c) / b;
6649    }
6650    else
6651    {
6652       /* no or infinitely many solutions */
6653       return TRUE;
6654    }
6655 
6656    if( wl != SCIP_INVALID )  /*lint !e777 */
6657    {
6658       assert(xl != NULL);
6659       assert(yl != NULL);
6660 
6661       *xl = (SCIP_Real)(x0  + tl * (x1  - x0 ));
6662       *yl = (SCIP_Real)(y0_ + tl * (y1_ - y0_));
6663 
6664       if( SCIPisInfinity(scip, -*xl) || SCIPisInfinity(scip, -*yl) || !SCIPisRelEQ(scip, *xl * *yl, wl) )
6665       {
6666          SCIPdebugMsg(scip, "probable numerical difficulties, give up\n");
6667          return TRUE;
6668       }
6669    }
6670 
6671    if( wu != SCIP_INVALID )  /*lint !e777 */
6672    {
6673       assert(xu != NULL);
6674       assert(yu != NULL);
6675 
6676       *xu = (SCIP_Real)(x0  + tu * (x1 -  x0));
6677       *yu = (SCIP_Real)(y0_ + tu * (y1_ - y0_));
6678 
6679       if( SCIPisInfinity(scip, *xu) || SCIPisInfinity(scip, *yu) || !SCIPisRelEQ(scip, *xu * *yu, wu) )
6680       {
6681          SCIPdebugMsg(scip, "probable numerical difficulties, give up\n");
6682          return TRUE;
6683       }
6684    }
6685 
6686    return FALSE;
6687 }
6688 
6689 /** generate coefficients for a plane through points (x1, y1_, x1*y1) and (x2, y2, x2*y2)
6690  *  such that intersecting it with one of them (the first if whichuse is FALSE, the second otherwise)
6691  *  gives a tangent to the curve x*y = k
6692  *
6693  *  Returns TRUE on error and FALSE on success.
6694  */
6695 static
generateCutLTIgenMulCoeff(SCIP * scip,SCIP_Real x1,SCIP_Real y1_,SCIP_Real x2,SCIP_Real y2,SCIP_Bool whichuse,SCIP_Real * cx,SCIP_Real * cy,SCIP_Real * cw)6696 SCIP_Bool generateCutLTIgenMulCoeff(
6697    SCIP*                 scip,
6698    SCIP_Real             x1,
6699    SCIP_Real             y1_,
6700    SCIP_Real             x2,
6701    SCIP_Real             y2,
6702    SCIP_Bool             whichuse,
6703    SCIP_Real*            cx,
6704    SCIP_Real*            cy,
6705    SCIP_Real*            cw
6706    )
6707 {
6708    SCIP_Real xd;
6709    SCIP_Real yd;
6710    SCIP_Real xo;
6711    SCIP_Real yo;
6712 
6713    assert(cx != NULL);
6714    assert(cy != NULL);
6715    assert(cw != NULL);
6716 
6717    /* the x-y slope of this constraint must be tangent to a curve x*y = k at (xD,yD) */
6718    if( !whichuse )
6719    {
6720       xd = x1;
6721       xo = x2;
6722       yd = y1_;
6723       yo = y2;
6724    }
6725    else
6726    {
6727       xd = x2;
6728       xo = x1;
6729       yd = y2;
6730       yo = y1_;
6731    }
6732 
6733    *cx = yd;
6734    *cy = xd;
6735 
6736    /* lift it so that it touches the other curve */
6737 
6738    /* if the two points are on the same curve, then no cut */
6739    if( SCIPisZero(scip, xo * yo - xd * yd) )
6740       return TRUE;
6741 
6742    /* should ALWAYS be negative */
6743    *cw = (2.0 * xd * yd - (*cx * xo + *cy * yo)) / (xo * yo - xd * yd);
6744 
6745    return FALSE;
6746 }
6747 
6748 /** computes coefficients of a lifted-tangent inequality for x*y = w
6749  *
6750  *  The code is an adaptation of the methods in exprMul-upperHull.cpp in Couenne/stable/0.4 rev773,
6751  *  written by P. Belotti and licensed under Eclipse Public License.
6752  */
6753 static
generateCutLTIcomputeCoefs(SCIP * scip,SCIP_Real xl,SCIP_Real xu,SCIP_Real x0,SCIP_Real yl,SCIP_Real yu,SCIP_Real y0_,SCIP_Real wl,SCIP_Real wu,SCIP_Real w0,SCIP_Real * cx,SCIP_Real * cy,SCIP_Real * cw,SCIP_Real * c0,SCIP_Bool * success)6754 void generateCutLTIcomputeCoefs(
6755    SCIP*                 scip,               /**< SCIP data structure */
6756    SCIP_Real             xl,                 /**< lower bound on x */
6757    SCIP_Real             xu,                 /**< upper bound on x */
6758    SCIP_Real             x0,                 /**< reference point for x */
6759    SCIP_Real             yl,                 /**< lower bound on y */
6760    SCIP_Real             yu,                 /**< upper bound on y */
6761    SCIP_Real             y0_,                /**< reference point for y */
6762    SCIP_Real             wl,                 /**< lower bound on w */
6763    SCIP_Real             wu,                 /**< upper bound on w */
6764    SCIP_Real             w0,                 /**< reference point for w */
6765    SCIP_Real*            cx,                 /**< buffer where to store cut coefficient for x */
6766    SCIP_Real*            cy,                 /**< buffer where to store cut coefficient for y */
6767    SCIP_Real*            cw,                 /**< buffer where to store cut coefficient for w */
6768    SCIP_Real*            c0,                 /**< buffer where to store cut left-hand-side */
6769    SCIP_Bool*            success             /**< buffer where to indicate whether cut coefficients were computed */
6770    )
6771 {
6772    SCIP_Bool flipx;
6773    SCIP_Bool flipy;
6774    SCIP_Bool flipw;
6775    SCIP_Real tmp;
6776    SCIP_Real xlow = 0.0;
6777    SCIP_Real ylow = 0.0;
6778    SCIP_Real xupp = 0.0;
6779    SCIP_Real yupp = 0.0;
6780    SCIP_Real c0x;
6781    SCIP_Real c0y;
6782    SCIP_Real c0w;
6783 
6784    assert(scip != NULL);
6785    assert(cx != NULL);
6786    assert(cy != NULL);
6787    assert(cw != NULL);
6788    assert(c0 != NULL);
6789    assert(success != NULL);
6790 
6791    *success = FALSE;
6792    *cx = 0.0;
6793    *cy = 0.0;
6794    *cw = 0.0;
6795    *c0 = 0.0;
6796 
6797    SCIPdebugMsg(scip, "entering points:\n");
6798    SCIPdebugMsg(scip, "x: %9g\t[%9g\t%9g]\n", x0, xl, xu);
6799    SCIPdebugMsg(scip, "y: %9g\t[%9g\t%9g]\n", y0_, yl, yu);
6800    SCIPdebugMsg(scip, "w: %9g\t[%9g\t%9g]\n", w0, wl, wu);
6801 
6802    /* generateCutLTI should have recognized these */
6803    assert(wl >= 0.0 || wu <= 0.0);
6804    assert(!SCIPisInfinity(scip, -wl));
6805    assert(!SCIPisInfinity(scip,  wu));
6806 
6807    assert(SCIPisFeasGE(scip, x0, xl));
6808    assert(SCIPisFeasLE(scip, x0, xu));
6809    assert(SCIPisFeasGE(scip, y0_, yl));
6810    assert(SCIPisFeasLE(scip, y0_, yu));
6811 
6812    /* preliminary bound tightening */
6813    if( wl >= 0.0 )
6814    {
6815       if( xl >= 0.0 || yl >= 0.0 || SCIPisLT(scip, xl * yl, wl) )
6816       {
6817          xl = MAX(xl, 0.0);
6818          yl = MAX(yl, 0.0);
6819       }
6820       else if( xu <= 0.0 || yu <= 0.0 || SCIPisLT(scip, xu * yu, wl) )
6821       {
6822          xu = MIN(xu, 0.0);
6823          yu = MIN(yu, 0.0);
6824       }
6825       else
6826       {
6827          /* both variables have mixed sign (xl < 0 && xu > 0 && yl < 0 && yu > 0) and both xl*yl and xu*yu are feasible
6828           * cannot generate cut for this
6829           */
6830          return;
6831       }
6832    }
6833    else
6834    {
6835       if( xl >= 0.0 || yu <= 0.0 || SCIPisGT(scip, xl * yu, wu) )
6836       {
6837          xl = MAX(xl, 0.0);
6838          yu = MIN(yu, 0.0);
6839       }
6840       else if( xu <= 0.0 || yl >= 0.0 || SCIPisGT(scip, xu * yl, wu))
6841       {
6842          xu = MIN(xu, 0.0);
6843          yl = MAX(yl, 0.0);
6844       }
6845       else
6846       {
6847          /* both variables have mixed sign (xl < 0 && xu > 0 && yl < 0 && yu > 0) and both xl*yu and xu*yl are feasible
6848           * cannot generate cut for this
6849           */
6850          return;
6851       }
6852    }
6853 
6854    /* if x or y is fixed now or even infeasible, then do not think about a cut */
6855    if( SCIPisGE(scip, xl, xu) || SCIPisGE(scip, yl, yu) )
6856       return;
6857 
6858    /* reduce to positive orthant by flipping variables */
6859    if( xl < 0.0 )
6860    {
6861       flipx = TRUE;
6862       tmp = xu;
6863       xu = -xl;
6864       xl = -tmp;
6865       x0 = -x0;
6866    }
6867    else
6868       flipx = FALSE;
6869 
6870    if( yl < 0.0 )
6871    {
6872       flipy = TRUE;
6873       tmp = yu;
6874       yu = -yl;
6875       yl = -tmp;
6876       y0_ = -y0_;
6877    }
6878    else
6879       flipy = FALSE;
6880 
6881    if( flipx ^ flipy )
6882    {
6883       flipw = TRUE;
6884       tmp = wu;
6885       wu = -wl;
6886       wl = -tmp;
6887       w0 = -w0;
6888    }
6889    else
6890       flipw = FALSE;
6891 
6892    /* project refpoint into box not only for numerical reasons, but also due to preliminary bound tightening above */
6893    x0 = MIN(xu, MAX(x0, xl));
6894    y0_ = MIN(yu, MAX(y0_, yl));
6895    w0 = MIN(wu, MAX(w0, wl));
6896 
6897    SCIPdebugMsg(scip, "reduced points:\n");
6898    SCIPdebugMsg(scip, "x: %9g\t[%9g\t%9g]\n", x0, xl, xu);
6899    SCIPdebugMsg(scip, "y: %9g\t[%9g\t%9g]\n", y0_, yl, yu);
6900    SCIPdebugMsg(scip, "w: %9g\t[%9g\t%9g]\n", w0, wl, wu);
6901 
6902    if( SCIPisGE(scip, xl * yl, wl) && SCIPisLE(scip, xu * yu, wu) )
6903    {
6904       SCIPdebugMsg(scip, "box for x and y inside feasible region -> nothing to separate\n");
6905       return;
6906    }
6907    if( SCIPisGE(scip, x0 * y0_, w0) )
6908    {
6909       SCIPdebugMsg(scip, "point to separate not below curve -> cannot separate\n");
6910       return;
6911    }
6912 
6913    /* find intersections of halfline from origin
6914     * return if no proper point could be found
6915     */
6916    if( generateCutLTIfindIntersection(scip, 0.0, 0.0, x0, y0_, wl, wu, &xlow, &ylow, &xupp, &yupp) )
6917       return;
6918 
6919    SCIPdebugMsg(scip, "intersections:\n");
6920    SCIPdebugMsg(scip, "lower: %9g\t%9g\tprod %9g\n", xlow, ylow, xlow*ylow);
6921    SCIPdebugMsg(scip, "upper: %9g\t%9g\tprod %9g\n", xupp, yupp, xupp*yupp);
6922 
6923    /* Case 1: If both are outside of bounding box, either NW or SE, then McCormick is sufficient, so return */
6924    if( (xlow <= xl && yupp >= yu) || (ylow <= yl && xupp >= xu) )
6925       return;
6926 
6927    /* There will be at least one cut. Define coefficients and rhs ---will have to change them back if (flipX || flipY) */
6928    if( xlow >= xl && xupp <= xu && ylow >= yl && yupp <= yu )
6929    {
6930       /* Case 2: both are inside. Easy lifting... */
6931       if( generateCutLTIgenMulCoeff(scip, xlow, ylow, xupp, yupp, FALSE, cx, cy, cw) )
6932          return;
6933 
6934       c0x = *cx * xlow;
6935       c0y = *cy * ylow;
6936       c0w = *cw * wl;
6937    }
6938    else if( xlow >= xl && ylow >= yl && (xupp > xu || yupp > yu) )
6939    {
6940       /* Case 3a and 3b: through lower curve, but not upper. */
6941       if( yupp > yu )
6942       {
6943          /* upper intersect is North; place it within box */
6944          assert(!SCIPisInfinity(scip, yu));
6945          yupp = yu;
6946          xupp = wu / yu;
6947       }
6948       else
6949       {
6950          /* upper intersect is East; place it within box */
6951          assert(!SCIPisInfinity(scip, xu));
6952          xupp = xu;
6953          yupp = wu / xu;
6954       }
6955 
6956       /* find intersection on low curve on half line through new point and (x0,y0_) */
6957       if( generateCutLTIfindIntersection(scip, xupp, yupp, x0, y0_, wl, SCIP_INVALID, &xlow, &ylow, NULL, NULL) )
6958          return;
6959 
6960       /* check whether McCormick is sufficient */
6961       if( xlow < xl || ylow < yl )
6962          return;
6963 
6964       /* lift inequality on lower point */
6965       if( generateCutLTIgenMulCoeff(scip, xlow, ylow, xupp, yupp, FALSE, cx, cy, cw) )
6966          return;
6967 
6968       c0x = *cx * xlow;
6969       c0y = *cy * ylow;
6970       c0w = *cw * wl;
6971    }
6972    else if( xupp <= xu && yupp <= yu && (xlow < xl || ylow < yl) )
6973    {
6974       /* Case 4a and 4b: viceversa (lift for validity) */
6975       if( ylow < yl )
6976       {
6977          /* upper intersect is South; place it within box */
6978          assert(!SCIPisZero(scip, yl));
6979          ylow = yl;
6980          xlow = wl / yl;
6981       }
6982       else
6983       {
6984          /* upper intersect is West; place it within box */
6985          assert(!SCIPisZero(scip, xl));
6986          xlow = xl;
6987          ylow = wl / xl;
6988       }
6989 
6990       /* find intersection on low curve on half line through new point and (x0,y0) */
6991       if( generateCutLTIfindIntersection(scip, xlow, ylow, x0, y0_, SCIP_INVALID, wu, NULL, NULL, &xupp, &yupp) )
6992          return;
6993 
6994       /* check whether McCormick is sufficient */
6995       if( xupp > xu || yupp > yu )
6996          return;
6997 
6998       /* lift inequality on UPPER point */
6999       if( generateCutLTIgenMulCoeff(scip, xlow, ylow, xupp, yupp, TRUE, cx, cy, cw) )
7000          return;
7001 
7002       c0x = *cx * xupp;
7003       c0y = *cy * yupp;
7004       c0w = *cw * wu;
7005    }
7006    else if( (xlow < xl && xupp > xu) || (ylow < yl && yupp > yu) )
7007    {
7008       /* Case 5: both outside of bounding box, N and S or W and E. */
7009 #ifdef SCIP_DISABLED_CODE
7010       SCIP_Real xlow2;
7011       SCIP_Real ylow2;
7012       SCIP_Real xupp2;
7013       SCIP_Real yupp2;
7014 #endif
7015 
7016       if( ylow < yl )
7017       {
7018          /* upper intersect is South; place it within box */
7019          assert(!SCIPisZero(scip, yl));
7020          assert(!SCIPisZero(scip, yu));
7021          ylow = yl;
7022          yupp = yu;
7023          xlow = wl / yl;
7024          xupp = wu / yu;
7025       }
7026       else
7027       {
7028          /* upper intersect is West; place it within box */
7029          assert(!SCIPisZero(scip, xl));
7030          assert(!SCIPisZero(scip, xu));
7031          xlow = xl;
7032          xupp = xu;
7033          ylow = wl / xl;
7034          yupp = wu / xu;
7035       }
7036 
7037       SCIPdebugMsg(scip, "New intersections:\n");
7038       SCIPdebugMsg(scip, "lower: %9g\t%9g\tprod %9g\n", xlow, ylow, xlow*ylow);
7039       SCIPdebugMsg(scip, "upper: %9g\t%9g\tprod %9g\n", xupp, yupp, xupp*yupp);
7040 
7041 #ifndef SCIP_DISABLED_CODE
7042       /* Nothing to find. Just separate two inequalities at the same point, just using different support */
7043       if( generateCutLTIgenMulCoeff(scip, xlow, ylow, xupp, yupp, FALSE, cx, cy, cw) )
7044       {
7045          if( generateCutLTIgenMulCoeff(scip, xlow, ylow, xupp, yupp, TRUE, cx, cy, cw) )
7046             return;
7047 
7048          c0x = *cx * xupp;
7049          c0y = *cy * yupp;
7050          c0w = *cw * wu;
7051       }
7052       else
7053       {
7054          c0x = *cx * xlow;
7055          c0y = *cy * ylow;
7056          c0w = *cw * wl;
7057       }
7058 
7059 #else
7060       /* find the intersection on the lower (upper) curve on the line through xLP and the upper (lower) point
7061        * this does not seem to work (cuts off solution at nous2), so it is disabled for now
7062        */
7063       if( generateCutLTIfindIntersection(scip, xlow, ylow, x0, y0_, SCIP_INVALID, wu, NULL, NULL, &xupp2, &yupp2) ||
7064          generateCutLTIgenMulCoeff(scip, xlow, ylow, xupp2, yupp2, FALSE, cx, cx, cw) )
7065       {
7066          if( generateCutLTIfindIntersection(scip, xupp, yupp, x0, y0_, wl, SCIP_INVALID, &xlow2, &ylow2, NULL, NULL) ||
7067             generateCutLTIgenMulCoeff(scip, xlow2, ylow2, xupp, yupp, TRUE, cx, cy, cw) )
7068             return;
7069 
7070          c0x = *cx * xupp;
7071          c0y = *cy * yupp;
7072          c0w = *cw * wu;
7073       }
7074       else
7075       {
7076          c0x = *cx * xlow;
7077          c0y = *cy * ylow;
7078          c0w = *cw * wl;
7079       }
7080 #endif
7081    }
7082    else
7083    {
7084       SCIPdebugMsg(scip, "points are in a weird position:\n");
7085       SCIPdebugMsg(scip, "lower: %9g\t%9g\tprod %9g\n", xlow, ylow, xlow*ylow);
7086       SCIPdebugMsg(scip, "upper: %9g\t%9g\tprod %9g\n", xupp, yupp, xupp*yupp);
7087 
7088       return;
7089    }
7090 
7091    SCIPdebugMsg(scip, "cut w.r.t. reduced points: %gx-%g %+gy-%g %+gw-%g >= 0\n",
7092       *cx, c0x, *cy, c0y, *cw, c0w);
7093 
7094    /* re-transform back into original variables */
7095    if( flipx )
7096       *cx = -*cx;
7097    if( flipy )
7098       *cy = -*cy;
7099    if( flipw )
7100       *cw = -*cw;
7101 
7102    *c0 = c0x + c0y + c0w;
7103 
7104    *success = TRUE;
7105 }
7106 
7107 /** tries to generate a cut if constraint quadratic function is factorable and there are linear variables
7108  *
7109  *  Computes what is called a lifted tangent inequality described in@n
7110  *   Belotti, Miller, Namazifar, Lifted inequalities for bounded products of variables, SIAG/OPT Views-and-News 22:1, 2011
7111  */
7112 static
generateCutLTI(SCIP * scip,SCIP_CONS * cons,SCIP_SIDETYPE violside,SCIP_Real * ref,SCIP_SOL * sol,SCIP_ROWPREP * rowprep,SCIP_Bool * success)7113 SCIP_RETCODE generateCutLTI(
7114    SCIP*                 scip,               /**< SCIP data structure */
7115    SCIP_CONS*            cons,               /**< constraint */
7116    SCIP_SIDETYPE         violside,           /**< for which side a cut should be generated */
7117    SCIP_Real*            ref,                /**< reference solution where to generate the cut */
7118    SCIP_SOL*             sol,                /**< solution that shall be cutoff, NULL for LP solution */
7119    SCIP_ROWPREP*         rowprep,            /**< rowprep to store cut data */
7120    SCIP_Bool*            success             /**< buffer to indicate whether a cut was successfully computed */
7121    )
7122 {
7123    SCIP_CONSDATA* consdata;
7124    SCIP_Real leftminactivity;
7125    SCIP_Real leftmaxactivity;
7126    SCIP_Real leftrefactivity;
7127    SCIP_Real rightminactivity;
7128    SCIP_Real rightmaxactivity;
7129    SCIP_Real rightrefactivity;
7130    SCIP_Real rhsminactivity;
7131    SCIP_Real rhsmaxactivity;
7132    SCIP_Real rhsrefactivity;
7133    SCIP_Real coefleft;
7134    SCIP_Real coefright;
7135    SCIP_Real coefrhs;
7136    SCIP_Real cutlhs;
7137    int i;
7138 
7139    assert(scip != NULL);
7140    assert(cons != NULL);
7141    assert(ref  != NULL);
7142    assert(rowprep != NULL);
7143    assert(success != NULL);
7144    /* currently only separate LP solution or solutions given as SCIP_SOL, i.e., no cutgeneration during initlp */
7145    assert(sol != NULL || SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_OPTIMAL);
7146 
7147    consdata = SCIPconsGetData(cons);
7148    assert(consdata != NULL);
7149    assert(consdata->nlinvars > 0);
7150    assert(consdata->factorleft != NULL);
7151    assert(consdata->factorright != NULL);
7152 
7153    *success = FALSE;
7154    rowprep->sidetype = SCIP_SIDETYPE_LEFT;
7155 
7156    /* write violated constraints as factorleft * factorright '==' rhs
7157     * where rhs are constraint sides - activity bound of linear part
7158     */
7159    rhsminactivity = consdata->lhs;
7160    rhsmaxactivity = consdata->rhs;
7161    rhsrefactivity = (violside == SCIP_SIDETYPE_LEFT ? consdata->lhs : consdata->rhs);
7162 
7163    for( i = 0; i < consdata->nlinvars; ++i )
7164    {
7165       if( !SCIPisInfinity(scip, -rhsminactivity) )
7166       {
7167          if( consdata->lincoefs[i] < 0.0 )
7168          {
7169             if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(consdata->linvars[i])) )
7170                rhsminactivity = -SCIPinfinity(scip);
7171             else
7172                rhsminactivity -= consdata->lincoefs[i] * SCIPvarGetLbLocal(consdata->linvars[i]);
7173          }
7174          else
7175          {
7176             assert(consdata->lincoefs[i] > 0.0);
7177             if( SCIPisInfinity(scip, SCIPvarGetUbLocal(consdata->linvars[i])) )
7178                rhsminactivity = -SCIPinfinity(scip);
7179             else
7180                rhsminactivity -= consdata->lincoefs[i] * SCIPvarGetUbLocal(consdata->linvars[i]);
7181          }
7182       }
7183       if( !SCIPisInfinity(scip, rhsmaxactivity) )
7184       {
7185          if( consdata->lincoefs[i] < 0.0 )
7186          {
7187             if( SCIPisInfinity(scip, SCIPvarGetUbLocal(consdata->linvars[i])) )
7188                rhsmaxactivity = SCIPinfinity(scip);
7189             else
7190                rhsmaxactivity -= consdata->lincoefs[i] * SCIPvarGetUbLocal(consdata->linvars[i]);
7191          }
7192          else
7193          {
7194             assert(consdata->lincoefs[i] > 0.0);
7195             if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(consdata->linvars[i])) )
7196                rhsmaxactivity = SCIPinfinity(scip);
7197             else
7198                rhsmaxactivity -= consdata->lincoefs[i] * SCIPvarGetLbLocal(consdata->linvars[i]);
7199          }
7200       }
7201       rhsrefactivity -= consdata->lincoefs[i] * SCIPgetSolVal(scip, sol, consdata->linvars[i]);
7202    }
7203 
7204    if( SCIPisInfinity(scip, -rhsminactivity) || SCIPisInfinity(scip, rhsmaxactivity) )
7205    {
7206       /* if right hand side is unbounded, then cannot do LTI */
7207       return SCIP_OKAY;
7208    }
7209 
7210    if( !SCIPisFeasPositive(scip, rhsminactivity) && !SCIPisFeasNegative(scip, rhsmaxactivity) )
7211    {
7212       /* if right hand side has 0 inside activity, then cannot do anything
7213        * if it has 0.0 as min or max activity, then a usual McCormick should be sufficient, too
7214        */
7215       return SCIP_OKAY;
7216    }
7217 
7218    leftminactivity = consdata->factorleft[consdata->nquadvars];
7219    leftmaxactivity = consdata->factorleft[consdata->nquadvars];
7220    leftrefactivity = consdata->factorleft[consdata->nquadvars];
7221    rightminactivity = consdata->factorright[consdata->nquadvars];
7222    rightmaxactivity = consdata->factorright[consdata->nquadvars];
7223    rightrefactivity = consdata->factorright[consdata->nquadvars];
7224    for( i = 0; i < consdata->nquadvars; ++i )
7225    {
7226       if( !SCIPisInfinity(scip, -leftminactivity) )
7227       {
7228          if( consdata->factorleft[i] > 0.0 )
7229          {
7230             if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(consdata->quadvarterms[i].var)) )
7231                leftminactivity = -SCIPinfinity(scip);
7232             else
7233                leftminactivity += consdata->factorleft[i] * SCIPvarGetLbLocal(consdata->quadvarterms[i].var);
7234          }
7235          else if( consdata->factorleft[i] < 0.0 )
7236          {
7237             if( SCIPisInfinity(scip, SCIPvarGetUbLocal(consdata->quadvarterms[i].var)) )
7238                leftminactivity = -SCIPinfinity(scip);
7239             else
7240                leftminactivity += consdata->factorleft[i] * SCIPvarGetUbLocal(consdata->quadvarterms[i].var);
7241          }
7242       }
7243       if( !SCIPisInfinity(scip, leftmaxactivity) )
7244       {
7245          if( consdata->factorleft[i] > 0.0 )
7246          {
7247             if( SCIPisInfinity(scip, SCIPvarGetUbLocal(consdata->quadvarterms[i].var)) )
7248                leftmaxactivity = SCIPinfinity(scip);
7249             else
7250                leftmaxactivity += consdata->factorleft[i] * SCIPvarGetUbLocal(consdata->quadvarterms[i].var);
7251          }
7252          else if( consdata->factorleft[i] < 0.0 )
7253          {
7254             if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(consdata->quadvarterms[i].var)) )
7255                leftmaxactivity = SCIPinfinity(scip);
7256             else
7257                leftmaxactivity += consdata->factorleft[i] * SCIPvarGetLbLocal(consdata->quadvarterms[i].var);
7258          }
7259       }
7260       leftrefactivity += consdata->factorleft[i] * ref[i];
7261 
7262       if( !SCIPisInfinity(scip, -rightminactivity) )
7263       {
7264          if( consdata->factorright[i] > 0.0 )
7265          {
7266             if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(consdata->quadvarterms[i].var)) )
7267                rightminactivity = -SCIPinfinity(scip);
7268             else
7269                rightminactivity += consdata->factorright[i] * SCIPvarGetLbLocal(consdata->quadvarterms[i].var);
7270          }
7271          else if( consdata->factorright[i] < 0.0 )
7272          {
7273             if( SCIPisInfinity(scip, SCIPvarGetUbLocal(consdata->quadvarterms[i].var)) )
7274                rightminactivity = -SCIPinfinity(scip);
7275             else
7276                rightminactivity += consdata->factorright[i] * SCIPvarGetUbLocal(consdata->quadvarterms[i].var);
7277          }
7278       }
7279       if( !SCIPisInfinity(scip, rightmaxactivity) )
7280       {
7281          if( consdata->factorright[i] > 0.0 )
7282          {
7283             if( SCIPisInfinity(scip, SCIPvarGetUbLocal(consdata->quadvarterms[i].var)) )
7284                rightmaxactivity = SCIPinfinity(scip);
7285             else
7286                rightmaxactivity += consdata->factorright[i] * SCIPvarGetUbLocal(consdata->quadvarterms[i].var);
7287          }
7288          else if( consdata->factorright[i] < 0.0 )
7289          {
7290             if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(consdata->quadvarterms[i].var)) )
7291                rightmaxactivity = SCIPinfinity(scip);
7292             else
7293                rightmaxactivity += consdata->factorright[i] * SCIPvarGetLbLocal(consdata->quadvarterms[i].var);
7294          }
7295       }
7296       rightrefactivity += consdata->factorright[i] * ref[i];
7297    }
7298 
7299    /* if activities exceed "opposite" infinity, huge bounds seem to be involved, for which the below method is not prepared */
7300    if( SCIPisInfinity(scip, leftminactivity)  || SCIPisInfinity(scip, -leftmaxactivity) ||
7301        SCIPisInfinity(scip, rightminactivity) || SCIPisInfinity(scip, -rightmaxactivity) )
7302       return SCIP_OKAY;
7303 
7304    /* if activity in reference point exceeds value for infinity, then the below method will also not work properly */
7305    if( SCIPisInfinity(scip, REALABS(leftrefactivity)) || SCIPisInfinity(scip, REALABS(rightrefactivity)) )
7306       return SCIP_OKAY;
7307 
7308    /* if any of the factors is essentially fixed, give up and do usual method (numerically less sensitive, I hope) */
7309    if( SCIPisRelEQ(scip, leftminactivity, leftmaxactivity) || SCIPisRelEQ(scip, rightminactivity, rightmaxactivity) )
7310       return SCIP_OKAY;
7311 
7312    /* success can only be expected for separation of violated x*y <= w, assuming x>=0, y>=0
7313     * @todo we should check this early? */
7314 
7315    /* call Couenne magic */
7316    generateCutLTIcomputeCoefs(scip,
7317       leftminactivity, leftmaxactivity, leftrefactivity,
7318       rightminactivity, rightmaxactivity, rightrefactivity,
7319       rhsminactivity, rhsmaxactivity, rhsrefactivity,
7320       &coefleft, &coefright, &coefrhs, &cutlhs,
7321       success);
7322 
7323    if( !*success )
7324       return SCIP_OKAY;
7325 
7326    SCIPdebugMsg(scip, "LTI for x[%g,%g] * y[%g,%g] = w[%g,%g]: %gx %+gy %+gw >= %g;  feas: %g\n",
7327       leftminactivity, leftmaxactivity, rightminactivity, rightmaxactivity, rhsminactivity, rhsmaxactivity,
7328       coefleft, coefright, coefrhs, cutlhs,
7329       coefleft * leftrefactivity + coefright * rightrefactivity + coefrhs * rhsrefactivity - cutlhs
7330       );
7331 
7332    if( coefleft * leftrefactivity + coefright * rightrefactivity + coefrhs * rhsrefactivity >= cutlhs )
7333    {
7334       SCIPdebugMsg(scip, "does not cutoff point? :-(\n");
7335       *success = FALSE;
7336       return SCIP_OKAY;
7337    }
7338 
7339    /* setup cut coefs for
7340     *   coefleft * leftfactor + coefright * rightfactor + coefrhs * w >= cutlhs, where conslhs - lincoefs <= w <= consrhs - lincoefs
7341     */
7342    for( i = 0; i < consdata->nquadvars; ++i )
7343    {
7344       SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, consdata->quadvarterms[i].var, coefleft * consdata->factorleft[i] + coefright * consdata->factorright[i]) );
7345    }
7346    SCIPaddRowprepConstant(rowprep, coefleft * consdata->factorleft[i] + coefright * consdata->factorright[i]);
7347 
7348    for( i = 0; i < consdata->nlinvars; ++i )
7349    {
7350       SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, consdata->linvars[i], -coefrhs * consdata->lincoefs[i]) );
7351    }
7352    if( coefrhs > 0.0 )
7353    {
7354       /* use coefrhs * w <= coefrhs * (consrhs - lincoefs) */
7355       assert(!SCIPisInfinity(scip, consdata->rhs));
7356       SCIPaddRowprepConstant(rowprep, coefrhs * consdata->rhs);
7357    }
7358    else
7359    {
7360       /* use coefrhs * w <= coeflhs * (conslhs - lincoefs) */
7361       assert(!SCIPisInfinity(scip, -consdata->lhs));
7362       SCIPaddRowprepConstant(rowprep, coefrhs * consdata->lhs);
7363    }
7364    SCIPaddRowprepSide(rowprep, cutlhs);
7365 
7366    rowprep->local = TRUE;
7367 
7368    (void) SCIPsnprintf(rowprep->name, SCIP_MAXSTRLEN, "%s_lti_%" SCIP_LONGINT_FORMAT, SCIPconsGetName(cons), SCIPgetNLPs(scip));
7369 
7370    *success = TRUE;
7371 
7372    return SCIP_OKAY;
7373 }
7374 
7375 /** computes cut coefficients by linearizing a quadratic function */
7376 static
generateCutConvex(SCIP * scip,SCIP_CONS * cons,SCIP_SIDETYPE violside,SCIP_Real * ref,SCIP_ROWPREP * rowprep,SCIP_Bool * success)7377 SCIP_RETCODE generateCutConvex(
7378    SCIP*                 scip,               /**< SCIP data structure */
7379    SCIP_CONS*            cons,               /**< constraint */
7380    SCIP_SIDETYPE         violside,           /**< side for which to generate cut */
7381    SCIP_Real*            ref,                /**< reference solution where to generate the cut */
7382    SCIP_ROWPREP*         rowprep,            /**< rowprep to store cut data */
7383    SCIP_Bool*            success             /**< buffer to indicate whether a cut was successfully computed */
7384    )
7385 {
7386    SCIP_CONSDATA* consdata;
7387    SCIP_BILINTERM* bilinterm;
7388    SCIP_Real constant;
7389    SCIP_Real coef;
7390    SCIP_Real coef2;
7391    SCIP_VAR* var;
7392    int var2pos;
7393    int j;
7394    int k;
7395 
7396    assert(scip != NULL);
7397    assert(cons != NULL);
7398    assert(ref  != NULL);
7399    assert(success != NULL);
7400 
7401    consdata = SCIPconsGetData(cons);
7402    assert(consdata != NULL);
7403 
7404    *success = TRUE;
7405 
7406    /* do first-order Taylor for each term */
7407    for( j = 0; j < consdata->nquadvars && *success; ++j )
7408    {
7409       var = consdata->quadvarterms[j].var;
7410 
7411       /* initialize coefficients to linear coefficients of quadratic variables */
7412       SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, var, consdata->quadvarterms[j].lincoef) );
7413 
7414       /* add linearization of square term */
7415       coef = 0.0;
7416       constant = 0.0;
7417       SCIPaddSquareLinearization(scip, consdata->quadvarterms[j].sqrcoef, ref[j],
7418          consdata->quadvarterms[j].nadjbilin == 0 && SCIPvarGetType(var) < SCIP_VARTYPE_CONTINUOUS, &coef, &constant, success);
7419       SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, var, coef) );
7420       SCIPaddRowprepConstant(rowprep, constant);
7421 
7422       /* add linearization of bilinear terms that have var as first variable */
7423       for( k = 0; k < consdata->quadvarterms[j].nadjbilin && *success; ++k )
7424       {
7425          bilinterm = &consdata->bilinterms[consdata->quadvarterms[j].adjbilin[k]];
7426          if( bilinterm->var1 != var )
7427             continue;
7428          assert(bilinterm->var2 != var);
7429          assert(consdata->sepabilinvar2pos != NULL);
7430 
7431          var2pos = consdata->sepabilinvar2pos[consdata->quadvarterms[j].adjbilin[k]];
7432          assert(var2pos >= 0);
7433          assert(var2pos < consdata->nquadvars);
7434          assert(consdata->quadvarterms[var2pos].var == bilinterm->var2);
7435 
7436          coef = 0.0;
7437          coef2 = 0.0;
7438          constant = 0.0;
7439          SCIPaddBilinLinearization(scip, bilinterm->coef, ref[j], ref[var2pos], &coef, &coef2, &constant, success);
7440          SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, var, coef) );
7441          SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, bilinterm->var2, coef2) );
7442          SCIPaddRowprepConstant(rowprep, constant);
7443       }
7444    }
7445 
7446    if( !*success )
7447    {
7448       SCIPdebugMsg(scip, "no success in linearization of <%s> in reference point\n", SCIPconsGetName(cons));
7449       return SCIP_OKAY;
7450    }
7451 
7452    rowprep->sidetype = violside;
7453    SCIPaddRowprepSide(rowprep, violside == SCIP_SIDETYPE_LEFT ? consdata->lhs : consdata->rhs);
7454 
7455    (void) SCIPsnprintf(rowprep->name, SCIP_MAXSTRLEN, "%s_side%d_linearization_%" SCIP_LONGINT_FORMAT, SCIPconsGetName(cons), violside, SCIPgetNLPs(scip));
7456 
7457    return SCIP_OKAY;
7458 }
7459 
7460 /** helper function to update the best relaxation for a bilinear term when using valid linear inequalities */
7461 static
updateBilinearRelaxation(SCIP * scip,SCIP_VAR * RESTRICT x,SCIP_VAR * RESTRICT y,SCIP_Real bilincoef,SCIP_SIDETYPE violside,SCIP_Real refx,SCIP_Real refy,SCIP_Real * RESTRICT ineqs,int nineqs,SCIP_Real mccormickval,SCIP_Real * RESTRICT bestcoefx,SCIP_Real * RESTRICT bestcoefy,SCIP_Real * RESTRICT bestconst,SCIP_Real * RESTRICT bestval,SCIP_Bool * success)7462 void updateBilinearRelaxation(
7463    SCIP*                 scip,               /**< SCIP data structure */
7464    SCIP_VAR* RESTRICT    x,                  /**< first variable */
7465    SCIP_VAR* RESTRICT    y,                  /**< second variable */
7466    SCIP_Real             bilincoef,          /**< coefficient of the bilinear term */
7467    SCIP_SIDETYPE         violside,           /**< side of quadratic constraint that is violated */
7468    SCIP_Real             refx,               /**< reference point for the x variable */
7469    SCIP_Real             refy,               /**< reference point for the y variable */
7470    SCIP_Real* RESTRICT   ineqs,              /**< coefficients of each linear inequality; stored as triple (xcoef,ycoef,constant) */
7471    int                   nineqs,             /**< total number of inequalities */
7472    SCIP_Real             mccormickval,       /**< value of the McCormick relaxation at the reference point */
7473    SCIP_Real* RESTRICT   bestcoefx,          /**< pointer to update the x coefficient */
7474    SCIP_Real* RESTRICT   bestcoefy,          /**< pointer to update the y coefficient */
7475    SCIP_Real* RESTRICT   bestconst,          /**< pointer to update the constant */
7476    SCIP_Real* RESTRICT   bestval,            /**< value of the best relaxation that have been found so far */
7477    SCIP_Bool*            success             /**< buffer to store whether we found a better relaxation */
7478    )
7479 {
7480    SCIP_Real constshift[2] = {0.0, 0.0};
7481    SCIP_Real constant;
7482    SCIP_Real xcoef;
7483    SCIP_Real ycoef;
7484    SCIP_Real lbx;
7485    SCIP_Real ubx;
7486    SCIP_Real lby;
7487    SCIP_Real uby;
7488    SCIP_Bool update;
7489    SCIP_Bool overestimate;
7490    int i;
7491 
7492    assert(x != y);
7493    assert(!SCIPisZero(scip, bilincoef));
7494    assert(nineqs >= 0 && nineqs <= 2);
7495    assert(bestcoefx != NULL);
7496    assert(bestcoefy != NULL);
7497    assert(bestconst != NULL);
7498    assert(bestval != NULL);
7499 
7500    /* no inequalities available */
7501    if( nineqs == 0 )
7502       return;
7503    assert(ineqs != NULL);
7504 
7505    lbx = SCIPvarGetLbLocal(x);
7506    ubx = SCIPvarGetUbLocal(x);
7507    lby = SCIPvarGetLbLocal(y);
7508    uby = SCIPvarGetUbLocal(y);
7509    overestimate = (violside == SCIP_SIDETYPE_LEFT);
7510 
7511    /* check cases for which we can't compute a tighter relaxation */
7512    if( SCIPisFeasLE(scip, refx, lbx) || SCIPisFeasGE(scip, refx, ubx)
7513       || SCIPisFeasLE(scip, refy, lby) || SCIPisFeasGE(scip, refy, uby) )
7514       return;
7515 
7516    /* due to the feasibility tolerances of the LP and NLP solver, it might possible that the reference point is
7517     * violating the linear inequalities; to ensure that we compute a valid underestimate, we relax the linear
7518     * inequality by changing its constant part
7519     */
7520    for( i = 0; i < nineqs; ++i )
7521    {
7522       constshift[i] = MAX(0.0, ineqs[3*i] * refx - ineqs[3*i+1] * refy - ineqs[3*i+2]);
7523       SCIPdebugMsg(scip, "constant shift of inequality %d = %.16f\n", i, constshift[i]);
7524    }
7525 
7526    /* try to use both inequalities */
7527    if( nineqs == 2 )
7528    {
7529       SCIPcomputeBilinEnvelope2(scip, bilincoef, lbx, ubx, refx, lby, uby, refy, overestimate, ineqs[0], ineqs[1],
7530          ineqs[2] + constshift[0], ineqs[3], ineqs[4], ineqs[5] + constshift[1], &xcoef, &ycoef, &constant, &update);
7531 
7532       if( update )
7533       {
7534          SCIP_Real val = xcoef * refx + ycoef * refy + constant;
7535          SCIP_Real relimpr = 1.0 - (REALABS(val - bilincoef * refx * refy) + 1e-4) / (REALABS(*bestval - bilincoef * refx * refy) + 1e-4);
7536          SCIP_Real absimpr = REALABS(val - (*bestval));
7537 
7538          /* update relaxation if possible */
7539          if( relimpr > 0.05 && absimpr > 1e-3 && ((overestimate && SCIPisRelLT(scip, val, *bestval)) || (!overestimate && SCIPisRelGT(scip, val, *bestval))) )
7540          {
7541             *bestcoefx = xcoef;
7542             *bestcoefy = ycoef;
7543             *bestconst = constant;
7544             *bestval = val;
7545             *success = TRUE;
7546          }
7547       }
7548    }
7549 
7550    /* use inequalities individually */
7551    for( i = 0; i < nineqs; ++i )
7552    {
7553       SCIPcomputeBilinEnvelope1(scip, bilincoef, lbx, ubx, refx, lby, uby, refy, overestimate, ineqs[3*i], ineqs[3*i+1],
7554          ineqs[3*i+2] + constshift[i], &xcoef, &ycoef, &constant, &update);
7555 
7556       if( update )
7557       {
7558          SCIP_Real val = xcoef * refx + ycoef * refy + constant;
7559          SCIP_Real relimpr = 1.0 - (REALABS(val - bilincoef * refx * refy) + 1e-4) / (REALABS(mccormickval - bilincoef * refx * refy) + 1e-4);
7560          SCIP_Real absimpr = REALABS(val - (*bestval));
7561 
7562          /* update relaxation if possible */
7563          if( relimpr > 0.05 && absimpr > 1e-3 && ((overestimate && SCIPisRelLT(scip, val, *bestval)) || (!overestimate && SCIPisRelGT(scip, val, *bestval))) )
7564          {
7565             *bestcoefx = xcoef;
7566             *bestcoefy = ycoef;
7567             *bestconst = constant;
7568             *bestval = val;
7569             *success = TRUE;
7570          }
7571       }
7572    }
7573 }
7574 
7575 /* returns the interiority of a reference point w.r.t. given bounds */
7576 static
getInteriority(SCIP * scip,SCIP_Real lbx,SCIP_Real ubx,SCIP_Real refx,SCIP_Real lby,SCIP_Real uby,SCIP_Real refy)7577 SCIP_Real getInteriority(
7578    SCIP*                 scip,               /**< SCIP data structure */
7579    SCIP_Real             lbx,                /**< lower bound of the first variable */
7580    SCIP_Real             ubx,                /**< upper bound of the first variable  */
7581    SCIP_Real             refx,               /**< reference point of the first variable */
7582    SCIP_Real             lby,                /**< lower bound of the second variable */
7583    SCIP_Real             uby,                /**< upper bound of the second variable  */
7584    SCIP_Real             refy                /**< reference point of the second variable */
7585    )
7586 {
7587    SCIP_Real interiorityx;
7588    SCIP_Real interiorityy;
7589 
7590    interiorityx = MIN(refx-lbx, ubx-refx) / MAX(ubx-lbx, SCIPepsilon(scip)); /*lint !e666*/
7591    interiorityy = MIN(refy-lby, uby-refy) / MAX(uby-lby, SCIPepsilon(scip)); /*lint !e666*/
7592 
7593    return 2.0*MIN(interiorityx, interiorityy);
7594 }
7595 
7596 /** computes cut coefficients for a nonconvex quadratic function */
7597 static
generateCutNonConvex(SCIP * scip,SCIP_CONSHDLRDATA * conshdlrdata,SCIP_CONS * cons,SCIP_SIDETYPE violside,SCIP_Real * ref,SCIP_ROWPREP * rowprep,SCIP_Bool * success)7598 SCIP_RETCODE generateCutNonConvex(
7599    SCIP*                 scip,               /**< SCIP data structure */
7600    SCIP_CONSHDLRDATA*    conshdlrdata,       /**< constraint handler data */
7601    SCIP_CONS*            cons,               /**< constraint */
7602    SCIP_SIDETYPE         violside,           /**< side for which to generate cut */
7603    SCIP_Real*            ref,                /**< reference solution where to generate the cut */
7604    SCIP_ROWPREP*         rowprep,            /**< rowprep to store cut data */
7605    SCIP_Bool*            success             /**< buffer to indicate whether a cut was successfully computed */
7606    )
7607 {
7608    SCIP_CONSDATA* consdata;
7609    SCIP_BILINTERM* bilinterm;
7610    SCIP_Real sqrcoef;
7611    SCIP_Real coef;
7612    SCIP_Real coef2;
7613    SCIP_Real constant;
7614    SCIP_VAR* var;
7615    int var2pos;
7616    int j;
7617    int k;
7618 
7619    assert(scip != NULL);
7620    assert(conshdlrdata != NULL);
7621    assert(cons != NULL);
7622    assert(ref  != NULL);
7623    assert(success != NULL);
7624 
7625    consdata = SCIPconsGetData(cons);
7626    assert(consdata != NULL);
7627 
7628    rowprep->local = TRUE;
7629    *success = TRUE;
7630 
7631    /* underestimate (secant, McCormick) or linearize each term separately */
7632    for( j = 0; j < consdata->nquadvars && *success; ++j )
7633    {
7634       var = consdata->quadvarterms[j].var;
7635 
7636       /* initialize coefficients to linear coefficients of quadratic variables */
7637       SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, var, consdata->quadvarterms[j].lincoef) );
7638 
7639       sqrcoef = consdata->quadvarterms[j].sqrcoef;
7640       if( sqrcoef != 0.0 )
7641       {
7642          coef = 0.0;
7643          constant = 0.0;
7644          if( (violside == SCIP_SIDETYPE_LEFT  && sqrcoef <= 0.0) || (violside == SCIP_SIDETYPE_RIGHT && sqrcoef > 0.0) )
7645          {
7646             /* convex -> linearize */
7647             SCIPaddSquareLinearization(scip, sqrcoef, ref[j], SCIPvarGetType(var) < SCIP_VARTYPE_CONTINUOUS, &coef,
7648                &constant, success);
7649          }
7650          else
7651          {
7652             /* not convex -> secant approximation */
7653             SCIPaddSquareSecant(scip, sqrcoef, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), ref[j], &coef,
7654                &constant, success);
7655          }
7656          SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, var, coef) );
7657          SCIPaddRowprepConstant(rowprep, constant);
7658       }
7659 
7660       /* relax each bilinear term */
7661       for( k = 0; k < consdata->quadvarterms[j].nadjbilin && (*success); ++k )
7662       {
7663          SCIP_VAR* x;
7664          SCIP_VAR* y;
7665          SCIP_Real refx;
7666          SCIP_Real refy;
7667          SCIP_Real lbx;
7668          SCIP_Real ubx;
7669          SCIP_Real lby;
7670          SCIP_Real uby;
7671          int idx;
7672 
7673          idx = consdata->quadvarterms[j].adjbilin[k];
7674          bilinterm = &consdata->bilinterms[idx];
7675          if( bilinterm->var1 != var )
7676             continue;
7677          assert(bilinterm->var2 != var);
7678          assert(consdata->sepabilinvar2pos != NULL);
7679 
7680          var2pos = consdata->sepabilinvar2pos[consdata->quadvarterms[j].adjbilin[k]];
7681          assert(var2pos >= 0);
7682          assert(var2pos < consdata->nquadvars);
7683          assert(consdata->quadvarterms[var2pos].var == bilinterm->var2);
7684 
7685          /* get data of the variables in the bilinear term */
7686          x = var;
7687          y = bilinterm->var2;
7688          refx = ref[j];
7689          refy = ref[var2pos];
7690          lbx = SCIPvarGetLbLocal(x);
7691          ubx = SCIPvarGetUbLocal(x);
7692          lby = SCIPvarGetLbLocal(y);
7693          uby = SCIPvarGetUbLocal(y);
7694          SCIPdebugMsg(scip, "bilinear term %g %s %s with (%g,%g) in [%g,%g]x[%g,%g] overestimate=%u\n", bilinterm->coef,
7695             SCIPvarGetName(x), SCIPvarGetName(y), refx, refy, lbx, ubx, lby, uby, violside == SCIP_SIDETYPE_LEFT);
7696 
7697          /* use the McCormick relaxation for under- or overestimating the bilinear term */
7698          coef = 0.0;
7699          coef2 = 0.0;
7700          constant = 0.0;
7701          SCIPaddBilinMcCormick(scip, bilinterm->coef, lbx, ubx, refx, lby, uby, refy,
7702             violside == SCIP_SIDETYPE_LEFT, &coef, &coef2, &constant, success);
7703          SCIPdebugMsg(scip, "McCormick = %g (%u)\n", refx * coef + refy * coef2 + constant, *success);
7704 
7705          /* tries to compute a tighter relaxation for xy by using valid linear inequalities */
7706          if( consdata->bilintermsidx != NULL && conshdlrdata->bilinestimators != NULL
7707             && ubx - lbx >= 0.1 && uby - lby >= 0.1
7708             && (SCIPgetNSepaRounds(scip) <= conshdlrdata->bilinineqmaxseparounds || SCIPgetDepth(scip) == 0) )
7709          {
7710             BILINESTIMATOR* bilinestimator;
7711             SCIP_Real mccormick;
7712             SCIP_Real score;
7713             int bilintermidx;
7714 
7715             mccormick = refx * coef + refy * coef2 + constant;
7716             score = getInteriority(scip, lbx, ubx, refx, lby, uby, refy);
7717 
7718             /* get data for bilinear term */
7719             bilintermidx = consdata->bilintermsidx[idx];
7720             assert(conshdlrdata->bilinestimators != NULL);
7721             bilinestimator = &(conshdlrdata->bilinestimators[bilintermidx]);
7722             assert(bilinestimator->x == x);
7723             assert(bilinestimator->y == y);
7724 
7725             /* reset the last improvement factor (used for getting better branching decisions) */
7726             bilinestimator->lastimprfac = 0.0;
7727 
7728             /* compute tighter relaxation for xy if the current score is large enough */
7729             if( SCIPisGE(scip, score, conshdlrdata->minscorebilinterms)
7730                && bilinestimator->nineqoverest + bilinestimator->ninequnderest > 0 )
7731             {
7732                SCIP_Real bestval = mccormick;
7733                SCIP_Bool updaterelax = FALSE;
7734 
7735                /*
7736                 * note that we check the sign of the bilinear coefficient together with violside in
7737                 * updateBilinearRelaxation in order to decide whether a valid under- or overestimate can be computed
7738                 */
7739 
7740                /* use overestimates */
7741                updateBilinearRelaxation(scip, x, y, bilinterm->coef, violside, refx, refy, bilinestimator->ineqoverest,
7742                   bilinestimator->nineqoverest, mccormick, &coef, &coef2, &constant, &bestval, &updaterelax);
7743 
7744                /* use underestimates */
7745                updateBilinearRelaxation(scip, x, y, bilinterm->coef, violside, refx, refy, bilinestimator->inequnderest,
7746                   bilinestimator->ninequnderest, mccormick, &coef, &coef2, &constant, &bestval, &updaterelax);
7747 
7748                SCIPdebugMsg(scip, "found better relaxation value: %u (%g)\n", updaterelax, bestval);
7749 
7750                /* check whether the new relaxation is under- or overestimating xy properly */
7751                if( updaterelax )
7752                {
7753                   /* update improvement factor */
7754                   bilinestimator->lastimprfac = 1.0 - REALABS(bestval - bilinterm->coef * refx * refy) / REALABS(mccormick - bilinterm->coef * refx * refy);
7755 
7756 #ifndef NDEBUG
7757                   assert(SCIPisEQ(scip, bestval, coef * refx + coef2 * refy + constant));
7758                   if( violside == SCIP_SIDETYPE_LEFT )
7759                   {
7760                      assert(SCIPisRelGE(scip, bestval, bilinterm->coef * refx * refy));
7761                      assert(SCIPisRelLE(scip, bestval, mccormick));
7762                   }
7763                   else
7764                   {
7765                      assert(SCIPisRelLE(scip, bestval, bilinterm->coef * refx * refy));
7766                      assert(SCIPisRelGE(scip, bestval, mccormick));
7767                   }
7768 #endif
7769                }
7770             }
7771          }
7772 
7773          SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, var, coef) );
7774          SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, bilinterm->var2, coef2) );
7775          SCIPaddRowprepConstant(rowprep, constant);
7776       }
7777    }
7778 
7779    if( !*success )
7780    {
7781       SCIPdebugMsg(scip, "no success to find estimator for nonconvex <%s>\n", SCIPconsGetName(cons));
7782       return SCIP_OKAY;
7783    }
7784 
7785    rowprep->sidetype = violside;
7786    SCIPaddRowprepSide(rowprep, violside == SCIP_SIDETYPE_LEFT ? consdata->lhs : consdata->rhs);
7787 
7788    (void) SCIPsnprintf(rowprep->name, SCIP_MAXSTRLEN, "%s_side%d_estimation_%" SCIP_LONGINT_FORMAT, SCIPconsGetName(cons), violside, SCIPgetNLPs(scip));
7789 
7790    return SCIP_OKAY;
7791 }
7792 
7793 /** generates a cut based on linearization (if convex) or McCormick (if nonconvex) in a given reference point */
7794 static
generateCut(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_Real * ref,SCIP_SOL * sol,SCIP_SIDETYPE violside,SCIP_ROW ** row,SCIP_Real * efficacy,SCIP_Bool checkcurvmultivar,SCIP_Real minefficacy)7795 SCIP_RETCODE generateCut(
7796    SCIP*                 scip,               /**< SCIP data structure */
7797    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
7798    SCIP_CONS*            cons,               /**< constraint */
7799    SCIP_Real*            ref,                /**< reference solution where to generate the cut */
7800    SCIP_SOL*             sol,                /**< point that we aim to separate, or NULL for LP solution */
7801    SCIP_SIDETYPE         violside,           /**< for which side a cut should be generated */
7802    SCIP_ROW**            row,                /**< storage for cut */
7803    SCIP_Real*            efficacy,           /**< buffer to store efficacy of row in reference solution, or NULL if not of interest */
7804    SCIP_Bool             checkcurvmultivar,  /**< are we allowed to check the curvature of a multivariate quadratic function, if not done yet */
7805    SCIP_Real             minefficacy         /**< minimal required efficacy */
7806    )
7807 {
7808    SCIP_CONSHDLRDATA* conshdlrdata;
7809    SCIP_CONSDATA* consdata;
7810    SCIP_ROWPREP*  rowprep;
7811    SCIP_Bool      success;
7812    SCIP_Real      viol = 0.0;
7813 
7814    assert(scip != NULL);
7815    assert(conshdlr != NULL);
7816    assert(cons != NULL);
7817    assert(ref != NULL);
7818    assert(row != NULL);
7819 
7820    conshdlrdata = SCIPconshdlrGetData(conshdlr);
7821    assert(conshdlrdata != NULL);
7822 
7823    consdata = SCIPconsGetData(cons);
7824    assert(consdata != NULL);
7825    assert(violside != SCIP_SIDETYPE_LEFT  || !SCIPisInfinity(scip, -consdata->lhs));
7826    assert(violside != SCIP_SIDETYPE_RIGHT || !SCIPisInfinity(scip,  consdata->rhs));
7827 
7828    *row = NULL;
7829 
7830    SCIP_CALL( SCIPcreateRowprep(scip, &rowprep, SCIP_SIDETYPE_RIGHT, SCIPconsIsLocal(cons)) );
7831    success = FALSE;
7832 
7833    /* if constraint function is factorable, then try to use factorable form to generate cut */
7834    if( consdata->factorleft != NULL )
7835    {
7836       if( consdata->nlinvars == 0 )
7837       {
7838          SCIP_CALL( generateCutFactorable(scip, cons, violside, ref, rowprep, &success) );
7839       }
7840       else if( sol != NULL || SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_OPTIMAL )
7841       {
7842          /* generateCutLTI needs reference values also for the linear variables, which we only have if sol is given or LP has been solved */
7843          SCIP_CALL( generateCutLTI(scip, cons, violside, ref, sol, rowprep, &success) );
7844       }
7845    }
7846 
7847    /* if constraint is not factorable or failed to generate cut, try default method */
7848    if( !success )
7849    {
7850       SCIP_CALL( checkCurvature(scip, cons, checkcurvmultivar) );
7851 
7852       if( (violside == SCIP_SIDETYPE_LEFT && consdata->isconcave) || (violside == SCIP_SIDETYPE_RIGHT && consdata->isconvex) )
7853       {
7854          SCIP_CALL( generateCutConvex(scip, cons, violside, ref, rowprep, &success) );
7855       }
7856       else
7857       {
7858          SCIP_CALL( generateCutNonConvex(scip, conshdlrdata, cons, violside, ref, rowprep, &success) );
7859       }
7860 
7861       SCIP_CALL( SCIPaddRowprepTerms(scip, rowprep, consdata->nlinvars, consdata->linvars, consdata->lincoefs) );
7862    }
7863 
7864    /* check if reference point violates cut at least a little bit */
7865    if( success && !SCIPisInfinity(scip, -minefficacy) )
7866    {
7867       viol = SCIPgetRowprepViolation(scip, rowprep, sol);
7868       if( viol <= 0.0 ) /*lint !e644*/
7869       {
7870          SCIPdebugMsg(scip, "skip cut for constraint <%s> because efficacy %g too low (< %g)\n", SCIPconsGetName(cons), viol, minefficacy);
7871          success = FALSE;
7872       }
7873    }
7874 
7875    /* cleanup and improve cut */
7876    if( success )
7877    {
7878       SCIP_Real coefrange;
7879 
7880       /* merge terms */
7881       SCIPmergeRowprepTerms(scip, rowprep);
7882 
7883       /* improve coefficients */
7884       SCIP_CALL( SCIPcleanupRowprep(scip, rowprep, sol, conshdlrdata->cutmaxrange, minefficacy, &coefrange, &viol) );
7885       success = coefrange <= conshdlrdata->cutmaxrange;
7886    }
7887 
7888    /* check that side is finite */ /*lint --e{514} */
7889    success &= !SCIPisInfinity(scip, REALABS(rowprep->side)); /*lint !e514*/
7890 
7891    /* check whether maximal coef is finite, if any */ /*lint --e{514} */
7892    success &= (rowprep->nvars == 0) || !SCIPisInfinity(scip, REALABS(rowprep->coefs[0])); /*lint !e514*/
7893 
7894    /* check if reference point violates cut sufficiently */
7895    if( success && !SCIPisInfinity(scip, -minefficacy) && viol < minefficacy ) /*lint !e644*/
7896    {
7897       SCIPdebugMsg(scip, "skip cut for constraint <%s> because efficacy %g too low (< %g)\n", SCIPconsGetName(cons), viol, minefficacy);
7898       success = FALSE;
7899    }
7900 
7901    /* generate row */
7902    if( success )
7903    {
7904       SCIP_CALL( SCIPgetRowprepRowCons(scip, row, rowprep, cons) );
7905 
7906       SCIPdebugMsg(scip, "found cut <%s>, lhs=%g, rhs=%g, mincoef=%g, maxcoef=%g, range=%g, nnz=%d, efficacy=%g\n",
7907          SCIProwGetName(*row), SCIProwGetLhs(*row), SCIProwGetRhs(*row),
7908          rowprep->nvars > 0 ? rowprep->coefs[rowprep->nvars-1] : 0.0, rowprep->nvars > 0 ? rowprep->coefs[0] : 0.0,
7909          rowprep->nvars > 0 ? rowprep->coefs[0]/rowprep->coefs[rowprep->nvars-1] : 1.0,
7910          SCIProwGetNNonz(*row), viol);  /*lint !e414 */
7911 
7912       if( efficacy != NULL )
7913          *efficacy = viol;
7914    }
7915 
7916    SCIPfreeRowprep(scip, &rowprep);
7917 
7918    return SCIP_OKAY;
7919 }
7920 
7921 /** computes eigen decomposition of A, where \f$ f(x) = x^T A x + b^T x \f$.
7922  *
7923  * The eigen decomposition is given by A = P D P^T, where D is diagonal formed by the eigenvalues and P is orthonormal
7924  * whose columns are the eigenvectors; we also compute b^T * P, in case one needs the change of variables P^T x = y <=>
7925  * x = P y We store P^T in an array, specifically, in consdata->eigenvectors we store P^T row-wise, i.e., the first row
7926  * of P^T is stored in eigenvector[0..n-1], the second row is stored in eigenvectors[n..2n-1], etc; equivalently, the
7927  * first eigenvector is eigenvector[0..n-1], the second one is eigenvectors[n..2n-1], etc.
7928  *
7929  * @todo: - at the moment of writing, checkCurvature computes the eigenvalues (and vectors) for determining curvature
7930  *          when it can't to it via other considerations. so one could try to merge both methods together.
7931  *        - it seems that if A is of the form [I 0; 0 A'], one only needs to compute the decomposition for A' so one
7932  *          could do better in terms of memory and speed. For instance, when the matrix is diagonal, the eigenvectors
7933  *          are the identity matrix and the eigenvalues are readily available from the constraint, so one could adapt
7934  *          the functions that uses the eigenvectors in this particular case. One could also think about storing the
7935  *          eigenvectors in a sparse fashion, though eigenvectors are seldom sparse.
7936  */
7937 static
computeED(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons)7938 SCIP_RETCODE computeED(
7939    SCIP*                 scip,               /**< SCIP data structure */
7940    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
7941    SCIP_CONS*            cons                /**< constraint */
7942    )
7943 {
7944    SCIP_CONSDATA* consdata;
7945    int n;
7946    int nn;
7947    int row;
7948    int col;
7949    int i;
7950    int j;
7951    double*        matrix;
7952    SCIP_HASHMAP*  var2index;
7953 
7954    SCIPdebugMsg(scip, "computing ED for cons %s\n", SCIPconsGetName(cons));
7955 
7956    assert(scip != NULL);
7957    assert(conshdlr != NULL);
7958    assert(cons != NULL);
7959 
7960    consdata = SCIPconsGetData(cons);
7961    assert(consdata != NULL);
7962 
7963    /* function has to be convex with finite rhs or concave with finite lhs */
7964    assert((consdata->isconvex && !SCIPisInfinity(scip, consdata->rhs)) ||
7965          (consdata->isconcave && !SCIPisInfinity(scip, -consdata->lhs)));
7966 
7967    /* can't compute eigenvectors without IPOPT */
7968    if( !SCIPisIpoptAvailableIpopt() )
7969    {
7970       consdata->isedavailable = FALSE;
7971       return SCIP_OKAY;
7972    }
7973 
7974    /* @todo: - it seems that if A is of the form [I 0; 0 A'], one only needs to compute the decomposition for A'
7975     *          so one could do better in terms of memory and speed
7976     *        - if n too big don't compute SVD
7977     */
7978    n = consdata->nquadvars;
7979 
7980    /* do not compute eigendecomposition if n is too large */
7981    nn = n * n;
7982    if( nn < 0 || (unsigned) (int) nn > UINT_MAX / sizeof(SCIP_Real) )
7983    {
7984       SCIPdebugMsg(scip, "n is too large to compute eigendecomposition\n");
7985       consdata->isedavailable = FALSE;
7986       return SCIP_OKAY;
7987    }
7988 
7989    /* we just need to pass the upper triangle of A since it is symmetric; build it here */
7990    SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->eigenvectors, nn) );
7991    matrix = consdata->eigenvectors;
7992    BMSclearMemoryArray(matrix, nn);
7993 
7994    /* @todo if we are called in solving stage (or late from initsol), we can avoid the hashmap by using sepabilinvar2pos */
7995    SCIP_CALL( SCIPhashmapCreate(&var2index, SCIPblkmem(scip), n) );
7996 
7997    for( i = 0; i < n; ++i )
7998    {
7999       SCIP_CALL( SCIPhashmapInsertInt(var2index, consdata->quadvarterms[i].var, i) );
8000       matrix[i*n + i] = consdata->quadvarterms[i].sqrcoef;
8001 #ifdef DEBUG_PROJ
8002       printf("inserting in position %d, value %g\n", i*n + i, consdata->quadvarterms[i].sqrcoef);
8003 #endif
8004    }
8005 
8006    for( i = 0; i < consdata->nbilinterms; ++i )
8007    {
8008       assert(SCIPhashmapExists(var2index, consdata->bilinterms[i].var1));
8009       assert(SCIPhashmapExists(var2index, consdata->bilinterms[i].var2));
8010       row = SCIPhashmapGetImageInt(var2index, consdata->bilinterms[i].var1);
8011       col = SCIPhashmapGetImageInt(var2index, consdata->bilinterms[i].var2);
8012       if( row < col )
8013       {
8014          matrix[row * n + col] = consdata->bilinterms[i].coef/2;
8015 #ifdef DEBUG_PROJ
8016          printf("inserting in position %d, value %g\n", row*n + col, consdata->bilinterms[i].coef/2);
8017 #endif
8018       }
8019       else
8020       {
8021          matrix[col * n + row] = consdata->bilinterms[i].coef/2;
8022 #ifdef DEBUG_PROJ
8023          printf("inserting in position %d, value %g\n", col*n + row, consdata->bilinterms[i].coef/2);
8024 #endif
8025       }
8026    }
8027 
8028 #ifdef DEBUG_PROJ
8029    printf("matrix built:\n");
8030    for( i = 0; i < n; i++ )
8031    {
8032       for( j = 0; j < n; j++ )
8033          printf("%g ", matrix[i*n + j]);
8034       printf("\n");
8035    }
8036 #endif
8037 
8038    /* compute eigenvalues and eigenvectors */
8039    SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->eigenvalues, n) );
8040 
8041    if( LapackDsyev(TRUE, n, matrix, consdata->eigenvalues) != SCIP_OKAY )
8042    {
8043       SCIPdebugMsg(scip, "couldn't compute ED for cons %s\n", SCIPconsGetName(cons));
8044       consdata->isedavailable = FALSE;
8045    }
8046    else
8047    {
8048       consdata->isedavailable = TRUE;
8049 
8050       /* compute b^T*P */
8051       SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &consdata->bp, n) );
8052       for( i = 0; i < n; i++ )
8053          for( j = 0; j < n; j++ )
8054             consdata->bp[i] += consdata->quadvarterms[j].lincoef * matrix[i*n + j];
8055 
8056 #ifdef DEBUG_PROJ
8057       printf("eigenvalues:\n");
8058       for( j = 0; j < n; j++ )
8059          printf("%g ", consdata->eigenvalues[j]);
8060 
8061       printf("\neigenvectors (P^T):\n");
8062       for( i = 0; i < n; i++ )
8063       {
8064          for( j = 0; j < n; j++ )
8065             printf("%g ", matrix[i*n + j]);
8066          printf("\n");
8067       }
8068 
8069       printf("b*P^T:\n");
8070       for( j = 0; j < n; j++ )
8071          printf("%g ", consdata->bp[j]);
8072       printf("svd computed successfully\n");
8073 #endif
8074    }
8075 
8076    SCIPhashmapFree(&var2index);
8077 
8078    return SCIP_OKAY;
8079 }
8080 
8081 /** computes an interior point for the quadratic part of the convex constraint
8082  *
8083  *  There are different methods for computing the interior point
8084  *  - 'a'ny: solves min 0, f(x) <= rhs, x in bounds
8085  *  - 'm'ost interior: solves min f(x), x in bounds
8086  *
8087  * @todo: other methods for computing an interior point?
8088  */
8089 static
computeInteriorPoint(SCIP * scip,SCIP_CONS * cons,char method,SCIP_Bool * success)8090 SCIP_RETCODE computeInteriorPoint(
8091    SCIP*                 scip,               /**< SCIP data structure */
8092    SCIP_CONS*            cons,               /**< constraint */
8093    char                  method,             /**< method for computing interior point ('a' any point, 'm'ost interior) */
8094    SCIP_Bool*            success             /**< buffer to store if an interior point was found */
8095    )
8096 {
8097    SCIP_CONSDATA* consdata;
8098    SCIP_QUADELEM* nlrowquadelems;
8099    SCIP_NLPIPROBLEM* prob;
8100    SCIP_NLPI* nlpi;
8101    SCIP_Real* interiorpoint;
8102    SCIP_Real* lbs;
8103    SCIP_Real* ubs;
8104    SCIP_Real* lincoefs;
8105    SCIP_Real nlpiside;
8106    char probname[SCIP_MAXSTRLEN];
8107    int* lininds;
8108    int nlrownquadelems;
8109    int nquadvars;
8110    int i;
8111 
8112    assert(scip != NULL);
8113    assert(cons != NULL);
8114 
8115    assert(success != NULL);
8116    *success = FALSE;
8117 
8118    consdata = SCIPconsGetData(cons);
8119    assert(consdata != NULL);
8120 
8121    assert((consdata->isconvex && !SCIPisInfinity(scip, consdata->rhs)) ||
8122          (consdata->isconcave && !SCIPisInfinity(scip, -consdata->lhs)));
8123 
8124    /* need an NLP solver */
8125    if( SCIPgetNNlpis(scip) == 0 )
8126       return SCIP_OKAY;
8127 
8128    prob = NULL;
8129    lbs = NULL;
8130    ubs = NULL;
8131    lincoefs = NULL;
8132    lininds = NULL;
8133 
8134 #ifdef SCIP_DEBUG_INT
8135    SCIPinfoMessage(scip, NULL, "Computing interior point for\n");
8136    SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
8137    SCIPinfoMessage(scip, NULL, ";\n");
8138 #endif
8139 
8140    /* in the convex case, we try to find an interior point of x^T A x + b^T x <= rhs - maximum activity linear part
8141     * in the concave case: lhs - minimum activity linear part <= x^T A x + b^T x; we compute activities ourselves,
8142     * since consdata->max(min)linactivity are only computed when lhs (rhs) is finite and this not always holds
8143     */
8144    if( consdata->isconvex )
8145    {
8146       /* compute maximum activity */
8147       nlpiside = 0;
8148       for( i = 0; i < consdata->nlinvars; ++i )
8149       {
8150          if( consdata->lincoefs[i] >= 0.0 )
8151          {
8152             if( SCIPisInfinity(scip, SCIPvarGetUbLocal(consdata->linvars[i]) ) )
8153                nlpiside = SCIPinfinity(scip);
8154             else
8155                nlpiside += consdata->lincoefs[i] * SCIPvarGetUbLocal(consdata->linvars[i]);
8156          }
8157          else
8158          {
8159             if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(consdata->linvars[i]) ) )
8160                nlpiside = SCIPinfinity(scip);
8161             else
8162                nlpiside += consdata->lincoefs[i] * SCIPvarGetLbLocal(consdata->linvars[i]);
8163          }
8164 
8165          if( SCIPisInfinity(scip, nlpiside) )
8166          {
8167             SCIPdebugMsg(scip, "maximum activity is infinity: there is no interior point for fun <= rhs - maxlinactivity!\n");
8168             return SCIP_OKAY;
8169          }
8170       }
8171 
8172       if( consdata->nlinvars == 0 )
8173          nlpiside = INTERIOR_EPS;
8174 
8175       nlpiside = consdata->rhs - nlpiside;
8176    }
8177    else
8178    {
8179       /* compute minimum activity */
8180       nlpiside = 0;
8181       for( i = 0; i < consdata->nlinvars; ++i )
8182       {
8183          if( consdata->lincoefs[i] >= 0.0 )
8184          {
8185             if( SCIPisInfinity(scip, -SCIPvarGetLbLocal(consdata->linvars[i])) )
8186                nlpiside = -SCIPinfinity(scip);
8187             else
8188                nlpiside += consdata->lincoefs[i] * SCIPvarGetLbLocal(consdata->linvars[i]);
8189          }
8190          else
8191          {
8192             if( SCIPisInfinity(scip, SCIPvarGetUbLocal(consdata->linvars[i])) )
8193                nlpiside = -SCIPinfinity(scip);
8194             else
8195                nlpiside += consdata->lincoefs[i] * SCIPvarGetUbLocal(consdata->linvars[i]);
8196          }
8197 
8198          if( SCIPisInfinity(scip,  -nlpiside) )
8199          {
8200             SCIPdebugMsg(scip, "minimum activity is -infinity: there is no interior point for fun >= lhs - minlinactivity!\n");
8201             return SCIP_OKAY;
8202          }
8203       }
8204 
8205       if( consdata->nlinvars == 0 )
8206          nlpiside = INTERIOR_EPS;
8207 
8208       nlpiside = consdata->lhs - nlpiside;
8209    }
8210 
8211    nquadvars = consdata->nquadvars;
8212 
8213    /* if we are looking for any interior point and the 0 is one, then use it */
8214    if( method == 'a' && ((consdata->isconvex && SCIPisGE(scip, nlpiside, 0.0))
8215             || (consdata->isconcave && SCIPisLE(scip, nlpiside, 0.0))) )
8216    {
8217 #ifdef SCIP_DEBUG_INT
8218       SCIPinfoMessage(scip, NULL, "Computation successful, 0 is interior point.\n");
8219 #endif
8220 
8221       SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &(consdata->interiorpoint), nquadvars) );
8222       *success = TRUE;
8223       return SCIP_OKAY;
8224    }
8225 
8226    /* build nlrow */
8227    if( consdata->nlrow == NULL )
8228    {
8229       SCIP_CALL( createNlRow(scip, cons) );
8230       assert(consdata->nlrow != NULL);
8231    }
8232 
8233    nlpi = SCIPgetNlpis(scip)[0];
8234    assert(nlpi != NULL);
8235 
8236    /* initializing the subproblem */
8237    (void) SCIPsnprintf(probname, SCIP_MAXSTRLEN, "%s_subquad", SCIPgetProbName(scip));
8238    SCIP_CALL( SCIPnlpiCreateProblem(nlpi, &prob, probname) );
8239    if( prob == NULL )
8240    {
8241 #ifdef SCIP_DEBUG_INT
8242       SCIPinfoMessage(scip, NULL, "Creation of interior point problem failed..\n");
8243 #endif
8244       return SCIP_OKAY;
8245    }
8246    assert(prob != NULL);
8247 
8248 #ifdef SCIP_DEBUG_INT
8249    SCIP_CALL( SCIPnlpiSetIntPar(nlpi, prob, SCIP_NLPPAR_VERBLEVEL, 0) );
8250 #endif
8251    /* TODO: maybe one should set some generous iteration limit and/or a timelimit (remaining scip solve time)? */
8252 
8253    /* ask for memory to store data needed to create vars and linear coefficients */
8254    SCIP_CALL( SCIPallocBufferArray(scip, &lbs, nquadvars) );
8255    SCIP_CALL( SCIPallocBufferArray(scip, &ubs, nquadvars) );
8256    SCIP_CALL( SCIPallocBufferArray(scip, &lininds, nquadvars) );
8257    SCIP_CALL( SCIPallocBufferArray(scip, &lincoefs, nquadvars) );
8258 
8259    /* get bounds and linear coefficients */
8260    for( i = 0; i < nquadvars; i++ )
8261    {
8262       lbs[i] = SCIPvarGetLbGlobal(consdata->quadvarterms[i].var);
8263       ubs[i] = SCIPvarGetUbGlobal(consdata->quadvarterms[i].var);
8264 
8265       lincoefs[i] = consdata->quadvarterms[i].lincoef;
8266       lininds[i] = i;
8267    }
8268 
8269    /* add vars */
8270    SCIP_CALL( SCIPnlpiAddVars(nlpi, prob, nquadvars, lbs, ubs, NULL) );
8271 
8272    /* get nlrow info */
8273    nlrownquadelems = SCIPnlrowGetNQuadElems(consdata->nlrow);
8274    nlrowquadelems = SCIPnlrowGetQuadElems(consdata->nlrow);
8275 
8276 #ifndef NDEBUG
8277    {
8278       SCIP_VAR** nlrowquadvars;
8279 
8280       nlrowquadvars = SCIPnlrowGetQuadVars(consdata->nlrow);
8281       for( i = 0; i < nlrownquadelems; i++ )
8282       {
8283          assert(nlrowquadvars[nlrowquadelems[i].idx1] == consdata->quadvarterms[nlrowquadelems[i].idx1].var);
8284          assert(nlrowquadvars[nlrowquadelems[i].idx2] == consdata->quadvarterms[nlrowquadelems[i].idx2].var);
8285       }
8286    }
8287 #endif
8288 
8289    (void) SCIPsnprintf(probname, SCIP_MAXSTRLEN, "%s", SCIPconsGetName(cons));
8290 
8291    switch( method )
8292    {
8293       case 'a':
8294          /* add constraint */
8295          if( consdata->isconvex )
8296          {
8297             SCIP_CALL( SCIPnlpiAddConstraints(nlpi, prob, 1, NULL, &nlpiside, &nquadvars, &lininds, &lincoefs,
8298                      &nlrownquadelems, &nlrowquadelems, NULL, NULL, NULL) );
8299          }
8300          else
8301          {
8302             SCIP_CALL( SCIPnlpiAddConstraints(nlpi, prob, 1, &nlpiside, NULL, &nquadvars, &lininds, &lincoefs,
8303                      &nlrownquadelems, &nlrowquadelems, NULL, NULL, NULL) );
8304          }
8305          break;
8306 
8307       case 'm':
8308          /* add objective */
8309          if( consdata->isconvex )
8310          {
8311             SCIP_CALL( SCIPnlpiSetObjective(nlpi, prob, nquadvars, lininds, lincoefs,
8312                      nlrownquadelems, nlrowquadelems, NULL, NULL, 0.0) );
8313          }
8314          else
8315          {
8316             /* NLPI assumes minimization: change signs */
8317             for( i = 0; i < nquadvars; i++ )
8318                lincoefs[i] *= -1;
8319 
8320             /* WARNING: this pointer is not ours, information should be restored! */
8321             for( i = 0; i < nlrownquadelems; i++ )
8322                nlrowquadelems->coef *= -1;
8323 
8324             SCIP_CALL( SCIPnlpiSetObjective(nlpi, prob, nquadvars, lininds, lincoefs,
8325                      nlrownquadelems, nlrowquadelems, NULL, NULL, 0.0) );
8326 
8327             /* WARNING: restore information! */
8328             for( i = 0; i < nlrownquadelems; i++ )
8329                nlrowquadelems->coef *= -1;
8330          }
8331          break;
8332 
8333       default:
8334          SCIPerrorMessage("undefined method for computing interior point: %c\n", method);
8335          return SCIP_INVALIDDATA;
8336    }
8337 
8338    /* set NLP tolerances; we don't really need an optimal solution to this NLP */
8339    SCIP_CALL( SCIPnlpiSetRealPar(nlpi, prob, SCIP_NLPPAR_FEASTOL, SCIPfeastol(scip)) ); /*lint !e666*/
8340    SCIP_CALL( SCIPnlpiSetRealPar(nlpi, prob, SCIP_NLPPAR_RELOBJTOL, MAX(SCIPfeastol(scip), SCIPdualfeastol(scip))) ); /*lint !e666*/
8341 
8342    /* solve NLP problem */
8343    SCIP_CALL( SCIPnlpiSolve(nlpi, prob) );
8344 
8345    /* check termination status */
8346    if( SCIPnlpiGetTermstat(nlpi, prob) != SCIP_NLPTERMSTAT_OKAY )
8347    {
8348       SCIPdebugMsg(scip, "cons <%s>: NLP Solver termination status not okay: %d\n",
8349             SCIPconsGetName(cons), SCIPnlpiGetTermstat(nlpi, prob));
8350       *success = FALSE;
8351       goto TERMINATE;
8352    }
8353 
8354    /* check solution status */
8355    switch( SCIPnlpiGetSolstat(nlpi, prob) )
8356    {
8357       case SCIP_NLPSOLSTAT_GLOBOPT:
8358       case SCIP_NLPSOLSTAT_LOCOPT:
8359       case SCIP_NLPSOLSTAT_FEASIBLE:
8360          /* fallthrough */
8361          SCIPdebugMsg(scip, "cons <%s>: found an interior point.  solution status: %d, termination status: %d\n",
8362                SCIPconsGetName(cons), SCIPnlpiGetSolstat(nlpi, prob), SCIPnlpiGetTermstat(nlpi, prob));
8363          break;
8364 
8365       case SCIP_NLPSOLSTAT_LOCINFEASIBLE:
8366       case SCIP_NLPSOLSTAT_GLOBINFEASIBLE:
8367       case SCIP_NLPSOLSTAT_UNKNOWN:
8368          /* fallthrough */
8369          /* TODO: we could still use the point, and let evaluateGauge decide whether the point is interior or not */
8370          SCIPdebugMsg(scip, "cons <%s>: failed to find an interior point.  solution status: %d, termination status: %d\n",
8371                SCIPconsGetName(cons), SCIPnlpiGetSolstat(nlpi, prob), SCIPnlpiGetTermstat(nlpi, prob));
8372          goto TERMINATE;
8373 
8374       case SCIP_NLPSOLSTAT_UNBOUNDED:
8375       default:
8376          /* fallthrough */
8377          SCIPerrorMessage("cons <%s>: undefined behaviour of NLP Solver.  solution status: %d, termination status: %d\n",
8378                SCIPconsGetName(cons), SCIPnlpiGetSolstat(nlpi, prob), SCIPnlpiGetTermstat(nlpi, prob));
8379          SCIPABORT();
8380          goto TERMINATE; /*lint !e527*/
8381    }
8382 
8383    /* fetch solution
8384     * note: nlpiGetSolution (at least for IPOPT) makes interiorpoint point to the internal solution stored in the
8385     * nlpi problem data structure; we need to copy it here because it will be destroyed once the problem is free'd
8386     */
8387    SCIP_CALL( SCIPnlpiGetSolution(nlpi, prob, &interiorpoint, NULL, NULL, NULL, NULL) );
8388 
8389    SCIP_CALL( SCIPallocBlockMemoryArray(scip, &(consdata->interiorpoint), nquadvars) );
8390 
8391    for( i = 0; i < nquadvars; i++ )
8392    {
8393       if( SCIPisFeasZero(scip, interiorpoint[i]) )
8394          consdata->interiorpoint[i] = 0.0;
8395       else
8396          consdata->interiorpoint[i] = interiorpoint[i];
8397    }
8398 
8399    *success = TRUE;
8400 
8401 TERMINATE:
8402 
8403 #ifdef SCIP_DEBUG_INT
8404    printf("Computation of interior point for cons <%s>:\n", SCIPconsGetName(cons));
8405    printf(" - has %d linear variables\n", consdata->nlinvars);
8406    if( consdata->isconvex )
8407    {
8408       printf(" - is convex. rhs: %g maximum activity of linear variables: %g\n", consdata->rhs, consdata->rhs - nlpiside);
8409       printf(" - searched for point whose quadratic part is <= %g\n", nlpiside);
8410    }
8411    else
8412    {
8413       printf(" - is concave. lhs: %g minimum activity of linear variables: %g\n", consdata->lhs, consdata->lhs - nlpiside);
8414       printf(" - searched for point whose quadratic part is >= %g\n", nlpiside);
8415    }
8416 
8417    if( *success )
8418    {
8419       printf("Computation successful, NLP soltat: %d, termstat: %d\nPoint found:\n",
8420          SCIPnlpiGetSolstat(nlpi, prob), SCIPnlpiGetTermstat(nlpi, prob));
8421       for( i = 0; i < nquadvars; i++ )
8422       {
8423          printf("%s = %g\n", SCIPvarGetName(consdata->quadvarterms[i].var), consdata->interiorpoint[i]);
8424       }
8425    }
8426    else
8427    {
8428       printf("Computation failed. NLP soltat: %d, termstat: %d\n",
8429                SCIPnlpiGetSolstat(nlpi, prob), SCIPnlpiGetTermstat(nlpi, prob));
8430       printf("run with SCIP_DEBUG for more info\n");
8431       SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
8432       SCIPinfoMessage(scip, NULL, ";\n");
8433       /* FIXME: instance camshape100 says that there is no interior point (interior empty)
8434        * is there something intelligent that can be said?
8435        */
8436    }
8437 #endif
8438 
8439    /* free memory */
8440    SCIPfreeBufferArrayNull(scip, &lbs);
8441    SCIPfreeBufferArrayNull(scip, &ubs);
8442    SCIPfreeBufferArrayNull(scip, &lininds);
8443    SCIPfreeBufferArrayNull(scip, &lincoefs);
8444    SCIP_CALL( SCIPnlpiFreeProblem(nlpi, &prob) );
8445 
8446    return SCIP_OKAY;
8447 }
8448 
8449 /** compute gauge function of the set \f$S - s_0\f$ where \f$ S = \{ x : f(x) \le c \}\f$ and \f$ s_0 \in \mathring S\f$.
8450  *
8451  * Here, \f$ f(x) \f$ is a purely quadratic (i.e, all \f$x\f$ variables appear in a bilinear or quadratic term).
8452  * Explicitly, \f$ f(x) = \pm x^T A x \pm b^T x \f$ depending whether \f$A\f$
8453  * is positive semidefinite (+) or negative semidefinite (-).
8454  * The constant \f$c\f$ is rhs - maximum activity of the purely linear part of the constraint
8455  * if \f$A \succeq 0\f$ and minimum activity - lhs if \f$A \preceq 0\f$.
8456  * This is computed only at INITSOL.
8457  *
8458  * The method does:
8459  * 1. compute interior point
8460  * 2. compute gauge function
8461  */
8462 static
computeGauge(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons)8463 SCIP_RETCODE computeGauge(
8464    SCIP*                 scip,               /**< SCIP data structure */
8465    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
8466    SCIP_CONS*            cons                /**< constraint */
8467    )
8468 {
8469    SCIP_CONSHDLRDATA* conshdlrdata;
8470    SCIP_CONSDATA* consdata;
8471    SCIP_QUADVARTERM* quadvarterm;
8472    SCIP_BILINTERM* bilinterm;
8473    SCIP_Bool success;
8474    SCIP_Bool convex;
8475    int i;
8476    int j;
8477 
8478    assert(scip != NULL);
8479    assert(conshdlr != NULL);
8480    assert(cons != NULL);
8481 
8482    consdata = SCIPconsGetData(cons);
8483    assert(consdata != NULL);
8484 
8485    conshdlrdata = SCIPconshdlrGetData(conshdlr);
8486    assert(conshdlrdata != NULL);
8487    assert(conshdlrdata->gaugecuts);
8488 
8489    /* function has to be convex with finite rhs or concave with finite lhs */
8490    convex = consdata->isconvex && !SCIPisInfinity(scip, consdata->rhs);
8491    assert(convex || (consdata->isconcave && !SCIPisInfinity(scip, -consdata->lhs)));
8492 
8493    SCIPdebugMsg(scip, "cons %s: is %s\n", SCIPconsGetName(cons), convex ? "convex" : "concave");
8494 
8495    /* 1. */
8496    SCIP_CALL( computeInteriorPoint(scip, cons, conshdlrdata->interiorcomputation, &success) );
8497 
8498    /* if success, compute gaugecoefs (b_gauge) and gaugeconst (c_gauge) */
8499    if( !success )
8500    {
8501       SCIPdebugMsg(scip, "failed to compute gauge function\n");
8502       consdata->isgaugeavailable = FALSE;
8503       return SCIP_OKAY;
8504    }
8505 
8506    /* 2.
8507     * we are going to evaluate the function at interiorpoint; so, we need to compute interiorpoint^T A interiorpoint;
8508     * therefore, we need a mechanism that for a given variable, it returns its interior point value
8509     * fortunately, sepabilinvar2pos in consdata gives us all the information that we need
8510     */
8511 
8512    SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &(consdata->gaugecoefs), consdata->nquadvars) );
8513 
8514    /* compute value of quadratic part at interior point, build map and compute gaugeconst (c_gauge) */
8515    consdata->interiorpointval = 0;
8516    consdata->gaugeconst = 0;
8517    for( i = 0; i < consdata->nquadvars; i++ )
8518    {
8519       SCIP_Real val;
8520       SCIP_Real val2;
8521 
8522       val = consdata->interiorpoint[i];
8523       quadvarterm = &consdata->quadvarterms[i];
8524 
8525       consdata->interiorpointval += (quadvarterm->lincoef + quadvarterm->sqrcoef * val) * val;
8526       consdata->gaugeconst += quadvarterm->sqrcoef * val * val;
8527 
8528       for( j = 0; j < quadvarterm->nadjbilin; ++j )
8529       {
8530          int bilintermidx;
8531 
8532          bilintermidx = quadvarterm->adjbilin[j];
8533          bilinterm = &consdata->bilinterms[bilintermidx];
8534 
8535          if( bilinterm->var1 != quadvarterm->var )
8536             continue;
8537 
8538          /* the index of the variable associated with var2 in bilinterm should be given by sepabilinvar2pos */
8539          assert(consdata->sepabilinvar2pos != NULL); /* this should have been computed in INITSOL */
8540          assert(consdata->quadvarterms[consdata->sepabilinvar2pos[bilintermidx]].var == bilinterm->var2);
8541 
8542          val2 = consdata->interiorpoint[consdata->sepabilinvar2pos[bilintermidx]];
8543 
8544          consdata->interiorpointval += bilinterm->coef * val * val2;
8545          consdata->gaugeconst += bilinterm->coef * val * val2;
8546       }
8547    }
8548 
8549    /* compute gaugecoefs (b_gauge = b + 2 * A * interiorpoint) */
8550    for( i = 0; i < consdata->nquadvars; i++ )
8551    {
8552       quadvarterm = &consdata->quadvarterms[i];
8553       consdata->gaugecoefs[i] += quadvarterm->lincoef + 2.0 * quadvarterm->sqrcoef * consdata->interiorpoint[i];
8554 
8555       for( j = 0; j < quadvarterm->nadjbilin; j++ )
8556       {
8557          int varpos;
8558          int bilintermidx;
8559 
8560          bilintermidx = quadvarterm->adjbilin[j];
8561          bilinterm = &consdata->bilinterms[bilintermidx];
8562 
8563          if( bilinterm->var1 == quadvarterm->var )
8564          {
8565             varpos = consdata->sepabilinvar2pos[bilintermidx];
8566 
8567             /* the index of the variable associated with var2 in bilinterm should be given by sepabilinvar2pos */
8568             assert(consdata->quadvarterms[varpos].var == bilinterm->var2);
8569 
8570             consdata->gaugecoefs[i] += bilinterm->coef * consdata->interiorpoint[varpos];
8571             consdata->gaugecoefs[varpos] += bilinterm->coef * consdata->interiorpoint[i];
8572          }
8573       }
8574    }
8575 
8576 #ifdef SCIP_DEBUG_INT
8577    printf("quadratic part at interior point: %g\n", consdata->interiorpointval);
8578 
8579    for( j = 0; j < consdata->nquadvars; j++ )
8580    {
8581       printf("b_gauge[%s] = %g\n", SCIPvarGetName(consdata->quadvarterms[j].var), consdata->gaugecoefs[j]);
8582    }
8583    printf("c_gauge = %g\n", consdata->gaugeconst);
8584 #endif
8585 
8586    SCIPdebugMsg(scip, "gauge function computed successfully\n");
8587    consdata->isgaugeavailable = TRUE;
8588 
8589    return SCIP_OKAY;
8590 }
8591 
8592 /** evaluates gauge function of the set \f$S - s_0\f$ where \f$ S = \{ x : f(x) \le c \}\f$ and \f$ s_0 \in \mathring S\f$.
8593  *
8594  * \f$ S = \{ x : f(x) \le c \}\f$ at \f$sol - s_0\f$;
8595  * see computeGauge() for more details
8596  *
8597  * @todo Think about if user should tell that function is convex or ...
8598  */
8599 static
evaluateGauge(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_SOL * refsol,SCIP_Real * gaugeval,SCIP_Bool * success)8600 SCIP_RETCODE evaluateGauge(
8601    SCIP*                 scip,               /**< SCIP data structure */
8602    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
8603    SCIP_CONS*            cons,               /**< constraint */
8604    SCIP_SOL*             refsol,             /**< reference point where to generate cut, or NULL if sol should be used */
8605    SCIP_Real*            gaugeval,           /**< buffer to store the value of the gauge function */
8606    SCIP_Bool*            success             /**< buffer to store if evaluation was successful */
8607    )
8608 {
8609    SCIP_CONSDATA* consdata;
8610    SCIP_Real side;
8611    SCIP_Real aterm;
8612    SCIP_Real bterm;
8613    SCIP_Real cterm;
8614    SCIP_Bool convex;
8615    int i;
8616 
8617    assert(scip != NULL);
8618    assert(conshdlr != NULL);
8619    assert(cons != NULL);
8620 
8621    consdata = SCIPconsGetData(cons);
8622    assert(consdata != NULL);
8623    assert(consdata->isgaugeavailable);
8624 
8625    *success = FALSE;
8626 
8627    convex = consdata->isconvex && !SCIPisInfinity(scip, consdata->rhs);
8628 
8629    SCIPdebugMsg(scip, "cons %s: is %s\n", SCIPconsGetName(cons), convex ? "convex" : "concave");
8630 
8631    /* evaluate gauge function at x0 = (refsol - interior point)
8632     *
8633     * compute aterm = side - function(interior point)
8634     */
8635    if( convex )
8636    {
8637       side = consdata->rhs;
8638       for( i = 0; i < consdata->nlinvars; i++ )
8639          side -= SCIPgetSolVal(scip, refsol, consdata->linvars[i]) * consdata->lincoefs[i];
8640 
8641       aterm = side - consdata->interiorpointval;
8642 
8643       /* it can happen that the interior point is not really interior, since we are not so strict at the moment of
8644        * computing the interior point, which makes sense in the case that the constraint is quadratic <= linear expr,
8645        * since we compute a point in quadratic <= min linear expr and it might be that this set consists of a single
8646        * point which will not be interior. furthermore, if this set is empty, we could just take any point and it could
8647        * happen that for some value of linear expr, the point is actually interior, but for many it could not be.
8648        * also, if min linear expr = -infinity, we might have computed an interior point using some finite value.
8649        * the point will not be an interior point, if and only if aterm is negative.
8650        */
8651 #ifdef SCIP_DEBUG_GAUGE
8652       if( SCIPisLE(scip, aterm, 0.0) )
8653       {
8654          printf("For current level, there is no interior point. ");
8655          printf("rhs: %g level: %.15g interiorpointval: %.15g\n", consdata->rhs, side, consdata->interiorpointval);
8656          if( consdata->nlinvars == 1 )
8657          {
8658             SCIP_VAR* var;
8659 
8660             var = consdata->linvars[0];
8661             printf("var <%s> = %g in [%.15g, %.15g] is linpart\n", SCIPvarGetName(var),
8662                   SCIPgetSolVal(scip, refsol, var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
8663          }
8664       }
8665       else
8666       {
8667          printf("For current level, there is interior point. ");
8668          printf("rhs: %g level: %.15g interiorpointval: %.15g\n", consdata->rhs, side, consdata->interiorpointval);
8669       }
8670 #endif
8671       if( !SCIPisPositive(scip, aterm) )
8672       {
8673          *gaugeval = -1.0;
8674          return SCIP_OKAY;
8675       }
8676    }
8677    else
8678    {
8679       side = consdata->lhs;
8680       for( i = 0; i < consdata->nlinvars; i++ )
8681          side -= SCIPgetSolVal(scip, refsol, consdata->linvars[i]) * consdata->lincoefs[i];
8682 
8683       aterm = side - consdata->interiorpointval;
8684 
8685 #ifdef SCIP_DEBUG_GAUGE
8686       if( SCIPisGE(scip, aterm, 0.0) )
8687       {
8688          printf("For current level, there is no interior point. ");
8689          printf("lhs: %g level: %.15g interiorpointval: %.15g\n", consdata->lhs, side, consdata->interiorpointval);
8690          if( consdata->nlinvars == 1 )
8691          {
8692             SCIP_VAR* var;
8693 
8694             var = consdata->linvars[0];
8695             printf("var <%s> = %g in [%.15g, %.15g] is linpart\n", SCIPvarGetName(var),
8696                   SCIPgetSolVal(scip, refsol, var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
8697          }
8698       }
8699       else
8700       {
8701          printf("For current level, there is interior point. ");
8702          printf("lhs: %g level: %.15g interiorpointval: %.15g\n", consdata->lhs, side, consdata->interiorpointval);
8703       }
8704 #endif
8705       if( !SCIPisNegative(scip, aterm) )
8706       {
8707          *gaugeval = -1.0;
8708          return SCIP_OKAY;
8709       }
8710    }
8711 
8712    /* compute bterm = b_gauge^T * refsol - f(interiorpoint) - c_gauge
8713     * compute cterm = f(refsol) - b_gauge^T * refsol + c_gauge */
8714    bterm = -consdata->interiorpointval - consdata->gaugeconst;
8715    cterm = consdata->gaugeconst;
8716    for( i = 0; i < consdata->nquadvars; i++ )
8717    {
8718       SCIP_Real val;
8719 
8720       val = SCIPgetSolVal(scip, refsol, consdata->quadvarterms[i].var);
8721       bterm += consdata->gaugecoefs[i] * val;
8722       cterm -= consdata->gaugecoefs[i] * val;
8723       cterm += (consdata->quadvarterms[i].lincoef + consdata->quadvarterms[i].sqrcoef * val) * val;
8724    }
8725 
8726    for( i = 0; i < consdata->nbilinterms; i++ )
8727    {
8728       SCIP_VAR* var1;
8729       SCIP_VAR* var2;
8730 
8731       var1 = consdata->bilinterms[i].var1;
8732       var2 = consdata->bilinterms[i].var2;
8733       cterm += consdata->bilinterms[i].coef * SCIPgetSolVal(scip, refsol, var1) * SCIPgetSolVal(scip, refsol, var2);
8734    }
8735 
8736    /* now compute gauge */
8737    if( convex && cterm < 0.0 )
8738    {
8739       assert(SCIPisZero(scip, cterm));
8740       cterm = 0.0;
8741    }
8742    else if( !convex && cterm > 0.0 )
8743    {
8744       assert(SCIPisZero(scip, cterm));
8745       cterm = 0.0;
8746    }
8747    assert(bterm*bterm + 4*aterm*cterm >= 0);
8748 
8749    if( convex )
8750    {
8751       *gaugeval = bterm + sqrt(bterm*bterm + 4 * aterm * cterm);
8752       *gaugeval = *gaugeval / (2 * aterm);
8753    }
8754    else
8755    {
8756       *gaugeval = bterm - sqrt(bterm*bterm + 4 * aterm * cterm);
8757       *gaugeval = *gaugeval / (2 * aterm);
8758    }
8759    assert(!SCIPisNegative(scip, *gaugeval));
8760    *success = TRUE;
8761 
8762 #ifdef SCIP_DEBUG_GAUGE
8763    printf("Gauge's aterm = %g, bterm = %g, cterm = %g\n", aterm, bterm, cterm);
8764 #endif
8765    return SCIP_OKAY;
8766 }
8767 
8768 /** compute projection of refsol onto feasible region of cons; stores the projection in ref
8769  *
8770  * This method solves
8771  * \f[
8772  *      \min \{ ||x - \bar x||^2 : x^T A x + 2 b^T x \le c \}
8773  * \f]
8774  * where \f$ \bar x \f$ is refsol.
8775  * Note that \f$ \bar x \f$ is not feasible, so the optimal solution actually satisfies
8776  * \f[
8777  *      \min \{ ||x - \bar x||^2 : x^T A x + 2 b^T x = c \}
8778  * \f]
8779  * Using the eigendecomposition \f$ A = P D P^T \f$, the change of variables \f$ y = P^T x
8780  * \f$ and the optimality conditions, this reduces to finding \f$ \rho \f$ such that
8781  * \f[
8782  *      y(\rho) = (I + \rho D)^{-1} (\bar y - \rho \bar b)
8783  * \f]
8784  * makes the constraint active. In the previous formula, \f$ \bar y = P^T \bar x\f$ and \f$ \bar b = P^T b \f$.  If \f$
8785  * D \neq 0 \f$, the function
8786  * \f[
8787  *   \varphi(\rho) := y(\rho)^T D y(\rho) + 2 \bar b^T y(\rho) - c
8788  * \f]
8789  * is strictly convex. So this method actually computes the unique 0 of this function using Newton's method.
8790  */
8791 static
computeReferencePointProjection(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_SOL * refsol,SCIP_Real * ref)8792 SCIP_RETCODE computeReferencePointProjection(
8793    SCIP*                 scip,               /**< SCIP data structure */
8794    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
8795    SCIP_CONS*            cons,               /**< constraint */
8796    SCIP_SOL*             refsol,             /**< the given point to project, or NULL if LP solution should be used */
8797    SCIP_Real*            ref                 /**< array to store reference point */
8798    )
8799 {
8800    SCIP_CONSDATA* consdata;
8801    SCIP_Real* pt; /* stores P^T */
8802    SCIP_Real* bp;
8803    SCIP_Real* D;
8804    SCIP_Real* y0_;
8805    SCIP_Real* yrho;
8806    SCIP_Real* yrhoprime;
8807    SCIP_Real c;
8808    SCIP_Real c1;
8809    SCIP_Real c2;
8810    SCIP_Real rho;
8811    SCIP_Real phirho;
8812    SCIP_Real phirhoprime;
8813    SCIP_Bool isconcave;
8814    int iter;
8815    int i;
8816    int j;
8817    int n;
8818 
8819    assert(scip != NULL);
8820    assert(conshdlr != NULL);
8821    assert(cons != NULL);
8822 
8823    consdata = SCIPconsGetData(cons);
8824    assert(consdata != NULL);
8825    assert(consdata->isedavailable);
8826 
8827    SCIPdebugMessage("computing projection\n");
8828 
8829    /* get the data we need */
8830    pt = consdata->eigenvectors;
8831    D  = consdata->eigenvalues;
8832    n  = consdata->nquadvars;
8833    bp = consdata->bp;
8834    c  = consdata->rhs;
8835    c1 = 0;
8836    c2 = 0;
8837    for( i = 0; i < consdata->nlinvars; i++ )
8838    {
8839       c1 += consdata->lincoefs[i] * SCIPgetSolVal(scip, refsol, consdata->linvars[i]);
8840       c2 -= consdata->lincoefs[i] * consdata->lincoefs[i];
8841    }
8842    c2 /= 2.0;
8843 
8844    /* determine if convex or concave */
8845    isconcave = consdata->isconcave;
8846    assert((isconcave && !SCIPisInfinity(scip, -consdata->lhs)) || !SCIPisInfinity(scip, consdata->rhs));
8847 
8848    SCIP_CALL( SCIPallocClearBufferArray(scip, &y0_, n) );
8849    SCIP_CALL( SCIPallocBufferArray(scip, &yrho, n) );
8850    SCIP_CALL( SCIPallocBufferArray(scip, &yrhoprime, n) );
8851 
8852    /* change data if function is concave */
8853    if( isconcave )
8854    {
8855       c  = -consdata->lhs;
8856       c1 = - c1;
8857       for( i = 0; i < n; i++ )
8858       {
8859          D[i]  = -D[i];
8860          bp[i] = -bp[i];
8861       }
8862    }
8863 
8864    /* change coordinates: compute y(0) = x_0' * P */
8865    for( i = 0; i < n; i++ )
8866       for( j = 0; j < n; j++ )
8867          y0_[i] += SCIPgetSolVal(scip, refsol, consdata->quadvarterms[j].var) * pt[i*n + j];
8868 
8869 #ifdef DEBUG_PROJ
8870    /* debug output */
8871    printf("\nP^T:\n");
8872    for( i = 0; i < n; i++ )
8873    {
8874       for( j = 0; j < n; j++ )
8875          printf("%g ", pt[i*n + j]);
8876       printf("\n");
8877    }
8878    printf("x_0: ");
8879    for( i = 0; i < n; i++ )
8880       printf("%g ", SCIPgetSolVal(scip, refsol, consdata->quadvarterms[i].var));
8881    printf("\n");
8882    printf("P^T x_0: ");
8883    for( i = 0; i < n; i++ )
8884       printf("%g ", y0_[i]);
8885    printf("\n");
8886    printf("P^T b: ");
8887    for( i = 0; i < n; i++ )
8888       printf("%g ", bp[i]);
8889    printf("\n");
8890    printf("<d,linvars> = %g\n", c1);
8891    printf("-norm(d)^2/2 = %g\n", c2);
8892 #endif
8893 
8894    /* perform newton's method:  rho^+ = rho - phi(rho)/phi'(rho) */
8895    rho = 0.0;
8896    phirho = c;
8897    phirhoprime = 1.0;
8898    for( iter = 0; iter < 9; iter++ )
8899    {
8900       assert(phirhoprime != 0.0);
8901       rho = rho - (phirho - c)/ phirhoprime;
8902 
8903       /* compute phi(rho) and phi'(rho):
8904        * note that formulas were deduced for constraints of the form x' A x + 2 b x, so we use b/2 in the formulas:
8905        * c1        = <lin_coefs, sol_lin_vars>
8906        * c2        = - norm(lin_coefs)^2/2
8907        * y(rho)    = (I + rho * D)^-1 * (y(0) - rho * bp/2)
8908        * y'(rho)   = -(I + rho * D)^-2 * (D y(0) + bp/2)
8909        * phi(rho)  = <y(rho), D * y(rho) + pb> + c1 + c2*rho
8910        * phi'(rho) = <y'(rho), 2 * D * y(rho) + pb> + c2
8911        */
8912       phirho = 0.0;
8913       phirhoprime = 0.0;
8914       for( i = 0; i < n; i++ )
8915       {
8916          assert(1.0 + rho * D[i] != 0.0);
8917          yrho[i]      = (y0_[i] - rho * bp[i]/2.0) / (1.0 + rho * D[i]);
8918          yrhoprime[i] = -(D[i] * y0_[i] + bp[i]/2.0) / ( (1.0 + rho * D[i])*(1.0 + rho * D[i]) );
8919          phirho      += yrho[i] * (yrho[i] * D[i] + bp[i]);
8920          phirhoprime += yrhoprime[i] * (2 * D[i] * yrho[i] + bp[i]);
8921       }
8922       phirho      += c2 * rho + c1;
8923       phirhoprime += c2;
8924 #ifdef DEBUG_PROJ
8925       printf("iteration %d: rho = %g, phirho = %g, phirho' = %g\n", iter, rho, phirho, phirhoprime);
8926 #endif
8927    }
8928 
8929    /* come back to the original coordinates: new ref point is P*yrho */
8930    for( i = 0; i < n; i++ )
8931    {
8932       ref[i] = 0.0;
8933 
8934       for( j = 0; j < n; j++ )
8935          ref[i] += pt[j*n + i] * yrho[j];
8936    }
8937 
8938    /* change data back if function is concave */
8939    if( isconcave )
8940    {
8941       for( i = 0; i < n; i++ )
8942       {
8943          D[i]  = -D[i];
8944          bp[i] = -bp[i];
8945       }
8946    }
8947 
8948 #ifdef SCIP_DISABLED_CODE
8949    /* project onto bounds; this is important for some cut generation methods such as generateCutLTI */
8950    for( j = 0; j < consdata->nquadvars; ++j )
8951    {
8952       SCIP_Real lb;
8953       SCIP_Real ub;
8954       SCIP_VAR* var;
8955 
8956       var = consdata->quadvarterms[j].var;
8957       lb  = SCIPvarGetLbLocal(var);
8958       ub  = SCIPvarGetUbLocal(var);
8959       /* do not like variables at infinity */
8960       assert(!SCIPisInfinity(scip,  lb));
8961       assert(!SCIPisInfinity(scip, -ub));
8962 
8963       ref[j] = MIN(ub, MAX(lb, ref[j])); /* project value into bounds */
8964    }
8965 #endif
8966 
8967 #ifdef DEBUG_PROJ
8968    printf("modified reference point by a projection:\n");
8969    for( j = 0; j < consdata->nquadvars; ++j )
8970    {
8971       printf("%s = %g\n", SCIPvarGetName(consdata->quadvarterms[j].var), ref[j]);
8972    }
8973 #endif
8974 
8975    SCIPfreeBufferArray(scip, &y0_);
8976    SCIPfreeBufferArray(scip, &yrho);
8977    SCIPfreeBufferArray(scip, &yrhoprime);
8978 
8979    return SCIP_OKAY;
8980 }
8981 
8982 /** compute reference point suggested by gauge function */
8983 static
computeReferencePointGauge(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_SOL * refsol,SCIP_Real * ref,SCIP_Bool * success)8984 SCIP_RETCODE computeReferencePointGauge(
8985    SCIP*                 scip,               /**< SCIP data structure */
8986    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
8987    SCIP_CONS*            cons,               /**< constraint */
8988    SCIP_SOL*             refsol,             /**< reference point where to compute gauge, or NULL if LP solution should be used */
8989    SCIP_Real*            ref,                /**< array to store reference point */
8990    SCIP_Bool*            success             /**< buffer to store whether we succeeded computing reference point */
8991    )
8992 {
8993    SCIP_CONSDATA* consdata;
8994    SCIP_Real gaugeval;
8995    SCIP_Real intpoint;
8996    SCIP_Real lb;
8997    SCIP_Real ub;
8998    SCIP_VAR* var;
8999    int j;
9000 
9001    assert(scip != NULL);
9002    assert(conshdlr != NULL);
9003    assert(cons != NULL);
9004 
9005    consdata = SCIPconsGetData(cons);
9006    assert(consdata != NULL);
9007    assert(consdata->isgaugeavailable);
9008 
9009    SCIPdebugMsg(scip, "evaluating gauge\n");
9010    SCIP_CALL( evaluateGauge(scip, conshdlr, cons, refsol, &gaugeval, success) );
9011 
9012    if( !(*success) )
9013    {
9014 #ifdef SCIP_DEBUG_GAUGE
9015       printf("Couldn't evaluate gauge!\n");
9016 #endif
9017       return SCIP_OKAY;
9018    }
9019 
9020 #ifdef SCIP_DEBUG_GAUGE
9021    {
9022       SCIP_Real level;
9023 
9024       level = consdata->rhs;
9025       for( j = 0; j < consdata->nlinvars; j++ )
9026          level -= SCIPgetSolVal(scip, refsol, consdata->linvars[j]) * consdata->lincoefs[j];
9027 
9028       printf("Summary:\n");
9029       printf("For cons <%s>: gauge at level %g evaluated at (refsol - intpoint) is %.10f\n",
9030             SCIPconsGetName(cons), level, gaugeval);
9031       printf("refsol - intpoint:\n");
9032 
9033       for( j = 0; j < consdata->nquadvars; ++j )
9034       {
9035          SCIP_VAR* vvar;
9036          vvar = consdata->quadvarterms[j].var;
9037          printf("%s: % 20.15g  - %g = %g\n", SCIPvarGetName(vvar), SCIPgetSolVal(scip, refsol, vvar),
9038                consdata->interiorpoint[j], SCIPgetSolVal(scip, refsol, vvar) - consdata->interiorpoint[j]);
9039       }
9040       if( SCIPisFeasLE(scip, gaugeval, 1.0) )
9041          printf("refsol is in the closure of the region (gaugeval <= 1), don't modify reference point\n");
9042    }
9043 #endif
9044 
9045    /* scale gauge value so that final point is close to the boundary, but not on the boundary (weakens the cut) */
9046    gaugeval *= GAUGESCALE;
9047 
9048    /* if the point is not sufficiently violated, we don't modify it */
9049    if( SCIPisFeasLE(scip, gaugeval, 1.0) )
9050    {
9051       *success = FALSE;
9052       return SCIP_OKAY;
9053    }
9054 
9055    /* set reference to (refsol - interior point)/gaugeval + interior point and project onto bounds this is important for
9056     * some cut generation methods such as generateCutLTI
9057     * @todo remove the projection onto the bounds; generateCutLTI shouldn't be called for convex constraints
9058     */
9059    for( j = 0; j < consdata->nquadvars; ++j )
9060    {
9061       var = consdata->quadvarterms[j].var;
9062       lb  = SCIPvarGetLbLocal(var);
9063       ub  = SCIPvarGetUbLocal(var);
9064       /* do not like variables at infinity */
9065       assert(!SCIPisInfinity(scip,  lb));
9066       assert(!SCIPisInfinity(scip, -ub));
9067 
9068       intpoint = consdata->interiorpoint[j];
9069       ref[j] = (SCIPgetSolVal(scip, refsol, var) - intpoint) / gaugeval + intpoint;
9070       ref[j] = MIN(ub, MAX(lb, ref[j])); /* project value into bounds */
9071    }
9072 
9073 #ifdef SCIP_DEBUG_GAUGE
9074    printf("successful application of guage: %g\n", gaugeval);
9075    printf("modified reference point:\n");
9076    for( j = 0; j < consdata->nquadvars; ++j )
9077    {
9078       printf("%s = % 20.15g\n", SCIPvarGetName(consdata->quadvarterms[j].var), ref[j]);
9079    }
9080 #endif
9081 
9082    return SCIP_OKAY;
9083 }
9084 
9085 /** generates a cut based on linearization (if convex) or McCormick (if nonconvex) in a solution
9086  * @note mode indicates whether we should modify the point we want to cutoff (sol) via gauge or projection,
9087  * or if just normal linearization should be use, or the default way (whatever is specified via settings)
9088  */
9089 static
generateCutSol(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_SOL * sol,SCIP_SOL * refsol,SCIP_SIDETYPE violside,SCIP_ROW ** row,SCIP_Real * efficacy,SCIP_Bool checkcurvmultivar,SCIP_Real minefficacy,char mode)9090 SCIP_RETCODE generateCutSol(
9091    SCIP*                 scip,               /**< SCIP data structure */
9092    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
9093    SCIP_CONS*            cons,               /**< constraint */
9094    SCIP_SOL*             sol,                /**< solution where to generate cut, or NULL if LP solution should be used */
9095    SCIP_SOL*             refsol,             /**< reference point where to generate cut, or NULL if sol should be used */
9096    SCIP_SIDETYPE         violside,           /**< for which side a cut should be generated */
9097    SCIP_ROW**            row,                /**< storage for cut */
9098    SCIP_Real*            efficacy,           /**< buffer to store efficacy of row in reference solution, or NULL if not of interest */
9099    SCIP_Bool             checkcurvmultivar,  /**< are we allowed to check the curvature of a multivariate quadratic function, if not done yet */
9100    SCIP_Real             minefficacy,        /**< minimal required efficacy */
9101    char                  mode                /**< mode of execution 'g'auge, 'p'rojection, 'l'inearization gradient, 'd'efault */
9102    )
9103 {
9104    SCIP_CONSHDLRDATA* conshdlrdata;
9105    SCIP_CONSDATA* consdata;
9106    SCIP_VAR*  var;
9107    SCIP_Real  lb;
9108    SCIP_Real  ub;
9109    SCIP_Real* ref;
9110    SCIP_Bool success;
9111    int j;
9112 
9113    assert(scip != NULL);
9114    assert(conshdlr != NULL);
9115    assert(cons != NULL);
9116 
9117    consdata = SCIPconsGetData(cons);
9118    assert(consdata != NULL);
9119 
9120    conshdlrdata = SCIPconshdlrGetData(conshdlr);
9121    assert(conshdlrdata != NULL);
9122 
9123    if( refsol == NULL )
9124       refsol = sol;
9125 
9126    /* get reference point */
9127    SCIP_CALL( SCIPallocBufferArray(scip, &ref, consdata->nquadvars) );
9128    success = FALSE;
9129 
9130    if( mode == 'd')
9131    {
9132       if( (consdata->isconvex && violside == SCIP_SIDETYPE_RIGHT) ||
9133             (consdata->isconcave && violside == SCIP_SIDETYPE_LEFT) )
9134       {
9135          if( conshdlrdata->gaugecuts && consdata->isgaugeavailable )
9136          {
9137             SCIP_CALL( computeReferencePointGauge(scip, conshdlr, cons, refsol, ref, &success) );
9138          }
9139          else if( conshdlrdata->projectedcuts && consdata->isedavailable )
9140          {
9141             SCIPdebugMessage("use the projection of refsol onto the region defined by the constraint as reference point\n");
9142             SCIP_CALL( computeReferencePointProjection(scip, conshdlr, cons, refsol, ref) );
9143             success = TRUE;
9144          }
9145       }
9146 
9147       if( success )
9148       {
9149          SCIP_CALL( generateCut(scip, conshdlr, cons, ref, sol, violside, row, efficacy, checkcurvmultivar, minefficacy) );
9150 
9151          /* if cut fails, try again without modifying reference point */
9152          if( *row == NULL || (efficacy != NULL && !SCIPisGT(scip, *efficacy, minefficacy)) || !SCIPisCutApplicable(scip, *row) )  /*lint !e644 */
9153          {
9154             SCIPdebugMsg(scip, "%s cut fail, try without modifying\n", conshdlrdata->gaugecuts ? "gauge" : "projected");
9155             success = FALSE;
9156          }
9157       }
9158 
9159       /* note that this is not the same as calling this method with mode 'l', 'l' assume convex/concave function */
9160       if( !success )
9161       {
9162          for( j = 0; j < consdata->nquadvars; ++j )
9163          {
9164             var = consdata->quadvarterms[j].var;
9165             lb  = SCIPvarGetLbLocal(var);
9166             ub  = SCIPvarGetUbLocal(var);
9167             /* do not like variables at infinity */
9168             assert(!SCIPisInfinity(scip,  lb));
9169             assert(!SCIPisInfinity(scip, -ub));
9170 
9171             ref[j] = SCIPgetSolVal(scip, refsol, var);
9172             ref[j] = MIN(ub, MAX(lb, ref[j])); /* project value into bounds */
9173          }
9174 
9175          SCIP_CALL( generateCut(scip, conshdlr, cons, ref, sol, violside, row, efficacy, checkcurvmultivar, minefficacy) );
9176       }
9177    }
9178    /* gauge cut */
9179    if( mode == 'g' )
9180    {
9181       assert((consdata->isconvex && violside == SCIP_SIDETYPE_RIGHT) || (consdata->isconcave && violside == SCIP_SIDETYPE_LEFT));
9182       if( conshdlrdata->gaugecuts && consdata->isgaugeavailable )
9183       {
9184          SCIP_CALL( computeReferencePointGauge(scip, conshdlr, cons, refsol, ref, &success) );
9185       }
9186       if( success )
9187       {
9188          SCIP_CALL( generateCut(scip, conshdlr, cons, ref, sol, violside, row, efficacy, checkcurvmultivar, minefficacy) );
9189       }
9190    }
9191    /* projection cut */
9192    if( mode == 'p' )
9193    {
9194       assert((consdata->isconvex && violside == SCIP_SIDETYPE_RIGHT) || (consdata->isconcave && violside == SCIP_SIDETYPE_LEFT));
9195       if( conshdlrdata->projectedcuts && consdata->isedavailable )
9196       {
9197          SCIP_CALL( computeReferencePointProjection(scip, conshdlr, cons, refsol, ref) );
9198          SCIP_CALL( generateCut(scip, conshdlr, cons, ref, sol, violside, row, efficacy, checkcurvmultivar, minefficacy) );
9199       }
9200    }
9201    /* gradient linearization cut at refsol */
9202    if( mode == 'l' )
9203    {
9204       assert((consdata->isconvex && violside == SCIP_SIDETYPE_RIGHT) || (consdata->isconcave && violside == SCIP_SIDETYPE_LEFT));
9205       for( j = 0; j < consdata->nquadvars; ++j )
9206       {
9207          var = consdata->quadvarterms[j].var;
9208          lb  = SCIPvarGetLbLocal(var);
9209          ub  = SCIPvarGetUbLocal(var);
9210          /* do not like variables at infinity */
9211          assert(!SCIPisInfinity(scip,  lb));
9212          assert(!SCIPisInfinity(scip, -ub));
9213 
9214          ref[j] = SCIPgetSolVal(scip, refsol, var);
9215          ref[j] = MIN(ub, MAX(lb, ref[j])); /* project value into bounds */
9216       }
9217       SCIP_CALL( generateCut(scip, conshdlr, cons, ref, sol, violside, row, efficacy, checkcurvmultivar, minefficacy) );
9218    }
9219 
9220    SCIPfreeBufferArray(scip, &ref);
9221 
9222    return SCIP_OKAY;
9223 }
9224 
9225 /** tries to find a cut that intersects with an unbounded ray of the LP
9226  *
9227  *  For convex functions, we do this by linearizing in the feasible solution of the LPI.
9228  *  For nonconvex functions, we just call generateCutSol with the unbounded solution as reference point.
9229  */
9230 static
generateCutUnboundedLP(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_SIDETYPE violside,SCIP_ROW ** row,SCIP_Real * rowrayprod,SCIP_Bool checkcurvmultivar)9231 SCIP_RETCODE generateCutUnboundedLP(
9232    SCIP*                 scip,               /**< SCIP data structure */
9233    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
9234    SCIP_CONS*            cons,               /**< constraint */
9235    SCIP_SIDETYPE         violside,           /**< for which side a cut should be generated */
9236    SCIP_ROW**            row,                /**< storage for cut */
9237    SCIP_Real*            rowrayprod,         /**< buffer to store product of ray with row coefficients, or NULL if not of interest */
9238    SCIP_Bool             checkcurvmultivar   /**< are we allowed to check the curvature of a multivariate quadratic function, if not done yet */
9239    )
9240 {
9241    SCIP_CONSDATA* consdata;
9242    SCIP_BILINTERM* bilinterm;
9243    SCIP_VAR*  var;
9244    SCIP_Real* ref;
9245    SCIP_Real  matrixrayprod;
9246    SCIP_Real  linrayprod;
9247    SCIP_Real  quadrayprod;
9248    SCIP_Real  rayval;
9249    int i;
9250    int j;
9251 
9252    assert(scip != NULL);
9253    assert(conshdlr != NULL);
9254    assert(cons != NULL);
9255    assert(row  != NULL);
9256    assert(SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_UNBOUNDEDRAY);
9257 
9258    consdata = SCIPconsGetData(cons);
9259    assert(consdata != NULL);
9260 
9261    *row = NULL;
9262 
9263    if( !SCIPhasPrimalRay(scip) )
9264    {
9265       SCIPdebugMsg(scip, "do not have primal ray, thus cannot resolve unboundedness\n");
9266       return SCIP_OKAY;
9267    }
9268 
9269    SCIP_CALL( checkCurvature(scip, cons, checkcurvmultivar) );
9270    if( (!consdata->isconvex && violside == SCIP_SIDETYPE_RIGHT) ||
9271       (!consdata->isconcave && violside == SCIP_SIDETYPE_LEFT) )
9272    {
9273       /* if not convex, just call generateCut and hope it's getting something useful */
9274       SCIP_CALL( generateCutSol(scip, conshdlr, cons, NULL, NULL, violside, row, NULL, FALSE, -SCIPinfinity(scip), 'd') );
9275 
9276       /* compute product of cut coefficients with ray, if required */
9277       if( *row != NULL && rowrayprod != NULL )
9278       {
9279          *rowrayprod = 0.0;
9280          for( i = 0; i < SCIProwGetNNonz(*row); ++i )
9281          {
9282             assert(SCIProwGetCols(*row)[i] != NULL);
9283             var = SCIPcolGetVar(SCIProwGetCols(*row)[i]);
9284             assert(var != NULL);
9285 
9286             *rowrayprod += SCIProwGetVals(*row)[i] * SCIPgetPrimalRayVal(scip, var);
9287          }
9288       }
9289 
9290       return SCIP_OKAY;
9291    }
9292 
9293    /* we seek for a linearization of the quadratic function such that it intersects with the unbounded ray
9294     * that is, we need a reference point ref such that for the gradient g of xAx+bx in ref, we have
9295     *   <g, ray> > 0.0 if rhs is finite and <g, ray> < 0.0 if lhs is finite
9296     * Since g = 2*A*ref + b, we have <g, ray> = <2*A*ref + b, ray> = <ref, 2*A*ray> + <b,ray>
9297     * initially, for finite rhs, we set ref_i = 1.0 if (A*ray)_i > 0.0 and ref_i = -1.0 if (A*ray)_i < 0.0 (for finite lhs analog)
9298     * <ref, 2*A*ray> + <b,ray> is sufficiently larger 0.0, we call generateCut for this point, otherwise, we scale up ref
9299     */
9300 
9301    quadrayprod = 0.0; /* <ref, 2*A*ray> */
9302    linrayprod = 0.0;  /* <b, ray> */
9303    SCIP_CALL( SCIPallocBufferArray(scip, &ref, consdata->nquadvars) );
9304    for( i = 0; i < consdata->nquadvars; ++i )
9305    {
9306       var = consdata->quadvarterms[i].var;
9307       rayval = SCIPgetPrimalRayVal(scip, var);
9308 
9309       /* compute i-th entry of (2*A*ray) */
9310       matrixrayprod = 2.0 * consdata->quadvarterms[i].sqrcoef * rayval;
9311       for( j = 0; j < consdata->quadvarterms[i].nadjbilin; ++j )
9312       {
9313          bilinterm = &consdata->bilinterms[consdata->quadvarterms[i].adjbilin[j]];
9314          matrixrayprod += bilinterm->coef * SCIPgetPrimalRayVal(scip, bilinterm->var1 == var ? bilinterm->var2 : bilinterm->var1);
9315       }
9316 
9317       if( SCIPisPositive(scip, matrixrayprod) )
9318          ref[i] = (violside == SCIP_SIDETYPE_RIGHT ?  1.0 : -1.0);
9319       else if( SCIPisNegative(scip, matrixrayprod) )
9320          ref[i] = (violside == SCIP_SIDETYPE_RIGHT ? -1.0 :  1.0);
9321       else
9322          ref[i] = 0.0;
9323 
9324       quadrayprod += matrixrayprod * ref[i];
9325       linrayprod += consdata->quadvarterms[i].lincoef * rayval;
9326    }
9327    assert((violside == SCIP_SIDETYPE_RIGHT && quadrayprod >= 0.0) || (violside == SCIP_SIDETYPE_LEFT && quadrayprod <= 0.0));
9328 
9329    if( SCIPisZero(scip, quadrayprod) )
9330    {
9331       SCIPdebugMsg(scip, "ray is zero along cons <%s>\n", SCIPconsGetName(cons));
9332       SCIPfreeBufferArray(scip, &ref);
9333       return SCIP_OKAY;
9334    }
9335 
9336    /* add linear part to linrayprod */
9337    for( i = 0; i < consdata->nlinvars; ++i )
9338       linrayprod += consdata->lincoefs[i] * SCIPgetPrimalRayVal(scip, consdata->linvars[i]);
9339 
9340    SCIPdebugMsg(scip, "initially have <b,ray> = %g and <ref, 2*A*ref> = %g\n", linrayprod, quadrayprod);
9341 
9342    /* we scale the refpoint up, such that <ref, 2*A*ray> >= -2*<b, ray> (rhs finite) or <ref, 2*A*ray> <= -2*<b, ray> (lhs finite), if <b,ray> is not zero
9343     * if <b,ray> is zero, then we scale refpoint up if |<ref, 2*A*ray>| < 1.0
9344     */
9345    if( (!SCIPisZero(scip, linrayprod) && violside == SCIP_SIDETYPE_RIGHT && quadrayprod < -2*linrayprod) ||
9346       ( !SCIPisZero(scip, linrayprod) && violside == SCIP_SIDETYPE_LEFT  && quadrayprod > -2*linrayprod) ||
9347       (SCIPisZero(scip, linrayprod) && REALABS(quadrayprod) < 1.0) )
9348    {
9349       SCIP_Real scale;
9350 
9351       if( !SCIPisZero(scip, linrayprod) )
9352          scale = 2*REALABS(linrayprod/quadrayprod);  /*lint !e795 */
9353       else
9354          scale = 1.0/REALABS(quadrayprod);
9355 
9356       SCIPdebugMsg(scip, "scale refpoint by %g\n", scale);
9357       for( i = 0; i < consdata->nquadvars; ++i )
9358          ref[i] *= scale;
9359       quadrayprod *= scale;
9360    }
9361 
9362    if( rowrayprod != NULL )
9363       *rowrayprod = quadrayprod + linrayprod;
9364 
9365    SCIPdebugMsg(scip, "calling generateCut, expecting ray product %g\n", quadrayprod + linrayprod);
9366    SCIP_CALL( generateCut(scip, conshdlr, cons, ref, NULL, violside, row, NULL, FALSE, -SCIPinfinity(scip)) );
9367 
9368    SCIPfreeBufferArray(scip, &ref);
9369 
9370    return SCIP_OKAY;
9371 }
9372 
9373 /** processes a cut for constraint cons, i.e., checks numerics and possibly adds cut to sepastore */
9374 static
processCut(SCIP * scip,SCIP_ROW ** row,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_Real efficacy,SCIP_Real minefficacy,SCIP_Bool inenforcement,SCIP_Real * bestefficacy,SCIP_RESULT * result)9375 SCIP_RETCODE processCut(
9376    SCIP*                 scip,               /**< SCIP data structure */
9377    SCIP_ROW**            row,                /**< cut to process */
9378    SCIP_CONSHDLR*        conshdlr,           /**< quadratic constraints handler */
9379    SCIP_CONS*            cons,               /**< constraint */
9380    SCIP_Real             efficacy,           /**< efficacy of row in reference solution */
9381    SCIP_Real             minefficacy,        /**< minimal efficacy */
9382    SCIP_Bool             inenforcement,      /**< whether we are in constraint enforcement */
9383    SCIP_Real*            bestefficacy,       /**< buffer to store best efficacy of a cut that was added to the LP, if found; or NULL if not of interest */
9384    SCIP_RESULT*          result              /**< result of separation */
9385    )
9386 {
9387    SCIP_CONSDATA* consdata;
9388    SCIP_CONSHDLRDATA* conshdlrdata;
9389 
9390    assert(scip != NULL);
9391    assert(row != NULL);
9392    assert(conshdlr != NULL);
9393    assert(result != NULL);
9394    assert(cons != NULL);
9395 
9396    /* no cut to process */
9397    if( *row == NULL )
9398       return SCIP_OKAY;
9399 
9400    conshdlrdata = SCIPconshdlrGetData(conshdlr);
9401    assert(conshdlrdata != NULL);
9402 
9403    consdata = SCIPconsGetData(cons);
9404    assert(consdata != NULL);
9405 
9406    if( SCIPisGT(scip, efficacy, minefficacy) && SCIPisCutApplicable(scip, *row) )  /*lint !e644 */
9407    {
9408       SCIP_Bool infeasible;
9409 
9410       /* cut cuts off solution */
9411       SCIP_CALL( SCIPaddRow(scip, *row, FALSE /* forcecut */, &infeasible) );
9412       if( infeasible )
9413       {
9414          SCIPdebugMessage("cut for constraint <%s> is infeasible -> cutoff.\n", SCIPconsGetName(cons));
9415          *result = SCIP_CUTOFF;
9416       }
9417       else
9418       {
9419          SCIPdebugMessage("add cut with efficacy %g for constraint <%s> violated by %g\n", efficacy,
9420                SCIPconsGetName(cons), consdata->lhsviol+consdata->rhsviol);
9421          *result = SCIP_SEPARATED;
9422       }
9423       SCIP_CALL( SCIPresetConsAge(scip, cons) );
9424 
9425       /* mark row as not removable from LP for current node, if in enforcement */
9426       if( inenforcement && !conshdlrdata->enfocutsremovable )
9427          SCIPmarkRowNotRemovableLocal(scip, *row);
9428    }
9429    if( bestefficacy != NULL && efficacy > *bestefficacy )
9430       *bestefficacy = efficacy;
9431 
9432    SCIP_CALL( SCIPreleaseRow (scip, row) );
9433    return SCIP_OKAY;
9434 }
9435 
9436 /** tries to separate solution or LP solution by a linear cut
9437  *
9438  *  assumes that constraint violations have been computed
9439  */
9440 static
separatePoint(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,int nusefulconss,SCIP_SOL * sol,SCIP_Real minefficacy,SCIP_Bool inenforcement,SCIP_RESULT * result,SCIP_Real * bestefficacy)9441 SCIP_RETCODE separatePoint(
9442    SCIP*                 scip,               /**< SCIP data structure */
9443    SCIP_CONSHDLR*        conshdlr,           /**< quadratic constraints handler */
9444    SCIP_CONS**           conss,              /**< constraints */
9445    int                   nconss,             /**< number of constraints */
9446    int                   nusefulconss,       /**< number of constraints that seem to be useful */
9447    SCIP_SOL*             sol,                /**< solution to separate, or NULL if LP solution should be used */
9448    SCIP_Real             minefficacy,        /**< minimal efficacy of a cut if it should be added to the LP */
9449    SCIP_Bool             inenforcement,      /**< whether we are in constraint enforcement */
9450    SCIP_RESULT*          result,             /**< result of separation */
9451    SCIP_Real*            bestefficacy        /**< buffer to store best efficacy of a cut that was added to the LP, if found; or NULL if not of interest */
9452    )
9453 {
9454    SCIP_CONSHDLRDATA* conshdlrdata;
9455    SCIP_CONSDATA*     consdata;
9456    SCIP_Real          efficacy;
9457    SCIP_SIDETYPE      violside;
9458    int                c;
9459    SCIP_ROW*          row;
9460 
9461    assert(scip != NULL);
9462    assert(conshdlr != NULL);
9463    assert(conss != NULL || nconss == 0);
9464    assert(nusefulconss <= nconss);
9465    assert(result != NULL);
9466 
9467    *result = SCIP_FEASIBLE;
9468 
9469    conshdlrdata = SCIPconshdlrGetData(conshdlr);
9470    assert(conshdlrdata != NULL);
9471 
9472    if( bestefficacy != NULL )
9473       *bestefficacy = 0.0;
9474 
9475    row = NULL;
9476    /* loop over both sides of each constraint */
9477    for( c = 0, violside = SCIP_SIDETYPE_LEFT; c < nconss; c = (violside == SCIP_SIDETYPE_LEFT ? c : c+1), violside = (violside == SCIP_SIDETYPE_LEFT ? SCIP_SIDETYPE_RIGHT : SCIP_SIDETYPE_LEFT) )
9478    {
9479       assert(conss != NULL);
9480       consdata = SCIPconsGetData(conss[c]);
9481       assert(consdata != NULL);
9482 
9483       /* if side not violated, then go on */
9484       if( !SCIPisGT(scip, violside == SCIP_SIDETYPE_LEFT ? consdata->lhsviol : consdata->rhsviol, SCIPfeastol(scip)) )
9485          continue;
9486 
9487       /* we are not feasible anymore */
9488       if( *result == SCIP_FEASIBLE )
9489          *result = SCIP_DIDNOTFIND;
9490 
9491       /* generate cut */
9492       if( sol == NULL && SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_UNBOUNDEDRAY )
9493       {
9494          /* if the LP is unbounded, then we need a cut that cuts into the direction of a hopefully existing primal ray
9495           * that is, assume a ray r is given such that p + t*r is feasible for the LP for all t >= t_0 and some p
9496           * given a cut lhs <= <c,x> <= rhs, we check whether it imposes an upper bound on t and thus bounds the ray
9497           * this is given if rhs < infinity and <c,r> > 0, since we then enforce <c,p+t*r> = <c,p> + t<c,r> <= rhs, i.e., t <= (rhs - <c,p>)/<c,r>
9498           * similar, lhs > -infinity and <c,r> < 0 is good
9499           */
9500          SCIP_Real rayprod;
9501 
9502          rayprod = 0.0; /* for compiler */
9503          SCIP_CALL( generateCutUnboundedLP(scip, conshdlr, conss[c], violside, &row, &rayprod, conshdlrdata->checkcurvature) );
9504 
9505          if( row != NULL )
9506          {
9507             if( !SCIPisInfinity(scip, SCIProwGetRhs(row)) && SCIPisPositive(scip, rayprod) )
9508                efficacy =  rayprod;
9509             else if( !SCIPisInfinity(scip, -SCIProwGetLhs(row)) && SCIPisNegative(scip, rayprod) )
9510                efficacy = -rayprod;
9511             else
9512                efficacy = 0.0;
9513 
9514             SCIP_CALL( processCut(scip, &row, conshdlr, conss[c], efficacy, minefficacy, inenforcement, bestefficacy, result) );
9515          }
9516          continue;
9517       }
9518       else
9519       {
9520          SCIP_CALL( generateCutSol(scip, conshdlr, conss[c], sol, NULL, violside, &row, &efficacy,
9521             conshdlrdata->checkcurvature, minefficacy, 'd') );
9522 
9523          SCIP_CALL( processCut(scip, &row, conshdlr, conss[c], efficacy, minefficacy, inenforcement, bestefficacy, result) );
9524       }
9525 
9526       if( *result == SCIP_CUTOFF )
9527          break;
9528 
9529       /* enforce only useful constraints
9530        * others are only checked and enforced if we are still feasible or have not found a separating cut yet
9531        */
9532       if( c >= nusefulconss && *result == SCIP_SEPARATED )
9533          break;
9534    }
9535 
9536    return SCIP_OKAY;
9537 }
9538 
9539 /** adds linearizations cuts for convex constraints w.r.t. a given reference point to cutpool and sepastore
9540  *
9541  *  - If separatedlpsol is not NULL, then a cut that separates the LP solution is added to the sepastore and is forced to enter the LP.
9542  *  - If separatedlpsol is not NULL, but cut does not separate the LP solution, then it is added to the cutpool only.
9543  *  - If separatedlpsol is NULL, then cut is added to cutpool only.
9544  */
9545 static
addLinearizationCuts(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_SOL * ref,SCIP_Bool * separatedlpsol,SCIP_Real minefficacy)9546 SCIP_RETCODE addLinearizationCuts(
9547    SCIP*                 scip,               /**< SCIP data structure */
9548    SCIP_CONSHDLR*        conshdlr,           /**< quadratic constraints handler */
9549    SCIP_CONS**           conss,              /**< constraints */
9550    int                   nconss,             /**< number of constraints */
9551    SCIP_SOL*             ref,                /**< reference point where to linearize, or NULL for LP solution */
9552    SCIP_Bool*            separatedlpsol,     /**< buffer to store whether a cut that separates the current LP solution was found and added to LP,
9553                                               *   or NULL if adding to cutpool only */
9554    SCIP_Real             minefficacy         /**< minimal efficacy of a cut when checking for separation of LP solution */
9555    )
9556 {
9557    SCIP_CONSHDLRDATA* conshdlrdata;
9558    SCIP_CONSDATA* consdata;
9559    SCIP_Bool addedtolp;
9560    SCIP_ROW* row;
9561    int c;
9562 
9563    assert(scip != NULL);
9564    assert(conshdlr != NULL);
9565    assert(conss != NULL || nconss == 0);
9566 
9567    conshdlrdata = SCIPconshdlrGetData(conshdlr);
9568    assert(conshdlrdata != NULL);
9569 
9570    if( separatedlpsol != NULL )
9571       *separatedlpsol = FALSE;
9572 
9573    for( c = 0; c < nconss; ++c )
9574    {
9575       assert(conss[c] != NULL);  /*lint !e613 */
9576 
9577       if( SCIPconsIsLocal(conss[c]) || !SCIPconsIsEnabled(conss[c]) )  /*lint !e613 */
9578          continue;
9579 
9580       SCIP_CALL( checkCurvature(scip, conss[c], conshdlrdata->checkcurvature) );  /*lint !e613 */
9581 
9582       consdata = SCIPconsGetData(conss[c]);  /*lint !e613 */
9583       assert(consdata != NULL);
9584 
9585       if( consdata->isconvex && !SCIPisInfinity(scip, consdata->rhs) )
9586       {
9587          SCIP_CALL( generateCutSol(scip, conshdlr, conss[c], NULL, ref, SCIP_SIDETYPE_RIGHT, &row, NULL,
9588                conshdlrdata->checkcurvature, -SCIPinfinity(scip), 'l') );  /*lint !e613 */
9589       }
9590       else if( consdata->isconcave && !SCIPisInfinity(scip, -consdata->lhs) )
9591       {
9592          SCIP_CALL( generateCutSol(scip, conshdlr, conss[c], NULL, ref, SCIP_SIDETYPE_LEFT,  &row, NULL,
9593                conshdlrdata->checkcurvature, -SCIPinfinity(scip), 'l') );  /*lint !e613 */
9594       }
9595       else
9596          continue;
9597 
9598       if( row == NULL )
9599          continue;
9600 
9601       addedtolp = FALSE;
9602 
9603       /* if caller wants, then check if cut separates LP solution and add to sepastore if so */
9604       if( separatedlpsol != NULL )
9605       {
9606          SCIP_Real efficacy;
9607 
9608          efficacy = -SCIPgetRowLPFeasibility(scip, row);
9609          if( efficacy >= minefficacy )
9610          {
9611             SCIP_Bool infeasible;
9612 
9613             *separatedlpsol = TRUE;
9614             addedtolp = TRUE;
9615             SCIP_CALL( SCIPaddRow(scip, row, TRUE, &infeasible) );
9616             assert( ! infeasible );
9617             SCIPdebugMsg(scip, "added linearization cut <%s> to LP, efficacy = %g\n", SCIProwGetName(row), efficacy);
9618          }
9619       }
9620 
9621       if( !SCIProwIsLocal(row) && !addedtolp )
9622       {
9623          SCIP_CALL( SCIPaddPoolCut(scip, row) );
9624          SCIPdebugMsg(scip, "added linearization cut <%s> to cutpool\n", SCIProwGetName(row));
9625       }
9626 
9627       SCIP_CALL( SCIPreleaseRow(scip, &row) );
9628    }
9629 
9630    return SCIP_OKAY;
9631 }
9632 
9633 /** processes the event that a new primal solution has been found */
9634 static
SCIP_DECL_EVENTEXEC(processNewSolutionEvent)9635 SCIP_DECL_EVENTEXEC(processNewSolutionEvent)
9636 {
9637    SCIP_CONSHDLRDATA* conshdlrdata;
9638    SCIP_CONSHDLR* conshdlr;
9639    SCIP_CONS**    conss;
9640    int            nconss;
9641    SCIP_SOL*      sol;
9642 
9643    assert(scip != NULL);
9644    assert(event != NULL);
9645    assert(eventdata != NULL);
9646    assert(eventhdlr != NULL);
9647 
9648    assert((SCIPeventGetType(event) & SCIP_EVENTTYPE_SOLFOUND) != 0);
9649 
9650    conshdlr = (SCIP_CONSHDLR*)eventdata;
9651 
9652    nconss = SCIPconshdlrGetNConss(conshdlr);
9653 
9654    if( nconss == 0 )
9655       return SCIP_OKAY;
9656 
9657    sol = SCIPeventGetSol(event);
9658    assert(sol != NULL);
9659 
9660    conshdlrdata = SCIPconshdlrGetData(conshdlr);
9661    assert(conshdlrdata != NULL);
9662 
9663    /* we are only interested in solution coming from some heuristic other than trysol, but not from the tree
9664     * the reason for ignoring trysol solutions is that they may come from an NLP solve in sepalp, where we already added linearizations,
9665     * or are from the tree, but postprocessed via proposeFeasibleSolution
9666     */
9667    if( SCIPsolGetHeur(sol) == NULL || SCIPsolGetHeur(sol) == conshdlrdata->trysolheur )
9668       return SCIP_OKAY;
9669 
9670    conss = SCIPconshdlrGetConss(conshdlr);
9671    assert(conss != NULL);
9672 
9673    SCIPdebugMsg(scip, "caught new sol event %" SCIP_EVENTTYPE_FORMAT " from heur <%s>; have %d conss\n", SCIPeventGetType(event), SCIPheurGetName(SCIPsolGetHeur(sol)), nconss);
9674 
9675    SCIP_CALL( addLinearizationCuts(scip, conshdlr, conss, nconss, sol, NULL, 0.0) );
9676 
9677    return SCIP_OKAY;
9678 }
9679 
9680 /** registers branching candidates according to convexification gap rule
9681  *
9682  * That is, computes for every nonconvex term the gap between the terms value in the LP solution and the value of the underestimator
9683  * as it would be (and maybe has been) constructed by the separation routines of this constraint handler. Then it registers all
9684  * variables occurring in each term with the computed gap. If variables appear in more than one term, they are registered several times.
9685  */
9686 static
registerBranchingCandidatesGap(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_SOL * sol,int * nnotify)9687 SCIP_RETCODE registerBranchingCandidatesGap(
9688    SCIP*                 scip,               /**< SCIP data structure */
9689    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
9690    SCIP_CONS**           conss,              /**< constraints to check */
9691    int                   nconss,             /**< number of constraints to check */
9692    SCIP_SOL*             sol,                /**< solution to enforce (NULL for the LP solution) */
9693    int*                  nnotify             /**< counter for number of notifications performed */
9694    )
9695 {
9696    SCIP_CONSHDLRDATA* conshdlrdata;
9697    SCIP_CONSDATA*     consdata;
9698    int                c;
9699    int                j;
9700    SCIP_Bool          xbinary;
9701    SCIP_Bool          ybinary;
9702    SCIP_Bool          xunbounded;
9703    SCIP_Bool          yunbounded;
9704    SCIP_VAR*          x;
9705    SCIP_VAR*          y;
9706    SCIP_Real          xlb;
9707    SCIP_Real          xub;
9708    SCIP_Real          xval;
9709    SCIP_Real          ylb;
9710    SCIP_Real          yub;
9711    SCIP_Real          yval;
9712    SCIP_Real          gap;
9713    SCIP_Real          coef_;
9714 
9715    assert(scip != NULL);
9716    assert(conshdlr != NULL);
9717    assert(conss != NULL || nconss == 0);
9718 
9719    *nnotify = 0;
9720    yval = SCIP_INVALID;
9721    xval = SCIP_INVALID;
9722 
9723    conshdlrdata = SCIPconshdlrGetData(conshdlr);
9724    assert(conshdlr != NULL);
9725 
9726    for( c = 0; c < nconss; ++c )
9727    {
9728       assert(conss != NULL);
9729       consdata = SCIPconsGetData(conss[c]);
9730       assert(consdata != NULL);
9731 
9732       if( !consdata->nquadvars )
9733          continue;
9734 
9735       if( (!SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) || consdata->isconcave) &&
9736          ( !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) || consdata->isconvex ) )
9737          continue;
9738       SCIPdebugMsg(scip, "cons <%s> violation: %g %g  convex: %u %u\n", SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol, consdata->isconvex, consdata->isconcave);
9739 
9740       /* square terms */
9741       for( j = 0; j < consdata->nquadvars; ++j )
9742       {
9743          x = consdata->quadvarterms[j].var;
9744          if( (SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) && consdata->quadvarterms[j].sqrcoef < 0) ||
9745             ( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && consdata->quadvarterms[j].sqrcoef > 0) )
9746          {
9747             xlb = SCIPvarGetLbLocal(x);
9748             xub = SCIPvarGetUbLocal(x);
9749             if( SCIPisRelEQ(scip, xlb, xub) )
9750             {
9751                SCIPdebugMsg(scip, "ignore fixed variable <%s>[%g, %g], diff %g\n", SCIPvarGetName(x), xlb, xub, xub-xlb);
9752                continue;
9753             }
9754 
9755             xval = SCIPgetSolVal(scip, sol, x);
9756 
9757             /* if variable is at bounds, then no need to branch, since secant is exact there */
9758             if( SCIPisLE(scip, xval, xlb) || SCIPisGE(scip, xval, xub) )
9759                continue;
9760 
9761             if( SCIPisInfinity(scip, -xlb) || SCIPisInfinity(scip, xub) )
9762                gap = SCIPinfinity(scip);
9763             else
9764                gap = (xval-xlb)*(xub-xval)/(1+2*ABS(xval));
9765             assert(!SCIPisFeasNegative(scip, gap));
9766             SCIP_CALL( SCIPaddExternBranchCand(scip, x, MAX(gap, 0.0), SCIP_INVALID) );
9767             ++*nnotify;
9768          }
9769       }
9770 
9771       /* bilinear terms */
9772       for( j = 0; j < consdata->nbilinterms; ++j )
9773       {
9774          /* if any of the variables if fixed, then it actually behaves like a linear term, so we don't need to branch on it */
9775          x = consdata->bilinterms[j].var1;
9776          xlb = SCIPvarGetLbLocal(x);
9777          xub = SCIPvarGetUbLocal(x);
9778          if( SCIPisRelEQ(scip, xlb, xub) )
9779             continue;
9780 
9781          y = consdata->bilinterms[j].var2;
9782          ylb = SCIPvarGetLbLocal(y);
9783          yub = SCIPvarGetUbLocal(y);
9784          if( SCIPisRelEQ(scip, ylb, yub) )
9785             continue;
9786 
9787          xunbounded = SCIPisInfinity(scip, -xlb) || SCIPisInfinity(scip, xub);
9788          yunbounded = SCIPisInfinity(scip, -ylb) || SCIPisInfinity(scip, yub);
9789 
9790          /* compute gap, if both variable are bounded */
9791          gap = SCIPinfinity(scip);
9792          if( !xunbounded && !yunbounded )
9793          {
9794             xval = SCIPgetSolVal(scip, sol, x);
9795             yval = SCIPgetSolVal(scip, sol, y);
9796 
9797             /* if both variables are at one of its bounds, then no need to branch, since McCormick is exact there */
9798             if( (SCIPisLE(scip, xval, xlb) || SCIPisGE(scip, xval, xub)) &&
9799                ( SCIPisLE(scip, yval, ylb) || SCIPisGE(scip, yval, yub)) )
9800                continue;
9801 
9802             xval = MAX(xlb, MIN(xval, xub));
9803             yval = MAX(ylb, MIN(yval, yub));
9804 
9805             coef_ = SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) ? -consdata->bilinterms[j].coef : consdata->bilinterms[j].coef;
9806             if( coef_ > 0.0 )
9807             {
9808                if( (xub-xlb)*yval + (yub-ylb)*xval <= xub*yub - xlb*ylb )
9809                   gap = (xval*yval - xlb*yval - ylb*xval + xlb*ylb) / (1+sqrt(xval*xval + yval*yval));
9810                else
9811                   gap = (xval*yval - xval*yub - yval*xub + xub*yub) / (1+sqrt(xval*xval + yval*yval));
9812             }
9813             else
9814             { /* coef_ < 0 */
9815                if( (xub-xlb)*yval - (yub-ylb)*xval <= xub*ylb - xlb*yub )
9816                   gap = -(xval*yval - xval*ylb - yval*xub + xub*ylb) / (1+sqrt(xval*xval + yval*yval));
9817                else
9818                   gap = -(xval*yval - xval*yub - yval*xlb + xlb*yub) / (1+sqrt(xval*xval + yval*yval));
9819             }
9820 
9821             assert(!SCIPisNegative(scip, gap / MAX3(MAX(REALABS(xlb), REALABS(xub)), MAX(REALABS(ylb), REALABS(yub)), 1.0)));  /*lint !e666*/
9822             if( gap < 0.0 )
9823                gap = 0.0;
9824 
9825             /* use tighter relaxation when using linear inequalities to adjust the branching scores for bilinear terms */
9826             if( consdata->bilintermsidx != NULL && conshdlrdata->usebilinineqbranch )
9827             {
9828                BILINESTIMATOR* bilinestimator;
9829                int bilinidx;
9830 
9831                assert(conshdlrdata->bilinestimators != NULL);
9832 
9833                bilinidx = consdata->bilintermsidx[j];
9834                assert(bilinidx >= 0 && bilinidx < conshdlrdata->nbilinterms);
9835 
9836                bilinestimator = &conshdlrdata->bilinestimators[bilinidx];
9837                assert(bilinestimator != NULL);
9838                assert(bilinestimator->x == x);
9839                assert(bilinestimator->y == y);
9840 
9841                if( SCIPisGT(scip, bilinestimator->lastimprfac, 0.0) )
9842                   gap *= MAX(0.0, 1.0 - bilinestimator->lastimprfac);
9843             }
9844          }
9845 
9846          /* if one of the variables is binary or integral with domain width 1, then branching on this makes the term linear, so prefer this */
9847          xbinary = SCIPvarIsBinary(x) || (SCIPvarIsIntegral(x) && xub - xlb < 1.5);
9848          ybinary = SCIPvarIsBinary(y) || (SCIPvarIsIntegral(y) && yub - ylb < 1.5);
9849          if( xbinary )
9850          {
9851             SCIP_CALL( SCIPaddExternBranchCand(scip, x, gap, SCIP_INVALID) );
9852             ++*nnotify;
9853          }
9854          if( ybinary )
9855          {
9856             SCIP_CALL( SCIPaddExternBranchCand(scip, y, gap, SCIP_INVALID) );
9857             ++*nnotify;
9858          }
9859          if( xbinary || ybinary )
9860             continue;
9861 
9862          /* if one of the variables is unbounded, then branch on it first */
9863          if( xunbounded )
9864          {
9865             SCIP_CALL( SCIPaddExternBranchCand(scip, x, gap, SCIP_INVALID) );
9866             ++*nnotify;
9867          }
9868          if( yunbounded )
9869          {
9870             SCIP_CALL( SCIPaddExternBranchCand(scip, y, gap, SCIP_INVALID) );
9871             ++*nnotify;
9872          }
9873          if( xunbounded || yunbounded )
9874             continue;
9875 
9876          /* if both variables are integral, prefer the one with the smaller domain, so variable gets fixed soon
9877           * does not seem to work well on tln instances, so disable for now and may look at it later again
9878           */
9879 #ifdef BRANCHTOLINEARITY
9880          if( SCIPvarIsIntegral(x) && SCIPvarIsIntegral(y) )
9881          {
9882             if( SCIPisLT(scip, xub-xlb, yub-ylb) )
9883             {
9884                SCIP_CALL( SCIPaddExternBranchCand(scip, x, gap, SCIP_INVALID) );
9885                ++*nnotify;
9886                continue;
9887             }
9888             if( SCIPisGT(scip, xub-xlb, yub-ylb) )
9889             {
9890                SCIP_CALL( SCIPaddExternBranchCand(scip, y, gap, SCIP_INVALID) );
9891                ++*nnotify;
9892                continue;
9893             }
9894          }
9895 #endif
9896 
9897          /* in the regular case, suggest those variables which are not at its bounds for branching
9898           * this is, because after branching both variables will be one the bounds, and McCormick will be exact then */
9899          if( !SCIPisLE(scip, xval, xlb) && !SCIPisGE(scip, xval, xub) )
9900          {
9901             SCIP_CALL( SCIPaddExternBranchCand(scip, x, gap, SCIP_INVALID) );
9902             ++*nnotify;
9903          }
9904          if( !SCIPisLE(scip, yval, ylb) && !SCIPisGE(scip, yval, yub) )
9905          {
9906             SCIP_CALL( SCIPaddExternBranchCand(scip, y, gap, SCIP_INVALID) );
9907             ++*nnotify;
9908          }
9909       }
9910    }
9911 
9912    SCIPdebugMsg(scip, "registered %d branching candidates\n", *nnotify);
9913 
9914    return SCIP_OKAY;
9915 }
9916 
9917 /** registers branching candidates according to constraint violation rule
9918  *
9919  * That is, registers all variables appearing in nonconvex terms^1 with a score that is the violation of the constraint.
9920  * This is the same rule as is applied in cons_nonlinear and other nonlinear constraint handlers.
9921  *
9922  * 1) We mean all quadratic variables that appear either in a nonconvex square term or in a bilinear term, if the constraint
9923  * itself is nonconvex. (and this under the assumption that the rhs is violated; for violated lhs, swap terms)
9924  */
9925 static
registerBranchingCandidatesViolation(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_SOL * sol,int * nnotify)9926 SCIP_RETCODE registerBranchingCandidatesViolation(
9927    SCIP*                 scip,               /**< SCIP data structure */
9928    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
9929    SCIP_CONS**           conss,              /**< constraints to check */
9930    int                   nconss,             /**< number of constraints to check */
9931    SCIP_SOL*             sol,                /**< solution to enforce (NULL for the LP solution) */
9932    int*                  nnotify             /**< counter for number of notifications performed */
9933    )
9934 {
9935    SCIP_CONSDATA*     consdata;
9936    SCIP_QUADVARTERM*  quadvarterm;
9937    int                c;
9938    int                j;
9939    SCIP_VAR*          x;
9940    SCIP_Real          xlb;
9941    SCIP_Real          xub;
9942    SCIP_Real          xval;
9943 
9944    assert(scip != NULL);
9945    assert(conshdlr != NULL);
9946    assert(conss != NULL || nconss == 0);
9947 
9948    *nnotify = 0;
9949 
9950    for( c = 0; c < nconss; ++c )
9951    {
9952       assert(conss != NULL);
9953       consdata = SCIPconsGetData(conss[c]);
9954       assert(consdata != NULL);
9955 
9956       if( !consdata->nquadvars )
9957          continue;
9958 
9959       if( (!SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) || consdata->isconcave) &&
9960          ( !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) || consdata->isconvex ) )
9961          continue;
9962       SCIPdebugMsg(scip, "cons %s violation: %g %g  convex: %u %u\n", SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol, consdata->isconvex, consdata->isconcave);
9963 
9964       for( j = 0; j < consdata->nquadvars; ++j )
9965       {
9966          quadvarterm = &consdata->quadvarterms[j];
9967          if( (SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) && quadvarterm->sqrcoef < 0) ||
9968              (SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && quadvarterm->sqrcoef > 0) ||
9969              quadvarterm->nadjbilin > 0 )
9970          {
9971             x = quadvarterm->var;
9972             xlb = SCIPvarGetLbLocal(x);
9973             xub = SCIPvarGetUbLocal(x);
9974 
9975             if( quadvarterm->nadjbilin == 0 )
9976             {
9977                xval = SCIPgetSolVal(scip, sol, x);
9978 
9979                /* if variable is at bounds and only in a nonconvex square term, then no need to branch, since secant is exact there */
9980                if( SCIPisLE(scip, xval, xlb) || SCIPisGE(scip, xval, xub) )
9981                   continue;
9982             }
9983 
9984             if( SCIPisRelEQ(scip, xlb, xub) )
9985             {
9986                SCIPdebugMsg(scip, "ignore fixed variable <%s>[%g, %g], diff %g\n", SCIPvarGetName(x), xlb, xub, xub-xlb);
9987                continue;
9988             }
9989 
9990             SCIP_CALL( SCIPaddExternBranchCand(scip, x, MAX(consdata->lhsviol, consdata->rhsviol), SCIP_INVALID) );
9991             ++*nnotify;
9992          }
9993       }
9994    }
9995 
9996    SCIPdebugMsg(scip, "registered %d branching candidates\n", *nnotify);
9997 
9998    return SCIP_OKAY;
9999 }
10000 
10001 /** registers branching candidates according to centrality rule
10002  *
10003  * That is, registers all variables appearing in nonconvex terms^1 with a score that is given by the distance of the
10004  * variable value from its bounds. This rule should not make sense, as the distance to the bounds is also (often) considered
10005  * by the branching rule later on.
10006  *
10007  * 1) We mean all quadratic variables that appear either in a nonconvex square term or in a bilinear term, if the constraint
10008  * itself is nonconvex. (and this under the assumption that the rhs is violated; for violated lhs, swap terms)
10009  */
10010 static
registerBranchingCandidatesCentrality(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_SOL * sol,int * nnotify)10011 SCIP_RETCODE registerBranchingCandidatesCentrality(
10012    SCIP*                 scip,               /**< SCIP data structure */
10013    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
10014    SCIP_CONS**           conss,              /**< constraints to check */
10015    int                   nconss,             /**< number of constraints to check */
10016    SCIP_SOL*             sol,                /**< solution to enforce (NULL for the LP solution) */
10017    int*                  nnotify             /**< counter for number of notifications performed */
10018    )
10019 {
10020    SCIP_CONSDATA*     consdata;
10021    SCIP_QUADVARTERM*  quadvarterm;
10022    int                c;
10023    int                j;
10024    SCIP_VAR*          x;
10025    SCIP_Real          xlb;
10026    SCIP_Real          xub;
10027    SCIP_Real          xval;
10028    SCIP_Real          score;
10029 
10030    assert(scip != NULL);
10031    assert(conshdlr != NULL);
10032    assert(conss != NULL || nconss == 0);
10033 
10034    *nnotify = 0;
10035 
10036    for( c = 0; c < nconss; ++c )
10037    {
10038       assert(conss != NULL);
10039       consdata = SCIPconsGetData(conss[c]);
10040       assert(consdata != NULL);
10041 
10042       if( !consdata->nquadvars )
10043          continue;
10044 
10045       if( (!SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) || consdata->isconcave) &&
10046          ( !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) || consdata->isconvex ) )
10047          continue;
10048       SCIPdebugMsg(scip, "cons %s violation: %g %g  convex: %u %u\n", SCIPconsGetName(conss[c]), consdata->lhsviol, consdata->rhsviol, consdata->isconvex, consdata->isconcave);
10049 
10050       for( j = 0; j < consdata->nquadvars; ++j )
10051       {
10052          quadvarterm = &consdata->quadvarterms[j];
10053          if( (SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) && quadvarterm->sqrcoef < 0) ||
10054              (SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && quadvarterm->sqrcoef > 0) ||
10055              quadvarterm->nadjbilin > 0 )
10056          {
10057             x = quadvarterm->var;
10058             xlb = SCIPvarGetLbLocal(x);
10059             xub = SCIPvarGetUbLocal(x);
10060 
10061             if( SCIPisRelEQ(scip, xlb, xub) )
10062             {
10063                SCIPdebugMsg(scip, "ignore fixed variable <%s>[%g, %g], diff %g\n", SCIPvarGetName(x), xlb, xub, xub-xlb);
10064                continue;
10065             }
10066 
10067             xval = SCIPgetSolVal(scip, sol, x);
10068             xval = MAX(xlb, MIN(xub, xval));
10069 
10070             /* compute relative difference of xval to each of its bounds
10071              * and scale such that if xval were in the middle, we get a score of 1
10072              * and if xval is on one its bounds, the score is 0
10073              */
10074             if( SCIPisInfinity(scip, -xlb) || SCIPisInfinity(scip, xub) )
10075             {
10076                if( (!SCIPisInfinity(scip, -xlb) && SCIPisEQ(scip, xval, xlb)) || (!SCIPisInfinity(scip, xub) && SCIPisEQ(scip, xval, xub)) )
10077                   score = 0.0;
10078                else
10079                   score = 1.0;
10080             }
10081             else
10082             {
10083                score = 4.0 * (xval - xlb) * (xub - xval) / ((xub - xlb) * (xub - xlb));
10084             }
10085 
10086             SCIP_CALL( SCIPaddExternBranchCand(scip, x, score, SCIP_INVALID) );
10087             ++*nnotify;
10088          }
10089       }
10090    }
10091 
10092    SCIPdebugMsg(scip, "registered %d branching candidates\n", *nnotify);
10093 
10094    return SCIP_OKAY;
10095 }
10096 
10097 /** registers branching candidates */
10098 static
registerBranchingCandidates(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_SOL * sol,int * nnotify)10099 SCIP_RETCODE registerBranchingCandidates(
10100    SCIP*                 scip,               /**< SCIP data structure */
10101    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
10102    SCIP_CONS**           conss,              /**< constraints to check */
10103    int                   nconss,             /**< number of constraints to check */
10104    SCIP_SOL*             sol,                /**< solution to enforce (NULL for the LP solution) */
10105    int*                  nnotify             /**< counter for number of notifications performed */
10106    )
10107 {
10108    SCIP_CONSHDLRDATA* conshdlrdata;
10109 
10110    conshdlrdata = SCIPconshdlrGetData(conshdlr);
10111    assert(conshdlrdata != NULL);
10112 
10113    switch( conshdlrdata->branchscoring )
10114    {
10115       case 'g' :
10116          SCIP_CALL( registerBranchingCandidatesGap(scip, conshdlr, conss, nconss, sol, nnotify) );
10117          break;
10118 
10119       case 'v' :
10120          SCIP_CALL( registerBranchingCandidatesViolation(scip, conshdlr, conss, nconss, sol, nnotify) );
10121          break;
10122 
10123       case 'c' :
10124          SCIP_CALL( registerBranchingCandidatesCentrality(scip, conshdlr, conss, nconss, sol, nnotify) );
10125          break;
10126 
10127       default :
10128          SCIPerrorMessage("invalid branchscoring selection");
10129          SCIPABORT();
10130          return SCIP_ERROR; /*lint !e527*/
10131    }
10132 
10133    return SCIP_OKAY;
10134 }
10135 
10136 
10137 /** registers a quadratic variable from a violated constraint as branching candidate that has a large absolute value in the (LP) relaxation */
10138 static
registerLargeRelaxValueVariableForBranching(SCIP * scip,SCIP_CONS ** conss,int nconss,SCIP_SOL * sol,SCIP_VAR ** brvar)10139 SCIP_RETCODE registerLargeRelaxValueVariableForBranching(
10140    SCIP*                 scip,               /**< SCIP data structure */
10141    SCIP_CONS**           conss,              /**< constraints */
10142    int                   nconss,             /**< number of constraints */
10143    SCIP_SOL*             sol,                /**< solution to enforce (NULL for the LP solution) */
10144    SCIP_VAR**            brvar               /**< buffer to store branching variable */
10145    )
10146 {
10147    SCIP_CONSDATA*      consdata;
10148    SCIP_Real           val;
10149    SCIP_Real           brvarval;
10150    int                 i;
10151    int                 c;
10152 
10153    assert(scip  != NULL);
10154    assert(conss != NULL || nconss == 0);
10155 
10156    *brvar = NULL;
10157    brvarval = -1.0;
10158 
10159    for( c = 0; c < nconss; ++c )
10160    {
10161       assert(conss != NULL);
10162       consdata = SCIPconsGetData(conss[c]);
10163       assert(consdata != NULL);
10164 
10165       if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
10166          continue;
10167 
10168       for( i = 0; i < consdata->nquadvars; ++i )
10169       {
10170          /* do not propose fixed variables */
10171          if( SCIPisRelEQ(scip, SCIPvarGetLbLocal(consdata->quadvarterms[i].var), SCIPvarGetUbLocal(consdata->quadvarterms[i].var)) )
10172             continue;
10173          val = SCIPgetSolVal(scip, sol, consdata->quadvarterms[i].var);
10174          if( ABS(val) > brvarval )
10175          {
10176             brvarval = ABS(val);
10177             *brvar = consdata->quadvarterms[i].var;
10178          }
10179       }
10180    }
10181 
10182    if( *brvar != NULL )
10183    {
10184       SCIP_CALL( SCIPaddExternBranchCand(scip, *brvar, brvarval, SCIP_INVALID) );
10185    }
10186 
10187    return SCIP_OKAY;
10188 }
10189 
10190 /** replaces violated quadratic constraints where all quadratic variables are fixed by linear constraints */
10191 static
replaceByLinearConstraints(SCIP * scip,SCIP_CONS ** conss,int nconss,SCIP_Bool * addedcons,SCIP_Bool * reduceddom,SCIP_Bool * infeasible)10192 SCIP_RETCODE replaceByLinearConstraints(
10193    SCIP*                 scip,               /**< SCIP data structure */
10194    SCIP_CONS**           conss,              /**< constraints */
10195    int                   nconss,             /**< number of constraints */
10196    SCIP_Bool*            addedcons,          /**< buffer to store whether a linear constraint was added */
10197    SCIP_Bool*            reduceddom,         /**< whether a domain has been reduced */
10198    SCIP_Bool*            infeasible          /**< whether we detected infeasibility */
10199    )
10200 {
10201    SCIP_CONS*          cons;
10202    SCIP_CONSDATA*      consdata;
10203    SCIP_RESULT         checkresult;
10204    SCIP_VAR*           var;
10205    SCIP_Bool           tightened;
10206    SCIP_Real           constant;
10207    SCIP_Real           val1;
10208    SCIP_Real           val2;
10209    int                 i;
10210    int                 c;
10211 
10212    assert(scip  != NULL);
10213    assert(conss != NULL || nconss == 0);
10214    assert(addedcons != NULL);
10215    assert(reduceddom != NULL);
10216    assert(infeasible != NULL);
10217 
10218    *addedcons = FALSE;
10219    *reduceddom = FALSE;
10220    *infeasible = FALSE;
10221 
10222    for( c = 0; c < nconss; ++c )
10223    {
10224       assert(conss != NULL);
10225       consdata = SCIPconsGetData(conss[c]);
10226       assert(consdata != NULL);
10227 
10228       if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
10229          continue;
10230 
10231       constant = 0.0;
10232 
10233       for( i = 0; i < consdata->nquadvars; ++i )
10234       {
10235          var = consdata->quadvarterms[i].var;
10236 
10237          /* variables should be fixed if constraint is violated */
10238          assert(SCIPisRelEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)));
10239 
10240          val1 = (SCIPvarGetUbLocal(var) + SCIPvarGetLbLocal(var)) / 2.0;
10241          constant += (consdata->quadvarterms[i].lincoef + consdata->quadvarterms[i].sqrcoef * val1) * val1;
10242 
10243          SCIPdebugMessage("<%s>: [%.15g, %.15g]\n", SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
10244 
10245          /* if variable is not fixed w.r.t. absolute eps yet, then try to fix it
10246           * (SCIPfixVar() doesn't allow for small tightenings, so tighten lower and upper bound separately)
10247           */
10248          if( !SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
10249          {
10250             SCIP_CALL( SCIPtightenVarLb(scip, var, val1, TRUE, infeasible, &tightened) );
10251             if( *infeasible )
10252             {
10253                SCIPdebugMsg(scip, "Fixing almost fixed variable <%s> lead to infeasibility.\n", SCIPvarGetName(var));
10254                return SCIP_OKAY;
10255             }
10256             if( tightened )
10257             {
10258                SCIPdebugMsg(scip, "Tightened lower bound of almost fixed variable <%s>.\n", SCIPvarGetName(var));
10259                *reduceddom = TRUE;
10260             }
10261 
10262             SCIP_CALL( SCIPtightenVarUb(scip, var, val1, TRUE, infeasible, &tightened) );
10263             if( *infeasible )
10264             {
10265                SCIPdebugMsg(scip, "Fixing almost fixed variable <%s> lead to infeasibility.\n", SCIPvarGetName(var));
10266                return SCIP_OKAY;
10267             }
10268             if( tightened )
10269             {
10270                SCIPdebugMsg(scip, "Tightened upper bound of almost fixed variable <%s>.\n", SCIPvarGetName(var));
10271                *reduceddom = TRUE;
10272             }
10273          }
10274       }
10275 
10276       /* if some quadratic variable was fixed now, then restart node (next enfo round) */
10277       if( *reduceddom )
10278          return SCIP_OKAY;
10279 
10280       for( i = 0; i < consdata->nbilinterms; ++i )
10281       {
10282          val1 = (SCIPvarGetUbLocal(consdata->bilinterms[i].var1) + SCIPvarGetLbLocal(consdata->bilinterms[i].var1)) / 2.0;
10283          val2 = (SCIPvarGetUbLocal(consdata->bilinterms[i].var2) + SCIPvarGetLbLocal(consdata->bilinterms[i].var2)) / 2.0;
10284          constant += consdata->bilinterms[i].coef * val1 * val2;
10285       }
10286 
10287       /* check if we have a bound change */
10288       if ( consdata->nlinvars == 1 )
10289       {
10290          SCIP_Real coef;
10291          SCIP_Real lhs;
10292          SCIP_Real rhs;
10293 
10294          coef = *consdata->lincoefs;
10295          var = *consdata->linvars;
10296 
10297          assert( ! SCIPisZero(scip, coef) );
10298 
10299          /* compute lhs/rhs, divide already by |coef| */
10300          if ( SCIPisInfinity(scip, -consdata->lhs) )
10301             lhs = -SCIPinfinity(scip);
10302          else
10303             lhs = (consdata->lhs - constant) / REALABS(coef);
10304 
10305          if ( SCIPisInfinity(scip, consdata->rhs) )
10306             rhs = SCIPinfinity(scip);
10307          else
10308             rhs = (consdata->rhs - constant) / REALABS(coef);
10309 
10310          SCIPdebugMsg(scip, "Linear constraint with one variable: %.15g <= %g <%s> <= %.15g\n", lhs, coef > 0.0 ? 1.0 : -1.0, SCIPvarGetName(var), rhs);
10311 
10312          SCIPdebugMessage("<%s>: [%.15g, %.15g]\n", SCIPvarGetName(var), SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var));
10313 
10314          if ( coef < 0.0 )
10315          {
10316             /* swap lhs and rhs, with negated sign */
10317             SCIP_Real h;
10318             h = rhs;
10319             rhs = -lhs;
10320             lhs = -h;
10321          }
10322          SCIPdebugMsg(scip, "Linear constraint is a bound: %.15g <= <%s> <= %.15g\n", lhs, SCIPvarGetName(var), rhs);
10323 
10324          if( SCIPisInfinity(scip, -rhs) || SCIPisInfinity(scip, lhs) )
10325          {
10326             SCIPdebugMsg(scip, "node will marked as infeasible since lb/ub of %s is +/-infinity\n",
10327                SCIPvarGetName(var));
10328 
10329             *infeasible = TRUE;
10330             return SCIP_OKAY;
10331          }
10332 
10333          if ( ! SCIPisInfinity(scip, -lhs) )
10334          {
10335             SCIP_CALL( SCIPtightenVarLb(scip, var, lhs, TRUE, infeasible, &tightened) );
10336             if ( *infeasible )
10337             {
10338                SCIPdebugMsg(scip, "Lower bound leads to infeasibility.\n");
10339                return SCIP_OKAY;
10340             }
10341             if ( tightened )
10342             {
10343                SCIPdebugMsg(scip, "Lower bound changed.\n");
10344                *reduceddom = TRUE;
10345                return SCIP_OKAY;
10346             }
10347          }
10348 
10349          if ( ! SCIPisInfinity(scip, rhs) )
10350          {
10351             SCIP_CALL( SCIPtightenVarUb(scip, var, rhs, TRUE, infeasible, &tightened) );
10352             if ( *infeasible )
10353             {
10354                SCIPdebugMsg(scip, "Upper bound leads to infeasibility.\n");
10355                return SCIP_OKAY;
10356             }
10357             if ( tightened )
10358             {
10359                SCIPdebugMsg(scip, "Upper bound changed.\n");
10360                *reduceddom = TRUE;
10361                return SCIP_OKAY;
10362             }
10363          }
10364       }
10365       else
10366       {
10367          SCIP_CALL( SCIPcreateConsLinear(scip, &cons, SCIPconsGetName(conss[c]),
10368                consdata->nlinvars, consdata->linvars, consdata->lincoefs,
10369                (SCIPisInfinity(scip, -consdata->lhs) ? -SCIPinfinity(scip) : (consdata->lhs - constant)),
10370                (SCIPisInfinity(scip,  consdata->rhs) ?  SCIPinfinity(scip) : (consdata->rhs - constant)),
10371                SCIPconsIsInitial(conss[c]), SCIPconsIsSeparated(conss[c]), SCIPconsIsEnforced(conss[c]),
10372                SCIPconsIsChecked(conss[c]), SCIPconsIsPropagated(conss[c]),  TRUE,
10373                SCIPconsIsModifiable(conss[c]), SCIPconsIsDynamic(conss[c]), SCIPconsIsRemovable(conss[c]),
10374                SCIPconsIsStickingAtNode(conss[c])) );
10375 
10376          SCIPdebugMsg(scip, "replace quadratic constraint <%s> by linear constraint after all quadratic vars have been fixed\n", SCIPconsGetName(conss[c]) );
10377          SCIPdebugPrintCons(scip, cons, NULL);
10378 
10379          SCIP_CALL( SCIPcheckCons(scip, cons, NULL, FALSE, FALSE, FALSE, &checkresult) );
10380 
10381          if( checkresult != SCIP_INFEASIBLE && SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_OPTIMAL )
10382          {
10383             SCIPdebugMsg(scip, "linear constraint is feasible and LP optimal, thus do not add\n");
10384          }
10385          else
10386          {
10387             SCIP_CALL( SCIPaddConsLocal(scip, cons, NULL) );
10388             *addedcons = TRUE;
10389          }
10390          SCIP_CALL( SCIPreleaseCons(scip, &cons) );
10391       }
10392       SCIP_CALL( SCIPdelConsLocal(scip, conss[c]) );
10393    }
10394 
10395    return SCIP_OKAY;
10396 }
10397 
10398 /** tightens a lower bound on a variable and checks the result */
10399 static
propagateBoundsTightenVarLb(SCIP * scip,SCIP_CONS * cons,SCIP_Real intervalinfty,SCIP_VAR * var,SCIP_Real bnd,SCIP_RESULT * result,int * nchgbds)10400 SCIP_RETCODE propagateBoundsTightenVarLb(
10401    SCIP*                 scip,               /**< SCIP data structure */
10402    SCIP_CONS*            cons,               /**< constraint where we currently propagate */
10403    SCIP_Real             intervalinfty,      /**< infinity value used in interval operations */
10404    SCIP_VAR*             var,                /**< variable which domain we might reduce */
10405    SCIP_Real             bnd,                /**< new lower bound for variable */
10406    SCIP_RESULT*          result,             /**< result to update if there was a tightening or cutoff */
10407    int*                  nchgbds             /**< counter to increase if a bound was tightened */
10408    )
10409 {
10410    SCIP_Bool infeas;
10411    SCIP_Bool tightened;
10412 
10413    assert(scip != NULL);
10414    assert(cons != NULL);
10415    assert(intervalinfty > 0.0);
10416    assert(bnd > -intervalinfty);
10417    assert(var != NULL);
10418    assert(result != NULL);
10419    assert(*result == SCIP_DIDNOTFIND || *result == SCIP_REDUCEDDOM);
10420    assert(nchgbds != NULL);
10421 
10422    /* new bound is no improvement */
10423    if( SCIPisHugeValue(scip, -bnd) || SCIPisLE(scip, bnd, SCIPvarGetLbLocal(var)) )
10424       return SCIP_OKAY;
10425 
10426    if( SCIPisInfinity(scip, bnd) )
10427    { /* domain will be outside [-infty, +infty] -> declare node infeasible */
10428       *result = SCIP_CUTOFF;
10429       SCIP_CALL( SCIPresetConsAge(scip, cons) );
10430       return SCIP_OKAY;
10431    }
10432 
10433    /* new lower bound is very low (between -intervalinfty and -SCIPinfinity()) */
10434    if( SCIPisInfinity(scip, -bnd) )
10435       return SCIP_OKAY;
10436 
10437    bnd = SCIPadjustedVarLb(scip, var, bnd);
10438    SCIP_CALL( SCIPtightenVarLb(scip, var, bnd, FALSE, &infeas, &tightened) );
10439    if( infeas )
10440    {
10441       SCIPdebugMsg(scip, "%s found constraint <%s> infeasible due to tightened lower bound %g for variable <%s>\n",
10442          SCIPinProbing(scip) ? "in probing" : "", SCIPconsGetName(cons), bnd, SCIPvarGetName(var));
10443       *result = SCIP_CUTOFF;
10444       SCIP_CALL( SCIPresetConsAge(scip, cons) );
10445       return SCIP_OKAY;
10446    }
10447    if( tightened )
10448    {
10449       SCIPdebugMsg(scip, "%s tightened lower bound of variable <%s> in constraint <%s> to %g\n",
10450          SCIPinProbing(scip) ? "in probing" : "", SCIPvarGetName(var), SCIPconsGetName(cons), bnd);
10451       ++*nchgbds;
10452       *result = SCIP_REDUCEDDOM;
10453       SCIP_CALL( SCIPresetConsAge(scip, cons) );
10454    }
10455 
10456    return SCIP_OKAY;
10457 }
10458 
10459 /** tightens an upper bound on a variable and checks the result */
10460 static
propagateBoundsTightenVarUb(SCIP * scip,SCIP_CONS * cons,SCIP_Real intervalinfty,SCIP_VAR * var,SCIP_Real bnd,SCIP_RESULT * result,int * nchgbds)10461 SCIP_RETCODE propagateBoundsTightenVarUb(
10462    SCIP*                 scip,               /**< SCIP data structure */
10463    SCIP_CONS*            cons,               /**< constraint where we currently propagate */
10464    SCIP_Real             intervalinfty,      /**< infinity value used in interval operations */
10465    SCIP_VAR*             var,                /**< variable which domain we might reduce */
10466    SCIP_Real             bnd,                /**< new upper bound for variable */
10467    SCIP_RESULT*          result,             /**< result to update if there was a tightening or cutoff */
10468    int*                  nchgbds             /**< counter to increase if a bound was tightened */
10469    )
10470 {
10471    SCIP_Bool infeas;
10472    SCIP_Bool tightened;
10473 
10474    assert(scip != NULL);
10475    assert(cons != NULL);
10476    assert(intervalinfty > 0.0);
10477    assert(bnd < intervalinfty);
10478    assert(var != NULL);
10479    assert(result != NULL);
10480    assert(*result == SCIP_DIDNOTFIND || *result == SCIP_REDUCEDDOM);
10481    assert(nchgbds != NULL);
10482 
10483    /* new bound is no improvement */
10484    if( SCIPisHugeValue(scip, bnd) || SCIPisGE(scip, bnd, SCIPvarGetUbLocal(var)) )
10485       return SCIP_OKAY;
10486 
10487    if( SCIPisInfinity(scip, -bnd) )
10488    { /* domain will be outside [-infty, +infty] -> declare node infeasible */
10489       *result = SCIP_CUTOFF;
10490       SCIP_CALL( SCIPresetConsAge(scip, cons) );
10491       return SCIP_OKAY;
10492    }
10493 
10494    /* new upper bound is very high (between SCIPinfinity() and intervalinfty) */
10495    if( SCIPisInfinity(scip, bnd) )
10496       return SCIP_OKAY;
10497 
10498    bnd = SCIPadjustedVarUb(scip, var, bnd);
10499    SCIP_CALL( SCIPtightenVarUb(scip, var, bnd, FALSE, &infeas, &tightened) );
10500    if( infeas )
10501    {
10502       SCIPdebugMsg(scip, "%s found constraint <%s> infeasible due to tightened upper bound %g for variable <%s>\n",
10503          SCIPinProbing(scip) ? "in probing" : "", SCIPconsGetName(cons), bnd, SCIPvarGetName(var));
10504       *result = SCIP_CUTOFF;
10505       SCIP_CALL( SCIPresetConsAge(scip, cons) );
10506       return SCIP_OKAY;
10507    }
10508    if( tightened )
10509    {
10510       SCIPdebugMsg(scip, "%s tightened upper bound of variable <%s> in constraint <%s> to %g\n",
10511          SCIPinProbing(scip) ? "in probing" : "", SCIPvarGetName(var), SCIPconsGetName(cons), bnd);
10512       ++*nchgbds;
10513       *result = SCIP_REDUCEDDOM;
10514       SCIP_CALL( SCIPresetConsAge(scip, cons) );
10515    }
10516 
10517    return SCIP_OKAY;
10518 }
10519 
10520 /** solves a quadratic equation \f$ a x^2 + b x \in rhs \f$ (with b an interval) and reduces bounds on x or deduces infeasibility if possible */
10521 static
propagateBoundsQuadVar(SCIP * scip,SCIP_CONS * cons,SCIP_Real intervalinfty,SCIP_VAR * var,SCIP_Real a,SCIP_INTERVAL b,SCIP_INTERVAL rhs,SCIP_RESULT * result,int * nchgbds)10522 SCIP_RETCODE propagateBoundsQuadVar(
10523    SCIP*                 scip,               /**< SCIP data structure */
10524    SCIP_CONS*            cons,               /**< constraint where we currently propagate */
10525    SCIP_Real             intervalinfty,      /**< infinity value used in interval operations */
10526    SCIP_VAR*             var,                /**< variable which bounds with might tighten */
10527    SCIP_Real             a,                  /**< coefficient in square term */
10528    SCIP_INTERVAL         b,                  /**< coefficient in linear term */
10529    SCIP_INTERVAL         rhs,                /**< right hand side of quadratic equation */
10530    SCIP_RESULT*          result,             /**< result of propagation */
10531    int*                  nchgbds             /**< buffer where to add number of tightened bounds */
10532    )
10533 {
10534    SCIP_INTERVAL newrange;
10535 
10536    assert(scip != NULL);
10537    assert(cons != NULL);
10538    assert(var != NULL);
10539    assert(result != NULL);
10540    assert(nchgbds != NULL);
10541 
10542    /* compute solution of a*x^2 + b*x \in rhs */
10543    if( a == 0.0 && SCIPintervalGetInf(b) == 0.0 && SCIPintervalGetSup(b) == 0.0 )
10544    {
10545       /* relatively easy case: 0.0 \in rhs, thus check if infeasible or just redundant */
10546       if( SCIPintervalGetInf(rhs) > 0.0 || SCIPintervalGetSup(rhs) < 0.0 )
10547       {
10548          SCIPdebugMsg(scip, "found <%s> infeasible due to domain propagation for quadratic variable <%s>\n", SCIPconsGetName(cons), SCIPvarGetName(var));
10549          SCIP_CALL( SCIPresetConsAge(scip, cons) );
10550          *result = SCIP_CUTOFF;
10551       }
10552       return SCIP_OKAY;
10553    }
10554    else
10555    {
10556       SCIP_INTERVAL a_;
10557       SCIP_INTERVAL xbnds;
10558 
10559       SCIPintervalSet(&a_, a);
10560       SCIPintervalSetBounds(&xbnds, -infty2infty(SCIPinfinity(scip), intervalinfty, -SCIPvarGetLbLocal(var)), infty2infty(SCIPinfinity(scip), intervalinfty, SCIPvarGetUbLocal(var))); /*lint !e666*/
10561       SCIPintervalSolveUnivariateQuadExpression(intervalinfty, &newrange, a_, b, rhs, xbnds);
10562    }
10563 
10564    /* SCIPdebugMsg(scip, "%g x^2 + [%g, %g] x in [%g, %g] and x in [%g,%g] -> [%g, %g]\n", a, b.inf, b.sup, rhs.inf, rhs.sup, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var), newrange.inf, newrange.sup); */
10565 
10566    if( SCIPisInfinity(scip, SCIPintervalGetInf(newrange)) || SCIPisInfinity(scip, -SCIPintervalGetSup(newrange)) )
10567    {
10568       /* domain outside [-infty, +infty] -> declare node infeasible */
10569       SCIPdebugMsg(scip, "found <%s> infeasible because propagated domain of quadratic variable <%s> is outside of (-infty, +infty)\n",
10570          SCIPconsGetName(cons), SCIPvarGetName(var));
10571       *result = SCIP_CUTOFF;
10572       SCIP_CALL( SCIPresetConsAge(scip, cons) );
10573       return SCIP_OKAY;
10574    }
10575 
10576    if( SCIPintervalIsEmpty(intervalinfty, newrange) )
10577    {
10578       SCIPdebugMsg(scip, "found <%s> infeasible due to domain propagation for quadratic variable <%s>\n", SCIPconsGetName(cons), SCIPvarGetName(var));
10579       *result = SCIP_CUTOFF;
10580       return SCIP_OKAY;
10581    }
10582 
10583    if( !SCIPisInfinity(scip, -SCIPintervalGetInf(newrange)) )
10584    {
10585       SCIP_CALL( propagateBoundsTightenVarLb(scip, cons, intervalinfty, var, SCIPintervalGetInf(newrange), result, nchgbds) );
10586       if( *result == SCIP_CUTOFF )
10587          return SCIP_OKAY;
10588    }
10589 
10590    if( !SCIPisInfinity(scip,  SCIPintervalGetSup(newrange)) )
10591    {
10592       SCIP_CALL( propagateBoundsTightenVarUb(scip, cons, intervalinfty, var, SCIPintervalGetSup(newrange), result, nchgbds) );
10593       if( *result == SCIP_CUTOFF )
10594          return SCIP_OKAY;
10595    }
10596 
10597    return SCIP_OKAY;
10598 }
10599 
10600 /* The new version below computes potentially tighter bounds, but also always adds a small safety area since it is not implemented roundingsafe.
10601  * This may be a reason why it gives worse results on one of two instances.
10602  * Further, I have only very few instances where one can expect a difference.
10603  */
10604 #ifndef PROPBILINNEW
10605 /** tries to deduce domain reductions for x in xsqrcoef x^2 + xlincoef x + ysqrcoef y^2 + ylincoef y + bilincoef x y \\in rhs
10606  *
10607  *  @note Domain reductions for y are not deduced.
10608  */
10609 static
propagateBoundsBilinearTerm(SCIP * scip,SCIP_CONS * cons,SCIP_Real intervalinfty,SCIP_VAR * x,SCIP_Real xsqrcoef,SCIP_Real xlincoef,SCIP_VAR * y,SCIP_Real ysqrcoef,SCIP_Real ylincoef,SCIP_Real bilincoef,SCIP_INTERVAL rhs,SCIP_RESULT * result,int * nchgbds)10610 SCIP_RETCODE propagateBoundsBilinearTerm(
10611    SCIP*                 scip,               /**< SCIP data structure */
10612    SCIP_CONS*            cons,               /**< the constraint, where the bilinear term belongs to */
10613    SCIP_Real             intervalinfty,      /**< infinity value used in interval operations */
10614    SCIP_VAR*             x,                  /**< first variable */
10615    SCIP_Real             xsqrcoef,           /**< square coefficient of x */
10616    SCIP_Real             xlincoef,           /**< linear coefficient of x */
10617    SCIP_VAR*             y,                  /**< second variable */
10618    SCIP_Real             ysqrcoef,           /**< square coefficient of y */
10619    SCIP_Real             ylincoef,           /**< linear coefficient of y */
10620    SCIP_Real             bilincoef,          /**< bilinear coefficient of x*y */
10621    SCIP_INTERVAL         rhs,                /**< right hand side of quadratic equation */
10622    SCIP_RESULT*          result,             /**< pointer to store result of domain propagation */
10623    int*                  nchgbds             /**< counter to increment if domain reductions are found */
10624    )
10625 {
10626    SCIP_INTERVAL myrhs;
10627    SCIP_INTERVAL varbnds;
10628    SCIP_INTERVAL lincoef;
10629 
10630    assert(scip != NULL);
10631    assert(cons != NULL);
10632    assert(x != NULL);
10633    assert(y != NULL);
10634    assert(x != y);
10635    assert(result != NULL);
10636    assert(*result == SCIP_DIDNOTFIND || *result == SCIP_REDUCEDDOM);
10637    assert(nchgbds != NULL);
10638    assert(bilincoef != 0.0);
10639 
10640    if( SCIPintervalIsEntire(intervalinfty, rhs) )
10641       return SCIP_OKAY;
10642 
10643    /* try to find domain reductions for x */
10644    SCIPintervalSetBounds(&varbnds, MIN(SCIPvarGetLbLocal(y), SCIPvarGetUbLocal(y)), MAX(SCIPvarGetLbLocal(y), SCIPvarGetUbLocal(y)));  /*lint !e666 */
10645 
10646    /* put ysqrcoef*y^2 + ylincoef * y into rhs */
10647    if( SCIPintervalGetSup(rhs) >= intervalinfty )
10648    {
10649       /* if rhs is unbounded by above, it is sufficient to get an upper bound on ysqrcoef*y^2 + ylincoef * y */
10650       SCIP_ROUNDMODE roundmode;
10651       SCIP_Real      tmp;
10652 
10653       SCIPintervalSet(&lincoef, ylincoef);
10654       tmp = SCIPintervalQuadUpperBound(intervalinfty, ysqrcoef, lincoef, varbnds);
10655       roundmode = SCIPintervalGetRoundingMode();
10656       SCIPintervalSetRoundingModeDownwards();
10657       SCIPintervalSetBounds(&myrhs, SCIPintervalGetInf(rhs) - tmp, intervalinfty);
10658       SCIPintervalSetRoundingMode(roundmode);
10659    }
10660    else if( SCIPintervalGetInf(rhs) <= -intervalinfty )
10661    {
10662       /* if rhs is unbounded by below, it is sufficient to get a  lower bound on ysqrcoef*y^2 + ylincoef * y */
10663       SCIP_ROUNDMODE roundmode;
10664       SCIP_Real      tmp;
10665 
10666       SCIPintervalSet(&lincoef, -ylincoef);
10667       tmp = -SCIPintervalQuadUpperBound(intervalinfty, -ysqrcoef, lincoef, varbnds);
10668       roundmode = SCIPintervalGetRoundingMode();
10669       SCIPintervalSetRoundingModeUpwards();
10670       SCIPintervalSetBounds(&myrhs, -intervalinfty, SCIPintervalGetSup(rhs) - tmp);
10671       SCIPintervalSetRoundingMode(roundmode);
10672    }
10673    else
10674    {
10675       /* if rhs is bounded, we need both bounds on ysqrcoef*y^2 + ylincoef * y */
10676       SCIP_INTERVAL tmp;
10677 
10678       SCIPintervalSet(&lincoef, ylincoef);
10679       SCIPintervalQuad(intervalinfty, &tmp, ysqrcoef, lincoef, varbnds);
10680       SCIPintervalSub(intervalinfty, &myrhs, rhs, tmp);
10681    }
10682 
10683    /* create equation xsqrcoef * x^2 + (xlincoef + bilincoef * [ylb, yub]) * x \in myrhs */
10684    SCIPintervalMulScalar(intervalinfty, &lincoef, varbnds, bilincoef);
10685    SCIPintervalAddScalar(intervalinfty, &lincoef, lincoef, xlincoef);
10686 
10687    /* propagate bounds on x */
10688    SCIP_CALL( propagateBoundsQuadVar(scip, cons, intervalinfty, x, xsqrcoef, lincoef, myrhs, result, nchgbds) );
10689 
10690    return SCIP_OKAY;
10691 }
10692 #else
10693 /** tries to deduce domain reductions for x in xsqrcoef x^2 + xlincoef x + ysqrcoef y^2 + ylincoef y + bilincoef x y \\in rhs
10694  *
10695  *  @note Domain reductions for y are not deduced.
10696  */
10697 static
propagateBoundsBilinearTerm(SCIP * scip,SCIP_CONS * cons,SCIP_Real intervalinfty,SCIP_VAR * x,SCIP_Real xsqrcoef,SCIP_Real xlincoef,SCIP_VAR * y,SCIP_Real ysqrcoef,SCIP_Real ylincoef,SCIP_Real bilincoef,SCIP_INTERVAL rhs,SCIP_RESULT * result,int * nchgbds)10698 SCIP_RETCODE propagateBoundsBilinearTerm(
10699    SCIP*                 scip,               /**< SCIP data structure */
10700    SCIP_CONS*            cons,               /**< the constraint, where the bilinear term belongs to */
10701    SCIP_Real             intervalinfty,      /**< infinity value used in interval operations */
10702    SCIP_VAR*             x,                  /**< first variable */
10703    SCIP_Real             xsqrcoef,           /**< square coefficient of x */
10704    SCIP_Real             xlincoef,           /**< linear coefficient of x */
10705    SCIP_VAR*             y,                  /**< second variable */
10706    SCIP_Real             ysqrcoef,           /**< square coefficient of y */
10707    SCIP_Real             ylincoef,           /**< linear coefficient of y */
10708    SCIP_Real             bilincoef,          /**< bilinear coefficient of x*y */
10709    SCIP_INTERVAL         rhs,                /**< right hand side of quadratic equation */
10710    SCIP_RESULT*          result,             /**< pointer to store result of domain propagation */
10711    int*                  nchgbds             /**< counter to increment if domain reductions are found */
10712    )
10713 {
10714    SCIP_INTERVAL xbnds;
10715    SCIP_INTERVAL ybnds;
10716 
10717    assert(scip != NULL);
10718    assert(cons != NULL);
10719    assert(x != NULL);
10720    assert(y != NULL);
10721    assert(x != y);
10722    assert(result != NULL);
10723    assert(*result == SCIP_DIDNOTFIND || *result == SCIP_REDUCEDDOM);
10724    assert(nchgbds != NULL);
10725    assert(bilincoef != 0.0);
10726 
10727    if( SCIPintervalIsEntire(intervalinfty, rhs) )
10728       return SCIP_OKAY;
10729 
10730    SCIPintervalSetBounds(&xbnds,
10731       -infty2infty(SCIPinfinity(scip), intervalinfty, -MIN(SCIPvarGetLbLocal(x), SCIPvarGetUbLocal(x))),   /*lint !e666*/
10732       +infty2infty(SCIPinfinity(scip), intervalinfty,  MAX(SCIPvarGetLbLocal(x), SCIPvarGetUbLocal(x))));  /*lint !e666*/
10733    SCIPintervalSetBounds(&ybnds,
10734       -infty2infty(SCIPinfinity(scip), intervalinfty, -MIN(SCIPvarGetLbLocal(y), SCIPvarGetUbLocal(y))),   /*lint !e666*/
10735       +infty2infty(SCIPinfinity(scip), intervalinfty,  MAX(SCIPvarGetLbLocal(y), SCIPvarGetUbLocal(y))));  /*lint !e666*/
10736 
10737    /* try to find domain reductions for x */
10738    SCIPintervalSolveBivariateQuadExpressionAllScalar(intervalinfty, &xbnds, xsqrcoef, ysqrcoef, bilincoef, xlincoef, ylincoef, rhs, xbnds, ybnds);
10739 
10740    if( SCIPintervalIsEmpty(intervalinfty, xbnds) )
10741    {
10742       SCIPdebugMsg(scip, "found <%s> infeasible due to domain propagation for quadratic variable <%s>\n", SCIPconsGetName(cons), SCIPvarGetName(x));
10743       *result = SCIP_CUTOFF;
10744       return SCIP_OKAY;
10745    }
10746 
10747    if( !SCIPisInfinity(scip, -SCIPintervalGetInf(xbnds)) )
10748    {
10749       SCIP_CALL( propagateBoundsTightenVarLb(scip, cons, intervalinfty, x, SCIPintervalGetInf(xbnds), result, nchgbds) );
10750       if( *result == SCIP_CUTOFF )
10751          return SCIP_OKAY;
10752    }
10753 
10754    if( !SCIPisInfinity(scip,  SCIPintervalGetSup(xbnds)) )
10755    {
10756       SCIP_CALL( propagateBoundsTightenVarUb(scip, cons, intervalinfty, x, SCIPintervalGetSup(xbnds), result, nchgbds) );
10757       if( *result == SCIP_CUTOFF )
10758          return SCIP_OKAY;
10759    }
10760 
10761    return SCIP_OKAY;
10762 }
10763 #endif
10764 
10765 /** computes the minimal and maximal activity for the quadratic part in a constraint data
10766  *
10767  *  Only sums up terms that contribute finite values.
10768  *  Gives the number of terms that contribute infinite values.
10769  *  Only computes those activities where the corresponding side of the constraint is finite.
10770  */
10771 static
propagateBoundsGetQuadActivity(SCIP * scip,SCIP_CONSDATA * consdata,SCIP_Real intervalinfty,SCIP_Real * minquadactivity,SCIP_Real * maxquadactivity,int * minactivityinf,int * maxactivityinf,SCIP_INTERVAL * quadactcontr)10772 void propagateBoundsGetQuadActivity(
10773    SCIP*                 scip,               /**< SCIP data structure */
10774    SCIP_CONSDATA*        consdata,           /**< constraint data */
10775    SCIP_Real             intervalinfty,      /**< infinity value used in interval operations */
10776    SCIP_Real*            minquadactivity,    /**< minimal activity of quadratic variable terms where only terms with finite minimal activity contribute */
10777    SCIP_Real*            maxquadactivity,    /**< maximal activity of quadratic variable terms where only terms with finite maximal activity contribute */
10778    int*                  minactivityinf,     /**< number of quadratic variables that contribute -infinity to minimal activity */
10779    int*                  maxactivityinf,     /**< number of quadratic variables that contribute +infinity to maximal activity */
10780    SCIP_INTERVAL*        quadactcontr        /**< contribution of each quadratic variables to quadactivity */
10781    )
10782 {  /*lint --e{666}*/
10783    SCIP_ROUNDMODE prevroundmode;
10784    int       i;
10785    int       j;
10786    int       k;
10787    SCIP_INTERVAL tmp;
10788    SCIP_Real bnd;
10789    SCIP_INTERVAL xrng;
10790    SCIP_INTERVAL lincoef;
10791 
10792    assert(scip != NULL);
10793    assert(consdata != NULL);
10794    assert(minquadactivity != NULL);
10795    assert(maxquadactivity != NULL);
10796    assert(minactivityinf != NULL);
10797    assert(maxactivityinf != NULL);
10798    assert(quadactcontr != NULL);
10799 
10800    /* if lhs is -infinite, then we do not compute a maximal activity, so we set it to  infinity
10801     * if rhs is  infinite, then we do not compute a minimal activity, so we set it to -infinity
10802     */
10803    *minquadactivity = SCIPisInfinity(scip,  consdata->rhs) ? -intervalinfty : 0.0;
10804    *maxquadactivity = SCIPisInfinity(scip, -consdata->lhs) ?  intervalinfty : 0.0;
10805 
10806    *minactivityinf = 0;
10807    *maxactivityinf = 0;
10808 
10809    if( consdata->nquadvars == 0 )
10810    {
10811       SCIPintervalSet(&consdata->quadactivitybounds, 0.0);
10812       return;
10813    }
10814 
10815    for( i = 0; i < consdata->nquadvars; ++i )
10816    {
10817       /* there should be no quadratic variables fixed at -/+ infinity due to our locks */
10818       assert(!SCIPisInfinity(scip,  SCIPvarGetLbLocal(consdata->quadvarterms[i].var)));
10819       assert(!SCIPisInfinity(scip, -SCIPvarGetUbLocal(consdata->quadvarterms[i].var)));
10820 
10821       SCIPintervalSetBounds(&quadactcontr[i], -intervalinfty, intervalinfty);
10822 
10823       SCIPintervalSetBounds(&xrng,
10824          -infty2infty(SCIPinfinity(scip), intervalinfty, -MIN(SCIPvarGetLbLocal(consdata->quadvarterms[i].var), SCIPvarGetUbLocal(consdata->quadvarterms[i].var))),
10825          +infty2infty(SCIPinfinity(scip), intervalinfty,  MAX(SCIPvarGetLbLocal(consdata->quadvarterms[i].var), SCIPvarGetUbLocal(consdata->quadvarterms[i].var))));
10826 
10827       SCIPintervalSet(&lincoef, consdata->quadvarterms[i].lincoef);
10828       for( j = 0; j < consdata->quadvarterms[i].nadjbilin; ++j )
10829       {
10830          k = consdata->quadvarterms[i].adjbilin[j];
10831          if( consdata->bilinterms[k].var1 != consdata->quadvarterms[i].var )
10832             continue; /* handle this term later */
10833 
10834          SCIPintervalSetBounds(&tmp,
10835             -infty2infty(SCIPinfinity(scip), intervalinfty, -MIN(SCIPvarGetLbLocal(consdata->bilinterms[k].var2), SCIPvarGetUbLocal(consdata->bilinterms[k].var2))),
10836             +infty2infty(SCIPinfinity(scip), intervalinfty,  MAX(SCIPvarGetLbLocal(consdata->bilinterms[k].var2), SCIPvarGetUbLocal(consdata->bilinterms[k].var2))));
10837          SCIPintervalMulScalar(intervalinfty, &tmp, tmp, consdata->bilinterms[k].coef);
10838          SCIPintervalAdd(intervalinfty, &lincoef, lincoef, tmp);
10839       }
10840 
10841       if( !SCIPisInfinity(scip, -consdata->lhs) )
10842       {
10843          /* compute maximal activity only if there is a finite left hand side */
10844          bnd = SCIPintervalQuadUpperBound(intervalinfty, consdata->quadvarterms[i].sqrcoef, lincoef, xrng);
10845          if( bnd >= intervalinfty )
10846          {
10847             ++*maxactivityinf;
10848          }
10849          else if( SCIPisInfinity(scip, -bnd) )
10850          {
10851             /* if maximal activity is below value for -infinity, let's take -1e10 as upper bound on maximal activity
10852              * @todo Something better?
10853              */
10854             bnd = -sqrt(SCIPinfinity(scip));
10855             *maxquadactivity += bnd;
10856             quadactcontr[i].sup = bnd;
10857          }
10858          else
10859          {
10860             prevroundmode = SCIPintervalGetRoundingMode();
10861             SCIPintervalSetRoundingModeUpwards();
10862             *maxquadactivity += bnd;
10863             SCIPintervalSetRoundingMode(prevroundmode);
10864             quadactcontr[i].sup = bnd;
10865          }
10866       }
10867 
10868       if( !SCIPisInfinity(scip,  consdata->rhs) )
10869       {
10870          /* compute minimal activity only if there is a finite right hand side */
10871          SCIPintervalSetBounds(&lincoef, -SCIPintervalGetSup(lincoef), -SCIPintervalGetInf(lincoef));
10872          bnd = -SCIPintervalQuadUpperBound(intervalinfty, -consdata->quadvarterms[i].sqrcoef, lincoef, xrng);
10873 
10874          if( bnd <= -intervalinfty )
10875          {
10876             ++*minactivityinf;
10877          }
10878          else if( SCIPisInfinity(scip, bnd) )
10879          {
10880             /* if minimal activity is above value for infinity, let's take 1e10 as lower bound on minimal activity
10881              * @todo Something better?
10882              */
10883             bnd = sqrt(SCIPinfinity(scip));
10884             *minquadactivity += bnd;
10885             quadactcontr[i].inf = bnd;
10886          }
10887          else
10888          {
10889             prevroundmode = SCIPintervalGetRoundingMode();
10890             SCIPintervalSetRoundingModeDownwards();
10891             *minquadactivity += bnd;
10892             SCIPintervalSetRoundingMode(prevroundmode);
10893             quadactcontr[i].inf = bnd;
10894          }
10895       }
10896    }
10897 
10898    SCIPintervalSetBounds(&consdata->quadactivitybounds,
10899       (*minactivityinf > 0 ? -intervalinfty : *minquadactivity),
10900       (*maxactivityinf > 0 ?  intervalinfty : *maxquadactivity));
10901    assert(!SCIPintervalIsEmpty(intervalinfty, consdata->quadactivitybounds));
10902 }
10903 
10904 /** propagates bounds on a quadratic constraint */
10905 static
propagateBoundsCons(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS * cons,SCIP_RESULT * result,int * nchgbds,SCIP_Bool * redundant)10906 SCIP_RETCODE propagateBoundsCons(
10907    SCIP*                 scip,               /**< SCIP data structure */
10908    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
10909    SCIP_CONS*            cons,               /**< constraint to process */
10910    SCIP_RESULT*          result,             /**< pointer to store the result of the propagation call */
10911    int*                  nchgbds,            /**< buffer where to add the the number of changed bounds */
10912    SCIP_Bool*            redundant           /**< buffer where to store whether constraint has been found to be redundant */
10913    )
10914 {  /*lint --e{666}*/
10915    SCIP_CONSDATA*     consdata;
10916    SCIP_INTERVAL      consbounds;    /* lower and upper bounds of constraint */
10917    SCIP_INTERVAL      consactivity;  /* activity of linear plus quadratic part */
10918    SCIP_Real          intervalinfty; /* infinity used for interval computation */
10919    SCIP_Real          minquadactivity; /* lower bound on finite activities of quadratic part */
10920    SCIP_Real          maxquadactivity; /* upper bound on finite activities of quadratic part */
10921    int                quadminactinf; /* number of quadratic variables that contribute -infinity to minimal activity of quadratic term */
10922    int                quadmaxactinf; /* number of quadratic variables that contribute +infinity to maximal activity of quadratic term */
10923    SCIP_INTERVAL*     quadactcontr;  /* contribution of each quadratic variable term to quadactivity */
10924 
10925    SCIP_VAR*          var;
10926    SCIP_INTERVAL      rhs;           /* right hand side of quadratic equation */
10927    SCIP_INTERVAL      tmp;
10928    SCIP_ROUNDMODE     roundmode;
10929    SCIP_Real          bnd;
10930    int                i;
10931 
10932    assert(scip != NULL);
10933    assert(conshdlr != NULL);
10934    assert(cons != NULL);
10935    assert(result != NULL);
10936    assert(nchgbds != NULL);
10937    assert(redundant != NULL);
10938 
10939    consdata = SCIPconsGetData(cons);
10940    assert(consdata != NULL);
10941 
10942    *result = SCIP_DIDNOTRUN;
10943    *redundant = FALSE;
10944 
10945    *result = SCIP_DIDNOTFIND;
10946 
10947    intervalinfty = 1000 * SCIPinfinity(scip) * SCIPinfinity(scip);
10948 
10949    quadactcontr = NULL;
10950    quadminactinf = -1;
10951    quadmaxactinf = -1;
10952 
10953    SCIPdebugMsg(scip, "start domain propagation for constraint <%s>\n", SCIPconsGetName(cons));
10954 
10955    /* make sure we have activity of linear term and that they are consistent */
10956    consdataUpdateLinearActivity(scip, consdata, intervalinfty);
10957    assert(consdata->minlinactivity != SCIP_INVALID);  /*lint !e777 */
10958    assert(consdata->maxlinactivity != SCIP_INVALID);  /*lint !e777 */
10959    assert(consdata->minlinactivityinf >= 0);
10960    assert(consdata->maxlinactivityinf >= 0);
10961 
10962    /* sort quadratic variable terms, in case we need to search for terms occuring in bilinear terms later
10963     * we sort here already, since we rely on a constant variable order during this method
10964     */
10965    if( consdata->nbilinterms > 0 )
10966    {
10967       SCIP_CALL( consdataSortQuadVarTerms(scip, consdata) );
10968    }
10969 
10970    /* compute activity of quad term part, if not up to date
10971     * in that case, we also collect the contribution of each quad var term for later */
10972    if( SCIPintervalIsEmpty(intervalinfty, consdata->quadactivitybounds) )
10973    {
10974       SCIP_CALL( SCIPallocBufferArray(scip, &quadactcontr, consdata->nquadvars) );
10975       propagateBoundsGetQuadActivity(scip, consdata, intervalinfty, &minquadactivity, &maxquadactivity, &quadminactinf, &quadmaxactinf, quadactcontr);
10976       assert(!SCIPintervalIsEmpty(intervalinfty, consdata->quadactivitybounds));
10977    }
10978 
10979    SCIPdebugMsg(scip, "linear activity: [%g, %g]   quadratic activity: [%g, %g]\n",
10980       (consdata->minlinactivityinf > 0 ? -intervalinfty : consdata->minlinactivity),
10981       (consdata->maxlinactivityinf > 0 ?  intervalinfty : consdata->maxlinactivity),
10982       consdata->quadactivitybounds.inf, consdata->quadactivitybounds.sup);
10983 
10984    /* extend constraint bounds by epsilon to avoid some numerical difficulties */
10985    SCIPintervalSetBounds(&consbounds,
10986       -infty2infty(SCIPinfinity(scip), intervalinfty, -consdata->lhs+SCIPepsilon(scip)),
10987       +infty2infty(SCIPinfinity(scip), intervalinfty,  consdata->rhs+SCIPepsilon(scip)));
10988 
10989    /* check redundancy and infeasibility */
10990    SCIPintervalSetBounds(&consactivity, consdata->minlinactivityinf > 0 ? -intervalinfty : consdata->minlinactivity,
10991       consdata->maxlinactivityinf > 0 ? intervalinfty : consdata->maxlinactivity);
10992    SCIPintervalAdd(intervalinfty, &consactivity, consactivity, consdata->quadactivitybounds);
10993    if( SCIPintervalIsSubsetEQ(intervalinfty, consactivity, consbounds) )
10994    {
10995       SCIPdebugMsg(scip, "found constraint <%s> to be redundant: sides: [%g, %g], activity: [%g, %g]\n",
10996          SCIPconsGetName(cons), consdata->lhs, consdata->rhs, SCIPintervalGetInf(consactivity), SCIPintervalGetSup(consactivity));
10997       *redundant = TRUE;
10998       goto CLEANUP;
10999    }
11000 
11001    /* was SCIPintervalAreDisjoint(consbounds, consactivity), but that would allow violations up to eps only
11002     * we need to decide feasibility w.r.t. feastol (but still want to propagate w.r.t. eps)
11003     */
11004    if( (!SCIPisInfinity(scip, -consdata->lhs) && SCIPisGT(scip, consdata->lhs-SCIPfeastol(scip), SCIPintervalGetSup(consactivity))) ||
11005        (!SCIPisInfinity(scip,  consdata->rhs) && SCIPisLT(scip, consdata->rhs+SCIPfeastol(scip), SCIPintervalGetInf(consactivity))) )
11006    {
11007       SCIPdebugMsg(scip, "found constraint <%s> to be infeasible; sides: [%g, %g], activity: [%g, %g], infeas: %g\n",
11008          SCIPconsGetName(cons), consdata->lhs, consdata->rhs, SCIPintervalGetInf(consactivity), SCIPintervalGetSup(consactivity),
11009          MAX(consdata->lhs - SCIPintervalGetSup(consactivity), SCIPintervalGetInf(consactivity) - consdata->rhs));
11010       *result = SCIP_CUTOFF;
11011       goto CLEANUP;
11012    }
11013 
11014    /* propagate linear part \in rhs = consbounds - quadactivity (use the one from consdata, since that includes infinities) */
11015    SCIPintervalSub(intervalinfty, &rhs, consbounds, consdata->quadactivitybounds);
11016    if( !SCIPintervalIsEntire(intervalinfty, rhs) )
11017    {
11018       SCIP_Real coef;
11019 
11020       for( i = 0; i < consdata->nlinvars; ++i )
11021       {
11022          coef = consdata->lincoefs[i];
11023          var  = consdata->linvars[i];
11024 
11025          /* skip fixed variables
11026           * @todo is that a good or a bad idea?
11027           *   we can't expect much more tightening, but may detect infeasiblity, but shouldn't the check on the constraints activity detect that?
11028           */
11029          if( SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
11030             continue;
11031 
11032          /* due to large variable bounds and large coefficients, it might happen that the activity of the linear part
11033           * exceeds +/-SCIPinfinity() after updating the activities in consdataUpdateLinearActivity{Lb,Ub}Change; in
11034           * order to detect this case we need to check whether the value of consdata->{min,max}linactivity is infinite
11035           * (see #1433)
11036           */
11037          if( coef > 0.0 )
11038          {
11039             if( SCIPintervalGetSup(rhs) < intervalinfty )
11040             {
11041                assert(consdata->minlinactivity != SCIP_INVALID);  /*lint !e777 */
11042                /* try to tighten the upper bound on var x */
11043                if( consdata->minlinactivityinf == 0 && !SCIPisInfinity(scip, -consdata->minlinactivity) )
11044                {
11045                   assert(!SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)));
11046                   /* tighten upper bound on x to (rhs.sup - (minlinactivity - coef * xlb)) / coef */
11047                   roundmode = SCIPintervalGetRoundingMode();
11048                   SCIPintervalSetRoundingModeUpwards();
11049                   bnd  = SCIPintervalGetSup(rhs);
11050                   bnd -= consdata->minlinactivity;
11051                   bnd += coef * SCIPvarGetLbLocal(var);
11052                   bnd /= coef;
11053                   SCIPintervalSetRoundingMode(roundmode);
11054                   SCIP_CALL( propagateBoundsTightenVarUb(scip, cons, intervalinfty, var, bnd, result, nchgbds) );
11055                   if( *result == SCIP_CUTOFF )
11056                      break;
11057                }
11058                else if( consdata->minlinactivityinf == 1 && SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) )
11059                {
11060                   /* x was the variable that made the minimal linear activity equal -infinity, so
11061                    * we tighten upper bound on x to just (rhs.sup - minlinactivity) / coef */
11062                   roundmode = SCIPintervalGetRoundingMode();
11063                   SCIPintervalSetRoundingModeUpwards();
11064                   bnd  = SCIPintervalGetSup(rhs);
11065                   bnd -= consdata->minlinactivity;
11066                   bnd /= coef;
11067                   SCIPintervalSetRoundingMode(roundmode);
11068                   SCIP_CALL( propagateBoundsTightenVarUb(scip, cons, intervalinfty, var, bnd, result, nchgbds) );
11069                   if( *result == SCIP_CUTOFF )
11070                      break;
11071                }
11072                /* otherwise the minimal activity is -infinity and x is not solely responsible for this */
11073             }
11074 
11075             if( SCIPintervalGetInf(rhs) > -intervalinfty )
11076             {
11077                assert(consdata->maxlinactivity != SCIP_INVALID);  /*lint !e777 */
11078                /* try to tighten the lower bound on var x */
11079                if( consdata->maxlinactivityinf == 0 && !SCIPisInfinity(scip, consdata->maxlinactivity) )
11080                {
11081                   assert(!SCIPisInfinity(scip, SCIPvarGetUbLocal(var)));
11082                   /* tighten lower bound on x to (rhs.inf - (maxlinactivity - coef * xub)) / coef */
11083                   roundmode = SCIPintervalGetRoundingMode();
11084                   SCIPintervalSetRoundingModeDownwards();
11085                   bnd  = SCIPintervalGetInf(rhs);
11086                   bnd -= consdata->maxlinactivity;
11087                   bnd += coef * SCIPvarGetUbLocal(var);
11088                   bnd /= coef;
11089                   SCIPintervalSetRoundingMode(roundmode);
11090                   SCIP_CALL( propagateBoundsTightenVarLb(scip, cons, intervalinfty, var, bnd, result, nchgbds) );
11091                   if( *result == SCIP_CUTOFF )
11092                      break;
11093                }
11094                else if( consdata->maxlinactivityinf == 1 && SCIPisInfinity(scip, SCIPvarGetUbLocal(var)) )
11095                {
11096                   /* x was the variable that made the maximal linear activity equal infinity, so
11097                    * we tighten upper bound on x to just (rhs.inf - maxlinactivity) / coef */
11098                   roundmode = SCIPintervalGetRoundingMode();
11099                   SCIPintervalSetRoundingModeDownwards();
11100                   bnd  = SCIPintervalGetInf(rhs);
11101                   bnd -= consdata->maxlinactivity;
11102                   bnd /= coef;
11103                   SCIPintervalSetRoundingMode(roundmode);
11104                   SCIP_CALL( propagateBoundsTightenVarLb(scip, cons, intervalinfty, var, bnd, result, nchgbds) );
11105                   if( *result == SCIP_CUTOFF )
11106                      break;
11107                }
11108                /* otherwise the maximal activity is +infinity and x is not solely responsible for this */
11109             }
11110          }
11111          else
11112          {
11113             assert(coef < 0.0 );
11114             if( SCIPintervalGetInf(rhs) > -intervalinfty )
11115             {
11116                assert(consdata->maxlinactivity != SCIP_INVALID);  /*lint !e777 */
11117                /* try to tighten the upper bound on var x */
11118                if( consdata->maxlinactivityinf == 0  && !SCIPisInfinity(scip, consdata->maxlinactivity) )
11119                {
11120                   assert(!SCIPisInfinity(scip, SCIPvarGetLbLocal(var)));
11121                   /* compute upper bound on x to (maxlinactivity - coef * xlb) - rhs.inf / (-coef) */
11122                   roundmode = SCIPintervalGetRoundingMode();
11123                   SCIPintervalSetRoundingModeUpwards();
11124                   bnd  = consdata->maxlinactivity;
11125                   bnd += (-coef) * SCIPvarGetLbLocal(var);
11126                   bnd -= SCIPintervalGetInf(rhs);
11127                   bnd /= (-coef);
11128                   SCIPintervalSetRoundingMode(roundmode);
11129                   SCIP_CALL( propagateBoundsTightenVarUb(scip, cons, intervalinfty, var, bnd, result, nchgbds) );
11130                   if( *result == SCIP_CUTOFF )
11131                      break;
11132                }
11133                else if( consdata->maxlinactivityinf == 1 && SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)) )
11134                {
11135                   /* x was the variable that made the maximal linear activity equal infinity, so
11136                    * we tighten upper bound on x to just (maxlinactivity - rhs.inf) / (-coef) */
11137                   roundmode = SCIPintervalGetRoundingMode();
11138                   SCIPintervalSetRoundingModeUpwards();
11139                   bnd  = consdata->maxlinactivity;
11140                   bnd -= SCIPintervalGetInf(rhs);
11141                   bnd /= (-coef);
11142                   SCIPintervalSetRoundingMode(roundmode);
11143                   SCIP_CALL( propagateBoundsTightenVarUb(scip, cons, intervalinfty, var, bnd, result, nchgbds) );
11144                   if( *result == SCIP_CUTOFF )
11145                      break;
11146                }
11147                /* otherwise the maximal activity is infinity and x is not solely responsible for this */
11148             }
11149 
11150             if( SCIPintervalGetSup(rhs) < intervalinfty )
11151             {
11152                assert(consdata->minlinactivity != SCIP_INVALID);  /*lint !e777 */
11153                /* try to tighten the lower bound on var x */
11154                if( consdata->minlinactivityinf == 0 && !SCIPisInfinity(scip, -consdata->minlinactivity) )
11155                {
11156                   assert(!SCIPisInfinity(scip, SCIPvarGetUbLocal(var)));
11157                   /* compute lower bound on x to (minlinactivity - coef * xub) - rhs.sup / (-coef) */
11158                   roundmode = SCIPintervalGetRoundingMode();
11159                   SCIPintervalSetRoundingModeDownwards();
11160                   bnd  = consdata->minlinactivity;
11161                   bnd += (-coef) * SCIPvarGetUbLocal(var);
11162                   bnd -= SCIPintervalGetSup(rhs);
11163                   bnd /= (-coef);
11164                   SCIPintervalSetRoundingMode(roundmode);
11165                   SCIP_CALL( propagateBoundsTightenVarLb(scip, cons, intervalinfty, var, bnd, result, nchgbds) );
11166                   if( *result == SCIP_CUTOFF )
11167                      break;
11168                }
11169                else if( consdata->minlinactivityinf == 1 && SCIPisInfinity(scip, SCIPvarGetUbLocal(var)) )
11170                {
11171                   /* x was the variable that made the maximal linear activity equal -infinity, so
11172                    * we tighten lower bound on x to just (minlinactivity - rhs.sup) / (-coef) */
11173                   roundmode = SCIPintervalGetRoundingMode();
11174                   SCIPintervalSetRoundingModeDownwards();
11175                   bnd  = consdata->minlinactivity;
11176                   bnd -= SCIPintervalGetSup(rhs);
11177                   bnd /= (-coef);
11178                   SCIPintervalSetRoundingMode(roundmode);
11179                   SCIP_CALL( propagateBoundsTightenVarLb(scip, cons, intervalinfty, var, bnd, result, nchgbds) );
11180                   if( *result == SCIP_CUTOFF )
11181                      break;
11182                }
11183                /* otherwise the minimal activity is -infinity and x is not solely responsible for this */
11184             }
11185          }
11186       }
11187       if( *result == SCIP_CUTOFF )
11188          goto CLEANUP;
11189    }
11190 
11191    /* propagate quadratic part \in rhs = consbounds - linactivity */
11192    assert(consdata->minlinactivity != SCIP_INVALID);  /*lint !e777 */
11193    assert(consdata->maxlinactivity != SCIP_INVALID);  /*lint !e777 */
11194    consdataUpdateLinearActivity(scip, consdata, intervalinfty); /* make sure, activities of linear part did not become invalid by above bound changes, if any */
11195    assert(consdata->minlinactivityinf > 0 || consdata->maxlinactivityinf > 0 || consdata->minlinactivity <= consdata->maxlinactivity);
11196    SCIPintervalSetBounds(&tmp,
11197       (consdata->minlinactivityinf > 0 ? -intervalinfty : consdata->minlinactivity),
11198       (consdata->maxlinactivityinf > 0 ?  intervalinfty : consdata->maxlinactivity));
11199    SCIPintervalSub(intervalinfty, &rhs, consbounds, tmp);
11200    if( !SCIPintervalIsEntire(intervalinfty, rhs) )
11201    {
11202       if( consdata->nquadvars == 1 )
11203       {
11204          /* quadratic part is just a*x^2+b*x -> a common case that we treat directly */
11205          SCIP_INTERVAL lincoef;    /* linear coefficient of quadratic equation */
11206 
11207          assert(consdata->nbilinterms == 0);
11208 
11209          var = consdata->quadvarterms[0].var;
11210          SCIPintervalSet(&lincoef, consdata->quadvarterms[0].lincoef);
11211 
11212          /* propagate a*x^2 + b*x \in rhs */
11213          SCIP_CALL( propagateBoundsQuadVar(scip, cons, intervalinfty, var, consdata->quadvarterms[0].sqrcoef, lincoef, rhs, result, nchgbds) );
11214       }
11215       else if( consdata->nbilinterms == 1 && consdata->nquadvars == 2 )
11216       {
11217          /* quadratic part is just ax*x^2+bx*x + ay*y^2+by*y + c*xy -> a common case that we treat directly */
11218          assert(consdata->bilinterms[0].var1 == consdata->quadvarterms[0].var || consdata->bilinterms[0].var1 == consdata->quadvarterms[1].var);
11219          assert(consdata->bilinterms[0].var2 == consdata->quadvarterms[0].var || consdata->bilinterms[0].var2 == consdata->quadvarterms[1].var);
11220 
11221          /* find domain reductions for x from a_x x^2 + b_x x + a_y y^2 + b_y y + c x y \in rhs */
11222          SCIP_CALL( propagateBoundsBilinearTerm(scip, cons, intervalinfty,
11223                consdata->quadvarterms[0].var, consdata->quadvarterms[0].sqrcoef, consdata->quadvarterms[0].lincoef,
11224                consdata->quadvarterms[1].var, consdata->quadvarterms[1].sqrcoef, consdata->quadvarterms[1].lincoef,
11225                consdata->bilinterms[0].coef,
11226                rhs, result, nchgbds) );
11227          if( *result != SCIP_CUTOFF )
11228          {
11229             /* find domain reductions for y from a_x x^2 + b_x x + a_y y^2 + b_y y + c x y \in rhs */
11230             SCIP_CALL( propagateBoundsBilinearTerm(scip, cons, intervalinfty,
11231                   consdata->quadvarterms[1].var, consdata->quadvarterms[1].sqrcoef, consdata->quadvarterms[1].lincoef,
11232                   consdata->quadvarterms[0].var, consdata->quadvarterms[0].sqrcoef, consdata->quadvarterms[0].lincoef,
11233                   consdata->bilinterms[0].coef,
11234                   rhs, result, nchgbds) );
11235          }
11236       }
11237       else
11238       {
11239          /* general case */
11240 
11241          /* compute "advanced" information on quad var term activities, if not up-to-date */
11242          if( quadminactinf == -1  )
11243          {
11244             assert(quadactcontr == NULL);
11245             SCIP_CALL( SCIPallocBufferArray(scip, &quadactcontr, consdata->nquadvars) );
11246             propagateBoundsGetQuadActivity(scip, consdata, intervalinfty, &minquadactivity, &maxquadactivity, &quadminactinf, &quadmaxactinf, quadactcontr);
11247          }
11248          assert(quadactcontr != NULL);
11249          assert(quadminactinf >= 0);
11250          assert(quadmaxactinf >= 0);
11251 
11252          /* if the quad activities are not hopelessly unbounded on useful sides, try to deduce domain reductions on quad vars */
11253          if( (SCIPintervalGetSup(rhs) <  intervalinfty && quadminactinf <= 1) ||
11254             ( SCIPintervalGetInf(rhs) > -intervalinfty && quadmaxactinf <= 1) )
11255          {
11256             SCIP_INTERVAL lincoef;
11257             SCIP_INTERVAL rhs2;
11258             int j;
11259             int k;
11260 
11261             for( i = 0; i < consdata->nquadvars; ++i )
11262             {
11263                var = consdata->quadvarterms[i].var;
11264 
11265                /* skip fixed variables
11266                 * @todo is that a good or a bad idea?
11267                 *   we can't expect much more tightening, but may detect infeasiblity, but shouldn't the check on the constraints activity detect that?
11268                 */
11269                if( SCIPisEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
11270                   continue;
11271 
11272                /* compute rhs2 such that we can propagate quadvarterm(x_i) \in rhs2 */
11273 
11274                /* setup rhs2.sup = rhs.sup - (quadactivity.inf - quadactcontr[i].inf), if everything were finite
11275                 * if only quadactcontr[i].inf is infinite (i.e., the other i are all finite), we just get rhs2.sup = rhs.sup
11276                 * otherwise we get rhs2.sup = infinity */
11277                if( SCIPintervalGetSup(rhs) < intervalinfty )
11278                {
11279                   if( quadminactinf == 0 || (quadminactinf == 1 && SCIPintervalGetInf(quadactcontr[i]) <= -intervalinfty) )
11280                   {
11281                      roundmode = SCIPintervalGetRoundingMode();
11282                      SCIPintervalSetRoundingModeUpwards();
11283                      rhs2.sup = rhs.sup - minquadactivity;  /*lint !e644*/
11284                      /* if the residual quad min activity w.r.t. quad var term i is finite and nonzero, so add it to right hand side */
11285                      if( quadminactinf == 0 && SCIPintervalGetInf(quadactcontr[i]) != 0.0 )
11286                         rhs2.sup += SCIPintervalGetInf(quadactcontr[i]);
11287                      SCIPintervalSetRoundingMode(roundmode);
11288                   }
11289                   else
11290                   {
11291                      /* there are either >= 2 quad var terms contributing -infinity, or there is one which is not i */
11292                      rhs2.sup = intervalinfty;
11293                   }
11294                }
11295                else
11296                {
11297                   rhs2.sup = intervalinfty;
11298                }
11299 
11300                /* setup rhs2.inf = rhs.inf - (quadactivity.sup - quadactcontr[i].sup), see also above */
11301                if( SCIPintervalGetInf(rhs) > -intervalinfty )
11302                {
11303                   if( quadmaxactinf == 0 || (quadmaxactinf == 1 && SCIPintervalGetSup(quadactcontr[i]) >= intervalinfty) )
11304                   {
11305                      roundmode = SCIPintervalGetRoundingMode();
11306                      SCIPintervalSetRoundingModeDownwards();
11307                      rhs2.inf = rhs.inf - maxquadactivity;  /*lint !e644*/
11308                      /* if the residual quad max activity w.r.t. quad var term i is finite and nonzero, so add it to right hand side */
11309                      if( quadmaxactinf == 0 && SCIPintervalGetSup(quadactcontr[i]) != 0.0 )
11310                         rhs2.inf += SCIPintervalGetSup(quadactcontr[i]);
11311                      SCIPintervalSetRoundingMode(roundmode);
11312                   }
11313                   else
11314                   {
11315                      /* there are either >= 2 quad var terms contributing infinity, or there is one which is not i */
11316                      rhs2.inf = -intervalinfty;
11317                   }
11318                }
11319                else
11320                {
11321                   rhs2.inf = -intervalinfty;
11322                }
11323                assert(!SCIPintervalIsEmpty(intervalinfty, rhs2));
11324 
11325                /* if rhs2 is entire, then there is nothing we could propagate */
11326                if( SCIPintervalIsEntire(intervalinfty, rhs2) )
11327                   continue;
11328 
11329                /* assemble linear coefficient for quad equation a*x^2 + b*x \in rhs2 */
11330                SCIPintervalSet(&lincoef, consdata->quadvarterms[i].lincoef);
11331                for( j = 0; j < consdata->quadvarterms[i].nadjbilin; ++j )
11332                {
11333                   k = consdata->quadvarterms[i].adjbilin[j];
11334 #if 1
11335                   if( consdata->bilinterms[k].var1 == var )
11336                   {
11337                      /* bilinear term k contributes to the activity of quad var term i, so just add bounds to linear coef */
11338                      SCIPintervalSetBounds(&tmp,
11339                         -infty2infty(SCIPinfinity(scip), intervalinfty, -MIN(SCIPvarGetLbLocal(consdata->bilinterms[k].var2), SCIPvarGetUbLocal(consdata->bilinterms[k].var2))),
11340                         +infty2infty(SCIPinfinity(scip), intervalinfty,  MAX(SCIPvarGetLbLocal(consdata->bilinterms[k].var2), SCIPvarGetUbLocal(consdata->bilinterms[k].var2))));
11341                      SCIPintervalMulScalar(intervalinfty, &tmp, tmp, consdata->bilinterms[k].coef);
11342                      SCIPintervalAdd(intervalinfty, &lincoef, lincoef, tmp);
11343                   }
11344                   else
11345                   {
11346                      /* bilinear term k does not contribute to the activity of quad var term i
11347                       * so bounds on term k are contained in rhs2
11348                       * if they are finite, we try to remove them from rhs2 and update lincoef instead
11349                       * if the bounds on bilinear term k as added to rhs2 are old due to recent bound tightening, we may not do best possible, but still correct
11350                       * HOWEVER: when computing rhs2, we may not just have added the bounds for the bilinear term, but for the associated quadratic term
11351                       *   for this complete term, we used SCIPintervalQuad to compute the bounds
11352                       *   since we do not want to repeat a call to SCIPintervalQuad for that quadratic term with bilinear term k removed,
11353                       *   we only remove the bounds for the bilinear term k from rhs2 if the associated quadratic term consists only of this bilinear term,
11354                       *   i.e., the quadratic term corresponding to var1 should be only var1*var2, but have no square or linear coefs or other bilinear terms
11355                       *   (for efficiency reasons, we check here only if there are any other bilinear terms than var1*var2 associated with var1, even if they are not associated with the quad var term for var1)
11356                       */
11357                      SCIP_INTERVAL me;
11358                      SCIP_INTERVAL bilinbounds;
11359                      int otherpos;
11360 
11361                      assert(consdata->bilinterms[k].var2 == var);
11362 
11363                      assert(consdata->quadvarssorted);
11364                      SCIP_CALL( consdataFindQuadVarTerm(scip, consdata, consdata->bilinterms[k].var1, &otherpos) );
11365                      assert(otherpos >= 0);
11366                      assert(consdata->quadvarterms[otherpos].var == consdata->bilinterms[k].var1);
11367 
11368                      if( (consdata->quadvarterms[otherpos].sqrcoef != 0.0) || consdata->quadvarterms[otherpos].lincoef != 0.0 ||
11369                           consdata->quadvarterms[otherpos].nadjbilin > 1 )
11370                         continue;
11371 
11372                      /* set tmp to bounds of other variable and multiply with bilin coef */
11373                      SCIPintervalSetBounds(&tmp,
11374                         -infty2infty(SCIPinfinity(scip), intervalinfty, -MIN(SCIPvarGetLbLocal(consdata->bilinterms[k].var1), SCIPvarGetUbLocal(consdata->bilinterms[k].var1))),
11375                         +infty2infty(SCIPinfinity(scip), intervalinfty,  MAX(SCIPvarGetLbLocal(consdata->bilinterms[k].var1), SCIPvarGetUbLocal(consdata->bilinterms[k].var1))));
11376                      SCIPintervalMulScalar(intervalinfty, &tmp, tmp, consdata->bilinterms[k].coef);
11377 
11378                      /* set me to bounds of i'th variable */
11379                      SCIPintervalSetBounds(&me,
11380                         -infty2infty(SCIPinfinity(scip), intervalinfty, -MIN(SCIPvarGetLbLocal(consdata->bilinterms[k].var2), SCIPvarGetUbLocal(consdata->bilinterms[k].var2))),
11381                         +infty2infty(SCIPinfinity(scip), intervalinfty,  MAX(SCIPvarGetLbLocal(consdata->bilinterms[k].var2), SCIPvarGetUbLocal(consdata->bilinterms[k].var2))));
11382 
11383                      /* remove me*tmp from rhs2 */
11384 
11385                      roundmode = SCIPintervalGetRoundingMode();
11386 
11387                      if( rhs2.inf > -intervalinfty )
11388                      {
11389                         /* need upward rounding for SCIPintervalMulSup */
11390                         SCIPintervalSetRoundingModeUpwards();
11391                         SCIPintervalMulSup(intervalinfty, &bilinbounds, me, tmp);
11392                         /* rhs2.inf += bilinbounds.sup, but we are in upward rounding */
11393                         if( bilinbounds.sup < intervalinfty )
11394                            rhs2.inf = SCIPintervalNegateReal(SCIPintervalNegateReal(rhs2.inf) - bilinbounds.sup);
11395                      }
11396 
11397                      if( rhs2.sup <  intervalinfty )
11398                      {
11399                         /* need downward rounding for SCIPintervalMulInf */
11400                         SCIPintervalSetRoundingModeDownwards();
11401                         SCIPintervalMulInf(intervalinfty, &bilinbounds, me, tmp);
11402                         /* rhs2.sup += bilinbounds.inf, but we are in downward rounding */
11403                         if( bilinbounds.inf > -intervalinfty )
11404                            rhs2.sup = SCIPintervalNegateReal(SCIPintervalNegateReal(rhs2.sup) - bilinbounds.inf);
11405                      }
11406 
11407                      SCIPintervalSetRoundingMode(roundmode);
11408 
11409                      /* in theory, rhs2 should not be empty here
11410                       * what we tried to do here is to remove the contribution of the k'th bilinear term (=bilinbounds) to [minquadactivity,maxquadactivity] from rhs2
11411                       * however, quadactivity is computed differently (as x*(a1*y1+...+an*yn)) than bilinbounds (a*ak*yk) and since interval arithmetics do overestimation,
11412                       * it can happen that bilinbounds is actually slightly larger than quadactivity, which results in rhs2 being (slightly) empty
11413                       * a proper fix could be to compute the quadactivity also as x*a1*y1+...+x*an*yn in propagateBoundsGetQuadAcitivity if sqrcoef=0, but due to taking
11414                       * also infinite bounds into account, this complicates the code even further
11415                       * instead, I'll just work around this by turning an empty rhs2 into a small non-empty one
11416                       */
11417                      if( SCIPintervalIsEmpty(intervalinfty, rhs2) )
11418                      {
11419                         assert(SCIPrelDiff(rhs2.inf, rhs2.sup) < 1e-6);
11420                         SCIPswapReals(&rhs2.inf, &rhs2.sup);
11421                      }
11422 
11423                      /* add tmp to lincoef */
11424                      SCIPintervalAdd(intervalinfty, &lincoef, lincoef, tmp);
11425                   }
11426 #else
11427                   if( consdata->bilinterms[k].var1 != var )
11428                      continue; /* this term does not contribute to the activity of quad var term i */
11429 
11430                   SCIPintervalSetBounds(&tmp,
11431                      -infty2infty(SCIPinfinity(scip), intervalinfty, -MIN(SCIPvarGetLbLocal(consdata->bilinterms[k].var2), SCIPvarGetUbLocal(consdata->bilinterms[k].var2))),
11432                      +infty2infty(SCIPinfinity(scip), intervalinfty,  MAX(SCIPvarGetLbLocal(consdata->bilinterms[k].var2), SCIPvarGetUbLocal(consdata->bilinterms[k].var2))));
11433                   SCIPintervalMulScalar(intervalinfty, &tmp, tmp, consdata->bilinterms[k].coef);
11434                   SCIPintervalAdd(intervalinfty, &lincoef, lincoef, tmp);
11435 #endif
11436                }
11437 
11438                /* deduce domain reductions for x_i */
11439                SCIP_CALL( propagateBoundsQuadVar(scip, cons, intervalinfty, var, consdata->quadvarterms[i].sqrcoef, lincoef, rhs2, result, nchgbds) );
11440                if( *result == SCIP_CUTOFF )
11441                   goto CLEANUP;
11442             }
11443          }
11444       }
11445    }
11446 
11447  CLEANUP:
11448    SCIPfreeBufferArrayNull(scip, &quadactcontr);
11449 
11450    return SCIP_OKAY;
11451 }
11452 
11453 /** calls domain propagation for a set of constraints */
11454 static
propagateBounds(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_RESULT * result,int * nchgbds)11455 SCIP_RETCODE propagateBounds(
11456    SCIP*                 scip,               /**< SCIP data structure */
11457    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
11458    SCIP_CONS**           conss,              /**< constraints to process */
11459    int                   nconss,             /**< number of constraints */
11460    SCIP_RESULT*          result,             /**< pointer to store the result of the propagation calls */
11461    int*                  nchgbds             /**< buffer where to add the the number of changed bounds */
11462    )
11463 {
11464    SCIP_CONSHDLRDATA* conshdlrdata;
11465    SCIP_RESULT propresult;
11466    SCIP_Bool   redundant;
11467    int         c;
11468    int         roundnr;
11469    SCIP_Bool   success;
11470    int         maxproprounds;
11471 
11472    assert(scip != NULL);
11473    assert(conshdlr != NULL);
11474    assert(conss != NULL || nconss == 0);
11475    assert(result != NULL);
11476    assert(nchgbds != NULL);
11477 
11478    assert(SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING || SCIPgetStage(scip) == SCIP_STAGE_SOLVING);
11479 
11480    conshdlrdata = SCIPconshdlrGetData(conshdlr);
11481    assert(conshdlrdata != NULL);
11482 
11483    *result = SCIP_DIDNOTFIND;
11484    roundnr = 0;
11485    if( SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING )
11486       maxproprounds = conshdlrdata->maxproproundspresolve;
11487    else
11488       maxproprounds = conshdlrdata->maxproprounds;
11489 
11490    do
11491    {
11492       success = FALSE;
11493       ++roundnr;
11494 
11495       SCIPdebugMsg(scip, "starting domain propagation round %d of %d for %d constraints\n", roundnr, maxproprounds, nconss);
11496 
11497       for( c = 0; c < nconss && *result != SCIP_CUTOFF; ++c )
11498       {
11499          assert(conss != NULL);
11500          if( !SCIPconsIsEnabled(conss[c]) )
11501             continue;
11502 
11503          if( SCIPconsIsMarkedPropagate(conss[c]) )
11504          {
11505             /* unmark constraint for propagation */
11506             SCIP_CALL( SCIPunmarkConsPropagate(scip, conss[c]) );
11507 
11508             SCIP_CALL( propagateBoundsCons(scip, conshdlr, conss[c], &propresult, nchgbds, &redundant) );
11509             if( propresult != SCIP_DIDNOTFIND && propresult != SCIP_DIDNOTRUN )
11510             {
11511                *result = propresult;
11512                success = TRUE;
11513             }
11514             if( redundant )
11515             {
11516                SCIPdebugMsg(scip, "deleting constraint <%s> locally\n", SCIPconsGetName(conss[c]));
11517                SCIP_CALL( SCIPdelConsLocal(scip, conss[c]) );
11518             }
11519          }
11520       }
11521    }
11522    while( success && *result != SCIP_CUTOFF && roundnr < maxproprounds );
11523 
11524    return SCIP_OKAY;
11525 }
11526 
11527 /** checks for a linear variable that can be increase or decreased without harming feasibility */
11528 static
consdataFindUnlockedLinearVar(SCIP * scip,SCIP_CONSDATA * consdata)11529 void consdataFindUnlockedLinearVar(
11530    SCIP*                 scip,               /**< SCIP data structure */
11531    SCIP_CONSDATA*        consdata            /**< constraint data */
11532    )
11533 {
11534    int i;
11535    int downlock;
11536    int uplock;
11537 
11538    consdata->linvar_maydecrease = -1;
11539    consdata->linvar_mayincrease = -1;
11540 
11541    /* check for a linear variable that can be increase or decreased without harming feasibility */
11542    for( i = 0; i < consdata->nlinvars; ++i )
11543    {
11544       /* compute locks of i'th linear variable */
11545       assert(consdata->lincoefs[i] != 0.0);
11546       if( consdata->lincoefs[i] > 0.0 )
11547       {
11548          downlock = !SCIPisInfinity(scip, -consdata->lhs) ? 1 : 0;  /* lhs <= x -> downlock on x */
11549          uplock = !SCIPisInfinity(scip,  consdata->rhs) ? 1 : 0;    /* x <= rhs -> uplock on x */
11550       }
11551       else
11552       {
11553          downlock = !SCIPisInfinity(scip,  consdata->rhs) ? 1 : 0;  /* -x <= rhs -> downlock on x */
11554          uplock = !SCIPisInfinity(scip, -consdata->lhs) ? 1 : 0;    /* lhs <= -x -> uplock on x */
11555       }
11556 
11557       if( SCIPvarGetNLocksDownType(consdata->linvars[i], SCIP_LOCKTYPE_MODEL) - downlock == 0 )
11558       {
11559          /* for a*x + q(y) \in [lhs, rhs], we can decrease x without harming other constraints */
11560          /* if we have already one candidate, then take the one where the loss in the objective function is less */
11561          if( (consdata->linvar_maydecrease < 0) ||
11562             (SCIPvarGetObj(consdata->linvars[consdata->linvar_maydecrease]) / consdata->lincoefs[consdata->linvar_maydecrease] > SCIPvarGetObj(consdata->linvars[i]) / consdata->lincoefs[i]) )
11563             consdata->linvar_maydecrease = i;
11564       }
11565 
11566       if( SCIPvarGetNLocksUpType(consdata->linvars[i], SCIP_LOCKTYPE_MODEL) - uplock == 0 )
11567       {
11568          /* for a*x + q(y) \in [lhs, rhs], we can increase x without harm */
11569          /* if we have already one candidate, then take the one where the loss in the objective function is less */
11570          if( (consdata->linvar_mayincrease < 0) ||
11571             (SCIPvarGetObj(consdata->linvars[consdata->linvar_mayincrease]) / consdata->lincoefs[consdata->linvar_mayincrease] > SCIPvarGetObj(consdata->linvars[i]) / consdata->lincoefs[i]) )
11572             consdata->linvar_mayincrease = i;
11573       }
11574    }
11575 
11576 #ifdef SCIP_DEBUG
11577    if( consdata->linvar_mayincrease >= 0 )
11578    {
11579       SCIPdebugMsg(scip, "may increase <%s> to become feasible\n", SCIPvarGetName(consdata->linvars[consdata->linvar_mayincrease]));
11580    }
11581    if( consdata->linvar_maydecrease >= 0 )
11582    {
11583       SCIPdebugMsg(scip, "may decrease <%s> to become feasible\n", SCIPvarGetName(consdata->linvars[consdata->linvar_maydecrease]));
11584    }
11585 #endif
11586 }
11587 
11588 /** Given a solution where every quadratic constraint is either feasible or can be made feasible by
11589  *  moving a linear variable, construct the corresponding feasible solution and pass it to the trysol heuristic.
11590  *
11591  *  The method assumes that this is always possible and that not all constraints are feasible already.
11592  */
11593 static
proposeFeasibleSolution(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,SCIP_SOL * sol,SCIP_Bool * success)11594 SCIP_RETCODE proposeFeasibleSolution(
11595    SCIP*                 scip,               /**< SCIP data structure */
11596    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
11597    SCIP_CONS**           conss,              /**< constraints to process */
11598    int                   nconss,             /**< number of constraints */
11599    SCIP_SOL*             sol,                /**< solution to process */
11600    SCIP_Bool*            success             /**< buffer to store whether we succeeded to construct a solution that satisfies all provided constraints */
11601    )
11602 {
11603    SCIP_CONSHDLRDATA* conshdlrdata;
11604    SCIP_CONSDATA* consdata;
11605    SCIP_SOL* newsol;
11606    SCIP_VAR* var;
11607    int c;
11608    SCIP_Real viol;
11609    SCIP_Real delta;
11610    SCIP_Real gap;
11611    SCIP_Bool solviolbounds;
11612    SCIP_Bool solchanged;
11613 
11614    assert(scip  != NULL);
11615    assert(conshdlr != NULL);
11616    assert(conss != NULL || nconss == 0);
11617    assert(success != NULL);
11618 
11619    *success = FALSE;
11620 
11621    /* don't propose new solutions if not in presolve or solving */
11622    if( SCIPgetStage(scip) < SCIP_STAGE_INITPRESOLVE || SCIPgetStage(scip) >= SCIP_STAGE_SOLVED )
11623       return SCIP_OKAY;
11624 
11625    conshdlrdata = SCIPconshdlrGetData(conshdlr);
11626    assert(conshdlrdata != NULL);
11627 
11628    if( sol != NULL )
11629    {
11630       SCIP_CALL( SCIPcreateSolCopy(scip, &newsol, sol) );
11631    }
11632    else
11633    {
11634       SCIP_CALL( SCIPcreateLPSol(scip, &newsol, NULL) );
11635    }
11636    SCIP_CALL( SCIPunlinkSol(scip, newsol) );
11637    solchanged = FALSE;
11638 
11639    SCIPdebugMsg(scip, "attempt to make solution from <%s> feasible by shifting linear variable\n",
11640       sol != NULL ? (SCIPsolGetHeur(sol) != NULL ? SCIPheurGetName(SCIPsolGetHeur(sol)) : "tree") : "LP");
11641 
11642    for( c = 0; c < nconss; ++c )
11643    {
11644       consdata = SCIPconsGetData(conss[c]);  /*lint !e613*/
11645       assert(consdata != NULL);
11646 
11647       /* recompute violation of constraint in case newsol is not identical to sol anymore */
11648       if( solchanged )
11649       {
11650          SCIP_CALL( computeViolation(scip, conss[c], newsol, &solviolbounds) );  /*lint !e613*/
11651          assert(!solviolbounds);
11652       }
11653 
11654       if( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) )
11655          viol = consdata->lhs - consdata->activity;
11656       else if( SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
11657          viol = consdata->rhs - consdata->activity;
11658       else
11659          continue; /* constraint is satisfied */
11660 
11661       assert(viol != 0.0);
11662       if( consdata->linvar_mayincrease >= 0 &&
11663          ((viol > 0.0 && consdata->lincoefs[consdata->linvar_mayincrease] > 0.0) || (viol < 0.0 && consdata->lincoefs[consdata->linvar_mayincrease] < 0.0)) )
11664       {
11665          /* have variable where increasing makes the constraint less violated */
11666          var = consdata->linvars[consdata->linvar_mayincrease];
11667          /* compute how much we would like to increase var */
11668          delta = viol / consdata->lincoefs[consdata->linvar_mayincrease];
11669          assert(delta > 0.0);
11670          /* if var has an upper bound, may need to reduce delta */
11671          if( !SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) )
11672          {
11673             gap = SCIPvarGetUbGlobal(var) - SCIPgetSolVal(scip, newsol, var);
11674             delta = MIN(MAX(0.0, gap), delta);
11675          }
11676          if( SCIPisPositive(scip, delta) && !SCIPisInfinity(scip, REALABS(delta)) )
11677          {
11678             /* if variable is integral, round delta up so that it will still have an integer value */
11679             if( SCIPvarIsIntegral(var) )
11680                delta = SCIPceil(scip, delta);
11681 
11682             SCIP_CALL( SCIPincSolVal(scip, newsol, var, delta) );
11683             /*lint --e{613} */
11684             SCIPdebugMsg(scip, "increase <%s> by %g to %g to remedy lhs-violation %g of cons <%s>\n", SCIPvarGetName(var), delta, SCIPgetSolVal(scip, newsol, var), viol, SCIPconsGetName(conss[c]));
11685 
11686             solchanged = TRUE;
11687 
11688             /* adjust constraint violation, if satisfied go on to next constraint */
11689             viol -= consdata->lincoefs[consdata->linvar_mayincrease] * delta;
11690             if( SCIPisZero(scip, viol) )
11691                continue;
11692          }
11693       }
11694 
11695       assert(viol != 0.0);
11696       if( consdata->linvar_maydecrease >= 0 &&
11697          ((viol > 0.0 && consdata->lincoefs[consdata->linvar_maydecrease] < 0.0) || (viol < 0.0 && consdata->lincoefs[consdata->linvar_maydecrease] > 0.0)) )
11698       {
11699          /* have variable where decreasing makes constraint less violated */
11700          var = consdata->linvars[consdata->linvar_maydecrease];
11701          /* compute how much we would like to decrease var */
11702          delta = viol / consdata->lincoefs[consdata->linvar_maydecrease];
11703          assert(delta < 0.0);
11704          /* if var has a lower bound, may need to reduce delta */
11705          if( !SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) )
11706          {
11707             gap = SCIPgetSolVal(scip, newsol, var) - SCIPvarGetLbGlobal(var);
11708             delta = MAX(MIN(0.0, gap), delta);
11709          }
11710          if( SCIPisNegative(scip, delta) && !SCIPisInfinity(scip, REALABS(delta)) )
11711          {
11712             /* if variable is integral, round delta down so that it will still have an integer value */
11713             if( SCIPvarIsIntegral(var) )
11714                delta = SCIPfloor(scip, delta);
11715             SCIP_CALL( SCIPincSolVal(scip, newsol, var, delta) );
11716             /*lint --e{613} */
11717             SCIPdebugMsg(scip, "increase <%s> by %g to %g to remedy rhs-violation %g of cons <%s>\n", SCIPvarGetName(var), delta, SCIPgetSolVal(scip, newsol, var), viol, SCIPconsGetName(conss[c]));
11718 
11719             solchanged = TRUE;
11720 
11721             /* adjust constraint violation, if satisfied go on to next constraint */
11722             viol -= consdata->lincoefs[consdata->linvar_maydecrease] * delta;
11723             if( SCIPisZero(scip, viol) )
11724                continue;
11725          }
11726       }
11727 
11728       /* still here... so probably we could not make constraint feasible due to variable bounds, thus give up */
11729       break;
11730    }
11731 
11732    /* if we have a solution that should satisfy all quadratic constraints and has a better objective than the current upper bound,
11733     * then pass it to the trysol heuristic
11734     */
11735    if( c == nconss && (SCIPisInfinity(scip, SCIPgetUpperbound(scip)) || SCIPisSumLT(scip, SCIPgetSolTransObj(scip, newsol), SCIPgetUpperbound(scip))) )
11736    {
11737       SCIPdebugMsg(scip, "pass solution with objective val %g to trysol heuristic\n", SCIPgetSolTransObj(scip, newsol));
11738       assert(solchanged);
11739 
11740       assert(conshdlrdata->trysolheur != NULL);
11741       SCIP_CALL( SCIPheurPassSolTrySol(scip, conshdlrdata->trysolheur, newsol) );
11742 
11743       *success = TRUE;
11744    }
11745 
11746    SCIP_CALL( SCIPfreeSol(scip, &newsol) );
11747 
11748    return SCIP_OKAY;
11749 }
11750 
11751 /** helper function to enforce constraints */
11752 static
enforceConstraint(SCIP * scip,SCIP_CONSHDLR * conshdlr,SCIP_CONS ** conss,int nconss,int nusefulconss,SCIP_SOL * sol,SCIP_Bool solinfeasible,SCIP_RESULT * result)11753 SCIP_RETCODE enforceConstraint(
11754    SCIP*                 scip,               /**< SCIP data structure */
11755    SCIP_CONSHDLR*        conshdlr,           /**< constraint handler */
11756    SCIP_CONS**           conss,              /**< constraints to process */
11757    int                   nconss,             /**< number of constraints */
11758    int                   nusefulconss,       /**< number of useful (non-obsolete) constraints to process */
11759    SCIP_SOL*             sol,                /**< solution to enforce (NULL for the LP solution) */
11760    SCIP_Bool             solinfeasible,      /**< was the solution already declared infeasible by a constraint handler? */
11761    SCIP_RESULT*          result              /**< pointer to store the result of the enforcing call */
11762    )
11763 {
11764    SCIP_CONSHDLRDATA* conshdlrdata;
11765    SCIP_CONSDATA*     consdata;
11766    SCIP_CONS*         maxviolcon;
11767    SCIP_Real          maxviol;
11768    SCIP_RESULT        propresult;
11769    SCIP_RESULT        separateresult;
11770    int                nchgbds;
11771    int                nnotify;
11772    SCIP_Real          sepaefficacy;
11773    SCIP_Bool          solviolbounds;
11774 
11775    assert(scip != NULL);
11776    assert(conshdlr != NULL);
11777    assert(conss != NULL || nconss == 0);
11778    assert(nconss >= 0);
11779    assert(nusefulconss >= 0);
11780    assert(result != NULL);
11781 
11782    conshdlrdata = SCIPconshdlrGetData(conshdlr);
11783    assert(conshdlrdata != NULL);
11784 
11785    SCIP_CALL( computeViolations(scip, conss, nconss, sol, &solviolbounds, &maxviolcon) );
11786 
11787    if( maxviolcon == NULL )
11788    {
11789       *result = SCIP_FEASIBLE;
11790       return SCIP_OKAY;
11791    }
11792 
11793    *result = SCIP_INFEASIBLE;
11794 
11795    if( solviolbounds )
11796    {
11797       /* if LP solution violates variable bounds, then this should be because a row was added that
11798        * introduced this variable newly to the LP, in which case it gets value 0.0; the row should
11799        * have been added to resolve an infeasibility, so solinfeasible should be TRUE
11800        * see also issue #627
11801        */
11802       assert(solinfeasible);
11803       /* however, if solinfeasible is actually not TRUE, then better cut off the node to avoid that SCIP
11804        * stops because infeasible cannot be resolved */
11805       /*lint --e{774} */
11806       if( !solinfeasible )
11807          *result = SCIP_CUTOFF;
11808       return SCIP_OKAY;
11809    }
11810 
11811    consdata = SCIPconsGetData(maxviolcon);
11812    assert(consdata != NULL);
11813    maxviol = consdata->lhsviol + consdata->rhsviol;
11814    assert(SCIPisGT(scip, maxviol, SCIPfeastol(scip)));
11815 
11816    SCIPdebugMsg(scip, "enforcement with max violation %g in cons <%s> for %s solution\n", maxviol, SCIPconsGetName(maxviolcon),
11817          sol == NULL ? "LP" : "relaxation");
11818 
11819    /* if we are above the 100'th enforcement round for this node, something is strange
11820     * (maybe the LP / relaxator does not think that the cuts we add are violated, or we do ECP on a high-dimensional convex function)
11821     * in this case, check if some limit is hit or SCIP should stop for some other reason and terminate enforcement by creating a dummy node
11822     * (in optimized more, returning SCIP_INFEASIBLE in *result would be sufficient, but in debug mode this would give an assert in scip.c)
11823     * the reason to wait for 100 rounds is to avoid calls to SCIPisStopped in normal runs, which may be expensive
11824     * we only increment nenforounds until 101 to avoid an overflow
11825     */
11826    if( conshdlrdata->lastenfonode == SCIPgetCurrentNode(scip) )
11827    {
11828       if( conshdlrdata->nenforounds > 100 )
11829       {
11830          if( SCIPisStopped(scip) )
11831          {
11832             SCIP_NODE* child;
11833 
11834             SCIP_CALL( SCIPcreateChild(scip, &child, 1.0, SCIPnodeGetEstimate(SCIPgetCurrentNode(scip))) );
11835             *result = SCIP_BRANCHED;
11836 
11837             return SCIP_OKAY;
11838          }
11839       }
11840 
11841       ++conshdlrdata->nenforounds;
11842 
11843       /* cut off the current subtree, if a limit on the enforcement rounds should be applied. At this point, feasible
11844        * solutions might get cut off; the enfolplimit parameter should therefore only be set if SCIP is used as a
11845        * heuristic solver and when the returned result (infeasible, optimal, the gap) can be ignored
11846        */
11847       if( conshdlrdata->enfolplimit != -1 && conshdlrdata->nenforounds > conshdlrdata->enfolplimit )
11848       {
11849          SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL,
11850             "cut off subtree because enforcement limit was reached; this might lead to incorrect results\n");
11851          *result = SCIP_CUTOFF;
11852          return SCIP_OKAY;
11853       }
11854    }
11855    else
11856    {
11857       conshdlrdata->lastenfonode = SCIPgetCurrentNode(scip);
11858       conshdlrdata->nenforounds = 0;
11859    }
11860 
11861    /* run domain propagation */
11862    nchgbds = 0;
11863    SCIP_CALL( propagateBounds(scip, conshdlr, conss, nconss, &propresult, &nchgbds) );
11864    if( propresult == SCIP_CUTOFF || propresult == SCIP_REDUCEDDOM )
11865    {
11866       SCIPdebugMsg(scip, "propagation succeeded (%s)\n", propresult == SCIP_CUTOFF ? "cutoff" : "reduceddom");
11867       *result = propresult;
11868       return SCIP_OKAY;
11869    }
11870 
11871    /* we would like a cut that is efficient enough that it is not redundant in the LP (>lpfeastol)
11872     * however, we also don't want very weak cuts, so try to reach at least feastol (=lpfeastol by default, though)
11873     */
11874    SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, sol, SCIPfeastol(scip), TRUE, &separateresult, &sepaefficacy) );
11875    if( separateresult == SCIP_CUTOFF )
11876    {
11877       SCIPdebugMsg(scip, "separation found cutoff\n");
11878       *result = SCIP_CUTOFF;
11879       return SCIP_OKAY;
11880    }
11881    if( separateresult == SCIP_SEPARATED )
11882    {
11883       SCIPdebugMsg(scip, "separation succeeded (bestefficacy = %g, minefficacy = %g)\n", sepaefficacy, SCIPfeastol(scip));
11884       *result = SCIP_SEPARATED;
11885       return SCIP_OKAY;
11886    }
11887 
11888    /* we are not feasible, the whole node is not infeasible, and we cannot find a good cut
11889     * -> collect variables for branching
11890     */
11891 
11892    SCIPdebugMsg(scip, "separation failed (bestefficacy = %g < %g = minefficacy ); max viol: %g\n", sepaefficacy, SCIPfeastol(scip), maxviol);
11893 
11894    /* find branching candidates */
11895    SCIP_CALL( registerBranchingCandidates(scip, conshdlr, conss, nconss, sol, &nnotify) );
11896 
11897    if( nnotify == 0 && !solinfeasible && SCIPfeastol(scip) > SCIPgetLPFeastol(scip) )
11898    {
11899       /* fallback 1: we also have no branching candidates, so try to find a weak cut */
11900       SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, sol, SCIPgetLPFeastol(scip), TRUE, &separateresult, &sepaefficacy) );
11901       if( separateresult == SCIP_CUTOFF )
11902       {
11903          SCIPdebugMsg(scip, "separation found cutoff\n");
11904          *result = SCIP_CUTOFF;
11905          return SCIP_OKAY;
11906       }
11907       if( separateresult == SCIP_SEPARATED )
11908       {
11909          SCIPdebugMsg(scip, "separation fallback succeeded, efficacy = %g\n", sepaefficacy);
11910          *result = SCIP_SEPARATED;
11911          return SCIP_OKAY;
11912       }
11913    }
11914 
11915    if( nnotify == 0 && !solinfeasible )
11916    {
11917       /* fallback 2: separation probably failed because of numerical difficulties with a convex constraint;
11918        *  if noone declared solution infeasible yet and we had not even found a weak cut, try to resolve by branching
11919        */
11920       SCIP_VAR* brvar = NULL;
11921       SCIP_CALL( registerLargeRelaxValueVariableForBranching(scip, conss, nconss, sol, &brvar) );
11922       if( brvar == NULL )
11923       {
11924          /* fallback 3: all quadratic variables seem to be fixed -> replace by linear constraint */
11925          SCIP_Bool addedcons;
11926          SCIP_Bool reduceddom;
11927          SCIP_Bool infeasible;
11928 
11929          SCIP_CALL( replaceByLinearConstraints(scip, conss, nconss, &addedcons, &reduceddom, &infeasible) );
11930          /* if the linear constraints are actually feasible, then adding them and returning SCIP_CONSADDED confuses SCIP
11931           * when it enforces the new constraints again and nothing resolves the infeasibility that we declare here
11932           * thus, we only add them if considered violated, and otherwise claim the solution is feasible (but print a
11933           * warning) */
11934          if ( infeasible )
11935             *result = SCIP_CUTOFF;
11936          else if ( addedcons )
11937             *result = SCIP_CONSADDED;
11938          else if ( reduceddom )
11939             *result = SCIP_REDUCEDDOM;
11940          else
11941          {
11942             *result = SCIP_FEASIBLE;
11943             SCIPwarningMessage(scip, "could not enforce feasibility by separating or branching; declaring solution with viol %g as feasible\n", maxviol);
11944             assert(!SCIPisInfinity(scip, maxviol));
11945          }
11946          return SCIP_OKAY;
11947       }
11948       else
11949       {
11950          SCIPdebugMsg(scip, "Could not find any usual branching variable candidate. Proposed variable <%s> with LP value %g for branching.\n",
11951             SCIPvarGetName(brvar), SCIPgetSolVal(scip, sol, brvar));
11952          nnotify = 1;
11953       }
11954    }
11955 
11956    assert(*result == SCIP_INFEASIBLE && (solinfeasible || nnotify > 0));
11957    return SCIP_OKAY;
11958 }
11959 
11960 /** tries to upgrade a nonlinear constraint into a quadratic constraint */
11961 static
SCIP_DECL_NONLINCONSUPGD(nonlinconsUpgdQuadratic)11962 SCIP_DECL_NONLINCONSUPGD(nonlinconsUpgdQuadratic)
11963 {
11964    SCIP_EXPRGRAPH* exprgraph;
11965    SCIP_EXPRGRAPHNODE* node;
11966    int i;
11967 
11968    assert(nupgdconss != NULL);
11969    assert(upgdconss != NULL);
11970 
11971    *nupgdconss = 0;
11972 
11973    node = SCIPgetExprgraphNodeNonlinear(scip, cons);
11974 
11975    /* no interest in linear constraints */
11976    if( node == NULL )
11977       return SCIP_OKAY;
11978 
11979    /* if a quadratic expression has been simplified, then all children of the node should be variables */
11980    if( !SCIPexprgraphAreAllNodeChildrenVars(node) )
11981       return SCIP_OKAY;
11982 
11983    switch( SCIPexprgraphGetNodeOperator(node) )
11984    {
11985    case SCIP_EXPR_VARIDX:
11986    case SCIP_EXPR_CONST:
11987    case SCIP_EXPR_PLUS:
11988    case SCIP_EXPR_MINUS:
11989    case SCIP_EXPR_SUM:
11990    case SCIP_EXPR_LINEAR:
11991       /* these should not appear as exprgraphnodes after constraint presolving */
11992       return SCIP_OKAY;
11993 
11994    case SCIP_EXPR_DIV:
11995    case SCIP_EXPR_SQRT:
11996    case SCIP_EXPR_REALPOWER:
11997    case SCIP_EXPR_INTPOWER:
11998    case SCIP_EXPR_SIGNPOWER:
11999    case SCIP_EXPR_EXP:
12000    case SCIP_EXPR_LOG:
12001    case SCIP_EXPR_SIN:
12002    case SCIP_EXPR_COS:
12003    case SCIP_EXPR_TAN:
12004       /* case SCIP_EXPR_ERF: */
12005       /* case SCIP_EXPR_ERFI: */
12006    case SCIP_EXPR_MIN:
12007    case SCIP_EXPR_MAX:
12008    case SCIP_EXPR_ABS:
12009    case SCIP_EXPR_SIGN:
12010    case SCIP_EXPR_PRODUCT:
12011    case SCIP_EXPR_POLYNOMIAL:
12012    case SCIP_EXPR_USER:
12013       /* these do not look like an quadratic expression (assuming the expression graph simplifier did run) */
12014       return SCIP_OKAY;
12015 
12016    case SCIP_EXPR_MUL:
12017    case SCIP_EXPR_SQUARE:
12018    case SCIP_EXPR_QUADRATIC:
12019       /* these mean that we have something quadratic */
12020       break;
12021 
12022    case SCIP_EXPR_PARAM:
12023    case SCIP_EXPR_LAST:
12024    default:
12025       SCIPwarningMessage(scip, "unexpected expression operator %d in nonlinear constraint <%s>\n", SCIPexprgraphGetNodeOperator(node), SCIPconsGetName(cons));
12026       return SCIP_OKAY;
12027    }
12028 
12029    /* setup a quadratic constraint */
12030 
12031    if( upgdconsssize < 1 )
12032    {
12033       /* request larger upgdconss array */
12034       *nupgdconss = -1;
12035       return SCIP_OKAY;
12036    }
12037 
12038    *nupgdconss = 1;
12039    SCIP_CALL( SCIPcreateConsQuadratic(scip, &upgdconss[0], SCIPconsGetName(cons),
12040          SCIPgetNLinearVarsNonlinear(scip, cons), SCIPgetLinearVarsNonlinear(scip, cons), SCIPgetLinearCoefsNonlinear(scip, cons),
12041          0, NULL, 0, NULL,
12042          SCIPgetLhsNonlinear(scip, cons), SCIPgetRhsNonlinear(scip, cons),
12043          SCIPconsIsInitial(cons), SCIPconsIsSeparated(cons), SCIPconsIsEnforced(cons),
12044          SCIPconsIsChecked(cons), SCIPconsIsPropagated(cons), SCIPconsIsLocal(cons),
12045          SCIPconsIsModifiable(cons), SCIPconsIsDynamic(cons), SCIPconsIsRemovable(cons)) );
12046    assert(!SCIPconsIsStickingAtNode(cons));
12047 
12048    exprgraph = SCIPgetExprgraphNonlinear(scip, SCIPconsGetHdlr(cons));
12049 
12050    /* add variables from expression tree as "quadratic" variables to quadratic constraint */
12051    for( i = 0; i < SCIPexprgraphGetNodeNChildren(node); ++i )
12052    {
12053       assert(SCIPexprgraphGetNodeChildren(node)[i] != NULL);
12054       SCIP_CALL( SCIPaddQuadVarQuadratic(scip, upgdconss[0], (SCIP_VAR*)SCIPexprgraphGetNodeVar(exprgraph, SCIPexprgraphGetNodeChildren(node)[i]), 0.0, 0.0) );
12055    }
12056 
12057    switch( SCIPexprgraphGetNodeOperator(node) )
12058    {
12059    case SCIP_EXPR_MUL:
12060       /* expression is product of two variables, so add bilinear term to constraint */
12061       assert(SCIPexprgraphGetNodeNChildren(node) == 2);
12062 
12063       SCIP_CALL( SCIPaddBilinTermQuadratic(scip, upgdconss[0],
12064             (SCIP_VAR*)SCIPexprgraphGetNodeVar(exprgraph, SCIPexprgraphGetNodeChildren(node)[0]),
12065             (SCIP_VAR*)SCIPexprgraphGetNodeVar(exprgraph, SCIPexprgraphGetNodeChildren(node)[1]),
12066             1.0) );
12067 
12068       break;
12069 
12070    case SCIP_EXPR_SQUARE:
12071       /* expression is square of a variable, so change square coefficient of quadratic variable */
12072       assert(SCIPexprgraphGetNodeNChildren(node) == 1);
12073 
12074       SCIP_CALL( SCIPaddSquareCoefQuadratic(scip, upgdconss[0],
12075             (SCIP_VAR*)SCIPexprgraphGetNodeVar(exprgraph, SCIPexprgraphGetNodeChildren(node)[0]),
12076             1.0) );
12077 
12078       break;
12079 
12080    case SCIP_EXPR_QUADRATIC:
12081    {
12082       /* expression is quadratic */
12083       SCIP_QUADELEM* quadelems;
12084       int nquadelems;
12085       SCIP_Real* lincoefs;
12086 
12087       quadelems  = SCIPexprgraphGetNodeQuadraticQuadElements(node);
12088       nquadelems = SCIPexprgraphGetNodeQuadraticNQuadElements(node);
12089       lincoefs   = SCIPexprgraphGetNodeQuadraticLinearCoefs(node);
12090 
12091       SCIPaddConstantQuadratic(scip, upgdconss[0], SCIPexprgraphGetNodeQuadraticConstant(node));
12092 
12093       if( lincoefs != NULL )
12094          for( i = 0; i < SCIPexprgraphGetNodeNChildren(node); ++i )
12095             if( lincoefs[i] != 0.0 )
12096             {
12097                /* linear term */
12098                SCIP_CALL( SCIPaddQuadVarLinearCoefQuadratic(scip, upgdconss[0],
12099                      (SCIP_VAR*)SCIPexprgraphGetNodeVar(exprgraph, SCIPexprgraphGetNodeChildren(node)[i]),
12100                      lincoefs[i]) );
12101             }
12102 
12103       for( i = 0; i < nquadelems; ++i )
12104       {
12105          assert(quadelems[i].idx1 < SCIPexprgraphGetNodeNChildren(node));
12106          assert(quadelems[i].idx2 < SCIPexprgraphGetNodeNChildren(node));
12107 
12108          if( quadelems[i].idx1 == quadelems[i].idx2 )
12109          {
12110             /* square term */
12111             SCIP_CALL( SCIPaddSquareCoefQuadratic(scip, upgdconss[0],
12112                   (SCIP_VAR*)SCIPexprgraphGetNodeVar(exprgraph, SCIPexprgraphGetNodeChildren(node)[quadelems[i].idx1]),
12113                   quadelems[i].coef) );
12114          }
12115          else
12116          {
12117             /* bilinear term */
12118             SCIP_CALL( SCIPaddBilinTermQuadratic(scip, upgdconss[0],
12119                   (SCIP_VAR*)SCIPexprgraphGetNodeVar(exprgraph, SCIPexprgraphGetNodeChildren(node)[quadelems[i].idx1]),
12120                   (SCIP_VAR*)SCIPexprgraphGetNodeVar(exprgraph, SCIPexprgraphGetNodeChildren(node)[quadelems[i].idx2]),
12121                   quadelems[i].coef) );
12122          }
12123       }
12124 
12125       break;
12126    }
12127 
12128    default:
12129       SCIPerrorMessage("you should not be here\n");
12130       return SCIP_ERROR;
12131    }  /*lint !e788 */
12132 
12133    return SCIP_OKAY;
12134 }
12135 
12136 /*
12137  * Callback methods of constraint handler
12138  */
12139 
12140 /** copy method for constraint handler plugins (called when SCIP copies plugins) */
12141 static
SCIP_DECL_CONSHDLRCOPY(conshdlrCopyQuadratic)12142 SCIP_DECL_CONSHDLRCOPY(conshdlrCopyQuadratic)
12143 {  /*lint --e{715}*/
12144    assert(scip != NULL);
12145    assert(conshdlr != NULL);
12146    assert(strcmp(SCIPconshdlrGetName(conshdlr), CONSHDLR_NAME) == 0);
12147 
12148    /* call inclusion method of constraint handler */
12149    SCIP_CALL( SCIPincludeConshdlrQuadratic(scip) );
12150 
12151    *valid = TRUE;
12152 
12153    return SCIP_OKAY;
12154 }
12155 
12156 /** destructor of constraint handler to free constraint handler data (called when SCIP is exiting) */
12157 static
SCIP_DECL_CONSFREE(consFreeQuadratic)12158 SCIP_DECL_CONSFREE(consFreeQuadratic)
12159 {
12160    SCIP_CONSHDLRDATA* conshdlrdata;
12161    int                i;
12162 
12163    assert(scip     != NULL);
12164    assert(conshdlr != NULL);
12165 
12166    conshdlrdata = SCIPconshdlrGetData(conshdlr);
12167    assert(conshdlrdata != NULL);
12168 
12169    for( i = 0; i < conshdlrdata->nquadconsupgrades; ++i )
12170    {
12171       assert(conshdlrdata->quadconsupgrades[i] != NULL);
12172       SCIPfreeBlockMemory(scip, &conshdlrdata->quadconsupgrades[i]); /*lint !e866*/
12173    }
12174    SCIPfreeBlockMemoryArrayNull(scip, &conshdlrdata->quadconsupgrades, conshdlrdata->quadconsupgradessize);
12175    SCIPfreeBlockMemory(scip, &conshdlrdata);
12176 
12177    return SCIP_OKAY;
12178 }
12179 
12180 /** initialization method of constraint handler (called after problem was transformed) */
12181 static
SCIP_DECL_CONSINIT(consInitQuadratic)12182 SCIP_DECL_CONSINIT(consInitQuadratic)
12183 {  /*lint --e{715} */
12184    SCIP_CONSHDLRDATA* conshdlrdata;
12185 
12186    assert(scip != NULL);
12187    assert(conshdlr != NULL);
12188 
12189    conshdlrdata = SCIPconshdlrGetData(conshdlr);
12190    assert(conshdlrdata != NULL);
12191 
12192    conshdlrdata->subnlpheur = SCIPfindHeur(scip, "subnlp");
12193    conshdlrdata->trysolheur = SCIPfindHeur(scip, "trysol");
12194 
12195    return SCIP_OKAY;
12196 }
12197 
12198 
12199 /** deinitialization method of constraint handler (called before transformed problem is freed) */
12200 static
SCIP_DECL_CONSEXIT(consExitQuadratic)12201 SCIP_DECL_CONSEXIT(consExitQuadratic)
12202 {  /*lint --e{715} */
12203    SCIP_CONSHDLRDATA* conshdlrdata;
12204 
12205    assert(scip != NULL);
12206    assert(conshdlr != NULL);
12207 
12208    conshdlrdata = SCIPconshdlrGetData(conshdlr);
12209    assert(conshdlrdata != NULL);
12210 
12211    conshdlrdata->subnlpheur = NULL;
12212    conshdlrdata->trysolheur = NULL;
12213 
12214    return SCIP_OKAY;
12215 }
12216 
12217 /** presolving deinitialization method of constraint handler (called after presolving has been finished) */
12218 static
SCIP_DECL_CONSEXITPRE(consExitpreQuadratic)12219 SCIP_DECL_CONSEXITPRE(consExitpreQuadratic)
12220 {  /*lint --e{715}*/
12221    SCIP_CONSHDLRDATA* conshdlrdata;
12222    SCIP_CONSDATA*     consdata;
12223    int                c;
12224 #ifndef NDEBUG
12225    int                i;
12226 #endif
12227 
12228    assert(scip != NULL);
12229    assert(conshdlr != NULL);
12230    assert(conss != NULL || nconss == 0);
12231 
12232    conshdlrdata = SCIPconshdlrGetData(conshdlr);
12233    assert(conshdlrdata != NULL);
12234 
12235    for( c = 0; c < nconss; ++c )
12236    {
12237       assert(conss != NULL);
12238       consdata = SCIPconsGetData(conss[c]);
12239       assert(consdata != NULL);
12240 
12241       if( !consdata->isremovedfixings )
12242       {
12243          SCIP_CALL( removeFixedVariables(scip, conshdlrdata->eventhdlr, conss[c]) );
12244       }
12245 
12246       /* make sure we do not have duplicate bilinear terms, quad var terms, or linear vars */
12247       SCIP_CALL( mergeAndCleanBilinearTerms(scip, conss[c]) );
12248       SCIP_CALL( mergeAndCleanQuadVarTerms(scip, conss[c]) );
12249       SCIP_CALL( mergeAndCleanLinearVars(scip, conss[c]) );
12250 
12251       assert(consdata->isremovedfixings);
12252       assert(consdata->linvarsmerged);
12253       assert(consdata->quadvarsmerged);
12254       assert(consdata->bilinmerged);
12255 
12256 #ifndef NDEBUG
12257       for( i = 0; i < consdata->nlinvars; ++i )
12258          assert(SCIPvarIsActive(consdata->linvars[i]));
12259 
12260       for( i = 0; i < consdata->nquadvars; ++i )
12261          assert(SCIPvarIsActive(consdata->quadvarterms[i].var));
12262 #endif
12263 
12264       /* tell SCIP that we have something nonlinear */
12265       if( SCIPconsIsAdded(conss[c]) && consdata->nquadvars > 0 )
12266          SCIPenableNLP(scip);
12267    }
12268 
12269    return SCIP_OKAY;
12270 }
12271 
12272 /** solving process initialization method of constraint handler (called when branch and bound process is about to begin)
12273  *
12274  *  @note Also called from consEnableQuadratic during solving stage.
12275  */
12276 static
SCIP_DECL_CONSINITSOL(consInitsolQuadratic)12277 SCIP_DECL_CONSINITSOL(consInitsolQuadratic)
12278 {
12279    SCIP_CONSHDLRDATA* conshdlrdata;
12280    SCIP_CONSDATA*     consdata;
12281    int                c;
12282    int                i;
12283 
12284    assert(scip     != NULL);
12285    assert(conshdlr != NULL);
12286    assert(conss    != NULL || nconss == 0);
12287 
12288    conshdlrdata = SCIPconshdlrGetData(conshdlr);
12289    assert(conshdlrdata != NULL);
12290 
12291    for( c = 0; c < nconss; ++c )
12292    {
12293       assert(conss != NULL);
12294       consdata = SCIPconsGetData(conss[c]);
12295       assert(consdata != NULL);
12296 
12297       /* check for a linear variable that can be increase or decreased without harming feasibility */
12298       consdataFindUnlockedLinearVar(scip, consdata);
12299 
12300       /* setup lincoefsmin, lincoefsmax */
12301       consdata->lincoefsmin = SCIPinfinity(scip);
12302       consdata->lincoefsmax = 0.0;
12303       for( i = 0; i < consdata->nlinvars; ++i )
12304       {
12305          consdata->lincoefsmin = MIN(consdata->lincoefsmin, REALABS(consdata->lincoefs[i]));  /*lint !e666 */
12306          consdata->lincoefsmax = MAX(consdata->lincoefsmax, REALABS(consdata->lincoefs[i]));  /*lint !e666 */
12307       }
12308 
12309       /* add nlrow representation to NLP, if NLP had been constructed */
12310       if( SCIPisNLPConstructed(scip) && SCIPconsIsEnabled(conss[c]) )
12311       {
12312          if( consdata->nlrow == NULL )
12313          {
12314             /* compute curvature for the quadratic constraint if not done yet */
12315             SCIP_CALL( checkCurvature(scip, conss[c], conshdlrdata->checkcurvature) );
12316 
12317             SCIP_CALL( createNlRow(scip, conss[c]) );
12318             assert(consdata->nlrow != NULL);
12319          }
12320          if( !SCIPnlrowIsInNLP(consdata->nlrow) )
12321          {
12322             SCIP_CALL( SCIPaddNlRow(scip, consdata->nlrow) );
12323          }
12324       }
12325 
12326       /* setup sepaquadvars and sepabilinvar2pos */
12327       assert(consdata->sepaquadvars == NULL);
12328       assert(consdata->sepabilinvar2pos == NULL);
12329       if( consdata->nquadvars > 0 )
12330       {
12331          SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->sepaquadvars,     consdata->nquadvars) );
12332          SCIP_CALL( SCIPallocBlockMemoryArray(scip, &consdata->sepabilinvar2pos, consdata->nbilinterms) );
12333 
12334          /* make sure, quadratic variable terms are sorted */
12335          SCIP_CALL( consdataSortQuadVarTerms(scip, consdata) );
12336 
12337          for( i = 0; i < consdata->nquadvars; ++i )
12338             consdata->sepaquadvars[i] = consdata->quadvarterms[i].var;
12339 
12340          for( i = 0; i < consdata->nbilinterms; ++i )
12341          {
12342             SCIP_CALL( consdataFindQuadVarTerm(scip, consdata, consdata->bilinterms[i].var2, &consdata->sepabilinvar2pos[i]) );
12343          }
12344       }
12345 
12346       if( conshdlrdata->checkfactorable )
12347       {
12348          /* check if constraint function is factorable, i.e., can be written as product of two linear functions */
12349          SCIP_CALL( checkFactorable(scip, conss[c]) );
12350       }
12351 
12352       /* compute gauge function using interior points per constraint, only when there are quadratic variables */
12353       if( conshdlrdata->gaugecuts && SCIPgetSubscipDepth(scip) == 0 && consdata->nquadvars > 0 )
12354       {
12355          SCIP_CALL( checkCurvature(scip, conss[c], conshdlrdata->checkcurvature) );  /*lint !e613 */
12356          if( (consdata->isconvex && !SCIPisInfinity(scip, consdata->rhs)) ||
12357                (consdata->isconcave && !SCIPisInfinity(scip, -consdata->lhs)) )
12358          {
12359             SCIP_CALL( computeGauge(scip, conshdlr, conss[c]) );
12360          }
12361       }
12362 
12363       /* compute eigendecomposition for convex quadratics */
12364       if( conshdlrdata->projectedcuts && SCIPgetSubscipDepth(scip) == 0 && consdata->nquadvars > 0 )
12365       {
12366          SCIP_CALL( checkCurvature(scip, conss[c], conshdlrdata->checkcurvature) );  /*lint !e613 */
12367          if( (consdata->isconvex && !SCIPisInfinity(scip, consdata->rhs)) ||
12368                (consdata->isconcave && !SCIPisInfinity(scip, -consdata->lhs)) )
12369          {
12370             SCIP_CALL( computeED(scip, conshdlr, conss[c]) );
12371          }
12372       }
12373 
12374       /* mark constraint for propagation */
12375       SCIP_CALL( SCIPmarkConsPropagate(scip, conss[c]) );
12376       consdata->ispropagated = FALSE;
12377    }
12378 
12379    if( SCIPgetStage(scip) != SCIP_STAGE_INITSOLVE )
12380    {
12381       /* if called from consEnableQuadratic, then don't do below */
12382       return SCIP_OKAY;
12383    }
12384 
12385    conshdlrdata->newsoleventfilterpos = -1;
12386    if( nconss != 0 && conshdlrdata->linearizeheursol )
12387    {
12388       SCIP_EVENTHDLR* eventhdlr;
12389 
12390       eventhdlr = SCIPfindEventhdlr(scip, CONSHDLR_NAME"_newsolution");
12391       assert(eventhdlr != NULL);
12392 
12393       /* @todo Should we catch every new solution or only new *best* solutions */
12394       SCIP_CALL( SCIPcatchEvent(scip, SCIP_EVENTTYPE_SOLFOUND, eventhdlr, (SCIP_EVENTDATA*)conshdlr, &conshdlrdata->newsoleventfilterpos) );
12395    }
12396 
12397    if( nconss != 0 && !SCIPisIpoptAvailableIpopt() && !SCIPisInRestart(scip) )
12398    {
12399       SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Quadratic constraint handler does not have LAPACK for eigenvalue computation. Will assume that matrices (with size > 2x2) are indefinite.\n");
12400    }
12401 
12402    /* reset flags and counters */
12403    conshdlrdata->sepanlp = FALSE;
12404    conshdlrdata->lastenfonode = NULL;
12405    conshdlrdata->nenforounds = 0;
12406 
12407    return SCIP_OKAY;
12408 }
12409 
12410 /** solving process deinitialization method of constraint handler (called before branch and bound process data is freed)
12411  *
12412  *  @note Also called from consDisableQuadratic during solving stage.
12413  */
12414 static
SCIP_DECL_CONSEXITSOL(consExitsolQuadratic)12415 SCIP_DECL_CONSEXITSOL(consExitsolQuadratic)
12416 {  /*lint --e{715}*/
12417    SCIP_CONSHDLRDATA* conshdlrdata;
12418    SCIP_CONSDATA* consdata;
12419    int c;
12420 
12421    assert(scip != NULL);
12422    assert(conshdlr != NULL);
12423    assert(conss != NULL || nconss == 0);
12424 
12425    conshdlrdata = SCIPconshdlrGetData(conshdlr);
12426    assert(conshdlrdata != NULL);
12427 
12428    for( c = 0; c < nconss; ++c )
12429    {
12430       consdata = SCIPconsGetData(conss[c]);  /*lint !e613*/
12431       assert(consdata != NULL);
12432 
12433       /* free nonlinear row representation, if not called from consDisableQuadratic */
12434       if( consdata->nlrow != NULL && SCIPgetStage(scip) == SCIP_STAGE_EXITSOLVE )
12435       {
12436          SCIP_CALL( SCIPreleaseNlRow(scip, &consdata->nlrow) );
12437       }
12438 
12439       assert(!SCIPconsIsEnabled(conss[c]) || consdata->sepaquadvars     != NULL || consdata->nquadvars == 0);   /*lint !e613 */
12440       assert(!SCIPconsIsEnabled(conss[c]) || consdata->sepabilinvar2pos != NULL || consdata->nquadvars == 0);   /*lint !e613 */
12441       SCIPfreeBlockMemoryArrayNull(scip, &consdata->sepaquadvars,     consdata->nquadvars);
12442       SCIPfreeBlockMemoryArrayNull(scip, &consdata->sepabilinvar2pos, consdata->nbilinterms);
12443 
12444       SCIPfreeBlockMemoryArrayNull(scip, &consdata->factorleft,  consdata->nquadvars + 1);
12445       SCIPfreeBlockMemoryArrayNull(scip, &consdata->factorright, consdata->nquadvars + 1);
12446 
12447       SCIPfreeBlockMemoryArrayNull(scip, &consdata->interiorpoint, consdata->nquadvars);
12448       SCIPfreeBlockMemoryArrayNull(scip, &consdata->gaugecoefs, consdata->nquadvars);
12449       SCIPfreeBlockMemoryArrayNull(scip, &consdata->eigenvalues, consdata->nquadvars);
12450       SCIPfreeBlockMemoryArrayNull(scip, &consdata->eigenvectors, (int)(consdata->nquadvars*consdata->nquadvars));
12451       SCIPfreeBlockMemoryArrayNull(scip, &consdata->bp, consdata->nquadvars);
12452    }
12453 
12454    if( SCIPgetStage(scip) != SCIP_STAGE_EXITSOLVE )
12455    {
12456       /* if called from consDisableQuadratic, then don't do below */
12457       return SCIP_OKAY;
12458    }
12459 
12460    if( conshdlrdata->newsoleventfilterpos >= 0 )
12461    {
12462       SCIP_EVENTHDLR* eventhdlr;
12463 
12464       eventhdlr = SCIPfindEventhdlr(scip, CONSHDLR_NAME"_newsolution");
12465       assert(eventhdlr != NULL);
12466 
12467       SCIP_CALL( SCIPdropEvent(scip, SCIP_EVENTTYPE_SOLFOUND, eventhdlr, (SCIP_EVENTDATA*)conshdlr, conshdlrdata->newsoleventfilterpos) );
12468       conshdlrdata->newsoleventfilterpos = -1;
12469    }
12470 
12471    /* free all stored bilinear terms in the constraint handler and constraint data; note that we might not want to
12472     * recollect all bilinear terms and therefore keep them even if consDisableQuadratic is called
12473     */
12474    SCIP_CALL( freeAllBilinearTerms(scip, conshdlrdata, conss, nconss) );
12475 
12476    return SCIP_OKAY;
12477 }
12478 
12479 /** frees specific constraint data */
12480 static
SCIP_DECL_CONSDELETE(consDeleteQuadratic)12481 SCIP_DECL_CONSDELETE(consDeleteQuadratic)
12482 {
12483    assert(scip != NULL);
12484    assert(conshdlr != NULL);
12485    assert(cons != NULL);
12486    assert(consdata != NULL);
12487    assert(SCIPconsGetData(cons) == *consdata);
12488 
12489    SCIP_CALL( consdataFree(scip, consdata) );
12490 
12491    assert(*consdata == NULL);
12492 
12493    return SCIP_OKAY;
12494 }
12495 
12496 /** transforms constraint data into data belonging to the transformed problem */
12497 static
SCIP_DECL_CONSTRANS(consTransQuadratic)12498 SCIP_DECL_CONSTRANS(consTransQuadratic)
12499 {
12500    SCIP_CONSDATA* sourcedata;
12501    SCIP_CONSDATA* targetdata;
12502    int            i;
12503 
12504    sourcedata = SCIPconsGetData(sourcecons);
12505    assert(sourcedata != NULL);
12506 
12507    SCIP_CALL( consdataCreate(scip, &targetdata,
12508          sourcedata->lhs, sourcedata->rhs,
12509          sourcedata->nlinvars, sourcedata->linvars, sourcedata->lincoefs,
12510          sourcedata->nquadvars, sourcedata->quadvarterms,
12511          sourcedata->nbilinterms, sourcedata->bilinterms,
12512          FALSE) );
12513 
12514    for( i = 0; i < targetdata->nlinvars; ++i )
12515    {
12516       SCIP_CALL( SCIPgetTransformedVar(scip, targetdata->linvars[i], &targetdata->linvars[i]) );
12517       SCIP_CALL( SCIPcaptureVar(scip, targetdata->linvars[i]) );
12518    }
12519 
12520    for( i = 0; i < targetdata->nquadvars; ++i )
12521    {
12522       SCIP_CALL( SCIPgetTransformedVar(scip, targetdata->quadvarterms[i].var, &targetdata->quadvarterms[i].var) );
12523       SCIP_CALL( SCIPcaptureVar(scip, targetdata->quadvarterms[i].var) );
12524    }
12525 
12526    for( i = 0; i < targetdata->nbilinterms; ++i )
12527    {
12528       SCIP_CALL( SCIPgetTransformedVar(scip, targetdata->bilinterms[i].var1, &targetdata->bilinterms[i].var1) );
12529       SCIP_CALL( SCIPgetTransformedVar(scip, targetdata->bilinterms[i].var2, &targetdata->bilinterms[i].var2) );
12530 
12531       if( SCIPvarCompare(targetdata->bilinterms[i].var1, targetdata->bilinterms[i].var2) > 0 )
12532       {
12533          SCIP_VAR* tmp;
12534          tmp = targetdata->bilinterms[i].var2;
12535          targetdata->bilinterms[i].var2 = targetdata->bilinterms[i].var1;
12536          targetdata->bilinterms[i].var1 = tmp;
12537       }
12538    }
12539 
12540    /* create target constraint */
12541    SCIP_CALL( SCIPcreateCons(scip, targetcons, SCIPconsGetName(sourcecons), conshdlr, targetdata,
12542          SCIPconsIsInitial(sourcecons), SCIPconsIsSeparated(sourcecons), SCIPconsIsEnforced(sourcecons),
12543          SCIPconsIsChecked(sourcecons), SCIPconsIsPropagated(sourcecons),  SCIPconsIsLocal(sourcecons),
12544          SCIPconsIsModifiable(sourcecons), SCIPconsIsDynamic(sourcecons), SCIPconsIsRemovable(sourcecons),
12545          SCIPconsIsStickingAtNode(sourcecons)) );
12546 
12547    SCIPdebugMsg(scip, "created transformed quadratic constraint ");
12548    SCIPdebugPrintCons(scip, *targetcons, NULL);
12549 
12550    return SCIP_OKAY;
12551 }
12552 
12553 /** LP initialization method of constraint handler (called before the initial LP relaxation at a node is solved) */
12554 static
SCIP_DECL_CONSINITLP(consInitlpQuadratic)12555 SCIP_DECL_CONSINITLP(consInitlpQuadratic)
12556 {
12557    SCIP_CONSHDLRDATA* conshdlrdata;
12558    SCIP_CONSDATA*     consdata;
12559    SCIP_VAR*          var;
12560    SCIP_ROW*          row;
12561    SCIP_Real*         x;
12562    int                c;
12563    int                i;
12564 
12565    assert(scip != NULL);
12566    assert(conshdlr != NULL);
12567    assert(conss != NULL || nconss == 0);
12568 
12569    conshdlrdata = SCIPconshdlrGetData(conshdlr);
12570    assert(conshdlrdata != NULL);
12571 
12572    *infeasible = FALSE;
12573 
12574    for( c = 0; c < nconss && !(*infeasible); ++c )
12575    {
12576       assert(conss[c] != NULL);  /*lint !e613 */
12577 
12578       if( !SCIPconsIsEnabled(conss[c]) )  /*lint !e613 */
12579          continue;
12580 
12581       SCIP_CALL( checkCurvature(scip, conss[c], conshdlrdata->checkcurvature) );  /*lint !e613 */
12582 
12583       consdata = SCIPconsGetData(conss[c]);  /*lint !e613 */
12584       assert(consdata != NULL);
12585 
12586       row = NULL;
12587 
12588       if( consdata->nquadvars == 0 )
12589       {
12590          /* if we are actually linear, add the constraint as row to the LP */
12591          SCIP_CALL( SCIPcreateEmptyRowCons(scip, &row, conss[c], SCIPconsGetName(conss[c]), consdata->lhs, consdata->rhs,
12592                SCIPconsIsLocal(conss[c]), FALSE , TRUE) );  /*lint !e613 */
12593          SCIP_CALL( SCIPaddVarsToRow(scip, row, consdata->nlinvars, consdata->linvars, consdata->lincoefs) );
12594          SCIP_CALL( SCIPaddRow(scip, row, FALSE, infeasible) );
12595          SCIP_CALL( SCIPreleaseRow (scip, &row) );
12596          continue;
12597       }
12598 
12599       /* alloc memory for reference point */
12600       SCIP_CALL( SCIPallocBufferArray(scip, &x, consdata->nquadvars) );
12601 
12602       /* for convex parts, add linearizations in 5 points */
12603       if( (consdata->isconvex && !SCIPisInfinity(scip,  consdata->rhs)) ||
12604          (consdata->isconcave && !SCIPisInfinity(scip, -consdata->lhs)) )
12605       {
12606          SCIP_Real lb;
12607          SCIP_Real ub;
12608          SCIP_Real lambda;
12609          int k;
12610 
12611          for( k = 0; k < 5; ++k )
12612          {
12613             lambda = 0.1 * (k+1); /* lambda = 0.1, 0.2, 0.3, 0.4, 0.5 */
12614             for( i = 0; i < consdata->nquadvars; ++i )
12615             {
12616                var = consdata->quadvarterms[i].var;
12617                lb = SCIPvarGetLbGlobal(var);
12618                ub = SCIPvarGetUbGlobal(var);
12619 
12620                if( ub > -INITLPMAXVARVAL )
12621                   lb = MAX(lb, -INITLPMAXVARVAL);
12622                if( lb <  INITLPMAXVARVAL )
12623                   ub = MIN(ub,  INITLPMAXVARVAL);
12624 
12625                /* make bounds finite */
12626                if( SCIPisInfinity(scip, -lb) )
12627                   lb = MIN(-10.0, ub - 0.1*REALABS(ub));  /*lint !e666 */
12628                if( SCIPisInfinity(scip,  ub) )
12629                   ub = MAX( 10.0, lb + 0.1*REALABS(lb));  /*lint !e666 */
12630 
12631                if( SCIPvarGetBestBoundType(var) == SCIP_BOUNDTYPE_LOWER )
12632                   x[i] = lambda * ub + (1.0 - lambda) * lb;
12633                else
12634                   x[i] = lambda * lb + (1.0 - lambda) * ub;
12635             }
12636 
12637             SCIP_CALL( generateCut(scip, conshdlr, conss[c], x, NULL, consdata->isconvex ? SCIP_SIDETYPE_RIGHT : SCIP_SIDETYPE_LEFT, &row, NULL,
12638                   FALSE, -SCIPinfinity(scip)) );  /*lint !e613 */
12639             if( row != NULL )
12640             {
12641                SCIPdebugMsg(scip, "initlp adds row <%s> for lambda = %g of conss <%s>\n", SCIProwGetName(row), lambda, SCIPconsGetName(conss[c]));  /*lint !e613 */
12642                SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) );
12643 
12644                SCIP_CALL( SCIPaddRow(scip, row, FALSE, infeasible) );
12645                SCIP_CALL( SCIPreleaseRow (scip, &row) );
12646             }
12647          }
12648       }
12649 
12650       /* for concave parts, add underestimator w.r.t. at most 2 reference points */
12651       if( !(*infeasible) && ((! consdata->isconvex && !SCIPisInfinity(scip,  consdata->rhs))
12652             || (! consdata->isconcave && !SCIPisInfinity(scip, -consdata->lhs))) )
12653       {
12654          SCIP_Bool unbounded;
12655          SCIP_Bool possquare;
12656          SCIP_Bool negsquare;
12657          SCIP_Real lb;
12658          SCIP_Real ub;
12659          SCIP_Real lambda;
12660          int k;
12661 
12662          unbounded = FALSE; /* whether there are unbounded variables */
12663          possquare = FALSE; /* whether there is a positive square term */
12664          negsquare = FALSE; /* whether there is a negative square term */
12665          for( k = 0; k < 2; ++k )
12666          {
12667             /* Set reference point to 0 projected on bounds for unbounded variables or in between lower and upper bound
12668              * for bounded variables in the first round, we set it closer to the best bound for one part of the
12669              * variables, in the second closer to the best bound for the other part of the variables.
12670              * Additionally, we use slightly different weights for each variable.
12671              * The reason for the latter is, that for a bilinear term with bounded variables, there are always two linear underestimators
12672              * if the same weight is used for both variables of a product, then rounding and luck decides which underestimator is chosen
12673              * of course, the possible number of cuts is something in the order of 2^nquadvars, and we choose two of them here.
12674              */
12675             for( i = 0; i < consdata->nquadvars; ++i )
12676             {
12677                var = consdata->quadvarterms[i].var;
12678                lb = SCIPvarGetLbGlobal(var);
12679                ub = SCIPvarGetUbGlobal(var);
12680 
12681                if( SCIPisInfinity(scip, -lb) )
12682                {
12683                   if( SCIPisInfinity(scip, ub) )
12684                      x[i] = 0.0;
12685                   else
12686                      x[i] = MIN(0.0, ub);
12687                   unbounded = TRUE;
12688                }
12689                else
12690                {
12691                   if( SCIPisInfinity(scip, ub) )
12692                   {
12693                      x[i] = MAX(0.0, lb);
12694                      unbounded = TRUE;
12695                   }
12696                   else
12697                   {
12698                      lambda = 0.4 + 0.2 * ((i+k)%2) + 0.01 * i / (double)consdata->nquadvars;
12699                      x[i] = lambda * SCIPvarGetBestBoundLocal(var) + (1.0-lambda) * SCIPvarGetWorstBoundLocal(var);
12700                   }
12701                }
12702 
12703                possquare |= consdata->quadvarterms[i].sqrcoef > 0.0;  /*lint !e514 */
12704                negsquare |= consdata->quadvarterms[i].sqrcoef < 0.0;  /*lint !e514 */
12705             }
12706 
12707             if( !consdata->isconvex  && !SCIPisInfinity(scip,  consdata->rhs) )
12708             {
12709                SCIP_CALL( generateCut(scip, conshdlr, conss[c], x, NULL, SCIP_SIDETYPE_RIGHT, &row, NULL,
12710                      conshdlrdata->checkcurvature, -SCIPinfinity(scip)) );  /*lint !e613 */
12711                if( row != NULL )
12712                {
12713                   SCIPdebugMsg(scip, "initlp adds row <%s> for rhs of conss <%s>, round %d\n", SCIProwGetName(row), SCIPconsGetName(conss[c]), k);  /*lint !e613 */
12714                   SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) );
12715 
12716                   SCIP_CALL( SCIPaddRow(scip, row, FALSE, infeasible) );
12717                   SCIP_CALL( SCIPreleaseRow (scip, &row) );
12718                }
12719             }
12720             if( !(*infeasible) && !consdata->isconcave && !SCIPisInfinity(scip, -consdata->lhs) )
12721             {
12722                SCIP_CALL( generateCut(scip, conshdlr, conss[c], x, NULL, SCIP_SIDETYPE_LEFT, &row, NULL,
12723                      conshdlrdata->checkcurvature, -SCIPinfinity(scip)) );  /*lint !e613 */
12724                if( row != NULL )
12725                {
12726                   SCIPdebugMsg(scip, "initlp adds row <%s> for lhs of conss <%s>, round %d\n", SCIProwGetName(row), SCIPconsGetName(conss[c]), k);  /*lint !e613 */
12727                   SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) );
12728 
12729                   SCIP_CALL( SCIPaddRow(scip, row, FALSE, infeasible) );
12730                   SCIP_CALL( SCIPreleaseRow (scip, &row) );
12731                }
12732             }
12733 
12734             /* if there are unbounded variables, then there is typically only at most one possible underestimator, so don't try another round
12735              * similar, if there are no bilinear terms and no linearizations of square terms, then the reference point does not matter, so don't do another round */
12736             if( unbounded ||
12737                (consdata->nbilinterms == 0 && (!possquare || SCIPisInfinity(scip,  consdata->rhs))) ||
12738                (consdata->nbilinterms == 0 && (!negsquare || SCIPisInfinity(scip, -consdata->lhs))) )
12739                break;
12740          }
12741       }
12742 
12743       SCIPfreeBufferArray(scip, &x);
12744    }
12745 
12746    /* store all bilinear terms into constraint handler data; this code is not in initsolve because the sub-NLP
12747     * heuristic triggers this callback and should not collect all bilinear terms
12748     */
12749    SCIP_CALL( storeAllBilinearTerms(scip, conshdlrdata, conss, nconss) );
12750 
12751    return SCIP_OKAY;
12752 }
12753 
12754 /** separation method of constraint handler for LP solutions */
12755 static
SCIP_DECL_CONSSEPALP(consSepalpQuadratic)12756 SCIP_DECL_CONSSEPALP(consSepalpQuadratic)
12757 {
12758    SCIP_CONSHDLRDATA* conshdlrdata;
12759    SCIP_Bool          solviolbounds;
12760    SCIP_CONS*         maxviolcon;
12761 
12762    assert(scip != NULL);
12763    assert(conshdlr != NULL);
12764    assert(conss != NULL || nconss == 0);
12765    assert(result != NULL);
12766 
12767    *result = SCIP_DIDNOTFIND;
12768 
12769    conshdlrdata = SCIPconshdlrGetData(conshdlr);
12770    assert(conshdlrdata != NULL);
12771 
12772    SCIP_CALL( computeViolations(scip, conss, nconss, NULL, &solviolbounds, &maxviolcon) );
12773 
12774    /* don't try to separate solutions that violate variable bounds */
12775    if( solviolbounds )
12776       return SCIP_OKAY;
12777 
12778    /* if nothing violated, then nothing to separate */
12779    if( maxviolcon == NULL )
12780       return SCIP_OKAY;
12781 
12782    /* at root, check if we want to solve the NLP relaxation and use its solutions as reference point
12783     * if there is something convex, then linearizing in the solution of the NLP relaxation can be very useful
12784     */
12785    if( SCIPgetDepth(scip) == 0 && !conshdlrdata->sepanlp &&
12786       (SCIPgetNContVars(scip) >= conshdlrdata->sepanlpmincont * SCIPgetNVars(scip) ||
12787          (SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_UNBOUNDEDRAY && conshdlrdata->sepanlpmincont <= 1.0)) &&
12788       SCIPisNLPConstructed(scip) && SCIPgetNNlpis(scip) > 0 )
12789    {
12790       SCIP_CONSDATA*  consdata;
12791       SCIP_NLPSOLSTAT solstat;
12792       SCIP_Bool       solvednlp;
12793       int c;
12794 
12795       solstat = SCIPgetNLPSolstat(scip);
12796       solvednlp = FALSE;
12797       if( solstat == SCIP_NLPSOLSTAT_UNKNOWN )
12798       {
12799          /* NLP is not solved yet, so we might want to do this
12800           * but first check whether there is a violated constraint side which corresponds to a convex function
12801           */
12802          for( c = 0; c < nconss; ++c )
12803          {
12804             assert(conss[c] != NULL);  /*lint !e613 */
12805 
12806             consdata = SCIPconsGetData(conss[c]);  /*lint !e613 */
12807             assert(consdata != NULL);
12808 
12809             /* skip feasible constraints */
12810             if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
12811                continue;
12812 
12813             /* make sure curvature has been checked */
12814             SCIP_CALL( checkCurvature(scip, conss[c], conshdlrdata->checkcurvature) );  /*lint !e613 */
12815 
12816             if( (SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) && consdata->isconvex) ||
12817                ( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && consdata->isconcave) )
12818                break;
12819          }
12820 
12821          if( c < nconss )
12822          {
12823             /* try to solve NLP and update solstat */
12824 
12825             /* ensure linear conss are in NLP */
12826             if( conshdlrdata->subnlpheur != NULL )
12827             {
12828                SCIP_CALL( SCIPaddLinearConsToNlpHeurSubNlp(scip, conshdlrdata->subnlpheur, TRUE, TRUE) );
12829             }
12830 
12831             /* set LP solution as starting values, if available */
12832             if( SCIPgetLPSolstat(scip) == SCIP_LPSOLSTAT_OPTIMAL )
12833             {
12834                SCIP_CALL( SCIPsetNLPInitialGuessSol(scip, NULL) );
12835             }
12836 
12837             /* SCIP_CALL( SCIPsetNLPIntPar(scip, SCIP_NLPPAR_VERBLEVEL, 1) ); */
12838             SCIP_CALL( SCIPsolveNLP(scip) );
12839 
12840             solstat = SCIPgetNLPSolstat(scip);
12841             SCIPdebugMsg(scip, "solved NLP relax, solution status: %d\n", solstat);
12842 
12843             solvednlp = TRUE;
12844          }
12845       }
12846 
12847       conshdlrdata->sepanlp = TRUE;
12848 
12849       if( solstat == SCIP_NLPSOLSTAT_GLOBINFEASIBLE )
12850       {
12851          SCIPdebugMsg(scip, "NLP relaxation is globally infeasible, thus can cutoff node\n");
12852          *result = SCIP_CUTOFF;
12853          return SCIP_OKAY;
12854       }
12855 
12856       if( solstat <= SCIP_NLPSOLSTAT_FEASIBLE )
12857       {
12858          /* if we have feasible NLP solution, generate linearization cuts there */
12859          SCIP_Bool lpsolseparated;
12860          SCIP_SOL* nlpsol;
12861 
12862          SCIP_CALL( SCIPcreateNLPSol(scip, &nlpsol, NULL) );
12863          assert(nlpsol != NULL);
12864 
12865          /* if we solved the NLP and solution is integral, then pass it to trysol heuristic */
12866          if( solvednlp && conshdlrdata->trysolheur != NULL )
12867          {
12868             int nfracvars;
12869 
12870             nfracvars = 0;
12871             if( SCIPgetNBinVars(scip) > 0 || SCIPgetNIntVars(scip) > 0 )
12872             {
12873                SCIP_CALL( SCIPgetNLPFracVars(scip, NULL, NULL, NULL, &nfracvars, NULL) );
12874             }
12875 
12876             if( nfracvars == 0 )
12877             {
12878                SCIPdebugMsg(scip, "pass solution with obj. value %g to trysol\n", SCIPgetSolOrigObj(scip, nlpsol));
12879                SCIP_CALL( SCIPheurPassSolTrySol(scip, conshdlrdata->trysolheur, nlpsol) );
12880             }
12881          }
12882 
12883          SCIP_CALL( addLinearizationCuts(scip, conshdlr, conss, nconss, nlpsol, &lpsolseparated, SCIPgetSepaMinEfficacy(scip)) );
12884 
12885          SCIP_CALL( SCIPfreeSol(scip, &nlpsol) );
12886 
12887          /* if a cut that separated the LP solution was added, then return, otherwise continue with usual separation in LP solution */
12888          if( lpsolseparated )
12889          {
12890             SCIPdebugMsg(scip, "linearization cuts separate LP solution\n");
12891             *result = SCIP_SEPARATED;
12892 
12893             return SCIP_OKAY;
12894          }
12895       }
12896    }
12897    /* if we do not want to try solving the NLP, or have no NLP, or have no NLP solver, or solving the NLP failed,
12898     * or separating with NLP solution as reference point failed, then try (again) with LP solution as reference point
12899     */
12900 
12901    SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, NULL, SCIPgetSepaMinEfficacy(scip), FALSE, result, NULL) );
12902 
12903    return SCIP_OKAY;
12904 }
12905 
12906 /** separation method of constraint handler for arbitrary primal solutions */
12907 static
SCIP_DECL_CONSSEPASOL(consSepasolQuadratic)12908 SCIP_DECL_CONSSEPASOL(consSepasolQuadratic)
12909 {
12910    SCIP_Bool          solviolbounds;
12911    SCIP_CONS*         maxviolcon;
12912 
12913    assert(scip != NULL);
12914    assert(conshdlr != NULL);
12915    assert(conss != NULL || nconss == 0);
12916    assert(sol != NULL);
12917    assert(result != NULL);
12918 
12919    *result = SCIP_DIDNOTFIND;
12920 
12921    SCIP_CALL( computeViolations(scip, conss, nconss, sol, &solviolbounds, &maxviolcon) );
12922 
12923    /* don't separate solution that are outside variable bounds */
12924    if( solviolbounds )
12925       return SCIP_OKAY;
12926 
12927    /* if nothing violated, then nothing to separate */
12928    if( maxviolcon == NULL )
12929       return SCIP_OKAY;
12930 
12931    SCIP_CALL( separatePoint(scip, conshdlr, conss, nconss, nusefulconss, sol, SCIPgetSepaMinEfficacy(scip), FALSE, result, NULL) );
12932 
12933    return SCIP_OKAY;
12934 }
12935 
12936 /** constraint enforcing method of constraint handler for LP solutions */
12937 static
SCIP_DECL_CONSENFOLP(consEnfolpQuadratic)12938 SCIP_DECL_CONSENFOLP(consEnfolpQuadratic)
12939 {  /*lint --e{715}*/
12940    SCIP_CALL( enforceConstraint(scip, conshdlr, conss, nconss, nusefulconss, NULL, solinfeasible, result) );
12941 
12942    return SCIP_OKAY;
12943 }
12944 
12945 /** constraint enforcing method of constraint handler for relaxation solutions */
12946 static
SCIP_DECL_CONSENFORELAX(consEnforelaxQuadratic)12947 SCIP_DECL_CONSENFORELAX(consEnforelaxQuadratic)
12948 {  /*lint --e{715}*/
12949    SCIP_CALL( enforceConstraint(scip, conshdlr, conss, nconss, nusefulconss, sol, solinfeasible, result) );
12950 
12951    return SCIP_OKAY;
12952 }
12953 
12954 /** constraint enforcing method of constraint handler for pseudo solutions */
12955 static
SCIP_DECL_CONSENFOPS(consEnfopsQuadratic)12956 SCIP_DECL_CONSENFOPS(consEnfopsQuadratic)
12957 {  /*lint --e{715}*/
12958    SCIP_Bool          solviolbounds;
12959    SCIP_CONS*         maxviolcon;
12960    SCIP_CONSDATA*     consdata;
12961    SCIP_RESULT        propresult;
12962    SCIP_VAR*          var;
12963    int                c;
12964    int                i;
12965    int                nchgbds;
12966    int                nnotify;
12967 
12968    assert(scip != NULL);
12969    assert(conss != NULL || nconss == 0);
12970 
12971    SCIP_CALL( computeViolations(scip, conss, nconss, NULL, &solviolbounds, &maxviolcon) );
12972 
12973    /* pseudo solutions should be within bounds by definition */
12974    assert(!solviolbounds);
12975 
12976    if( maxviolcon == NULL )
12977    {
12978       *result = SCIP_FEASIBLE;
12979       return SCIP_OKAY;
12980    }
12981 
12982    *result = SCIP_INFEASIBLE;
12983 
12984    SCIPdebugMsg(scip, "enfops with max violation in cons <%s>\n", SCIPconsGetName(maxviolcon));
12985 
12986    /* run domain propagation */
12987    nchgbds = 0;
12988    SCIP_CALL( propagateBounds(scip, conshdlr, conss, nconss, &propresult, &nchgbds) );
12989    if( propresult == SCIP_CUTOFF || propresult == SCIP_REDUCEDDOM )
12990    {
12991       *result = propresult;
12992       return SCIP_OKAY;
12993    }
12994 
12995    /* we are not feasible and we cannot proof that the whole node is infeasible
12996     * -> collect all variables in violated constraints for branching
12997     */
12998    nnotify = 0;
12999    for( c = 0; c < nconss; ++c )
13000    {
13001       assert(conss != NULL);
13002       consdata = SCIPconsGetData(conss[c]);
13003       assert(consdata != NULL);
13004 
13005       if( !SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) && !SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
13006          continue;
13007 
13008       for( i = 0; i < consdata->nlinvars; ++i )
13009       {
13010          var = consdata->linvars[i];
13011          if( !SCIPisRelEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
13012          {
13013             SCIP_CALL( SCIPaddExternBranchCand(scip, var, MAX(consdata->lhsviol, consdata->rhsviol), SCIP_INVALID) );
13014             ++nnotify;
13015          }
13016       }
13017 
13018       for( i = 0; i < consdata->nquadvars; ++i )
13019       {
13020          var = consdata->quadvarterms[i].var;
13021          if( !SCIPisRelEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
13022          {
13023             SCIP_CALL( SCIPaddExternBranchCand(scip, var, MAX(consdata->lhsviol, consdata->rhsviol), SCIP_INVALID) );
13024             ++nnotify;
13025          }
13026       }
13027    }
13028 
13029    if( nnotify == 0 )
13030    {
13031       SCIP_Bool addedcons;
13032       SCIP_Bool reduceddom;
13033       SCIP_Bool infeasible;
13034 
13035       /* if no branching candidate found, then all variables are almost fixed
13036        * calling replaceByLinearConstraints() should lead to fix all almost-fixed quadratic variables, and possibly replace some quad. conss by linear ones
13037        */
13038       SCIP_CALL( replaceByLinearConstraints(scip, conss, nconss, &addedcons, &reduceddom, &infeasible) );
13039       if( addedcons )
13040       {
13041          *result = SCIP_CONSADDED;
13042          return SCIP_OKAY;
13043       }
13044       if( reduceddom )
13045       {
13046          *result = SCIP_REDUCEDDOM;
13047          return SCIP_OKAY;
13048       }
13049       if( infeasible )
13050       {
13051          *result = SCIP_CUTOFF;
13052          return SCIP_OKAY;
13053       }
13054 
13055       SCIPdebugMsg(scip, "All variables in violated constraints fixed (up to epsilon). Cannot find branching candidate. Forcing solution of LP.\n");
13056       *result = SCIP_SOLVELP;
13057    }
13058 
13059    assert(*result == SCIP_SOLVELP || (*result == SCIP_INFEASIBLE && nnotify > 0));
13060    return SCIP_OKAY;
13061 }
13062 
13063 /** domain propagation method of constraint handler */
13064 static
SCIP_DECL_CONSPROP(consPropQuadratic)13065 SCIP_DECL_CONSPROP(consPropQuadratic)
13066 {
13067    int         nchgbds;
13068 
13069    assert(scip != NULL);
13070    assert(conshdlr != NULL);
13071    assert(conss != NULL || nconss == 0);
13072    assert(result != NULL);
13073 
13074    nchgbds = 0;
13075    SCIP_CALL( propagateBounds(scip, conshdlr, conss, nmarkedconss, result, &nchgbds) );
13076 
13077    return SCIP_OKAY;
13078 }  /*lint !e715 */
13079 
13080 /** presolving method of constraint handler */
13081 static
SCIP_DECL_CONSPRESOL(consPresolQuadratic)13082 SCIP_DECL_CONSPRESOL(consPresolQuadratic)
13083 {  /*lint --e{715,788}*/
13084    SCIP_CONSHDLRDATA* conshdlrdata;
13085    SCIP_CONSDATA*     consdata;
13086    SCIP_RESULT        solveresult;
13087    SCIP_Bool          redundant;
13088    SCIP_Bool          havechange;
13089    SCIP_Bool          doreformulations;
13090    int                c;
13091    int                i;
13092 
13093    assert(scip     != NULL);
13094    assert(conshdlr != NULL);
13095    assert(conss    != NULL || nconss == 0);
13096    assert(result   != NULL);
13097 
13098    *result = SCIP_DIDNOTFIND;
13099 
13100    /* if other presolvers did not find enough changes for another presolving round and we are in exhaustive presolving,
13101     * then try the reformulations (replacing products with binaries, disaggregation, setting default variable bounds)
13102     * otherwise, we wait with these
13103     */
13104    doreformulations = ((presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) != 0) && SCIPisPresolveFinished(scip);
13105    SCIPdebugMsg(scip, "presolving will %swait with reformulation\n", doreformulations ? "not " : "");
13106 
13107    conshdlrdata = SCIPconshdlrGetData(conshdlr);
13108    assert(conshdlrdata != NULL);
13109 
13110    for( c = 0; c < nconss; ++c )
13111    {
13112       assert(conss != NULL);
13113       consdata = SCIPconsGetData(conss[c]);
13114       assert(consdata != NULL);
13115 
13116       SCIPdebugMsg(scip, "process constraint <%s>\n", SCIPconsGetName(conss[c]));
13117       SCIPdebugPrintCons(scip, conss[c], NULL);
13118 
13119       if( !consdata->initialmerge )
13120       {
13121          SCIP_CALL( mergeAndCleanBilinearTerms(scip, conss[c]) );
13122          SCIP_CALL( mergeAndCleanQuadVarTerms(scip, conss[c]) );
13123          SCIP_CALL( mergeAndCleanLinearVars(scip, conss[c]) );
13124          consdata->initialmerge = TRUE;
13125 
13126          if( SCIPisInfinity(scip, consdata->lhs) || SCIPisInfinity(scip, -consdata->rhs) )
13127          {
13128             SCIPdebugMsg(scip, "lhs or rhs at wrong side of infinity -> declaring cutoff\n");
13129             *result = SCIP_CUTOFF;
13130             return SCIP_OKAY;
13131          }
13132       }
13133 
13134       havechange = FALSE;
13135 #ifdef CHECKIMPLINBILINEAR
13136       if( consdata->isimpladded && (presoltiming & SCIP_PRESOLTIMING_FAST) != 0 )
13137       {
13138          int nbilinremoved;
13139          SCIP_CALL( presolveApplyImplications(scip, conss[c], &nbilinremoved) );
13140          if( nbilinremoved > 0 )
13141          {
13142             *nchgcoefs += nbilinremoved;
13143             havechange = TRUE;
13144             *result = SCIP_SUCCESS;
13145          }
13146          assert(!consdata->isimpladded);
13147       }
13148 #endif
13149       /* call upgrade methods if the constraint has not been presolved yet or there has been a bound tightening or possibly be a change in variable type
13150        * we want to do this before (multi)aggregated variables are replaced, since that may change structure, e.g., introduce bilinear terms
13151        */
13152       if( !consdata->ispresolved || !consdata->ispropagated || nnewchgvartypes > 0 )
13153       {
13154          SCIP_Bool upgraded;
13155 
13156          SCIP_CALL( presolveUpgrade(scip, conshdlr, conss[c], &upgraded, nupgdconss, naddconss, presoltiming) );
13157          if( upgraded )
13158          {
13159             *result = SCIP_SUCCESS;
13160             continue;
13161          }
13162       }
13163 
13164       if( !consdata->isremovedfixings )
13165       {
13166          SCIP_CALL( removeFixedVariables(scip, conshdlrdata->eventhdlr, conss[c]) );
13167          assert(consdata->isremovedfixings);
13168          havechange = TRUE;
13169       }
13170 
13171       /* try to "solve" the constraint, e.g., reduce to a variable aggregation */
13172       SCIP_CALL( presolveSolve(scip, conss[c], &solveresult, &redundant, naggrvars) );
13173       if( solveresult == SCIP_CUTOFF )
13174       {
13175          SCIPdebugMsg(scip, "solving constraint <%s> says problem is infeasible in presolve\n", SCIPconsGetName(conss[c]));
13176          *result = SCIP_CUTOFF;
13177          return SCIP_OKAY;
13178       }
13179       if( redundant )
13180       {
13181          SCIP_CALL( SCIPdelCons(scip, conss[c]) );
13182          ++*ndelconss;
13183          *result = SCIP_SUCCESS;
13184          break;
13185       }
13186       if( solveresult == SCIP_SUCCESS )
13187       {
13188          *result = SCIP_SUCCESS;
13189          havechange = TRUE;
13190       }
13191 
13192       /* @todo divide constraint by gcd of coefficients if all are integral */
13193 
13194       if( doreformulations )
13195       {
13196          int naddconss_old;
13197 
13198          naddconss_old = *naddconss;
13199 
13200          SCIP_CALL( presolveTryAddAND(scip, conshdlr, conss[c], naddconss) );
13201          assert(*naddconss >= naddconss_old);
13202 
13203          if( *naddconss == naddconss_old )
13204          {
13205             /* user not so empathic about AND, or we don't have products of two binaries, so try this more general reformulation */
13206             SCIP_CALL( presolveTryAddLinearReform(scip, conshdlr, conss[c], naddconss) );
13207             assert(*naddconss >= naddconss_old);
13208          }
13209 
13210          if( conshdlrdata->maxdisaggrsize > 1 )
13211          {
13212             /* try disaggregation, if enabled */
13213             SCIP_CALL( presolveDisaggregate(scip, conshdlr, conss[c], naddconss) );
13214          }
13215 
13216          if( *naddconss > naddconss_old )
13217          {
13218             /* if something happened, report success and cleanup constraint */
13219             *result = SCIP_SUCCESS;
13220             havechange = TRUE;
13221             SCIP_CALL( mergeAndCleanBilinearTerms(scip, conss[c]) );
13222             SCIP_CALL( mergeAndCleanQuadVarTerms(scip, conss[c]) );
13223             SCIP_CALL( mergeAndCleanLinearVars(scip, conss[c]) );
13224          }
13225       }
13226 
13227       if( consdata->nlinvars == 0 && consdata->nquadvars == 0 )
13228       {
13229          /* all variables fixed or removed, constraint function is 0.0 now */
13230          if( (!SCIPisInfinity(scip, -consdata->lhs) && SCIPisFeasPositive(scip, consdata->lhs)) ||
13231             ( !SCIPisInfinity(scip,  consdata->rhs) && SCIPisFeasNegative(scip, consdata->rhs)) )
13232          { /* left hand side positive or right hand side negative */
13233             SCIPdebugMsg(scip, "constraint <%s> is constant and infeasible\n", SCIPconsGetName(conss[c]));
13234             SCIP_CALL( SCIPdelCons(scip, conss[c]) );
13235             ++*ndelconss;
13236             *result = SCIP_CUTOFF;
13237             return SCIP_OKAY;
13238          }
13239 
13240          /* left and right hand side are consistent */
13241          SCIPdebugMsg(scip, "constraint <%s> is constant and feasible, deleting\n", SCIPconsGetName(conss[c]));
13242          SCIP_CALL( SCIPdelCons(scip, conss[c]) );
13243          ++*ndelconss;
13244          *result = SCIP_SUCCESS;
13245          continue;
13246       }
13247 
13248       if( (presoltiming & SCIP_PRESOLTIMING_FAST) != 0 && !consdata->ispropagated )
13249       {
13250          /* try domain propagation if there were bound changes or constraint has changed (in which case, processVarEvents may have set ispropagated to false) */
13251          SCIP_RESULT propresult;
13252          int roundnr;
13253 
13254          roundnr = 0;
13255          do
13256          {
13257             ++roundnr;
13258 
13259             SCIPdebugMsg(scip, "starting domain propagation round %d of %d\n", roundnr, conshdlrdata->maxproproundspresolve);
13260 
13261             if( !consdata->ispropagated )
13262             {
13263                consdata->ispropagated = TRUE;
13264 
13265                SCIP_CALL( propagateBoundsCons(scip, conshdlr, conss[c], &propresult, nchgbds, &redundant) );
13266 
13267                if( propresult == SCIP_CUTOFF )
13268                {
13269                   SCIPdebugMsg(scip, "propagation on constraint <%s> says problem is infeasible in presolve\n",
13270                      SCIPconsGetName(conss[c]));
13271                   *result = SCIP_CUTOFF;
13272                   return SCIP_OKAY;
13273                }
13274 
13275                /* delete constraint if found redundant by bound tightening */
13276                if( redundant )
13277                {
13278                   SCIP_CALL( SCIPdelCons(scip, conss[c]) );
13279                   ++*ndelconss;
13280                   *result = SCIP_SUCCESS;
13281                   break;
13282                }
13283 
13284                if( propresult == SCIP_REDUCEDDOM )
13285                {
13286                   *result = SCIP_SUCCESS;
13287                   havechange = TRUE;
13288                }
13289             }
13290          }
13291          while( !consdata->ispropagated && roundnr < conshdlrdata->maxproproundspresolve );
13292 
13293          if( redundant )
13294             continue;
13295       }
13296 
13297       /* check if we have a single linear continuous variable that we can make implicit integer */
13298       if( (nnewchgvartypes != 0 || havechange || !consdata->ispresolved)
13299          && (SCIPisEQ(scip, consdata->lhs, consdata->rhs) && SCIPisIntegral(scip, consdata->lhs)) )
13300       {
13301          int       ncontvar;
13302          SCIP_VAR* candidate;
13303          SCIP_Bool fail;
13304 
13305          fail = FALSE;
13306          candidate = NULL;
13307          ncontvar = 0;
13308 
13309          for( i = 0; !fail && i < consdata->nlinvars; ++i )
13310          {
13311             if( !SCIPisIntegral(scip, consdata->lincoefs[i]) )
13312             {
13313                fail = TRUE;
13314             }
13315             else if( SCIPvarGetType(consdata->linvars[i]) == SCIP_VARTYPE_CONTINUOUS )
13316             {
13317                if( ncontvar > 0 ) /* now at 2nd continuous variable */
13318                   fail = TRUE;
13319                else if( SCIPisEQ(scip, ABS(consdata->lincoefs[i]), 1.0) )
13320                   candidate = consdata->linvars[i];
13321                ++ncontvar;
13322             }
13323          }
13324          for( i = 0; !fail && i < consdata->nquadvars; ++i )
13325             fail = SCIPvarGetType(consdata->quadvarterms[i].var) == SCIP_VARTYPE_CONTINUOUS ||
13326                !SCIPisIntegral(scip, consdata->quadvarterms[i].lincoef) ||
13327                !SCIPisIntegral(scip, consdata->quadvarterms[i].sqrcoef);
13328          for( i = 0; !fail && i < consdata->nbilinterms; ++i )
13329             fail = !SCIPisIntegral(scip, consdata->bilinterms[i].coef);
13330 
13331          if( !fail && candidate != NULL )
13332          {
13333             SCIP_Bool infeasible;
13334 
13335             SCIPdebugMsg(scip, "make variable <%s> implicit integer due to constraint <%s>\n", SCIPvarGetName(candidate), SCIPconsGetName(conss[c]));
13336 
13337             SCIP_CALL( SCIPchgVarType(scip, candidate, SCIP_VARTYPE_IMPLINT, &infeasible) );
13338             if( infeasible )
13339             {
13340                SCIPdebugMsg(scip, "infeasible upgrade of variable <%s> to integral type, domain is empty\n", SCIPvarGetName(candidate));
13341                *result = SCIP_CUTOFF;
13342 
13343                return SCIP_OKAY;
13344             }
13345 
13346             ++(*nchgvartypes);
13347             *result = SCIP_SUCCESS;
13348             havechange = TRUE;
13349          }
13350       }
13351 
13352       /* call upgrade methods again if constraint has been changed */
13353       if( havechange )
13354       {
13355          SCIP_Bool upgraded;
13356 
13357          SCIP_CALL( presolveUpgrade(scip, conshdlr, conss[c], &upgraded, nupgdconss, naddconss, presoltiming) );
13358          if( upgraded )
13359          {
13360             *result = SCIP_SUCCESS;
13361             continue;
13362          }
13363       }
13364 
13365       /* fix quadratic variables with proper square coefficients contained in a single quadratic constraint to their
13366        * upper or lower bounds
13367        */
13368       if( (presoltiming & SCIP_PRESOLTIMING_EXHAUSTIVE) != 0 && conshdlrdata->checkquadvarlocks != 'd'
13369          && SCIPisPresolveFinished(scip) )
13370       {
13371          SCIP_CONS* cons;
13372          SCIP_VAR* vars[2];
13373          SCIP_BOUNDTYPE boundtypes[2];
13374          SCIP_Real bounds[2];
13375          char name[SCIP_MAXSTRLEN];
13376 
13377          /* merge variables in order to get correct locks for quadratic variables */
13378          if( !consdata->initialmerge )
13379          {
13380             SCIP_CALL( mergeAndCleanBilinearTerms(scip, conss[c]) );
13381             SCIP_CALL( mergeAndCleanQuadVarTerms(scip, conss[c]) );
13382             SCIP_CALL( mergeAndCleanLinearVars(scip, conss[c]) );
13383             consdata->initialmerge = TRUE;
13384          }
13385 
13386          for( i = 0; i < consdata->nquadvars; ++i )
13387          {
13388             if( hasQuadvarHpProperty(scip, consdata, i) )
13389             {
13390                SCIP_VAR* var;
13391 
13392                var = consdata->quadvarterms[i].var;
13393                assert(var != NULL);
13394 
13395                /* try to change the variable type to binary */
13396                if( conshdlrdata->checkquadvarlocks == 't' && SCIPisEQ(scip, SCIPvarGetLbGlobal(var), 0.0) && SCIPisEQ(scip, SCIPvarGetUbGlobal(var), 1.0) )
13397                {
13398                   SCIP_Bool infeasible;
13399 
13400                   assert(SCIPvarGetType(var) != SCIP_VARTYPE_BINARY);
13401                   SCIP_CALL( SCIPchgVarType(scip, var, SCIP_VARTYPE_BINARY, &infeasible) );
13402 
13403                   if( infeasible )
13404                   {
13405                      SCIPdebugMsg(scip, "detect infeasibility after changing variable <%s> to binary type\n", SCIPvarGetName(var));
13406                      *result = SCIP_CUTOFF;
13407                      return SCIP_OKAY;
13408                   }
13409                }
13410                /* add bound disjunction constraint if bounds of variable are finite */
13411                else if( !SCIPisInfinity(scip, -SCIPvarGetLbGlobal(var)) && !SCIPisInfinity(scip, SCIPvarGetUbGlobal(var)) )
13412                {
13413                   vars[0] = var;
13414                   vars[1] = var;
13415                   boundtypes[0] = SCIP_BOUNDTYPE_LOWER;
13416                   boundtypes[1] = SCIP_BOUNDTYPE_UPPER;
13417                   bounds[0] = SCIPvarGetUbGlobal(var);
13418                   bounds[1] = SCIPvarGetLbGlobal(var);
13419 
13420                   SCIPdebugMsg(scip, "add bound disjunction constraint for %s\n", SCIPvarGetName(var));
13421 
13422                   (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "quadvarbnddisj_%s", SCIPvarGetName(var));
13423                   SCIP_CALL( SCIPcreateConsBounddisjunction(scip, &cons, name, 2, vars, boundtypes, bounds, TRUE, TRUE,
13424                         TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE) );
13425 
13426                   SCIP_CALL( SCIPaddCons(scip, cons) );
13427                   SCIP_CALL( SCIPreleaseCons(scip, &cons) );
13428                }
13429 
13430                *result = SCIP_SUCCESS;
13431             }
13432          }
13433       }
13434 
13435       consdata->ispresolved = TRUE;
13436    }
13437 
13438    return SCIP_OKAY;
13439 }
13440 
13441 /** variable rounding lock method of constraint handler */
13442 static
SCIP_DECL_CONSLOCK(consLockQuadratic)13443 SCIP_DECL_CONSLOCK(consLockQuadratic)
13444 {  /*lint --e{715}*/
13445    SCIP_CONSDATA* consdata;
13446    SCIP_Bool      haslb;
13447    SCIP_Bool      hasub;
13448    int            i;
13449 
13450    assert(scip != NULL);
13451    assert(cons != NULL);
13452    assert(locktype == SCIP_LOCKTYPE_MODEL);
13453 
13454    consdata = SCIPconsGetData(cons);
13455    assert(consdata != NULL);
13456 
13457    haslb = !SCIPisInfinity(scip, -consdata->lhs);
13458    hasub = !SCIPisInfinity(scip, consdata->rhs);
13459 
13460    for( i = 0; i < consdata->nlinvars; ++i )
13461    {
13462       if( consdata->lincoefs[i] > 0 )
13463       {
13464          if( haslb )
13465          {
13466             SCIP_CALL( SCIPaddVarLocksType(scip, consdata->linvars[i], locktype, nlockspos, nlocksneg) );
13467          }
13468          if( hasub )
13469          {
13470             SCIP_CALL( SCIPaddVarLocksType(scip, consdata->linvars[i], locktype, nlocksneg, nlockspos) );
13471          }
13472       }
13473       else
13474       {
13475          if( haslb )
13476          {
13477             SCIP_CALL( SCIPaddVarLocksType(scip, consdata->linvars[i], locktype, nlocksneg, nlockspos) );
13478          }
13479          if( hasub )
13480          {
13481             SCIP_CALL( SCIPaddVarLocksType(scip, consdata->linvars[i], locktype, nlockspos, nlocksneg) );
13482          }
13483       }
13484    }
13485 
13486    for( i = 0; i < consdata->nquadvars; ++i )
13487    {
13488       /* @todo try to be more clever, but variable locks that depend on the bounds of other variables are not trival to maintain */
13489       SCIP_CALL( SCIPaddVarLocksType(scip, consdata->quadvarterms[i].var, SCIP_LOCKTYPE_MODEL, nlockspos+nlocksneg,
13490          nlockspos+nlocksneg) );
13491    }
13492 
13493    return SCIP_OKAY;
13494 }
13495 
13496 /** constraint enabling notification method of constraint handler */
13497 static
SCIP_DECL_CONSENABLE(consEnableQuadratic)13498 SCIP_DECL_CONSENABLE(consEnableQuadratic)
13499 {
13500    SCIP_CONSHDLRDATA* conshdlrdata;
13501 
13502    assert(scip != NULL);
13503    assert(conshdlr != NULL);
13504    assert(cons != NULL);
13505    assert(SCIPconsIsTransformed(cons));
13506    assert(SCIPconsIsActive(cons));
13507 
13508    conshdlrdata = SCIPconshdlrGetData(conshdlr);
13509    assert(conshdlrdata != NULL);
13510 
13511    SCIPdebugMsg(scip, "enable cons <%s>\n", SCIPconsGetName(cons));
13512 
13513    /* catch variable events */
13514    SCIP_CALL( catchVarEvents(scip, conshdlrdata->eventhdlr, cons) );
13515 
13516    if( SCIPgetStage(scip) >= SCIP_STAGE_EXITPRESOLVE )
13517    {
13518       /* merge duplicate bilinear terms, move quad terms that are linear to linear vars */
13519       SCIP_CALL( mergeAndCleanBilinearTerms(scip, cons) );
13520       SCIP_CALL( mergeAndCleanQuadVarTerms(scip, cons) );
13521       SCIP_CALL( mergeAndCleanLinearVars(scip, cons) );
13522    }
13523 
13524    /* initialize solving data */
13525    if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING )
13526    {
13527       SCIP_CALL( consInitsolQuadratic(scip, conshdlr, &cons, 1) );
13528    }
13529 
13530    return SCIP_OKAY;
13531 }
13532 
13533 /** constraint disabling notification method of constraint handler */
13534 static
SCIP_DECL_CONSDISABLE(consDisableQuadratic)13535 SCIP_DECL_CONSDISABLE(consDisableQuadratic)
13536 {  /*lint --e{715}*/
13537    SCIP_CONSHDLRDATA* conshdlrdata;
13538 
13539    assert(scip != NULL);
13540    assert(conshdlr != NULL);
13541    assert(cons != NULL);
13542    assert(SCIPconsIsTransformed(cons));
13543 
13544    conshdlrdata = SCIPconshdlrGetData(conshdlr);
13545    assert(conshdlrdata != NULL);
13546 
13547    SCIPdebugMsg(scip, "disable cons <%s>\n", SCIPconsGetName(cons));
13548 
13549    /* free solving data */
13550    if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING )
13551    {
13552       SCIP_CALL( consExitsolQuadratic(scip, conshdlr, &cons, 1, FALSE) );
13553    }
13554 
13555    /* drop variable events */
13556    SCIP_CALL( dropVarEvents(scip, conshdlrdata->eventhdlr, cons) );
13557 
13558    return SCIP_OKAY;
13559 }
13560 
13561 /** constraint display method of constraint handler */
13562 static
SCIP_DECL_CONSPRINT(consPrintQuadratic)13563 SCIP_DECL_CONSPRINT(consPrintQuadratic)
13564 {  /*lint --e{715}*/
13565    SCIP_CONSDATA* consdata;
13566 
13567    assert(scip != NULL);
13568    assert(cons != NULL);
13569 
13570    consdata = SCIPconsGetData(cons);
13571    assert(consdata != NULL);
13572 
13573    /* print left hand side for ranged rows */
13574    if( !SCIPisInfinity(scip, -consdata->lhs)
13575       && !SCIPisInfinity(scip, consdata->rhs)
13576       && !SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
13577       SCIPinfoMessage(scip, file, "%.15g <= ", consdata->lhs);
13578 
13579    /* print coefficients and variables */
13580    if( consdata->nlinvars == 0 && consdata->nquadvars == 0 )
13581    {
13582       SCIPinfoMessage(scip, file, "0 ");
13583    }
13584    else
13585    {
13586       SCIP_VAR*** monomialvars;
13587       SCIP_Real** monomialexps;
13588       SCIP_Real*  monomialcoefs;
13589       int*        monomialnvars;
13590       int         nmonomials;
13591       int         monomialssize;
13592       int         j;
13593 
13594       monomialssize = consdata->nlinvars + 2 * consdata->nquadvars + consdata->nbilinterms;
13595       SCIP_CALL( SCIPallocBufferArray(scip, &monomialvars,  monomialssize) );
13596       SCIP_CALL( SCIPallocBufferArray(scip, &monomialexps,  monomialssize) );
13597       SCIP_CALL( SCIPallocBufferArray(scip, &monomialcoefs, monomialssize) );
13598       SCIP_CALL( SCIPallocBufferArray(scip, &monomialnvars, monomialssize) );
13599 
13600       nmonomials = 0;
13601       for( j = 0; j < consdata->nlinvars; ++j )
13602       {
13603          assert(nmonomials < monomialssize);
13604 
13605          SCIP_CALL( SCIPallocBufferArray(scip, &monomialvars[nmonomials], 1) );  /*lint !e866 */
13606 
13607          monomialvars[nmonomials][0] = consdata->linvars[j];
13608          monomialexps[nmonomials] = NULL;
13609          monomialcoefs[nmonomials] = consdata->lincoefs[j];
13610          monomialnvars[nmonomials] = 1;
13611          ++nmonomials;
13612       }
13613 
13614       for( j = 0; j < consdata->nquadvars; ++j )
13615       {
13616          if( consdata->quadvarterms[j].lincoef != 0.0 )
13617          {
13618             assert(nmonomials < monomialssize);
13619 
13620             SCIP_CALL( SCIPallocBufferArray(scip, &monomialvars[nmonomials], 1) );  /*lint !e866 */
13621 
13622             monomialvars[nmonomials][0] = consdata->quadvarterms[j].var;
13623             monomialexps[nmonomials] = NULL;
13624             monomialcoefs[nmonomials] = consdata->quadvarterms[j].lincoef;
13625             monomialnvars[nmonomials] = 1;
13626             ++nmonomials;
13627          }
13628 
13629          if( consdata->quadvarterms[j].sqrcoef != 0.0 )
13630          {
13631             assert(nmonomials < monomialssize);
13632 
13633             SCIP_CALL( SCIPallocBufferArray(scip, &monomialvars[nmonomials], 1) );  /*lint !e866 */
13634             SCIP_CALL( SCIPallocBufferArray(scip, &monomialexps[nmonomials], 1) );  /*lint !e866 */
13635 
13636             monomialvars[nmonomials][0] = consdata->quadvarterms[j].var;
13637             monomialexps[nmonomials][0] = 2.0;
13638             monomialcoefs[nmonomials] = consdata->quadvarterms[j].sqrcoef;
13639             monomialnvars[nmonomials] = 1;
13640             ++nmonomials;
13641          }
13642       }
13643 
13644       for( j = 0; j < consdata->nbilinterms; ++j )
13645       {
13646          assert(nmonomials < monomialssize);
13647 
13648          SCIP_CALL( SCIPallocBufferArray(scip, &monomialvars[nmonomials], 2) );  /*lint !e866 */
13649 
13650          monomialvars[nmonomials][0] = consdata->bilinterms[j].var1;
13651          monomialvars[nmonomials][1] = consdata->bilinterms[j].var2;
13652          monomialexps[nmonomials] = NULL;
13653          monomialcoefs[nmonomials] = consdata->bilinterms[j].coef;
13654          monomialnvars[nmonomials] = 2;
13655          ++nmonomials;
13656       }
13657 
13658       SCIP_CALL( SCIPwriteVarsPolynomial(scip, file, monomialvars, monomialexps, monomialcoefs, monomialnvars, nmonomials, TRUE) );
13659 
13660       for( j = nmonomials - 1; j >= 0 ; --j )
13661       {
13662          SCIPfreeBufferArrayNull(scip, &monomialexps[j]);
13663          SCIPfreeBufferArray(scip, &monomialvars[j]);
13664       }
13665 
13666       SCIPfreeBufferArray(scip, &monomialnvars);
13667       SCIPfreeBufferArray(scip, &monomialcoefs);
13668       SCIPfreeBufferArray(scip, &monomialexps);
13669       SCIPfreeBufferArray(scip, &monomialvars);
13670    }
13671 
13672    /* print right hand side */
13673    if( SCIPisEQ(scip, consdata->lhs, consdata->rhs) )
13674    {
13675       SCIPinfoMessage(scip, file, " == %.15g", consdata->rhs);
13676    }
13677    else if( !SCIPisInfinity(scip, consdata->rhs) )
13678    {
13679       SCIPinfoMessage(scip, file, " <= %.15g", consdata->rhs);
13680    }
13681    else if( !SCIPisInfinity(scip, -consdata->lhs) )
13682    {
13683       SCIPinfoMessage(scip, file, " >= %.15g", consdata->lhs);
13684    }
13685    else
13686    {
13687       /* should be ignored by parser */
13688       SCIPinfoMessage(scip, file, " [free]");
13689    }
13690 
13691    return SCIP_OKAY;
13692 }
13693 
13694 /** feasibility check method of constraint handler for integral solutions */
13695 static
SCIP_DECL_CONSCHECK(consCheckQuadratic)13696 SCIP_DECL_CONSCHECK(consCheckQuadratic)
13697 {  /*lint --e{715}*/
13698    SCIP_CONSHDLRDATA* conshdlrdata;
13699    SCIP_CONSDATA*     consdata;
13700    SCIP_Real          maxviol;
13701    int                c;
13702    SCIP_Bool          maypropfeasible; /* whether we may be able to propose a feasible solution */
13703    SCIP_Bool          solviolbounds;
13704 
13705    assert(scip != NULL);
13706    assert(conss != NULL || nconss == 0);
13707    assert(result != NULL);
13708 
13709    conshdlrdata = SCIPconshdlrGetData(conshdlr);
13710    assert(conshdlrdata != NULL);
13711 
13712    *result = SCIP_FEASIBLE;
13713 
13714    maxviol = 0.0;
13715    maypropfeasible = conshdlrdata->linfeasshift && (conshdlrdata->trysolheur != NULL) &&
13716       SCIPgetStage(scip) >= SCIP_STAGE_TRANSFORMED && SCIPgetStage(scip) <= SCIP_STAGE_SOLVING;
13717    for( c = 0; c < nconss; ++c )
13718    {
13719       assert(conss != NULL);
13720       SCIP_CALL( computeViolation(scip, conss[c], sol, &solviolbounds) );
13721       assert(!solviolbounds);  /* see also issue #627 */
13722 
13723       consdata = SCIPconsGetData(conss[c]);
13724       assert(consdata != NULL);
13725 
13726       if( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) || SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
13727       {
13728          *result = SCIP_INFEASIBLE;
13729          if( printreason )
13730          {
13731             SCIP_CALL( SCIPprintCons(scip, conss[c], NULL) );
13732             SCIPinfoMessage(scip, NULL, ";\n");
13733             if( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) )
13734             {
13735                SCIPinfoMessage(scip, NULL, "violation: left hand side is violated by %.15g\n", consdata->lhsviol);
13736             }
13737             if( SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)) )
13738             {
13739                SCIPinfoMessage(scip, NULL, "violation: right hand side is violated by %.15g\n", consdata->rhsviol);
13740             }
13741          }
13742          if( (conshdlrdata->subnlpheur == NULL || sol == NULL) && !maypropfeasible && !completely )
13743             return SCIP_OKAY;
13744          if( consdata->lhsviol > maxviol || consdata->rhsviol > maxviol )
13745             maxviol = consdata->lhsviol + consdata->rhsviol;
13746 
13747          /* do not try to shift linear variables if activity is at infinity (leads to setting variable to infinity in solution, which is not allowed) */
13748          if( maypropfeasible && SCIPisInfinity(scip, REALABS(consdata->activity)) )
13749             maypropfeasible = FALSE;
13750 
13751          if( maypropfeasible )
13752          {
13753             /* update information on linear variables that may be in- or decreased, if initsolve has not done so yet */
13754             if( SCIPgetStage(scip) >= SCIP_STAGE_TRANSFORMED && SCIPgetStage(scip) < SCIP_STAGE_INITSOLVE )
13755                consdataFindUnlockedLinearVar(scip, consdata);
13756 
13757             if( SCIPisGT(scip, consdata->lhsviol, SCIPfeastol(scip)) )
13758             {
13759                /* check if there is a variable which may help to get the left hand side satisfied
13760                 * if there is no such var, then we cannot get feasible */
13761                if( !(consdata->linvar_mayincrease >= 0 && consdata->lincoefs[consdata->linvar_mayincrease] > 0.0) &&
13762                   ! (consdata->linvar_maydecrease >= 0 && consdata->lincoefs[consdata->linvar_maydecrease] < 0.0) )
13763                   maypropfeasible = FALSE;
13764             }
13765             else
13766             {
13767                assert(SCIPisGT(scip, consdata->rhsviol, SCIPfeastol(scip)));
13768                /* check if there is a variable which may help to get the right hand side satisfied
13769                 * if there is no such var, then we cannot get feasible */
13770                if( !(consdata->linvar_mayincrease >= 0 && consdata->lincoefs[consdata->linvar_mayincrease] < 0.0) &&
13771                   ! (consdata->linvar_maydecrease >= 0 && consdata->lincoefs[consdata->linvar_maydecrease] > 0.0) )
13772                   maypropfeasible = FALSE;
13773             }
13774          }
13775       }
13776    }
13777 
13778    if( *result == SCIP_INFEASIBLE && maypropfeasible )
13779    {
13780       SCIP_Bool success;
13781 
13782       SCIP_CALL( proposeFeasibleSolution(scip, conshdlr, conss, nconss, sol, &success) );
13783 
13784       /* do not pass solution to NLP heuristic if we made it feasible this way */
13785       if( success )
13786          return SCIP_OKAY;
13787    }
13788 
13789    if( *result == SCIP_INFEASIBLE && conshdlrdata->subnlpheur != NULL && sol != NULL && !SCIPisInfinity(scip, maxviol) )
13790    {
13791       SCIP_CALL( SCIPupdateStartpointHeurSubNlp(scip, conshdlrdata->subnlpheur, sol, maxviol) );
13792    }
13793 
13794    return SCIP_OKAY;
13795 }
13796 
13797 /** constraint copying method of constraint handler */
13798 static
SCIP_DECL_CONSCOPY(consCopyQuadratic)13799 SCIP_DECL_CONSCOPY(consCopyQuadratic)
13800 {
13801    SCIP_CONSDATA*    consdata;
13802    SCIP_CONSDATA*    targetconsdata;
13803    SCIP_VAR**        linvars;
13804    SCIP_QUADVARTERM* quadvarterms;
13805    SCIP_BILINTERM*   bilinterms;
13806    int               i;
13807    int               j;
13808    int               k;
13809 
13810    assert(scip != NULL);
13811    assert(cons != NULL);
13812    assert(sourcescip != NULL);
13813    assert(sourceconshdlr != NULL);
13814    assert(sourcecons != NULL);
13815    assert(varmap != NULL);
13816    assert(valid != NULL);
13817 
13818    consdata = SCIPconsGetData(sourcecons);
13819    assert(consdata != NULL);
13820 
13821    linvars = NULL;
13822    quadvarterms = NULL;
13823    bilinterms = NULL;
13824 
13825    *valid = TRUE;
13826 
13827    if( consdata->nlinvars != 0 )
13828    {
13829       SCIP_CALL( SCIPallocBufferArray(sourcescip, &linvars, consdata->nlinvars) );
13830       for( i = 0; i < consdata->nlinvars; ++i )
13831       {
13832          SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, consdata->linvars[i], &linvars[i], varmap, consmap, global, valid) );
13833          assert(!(*valid) || linvars[i] != NULL);
13834 
13835          /* we do not copy, if a variable is missing */
13836          if( !(*valid) )
13837             goto TERMINATE;
13838       }
13839    }
13840 
13841    if( consdata->nbilinterms != 0 )
13842    {
13843       SCIP_CALL( SCIPallocBufferArray(sourcescip, &bilinterms, consdata->nbilinterms) );
13844    }
13845 
13846    if( consdata->nquadvars != 0 )
13847    {
13848       SCIP_CALL( SCIPallocBufferArray(sourcescip, &quadvarterms, consdata->nquadvars) );
13849       for( i = 0; i < consdata->nquadvars; ++i )
13850       {
13851          SCIP_CALL( SCIPgetVarCopy(sourcescip, scip, consdata->quadvarterms[i].var, &quadvarterms[i].var, varmap, consmap, global, valid) );
13852          assert(!(*valid) || quadvarterms[i].var != NULL);
13853 
13854          /* we do not copy, if a variable is missing */
13855          if( !(*valid) )
13856             goto TERMINATE;
13857 
13858          quadvarterms[i].lincoef   = consdata->quadvarterms[i].lincoef;
13859          quadvarterms[i].sqrcoef   = consdata->quadvarterms[i].sqrcoef;
13860          quadvarterms[i].eventdata = NULL;
13861          quadvarterms[i].nadjbilin = consdata->quadvarterms[i].nadjbilin;
13862          quadvarterms[i].adjbilin  = consdata->quadvarterms[i].adjbilin;
13863 
13864          assert(consdata->nbilinterms != 0 || consdata->quadvarterms[i].nadjbilin == 0);
13865 
13866          for( j = 0; j < consdata->quadvarterms[i].nadjbilin; ++j )
13867          {
13868             assert(bilinterms != NULL);
13869 
13870             k = consdata->quadvarterms[i].adjbilin[j];
13871             assert(consdata->bilinterms[k].var1 != NULL);
13872             assert(consdata->bilinterms[k].var2 != NULL);
13873             if( consdata->bilinterms[k].var1 == consdata->quadvarterms[i].var )
13874             {
13875                assert(consdata->bilinterms[k].var2 != consdata->quadvarterms[i].var);
13876                bilinterms[k].var1 = quadvarterms[i].var;
13877             }
13878             else
13879             {
13880                assert(consdata->bilinterms[k].var2 == consdata->quadvarterms[i].var);
13881                bilinterms[k].var2 = quadvarterms[i].var;
13882             }
13883             bilinterms[k].coef = consdata->bilinterms[k].coef;
13884          }
13885       }
13886    }
13887 
13888    assert(stickingatnode == FALSE);
13889    SCIP_CALL( SCIPcreateConsQuadratic2(scip, cons, name ? name : SCIPconsGetName(sourcecons),
13890          consdata->nlinvars, linvars, consdata->lincoefs,
13891          consdata->nquadvars, quadvarterms,
13892          consdata->nbilinterms, bilinterms,
13893          consdata->lhs, consdata->rhs,
13894          initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable) );
13895 
13896    /* copy information on curvature */
13897    targetconsdata = SCIPconsGetData(*cons);
13898    targetconsdata->isconvex      = consdata->isconvex;
13899    targetconsdata->isconcave     = consdata->isconcave;
13900    targetconsdata->iscurvchecked = consdata->iscurvchecked;
13901 
13902  TERMINATE:
13903    SCIPfreeBufferArrayNull(sourcescip, &quadvarterms);
13904    SCIPfreeBufferArrayNull(sourcescip, &bilinterms);
13905    SCIPfreeBufferArrayNull(sourcescip, &linvars);
13906 
13907    return SCIP_OKAY;
13908 }
13909 
13910 /** constraint parsing method of constraint handler */
13911 static
SCIP_DECL_CONSPARSE(consParseQuadratic)13912 SCIP_DECL_CONSPARSE(consParseQuadratic)
13913 {  /*lint --e{715}*/
13914    SCIP_VAR*** monomialvars;
13915    SCIP_Real** monomialexps;
13916    SCIP_Real*  monomialcoefs;
13917    char*       endptr;
13918    int*        monomialnvars;
13919    int         nmonomials;
13920 
13921    SCIP_Real lhs;
13922    SCIP_Real rhs;
13923 
13924    assert(scip != NULL);
13925    assert(success != NULL);
13926    assert(str != NULL);
13927    assert(name != NULL);
13928    assert(cons != NULL);
13929 
13930    /* set left and right hand side to their default values */
13931    lhs = -SCIPinfinity(scip);
13932    rhs =  SCIPinfinity(scip);
13933 
13934    (*success) = FALSE;
13935 
13936    /* return of string empty */
13937    if( !*str )
13938       return SCIP_OKAY;
13939 
13940    /* ignore whitespace */
13941    while( isspace((unsigned char)*str) )
13942       ++str;
13943 
13944    /* check for left hand side */
13945    if( isdigit((unsigned char)str[0]) || ((str[0] == '-' || str[0] == '+') && isdigit((unsigned char)str[1])) )
13946    {
13947       /* there is a number coming, maybe it is a left-hand-side */
13948       if( !SCIPstrToRealValue(str, &lhs, &endptr) )
13949       {
13950          SCIPerrorMessage("error parsing number from <%s>\n", str);
13951          return SCIP_OKAY;
13952       }
13953 
13954       /* ignore whitespace */
13955       while( isspace((unsigned char)*endptr) )
13956          ++endptr;
13957 
13958       if( endptr[0] != '<' || endptr[1] != '=' )
13959       {
13960          /* no '<=' coming, so it was the first coefficient, but not a left-hand-side */
13961          lhs = -SCIPinfinity(scip);
13962       }
13963       else
13964       {
13965          /* it was indeed a left-hand-side, so continue parsing after it */
13966          str = endptr + 2;
13967 
13968          /* ignore whitespace */
13969          while( isspace((unsigned char)*str) )
13970             ++str;
13971       }
13972    }
13973 
13974    SCIP_CALL( SCIPparseVarsPolynomial(scip, str, &monomialvars, &monomialexps, &monomialcoefs, &monomialnvars, &nmonomials, &endptr, success) );
13975 
13976    if( *success )
13977    {
13978       /* check for right hand side */
13979       str = endptr;
13980 
13981       /* ignore whitespace */
13982       while( isspace((unsigned char)*str) )
13983          ++str;
13984 
13985       if( *str && str[0] == '<' && str[1] == '=' )
13986       {
13987          /* we seem to get a right-hand-side */
13988          str += 2;
13989 
13990          if( !SCIPstrToRealValue(str, &rhs, &endptr) )
13991          {
13992             SCIPerrorMessage("error parsing right-hand-side from %s\n", str);
13993             *success = FALSE;
13994          }
13995       }
13996       else if( *str && str[0] == '>' && str[1] == '=' )
13997       {
13998          /* we seem to get a left-hand-side */
13999          str += 2;
14000 
14001          /* we should not have a left-hand-side already */
14002          assert(SCIPisInfinity(scip, -lhs));
14003 
14004          if( !SCIPstrToRealValue(str, &lhs, &endptr) )
14005          {
14006             SCIPerrorMessage("error parsing left-hand-side from %s\n", str);
14007             *success = FALSE;
14008          }
14009       }
14010       else if( *str && str[0] == '=' && str[1] == '=' )
14011       {
14012          /* we seem to get a left- and right-hand-side */
14013          str += 2;
14014 
14015          /* we should not have a left-hand-side already */
14016          assert(SCIPisInfinity(scip, -lhs));
14017 
14018          if( !SCIPstrToRealValue(str, &lhs, &endptr) )
14019          {
14020             SCIPerrorMessage("error parsing left-hand-side from %s\n", str);
14021             *success = FALSE;
14022          }
14023          else
14024          {
14025             rhs = lhs;
14026          }
14027       }
14028    }
14029 
14030    if( *success )
14031    {
14032       int i;
14033 
14034       /* setup constraint */
14035       assert(stickingatnode == FALSE);
14036       SCIP_CALL( SCIPcreateConsQuadratic(scip, cons, name, 0, NULL, NULL,
14037             0, NULL, NULL, NULL, lhs, rhs,
14038             initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable) );
14039 
14040       for( i = 0; i < nmonomials; ++i )
14041       {
14042          if( monomialnvars[i] == 0 )
14043          {
14044             /* constant monomial */
14045             SCIPaddConstantQuadratic(scip, *cons, monomialcoefs[i]);
14046          }
14047          else if( monomialnvars[i] == 1 && monomialexps[i][0] == 1.0 )
14048          {
14049             /* linear monomial */
14050             SCIP_CALL( SCIPaddLinearVarQuadratic(scip, *cons, monomialvars[i][0], monomialcoefs[i]) );
14051          }
14052          else if( monomialnvars[i] == 1 && monomialexps[i][0] == 2.0 )
14053          {
14054             /* square monomial */
14055             SCIP_CALL( SCIPaddQuadVarQuadratic(scip, *cons, monomialvars[i][0], 0.0, monomialcoefs[i]) );
14056          }
14057          else if( monomialnvars[i] == 2 && monomialexps[i][0] == 1.0 && monomialexps[i][1] == 1.0 )
14058          {
14059             /* bilinear term */
14060             SCIP_VAR* var1;
14061             SCIP_VAR* var2;
14062             int pos;
14063 
14064             var1 = monomialvars[i][0];
14065             var2 = monomialvars[i][1];
14066             if( var1 == var2 )
14067             {
14068                /* actually a square term */
14069                SCIP_CALL( SCIPaddQuadVarQuadratic(scip, *cons, var1, 0.0, monomialcoefs[i]) );
14070             }
14071             else
14072             {
14073                SCIP_CALL( SCIPfindQuadVarTermQuadratic(scip, *cons, var1, &pos) );
14074                if( pos == -1 )
14075                {
14076                   SCIP_CALL( SCIPaddQuadVarQuadratic(scip, *cons, var1, 0.0, 0.0) );
14077                }
14078 
14079                SCIP_CALL( SCIPfindQuadVarTermQuadratic(scip, *cons, var2, &pos) );
14080                if( pos == -1 )
14081                {
14082                   SCIP_CALL( SCIPaddQuadVarQuadratic(scip, *cons, var2, 0.0, 0.0) );
14083                }
14084             }
14085 
14086             SCIP_CALL( SCIPaddBilinTermQuadratic(scip, *cons, var1, var2, monomialcoefs[i]) );
14087          }
14088          else
14089          {
14090             SCIPerrorMessage("polynomial in quadratic constraint does not have degree at most 2\n");
14091             *success = FALSE;
14092             SCIP_CALL( SCIPreleaseCons(scip, cons) );
14093             break;
14094          }
14095       }
14096    }
14097 
14098    SCIPfreeParseVarsPolynomialData(scip, &monomialvars, &monomialexps, &monomialcoefs, &monomialnvars, nmonomials);
14099 
14100    return SCIP_OKAY;
14101 }
14102 
14103 /** constraint method of constraint handler which returns the variables (if possible) */
14104 static
SCIP_DECL_CONSGETVARS(consGetVarsQuadratic)14105 SCIP_DECL_CONSGETVARS(consGetVarsQuadratic)
14106 {  /*lint --e{715}*/
14107    SCIP_CONSDATA* consdata;
14108 
14109    assert(cons != NULL);
14110    assert(success != NULL);
14111 
14112    consdata = SCIPconsGetData(cons);
14113    assert(consdata != NULL);
14114 
14115    if( varssize < consdata->nlinvars + consdata->nquadvars )
14116       (*success) = FALSE;
14117    else
14118    {
14119       int i;
14120 
14121       assert(vars != NULL);
14122 
14123       BMScopyMemoryArray(vars, consdata->linvars, consdata->nlinvars);
14124 
14125       for( i = 0; i < consdata->nquadvars; ++i )
14126          vars[consdata->nlinvars+i] = consdata->quadvarterms[i].var;
14127 
14128       (*success) = TRUE;
14129    }
14130 
14131    return SCIP_OKAY;
14132 }
14133 
14134 /** constraint method of constraint handler which returns the number of variables (if possible) */
14135 static
SCIP_DECL_CONSGETNVARS(consGetNVarsQuadratic)14136 SCIP_DECL_CONSGETNVARS(consGetNVarsQuadratic)
14137 {  /*lint --e{715}*/
14138    SCIP_CONSDATA* consdata;
14139 
14140    assert(cons != NULL);
14141    assert(success != NULL);
14142 
14143    consdata = SCIPconsGetData(cons);
14144    assert(consdata != NULL);
14145 
14146    (*nvars) = consdata->nlinvars + consdata->nquadvars;
14147    (*success) = TRUE;
14148 
14149    return SCIP_OKAY;
14150 }
14151 
14152 
14153 /*
14154  * constraint specific interface methods
14155  */
14156 
14157 /** creates the handler for quadratic constraints and includes it in SCIP */
SCIPincludeConshdlrQuadratic(SCIP * scip)14158 SCIP_RETCODE SCIPincludeConshdlrQuadratic(
14159    SCIP*                 scip                /**< SCIP data structure */
14160    )
14161 {
14162    SCIP_CONSHDLRDATA* conshdlrdata;
14163    SCIP_CONSHDLR* conshdlr;
14164 
14165    /* create quadratic constraint handler data */
14166    SCIP_CALL( SCIPallocBlockMemory(scip, &conshdlrdata) );
14167    BMSclearMemory(conshdlrdata);
14168 
14169    /* include constraint handler */
14170    SCIP_CALL( SCIPincludeConshdlrBasic(scip, &conshdlr, CONSHDLR_NAME, CONSHDLR_DESC,
14171          CONSHDLR_ENFOPRIORITY, CONSHDLR_CHECKPRIORITY, CONSHDLR_EAGERFREQ, CONSHDLR_NEEDSCONS,
14172          consEnfolpQuadratic, consEnfopsQuadratic, consCheckQuadratic, consLockQuadratic,
14173          conshdlrdata) );
14174    assert(conshdlr != NULL);
14175 
14176    /* set non-fundamental callbacks via specific setter functions */
14177    SCIP_CALL( SCIPsetConshdlrCopy(scip, conshdlr, conshdlrCopyQuadratic, consCopyQuadratic) );
14178    SCIP_CALL( SCIPsetConshdlrDelete(scip, conshdlr, consDeleteQuadratic) );
14179    SCIP_CALL( SCIPsetConshdlrDisable(scip, conshdlr, consDisableQuadratic) );
14180    SCIP_CALL( SCIPsetConshdlrEnable(scip, conshdlr, consEnableQuadratic) );
14181    SCIP_CALL( SCIPsetConshdlrExit(scip, conshdlr, consExitQuadratic) );
14182    SCIP_CALL( SCIPsetConshdlrExitpre(scip, conshdlr, consExitpreQuadratic) );
14183    SCIP_CALL( SCIPsetConshdlrExitsol(scip, conshdlr, consExitsolQuadratic) );
14184    SCIP_CALL( SCIPsetConshdlrFree(scip, conshdlr, consFreeQuadratic) );
14185    SCIP_CALL( SCIPsetConshdlrGetVars(scip, conshdlr, consGetVarsQuadratic) );
14186    SCIP_CALL( SCIPsetConshdlrGetNVars(scip, conshdlr, consGetNVarsQuadratic) );
14187    SCIP_CALL( SCIPsetConshdlrInit(scip, conshdlr, consInitQuadratic) );
14188    SCIP_CALL( SCIPsetConshdlrInitsol(scip, conshdlr, consInitsolQuadratic) );
14189    SCIP_CALL( SCIPsetConshdlrInitlp(scip, conshdlr, consInitlpQuadratic) );
14190    SCIP_CALL( SCIPsetConshdlrParse(scip, conshdlr, consParseQuadratic) );
14191    SCIP_CALL( SCIPsetConshdlrPresol(scip, conshdlr, consPresolQuadratic, CONSHDLR_MAXPREROUNDS, CONSHDLR_PRESOLTIMING) );
14192    SCIP_CALL( SCIPsetConshdlrPrint(scip, conshdlr, consPrintQuadratic) );
14193    SCIP_CALL( SCIPsetConshdlrProp(scip, conshdlr, consPropQuadratic, CONSHDLR_PROPFREQ, CONSHDLR_DELAYPROP,
14194          CONSHDLR_PROP_TIMING) );
14195    SCIP_CALL( SCIPsetConshdlrSepa(scip, conshdlr, consSepalpQuadratic, consSepasolQuadratic, CONSHDLR_SEPAFREQ,
14196          CONSHDLR_SEPAPRIORITY, CONSHDLR_DELAYSEPA) );
14197    SCIP_CALL( SCIPsetConshdlrTrans(scip, conshdlr, consTransQuadratic) );
14198    SCIP_CALL( SCIPsetConshdlrEnforelax(scip, conshdlr, consEnforelaxQuadratic) );
14199 
14200    /* add quadratic constraint handler parameters */
14201    SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/replacebinaryprod",
14202          "max. length of linear term which when multiplied with a binary variables is replaced by an auxiliary variable and a linear reformulation (0 to turn off)",
14203          &conshdlrdata->replacebinaryprodlength, FALSE, INT_MAX, 0, INT_MAX, NULL, NULL) );
14204 
14205    SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/empathy4and",
14206          "empathy level for using the AND constraint handler: 0 always avoid using AND; 1 use AND sometimes; 2 use AND as often as possible",
14207          &conshdlrdata->empathy4and, FALSE, 2, 0, 2, NULL, NULL) );
14208 
14209    SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/binreforminitial",
14210          "whether to make non-varbound linear constraints added due to replacing products with binary variables initial",
14211          &conshdlrdata->binreforminitial, TRUE, FALSE, NULL, NULL) );
14212 
14213    SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/binreformbinaryonly",
14214          "whether to consider only binary variables when replacing products with binary variables",
14215          &conshdlrdata->binreformbinaryonly, FALSE, TRUE, NULL, NULL) );
14216 
14217    SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/binreformmaxcoef",
14218          "limit (as factor on 1/feastol) on coefficients and coef. range in linear constraints created when replacing products with binary variables",
14219          &conshdlrdata->binreformmaxcoef, TRUE, 1e-4, 0.0, SCIPinfinity(scip), NULL, NULL) );
14220 
14221    SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/cutmaxrange",
14222          "maximal coef range of a cut (maximal coefficient divided by minimal coefficient) in order to be added to LP relaxation",
14223          &conshdlrdata->cutmaxrange, TRUE, 1e+7, 0.0, SCIPinfinity(scip), NULL, NULL) );
14224 
14225    SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/mincurvcollectbilinterms",
14226          "minimal curvature of constraints to be considered when returning bilinear terms to other plugins",
14227          &conshdlrdata->mincurvcollectbilinterms, TRUE, 0.8, -SCIPinfinity(scip), SCIPinfinity(scip), NULL, NULL) );
14228 
14229    SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/linearizeheursol",
14230          "whether linearizations of convex quadratic constraints should be added to cutpool in a solution found by some heuristic",
14231          &conshdlrdata->linearizeheursol, TRUE, TRUE, NULL, NULL) );
14232 
14233    SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/checkcurvature",
14234          "whether multivariate quadratic functions should be checked for convexity/concavity",
14235          &conshdlrdata->checkcurvature, FALSE, TRUE, NULL, NULL) );
14236 
14237    SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/checkfactorable",
14238          "whether constraint functions should be checked to be factorable",
14239          &conshdlrdata->checkfactorable, TRUE, TRUE, NULL, NULL) );
14240 
14241    SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/checkquadvarlocks",
14242          "whether quadratic variables contained in a single constraint should be forced to be at their lower or upper bounds ('d'isable, change 't'ype, add 'b'ound disjunction)",
14243          &conshdlrdata->checkquadvarlocks, TRUE, 't', "bdt", NULL, NULL) );
14244 
14245    SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/linfeasshift",
14246          "whether to try to make solutions in check function feasible by shifting a linear variable (esp. useful if constraint was actually objective function)",
14247          &conshdlrdata->linfeasshift, TRUE, TRUE, NULL, NULL) );
14248 
14249    SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maxdisaggrsize",
14250          "maximum number of created constraints when disaggregating a quadratic constraint (<= 1: off)",
14251          &conshdlrdata->maxdisaggrsize, FALSE, 1, 1, INT_MAX, NULL, NULL) );
14252 
14253    SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/disaggrmergemethod",
14254          "strategy how to merge independent blocks to reach maxdisaggrsize limit (keep 'b'iggest blocks and merge others; keep 's'mallest blocks and merge other; merge small blocks into bigger blocks to reach 'm'ean sizes)",
14255          &conshdlrdata->disaggrmergemethod, TRUE, 'm', "bms", NULL, NULL) );
14256 
14257    SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maxproprounds",
14258          "limit on number of propagation rounds for a single constraint within one round of SCIP propagation during solve",
14259          &conshdlrdata->maxproprounds, TRUE, 1, 0, INT_MAX, NULL, NULL) );
14260 
14261    SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/maxproproundspresolve",
14262          "limit on number of propagation rounds for a single constraint within one round of SCIP presolve",
14263          &conshdlrdata->maxproproundspresolve, TRUE, 10, 0, INT_MAX, NULL, NULL) );
14264 
14265    SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/enfolplimit",
14266          "maximum number of enforcement rounds before declaring the LP relaxation infeasible (-1: no limit); WARNING: changing this parameter might lead to incorrect results!",
14267          &conshdlrdata->enfolplimit, TRUE, -1, -1, INT_MAX, NULL, NULL) );
14268 
14269    SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/sepanlpmincont",
14270          "minimal required fraction of continuous variables in problem to use solution of NLP relaxation in root for separation",
14271          &conshdlrdata->sepanlpmincont, FALSE, 1.0, 0.0, 2.0, NULL, NULL) );
14272 
14273    SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/enfocutsremovable",
14274          "are cuts added during enforcement removable from the LP in the same node?",
14275          &conshdlrdata->enfocutsremovable, TRUE, FALSE, NULL, NULL) );
14276 
14277    SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/gaugecuts",
14278          "should convex quadratics generated strong cuts via gauge function?",
14279          &conshdlrdata->gaugecuts, FALSE, FALSE, NULL, NULL) );
14280 
14281    SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/interiorcomputation",
14282          "how the interior point for gauge cuts should be computed: 'a'ny point per constraint, 'm'ost interior per constraint",
14283          &conshdlrdata->interiorcomputation, TRUE, 'a', "am", NULL, NULL) );
14284 
14285    SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/projectedcuts",
14286          "should convex quadratics generated strong cuts via projections?",
14287          &conshdlrdata->projectedcuts, FALSE, FALSE, NULL, NULL) );
14288 
14289    SCIP_CALL( SCIPaddCharParam(scip, "constraints/" CONSHDLR_NAME "/branchscoring",
14290          "which score to give branching candidates: convexification 'g'ap, constraint 'v'iolation, 'c'entrality of variable value in domain",
14291          &conshdlrdata->branchscoring, TRUE, 'g', "cgv", NULL, NULL) );
14292 
14293    SCIP_CALL( SCIPaddBoolParam(scip, "constraints/" CONSHDLR_NAME "/usebilinineqbranch",
14294          "should linear inequalities be consindered when computing the branching scores for bilinear terms?",
14295          &conshdlrdata->usebilinineqbranch, FALSE, FALSE, NULL, NULL) );
14296 
14297    SCIP_CALL( SCIPaddRealParam(scip, "constraints/" CONSHDLR_NAME "/minscorebilinterms",
14298          "minimal required score in order to use linear inequalities for tighter bilinear relaxations",
14299          &conshdlrdata->minscorebilinterms, FALSE, 0.01, 0.0, 1.0, NULL, NULL) );
14300 
14301    SCIP_CALL( SCIPaddIntParam(scip, "constraints/" CONSHDLR_NAME "/bilinineqmaxseparounds",
14302          "maximum number of separation rounds to use linear inequalities for the bilinear term relaxation in a local node",
14303          &conshdlrdata->bilinineqmaxseparounds, TRUE, 3, 0, INT_MAX, NULL, NULL) );
14304 
14305    conshdlrdata->eventhdlr = NULL;
14306    SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &(conshdlrdata->eventhdlr),CONSHDLR_NAME"_boundchange", "signals a bound change to a quadratic constraint",
14307          processVarEvent, NULL) );
14308    assert(conshdlrdata->eventhdlr != NULL);
14309 
14310    SCIP_CALL( SCIPincludeEventhdlrBasic(scip, NULL, CONSHDLR_NAME"_newsolution", "handles the event that a new primal solution has been found",
14311          processNewSolutionEvent, NULL) );
14312 
14313    /* include the quadratic constraint upgrade in the nonlinear constraint handler */
14314    SCIP_CALL( SCIPincludeNonlinconsUpgrade(scip, nonlinconsUpgdQuadratic, NULL, NONLINCONSUPGD_PRIORITY, TRUE, CONSHDLR_NAME) );
14315 
14316    return SCIP_OKAY;
14317 }
14318 
14319 /** includes a quadratic constraint update method into the quadratic constraint handler */
SCIPincludeQuadconsUpgrade(SCIP * scip,SCIP_DECL_QUADCONSUPGD ((* quadconsupgd)),int priority,SCIP_Bool active,const char * conshdlrname)14320 SCIP_RETCODE SCIPincludeQuadconsUpgrade(
14321    SCIP*                 scip,               /**< SCIP data structure */
14322    SCIP_DECL_QUADCONSUPGD((*quadconsupgd)),  /**< method to call for upgrading quadratic constraint */
14323    int                   priority,           /**< priority of upgrading method */
14324    SCIP_Bool             active,             /**< should the upgrading method be active by default? */
14325    const char*           conshdlrname        /**< name of the constraint handler */
14326    )
14327 {
14328    SCIP_CONSHDLR*        conshdlr;
14329    SCIP_CONSHDLRDATA*    conshdlrdata;
14330    SCIP_QUADCONSUPGRADE* quadconsupgrade;
14331    char                  paramname[SCIP_MAXSTRLEN];
14332    char                  paramdesc[SCIP_MAXSTRLEN];
14333    int                   i;
14334 
14335    assert(quadconsupgd != NULL);
14336    assert(conshdlrname != NULL );
14337 
14338    /* find the quadratic constraint handler */
14339    conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
14340    if( conshdlr == NULL )
14341    {
14342       SCIPerrorMessage("quadratic constraint handler not found\n");
14343       return SCIP_PLUGINNOTFOUND;
14344    }
14345 
14346    conshdlrdata = SCIPconshdlrGetData(conshdlr);
14347    assert(conshdlrdata != NULL);
14348 
14349    if( !conshdlrdataHasUpgrade(scip, conshdlrdata, quadconsupgd, conshdlrname) )
14350    {
14351       /* create a quadratic constraint upgrade data object */
14352       SCIP_CALL( SCIPallocBlockMemory(scip, &quadconsupgrade) );
14353       quadconsupgrade->quadconsupgd = quadconsupgd;
14354       quadconsupgrade->priority     = priority;
14355       quadconsupgrade->active       = active;
14356 
14357       /* insert quadratic constraint upgrade method into constraint handler data */
14358       assert(conshdlrdata->nquadconsupgrades <= conshdlrdata->quadconsupgradessize);
14359       if( conshdlrdata->nquadconsupgrades+1 > conshdlrdata->quadconsupgradessize )
14360       {
14361          int newsize;
14362 
14363          newsize = SCIPcalcMemGrowSize(scip, conshdlrdata->nquadconsupgrades+1);
14364          SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &conshdlrdata->quadconsupgrades, conshdlrdata->quadconsupgradessize, newsize) );
14365          conshdlrdata->quadconsupgradessize = newsize;
14366       }
14367       assert(conshdlrdata->nquadconsupgrades+1 <= conshdlrdata->quadconsupgradessize);
14368 
14369       for( i = conshdlrdata->nquadconsupgrades; i > 0 && conshdlrdata->quadconsupgrades[i-1]->priority < quadconsupgrade->priority; --i )
14370          conshdlrdata->quadconsupgrades[i] = conshdlrdata->quadconsupgrades[i-1];
14371       assert(0 <= i && i <= conshdlrdata->nquadconsupgrades);
14372       conshdlrdata->quadconsupgrades[i] = quadconsupgrade;
14373       conshdlrdata->nquadconsupgrades++;
14374 
14375       /* adds parameter to turn on and off the upgrading step */
14376       (void) SCIPsnprintf(paramname, SCIP_MAXSTRLEN, "constraints/" CONSHDLR_NAME "/upgrade/%s", conshdlrname);
14377       (void) SCIPsnprintf(paramdesc, SCIP_MAXSTRLEN, "enable quadratic upgrading for constraint handler <%s>", conshdlrname);
14378       SCIP_CALL( SCIPaddBoolParam(scip,
14379             paramname, paramdesc,
14380             &quadconsupgrade->active, FALSE, active, NULL, NULL) );
14381    }
14382 
14383    return SCIP_OKAY;
14384 }
14385 
14386 /** Creates and captures a quadratic constraint.
14387  *
14388  *  The constraint should be given in the form
14389  *  \f[
14390  *  \ell \leq \sum_{i=1}^n b_i x_i + \sum_{j=1}^m a_j y_j z_j \leq u,
14391  *  \f]
14392  *  where \f$x_i = y_j = z_k\f$ is possible.
14393  *
14394  *  @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
14395  */
SCIPcreateConsQuadratic(SCIP * scip,SCIP_CONS ** cons,const char * name,int nlinvars,SCIP_VAR ** linvars,SCIP_Real * lincoefs,int nquadterms,SCIP_VAR ** quadvars1,SCIP_VAR ** quadvars2,SCIP_Real * quadcoefs,SCIP_Real lhs,SCIP_Real rhs,SCIP_Bool initial,SCIP_Bool separate,SCIP_Bool enforce,SCIP_Bool check,SCIP_Bool propagate,SCIP_Bool local,SCIP_Bool modifiable,SCIP_Bool dynamic,SCIP_Bool removable)14396 SCIP_RETCODE SCIPcreateConsQuadratic(
14397    SCIP*                 scip,               /**< SCIP data structure */
14398    SCIP_CONS**           cons,               /**< pointer to hold the created constraint */
14399    const char*           name,               /**< name of constraint */
14400    int                   nlinvars,           /**< number of linear terms (n) */
14401    SCIP_VAR**            linvars,            /**< array with variables in linear part (x_i) */
14402    SCIP_Real*            lincoefs,           /**< array with coefficients of variables in linear part (b_i) */
14403    int                   nquadterms,         /**< number of quadratic terms (m) */
14404    SCIP_VAR**            quadvars1,          /**< array with first variables in quadratic terms (y_j) */
14405    SCIP_VAR**            quadvars2,          /**< array with second variables in quadratic terms (z_j) */
14406    SCIP_Real*            quadcoefs,          /**< array with coefficients of quadratic terms (a_j) */
14407    SCIP_Real             lhs,                /**< left hand side of quadratic equation (ell) */
14408    SCIP_Real             rhs,                /**< right hand side of quadratic equation (u) */
14409    SCIP_Bool             initial,            /**< should the LP relaxation of constraint be in the initial LP?
14410                                               *   Usually set to TRUE. Set to FALSE for 'lazy constraints'. */
14411    SCIP_Bool             separate,           /**< should the constraint be separated during LP processing?
14412                                               *   Usually set to TRUE. */
14413    SCIP_Bool             enforce,            /**< should the constraint be enforced during node processing?
14414                                               *   TRUE for model constraints, FALSE for additional, redundant constraints. */
14415    SCIP_Bool             check,              /**< should the constraint be checked for feasibility?
14416                                               *   TRUE for model constraints, FALSE for additional, redundant constraints. */
14417    SCIP_Bool             propagate,          /**< should the constraint be propagated during node processing?
14418                                               *   Usually set to TRUE. */
14419    SCIP_Bool             local,              /**< is constraint only valid locally?
14420                                               *   Usually set to FALSE. Has to be set to TRUE, e.g., for branching constraints. */
14421    SCIP_Bool             modifiable,         /**< is constraint modifiable (subject to column generation)?
14422                                               *   Usually set to FALSE. In column generation applications, set to TRUE if pricing
14423                                               *   adds coefficients to this constraint. */
14424    SCIP_Bool             dynamic,            /**< is constraint subject to aging?
14425                                               *   Usually set to FALSE. Set to TRUE for own cuts which
14426                                               *   are separated as constraints. */
14427    SCIP_Bool             removable           /**< should the relaxation be removed from the LP due to aging or cleanup?
14428                                               *   Usually set to FALSE. Set to TRUE for 'lazy constraints' and 'user cuts'. */
14429    )
14430 {
14431    SCIP_CONSHDLR* conshdlr;
14432    SCIP_CONSDATA* consdata;
14433    SCIP_HASHMAP*  quadvaridxs;
14434    SCIP_Real      sqrcoef;
14435    int i;
14436    int var1pos;
14437    int var2pos;
14438 
14439    int nbilinterms;
14440 
14441    assert(linvars != NULL || nlinvars == 0);
14442    assert(lincoefs != NULL || nlinvars == 0);
14443    assert(quadvars1 != NULL || nquadterms == 0);
14444    assert(quadvars2 != NULL || nquadterms == 0);
14445    assert(quadcoefs != NULL || nquadterms == 0);
14446 
14447    assert(modifiable == FALSE); /* we do not support column generation */
14448 
14449    /* find the quadratic constraint handler */
14450    conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
14451    if( conshdlr == NULL )
14452    {
14453       SCIPerrorMessage("quadratic constraint handler not found\n");
14454       return SCIP_PLUGINNOTFOUND;
14455    }
14456 
14457    /* create constraint data and constraint */
14458    SCIP_CALL( consdataCreateEmpty(scip, &consdata) );
14459 
14460    consdata->lhs = lhs;
14461    consdata->rhs = rhs;
14462 
14463    SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
14464          local, modifiable, dynamic, removable, FALSE) );
14465 
14466    /* add quadratic variables and remember their indices */
14467    SCIP_CALL( SCIPhashmapCreate(&quadvaridxs, SCIPblkmem(scip), nquadterms) );
14468    nbilinterms = 0;
14469    for( i = 0; i < nquadterms; ++i )
14470    {
14471       if( SCIPisZero(scip, quadcoefs[i]) )  /*lint !e613*/
14472          continue;
14473 
14474       /* if it is actually a square term, remember it's coefficient */
14475       /* cppcheck-suppress nullPointer */
14476       if( quadvars1[i] == quadvars2[i] )   /*lint !e613*/
14477          sqrcoef = quadcoefs[i];   /*lint !e613 */
14478       else
14479          sqrcoef = 0.0;
14480 
14481       /* add quadvars1[i], if not in there already */
14482       if( !SCIPhashmapExists(quadvaridxs, quadvars1[i]) )  /*lint !e613*/
14483       {
14484          SCIP_CALL( addQuadVarTerm(scip, *cons, quadvars1[i], 0.0, sqrcoef) );   /*lint !e613*/
14485          assert(consdata->nquadvars >= 0);
14486          assert(consdata->quadvarterms[consdata->nquadvars-1].var == quadvars1[i]);  /*lint !e613*/
14487 
14488          SCIP_CALL( SCIPhashmapInsertInt(quadvaridxs, quadvars1[i], consdata->nquadvars-1) ); /*lint !e613*/
14489       }
14490       else if( !SCIPisZero(scip, sqrcoef) )
14491       {
14492          /* if it's there already, but we got a square coefficient, add it to the previous one */
14493          var1pos = SCIPhashmapGetImageInt(quadvaridxs, quadvars1[i]);   /*lint !e613*/
14494          assert(consdata->quadvarterms[var1pos].var == quadvars1[i]);   /*lint !e613*/
14495          consdata->quadvarterms[var1pos].sqrcoef += sqrcoef;
14496       }
14497 
14498       /* cppcheck-suppress nullPointer */
14499       if( quadvars1[i] == quadvars2[i] )  /*lint !e613*/
14500          continue;
14501 
14502       /* add quadvars2[i], if not in there already */
14503       if( !SCIPhashmapExists(quadvaridxs, quadvars2[i]) )   /*lint !e613*/
14504       {
14505          assert(sqrcoef == 0.0);
14506          SCIP_CALL( addQuadVarTerm(scip, *cons, quadvars2[i], 0.0, 0.0) );   /*lint !e613*/
14507          assert(consdata->nquadvars >= 0);
14508          assert(consdata->quadvarterms[consdata->nquadvars-1].var == quadvars2[i]);  /*lint !e613*/
14509 
14510          SCIP_CALL( SCIPhashmapInsertInt(quadvaridxs, quadvars2[i], consdata->nquadvars-1) ); /*lint !e613*/
14511       }
14512 
14513       ++nbilinterms;
14514    }
14515 
14516    /* add bilinear terms, if we saw any */
14517    if( nbilinterms > 0 )
14518    {
14519       SCIP_CALL( consdataEnsureBilinSize(scip, consdata, nbilinterms) );
14520       for( i = 0; i < nquadterms; ++i )
14521       {
14522          if( SCIPisZero(scip, quadcoefs[i]) )  /*lint !e613*/
14523             continue;
14524 
14525          /* square terms have been taken care of already */
14526          if( quadvars1[i] == quadvars2[i] )  /*lint !e613 */
14527             continue;
14528 
14529          assert(SCIPhashmapExists(quadvaridxs, quadvars1[i]));  /*lint !e613*/
14530          assert(SCIPhashmapExists(quadvaridxs, quadvars2[i]));  /*lint !e613*/
14531 
14532          var1pos = SCIPhashmapGetImageInt(quadvaridxs, quadvars1[i]);  /*lint !e613*/
14533          var2pos = SCIPhashmapGetImageInt(quadvaridxs, quadvars2[i]);  /*lint !e613*/
14534 
14535          SCIP_CALL( addBilinearTerm(scip, *cons, var1pos, var2pos, quadcoefs[i]) );  /*lint !e613*/
14536       }
14537    }
14538 
14539    /* add linear variables */
14540    SCIP_CALL( consdataEnsureLinearVarsSize(scip, consdata, nlinvars) );
14541    for( i = 0; i < nlinvars; ++i )
14542    {
14543       if( SCIPisZero(scip, lincoefs[i]) )  /*lint !e613*/
14544          continue;
14545 
14546       /* if it's a linear coefficient for a quadratic variable, add it there, otherwise add as linear variable */
14547       if( SCIPhashmapExists(quadvaridxs, linvars[i]) )  /*lint !e613*/
14548       {
14549          var1pos = SCIPhashmapGetImageInt(quadvaridxs, linvars[i]);  /*lint !e613*/
14550          assert(consdata->quadvarterms[var1pos].var == linvars[i]);  /*lint !e613*/
14551          consdata->quadvarterms[var1pos].lincoef += lincoefs[i];  /*lint !e613*/
14552       }
14553       else
14554       {
14555          SCIP_CALL( addLinearCoef(scip, *cons, linvars[i], lincoefs[i]) );  /*lint !e613*/
14556       }
14557    }
14558 
14559    SCIPhashmapFree(&quadvaridxs);
14560 
14561    SCIPdebugMsg(scip, "created quadratic constraint ");
14562    SCIPdebugPrintCons(scip, *cons, NULL);
14563 
14564    return SCIP_OKAY;
14565 }
14566 
14567 /** creates and captures a quadratic constraint with all its
14568  *  flags set to their default values.
14569  *
14570  *  The constraint should be given in the form
14571  *  \f[
14572  *  \ell \leq \sum_{i=1}^n b_i x_i + \sum_{j=1}^m a_j y_j z_j \leq u,
14573  *  \f]
14574  *  where \f$x_i = y_j = z_k\f$ is possible.
14575  *
14576  *  @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
14577  */
SCIPcreateConsBasicQuadratic(SCIP * scip,SCIP_CONS ** cons,const char * name,int nlinvars,SCIP_VAR ** linvars,SCIP_Real * lincoefs,int nquadterms,SCIP_VAR ** quadvars1,SCIP_VAR ** quadvars2,SCIP_Real * quadcoefs,SCIP_Real lhs,SCIP_Real rhs)14578 SCIP_RETCODE SCIPcreateConsBasicQuadratic(
14579    SCIP*                 scip,               /**< SCIP data structure */
14580    SCIP_CONS**           cons,               /**< pointer to hold the created constraint */
14581    const char*           name,               /**< name of constraint */
14582    int                   nlinvars,           /**< number of linear terms (n) */
14583    SCIP_VAR**            linvars,            /**< array with variables in linear part (x_i) */
14584    SCIP_Real*            lincoefs,           /**< array with coefficients of variables in linear part (b_i) */
14585    int                   nquadterms,         /**< number of quadratic terms (m) */
14586    SCIP_VAR**            quadvars1,          /**< array with first variables in quadratic terms (y_j) */
14587    SCIP_VAR**            quadvars2,          /**< array with second variables in quadratic terms (z_j) */
14588    SCIP_Real*            quadcoefs,          /**< array with coefficients of quadratic terms (a_j) */
14589    SCIP_Real             lhs,                /**< left hand side of quadratic equation (ell) */
14590    SCIP_Real             rhs                 /**< right hand side of quadratic equation (u) */
14591    )
14592 {
14593    SCIP_CALL( SCIPcreateConsQuadratic(scip, cons, name, nlinvars, linvars, lincoefs,
14594          nquadterms, quadvars1, quadvars2, quadcoefs, lhs, rhs,
14595          TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) );
14596 
14597    return SCIP_OKAY;
14598 }
14599 
14600 /** Creates and captures a quadratic constraint.
14601  *
14602  * The constraint should be given in the form
14603  * \f[
14604  * \ell \leq \sum_{i=1}^n b_i x_i + \sum_{j=1}^m (a_j y_j^2 + b_j y_j) + \sum_{k=1}^p c_k v_k w_k \leq u.
14605  * \f]
14606  *
14607  *  @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
14608  */
SCIPcreateConsQuadratic2(SCIP * scip,SCIP_CONS ** cons,const char * name,int nlinvars,SCIP_VAR ** linvars,SCIP_Real * lincoefs,int nquadvarterms,SCIP_QUADVARTERM * quadvarterms,int nbilinterms,SCIP_BILINTERM * bilinterms,SCIP_Real lhs,SCIP_Real rhs,SCIP_Bool initial,SCIP_Bool separate,SCIP_Bool enforce,SCIP_Bool check,SCIP_Bool propagate,SCIP_Bool local,SCIP_Bool modifiable,SCIP_Bool dynamic,SCIP_Bool removable)14609 SCIP_RETCODE SCIPcreateConsQuadratic2(
14610    SCIP*                 scip,               /**< SCIP data structure */
14611    SCIP_CONS**           cons,               /**< pointer to hold the created constraint */
14612    const char*           name,               /**< name of constraint */
14613    int                   nlinvars,           /**< number of linear terms (n) */
14614    SCIP_VAR**            linvars,            /**< array with variables in linear part (x_i) */
14615    SCIP_Real*            lincoefs,           /**< array with coefficients of variables in linear part (b_i) */
14616    int                   nquadvarterms,      /**< number of quadratic terms (m) */
14617    SCIP_QUADVARTERM*     quadvarterms,       /**< quadratic variable terms */
14618    int                   nbilinterms,        /**< number of bilinear terms (p) */
14619    SCIP_BILINTERM*       bilinterms,         /**< bilinear terms */
14620    SCIP_Real             lhs,                /**< constraint left hand side (ell) */
14621    SCIP_Real             rhs,                /**< constraint right hand side (u) */
14622    SCIP_Bool             initial,            /**< should the LP relaxation of constraint be in the initial LP? */
14623    SCIP_Bool             separate,           /**< should the constraint be separated during LP processing? */
14624    SCIP_Bool             enforce,            /**< should the constraint be enforced during node processing? */
14625    SCIP_Bool             check,              /**< should the constraint be checked for feasibility? */
14626    SCIP_Bool             propagate,          /**< should the constraint be propagated during node processing? */
14627    SCIP_Bool             local,              /**< is constraint only valid locally? */
14628    SCIP_Bool             modifiable,         /**< is constraint modifiable (subject to column generation)? */
14629    SCIP_Bool             dynamic,            /**< is constraint dynamic? */
14630    SCIP_Bool             removable           /**< should the constraint be removed from the LP due to aging or cleanup? */
14631    )
14632 {
14633    SCIP_CONSHDLR* conshdlr;
14634    SCIP_CONSDATA* consdata;
14635 
14636    assert(modifiable == FALSE); /* we do not support column generation */
14637    assert(nlinvars == 0 || (linvars != NULL && lincoefs != NULL));
14638    assert(nquadvarterms == 0 || quadvarterms != NULL);
14639    assert(nbilinterms == 0 || bilinterms != NULL);
14640 
14641    /* find the quadratic constraint handler */
14642    conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
14643    if( conshdlr == NULL )
14644    {
14645       SCIPerrorMessage("quadratic constraint handler not found\n");
14646       return SCIP_PLUGINNOTFOUND;
14647    }
14648 
14649    /* create constraint data */
14650    SCIP_CALL( consdataCreate(scip, &consdata, lhs, rhs,
14651          nlinvars, linvars, lincoefs, nquadvarterms, quadvarterms, nbilinterms, bilinterms,
14652          TRUE) );
14653 
14654    /* create constraint */
14655    SCIP_CALL( SCIPcreateCons(scip, cons, name, conshdlr, consdata, initial, separate, enforce, check, propagate,
14656          local, modifiable, dynamic, removable, FALSE) );
14657 
14658    return SCIP_OKAY;
14659 }
14660 
14661 /** creates and captures a quadratic constraint in its most basic version, i.e.,
14662  *  all constraint flags are set to their default values.
14663  *
14664  * The constraint should be given in the form
14665  * \f[
14666  * \ell \leq \sum_{i=1}^n b_i x_i + \sum_{j=1}^m (a_j y_j^2 + b_j y_j) + \sum_{k=1}^p c_k v_k w_k \leq u.
14667  * \f]
14668  *
14669  *  @note the constraint gets captured, hence at one point you have to release it using the method SCIPreleaseCons()
14670  */
SCIPcreateConsBasicQuadratic2(SCIP * scip,SCIP_CONS ** cons,const char * name,int nlinvars,SCIP_VAR ** linvars,SCIP_Real * lincoefs,int nquadvarterms,SCIP_QUADVARTERM * quadvarterms,int nbilinterms,SCIP_BILINTERM * bilinterms,SCIP_Real lhs,SCIP_Real rhs)14671 SCIP_RETCODE SCIPcreateConsBasicQuadratic2(
14672    SCIP*                 scip,               /**< SCIP data structure */
14673    SCIP_CONS**           cons,               /**< pointer to hold the created constraint */
14674    const char*           name,               /**< name of constraint */
14675    int                   nlinvars,           /**< number of linear terms (n) */
14676    SCIP_VAR**            linvars,            /**< array with variables in linear part (x_i) */
14677    SCIP_Real*            lincoefs,           /**< array with coefficients of variables in linear part (b_i) */
14678    int                   nquadvarterms,      /**< number of quadratic terms (m) */
14679    SCIP_QUADVARTERM*     quadvarterms,       /**< quadratic variable terms */
14680    int                   nbilinterms,        /**< number of bilinear terms (p) */
14681    SCIP_BILINTERM*       bilinterms,         /**< bilinear terms */
14682    SCIP_Real             lhs,                /**< constraint left hand side (ell) */
14683    SCIP_Real             rhs                 /**< constraint right hand side (u) */
14684    )
14685 {
14686    SCIP_CALL( SCIPcreateConsQuadratic2(scip, cons, name, nlinvars, linvars, lincoefs,
14687          nquadvarterms, quadvarterms, nbilinterms, bilinterms, lhs, rhs,
14688          TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE) );
14689 
14690    return SCIP_OKAY;
14691 }
14692 
14693 
14694 /** Adds a constant to the constraint function, that is, subtracts a constant from both sides */
SCIPaddConstantQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_Real constant)14695 void SCIPaddConstantQuadratic(
14696    SCIP*                 scip,               /**< SCIP data structure */
14697    SCIP_CONS*            cons,               /**< constraint */
14698    SCIP_Real             constant            /**< constant to subtract from both sides */
14699    )
14700 {
14701    SCIP_CONSDATA* consdata;
14702 
14703    assert(scip != NULL);
14704    assert(cons != NULL);
14705    assert(!SCIPisInfinity(scip, REALABS(constant)));
14706 
14707    /* nlrow and solving data (see initsol) may become invalid when changing constraint */
14708    if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPconsIsEnabled(cons) )
14709    {
14710       SCIPerrorMessage("Cannot modify enabled constraint in solving stage.\n");
14711       SCIPABORT();
14712    }
14713 
14714    consdata = SCIPconsGetData(cons);
14715    assert(consdata != NULL);
14716    assert(consdata->lhs <= consdata->rhs);
14717 
14718    if( !SCIPisInfinity(scip, -consdata->lhs) )
14719       consdata->lhs -= constant;
14720    if( !SCIPisInfinity(scip,  consdata->rhs) )
14721       consdata->rhs -= constant;
14722 
14723    if( consdata->lhs > consdata->rhs )
14724    {
14725       assert(SCIPisEQ(scip, consdata->lhs, consdata->rhs));
14726       consdata->lhs = consdata->rhs;
14727    }
14728 }
14729 
14730 /** Adds a linear variable with coefficient to a quadratic constraint. */
SCIPaddLinearVarQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real coef)14731 SCIP_RETCODE SCIPaddLinearVarQuadratic(
14732    SCIP*                 scip,               /**< SCIP data structure */
14733    SCIP_CONS*            cons,               /**< constraint */
14734    SCIP_VAR*             var,                /**< variable */
14735    SCIP_Real             coef                /**< coefficient of variable */
14736    )
14737 {
14738    assert(scip != NULL);
14739    assert(cons != NULL);
14740    assert(var  != NULL);
14741    assert(!SCIPisInfinity(scip, REALABS(coef)));
14742 
14743    /* nlrow and solving data (see initsol) may become invalid when changing constraint */
14744    if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPconsIsEnabled(cons) )
14745    {
14746       SCIPerrorMessage("Cannot modify enabled constraint in solving stage.\n");
14747       return SCIP_INVALIDCALL;
14748    }
14749 
14750    SCIP_CALL( addLinearCoef(scip, cons, var, coef) );
14751 
14752    return SCIP_OKAY;
14753 }
14754 
14755 /** Adds a quadratic variable with linear and square coefficient to a quadratic constraint. */
SCIPaddQuadVarQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real lincoef,SCIP_Real sqrcoef)14756 SCIP_RETCODE SCIPaddQuadVarQuadratic(
14757    SCIP*                 scip,               /**< SCIP data structure */
14758    SCIP_CONS*            cons,               /**< constraint */
14759    SCIP_VAR*             var,                /**< variable */
14760    SCIP_Real             lincoef,            /**< linear coefficient of variable */
14761    SCIP_Real             sqrcoef             /**< square coefficient of variable */
14762    )
14763 {
14764    assert(scip != NULL);
14765    assert(cons != NULL);
14766    assert(var  != NULL);
14767    assert(!SCIPisInfinity(scip, REALABS(lincoef)));
14768    assert(!SCIPisInfinity(scip, REALABS(sqrcoef)));
14769 
14770    /* nlrow and solving data (see initsol) may become invalid when changing constraint */
14771    if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPconsIsEnabled(cons) )
14772    {
14773       SCIPerrorMessage("Cannot modify enabled constraint in solving stage.\n");
14774       return SCIP_INVALIDCALL;
14775    }
14776 
14777    SCIP_CALL( addQuadVarTerm(scip, cons, var, lincoef, sqrcoef) );
14778 
14779    return SCIP_OKAY;
14780 }
14781 
14782 /** Adds a linear coefficient for a quadratic variable.
14783  *
14784  *  Variable will be added with square coefficient 0.0 if not existing yet.
14785  */
SCIPaddQuadVarLinearCoefQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real coef)14786 SCIP_RETCODE SCIPaddQuadVarLinearCoefQuadratic(
14787    SCIP*                 scip,               /**< SCIP data structure */
14788    SCIP_CONS*            cons,               /**< constraint */
14789    SCIP_VAR*             var,                /**< variable */
14790    SCIP_Real             coef                /**< value to add to linear coefficient of variable */
14791    )
14792 {
14793    SCIP_CONSDATA* consdata;
14794    int pos;
14795 
14796    assert(scip != NULL);
14797    assert(cons != NULL);
14798    assert(var  != NULL);
14799    assert(!SCIPisInfinity(scip, REALABS(coef)));
14800 
14801    if( SCIPisZero(scip, coef) )
14802       return SCIP_OKAY;
14803 
14804    /* nlrow and solving data (see initsol) may become invalid when changing constraint */
14805    if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPconsIsEnabled(cons) )
14806    {
14807       SCIPerrorMessage("Cannot modify enabled constraint in solving stage.\n");
14808       return SCIP_INVALIDCALL;
14809    }
14810 
14811    consdata = SCIPconsGetData(cons);
14812    assert(consdata != NULL);
14813 
14814    SCIP_CALL( consdataFindQuadVarTerm(scip, consdata, var, &pos) );
14815    if( pos < 0 )
14816    {
14817       SCIP_CALL( addQuadVarTerm(scip, cons, var, coef, 0.0) );
14818       return SCIP_OKAY;
14819    }
14820    assert(pos < consdata->nquadvars);
14821    assert(consdata->quadvarterms[pos].var == var);
14822 
14823    consdata->quadvarterms[pos].lincoef += coef;
14824 
14825    /* update flags and invalid activities */
14826    consdata->ispropagated  = FALSE;
14827    consdata->ispresolved   = consdata->ispresolved && !SCIPisZero(scip, consdata->quadvarterms[pos].lincoef);
14828 
14829    SCIPintervalSetEmpty(&consdata->quadactivitybounds);
14830    consdata->activity = SCIP_INVALID;
14831 
14832    return SCIP_OKAY;
14833 }
14834 
14835 /** Adds a square coefficient for a quadratic variable.
14836  *
14837  *  Variable will be added with linear coefficient 0.0 if not existing yet.
14838  */
SCIPaddSquareCoefQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real coef)14839 SCIP_RETCODE SCIPaddSquareCoefQuadratic(
14840    SCIP*                 scip,               /**< SCIP data structure */
14841    SCIP_CONS*            cons,               /**< constraint */
14842    SCIP_VAR*             var,                /**< variable */
14843    SCIP_Real             coef                /**< value to add to square coefficient of variable */
14844    )
14845 {
14846    SCIP_CONSDATA* consdata;
14847    int pos;
14848 
14849    assert(scip != NULL);
14850    assert(cons != NULL);
14851    assert(var  != NULL);
14852    assert(!SCIPisInfinity(scip, REALABS(coef)));
14853 
14854    if( SCIPisZero(scip, coef) )
14855       return SCIP_OKAY;
14856 
14857    /* nlrow and solving data (see initsol) may become invalid when changing constraint */
14858    if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPconsIsEnabled(cons) )
14859    {
14860       SCIPerrorMessage("Cannot modify enabled constraint in solving stage.\n");
14861       return SCIP_INVALIDCALL;
14862    }
14863 
14864    consdata = SCIPconsGetData(cons);
14865    assert(consdata != NULL);
14866 
14867    SCIP_CALL( consdataFindQuadVarTerm(scip, consdata, var, &pos) );
14868    if( pos < 0 )
14869    {
14870       SCIP_CALL( addQuadVarTerm(scip, cons, var, 0.0, coef) );
14871       return SCIP_OKAY;
14872    }
14873    assert(pos < consdata->nquadvars);
14874    assert(consdata->quadvarterms[pos].var == var);
14875 
14876    consdata->quadvarterms[pos].sqrcoef += coef;
14877 
14878    /* update flags and invalid activities */
14879    consdata->isconvex      = FALSE;
14880    consdata->isconcave     = FALSE;
14881    consdata->iscurvchecked = FALSE;
14882    consdata->ispropagated  = FALSE;
14883    consdata->ispresolved   = consdata->ispresolved && !SCIPisZero(scip, consdata->quadvarterms[pos].sqrcoef);
14884 
14885    SCIPintervalSetEmpty(&consdata->quadactivitybounds);
14886    consdata->activity = SCIP_INVALID;
14887 
14888    return SCIP_OKAY;
14889 }
14890 
14891 /** Adds a bilinear term to a quadratic constraint.
14892  *
14893  *  Variables will be added with linear and square coefficient 0.0 if not existing yet.
14894  *  If variables are equal, only the square coefficient of the variable is updated.
14895  */
SCIPaddBilinTermQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var1,SCIP_VAR * var2,SCIP_Real coef)14896 SCIP_RETCODE SCIPaddBilinTermQuadratic(
14897    SCIP*                 scip,               /**< SCIP data structure */
14898    SCIP_CONS*            cons,               /**< constraint */
14899    SCIP_VAR*             var1,               /**< first variable */
14900    SCIP_VAR*             var2,               /**< second variable */
14901    SCIP_Real             coef                /**< coefficient of bilinear term */
14902    )
14903 {
14904    SCIP_CONSDATA* consdata;
14905    int            var1pos;
14906    int            var2pos;
14907 
14908    assert(scip != NULL);
14909    assert(cons != NULL);
14910    assert(var1 != NULL);
14911    assert(var2 != NULL);
14912    assert(!SCIPisInfinity(scip, REALABS(coef)));
14913 
14914    /* nlrow and solving data (see initsol) may become invalid when changing constraint */
14915    if( SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPconsIsEnabled(cons) )
14916    {
14917       SCIPerrorMessage("Cannot modify enabled constraint in solving stage.\n");
14918       return SCIP_INVALIDCALL;
14919    }
14920 
14921    if( var1 == var2 )
14922    {
14923       SCIP_CALL( SCIPaddSquareCoefQuadratic(scip, cons, var1, coef) );
14924       return SCIP_OKAY;
14925    }
14926 
14927    consdata = SCIPconsGetData(cons);
14928    assert(consdata != NULL);
14929 
14930    SCIP_CALL( consdataFindQuadVarTerm(scip, consdata, var1, &var1pos) );
14931    if( var1pos < 0 )
14932    {
14933       SCIP_CALL( addQuadVarTerm(scip, cons, var1, 0.0, 0.0) );
14934       var1pos = consdata->nquadvars-1;
14935    }
14936 
14937    if( !consdata->quadvarssorted )
14938    {
14939       SCIP_CALL( consdataSortQuadVarTerms(scip, consdata) );
14940       /* sorting may change the position of var1 */
14941       SCIP_CALL( consdataFindQuadVarTerm(scip, consdata, var1, &var1pos) );
14942       assert(var1pos >= 0);
14943    }
14944 
14945    assert(consdata->quadvarssorted);
14946    SCIP_CALL( consdataFindQuadVarTerm(scip, consdata, var2, &var2pos) );
14947    if( var2pos < 0 )
14948    {
14949       SCIP_CALL( addQuadVarTerm(scip, cons, var2, 0.0, 0.0) );
14950       var2pos = consdata->nquadvars-1;
14951    }
14952 
14953    assert(consdata->quadvarterms[var1pos].var == var1);
14954    assert(consdata->quadvarterms[var2pos].var == var2);
14955 
14956    SCIP_CALL( addBilinearTerm(scip, cons, var1pos, var2pos, coef) );
14957 
14958    return SCIP_OKAY;
14959 }
14960 
14961 /** Gets the quadratic constraint as a nonlinear row representation. */
SCIPgetNlRowQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_NLROW ** nlrow)14962 SCIP_RETCODE SCIPgetNlRowQuadratic(
14963    SCIP*                 scip,               /**< SCIP data structure */
14964    SCIP_CONS*            cons,               /**< constraint */
14965    SCIP_NLROW**          nlrow               /**< pointer to store nonlinear row */
14966    )
14967 {
14968    SCIP_CONSDATA* consdata;
14969 
14970    assert(cons  != NULL);
14971    assert(nlrow != NULL);
14972 
14973    consdata = SCIPconsGetData(cons);
14974    assert(consdata != NULL);
14975 
14976    if( consdata->nlrow == NULL )
14977    {
14978       SCIP_CALL( createNlRow(scip, cons) );
14979    }
14980    assert(consdata->nlrow != NULL);
14981    *nlrow = consdata->nlrow;
14982 
14983    return SCIP_OKAY;
14984 }
14985 
14986 /** Gets the number of variables in the linear term of a quadratic constraint. */
SCIPgetNLinearVarsQuadratic(SCIP * scip,SCIP_CONS * cons)14987 int SCIPgetNLinearVarsQuadratic(
14988    SCIP*                 scip,               /**< SCIP data structure */
14989    SCIP_CONS*            cons                /**< constraint */
14990    )
14991 {
14992    assert(scip != NULL);
14993    assert(cons != NULL);
14994    assert(SCIPconsGetData(cons) != NULL);
14995 
14996    return SCIPconsGetData(cons)->nlinvars;
14997 }
14998 
14999 /** Gets the variables in the linear part of a quadratic constraint.
15000  *  Length is given by SCIPgetNLinearVarsQuadratic.
15001  */
SCIPgetLinearVarsQuadratic(SCIP * scip,SCIP_CONS * cons)15002 SCIP_VAR** SCIPgetLinearVarsQuadratic(
15003    SCIP*                 scip,               /**< SCIP data structure */
15004    SCIP_CONS*            cons                /**< constraint */
15005    )
15006 {
15007    assert(scip != NULL);
15008    assert(cons != NULL);
15009    assert(SCIPconsGetData(cons) != NULL);
15010 
15011    return SCIPconsGetData(cons)->linvars;
15012 }
15013 
15014 /** Gets the coefficients in the linear part of a quadratic constraint.
15015  *  Length is given by SCIPgetNLinearVarsQuadratic.
15016  */
SCIPgetCoefsLinearVarsQuadratic(SCIP * scip,SCIP_CONS * cons)15017 SCIP_Real* SCIPgetCoefsLinearVarsQuadratic(
15018    SCIP*                 scip,               /**< SCIP data structure */
15019    SCIP_CONS*            cons                /**< constraint */
15020    )
15021 {
15022    assert(scip != NULL);
15023    assert(cons != NULL);
15024    assert(SCIPconsGetData(cons) != NULL);
15025 
15026    return SCIPconsGetData(cons)->lincoefs;
15027 }
15028 
15029 /** Gets the number of quadratic variable terms of a quadratic constraint.
15030  */
SCIPgetNQuadVarTermsQuadratic(SCIP * scip,SCIP_CONS * cons)15031 int SCIPgetNQuadVarTermsQuadratic(
15032    SCIP*                 scip,               /**< SCIP data structure */
15033    SCIP_CONS*            cons                /**< constraint */
15034    )
15035 {
15036    assert(scip != NULL);
15037    assert(cons != NULL);
15038    assert(SCIPconsGetData(cons) != NULL);
15039 
15040    return SCIPconsGetData(cons)->nquadvars;
15041 }
15042 
15043 /** Gets the quadratic variable terms of a quadratic constraint.
15044  *  Length is given by SCIPgetNQuadVarTermsQuadratic.
15045  */
SCIPgetQuadVarTermsQuadratic(SCIP * scip,SCIP_CONS * cons)15046 SCIP_QUADVARTERM* SCIPgetQuadVarTermsQuadratic(
15047    SCIP*                 scip,               /**< SCIP data structure */
15048    SCIP_CONS*            cons                /**< constraint */
15049    )
15050 {
15051    assert(scip != NULL);
15052    assert(cons != NULL);
15053    assert(SCIPconsGetData(cons) != NULL);
15054 
15055    return SCIPconsGetData(cons)->quadvarterms;
15056 }
15057 
15058 /** Ensures that quadratic variable terms are sorted. */
SCIPsortQuadVarTermsQuadratic(SCIP * scip,SCIP_CONS * cons)15059 SCIP_RETCODE SCIPsortQuadVarTermsQuadratic(
15060    SCIP*                 scip,               /**< SCIP data structure */
15061    SCIP_CONS*            cons                /**< constraint */
15062    )
15063 {
15064    assert(scip != NULL);
15065    assert(cons != NULL);
15066    assert(SCIPconsGetData(cons) != NULL);
15067 
15068    SCIP_CALL( consdataSortQuadVarTerms(scip, SCIPconsGetData(cons)) );
15069 
15070    return SCIP_OKAY;
15071 }
15072 
15073 /** Finds the position of a quadratic variable term for a given variable.
15074  *
15075  *  @note If the quadratic variable terms have not been sorted before, then a search may reorder the current order of the terms.
15076  */
SCIPfindQuadVarTermQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,int * pos)15077 SCIP_RETCODE SCIPfindQuadVarTermQuadratic(
15078    SCIP*                 scip,               /**< SCIP data structure */
15079    SCIP_CONS*            cons,               /**< constraint */
15080    SCIP_VAR*             var,                /**< variable to search for */
15081    int*                  pos                 /**< buffer to store position of quadvarterm for var, or -1 if not found */
15082    )
15083 {
15084    assert(scip != NULL);
15085    assert(cons != NULL);
15086    assert(SCIPconsGetData(cons) != NULL);
15087    assert(var != NULL);
15088    assert(pos != NULL);
15089 
15090    SCIP_CALL( consdataFindQuadVarTerm(scip, SCIPconsGetData(cons), var, pos) );
15091 
15092    return SCIP_OKAY;
15093 }
15094 
15095 /** Gets the number of bilinear terms of a quadratic constraint. */
SCIPgetNBilinTermsQuadratic(SCIP * scip,SCIP_CONS * cons)15096 int SCIPgetNBilinTermsQuadratic(
15097    SCIP*                 scip,               /**< SCIP data structure */
15098    SCIP_CONS*            cons                /**< constraint */
15099    )
15100 {
15101    assert(scip != NULL);
15102    assert(cons != NULL);
15103    assert(SCIPconsGetData(cons) != NULL);
15104 
15105    return SCIPconsGetData(cons)->nbilinterms;
15106 }
15107 
15108 /** Gets the bilinear terms of a quadratic constraint.
15109  *  Length is given by SCIPgetNBilinTermQuadratic.
15110  */
SCIPgetBilinTermsQuadratic(SCIP * scip,SCIP_CONS * cons)15111 SCIP_BILINTERM* SCIPgetBilinTermsQuadratic(
15112    SCIP*                 scip,               /**< SCIP data structure */
15113    SCIP_CONS*            cons                /**< constraint */
15114    )
15115 {
15116    assert(scip != NULL);
15117    assert(cons != NULL);
15118    assert(SCIPconsGetData(cons) != NULL);
15119 
15120    return SCIPconsGetData(cons)->bilinterms;
15121 }
15122 
15123 /** Gets the left hand side of a quadratic constraint. */
SCIPgetLhsQuadratic(SCIP * scip,SCIP_CONS * cons)15124 SCIP_Real SCIPgetLhsQuadratic(
15125    SCIP*                 scip,               /**< SCIP data structure */
15126    SCIP_CONS*            cons                /**< constraint */
15127    )
15128 {
15129    assert(scip != NULL);
15130    assert(cons != NULL);
15131    assert(SCIPconsGetData(cons) != NULL);
15132 
15133    return SCIPconsGetData(cons)->lhs;
15134 }
15135 
15136 /** Gets the right hand side of a quadratic constraint. */
SCIPgetRhsQuadratic(SCIP * scip,SCIP_CONS * cons)15137 SCIP_Real SCIPgetRhsQuadratic(
15138    SCIP*                 scip,               /**< SCIP data structure */
15139    SCIP_CONS*            cons                /**< constraint */
15140    )
15141 {
15142    assert(scip != NULL);
15143    assert(cons != NULL);
15144    assert(SCIPconsGetData(cons) != NULL);
15145 
15146    return SCIPconsGetData(cons)->rhs;
15147 }
15148 
15149 /** get index of a variable in linvars that may be decreased without making any other constraint infeasible, or -1 if none */
SCIPgetLinvarMayDecreaseQuadratic(SCIP * scip,SCIP_CONS * cons)15150 int SCIPgetLinvarMayDecreaseQuadratic(
15151    SCIP*                 scip,               /**< SCIP data structure */
15152    SCIP_CONS*            cons                /**< constraint */
15153    )
15154 {
15155    SCIP_CONSDATA*     consdata;
15156 
15157    assert(scip != NULL);
15158    assert(cons != NULL);
15159 
15160    consdata = SCIPconsGetData(cons);
15161    assert(consdata != NULL);
15162 
15163    /* check for a linear variable that can be increase or decreased without harming feasibility */
15164    consdataFindUnlockedLinearVar(scip, consdata);
15165 
15166    return consdata->linvar_maydecrease;
15167 }
15168 
15169 /** get index of a variable in linvars that may be increased without making any other constraint infeasible, or -1 if none */
SCIPgetLinvarMayIncreaseQuadratic(SCIP * scip,SCIP_CONS * cons)15170 int SCIPgetLinvarMayIncreaseQuadratic(
15171    SCIP*                 scip,               /**< SCIP data structure */
15172    SCIP_CONS*            cons                /**< constraint */
15173    )
15174 {
15175    SCIP_CONSDATA*     consdata;
15176 
15177    assert(scip != NULL);
15178    assert(cons != NULL);
15179 
15180    consdata = SCIPconsGetData(cons);
15181    assert(consdata != NULL);
15182 
15183    /* check for a linear variable that can be increase or decreased without harming feasibility */
15184    consdataFindUnlockedLinearVar(scip, consdata);
15185 
15186    return consdata->linvar_mayincrease;
15187 }
15188 
15189 /** Check the quadratic function of a quadratic constraint for its semi-definiteness, if not done yet. */
SCIPcheckCurvatureQuadratic(SCIP * scip,SCIP_CONS * cons)15190 SCIP_RETCODE SCIPcheckCurvatureQuadratic(
15191    SCIP*                 scip,               /**< SCIP data structure */
15192    SCIP_CONS*            cons                /**< constraint */
15193    )
15194 {
15195    assert(scip != NULL);
15196    assert(cons != NULL);
15197 
15198    SCIP_CALL( checkCurvature(scip, cons, TRUE) );
15199 
15200    return SCIP_OKAY;
15201 }
15202 
15203 /** Indicates whether the quadratic function of a quadratic constraint is (known to be) convex. */
SCIPisConvexQuadratic(SCIP * scip,SCIP_CONS * cons)15204 SCIP_Bool SCIPisConvexQuadratic(
15205    SCIP*                 scip,               /**< SCIP data structure */
15206    SCIP_CONS*            cons                /**< constraint */
15207    )
15208 {
15209    SCIP_CONSDATA* consdata;
15210    SCIP_Bool determined;
15211    SCIP_Bool isconvex;
15212    SCIP_Bool isconcave;
15213 
15214    assert(scip != NULL);
15215    assert(cons != NULL);
15216    assert(SCIPconsGetData(cons) != NULL);
15217 
15218    consdata = SCIPconsGetData(cons);
15219    assert(consdata != NULL);
15220 
15221    if( consdata->iscurvchecked )
15222       return consdata->isconvex;
15223 
15224    checkCurvatureEasy(scip, cons, NULL, &determined, TRUE, &isconvex, &isconcave, &consdata->maxnonconvexity);
15225 
15226    if( determined )
15227    {
15228       consdata->isconvex = isconvex;
15229       consdata->isconcave = isconcave;
15230       consdata->iscurvchecked = TRUE;
15231    }
15232 
15233    return isconvex;
15234 }
15235 
15236 /** Indicates whether the quadratic function of a quadratic constraint is (known to be) concave. */
SCIPisConcaveQuadratic(SCIP * scip,SCIP_CONS * cons)15237 SCIP_Bool SCIPisConcaveQuadratic(
15238    SCIP*                 scip,               /**< SCIP data structure */
15239    SCIP_CONS*            cons                /**< constraint */
15240    )
15241 {
15242    SCIP_CONSDATA* consdata;
15243    SCIP_Bool determined;
15244    SCIP_Bool isconvex;
15245    SCIP_Bool isconcave;
15246 
15247    assert(scip != NULL);
15248    assert(cons != NULL);
15249    assert(SCIPconsGetData(cons) != NULL);
15250 
15251    consdata = SCIPconsGetData(cons);
15252    assert(consdata != NULL);
15253 
15254    if( consdata->iscurvchecked )
15255       return consdata->isconcave;
15256 
15257    checkCurvatureEasy(scip, cons, NULL, &determined, TRUE, &isconvex, &isconcave, &consdata->maxnonconvexity);
15258 
15259    if( determined )
15260    {
15261       consdata->isconvex = isconvex;
15262       consdata->isconcave = isconcave;
15263       consdata->iscurvchecked = TRUE;
15264    }
15265 
15266    return isconcave;
15267 }
15268 
15269 /** Checks and indicates whether the quadratic constraint is convex. */
SCIPisConvexConsQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_HASHMAP * assumevarfixed,SCIP_Bool * result)15270 SCIP_RETCODE SCIPisConvexConsQuadratic(
15271    SCIP*                 scip,               /**< SCIP data structure */
15272    SCIP_CONS*            cons,               /**< constraint */
15273    SCIP_HASHMAP*         assumevarfixed,     /**< hashmap containing variables that should be assumed to be fixed, or NULL */
15274    SCIP_Bool*            result              /**< buffer where to store whether constraint is convex (under given variable fixing) */
15275    )
15276 {
15277    SCIP_CONSDATA* consdata;
15278    SCIP_Real maxnonconvexity;
15279    SCIP_Bool isconvex;
15280    SCIP_Bool isconcave;
15281    SCIP_Bool determined;
15282 
15283    assert(cons != NULL);
15284    assert(result != NULL);
15285 
15286    consdata = SCIPconsGetData(cons);
15287    assert(consdata != NULL);
15288 
15289    if( consdata->iscurvchecked )
15290    {
15291       /* if already checked and convex (w.r.t. all vars), then will also be convex after fixing some vars */
15292       if( consdata->isconvex && SCIPisInfinity(scip, -consdata->lhs) )
15293       {
15294          *result = TRUE;
15295          return SCIP_OKAY;
15296       }
15297       if( consdata->isconcave && SCIPisInfinity(scip, consdata->rhs) )
15298       {
15299          *result = TRUE;
15300          return SCIP_OKAY;
15301       }
15302 
15303       /* if no variables to be fixed, then previous negative result will hold */
15304       if( assumevarfixed == NULL )
15305       {
15306          *result = FALSE;
15307          return SCIP_OKAY;
15308       }
15309    }
15310 
15311    checkCurvatureEasy(scip, cons, assumevarfixed, &determined, TRUE, &isconvex, &isconcave, &maxnonconvexity);
15312    if( !determined )
15313    {
15314       SCIP_CALL( checkCurvatureExpensive(scip, cons, assumevarfixed, &isconvex, &isconcave, &maxnonconvexity) );
15315    }
15316 
15317    if( assumevarfixed == NULL )
15318    {
15319       consdata->isconvex = isconvex;
15320       consdata->isconcave = isconcave;
15321       consdata->maxnonconvexity = maxnonconvexity;
15322       consdata->iscurvchecked = TRUE;
15323    }
15324 
15325    if( (isconvex && SCIPisInfinity(scip, -consdata->lhs)) || (isconcave && SCIPisInfinity(scip, consdata->rhs)) )
15326    {
15327       *result = TRUE;
15328       return SCIP_OKAY;
15329    }
15330 
15331    *result = FALSE;
15332    return SCIP_OKAY;
15333 }
15334 
15335 /** Computes the violation of a constraint by a solution */
SCIPgetViolationQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_SOL * sol,SCIP_Real * violation)15336 SCIP_RETCODE SCIPgetViolationQuadratic(
15337    SCIP*                 scip,               /**< SCIP data structure */
15338    SCIP_CONS*            cons,               /**< constraint */
15339    SCIP_SOL*             sol,                /**< solution which violation to calculate, or NULL for LP solution */
15340    SCIP_Real*            violation           /**< pointer to store violation of constraint */
15341    )
15342 {
15343    SCIP_CONSDATA* consdata;
15344    SCIP_Bool solviolbounds;
15345 
15346    assert(scip != NULL);
15347    assert(cons != NULL);
15348    assert(violation != NULL);
15349 
15350    SCIP_CALL( computeViolation(scip, cons, sol, &solviolbounds) );
15351    /* we don't care here whether the solution violated variable bounds */
15352 
15353    consdata = SCIPconsGetData(cons);
15354    assert(consdata != NULL);
15355 
15356    *violation = MAX(consdata->lhsviol, consdata->rhsviol);
15357 
15358    return SCIP_OKAY;  /*lint !e438*/
15359 }
15360 
15361 /** Indicates whether the quadratic constraint is local w.r.t. the current local bounds.
15362  *
15363  *  That is, checks whether each variable with a square term is fixed and for each bilinear term at least one variable is fixed.
15364  */
SCIPisLinearLocalQuadratic(SCIP * scip,SCIP_CONS * cons)15365 SCIP_Bool SCIPisLinearLocalQuadratic(
15366    SCIP*                 scip,               /**< SCIP data structure */
15367    SCIP_CONS*            cons                /**< constraint */
15368    )
15369 {
15370    SCIP_CONSDATA* consdata;
15371    SCIP_VAR* var1;
15372    SCIP_VAR* var2;
15373    int i;
15374 
15375    assert(scip != NULL);
15376    assert(cons != NULL);
15377 
15378    consdata = SCIPconsGetData(cons);
15379    assert(consdata != NULL);
15380 
15381    /* check all square terms */
15382    for( i = 0; i < consdata->nquadvars; ++i )
15383    {
15384       if( consdata->quadvarterms[i].sqrcoef == 0.0 )
15385          continue;
15386 
15387       var1 = consdata->quadvarterms[i].var;
15388       assert(var1 != NULL);
15389 
15390       if( !SCIPisRelEQ(scip, SCIPvarGetLbLocal(var1), SCIPvarGetUbLocal(var1)) )
15391          return FALSE;
15392    }
15393 
15394    for( i = 0; i < consdata->nbilinterms; ++i )
15395    {
15396       var1 = consdata->bilinterms[i].var1;
15397       var2 = consdata->bilinterms[i].var2;
15398 
15399       assert(var1 != NULL);
15400       assert(var2 != NULL);
15401 
15402       if( !SCIPisRelEQ(scip, SCIPvarGetLbLocal(var1), SCIPvarGetUbLocal(var1)) &&
15403          ! SCIPisRelEQ(scip, SCIPvarGetLbLocal(var2), SCIPvarGetUbLocal(var2)) )
15404          return FALSE;
15405    }
15406 
15407    return TRUE;
15408 }
15409 
15410 /** Adds the constraint to an NLPI problem. */
SCIPaddToNlpiProblemQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_NLPI * nlpi,SCIP_NLPIPROBLEM * nlpiprob,SCIP_HASHMAP * scipvar2nlpivar,SCIP_Bool names)15411 SCIP_RETCODE SCIPaddToNlpiProblemQuadratic(
15412    SCIP*                 scip,               /**< SCIP data structure */
15413    SCIP_CONS*            cons,               /**< constraint */
15414    SCIP_NLPI*            nlpi,               /**< interface to NLP solver */
15415    SCIP_NLPIPROBLEM*     nlpiprob,           /**< NLPI problem where to add constraint */
15416    SCIP_HASHMAP*         scipvar2nlpivar,    /**< mapping from SCIP variables to variable indices in NLPI */
15417    SCIP_Bool             names               /**< whether to pass constraint names to NLPI */
15418    )
15419 {
15420    SCIP_CONSDATA* consdata;
15421    int            nlininds;
15422    int*           lininds;
15423    SCIP_Real*     linvals;
15424    int            nquadelems;
15425    SCIP_QUADELEM* quadelems;
15426    SCIP_VAR*      othervar;
15427    const char*    name;
15428    int            j;
15429    int            l;
15430    int            lincnt;
15431    int            quadcnt;
15432    int            idx1;
15433    int            idx2;
15434 
15435    assert(scip != NULL);
15436    assert(cons != NULL);
15437    assert(nlpi != NULL);
15438    assert(nlpiprob != NULL);
15439    assert(scipvar2nlpivar != NULL);
15440 
15441    consdata = SCIPconsGetData(cons);
15442    assert(consdata != NULL);
15443 
15444    /* count nonzeros in quadratic part */
15445    nlininds = consdata->nlinvars;
15446    nquadelems = consdata->nbilinterms;
15447    for( j = 0; j < consdata->nquadvars; ++j )
15448    {
15449       if( consdata->quadvarterms[j].sqrcoef != 0.0 )
15450          ++nquadelems;
15451       if( consdata->quadvarterms[j].lincoef != 0.0 )
15452          ++nlininds;
15453    }
15454 
15455    /* setup linear part */
15456    lininds = NULL;
15457    linvals = NULL;
15458    lincnt  = 0;
15459    if( nlininds > 0 )
15460    {
15461       SCIP_CALL( SCIPallocBufferArray(scip, &lininds, nlininds) );
15462       SCIP_CALL( SCIPallocBufferArray(scip, &linvals, nlininds) );
15463 
15464       for( j = 0; j < consdata->nlinvars; ++j )
15465       {
15466          linvals[j] = consdata->lincoefs[j];
15467          assert(SCIPhashmapExists(scipvar2nlpivar, consdata->linvars[j]));
15468          lininds[j] = SCIPhashmapGetImageInt(scipvar2nlpivar, consdata->linvars[j]);
15469       }
15470 
15471       lincnt = consdata->nlinvars;
15472    }
15473 
15474    /* setup quadratic part */
15475    quadelems = NULL;
15476    if( nquadelems > 0 )
15477    {
15478       SCIP_CALL( SCIPallocBufferArray(scip, &quadelems, nquadelems) );
15479    }
15480    quadcnt = 0;
15481 
15482    for( j = 0; j < consdata->nquadvars; ++j )
15483    {
15484       assert(SCIPhashmapExists(scipvar2nlpivar, consdata->quadvarterms[j].var));
15485       idx1 = SCIPhashmapGetImageInt(scipvar2nlpivar, consdata->quadvarterms[j].var);
15486       if( consdata->quadvarterms[j].lincoef != 0.0 )
15487       {
15488          assert(lininds != NULL);
15489          assert(linvals != NULL);
15490          /* coverity[var_deref_op] */
15491          lininds[lincnt] = idx1;
15492          linvals[lincnt] = consdata->quadvarterms[j].lincoef;
15493          ++lincnt;
15494       }
15495 
15496       if( consdata->quadvarterms[j].sqrcoef != 0.0 )
15497       {
15498          assert(quadcnt < nquadelems);
15499          assert(quadelems != NULL);
15500          /* coverity[var_deref_op] */
15501          quadelems[quadcnt].idx1 = idx1;
15502          quadelems[quadcnt].idx2 = idx1;
15503          quadelems[quadcnt].coef = consdata->quadvarterms[j].sqrcoef;
15504          ++quadcnt;
15505       }
15506 
15507       for( l = 0; l < consdata->quadvarterms[j].nadjbilin; ++l )
15508       {
15509          othervar = consdata->bilinterms[consdata->quadvarterms[j].adjbilin[l]].var2;
15510          /* if othervar is on position 2, then we process this bilinear term later (or it was processed already) */
15511          if( othervar == consdata->quadvarterms[j].var )
15512             continue;
15513 
15514          assert(quadcnt < nquadelems);
15515          assert(quadelems != NULL);
15516          assert(SCIPhashmapExists(scipvar2nlpivar, othervar));
15517          idx2 = SCIPhashmapGetImageInt(scipvar2nlpivar, othervar);
15518          /* coverity[var_deref_op] */
15519          quadelems[quadcnt].idx1 = MIN(idx1, idx2);
15520          quadelems[quadcnt].idx2 = MAX(idx1, idx2);
15521          quadelems[quadcnt].coef = consdata->bilinterms[consdata->quadvarterms[j].adjbilin[l]].coef;
15522          ++quadcnt;
15523       }
15524    }
15525 
15526    assert(quadcnt == nquadelems);
15527    assert(lincnt  == nlininds);
15528 
15529    name = names ? SCIPconsGetName(cons) : NULL;
15530 
15531    SCIP_CALL( SCIPnlpiAddConstraints(nlpi, nlpiprob, 1,
15532          &consdata->lhs, &consdata->rhs,
15533          &nlininds, &lininds, &linvals ,
15534          &nquadelems, &quadelems,
15535          NULL, NULL, &name) );
15536 
15537    SCIPfreeBufferArrayNull(scip, &quadelems);
15538    SCIPfreeBufferArrayNull(scip, &lininds);
15539    SCIPfreeBufferArrayNull(scip, &linvals);
15540 
15541    return SCIP_OKAY;
15542 }
15543 
15544 
15545 /** sets the left hand side of a quadratic constraint
15546  *
15547  *  @note This method may only be called during problem creation stage for an original constraint.
15548  */
SCIPchgLhsQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_Real lhs)15549 SCIP_RETCODE SCIPchgLhsQuadratic(
15550    SCIP*                 scip,               /**< SCIP data structure */
15551    SCIP_CONS*            cons,               /**< constraint data */
15552    SCIP_Real             lhs                 /**< new left hand side */
15553    )
15554 {
15555    SCIP_CONSDATA* consdata;
15556 
15557    assert(scip != NULL);
15558    assert(cons != NULL);
15559    assert(!SCIPisInfinity(scip, lhs));
15560 
15561    if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
15562    {
15563       SCIPerrorMessage("constraint is not quadratic\n");
15564       return SCIP_INVALIDDATA;
15565    }
15566 
15567    if( SCIPgetStage(scip) > SCIP_STAGE_PROBLEM || !SCIPconsIsOriginal(cons) )
15568    {
15569       SCIPerrorMessage("method may only be called during problem creation stage for original constraints\n");
15570       return SCIP_INVALIDDATA;
15571    }
15572 
15573    consdata = SCIPconsGetData(cons);
15574    assert(consdata != NULL);
15575    assert(!SCIPisInfinity(scip, consdata->lhs));
15576 
15577    /* adjust value to not be smaller than -inf */
15578    if( SCIPisInfinity(scip, -lhs) )
15579       lhs = -SCIPinfinity(scip);
15580 
15581    /* check for lhs <= rhs */
15582    if( !SCIPisLE(scip, lhs, consdata->rhs) )
15583       return SCIP_INVALIDDATA;
15584 
15585    consdata->lhs = lhs;
15586 
15587    return SCIP_OKAY;
15588 }
15589 
15590 /** sets the right hand side of a quadratic constraint
15591  *
15592  *  @note This method may only be called during problem creation stage for an original constraint.
15593  */
SCIPchgRhsQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_Real rhs)15594 SCIP_RETCODE SCIPchgRhsQuadratic(
15595    SCIP*                 scip,               /**< SCIP data structure */
15596    SCIP_CONS*            cons,               /**< constraint data */
15597    SCIP_Real             rhs                 /**< new right hand side */
15598    )
15599 {
15600    SCIP_CONSDATA* consdata;
15601 
15602    assert(scip != NULL);
15603    assert(cons != NULL);
15604    assert(!SCIPisInfinity(scip, -rhs));
15605 
15606    if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
15607    {
15608       SCIPerrorMessage("constraint is not quadratic\n");
15609       return SCIP_INVALIDDATA;
15610    }
15611 
15612    if( SCIPgetStage(scip) > SCIP_STAGE_PROBLEM || !SCIPconsIsOriginal(cons) )
15613    {
15614       SCIPerrorMessage("method may only be called during problem creation stage for original constraints\n");
15615       return SCIP_INVALIDDATA;
15616    }
15617 
15618    consdata = SCIPconsGetData(cons);
15619    assert(consdata != NULL);
15620    assert(!SCIPisInfinity(scip, -consdata->rhs));
15621 
15622    /* adjust value to not be greater than inf */
15623    if( SCIPisInfinity(scip, rhs) )
15624       rhs = SCIPinfinity(scip);
15625 
15626    /* check for lhs <= rhs */
15627    if( !SCIPisLE(scip, consdata->lhs, rhs) )
15628       return SCIP_INVALIDDATA;
15629 
15630    consdata->rhs = rhs;
15631 
15632    return SCIP_OKAY;
15633 }
15634 
15635 /** gets the feasibility of the quadratic constraint in the given solution */
SCIPgetFeasibilityQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_SOL * sol,SCIP_Real * feasibility)15636 SCIP_RETCODE SCIPgetFeasibilityQuadratic(
15637    SCIP*                 scip,               /**< SCIP data structure */
15638    SCIP_CONS*            cons,               /**< constraint data */
15639    SCIP_SOL*             sol,                /**< solution, or NULL to use current node's solution */
15640    SCIP_Real*            feasibility         /**< pointer to store the feasibility */
15641    )
15642 {
15643    SCIP_CONSDATA* consdata;
15644    SCIP_Bool solviolbounds;
15645 
15646    assert(scip != NULL);
15647    assert(cons != NULL);
15648    assert(feasibility != NULL);
15649 
15650    if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
15651    {
15652       SCIPerrorMessage("constraint is not quadratic\n");
15653       SCIPABORT();
15654    }
15655 
15656    SCIP_CALL( computeViolation(scip, cons, sol, &solviolbounds) );
15657 
15658    consdata = SCIPconsGetData(cons);
15659    assert(consdata != NULL);
15660 
15661    if( SCIPisInfinity(scip, consdata->rhs) && SCIPisInfinity(scip, -consdata->lhs) )
15662       *feasibility = SCIPinfinity(scip);
15663    else if( SCIPisInfinity(scip, -consdata->lhs) )
15664       *feasibility = (consdata->rhs - consdata->activity);
15665    else if( SCIPisInfinity(scip, consdata->rhs) )
15666       *feasibility = (consdata->activity - consdata->lhs);
15667    else
15668    {
15669       assert(!SCIPisInfinity(scip, -consdata->rhs));
15670       assert(!SCIPisInfinity(scip, consdata->lhs));
15671       *feasibility = MIN( consdata->rhs - consdata->activity, consdata->activity - consdata->lhs );
15672    }
15673 
15674    return SCIP_OKAY; /*lint !e438*/
15675 }
15676 
15677 /** gets the activity of the quadratic constraint in the given solution */
SCIPgetActivityQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_SOL * sol,SCIP_Real * activity)15678 SCIP_RETCODE SCIPgetActivityQuadratic(
15679    SCIP*                 scip,               /**< SCIP data structure */
15680    SCIP_CONS*            cons,               /**< constraint data */
15681    SCIP_SOL*             sol,                /**< solution, or NULL to use current node's solution */
15682    SCIP_Real*            activity            /**< pointer to store the activity */
15683    )
15684 {
15685    SCIP_CONSDATA* consdata;
15686    SCIP_Bool solviolbounds;
15687 
15688    assert(scip != NULL);
15689    assert(cons != NULL);
15690    assert(activity != NULL);
15691 
15692    if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
15693    {
15694       SCIPerrorMessage("constraint is not quadratic\n");
15695       SCIPABORT();
15696    }
15697 
15698    SCIP_CALL( computeViolation(scip, cons, sol, &solviolbounds) );
15699 
15700    consdata = SCIPconsGetData(cons);
15701    assert(consdata != NULL);
15702 
15703    *activity = consdata->activity;
15704 
15705    return SCIP_OKAY; /*lint !e438*/
15706 }
15707 
15708 /** changes the linear coefficient value for a given quadratic variable in a quadratic constraint data; if not
15709  *  available, it adds it
15710  *
15711  *  @note this is only allowed for original constraints and variables in problem creation stage
15712  */
SCIPchgLinearCoefQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real coef)15713 SCIP_RETCODE SCIPchgLinearCoefQuadratic(
15714    SCIP*                 scip,               /**< SCIP data structure */
15715    SCIP_CONS*            cons,               /**< constraint data */
15716    SCIP_VAR*             var,                /**< quadratic variable */
15717    SCIP_Real             coef                /**< new coefficient */
15718    )
15719 {
15720    SCIP_CONSDATA* consdata;
15721    SCIP_Bool found;
15722    int i;
15723 
15724    assert(scip != NULL);
15725    assert(cons != NULL);
15726    assert(var != NULL);
15727 
15728    if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0  )
15729    {
15730       SCIPerrorMessage("constraint is not quadratic\n");
15731       return SCIP_INVALIDDATA;
15732    }
15733 
15734    if( SCIPgetStage(scip) > SCIP_STAGE_PROBLEM || !SCIPconsIsOriginal(cons) || !SCIPvarIsOriginal(var) )
15735    {
15736       SCIPerrorMessage("method may only be called during problem creation stage for original constraints and variables\n");
15737       return SCIP_INVALIDDATA;
15738    }
15739 
15740    consdata =  SCIPconsGetData(cons);
15741    assert(consdata != NULL);
15742 
15743    /* check all quadratic variables */
15744    found = FALSE;
15745    for( i = 0; i < consdata->nquadvars; ++i )
15746    {
15747       if( var == consdata->quadvarterms[i].var )
15748       {
15749          if( found || SCIPisZero(scip, coef) )
15750          {
15751             consdata->quadvarterms[i].lincoef = 0.0;
15752 
15753             /* remember to merge quadratic variable terms */
15754             consdata->quadvarsmerged = FALSE;
15755          }
15756          else
15757             consdata->quadvarterms[i].lincoef = coef;
15758 
15759          found = TRUE;
15760       }
15761    }
15762 
15763    /* check all linear variables */
15764    i = 0;
15765    while( i < consdata->nlinvars )
15766    {
15767       if( var == consdata->linvars[i] )
15768       {
15769          if( found || SCIPisZero(scip, coef) )
15770          {
15771             SCIP_CALL( delLinearCoefPos(scip, cons, i) );
15772 
15773             /* decrease i by one since otherwise we would skip the coefficient which has been switched to position i */
15774             i--;
15775          }
15776          else
15777          {
15778             SCIP_CALL( chgLinearCoefPos(scip, cons, i, coef) );
15779          }
15780 
15781          found = TRUE;
15782       }
15783       i++;
15784    }
15785 
15786    /* add linear term if necessary */
15787    if( !found && !SCIPisZero(scip, coef) )
15788    {
15789       SCIP_CALL( addLinearCoef(scip, cons, var, coef) );
15790    }
15791 
15792    consdata->ispropagated = FALSE;
15793    consdata->ispresolved = FALSE;
15794 
15795    SCIPintervalSetEmpty(&consdata->quadactivitybounds);
15796    consdata->activity = SCIP_INVALID;
15797 
15798    return SCIP_OKAY;
15799 }
15800 
15801 /** changes the square coefficient value for a given quadratic variable in a quadratic constraint data; if not
15802  *  available, it adds it
15803  *
15804  *  @note this is only allowed for original constraints and variables in problem creation stage
15805  */
SCIPchgSquareCoefQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var,SCIP_Real coef)15806 SCIP_RETCODE SCIPchgSquareCoefQuadratic(
15807    SCIP*                 scip,               /**< SCIP data structure */
15808    SCIP_CONS*            cons,               /**< constraint data */
15809    SCIP_VAR*             var,                /**< quadratic variable */
15810    SCIP_Real             coef                /**< new coefficient */
15811    )
15812 {
15813    SCIP_CONSDATA* consdata;
15814    SCIP_Bool found;
15815    int i;
15816 
15817    assert(scip != NULL);
15818    assert(cons != NULL);
15819    assert(var != NULL);
15820    assert(!SCIPisInfinity(scip, REALABS(coef)));
15821 
15822    if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
15823    {
15824       SCIPerrorMessage("constraint is not quadratic\n");
15825       return SCIP_INVALIDDATA;
15826    }
15827 
15828    if( SCIPgetStage(scip) > SCIP_STAGE_PROBLEM || !SCIPconsIsOriginal(cons) || !SCIPvarIsOriginal(var) )
15829    {
15830       SCIPerrorMessage("method may only be called during problem creation stage for original constraints and variables\n");
15831       return SCIP_INVALIDDATA;
15832    }
15833 
15834    consdata = SCIPconsGetData(cons);
15835    assert(consdata != NULL);
15836 
15837    /* find the quadratic variable and change its quadratic coefficient */
15838    found = FALSE;
15839    for( i = 0; i < consdata->nquadvars; ++i )
15840    {
15841       if( var == consdata->quadvarterms[i].var )
15842       {
15843          consdata->quadvarterms[i].sqrcoef = (found || SCIPisZero(scip, coef)) ? 0.0 : coef;
15844          found = TRUE;
15845       }
15846    }
15847 
15848    /* add bilinear term if necessary */
15849    if( !found && !SCIPisZero(scip, coef) )
15850    {
15851       SCIP_CALL( addQuadVarTerm(scip, cons, var, 0.0, coef) );
15852    }
15853 
15854    /* update flags and invalidate activities */
15855    consdata->isconvex = FALSE;
15856    consdata->isconcave = FALSE;
15857    consdata->iscurvchecked = FALSE;
15858    consdata->ispropagated = FALSE;
15859    consdata->ispresolved = FALSE;
15860 
15861    SCIPintervalSetEmpty(&consdata->quadactivitybounds);
15862    consdata->activity = SCIP_INVALID;
15863 
15864    /* remember to merge quadratic variable terms */
15865    consdata->quadvarsmerged = FALSE;
15866 
15867    return SCIP_OKAY;
15868 }
15869 
15870 /** changes the bilinear coefficient value for a given quadratic variable in a quadratic constraint data; if not
15871  *  available, it adds it
15872  *
15873  *  @note this is only allowed for original constraints and variables in problem creation stage
15874  */
SCIPchgBilinCoefQuadratic(SCIP * scip,SCIP_CONS * cons,SCIP_VAR * var1,SCIP_VAR * var2,SCIP_Real coef)15875 SCIP_RETCODE SCIPchgBilinCoefQuadratic(
15876    SCIP*                 scip,               /**< SCIP data structure */
15877    SCIP_CONS*            cons,               /**< constraint */
15878    SCIP_VAR*             var1,               /**< first variable */
15879    SCIP_VAR*             var2,               /**< second variable */
15880    SCIP_Real             coef                /**< coefficient of bilinear term */
15881    )
15882 {
15883    SCIP_CONSDATA* consdata;
15884    SCIP_Bool found;
15885    int i;
15886 
15887    assert(scip != NULL);
15888    assert(cons != NULL);
15889    assert(var1 != NULL);
15890    assert(var2 != NULL);
15891    assert(!SCIPisInfinity(scip, REALABS(coef)));
15892 
15893    if( strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(cons)), CONSHDLR_NAME) != 0 )
15894    {
15895       SCIPerrorMessage("constraint is not quadratic\n");
15896       return SCIP_INVALIDDATA;
15897    }
15898 
15899    if( SCIPgetStage(scip) > SCIP_STAGE_PROBLEM || !SCIPconsIsOriginal(cons) || !SCIPvarIsOriginal(var1) || !SCIPvarIsOriginal(var2) )
15900    {
15901       SCIPerrorMessage("method may only be called during problem creation stage for original constraints and variables\n");
15902       return SCIP_INVALIDDATA;
15903    }
15904 
15905    if( var1 == var2 )
15906    {
15907       SCIP_CALL( SCIPchgSquareCoefQuadratic(scip, cons, var1, coef) );
15908       return SCIP_OKAY;
15909    }
15910 
15911    consdata = SCIPconsGetData(cons);
15912    assert(consdata != NULL);
15913 
15914    /* search array of bilinear terms */
15915    found = FALSE;
15916    for( i = 0; i < consdata->nbilinterms; ++i )
15917    {
15918       if( (consdata->bilinterms[i].var1 == var1 && consdata->bilinterms[i].var2 == var2) ||
15919             (consdata->bilinterms[i].var1 == var2 && consdata->bilinterms[i].var2 == var1)  )
15920       {
15921          if( found || SCIPisZero(scip, coef) )
15922          {
15923             consdata->bilinterms[i].coef = 0.0;
15924 
15925             /* remember to merge bilinear terms */
15926             consdata->bilinmerged = FALSE;
15927          }
15928          else
15929             consdata->bilinterms[i].coef = coef;
15930          found = TRUE;
15931       }
15932    }
15933 
15934    /* add bilinear term if necessary */
15935    if( !found )
15936    {
15937       SCIP_CALL( SCIPaddBilinTermQuadratic(scip, cons, var1, var2, coef) );
15938    }
15939 
15940    /* update flags and invalidate activities */
15941    consdata->isconvex = FALSE;
15942    consdata->isconcave = FALSE;
15943    consdata->iscurvchecked = FALSE;
15944    consdata->ispropagated = FALSE;
15945    consdata->ispresolved = FALSE;
15946 
15947    SCIPintervalSetEmpty(&consdata->quadactivitybounds);
15948    consdata->activity = SCIP_INVALID;
15949 
15950    return SCIP_OKAY;
15951 }
15952 
15953 /** returns the total number of bilinear terms that are contained in all quadratic constraints */
SCIPgetNAllBilinearTermsQuadratic(SCIP * scip)15954 int SCIPgetNAllBilinearTermsQuadratic(
15955    SCIP*                 scip                /**< SCIP data structure */
15956    )
15957 {
15958    SCIP_CONSHDLRDATA* conshdlrdata;
15959    SCIP_CONSHDLR* conshdlr;
15960 
15961    assert(scip != NULL);
15962 
15963    conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
15964 
15965    if( conshdlr == NULL )
15966       return 0;
15967 
15968    conshdlrdata = SCIPconshdlrGetData(conshdlr);
15969    assert(conshdlrdata != NULL);
15970 
15971    return conshdlrdata->nbilinterms;
15972 }
15973 
15974 /** returns all bilinear terms that are contained in all quadratic constraints */
SCIPgetAllBilinearTermsQuadratic(SCIP * scip,SCIP_VAR ** RESTRICT x,SCIP_VAR ** RESTRICT y,int * RESTRICT nbilinterms,int * RESTRICT nunderests,int * RESTRICT noverests,SCIP_Real * maxnonconvexity)15975 SCIP_RETCODE SCIPgetAllBilinearTermsQuadratic(
15976    SCIP*                 scip,               /**< SCIP data structure */
15977    SCIP_VAR** RESTRICT   x,                  /**< array to store first variable of each bilinear term */
15978    SCIP_VAR** RESTRICT   y,                  /**< array to second variable of each bilinear term */
15979    int* RESTRICT         nbilinterms,        /**< buffer to store the total number of bilinear terms */
15980    int* RESTRICT         nunderests,         /**< array to store the total number of constraints that require to underestimate a bilinear term */
15981    int* RESTRICT         noverests,          /**< array to store the total number of constraints that require to overestimate a bilinear term */
15982    SCIP_Real*            maxnonconvexity     /**< largest absolute value of nonconvex eigenvalues of all quadratic constraints containing a bilinear term */
15983    )
15984 {
15985    SCIP_CONSHDLRDATA* conshdlrdata;
15986    SCIP_CONSHDLR* conshdlr;
15987    int i;
15988 
15989    assert(scip != NULL);
15990    assert(x != NULL);
15991    assert(y != NULL);
15992    assert(nbilinterms != NULL);
15993    assert(nunderests != NULL);
15994    assert(noverests!= NULL);
15995    assert(maxnonconvexity != NULL);
15996 
15997    conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
15998 
15999    if( conshdlr == NULL )
16000    {
16001       *nbilinterms = 0;
16002       return SCIP_OKAY;
16003    }
16004 
16005    conshdlrdata = SCIPconshdlrGetData(conshdlr);
16006    assert(conshdlrdata != NULL);
16007 
16008    for( i = 0; i < conshdlrdata->nbilinterms; ++i )
16009    {
16010       x[i] = conshdlrdata->bilinestimators[i].x;
16011       y[i] = conshdlrdata->bilinestimators[i].y;
16012       nunderests[i] = conshdlrdata->bilinestimators[i].nunderest;
16013       noverests[i] = conshdlrdata->bilinestimators[i].noverest;
16014       maxnonconvexity[i] = conshdlrdata->bilinestimators[i].maxnonconvexity;
16015    }
16016 
16017    *nbilinterms = conshdlrdata->nbilinterms;
16018 
16019    return SCIP_OKAY;
16020 }
16021 
16022 /** helper function to compute the violation of an inequality of the form xcoef * x <= ycoef * y + constant for two
16023  *  corner points of the domain [lbx,ubx]x[lby,uby]
16024  */
16025 static
getIneqViol(SCIP_VAR * x,SCIP_VAR * y,SCIP_Real xcoef,SCIP_Real ycoef,SCIP_Real constant,SCIP_Real * viol1,SCIP_Real * viol2)16026 void getIneqViol(
16027    SCIP_VAR*             x,                  /**< first variable */
16028    SCIP_VAR*             y,                  /**< second variable */
16029    SCIP_Real             xcoef,              /**< x-coefficient */
16030    SCIP_Real             ycoef,              /**< y-coefficient */
16031    SCIP_Real             constant,           /**< constant */
16032    SCIP_Real*            viol1,              /**< buffer to store the violation of the first corner point */
16033    SCIP_Real*            viol2               /**< buffer to store the violation of the second corner point */
16034    )
16035 {
16036    SCIP_Real norm;
16037 
16038    assert(viol1 != NULL);
16039    assert(viol2 != NULL);
16040 
16041    norm = SQRT(SQR(xcoef) + SQR(ycoef));
16042 
16043    /* inequality can be used for underestimating xy if and only if xcoef * ycoef > 0 */
16044    if( xcoef * ycoef >= 0 )
16045    {
16046       /* violation for top-left and bottom-right corner */
16047       *viol1 = MAX(0, (xcoef * SCIPvarGetLbLocal(x)  - ycoef * SCIPvarGetUbLocal(y) - constant) / norm); /*lint !e666*/
16048       *viol2 = MAX(0, (xcoef * SCIPvarGetUbLocal(x)  - ycoef * SCIPvarGetLbLocal(y) - constant) / norm); /*lint !e666*/
16049    }
16050    else
16051    {
16052       /* violation for top-right and bottom-left corner */
16053       *viol1 = MAX(0, (xcoef * SCIPvarGetUbLocal(x)  - ycoef * SCIPvarGetUbLocal(y) - constant) / norm); /*lint !e666*/
16054       *viol2 = MAX(0, (xcoef * SCIPvarGetLbLocal(x)  - ycoef * SCIPvarGetLbLocal(y) - constant) / norm); /*lint !e666*/
16055    }
16056 
16057    return;
16058 }
16059 
16060 /** adds a globally valid inequality of the form xcoef x <= ycoef y + constant for a bilinear term (x,y)
16061  *
16062  *  @note the indices of bilinear terms match with the entries of bilinear terms returned by SCIPgetAllBilinearTermsQuadratic
16063  */
SCIPaddBilinearIneqQuadratic(SCIP * scip,SCIP_VAR * x,SCIP_VAR * y,int idx,SCIP_Real xcoef,SCIP_Real ycoef,SCIP_Real constant,SCIP_Bool * success)16064 SCIP_RETCODE SCIPaddBilinearIneqQuadratic(
16065    SCIP*                 scip,               /**< SCIP data structure */
16066    SCIP_VAR*             x,                  /**< first variable */
16067    SCIP_VAR*             y,                  /**< second variable */
16068    int                   idx,                /**< index of the bilinear term */
16069    SCIP_Real             xcoef,              /**< x coefficient */
16070    SCIP_Real             ycoef,              /**< y coefficient */
16071    SCIP_Real             constant,           /**< constant part */
16072    SCIP_Bool*            success             /**< buffer to store whether inequality has been accepted */
16073    )
16074 {
16075    SCIP_CONSHDLRDATA* conshdlrdata;
16076    SCIP_CONSHDLR* conshdlr;
16077    BILINESTIMATOR* bilinest;
16078    SCIP_Real* ineqs;
16079    SCIP_Real viol1 = 0.0;
16080    SCIP_Real viol2 = 0.0;
16081    int* nineqs;
16082    int i;
16083 
16084    assert(scip != NULL);
16085    assert(x != NULL);
16086    assert(y != NULL);
16087    assert(idx >= 0);
16088    assert(xcoef != SCIP_INVALID); /*lint !e777 */
16089    assert(ycoef != SCIP_INVALID); /*lint !e777 */
16090    assert(constant != SCIP_INVALID); /*lint !e777 */
16091    assert(success != NULL);
16092 
16093    *success = FALSE;
16094 
16095    /* ignore inequalities that only yield to a (possible) bound tightening */
16096    if( SCIPisFeasZero(scip, xcoef) || SCIPisFeasZero(scip, ycoef) )
16097       return SCIP_OKAY;
16098 
16099    /* get constraint handler and its data */
16100    conshdlr = SCIPfindConshdlr(scip, CONSHDLR_NAME);
16101    if( conshdlr == NULL )
16102       return SCIP_OKAY;
16103 
16104    conshdlrdata = SCIPconshdlrGetData(conshdlr);
16105    assert(conshdlrdata != NULL);
16106    assert(idx < conshdlrdata->nbilinterms);
16107 
16108    bilinest = &conshdlrdata->bilinestimators[idx];
16109    assert(bilinest != NULL);
16110    assert(bilinest->x == x);
16111    assert(bilinest->y == y);
16112 
16113    SCIPdebugMsg(scip, "add bilinear term inequality: %g %s <= %g %s + %g\n", xcoef, SCIPvarGetName(bilinest->x),
16114       ycoef, SCIPvarGetName(bilinest->y), constant);
16115 
16116    if( xcoef * ycoef > 0.0 )
16117    {
16118       ineqs = bilinest->inequnderest;
16119       nineqs = &bilinest->ninequnderest;
16120    }
16121    else
16122    {
16123       ineqs = bilinest->ineqoverest;
16124       nineqs = &bilinest->nineqoverest;
16125    }
16126 
16127    /* compute violation of the inequality of the important corner points */
16128    getIneqViol(x, y, xcoef, ycoef, constant, &viol1, &viol2);
16129    SCIPdebugMsg(scip, "violations of inequality = (%g,%g)\n", viol1, viol2);
16130 
16131    /* inequality does not cut off one of the important corner points */
16132    if( SCIPisFeasLE(scip, MAX(viol1, viol2), 0.0) )
16133       return SCIP_OKAY;
16134 
16135    /* check whether inequality exists already */
16136    for( i = 0; i < *nineqs; ++i )
16137    {
16138       if( SCIPisFeasEQ(scip, xcoef, ineqs[3*i]) && SCIPisFeasEQ(scip, ycoef, ineqs[3*i+1])
16139          && SCIPisFeasEQ(scip, constant, ineqs[3*i+2]) )
16140       {
16141          SCIPdebugMsg(scip, "inequality already found -> skip\n");
16142          return SCIP_OKAY;
16143       }
16144    }
16145 
16146    /* add inequality if we found less than two so far; otherwise compare the violations to decide which which
16147     * inequality might be replaced
16148     */
16149    if( *nineqs < 2 )
16150    {
16151       ineqs[3*(*nineqs)] = xcoef;
16152       ineqs[3*(*nineqs) + 1] = ycoef;
16153       ineqs[3*(*nineqs) + 2] = constant;
16154       ++(*nineqs);
16155       *success = TRUE;
16156    }
16157    else
16158    {
16159       SCIP_Real viols1[2] = {0.0, 0.0};
16160       SCIP_Real viols2[2] = {0.0, 0.0};
16161       SCIP_Real bestviol;
16162       int pos = -1;
16163 
16164       assert(*nineqs == 2);
16165 
16166       /* compute resulting violations of both corner points when replacing an existing inequality
16167        *
16168        * given the violations (v1,w1), (v2,w2), (v3,w3) we select two inequalities i and j that
16169        * maximize max{vi,vj} + max{wi,wj} this measurement guarantees that select inequalities that
16170        * separate both important corner points
16171        */
16172       getIneqViol(x, y, ineqs[0], ineqs[1], ineqs[2], &viols1[0], &viols2[0]);
16173       getIneqViol(x, y, ineqs[3], ineqs[4], ineqs[5], &viols1[1], &viols2[1]);
16174       bestviol = MAX(viols1[0], viols1[1]) + MAX(viols2[0], viols2[1]);
16175 
16176       for( i = 0; i < 2; ++i )
16177       {
16178          SCIP_Real viol = MAX(viol1, viols1[i]) + MAX(viol2, viols2[i]);
16179          if( SCIPisGT(scip, viol, bestviol) )
16180          {
16181             bestviol = viol;
16182             /* remember inequality that should be replaced */
16183             pos = 1 - i;
16184          }
16185       }
16186 
16187       /* replace inequality at pos when replacing an existing inequality improved the total violation */
16188       if( pos != -1 )
16189       {
16190          assert(pos >= 0 && pos < 2);
16191          ineqs[3*pos] = xcoef;
16192          ineqs[3*pos+1] = ycoef;
16193          ineqs[3*pos+2] = constant;
16194          *success = TRUE;
16195       }
16196    }
16197    SCIPdebugMsg(scip, "accepted inequality? %u\n", *success);
16198 
16199    return SCIP_OKAY;
16200 }
16201 
16202 
16203 /** creates a SCIP_ROWPREP datastructure
16204  *
16205  * Initial cut represents 0 <= 0.
16206  */
SCIPcreateRowprep(SCIP * scip,SCIP_ROWPREP ** rowprep,SCIP_SIDETYPE sidetype,SCIP_Bool local)16207 SCIP_RETCODE SCIPcreateRowprep(
16208    SCIP*                 scip,               /**< SCIP data structure */
16209    SCIP_ROWPREP**        rowprep,            /**< buffer to store pointer to rowprep */
16210    SCIP_SIDETYPE         sidetype,           /**< whether cut will be or lower-equal or larger-equal type */
16211    SCIP_Bool             local               /**< whether cut will be valid only locally */
16212    )
16213 {
16214    assert(scip != NULL);
16215    assert(rowprep != NULL);
16216 
16217    SCIP_CALL( SCIPallocBlockMemory(scip, rowprep) );
16218    BMSclearMemory(*rowprep);
16219 
16220    (*rowprep)->sidetype = sidetype;
16221    (*rowprep)->local = local;
16222 
16223    return SCIP_OKAY;
16224 }
16225 
16226 /** frees a SCIP_ROWPREP datastructure */
SCIPfreeRowprep(SCIP * scip,SCIP_ROWPREP ** rowprep)16227 void SCIPfreeRowprep(
16228    SCIP*                 scip,               /**< SCIP data structure */
16229    SCIP_ROWPREP**        rowprep             /**< pointer that stores pointer to rowprep */
16230    )
16231 {
16232    assert(scip != NULL);
16233    assert(rowprep != NULL);
16234    assert(*rowprep != NULL);
16235 
16236    SCIPfreeBlockMemoryArrayNull(scip, &(*rowprep)->vars, (*rowprep)->varssize);
16237    SCIPfreeBlockMemoryArrayNull(scip, &(*rowprep)->coefs, (*rowprep)->varssize);
16238    SCIPfreeBlockMemory(scip, rowprep);
16239 }
16240 
16241 /** creates a copy of a SCIP_ROWPREP datastructure */
SCIPcopyRowprep(SCIP * scip,SCIP_ROWPREP ** target,SCIP_ROWPREP * source)16242 SCIP_RETCODE SCIPcopyRowprep(
16243    SCIP*                 scip,               /**< SCIP data structure */
16244    SCIP_ROWPREP**        target,             /**< buffer to store pointer of rowprep copy */
16245    SCIP_ROWPREP*         source              /**< rowprep to copy */
16246    )
16247 {
16248    assert(scip != NULL);
16249    assert(target != NULL);
16250    assert(source != NULL);
16251 
16252    SCIP_CALL( SCIPduplicateBlockMemory(scip, target, source) );
16253    if( source->coefs != NULL )
16254    {
16255       SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*target)->coefs, source->coefs, source->varssize) );
16256    }
16257    if( source->vars != NULL )
16258    {
16259       SCIP_CALL( SCIPduplicateBlockMemoryArray(scip, &(*target)->vars, source->vars, source->varssize) );
16260    }
16261 
16262    return SCIP_OKAY;
16263 }
16264 
16265 /** ensures that rowprep has space for at least given number of additional terms
16266  *
16267  * Useful when knowing in advance how many terms will be added.
16268  */
SCIPensureRowprepSize(SCIP * scip,SCIP_ROWPREP * rowprep,int size)16269 SCIP_RETCODE SCIPensureRowprepSize(
16270    SCIP*                 scip,               /**< SCIP data structure */
16271    SCIP_ROWPREP*         rowprep,            /**< rowprep */
16272    int                   size                /**< number of additional terms for which to alloc space in rowprep */
16273    )
16274 {
16275    int oldsize;
16276 
16277    assert(scip != NULL);
16278    assert(rowprep != NULL);
16279    assert(size >= 0);
16280 
16281    if( rowprep->varssize >= rowprep->nvars + size )
16282       return SCIP_OKAY;  /* already enough space left */
16283 
16284    /* realloc vars and coefs array */
16285    oldsize = rowprep->varssize;
16286    rowprep->varssize = SCIPcalcMemGrowSize(scip, rowprep->nvars + size);
16287 
16288    SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &rowprep->vars,  oldsize, rowprep->varssize) );
16289    SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &rowprep->coefs, oldsize, rowprep->varssize) );
16290 
16291    return SCIP_OKAY;
16292 }
16293 
16294 /** prints a rowprep */
SCIPprintRowprep(SCIP * scip,SCIP_ROWPREP * rowprep,FILE * file)16295 void SCIPprintRowprep(
16296    SCIP*                 scip,               /**< SCIP data structure */
16297    SCIP_ROWPREP*         rowprep,            /**< rowprep to be printed */
16298    FILE*                 file                /**< file to print to, or NULL for stdout */
16299    )
16300 {
16301    int i;
16302 
16303    assert(scip != NULL);
16304    assert(rowprep != NULL);
16305 
16306    if( *rowprep->name != '\0' )
16307    {
16308       SCIPinfoMessage(scip, file, "[%s](%c) ", rowprep->name, rowprep->local ? 'l' : 'g');
16309    }
16310 
16311    for( i = 0; i < rowprep->nvars; ++i )
16312    {
16313       SCIPinfoMessage(scip, file, "%+.15g*<%s> ", rowprep->coefs[i], SCIPvarGetName(rowprep->vars[i]));
16314    }
16315 
16316    SCIPinfoMessage(scip, file, rowprep->sidetype == SCIP_SIDETYPE_LEFT ? ">= %.15g\n" : "<= %.15g\n", rowprep->side);
16317 }
16318 
16319 /** adds a term coef*var to a rowprep */
SCIPaddRowprepTerm(SCIP * scip,SCIP_ROWPREP * rowprep,SCIP_VAR * var,SCIP_Real coef)16320 SCIP_RETCODE SCIPaddRowprepTerm(
16321    SCIP*                 scip,               /**< SCIP data structure */
16322    SCIP_ROWPREP*         rowprep,            /**< rowprep */
16323    SCIP_VAR*             var,                /**< variable to add */
16324    SCIP_Real             coef                /**< coefficient to add */
16325    )
16326 {
16327    assert(scip != NULL);
16328    assert(rowprep != NULL);
16329    assert(var != NULL);
16330 
16331    if( coef == 0.0 )
16332       return SCIP_OKAY;
16333 
16334    SCIP_CALL( SCIPensureRowprepSize(scip, rowprep, 1) );
16335    assert(rowprep->varssize > rowprep->nvars);
16336 
16337    rowprep->vars[rowprep->nvars] = var;
16338    rowprep->coefs[rowprep->nvars] = coef;
16339    ++rowprep->nvars;
16340 
16341    return SCIP_OKAY;
16342 }
16343 
16344 /** adds several terms coef*var to a rowprep */
SCIPaddRowprepTerms(SCIP * scip,SCIP_ROWPREP * rowprep,int nvars,SCIP_VAR ** vars,SCIP_Real * coefs)16345 SCIP_RETCODE SCIPaddRowprepTerms(
16346    SCIP*                 scip,               /**< SCIP data structure */
16347    SCIP_ROWPREP*         rowprep,            /**< rowprep */
16348    int                   nvars,              /**< number of terms to add */
16349    SCIP_VAR**            vars,               /**< variables to add */
16350    SCIP_Real*            coefs               /**< coefficients to add */
16351    )
16352 {
16353    assert(scip != NULL);
16354    assert(rowprep != NULL);
16355    assert(vars != NULL || nvars == 0);
16356    assert(coefs != NULL || nvars == 0);
16357 
16358    if( nvars == 0 )
16359       return SCIP_OKAY;
16360 
16361    SCIP_CALL( SCIPensureRowprepSize(scip, rowprep, nvars) );
16362    assert(rowprep->varssize >= rowprep->nvars + nvars);
16363 
16364    /*lint --e{866} */
16365    BMScopyMemoryArray(rowprep->vars + rowprep->nvars, vars, nvars);
16366    BMScopyMemoryArray(rowprep->coefs + rowprep->nvars, coefs, nvars);
16367    rowprep->nvars += nvars;
16368 
16369    return SCIP_OKAY;
16370 }
16371 
16372 #ifdef NDEBUG
16373 #undef SCIPaddRowprepSide
16374 #undef SCIPaddRowprepConstant
16375 #endif
16376 
16377 /** adds constant value to side of rowprep */
SCIPaddRowprepSide(SCIP_ROWPREP * rowprep,SCIP_Real side)16378 void SCIPaddRowprepSide(
16379    SCIP_ROWPREP*         rowprep,            /**< rowprep */
16380    SCIP_Real             side                /**< constant value to be added to side */
16381    )
16382 {
16383    assert(rowprep != NULL);
16384 
16385    rowprep->side += side;
16386 }
16387 
16388 /** adds constant term to rowprep
16389  *
16390  * Substracts constant from side.
16391  */
SCIPaddRowprepConstant(SCIP_ROWPREP * rowprep,SCIP_Real constant)16392 void SCIPaddRowprepConstant(
16393    SCIP_ROWPREP*         rowprep,            /**< rowprep */
16394    SCIP_Real             constant            /**< constant value to be added */
16395    )
16396 {
16397    assert(rowprep != NULL);
16398 
16399    SCIPaddRowprepSide(rowprep, -constant);
16400 }
16401 
16402 /** computes violation of cut in a given solution */
SCIPgetRowprepViolation(SCIP * scip,SCIP_ROWPREP * rowprep,SCIP_SOL * sol)16403 SCIP_Real SCIPgetRowprepViolation(
16404    SCIP*                 scip,               /**< SCIP data structure */
16405    SCIP_ROWPREP*         rowprep,            /**< rowprep to be turned into a row */
16406    SCIP_SOL*             sol                 /**< solution or NULL for LP solution */
16407    )
16408 {
16409    SCIP_Real activity;
16410    int i;
16411 
16412    activity = 0.0;
16413    for( i = 0; i < rowprep->nvars; ++i )
16414    {
16415       /* Loose variable have the best bound as LP solution value.
16416        * HOWEVER, they become column variables when they are added to a row (via SCIPaddVarsToRow below).
16417        * When this happens, their LP solution value changes to 0.0!
16418        * So when calculating the row activity for an LP solution, we treat loose variable as if they were already column variables.
16419        */
16420       if( sol != NULL || SCIPvarGetStatus(rowprep->vars[i]) != SCIP_VARSTATUS_LOOSE )
16421          activity += rowprep->coefs[i] * SCIPgetSolVal(scip, sol, rowprep->vars[i]);
16422    }
16423 
16424    if( rowprep->sidetype == SCIP_SIDETYPE_RIGHT )
16425       /* cut is activity <= side -> violation is activity - side, if positive */
16426       return MAX(activity - rowprep->side, 0.0);
16427    else
16428       /* cut is activity >= side -> violation is side - activity, if positive */
16429       return MAX(rowprep->side - activity, 0.0);
16430 }
16431 
16432 /** Merge terms that use same variable and eliminate zero coefficients.
16433  *
16434  * Terms are sorted by variable (@see SCIPvarComp) after return.
16435  */
SCIPmergeRowprepTerms(SCIP * scip,SCIP_ROWPREP * rowprep)16436 void SCIPmergeRowprepTerms(
16437    SCIP*                 scip,               /**< SCIP data structure */
16438    SCIP_ROWPREP*         rowprep             /**< rowprep to be cleaned up */
16439    )
16440 {
16441    int i;
16442    int j;
16443 
16444    assert(scip != NULL);
16445    assert(rowprep != NULL);
16446 
16447    if( rowprep->nvars <= 1 )
16448       return;
16449 
16450    /* sort terms by variable index */
16451    SCIPsortPtrReal((void**)rowprep->vars, rowprep->coefs, SCIPvarComp, rowprep->nvars);
16452 
16453    /* merge terms with same variable, drop 0 coefficients */
16454    i = 0;
16455    j = 1;
16456    while( j < rowprep->nvars )
16457    {
16458       if( rowprep->vars[i] == rowprep->vars[j] )
16459       {
16460          /* merge term j into term i */
16461          rowprep->coefs[i] += rowprep->coefs[j];
16462          ++j;
16463          continue;
16464       }
16465 
16466       if( rowprep->coefs[i] == 0.0 )
16467       {
16468          /* move term j to position i */
16469          rowprep->coefs[i] = rowprep->coefs[j];
16470          rowprep->vars[i] = rowprep->vars[j];
16471          ++j;
16472          continue;
16473       }
16474 
16475       /* move term j to position i+1 and move on */
16476       if( j != i+1 )
16477       {
16478          rowprep->vars[i+1] = rowprep->vars[j];
16479          rowprep->coefs[i+1] = rowprep->coefs[j];
16480       }
16481       ++i;
16482       ++j;
16483    }
16484 
16485    /* remaining term can have coef zero -> forget about it */
16486    if( rowprep->coefs[i] == 0.0 )
16487       --i;
16488 
16489    /* i points to last term */
16490    rowprep->nvars = i+1;
16491 }
16492 
16493 /** sort cut terms by absolute value of coefficients, from largest to smallest */
16494 static
rowprepCleanupSortTerms(SCIP * scip,SCIP_ROWPREP * rowprep)16495 SCIP_RETCODE rowprepCleanupSortTerms(
16496    SCIP*                 scip,               /**< SCIP data structure */
16497    SCIP_ROWPREP*         rowprep             /**< rowprep to be sorted */
16498    )
16499 {
16500    int i;
16501 
16502    assert(scip != NULL);
16503    assert(rowprep != NULL);
16504 
16505    /* special treatment for cuts with few variables */
16506    switch( rowprep->nvars )
16507    {
16508       case 0:
16509       case 1:
16510          break;
16511 
16512       case 2:
16513       {
16514          if( REALABS(rowprep->coefs[0]) < REALABS(rowprep->coefs[1]) )
16515          {
16516             SCIP_Real tmp1;
16517             SCIP_VAR* tmp2;
16518 
16519             tmp1 = rowprep->coefs[0];
16520             rowprep->coefs[0] = rowprep->coefs[1];
16521             rowprep->coefs[1] = tmp1;
16522 
16523             tmp2 = rowprep->vars[0];
16524             rowprep->vars[0] = rowprep->vars[1];
16525             rowprep->vars[1] = tmp2;
16526          }
16527          break;
16528       }
16529 
16530       default :
16531       {
16532          SCIP_Real* abscoefs;
16533 
16534          SCIP_CALL( SCIPallocBufferArray(scip, &abscoefs, rowprep->nvars) );
16535          for( i = 0; i < rowprep->nvars; ++i )
16536             abscoefs[i] = REALABS(rowprep->coefs[i]);
16537          SCIPsortDownRealRealPtr(abscoefs, rowprep->coefs, (void**)rowprep->vars, rowprep->nvars);
16538          SCIPfreeBufferArray(scip, &abscoefs);
16539       }
16540    }
16541 
16542    /* forget about coefs that are exactly zero (unlikely to have some) */
16543    while( rowprep->nvars > 0 && rowprep->coefs[rowprep->nvars-1] == 0.0 )
16544       --rowprep->nvars;
16545 
16546    return SCIP_OKAY;
16547 }
16548 
16549 /** try to improve coef range by aggregating cut with variable bounds
16550  *
16551  * Assumes terms have been sorted by rowprepCleanupSortTerms().
16552  */
16553 static
rowprepCleanupImproveCoefrange(SCIP * scip,SCIP_ROWPREP * rowprep,SCIP_SOL * sol,SCIP_Real maxcoefrange)16554 void rowprepCleanupImproveCoefrange(
16555    SCIP*                 scip,               /**< SCIP data structure */
16556    SCIP_ROWPREP*         rowprep,            /**< rowprep to be improve */
16557    SCIP_SOL*             sol,                /**< solution that we try to cut off, or NULL for LP solution */
16558    SCIP_Real             maxcoefrange        /**< maximal allowed coefficients range */
16559    )
16560 {
16561    SCIP_VAR* var;
16562    SCIP_Real lb;
16563    SCIP_Real ub;
16564    SCIP_Real ref;
16565    SCIP_Real coef;
16566    SCIP_Real mincoef;
16567    SCIP_Real maxcoef;
16568    SCIP_Real loss[2];
16569    int maxcoefidx;
16570    int pos;
16571 
16572    maxcoefidx = 0;
16573    if( rowprep->nvars > 0 )
16574    {
16575       maxcoef = REALABS(rowprep->coefs[0]);
16576       mincoef = REALABS(rowprep->coefs[rowprep->nvars-1]);
16577    }
16578    else
16579       mincoef = maxcoef = 1.0;
16580 
16581    /* eliminate minimal or maximal coefs as long as coef range is too large
16582     * this is likely going to eliminate coefs that are within eps of 0.0
16583     * if not, then we do so after scaling (or should we enforce this here?)
16584     */
16585    while( maxcoef / mincoef > maxcoefrange  )
16586    {
16587       SCIPdebugMsg(scip, "cut coefficients have very large range: mincoef = %g maxcoef = %g\n", mincoef, maxcoef);
16588 
16589       /* max/min can only be > 1 if there is more than one var
16590        * we need this below for updating the max/min coef after eliminating a term
16591        */
16592       assert(rowprep->nvars > 1);
16593 
16594       /* try to reduce coef range by aggregating with variable bounds
16595        * that is, eliminate a term like a*x from a*x + ... <= side by adding -a*x <= -a*lb(x)
16596        * with ref(x) the reference point we try to eliminate, this would weaken the cut by a*(lb(x)-ref(x))
16597        *
16598        * we consider eliminating either the term with maximal or the one with minimal coefficient,
16599        * taking the one that leads to the least weakening of the cut
16600        *
16601        * TODO (suggested by @bzfserra, see !496):
16602        * - Also one could think of not completely removing the coefficient but do an aggregation that makes the coefficient look better. For instance:
16603        *   say you have $`a x + 0.1 y \leq r`$ and $`y`$ has only an upper bound, $`y \leq b`$,
16604        *   then you can't really remove $`y`$. However, you could aggregate it with $`0.9 \cdot (y \leq b)`$ to get
16605        *   $`a x + y \leq r + 0.9 b`$, which has better numerics (and hopefully still cuts the point... actually, if for the point you want to separate, $`y^* = b`$, then the violation is the same)
16606        */
16607 
16608       for( pos = 0; pos < 2; ++pos )
16609       {
16610          var = rowprep->vars[pos ? rowprep->nvars-1 : maxcoefidx];
16611          coef = rowprep->coefs[pos ? rowprep->nvars-1 : maxcoefidx];
16612          lb = SCIPvarGetLbLocal(var);
16613          ub = SCIPvarGetUbLocal(var);
16614          ref = SCIPgetSolVal(scip, sol, var);
16615          assert(coef != 0.0);
16616 
16617          /* make sure reference point is something reasonable within the bounds, preferable the value from the solution */
16618          if( SCIPisInfinity(scip, REALABS(ref)) )
16619             ref = 0.0;
16620          ref = MAX(lb, MIN(ub, ref));
16621 
16622          /* check whether we can eliminate coef*var from rowprep and how much we would loose w.r.t. ref(x) */
16623          if( ((coef > 0.0 && rowprep->sidetype == SCIP_SIDETYPE_RIGHT) || (coef < 0.0 && rowprep->sidetype == SCIP_SIDETYPE_LEFT)) )
16624          {
16625             /* we would need to aggregate with -coef*var <= -coef*lb(x) */
16626             if( SCIPisInfinity(scip, -lb) )
16627                loss[pos] = SCIP_INVALID;
16628             else
16629                loss[pos] = REALABS(coef) * (ref - lb);
16630          }
16631          else
16632          {
16633             assert((coef < 0.0 && rowprep->sidetype == SCIP_SIDETYPE_RIGHT) || (coef > 0.0 && rowprep->sidetype == SCIP_SIDETYPE_LEFT));
16634             /* we would need to aggregate with -coef*var >= -coef*ub(x) */
16635             if( SCIPisInfinity(scip, ub) )
16636                loss[pos] = SCIP_INVALID;
16637             else
16638                loss[pos] = REALABS(coef) * (ub - ref);
16639          }
16640          assert(loss[pos] >= 0.0);  /* assuming SCIP_INVALID >= 0 */
16641 
16642          SCIPdebugMsg(scip, "aggregating %g*<%s> %c= ... with <%s>[%g] %c= %g looses %g\n",
16643             coef, SCIPvarGetName(var), rowprep->sidetype == SCIP_SIDETYPE_RIGHT ? '<' : '>',
16644             SCIPvarGetName(var), ref,
16645             ((coef > 0.0 && rowprep->sidetype == SCIP_SIDETYPE_RIGHT) || (coef < 0.0 && rowprep->sidetype == SCIP_SIDETYPE_LEFT)) ? '>' : '<',
16646             ((coef > 0.0 && rowprep->sidetype == SCIP_SIDETYPE_RIGHT) || (coef < 0.0 && rowprep->sidetype == SCIP_SIDETYPE_LEFT)) ? lb : ub, loss[pos]);
16647       }
16648 
16649       /*lint --e{777} */
16650       if( loss[0] == SCIP_INVALID && loss[1] == SCIP_INVALID )
16651          break;  /* cannot eliminate coefficient */
16652 
16653       /* select position with smaller loss */
16654       pos = (loss[1] == SCIP_INVALID || loss[1] > loss[0]) ? 0 : 1;
16655 
16656       /* now do the actual elimination */
16657       var = rowprep->vars[pos ? rowprep->nvars-1 : maxcoefidx];
16658       coef = rowprep->coefs[pos ? rowprep->nvars-1 : maxcoefidx];
16659 
16660       /* eliminate coef*var from rowprep: increase side */
16661       if( ((coef > 0.0 && rowprep->sidetype == SCIP_SIDETYPE_RIGHT) || (coef < 0.0 && rowprep->sidetype == SCIP_SIDETYPE_LEFT)) )
16662       {
16663          /* we aggregate with -coef*var <= -coef*lb(x) */
16664          assert(!SCIPisInfinity(scip, -SCIPvarGetLbLocal(var)));
16665          SCIPaddRowprepConstant(rowprep, coef * SCIPvarGetLbLocal(var));
16666          rowprep->local |= SCIPisGT(scip, SCIPvarGetLbLocal(var), SCIPvarGetLbGlobal(var));
16667       }
16668       else
16669       {
16670          assert((coef < 0.0 && rowprep->sidetype == SCIP_SIDETYPE_RIGHT) || (coef > 0.0 && rowprep->sidetype == SCIP_SIDETYPE_LEFT));
16671          /* we aggregate with -coef*var >= -coef*ub(x) */
16672          assert(!SCIPisInfinity(scip, SCIPvarGetUbLocal(var)));
16673          SCIPaddRowprepConstant(rowprep, coef * SCIPvarGetUbLocal(var));
16674          rowprep->local |= SCIPisLT(scip, SCIPvarGetUbLocal(var), SCIPvarGetUbGlobal(var));
16675       }
16676 
16677       /* eliminate coef*var from rowprep: remove coef */
16678       if( pos == 0 )
16679       {
16680          /* set first term to zero */
16681          rowprep->coefs[maxcoefidx] = 0.0;
16682 
16683          /* update index */
16684          ++maxcoefidx;
16685 
16686          /* update maxcoef */
16687          maxcoef = REALABS(rowprep->coefs[maxcoefidx]);
16688       }
16689       else
16690       {
16691          /* forget last term */
16692          --rowprep->nvars;
16693 
16694          /* update mincoef */
16695          mincoef = REALABS(rowprep->coefs[rowprep->nvars-1]);
16696       }
16697    }
16698 
16699    /* if maximal coefs were removed, then there are now 0's in the beginning of the coefs array
16700     * -> move all remaining coefs and vars up front
16701     */
16702    if( maxcoefidx > 0 )
16703    {
16704       int i;
16705       for( i = maxcoefidx; i < rowprep->nvars; ++i )
16706       {
16707          rowprep->vars[i-maxcoefidx] = rowprep->vars[i];
16708          rowprep->coefs[i-maxcoefidx] = rowprep->coefs[i];
16709       }
16710       rowprep->nvars -= maxcoefidx;
16711    }
16712 }
16713 
16714 
16715 /** scales up rowprep if it seems useful */
16716 static
rowprepCleanupScaleup(SCIP * scip,SCIP_ROWPREP * rowprep,SCIP_Real * viol,SCIP_Real minviol)16717 void rowprepCleanupScaleup(
16718    SCIP*                 scip,               /**< SCIP data structure */
16719    SCIP_ROWPREP*         rowprep,            /**< rowprep to be improve */
16720    SCIP_Real*            viol,               /**< violation of cut in sol (input and output) */
16721    SCIP_Real             minviol             /**< minimal violation we try to achieve */
16722    )
16723 {
16724    SCIP_Real scalefactor;
16725    SCIP_Real mincoef;
16726    SCIP_Real maxcoef;
16727 
16728    assert(scip != NULL);
16729    assert(rowprep != NULL);
16730    assert(viol != NULL);
16731 
16732    /* if violation is very small than better don't scale up */
16733    if( *viol < ROWPREP_SCALEUP_VIOLNONZERO )
16734       return;
16735 
16736    /* if violation is already above minviol, then nothing to do */
16737    if( *viol >= minviol )
16738       return;
16739 
16740    /* if violation is sufficiently positive (>10*eps), but has not reached minviol,
16741     * then consider scaling up to reach approx MINVIOLFACTOR*minviol
16742     */
16743    scalefactor = ROWPREP_SCALEUP_MINVIOLFACTOR * minviol / *viol;
16744 
16745    /* scale by approx. scalefactor, if minimal coef is not so large yet and maximal coef and rhs don't get huge by doing so (or have been so before) */
16746    mincoef = rowprep->nvars > 0 ? REALABS(rowprep->coefs[rowprep->nvars-1]) : 1.0;
16747    maxcoef = rowprep->nvars > 0 ? REALABS(rowprep->coefs[0]) : 1.0;
16748    if( mincoef < ROWPREP_SCALEUP_MAXMINCOEF && scalefactor * maxcoef < ROWPREP_SCALEUP_MAXMAXCOEF && scalefactor * REALABS(rowprep->side) < ROWPREP_SCALEUP_MAXSIDE )
16749    {
16750       int scaleexp;
16751 
16752       /* SCIPinfoMessage(scip, NULL, "scale up by ~%g, viol=%g: ", scalefactor, myviol);
16753          SCIPprintRowprep(scip, rowprep, NULL); */
16754 
16755       /* SCIPscaleRowprep returns the actually applied scale factor */
16756       scaleexp = SCIPscaleRowprep(rowprep, scalefactor);
16757       *viol = ldexp(*viol, scaleexp);
16758 
16759       /* SCIPinfoMessage(scip, NULL, "scaled up by %g, viol=%g: ", ldexp(1.0, scaleexp), myviol);
16760          SCIPprintRowprep(scip, rowprep, NULL); */
16761    }
16762 }
16763 
16764 /** scales down rowprep if it improves coefs and keeps rowprep violated */
16765 static
rowprepCleanupScaledown(SCIP * scip,SCIP_ROWPREP * rowprep,SCIP_Real * viol,SCIP_Real minviol)16766 void rowprepCleanupScaledown(
16767    SCIP*                 scip,               /**< SCIP data structure */
16768    SCIP_ROWPREP*         rowprep,            /**< rowprep to be improve */
16769    SCIP_Real*            viol,               /**< violation of cut in sol (input and output) */
16770    SCIP_Real             minviol             /**< minimal violation we try to keep */
16771    )
16772 {
16773    SCIP_Real scalefactor;
16774 
16775    /* if maxcoef < ROWPREP_SCALEDOWN_MINMAXCOEF (or no terms), then don't consider scaling down */
16776    if( rowprep->nvars == 0 || REALABS(rowprep->coefs[0]) < ROWPREP_SCALEDOWN_MINMAXCOEF )
16777       return;
16778 
16779    /* consider scaling down so that maxcoef ~ 10 */
16780    scalefactor = 10.0 / REALABS(rowprep->coefs[0]);
16781 
16782    /* if minimal violation would be lost by scaling down, then increase scalefactor such that minviol is still reached */
16783    if( *viol > minviol && scalefactor * *viol < minviol )
16784    {
16785       assert(minviol > 0.0);  /* since viol >= 0, the if-condition should ensure that minviol > 0 */
16786       assert(*viol > 0.0);    /* since minviol > 0, the if-condition ensures viol > 0 */
16787       scalefactor = ROWPREP_SCALEUP_MINVIOLFACTOR * minviol / *viol;
16788    }
16789 
16790    /* scale by approx. scalefactor if scaling down and minimal coef does not get too small
16791     * myviol < minviol (-> scalefactor > 1) or mincoef < feastol before scaling is possible, in which case we also don't scale down
16792     */
16793    if( scalefactor < 1.0 && scalefactor * REALABS(rowprep->coefs[rowprep->nvars-1]) > ROWPREP_SCALEDOWN_MINCOEF )
16794    {
16795       int scaleexp;
16796 
16797       /* SCIPinfoMessage(scip, NULL, "scale down by ~%g, viol=%g: ", scalefactor, myviol);
16798          SCIPprintRowprep(scip, rowprep, NULL); */
16799 
16800       scaleexp = SCIPscaleRowprep(rowprep, scalefactor);
16801       *viol = ldexp(*viol, scaleexp);
16802 
16803       /* SCIPinfoMessage(scip, NULL, "scaled down by %g, viol=%g: ", ldexp(1.0, scaleexp), myviol);
16804          SCIPprintRowprep(scip, rowprep, NULL); */
16805    }
16806 }
16807 
16808 /** rounds almost integral coefs to integrals, thereby trying to relax the cut */
16809 static
rowprepCleanupIntegralCoefs(SCIP * scip,SCIP_ROWPREP * rowprep,SCIP_Real * viol)16810 void rowprepCleanupIntegralCoefs(
16811    SCIP*                 scip,               /**< SCIP data structure */
16812    SCIP_ROWPREP*         rowprep,            /**< rowprep to be improve */
16813    SCIP_Real*            viol                /**< violation of cut in sol (input), set to SCIP_INVALID if some coef changed */
16814    )
16815 {
16816    SCIP_Real coef;
16817    SCIP_Real roundcoef;
16818    int i;
16819 
16820    assert(scip != NULL);
16821    assert(rowprep != NULL);
16822    assert(viol != NULL);
16823 
16824    /* Coefficients smaller than epsilon are rounded to 0.0 when added to row and
16825     * coefficients very close to integral values are rounded to integers when added to LP.
16826     * Both cases can be problematic if variable value is very large (bad numerics).
16827     * Thus, we anticipate by rounding coef here, but also modify constant so that cut is still valid (if possible),
16828     * i.e., bound coef[i]*x by round(coef[i])*x + (coef[i]-round(coef[i])) * bound(x).
16829     * Or in other words, we aggregate with the variable bound.
16830     *
16831     * If the required bound of x is not finite, then only round coef (introduces an error).
16832     * @TODO If only the opposite bound is available, then one could move the coefficient
16833     *   away from the closest integer so that the SCIP_ROW won't try to round it.
16834     */
16835    for( i = 0; i < rowprep->nvars; ++i )
16836    {
16837       coef = rowprep->coefs[i];
16838       roundcoef = SCIPround(scip, coef);
16839       if( coef != roundcoef && SCIPisEQ(scip, coef, roundcoef) ) /*lint !e777*/
16840       {
16841          SCIP_Real xbnd;
16842          SCIP_VAR* var;
16843 
16844          var = rowprep->vars[i];
16845          if( rowprep->sidetype == SCIP_SIDETYPE_RIGHT )
16846             if( rowprep->local )
16847                xbnd = coef > roundcoef ? SCIPvarGetLbLocal(var)  : SCIPvarGetUbLocal(var);
16848             else
16849                xbnd = coef > roundcoef ? SCIPvarGetLbGlobal(var) : SCIPvarGetUbGlobal(var);
16850          else
16851             if( rowprep->local )
16852                xbnd = coef > roundcoef ? SCIPvarGetUbLocal(var)  : SCIPvarGetLbLocal(var);
16853             else
16854                xbnd = coef > roundcoef ? SCIPvarGetUbGlobal(var) : SCIPvarGetLbGlobal(var);
16855 
16856          if( !SCIPisInfinity(scip, REALABS(xbnd)) )
16857          {
16858             /* if there is a bound, then relax row side so rounding coef will not introduce an error */
16859             SCIPdebugMsg(scip, "var <%s> [%g,%g] has almost integral coef %.15g, round coefficient to %g and add constant %g\n",
16860                SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), coef, roundcoef, (coef-roundcoef) * xbnd);
16861             SCIPaddRowprepConstant(rowprep, (coef-roundcoef) * xbnd);
16862          }
16863          else
16864          {
16865             /* if there is no bound, then we make the coef integral, too, even though this will introduce an error
16866              * however, SCIP_ROW would do this anyway, but doing this here might eliminate some epsilon coefs (so they don't determine mincoef below)
16867              * and helps to get a more accurate row violation value
16868              */
16869             SCIPdebugMsg(scip, "var <%s> [%g,%g] has almost integral coef %.15g, round coefficient to %g without relaxing side (!)\n",
16870                SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var), coef, roundcoef);
16871          }
16872          rowprep->coefs[i] = roundcoef;
16873          *viol = SCIP_INVALID;
16874       }
16875    }
16876 
16877    /* forget about coefs that became exactly zero by the above step */
16878    while( rowprep->nvars > 0 && rowprep->coefs[rowprep->nvars-1] == 0.0 )
16879       --rowprep->nvars;
16880 }
16881 
16882 /** relaxes almost zero side */
16883 static
rowprepCleanupSide(SCIP * scip,SCIP_ROWPREP * rowprep,SCIP_Real * viol)16884 void rowprepCleanupSide(
16885    SCIP*                 scip,               /**< SCIP data structure */
16886    SCIP_ROWPREP*         rowprep,            /**< rowprep to be improve */
16887    SCIP_Real*            viol                /**< violation of cut in sol (input), set to SCIP_INVALID if some coef changed */
16888    )
16889 {
16890    /* SCIP_ROW handling will replace a side close to 0 by 0.0, even if that makes the row more restrictive
16891     * we thus relax the side here so that it will either be 0 now or will not be rounded to 0 later
16892     */
16893    if( !SCIPisZero(scip, rowprep->side) )
16894       return;
16895 
16896    if( rowprep->side > 0.0 && rowprep->sidetype == SCIP_SIDETYPE_RIGHT )
16897       rowprep->side =  1.1*SCIPepsilon(scip);
16898    else if( rowprep->side < 0.0 && rowprep->sidetype == SCIP_SIDETYPE_LEFT )
16899       rowprep->side = -1.1*SCIPepsilon(scip);
16900    else
16901       rowprep->side = 0.0;
16902 
16903    *viol = SCIP_INVALID;
16904 }
16905 
16906 /* Cleans up and attempts to improve rowprep
16907  *
16908  * Drops small or large coefficients if coefrange is too large, if this can be done by relaxing the cut.
16909  * Scales coefficients and side up to reach minimal violation, if possible.
16910  * Scaling is omitted if violation is very small (ROWPREP_SCALEUP_VIOLNONZERO) or
16911  * maximal coefficient would become huge (ROWPREP_SCALEUP_MAXMAXCOEF).
16912  * Scales coefficients and side down if they are large and if the minimal violation is still reached.
16913  * Rounds coefficients close to integral values to integrals, if this can be done by relaxing the cut.
16914  * Rounds side within epsilon of 0 to 0.0 or +/-1.1*epsilon, whichever relaxes the cut least.
16915  *
16916  * After return, the terms in the rowprep will be sorted by absolute value of coefficient, in decreasing order.
16917  */
SCIPcleanupRowprep(SCIP * scip,SCIP_ROWPREP * rowprep,SCIP_SOL * sol,SCIP_Real maxcoefrange,SCIP_Real minviol,SCIP_Real * coefrange,SCIP_Real * viol)16918 SCIP_RETCODE SCIPcleanupRowprep(
16919    SCIP*                 scip,               /**< SCIP data structure */
16920    SCIP_ROWPREP*         rowprep,            /**< rowprep to be cleaned */
16921    SCIP_SOL*             sol,                /**< solution that we try to cut off, or NULL for LP solution */
16922    SCIP_Real             maxcoefrange,       /**< maximal allowed coefficients range */
16923    SCIP_Real             minviol,            /**< minimal absolute violation the row should achieve (w.r.t. sol) */
16924    SCIP_Real*            coefrange,          /**< buffer to store coefrange of cleaned up cut, or NULL if not of interest */
16925    SCIP_Real*            viol                /**< buffer to store absolute violation of cleaned up cut in sol, or NULL if not of interest */
16926    )
16927 {
16928    SCIP_Real myviol;
16929 #ifdef SCIP_DEBUG
16930    SCIP_Real mincoef = 1.0;
16931    SCIP_Real maxcoef = 1.0;
16932 #endif
16933 
16934    assert(maxcoefrange > 1.0);   /* not much interesting otherwise */
16935 
16936    /* sort term by absolute value of coef. */
16937    SCIP_CALL( rowprepCleanupSortTerms(scip, rowprep) );
16938 
16939 #ifdef SCIP_DEBUG
16940    if( rowprep->nvars > 0 )
16941    {
16942       maxcoef = REALABS(rowprep->coefs[0]);
16943       mincoef = REALABS(rowprep->coefs[rowprep->nvars-1]);
16944    }
16945 
16946    SCIPinfoMessage(scip, NULL, "starting cleanup, coefrange %g: ", maxcoef/mincoef);
16947    SCIPprintRowprep(scip, rowprep, NULL);
16948 #endif
16949 
16950    /* improve coefficient range by aggregating out variables */
16951    rowprepCleanupImproveCoefrange(scip, rowprep, sol, maxcoefrange);
16952 
16953    /* get current violation in sol */
16954    myviol = SCIPgetRowprepViolation(scip, rowprep, sol);
16955    assert(myviol >= 0.0);
16956 
16957 #ifdef SCIP_DEBUG
16958    if( rowprep->nvars > 0 )
16959    {
16960       maxcoef = REALABS(rowprep->coefs[0]);
16961       mincoef = REALABS(rowprep->coefs[rowprep->nvars-1]);
16962    }
16963 
16964    SCIPinfoMessage(scip, NULL, "improved coefrange to %g, viol %g: ", maxcoef / mincoef, myviol);
16965    SCIPprintRowprep(scip, rowprep, NULL);
16966 #endif
16967 
16968    /* if there is interest in achieving some minimal violation, then possibly scale up to increase violation, updates myviol */
16969    if( minviol > 0.0 )
16970    {
16971       /* first, try to achieve scip's minefficacy (typically 1e-4) */
16972       if( SCIPgetSepaMinEfficacy(scip) > minviol )
16973          rowprepCleanupScaleup(scip, rowprep, &myviol, SCIPgetSepaMinEfficacy(scip));
16974       /* in case scip minefficacy could not be reached or was smaller than minviol, try with the given minviol */
16975       rowprepCleanupScaleup(scip, rowprep, &myviol, minviol);
16976    }
16977 
16978    /* scale down to improve numerics, updates myviol */
16979    rowprepCleanupScaledown(scip, rowprep, &myviol, MAX(SCIPgetSepaMinEfficacy(scip), minviol)); /*lint !e666*/
16980 
16981 #ifdef SCIP_DEBUG
16982    SCIPinfoMessage(scip, NULL, "applied scaling, viol %g: ", myviol);
16983    SCIPprintRowprep(scip, rowprep, NULL);
16984 #endif
16985 
16986    /* turn almost-integral coefs to integral values, may set myviol to SCIP_INVALID */
16987    rowprepCleanupIntegralCoefs(scip, rowprep, &myviol);
16988 
16989    /* relax almost-zero side, may set myviol to SCIP_INVALID */
16990    rowprepCleanupSide(scip, rowprep, &myviol);
16991 
16992 #ifdef SCIP_DEBUG
16993    SCIPinfoMessage(scip, NULL, "adjusted almost-integral coefs and sides, viol %g: ", myviol);
16994    SCIPprintRowprep(scip, rowprep, NULL);
16995 #endif
16996 
16997    /* compute final coefrange, if requested by caller */
16998    if( coefrange != NULL )
16999    {
17000       if( rowprep->nvars > 0 )
17001          *coefrange = REALABS(rowprep->coefs[0]) / REALABS(rowprep->coefs[rowprep->nvars-1]);
17002       else
17003          *coefrange = 1.0;
17004    }
17005 
17006    /* If we updated myviol correctly, then it should coincide with freshly computed violation.
17007     * I leave this assert off for now, since getting the tolerance in the EQ correctly is not trivial. We recompute viol below anyway.
17008     */
17009    /* assert(myviol == SCIP_INVALID || SCIPisEQ(scip, myviol, SCIPgetRowprepViolation(scip, rowprep, sol))); */
17010 
17011    /* compute final violation, if requested by caller */
17012    if( viol != NULL )  /*lint --e{777} */
17013       *viol = myviol == SCIP_INVALID ? SCIPgetRowprepViolation(scip, rowprep, sol) : myviol;
17014 
17015    return SCIP_OKAY;
17016 }
17017 
17018 /** scales a rowprep
17019  *
17020  * @return Exponent of actually applied scaling factor, if written as 2^x.
17021  */
SCIPscaleRowprep(SCIP_ROWPREP * rowprep,SCIP_Real factor)17022 int SCIPscaleRowprep(
17023    SCIP_ROWPREP*         rowprep,            /**< rowprep to be scaled */
17024    SCIP_Real             factor              /**< suggested scale factor */
17025    )
17026 {
17027    double v;
17028    int expon;
17029    int i;
17030 
17031    assert(rowprep != NULL);
17032    assert(factor > 0.0);
17033 
17034    /* write factor as v*2^expon with v in [0.5,1) */
17035    v = frexp(factor, &expon);
17036    /* adjust to v'*2^expon with v' in (0.5,1] by v'=v if v > 0.5, v'=1 if v=0.5 */
17037    if( v == 0.5 )
17038       --expon;
17039 
17040    /* multiply each coefficient by 2^expon */
17041    for( i = 0; i < rowprep->nvars; ++i )
17042       rowprep->coefs[i] = ldexp(rowprep->coefs[i], expon);
17043 
17044    /* multiply side by 2^expon */
17045    rowprep->side = ldexp(rowprep->side, expon);
17046 
17047    return expon;
17048 }
17049 
17050 /** generates a SCIP_ROW from a rowprep */
SCIPgetRowprepRowConshdlr(SCIP * scip,SCIP_ROW ** row,SCIP_ROWPREP * rowprep,SCIP_CONSHDLR * conshdlr)17051 SCIP_RETCODE SCIPgetRowprepRowConshdlr(
17052    SCIP*                 scip,               /**< SCIP data structure */
17053    SCIP_ROW**            row,                /**< buffer to store pointer to new row */
17054    SCIP_ROWPREP*         rowprep,            /**< rowprep to be turned into a row */
17055    SCIP_CONSHDLR*        conshdlr            /**< constraint handler */
17056    )
17057 {
17058    assert(scip != NULL);
17059    assert(row != NULL);
17060    assert(rowprep != NULL);
17061    assert(conshdlr != NULL);
17062 
17063    SCIP_CALL( SCIPcreateEmptyRowConshdlr(scip, row, conshdlr, rowprep->name,
17064       rowprep->sidetype == SCIP_SIDETYPE_LEFT  ? rowprep->side : -SCIPinfinity(scip),
17065       rowprep->sidetype == SCIP_SIDETYPE_RIGHT ? rowprep->side :  SCIPinfinity(scip),
17066       rowprep->local && (SCIPgetDepth(scip) > 0), FALSE, TRUE) );
17067 
17068    SCIP_CALL( SCIPaddVarsToRow(scip, *row, rowprep->nvars, rowprep->vars, rowprep->coefs) );
17069 
17070    return SCIP_OKAY;
17071 }
17072 
17073 /** generates a SCIP_ROW from a rowprep */
SCIPgetRowprepRowCons(SCIP * scip,SCIP_ROW ** row,SCIP_ROWPREP * rowprep,SCIP_CONS * cons)17074 SCIP_RETCODE SCIPgetRowprepRowCons(
17075    SCIP*                 scip,               /**< SCIP data structure */
17076    SCIP_ROW**            row,                /**< buffer to store pointer to new row */
17077    SCIP_ROWPREP*         rowprep,            /**< rowprep to be turned into a row */
17078    SCIP_CONS*            cons                /**< constraint */
17079    )
17080 {
17081    assert(scip != NULL);
17082    assert(row != NULL);
17083    assert(rowprep != NULL);
17084    assert(cons != NULL);
17085 
17086    SCIP_CALL( SCIPcreateEmptyRowCons(scip, row, cons, rowprep->name,
17087       rowprep->sidetype == SCIP_SIDETYPE_LEFT  ? rowprep->side : -SCIPinfinity(scip),
17088       rowprep->sidetype == SCIP_SIDETYPE_RIGHT ? rowprep->side :  SCIPinfinity(scip),
17089       rowprep->local && (SCIPgetDepth(scip) > 0), FALSE, TRUE) );
17090 
17091    SCIP_CALL( SCIPaddVarsToRow(scip, *row, rowprep->nvars, rowprep->vars, rowprep->coefs) );
17092 
17093    return SCIP_OKAY;
17094 }
17095 
17096 /** generates a SCIP_ROW from a rowprep */
SCIPgetRowprepRowSepa(SCIP * scip,SCIP_ROW ** row,SCIP_ROWPREP * rowprep,SCIP_SEPA * sepa)17097 SCIP_RETCODE SCIPgetRowprepRowSepa(
17098    SCIP*                 scip,               /**< SCIP data structure */
17099    SCIP_ROW**            row,                /**< buffer to store pointer to new row */
17100    SCIP_ROWPREP*         rowprep,            /**< rowprep to be turned into a row */
17101    SCIP_SEPA*            sepa                /**< separator */
17102    )
17103 {
17104    assert(scip != NULL);
17105    assert(row != NULL);
17106    assert(rowprep != NULL);
17107 
17108    SCIP_CALL( SCIPcreateEmptyRowSepa(scip, row, sepa, rowprep->name,
17109       rowprep->sidetype == SCIP_SIDETYPE_LEFT  ? rowprep->side : -SCIPinfinity(scip),
17110       rowprep->sidetype == SCIP_SIDETYPE_RIGHT ? rowprep->side :  SCIPinfinity(scip),
17111       rowprep->local && (SCIPgetDepth(scip) > 0), FALSE, TRUE) );
17112 
17113    SCIP_CALL( SCIPaddVarsToRow(scip, *row, rowprep->nvars, rowprep->vars, rowprep->coefs) );
17114 
17115    return SCIP_OKAY;
17116 }
17117