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