1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2022 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 scip.zib.de. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15
16 /**@file nlhdlr_quadratic.c
17 * @ingroup DEFPLUGINS_NLHDLR
18 * @brief nonlinear handler to handle quadratic expressions
19 * @author Felipe Serrano
20 * @author Antonia Chmiela
21 *
22 * Some definitions:
23 * - a `BILINEXPRTERM` is a product of two expressions
24 * - a `QUADEXPRTERM` stores an expression `expr` that is known to appear in a nonlinear, quadratic term, that is
25 * `expr^2` or `expr*other_expr`. It stores its `sqrcoef` (that can be 0), its linear coef and all the bilinear expression
26 * terms in which expr appears.
27 */
28
29 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
30
31 /* #define DEBUG_INTERSECTIONCUT */
32 /* #define INTERCUT_MOREDEBUG */
33 /* #define INTERCUTS_VERBOSE */
34
35 #ifdef INTERCUTS_VERBOSE
36 #define INTER_LOG
37 #endif
38
39 #ifdef INTER_LOG
40 #define INTERLOG(x) if( SCIPgetSubscipDepth(scip) == 0 && SCIPgetVerbLevel(scip) >= SCIP_VERBLEVEL_NORMAL ) { x }
41 #else
42 #define INTERLOG(x)
43 #endif
44
45 #include <string.h>
46
47 #include "scip/cons_nonlinear.h"
48 #include "scip/pub_nlhdlr.h"
49 #include "scip/nlhdlr_quadratic.h"
50 #include "scip/expr_pow.h"
51 #include "scip/expr_sum.h"
52 #include "scip/expr_var.h"
53 #include "scip/expr_product.h"
54 #include "scip/pub_misc_rowprep.h"
55
56 /* fundamental nonlinear handler properties */
57 #define NLHDLR_NAME "quadratic"
58 #define NLHDLR_DESC "handler for quadratic expressions"
59 #define NLHDLR_DETECTPRIORITY 1
60 #define NLHDLR_ENFOPRIORITY 100
61
62 /* properties of the quadratic nlhdlr statistics table */
63 #define TABLE_NAME_QUADRATIC "nlhdlr_quadratic"
64 #define TABLE_DESC_QUADRATIC "quadratic nlhdlr statistics table"
65 #define TABLE_POSITION_QUADRATIC 14700 /**< the position of the statistics table */
66 #define TABLE_EARLIEST_STAGE_QUADRATIC SCIP_STAGE_TRANSFORMED /**< output of the statistics table is only printed from this stage onwards */
67
68 /* some default values */
69 #define INTERCUTS_MINVIOL 1e-4
70 #define DEFAULT_USEINTERCUTS FALSE
71 #define DEFAULT_USESTRENGTH FALSE
72 #define DEFAULT_USEBOUNDS FALSE
73 #define BINSEARCH_MAXITERS 120
74 #define DEFAULT_NCUTSROOT 20
75 #define DEFAULT_NCUTS 2
76
77 /*
78 * Data structures
79 */
80
81 /** nonlinear handler expression data */
82 struct SCIP_NlhdlrExprData
83 {
84 SCIP_EXPR* qexpr; /**< quadratic expression (stored here again for convenient access) */
85
86 SCIP_EXPRCURV curvature; /**< curvature of the quadratic representation of the expression */
87
88 SCIP_INTERVAL linactivity; /**< activity of linear part */
89
90 /* activities of quadratic parts as defined in nlhdlrIntevalQuadratic */
91 SCIP_Real minquadfiniteact; /**< minimum activity of quadratic part where only terms with finite min
92 activity contribute */
93 SCIP_Real maxquadfiniteact; /**< maximum activity of quadratic part where only terms with finite max
94 activity contribute */
95 int nneginfinityquadact;/**< number of quadratic terms contributing -infinity to activity */
96 int nposinfinityquadact;/**< number of quadratic terms contributing +infinity to activity */
97 SCIP_INTERVAL* quadactivities; /**< activity of each quadratic term as defined in nlhdlrIntevalQuadratic */
98 SCIP_INTERVAL quadactivity; /**< activity of quadratic part (sum of quadactivities) */
99 SCIP_Longint activitiestag; /**< value of activities tag when activities were computed */
100
101 SCIP_CONS* cons; /**< if expr is the root of constraint cons, store cons; otherwise NULL */
102 SCIP_Bool separating; /**< whether we are using the nlhdlr also for separation */
103 SCIP_Bool origvars; /**< whether the quad expr in qexpr is in original (non-aux) variables */
104
105 int ncutsadded; /**< number of intersection cuts added for this quadratic */
106 };
107
108 /** nonlinear handler data */
109 struct SCIP_NlhdlrData
110 {
111 int ncutsgenerated; /**< total number of cuts that where generated by separateQuadratic */
112 int ncutsadded; /**< total number of cuts that where generated by separateQuadratic and actually added */
113 SCIP_Longint lastnodenumber; /**< number of last node for which cuts were (allowed to be) generated */
114 int lastncuts; /**< number of cuts already generated */
115
116 /* parameter */
117 SCIP_Bool useintersectioncuts; /**< whether to use intersection cuts for quadratic constraints or not */
118 SCIP_Bool usestrengthening; /**< whether the strengthening should be used */
119 SCIP_Bool useboundsasrays; /**< use bounds of variables in quadratic as rays for intersection cuts */
120 int ncutslimit; /**< limit for number of cuts generated consecutively */
121 int ncutslimitroot; /**< limit for number of cuts generated at root node */
122 int maxrank; /**< maximal rank a slackvar can have */
123 SCIP_Real mincutviolation; /**< minimal cut violation the generated cuts must fulfill to be added to the LP */
124 SCIP_Real minviolation; /**< minimal violation the constraint must fulfill such that a cut can be generated */
125 int atwhichnodes; /**< determines at which nodes cut is used (if it's -1, it's used only at the root node,
126 if it's n >= 0, it's used at every multiple of n) */
127 int nstrengthlimit; /**< limit for number of rays we do the strengthening for */
128 SCIP_Real cutcoefsum; /**< sum of average cutcoefs of a cut */
129 SCIP_Bool ignorebadrayrestriction; /**< should cut be generated even with bad numerics when restricting to ray? */
130 SCIP_Bool ignorehighre; /**< should cut be added even when range / efficacy is large? */
131
132 /* statistics */
133 int ncouldimprovedcoef; /**< number of times a coefficient could improve but didn't because of numerics */
134 int nbadrayrestriction; /**< number of times a cut was aborted because of numerics when restricting to ray */
135 int nbadnonbasic; /**< number of times a cut was aborted because the nonbasic row was not nonbasic enough */
136 int nhighre; /**< number of times a cut was not added because range / efficacy was too large */
137 int nphinonneg; /**< number of times a cut was aborted because phi is nonnegative at 0 */
138 int nstrengthenings; /**< number of successful strengthenings */
139 int nboundcuts; /**< number of successful bound cuts */
140 SCIP_Real ncalls; /**< number of calls to separation */
141 SCIP_Real densitysum; /**< sum of density of cuts */
142 };
143
144 /* structure to store rays. note that for a given ray, the entries in raysidx are sorted. */
145 struct Rays
146 {
147 SCIP_Real* rays; /**< coefficients of rays */
148 int* raysidx; /**< to which var the coef belongs; vars are [quadvars, linvars, auxvar] */
149 int* raysbegin; /**< positions of rays: coefs of i-th ray [raybegin[i], raybegin[i+1]) */
150 int* lpposray; /**< lp pos of var associated with the ray;
151 >= 0 -> lppos of var; < 0 -> var is row -lpposray -1 */
152
153 int rayssize; /**< size of rays and rays idx */
154 int nrays; /**< size of raysbegin is nrays + 1; size of lpposray */
155 };
156 typedef struct Rays RAYS;
157
158
159 /*
160 * Callback methods of the table
161 */
162
163 /** output method of statistics table to output file stream 'file' */
164 static
165 SCIP_DECL_TABLEOUTPUT(tableOutputQuadratic)
166 { /*lint --e{715}*/
167 SCIP_NLHDLR* nlhdlr;
168 SCIP_NLHDLRDATA* nlhdlrdata;
169 SCIP_CONSHDLR* conshdlr;
170
171 conshdlr = SCIPfindConshdlr(scip, "nonlinear");
172 assert(conshdlr != NULL);
173 nlhdlr = SCIPfindNlhdlrNonlinear(conshdlr, NLHDLR_NAME);
174 assert(nlhdlr != NULL);
175 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
176 assert(nlhdlrdata);
177
178 /* print statistics */
179 SCIPinfoMessage(scip, file, "Quadratic Nlhdlr : %10s %10s %10s %10s %10s %10s %10s %10s %10s %10s %10s \n", "GenCuts", "AddCuts", "CouldImpr", "NLargeRE",
180 "AbrtBadRay", "AbrtPosPhi", "AbrtNonBas", "NStrength", "AveCutcoef", "AveDensity", "AveBCutsFrac");
181 SCIPinfoMessage(scip, file, " %-17s:", "Quadratic Nlhdlr");
182 SCIPinfoMessage(scip, file, " %10d", nlhdlrdata->ncutsgenerated);
183 SCIPinfoMessage(scip, file, " %10d", nlhdlrdata->ncutsadded);
184 SCIPinfoMessage(scip, file, " %10d", nlhdlrdata->ncouldimprovedcoef);
185 SCIPinfoMessage(scip, file, " %10d", nlhdlrdata->nhighre);
186 SCIPinfoMessage(scip, file, " %10d", nlhdlrdata->nbadrayrestriction);
187 SCIPinfoMessage(scip, file, " %10d", nlhdlrdata->nphinonneg);
188 SCIPinfoMessage(scip, file, " %10d", nlhdlrdata->nbadnonbasic);
189 SCIPinfoMessage(scip, file, " %10d", nlhdlrdata->nstrengthenings);
190 SCIPinfoMessage(scip, file, " %10g", nlhdlrdata->nstrengthenings > 0 ? nlhdlrdata->cutcoefsum / nlhdlrdata->nstrengthenings : 0.0);
191 SCIPinfoMessage(scip, file, " %10g", nlhdlrdata->ncutsadded > 0 ? nlhdlrdata->densitysum / nlhdlrdata->ncutsadded : 0.0);
192 SCIPinfoMessage(scip, file, " %10g", nlhdlrdata->ncalls > 0 ? nlhdlrdata->nboundcuts / nlhdlrdata->ncalls : 0.0);
193 SCIPinfoMessage(scip, file, "\n");
194
195 return SCIP_OKAY;
196 }
197
198
199 /*
200 * static methods
201 */
202
203 /** adds cutcoef * (col - col*) to rowprep */
204 static
205 SCIP_RETCODE addColToCut(
206 SCIP* scip, /**< SCIP data structure */
207 SCIP_ROWPREP* rowprep, /**< rowprep to store intersection cut */
208 SCIP_SOL* sol, /**< solution to separate */
209 SCIP_Real cutcoef, /**< cut coefficient */
210 SCIP_COL* col /**< column to add to rowprep */
211 )
212 {
213 assert(col != NULL);
214
215 #ifdef DEBUG_INTERCUTS_NUMERICS
216 SCIPinfoMessage(scip, NULL, "adding col %s to cut. %g <= col <= %g\n", SCIPvarGetName(SCIPcolGetVar(col)),
217 SCIPvarGetLbLocal(SCIPcolGetVar(col)), SCIPvarGetUbLocal(SCIPcolGetVar(col)));
218 SCIPinfoMessage(scip, NULL, "col is active at %s. Value %.15f\n", SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_LOWER ? "lower bound" :
219 "upper bound" , SCIPcolGetPrimsol(col));
220 #endif
221
222 SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, SCIPcolGetVar(col), cutcoef) );
223 SCIProwprepAddConstant(rowprep, -cutcoef * SCIPgetSolVal(scip, sol, SCIPcolGetVar(col)) );
224
225 return SCIP_OKAY;
226 }
227
228 /** adds cutcoef * (slack - slack*) to rowprep
229 *
230 * row is lhs ≤ <coefs, vars> + constant ≤ rhs, thus slack is defined by
231 * slack + <coefs.vars> + constant = side
232 *
233 * If row (slack) is at upper, it means that <coefs,vars*> + constant = rhs, and so
234 * slack* = side - rhs --> slack - slack* = rhs - <coefs, vars> - constant.
235 *
236 * If row (slack) is at lower, then <coefs,vars*> + constant = lhs, and so
237 * slack* = side - lhs --> slack - slack* = lhs - <coefs, vars> - constant.
238 */
239 static
240 SCIP_RETCODE addRowToCut(
241 SCIP* scip, /**< SCIP data structure */
242 SCIP_ROWPREP* rowprep, /**< rowprep to store intersection cut */
243 SCIP_Real cutcoef, /**< cut coefficient */
244 SCIP_ROW* row, /**< row, whose slack we are ading to rowprep */
245 SCIP_Bool* success /**< if the row is nonbasic enough */
246 )
247 {
248 int i;
249 SCIP_COL** rowcols;
250 SCIP_Real* rowcoefs;
251 int nnonz;
252
253 assert(row != NULL);
254
255 rowcols = SCIProwGetCols(row);
256 rowcoefs = SCIProwGetVals(row);
257 nnonz = SCIProwGetNLPNonz(row);
258
259 #ifdef DEBUG_INTERCUTS_NUMERICS
260 SCIPinfoMessage(scip, NULL, "adding slack var row_%d to cut. %g <= row <= %g\n", SCIProwGetLPPos(row), SCIProwGetLhs(row), SCIProwGetRhs(row));
261 SCIPinfoMessage(scip, NULL, "row is active at %s = %.15f Activity %.15f\n", SCIProwGetBasisStatus(row) == SCIP_BASESTAT_LOWER ? "lhs" :
262 "rhs" , SCIProwGetBasisStatus(row) == SCIP_BASESTAT_LOWER ? SCIProwGetLhs(row) : SCIProwGetRhs(row),
263 SCIPgetRowActivity(scip, row));
264 #endif
265
266 if( SCIProwGetBasisStatus(row) == SCIP_BASESTAT_LOWER )
267 {
268 assert(!SCIPisInfinity(scip, -SCIProwGetLhs(row)));
269 if( ! SCIPisFeasEQ(scip, SCIProwGetLhs(row), SCIPgetRowActivity(scip, row)) )
270 {
271 *success = FALSE;
272 return SCIP_OKAY;
273 }
274
275 SCIProwprepAddConstant(rowprep, SCIProwGetLhs(row) * cutcoef);
276 }
277 else
278 {
279 assert(!SCIPisInfinity(scip, SCIProwGetRhs(row)));
280 if( ! SCIPisFeasEQ(scip, SCIProwGetRhs(row), SCIPgetRowActivity(scip, row)) )
281 {
282 *success = FALSE;
283 return SCIP_OKAY;
284 }
285
286 SCIProwprepAddConstant(rowprep, SCIProwGetRhs(row) * cutcoef);
287 }
288
289 for( i = 0; i < nnonz; i++ )
290 {
291 SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, SCIPcolGetVar(rowcols[i]), -rowcoefs[i] * cutcoef) );
292 }
293
294 SCIProwprepAddConstant(rowprep, -SCIProwGetConstant(row) * cutcoef);
295
296 return SCIP_OKAY;
297 }
298
299 /** constructs map between lp position of a basic variable and its row in the tableau */
300 static
301 SCIP_RETCODE constructBasicVars2TableauRowMap(
302 SCIP* scip, /**< SCIP data structure */
303 int* map /**< buffer to store the map */
304 )
305 {
306 int* basisind;
307 int nrows;
308 int i;
309
310 nrows = SCIPgetNLPRows(scip);
311 SCIP_CALL( SCIPallocBufferArray(scip, &basisind, nrows) );
312
313 SCIP_CALL( SCIPgetLPBasisInd(scip, basisind) );
314 for( i = 0; i < nrows; ++i )
315 {
316 if( basisind[i] >= 0 )
317 map[basisind[i]] = i;
318 }
319
320 SCIPfreeBufferArray(scip, &basisind);
321
322 return SCIP_OKAY;
323 }
324
325 /** counts the number of basic variables in the quadratic expr */
326 static
327 int countBasicVars(
328 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nlhdlr expression data */
329 SCIP_VAR* auxvar, /**< aux var of expr or NULL if not needed (e.g. separating real cons) */
330 SCIP_Bool* nozerostat /**< whether there is no variable with basis status zero */
331 )
332 {
333 SCIP_EXPR* qexpr;
334 SCIP_EXPR** linexprs;
335 SCIP_COL* col;
336 int i;
337 int nbasicvars = 0;
338 int nquadexprs;
339 int nlinexprs;
340
341 *nozerostat = TRUE;
342
343 qexpr = nlhdlrexprdata->qexpr;
344 SCIPexprGetQuadraticData(qexpr, NULL, &nlinexprs, &linexprs, NULL, &nquadexprs, NULL, NULL, NULL);
345
346 /* loop over quadratic vars */
347 for( i = 0; i < nquadexprs; ++i )
348 {
349 SCIP_EXPR* expr;
350
351 SCIPexprGetQuadraticQuadTerm(qexpr, i, &expr, NULL, NULL, NULL, NULL, NULL);
352
353 col = SCIPvarGetCol(SCIPgetExprAuxVarNonlinear(expr));
354 if( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_BASIC )
355 nbasicvars += 1;
356 else if( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_ZERO )
357 {
358 *nozerostat = FALSE;
359 return 0;
360 }
361 }
362
363 /* loop over linear vars */
364 for( i = 0; i < nlinexprs; ++i )
365 {
366 col = SCIPvarGetCol(SCIPgetExprAuxVarNonlinear(linexprs[i]));
367 if( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_BASIC )
368 nbasicvars += 1;
369 else if( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_ZERO )
370 {
371 *nozerostat = FALSE;
372 return 0;
373 }
374 }
375
376 /* finally consider the aux var (if it exists) */
377 if( auxvar != NULL )
378 {
379 col = SCIPvarGetCol(auxvar);
380 if( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_BASIC )
381 nbasicvars += 1;
382 else if( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_ZERO )
383 {
384 *nozerostat = FALSE;
385 return 0;
386 }
387 }
388
389 return nbasicvars;
390 }
391
392 /** stores the row of the tableau where `col` is basic
393 *
394 * In general, we will have
395 *
396 * basicvar1 = tableaurow var1
397 * basicvar2 = tableaurow var2
398 * ...
399 * basicvarn = tableaurow varn
400 *
401 * However, we want to store the the tableau row by columns. Thus, we need to know which of the basic vars `col` is.
402 *
403 * Note we only store the entries of the nonbasic variables
404 */
405 static
406 SCIP_RETCODE storeDenseTableauRow(
407 SCIP* scip, /**< SCIP data structure */
408 SCIP_COL* col, /**< basic columns to store its tableau row */
409 int* basicvarpos2tableaurow,/**< map between basic var and its tableau row */
410 int nbasiccol, /**< which basic var this is */
411 int raylength, /**< the length of a ray (the total number of basic vars) */
412 SCIP_Real* binvrow, /**< buffer to store row of Binv */
413 SCIP_Real* binvarow, /**< buffer to store row of Binv A */
414 SCIP_Real* tableaurows /**< pointer to store the tableau rows */
415 )
416 {
417 int nrows;
418 int ncols;
419 int lppos;
420 int i;
421 SCIP_COL** cols;
422 SCIP_ROW** rows;
423 int nray;
424
425 assert(nbasiccol < raylength);
426 assert(col != NULL);
427 assert(binvrow != NULL);
428 assert(binvarow != NULL);
429 assert(tableaurows != NULL);
430 assert(basicvarpos2tableaurow != NULL);
431 assert(SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_BASIC);
432
433 SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) );
434 SCIP_CALL( SCIPgetLPColsData(scip, &cols, &ncols) );
435
436 lppos = SCIPcolGetLPPos(col);
437
438 assert(basicvarpos2tableaurow[lppos] >= 0);
439
440 SCIP_CALL( SCIPgetLPBInvRow(scip, basicvarpos2tableaurow[lppos], binvrow, NULL, NULL) );
441 SCIP_CALL( SCIPgetLPBInvARow(scip, basicvarpos2tableaurow[lppos], binvrow, binvarow, NULL, NULL) );
442
443 nray = 0;
444 for( i = 0; i < ncols; ++i )
445 if( SCIPcolGetBasisStatus(cols[i]) != SCIP_BASESTAT_BASIC )
446 {
447 tableaurows[nbasiccol + nray * raylength] = binvarow[i];
448 nray++;
449 }
450 for( ; i < ncols + nrows; ++i )
451 if( SCIProwGetBasisStatus(rows[i - ncols]) != SCIP_BASESTAT_BASIC )
452 {
453 tableaurows[nbasiccol + nray * raylength] = binvrow[i - ncols];
454 nray++;
455 }
456
457 return SCIP_OKAY;
458 }
459
460 /** stores the rows of the tableau corresponding to the basic variables in the quadratic expression
461 *
462 * Also return a map storing to which var the entry of a ray corresponds, i.e., if the tableau is
463 *
464 * basicvar_1 = ray1_1 nonbasicvar_1 + ...
465 * basicvar_2 = ray1_2 nonbasicvar_1 + ...
466 * ...
467 * basicvar_n = ray1_n nonbasicvar_1 + ...
468 *
469 * The map maps k to the position of basicvar_k in the variables of the constraint assuming the variables are sorted as
470 * [quadratic vars, linear vars, auxvar].
471 */
472 static
473 SCIP_RETCODE storeDenseTableauRowsByColumns(
474 SCIP* scip, /**< SCIP data structure */
475 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nlhdlr expression data */
476 int raylength, /**< length of a ray of the tableau */
477 SCIP_VAR* auxvar, /**< aux var of expr or NULL if not needed (e.g. separating real cons) */
478 SCIP_Real* tableaurows, /**< buffer to store the tableau rows */
479 int* rayentry2conspos /**< buffer to store the map */
480 )
481 {
482 SCIP_EXPR* qexpr;
483 SCIP_EXPR** linexprs;
484 SCIP_Real* binvarow;
485 SCIP_Real* binvrow;
486 SCIP_COL* col;
487 int* basicvarpos2tableaurow; /* map between basic var and its tableau row */
488 int nrayentries;
489 int nquadexprs;
490 int nlinexprs;
491 int nrows;
492 int ncols;
493 int i;
494
495 nrows = SCIPgetNLPRows(scip);
496 ncols = SCIPgetNLPCols(scip);
497
498 SCIP_CALL( SCIPallocBufferArray(scip, &basicvarpos2tableaurow, ncols) );
499 SCIP_CALL( SCIPallocBufferArray(scip, &binvrow, nrows) );
500 SCIP_CALL( SCIPallocBufferArray(scip, &binvarow, ncols) );
501
502 for( i = 0; i < ncols; ++i )
503 basicvarpos2tableaurow[i] = -1;
504 SCIP_CALL( constructBasicVars2TableauRowMap(scip, basicvarpos2tableaurow) );
505
506 qexpr = nlhdlrexprdata->qexpr;
507 SCIPexprGetQuadraticData(qexpr, NULL, &nlinexprs, &linexprs, NULL, &nquadexprs, NULL, NULL, NULL);
508
509 /* entries of quadratic basic vars */
510 nrayentries = 0;
511 for( i = 0; i < nquadexprs; ++i )
512 {
513 SCIP_EXPR* expr;
514 SCIPexprGetQuadraticQuadTerm(qexpr, i, &expr, NULL, NULL, NULL, NULL, NULL);
515
516 col = SCIPvarGetCol(SCIPgetExprAuxVarNonlinear(expr));
517 if( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_BASIC )
518 {
519 SCIP_CALL( storeDenseTableauRow(scip, col, basicvarpos2tableaurow, nrayentries, raylength, binvrow, binvarow,
520 tableaurows) );
521
522 rayentry2conspos[nrayentries] = i;
523 nrayentries++;
524 }
525 }
526 /* entries of linear vars */
527 for( i = 0; i < nlinexprs; ++i )
528 {
529 col = SCIPvarGetCol(SCIPgetExprAuxVarNonlinear(linexprs[i]));
530 if( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_BASIC )
531 {
532 SCIP_CALL( storeDenseTableauRow(scip, col, basicvarpos2tableaurow, nrayentries, raylength, binvrow, binvarow,
533 tableaurows) );
534
535 rayentry2conspos[nrayentries] = nquadexprs + i;
536 nrayentries++;
537 }
538 }
539 /* entry of aux var (if it exists) */
540 if( auxvar != NULL )
541 {
542 col = SCIPvarGetCol(auxvar);
543 if( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_BASIC )
544 {
545 SCIP_CALL( storeDenseTableauRow(scip, col, basicvarpos2tableaurow, nrayentries, raylength, binvrow, binvarow,
546 tableaurows) );
547
548 rayentry2conspos[nrayentries] = nquadexprs + nlinexprs;
549 nrayentries++;
550 }
551 }
552 assert(nrayentries == raylength);
553
554 #ifdef DEBUG_INTERSECTIONCUT
555 for( i = 0; i < ncols; ++i )
556 {
557 SCIPinfoMessage(scip, NULL, "%d column of tableau is: ",i);
558 for( int j = 0; j < raylength; ++j )
559 SCIPinfoMessage(scip, NULL, "%g ", tableaurows[i * raylength + j]);
560 SCIPinfoMessage(scip, NULL, "\n");
561 }
562 #endif
563
564 SCIPfreeBufferArray(scip, &binvarow);
565 SCIPfreeBufferArray(scip, &binvrow);
566 SCIPfreeBufferArray(scip, &basicvarpos2tableaurow);
567
568 return SCIP_OKAY;
569 }
570
571 /** initializes rays data structure */
572 static
573 SCIP_RETCODE createRays(
574 SCIP* scip, /**< SCIP data structure */
575 RAYS** rays /**< rays data structure */
576 )
577 {
578 SCIP_CALL( SCIPallocBuffer(scip, rays) );
579 BMSclearMemory(*rays);
580
581 SCIP_CALL( SCIPallocBufferArray(scip, &(*rays)->rays, SCIPgetNLPCols(scip)) );
582 SCIP_CALL( SCIPallocBufferArray(scip, &(*rays)->raysidx, SCIPgetNLPCols(scip)) );
583
584 /* overestimate raysbegin and lpposray */
585 SCIP_CALL( SCIPallocBufferArray(scip, &(*rays)->raysbegin, SCIPgetNLPCols(scip) + SCIPgetNLPRows(scip) + 1) );
586 SCIP_CALL( SCIPallocBufferArray(scip, &(*rays)->lpposray, SCIPgetNLPCols(scip) + SCIPgetNLPRows(scip)) );
587 (*rays)->raysbegin[0] = 0;
588
589 (*rays)->rayssize = SCIPgetNLPCols(scip);
590
591 return SCIP_OKAY;
592 }
593
594 /** initializes rays data structure for bound rays */
595 static
596 SCIP_RETCODE createBoundRays(
597 SCIP* scip, /**< SCIP data structure */
598 RAYS** rays, /**< rays data structure */
599 int size /**< number of rays to allocate */
600 )
601 {
602 SCIP_CALL( SCIPallocBuffer(scip, rays) );
603 BMSclearMemory(*rays);
604
605 SCIP_CALL( SCIPallocBufferArray(scip, &(*rays)->rays, size) );
606 SCIP_CALL( SCIPallocBufferArray(scip, &(*rays)->raysidx, size) );
607
608 /* overestimate raysbegin and lpposray */
609 SCIP_CALL( SCIPallocBufferArray(scip, &(*rays)->raysbegin, size + 1) );
610 SCIP_CALL( SCIPallocBufferArray(scip, &(*rays)->lpposray, size) );
611 (*rays)->raysbegin[0] = 0;
612
613 (*rays)->rayssize = size;
614
615 return SCIP_OKAY;
616 }
617
618 /** frees rays data structure */
619 static
620 void freeRays(
621 SCIP* scip, /**< SCIP data structure */
622 RAYS** rays /**< rays data structure */
623 )
624 {
625 if( *rays == NULL )
626 return;
627
628 SCIPfreeBufferArray(scip, &(*rays)->lpposray);
629 SCIPfreeBufferArray(scip, &(*rays)->raysbegin);
630 SCIPfreeBufferArray(scip, &(*rays)->raysidx);
631 SCIPfreeBufferArray(scip, &(*rays)->rays);
632
633 SCIPfreeBuffer(scip, rays);
634 }
635
636 /** inserts entry to rays data structure; checks and resizes if more space is needed */
637 static
638 SCIP_RETCODE insertRayEntry(
639 SCIP* scip, /**< SCIP data structure */
640 RAYS* rays, /**< rays data structure */
641 SCIP_Real coef, /**< coefficient to insert */
642 int coefidx, /**< index of coefficient (conspos of var it corresponds to) */
643 int coefpos /**< where to insert coefficient */
644 )
645 {
646 /* check for size */
647 if( rays->rayssize <= coefpos + 1 )
648 {
649 int newsize;
650
651 newsize = SCIPcalcMemGrowSize(scip, coefpos + 1);
652 SCIP_CALL( SCIPreallocBufferArray(scip, &(rays->rays), newsize) );
653 SCIP_CALL( SCIPreallocBufferArray(scip, &(rays->raysidx), newsize) );
654 rays->rayssize = newsize;
655 }
656
657 /* insert entry */
658 rays->rays[coefpos] = coef;
659 rays->raysidx[coefpos] = coefidx;
660
661 return SCIP_OKAY;
662 }
663
664 /** constructs map between the lppos of a variables and its position in the constraint assuming the constraint variables
665 * are sorted as [quad vars, lin vars, aux var (if it exists)]
666 *
667 * If a variable doesn't appear in the constraint, then its position is -1.
668 */
669 static
670 void constructLPPos2ConsPosMap(
671 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nlhdlr expression data */
672 SCIP_VAR* auxvar, /**< aux var of the expr */
673 int* map /**< buffer to store the mapping */
674 )
675 {
676 SCIP_EXPR* qexpr;
677 SCIP_EXPR** linexprs;
678 int nquadexprs;
679 int nlinexprs;
680 int lppos;
681 int i;
682
683 qexpr = nlhdlrexprdata->qexpr;
684 SCIPexprGetQuadraticData(qexpr, NULL, &nlinexprs, &linexprs, NULL, &nquadexprs, NULL, NULL, NULL);
685
686 /* set pos of quadratic vars */
687 for( i = 0; i < nquadexprs; ++i )
688 {
689 SCIP_EXPR* expr;
690 SCIPexprGetQuadraticQuadTerm(qexpr, i, &expr, NULL, NULL, NULL, NULL, NULL);
691
692 lppos = SCIPcolGetLPPos(SCIPvarGetCol(SCIPgetExprAuxVarNonlinear(expr)));
693 map[lppos] = i;
694 }
695 /* set pos of lin vars */
696 for( i = 0; i < nlinexprs; ++i )
697 {
698 lppos = SCIPcolGetLPPos(SCIPvarGetCol(SCIPgetExprAuxVarNonlinear(linexprs[i])));
699 map[lppos] = nquadexprs + i;
700 }
701 /* set pos of aux var (if it exists) */
702 if( auxvar != NULL )
703 {
704 lppos = SCIPcolGetLPPos(SCIPvarGetCol(auxvar));
705 map[lppos] = nquadexprs + nlinexprs;
706 }
707
708 return;
709 }
710
711 /** inserts entries of factor * nray-th column of densetableaucols into rays data structure */
712 static
713 SCIP_RETCODE insertRayEntries(
714 SCIP* scip, /**< SCIP data structure */
715 RAYS* rays, /**< rays data structure */
716 SCIP_Real* densetableaucols, /**< column of the tableau in dense format */
717 int* rayentry2conspos, /**< map between rayentry and conspos of associated var */
718 int raylength, /**< length of a tableau column */
719 int nray, /**< which tableau column to insert */
720 int conspos, /**< conspos of ray's nonbasic var in the cons; -1 if not in the cons */
721 SCIP_Real factor, /**< factor to multiply each tableau col */
722 int* nnonz, /**< position to start adding the ray in rays and buffer to store nnonz */
723 SCIP_Bool* success /**< we can't separate if there is a nonzero ray with basis status ZERO */
724 )
725 {
726 int i;
727
728 *success = TRUE;
729
730 for( i = 0; i < raylength; ++i )
731 {
732 SCIP_Real coef;
733
734 /* we have a nonzero ray with base stat zero -> can't generate cut */
735 if( factor == 0.0 && ! SCIPisZero(scip, densetableaucols[nray * raylength + i]) )
736 {
737 *success = FALSE;
738 return SCIP_OKAY;
739 }
740
741 coef = factor * densetableaucols[nray * raylength + i];
742
743 /* this might be a source of numerical issues
744 * TODO: in case of problems, an idea would be to scale the ray entries; compute the cut coef and scale it back down
745 * another idea would be to check against a smaller epsilion.
746 * The problem is that if the cut coefficient is 1/t where lpsol + t*ray intersects the S-free set.
747 * Now if t is super big, then a super small coefficient would have had an impact...
748 */
749 if( SCIPisZero(scip, coef) )
750 continue;
751
752 /* check if nonbasic var entry should come before this one */
753 if( conspos > -1 && conspos < rayentry2conspos[i] )
754 {
755 /* add nonbasic entry */
756 assert(factor != 0.0);
757
758 #ifdef DEBUG_INTERSECTIONCUT
759 SCIPinfoMessage(scip, NULL, "ray belongs to nonbasic var pos %d in cons\n", conspos);
760 #endif
761
762 SCIP_CALL( insertRayEntry(scip, rays, -factor, conspos, *nnonz) );
763 (*nnonz)++;
764
765 /* we are done with nonbasic entry */
766 conspos = -1;
767 }
768
769 SCIP_CALL( insertRayEntry(scip, rays, coef, rayentry2conspos[i], *nnonz) );
770 (*nnonz)++;
771 }
772
773 /* if nonbasic entry was not added and should still be added, then it should go at the end */
774 if( conspos > -1 )
775 {
776 /* add nonbasic entry */
777 assert(factor != 0.0);
778
779 #ifdef DEBUG_INTERSECTIONCUT
780 SCIPinfoMessage(scip, NULL, "ray belongs to nonbasic var pos %d in cons\n", conspos);
781 #endif
782
783 SCIP_CALL( insertRayEntry(scip, rays, -factor, conspos, *nnonz) );
784 (*nnonz)++;
785 }
786
787 /* finished ray entry; store its end */
788 rays->raysbegin[rays->nrays + 1] = *nnonz;
789
790 #ifdef DEBUG_INTERSECTIONCUT
791 SCIPinfoMessage(scip, NULL, "entries of ray %d are between [%d, %d):\n", rays->nrays, rays->raysbegin[rays->nrays], *nnonz);
792 for( i = rays->raysbegin[rays->nrays]; i < *nnonz; ++i )
793 SCIPinfoMessage(scip, NULL, "(%d, %g), ", rays->raysidx[i], rays->rays[i]);
794 SCIPinfoMessage(scip, NULL, "\n");
795 #endif
796
797 return SCIP_OKAY;
798 }
799
800 /** stores rays in sparse form
801 *
802 * The first rays correspond to the nonbasic variables
803 * and the last rays to the nonbasic slack variables.
804 *
805 * More details: The LP tableau is of the form
806 *
807 * basicvar_1 = ray1_1 nonbasicvar_1 + ... + raym_1 nonbasicvar_m
808 * basicvar_2 = ray1_2 nonbasicvar_1 + ... + raym_2 nonbasicvar_m
809 * ...
810 * basicvar_n = ray1_n nonbasicvar_1 + ... + raym_n nonbasicvar_m
811 * nonbasicvar_1 = 1.0 nonbasicvar_1 + ... + 0.0 nonbasicvar_m
812 * ...
813 * nonbasicvar_m = 0.0 nonbasicvar_1 + ... + 1.0 nonbasicvar_m
814 *
815 * so rayk = (rayk_1, ... rayk_n, e_k)
816 * We store the entries of the rays associated to the variables present in the quadratic expr.
817 * We do not store zero rays.
818 *
819 * Also, we store the rays as if every nonbasic variable was at lower (so that all rays moves to infinity)
820 * Since the tableau is:
821 *
822 * basicvar + Binv L (nonbasic_lower - lb) + Binv U (nonbasic_upper - ub) = basicvar_sol
823 *
824 * then:
825 *
826 * basicvar = basicvar_sol - Binv L (nonbasic_lower - lb) + Binv U (ub - nonbasic_upper)
827 *
828 * and so the entries of the rays associated with the basic variables are:
829 * rays_basicvars = [-BinvL, BinvU].
830 *
831 * So we flip the sign of the rays associated to nonbasic vars at lower.
832 * In constrast, the nonbasic part of the ray has a 1.0 for nonbasic at lower and a -1.0 for nonbasic at upper, i.e.
833 * nonbasic_lower = lb + 1.0(nonbasic_lower - lb) and
834 * nonbasic_upper = ub - 1.0(ub - nonbasic_upper)
835 */
836 static
837 SCIP_RETCODE createAndStoreSparseRays(
838 SCIP* scip, /**< SCIP data structure */
839 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nlhdlr expression data */
840 SCIP_VAR* auxvar, /**< aux var of expr or NULL if not needed (e.g. separating real cons) */
841 RAYS** raysptr, /**< buffer to store rays datastructure */
842 SCIP_Bool* success /**< we can't separate if there is a var with basis status ZERO */
843 )
844 {
845 SCIP_Real* densetableaucols;
846 SCIP_COL** cols;
847 SCIP_ROW** rows;
848 RAYS* rays;
849 int* rayentry2conspos;
850 int* lppos2conspos;
851 int nnonbasic;
852 int nrows;
853 int ncols;
854 int nnonz;
855 int raylength;
856 int i;
857
858 nrows = SCIPgetNLPRows(scip);
859 ncols = SCIPgetNLPCols(scip);
860
861 *success = TRUE;
862
863 raylength = countBasicVars(nlhdlrexprdata, auxvar, success);
864 if( ! *success )
865 {
866 SCIPdebugMsg(scip, "failed to store sparse rays: there is a var with base status zero\n");
867 return SCIP_OKAY;
868 }
869
870 SCIP_CALL( SCIPallocBufferArray(scip, &densetableaucols, raylength * (ncols + nrows)) );
871 SCIP_CALL( SCIPallocBufferArray(scip, &rayentry2conspos, raylength) );
872
873 /* construct dense tableau and map between ray entries and position of corresponding var in quad cons */
874 SCIP_CALL( storeDenseTableauRowsByColumns(scip, nlhdlrexprdata, raylength, auxvar,
875 densetableaucols, rayentry2conspos) );
876
877 /* build rays sparsely now */
878 SCIP_CALL( SCIPallocBufferArray(scip, &lppos2conspos, ncols) );
879 for( i = 0; i < ncols; ++i )
880 lppos2conspos[i] = -1;
881
882 constructLPPos2ConsPosMap(nlhdlrexprdata, auxvar, lppos2conspos);
883
884 /* store sparse rays */
885 SCIP_CALL( createRays(scip, raysptr) );
886 rays = *raysptr;
887
888 nnonz = 0;
889 nnonbasic = 0;
890
891 /* go through nonbasic variables */
892 cols = SCIPgetLPCols(scip);
893 for( i = 0; i < ncols; ++i )
894 {
895 int oldnnonz = nnonz;
896 SCIP_COL* col;
897 SCIP_Real factor;
898
899 col = cols[i];
900
901 /* set factor to store basic entries of ray as = [-BinvL, BinvU] */
902 if( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_LOWER )
903 factor = -1.0;
904 else if( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_UPPER )
905 factor = 1.0;
906 else if( SCIPcolGetBasisStatus(col) == SCIP_BASESTAT_ZERO )
907 factor = 0.0;
908 else
909 continue;
910
911 SCIP_CALL( insertRayEntries(scip, rays, densetableaucols, rayentry2conspos, raylength, nnonbasic,
912 lppos2conspos[SCIPcolGetLPPos(col)], factor, &nnonz, success) );
913
914 #ifdef DEBUG_INTERSECTIONCUT
915 SCIPinfoMessage(scip, NULL, "looked at ray of var %s with basestat %d, it has %d nonzeros\n-----------------\n",
916 SCIPvarGetName(SCIPcolGetVar(col)), SCIPcolGetBasisStatus(col), nnonz - oldnnonz);
917 #endif
918 if( ! (*success) )
919 {
920 #ifdef DEBUG_INTERSECTIONCUT
921 SCIPdebugMsg(scip, "nonzero ray associated with variable <%s> has base status zero -> abort storing rays\n",
922 SCIPvarGetName(SCIPcolGetVar(col)));
923 #endif
924 goto CLEANUP;
925 }
926
927 /* if ray is non zero remember who it belongs to */
928 assert(oldnnonz <= nnonz);
929 if( oldnnonz < nnonz )
930 {
931 rays->lpposray[rays->nrays] = SCIPcolGetLPPos(col);
932 (rays->nrays)++;
933 }
934 nnonbasic++;
935 }
936
937 /* go through nonbasic slack variables */
938 rows = SCIPgetLPRows(scip);
939 for( i = 0; i < nrows; ++i )
940 {
941 int oldnnonz = nnonz;
942 SCIP_ROW* row;
943 SCIP_Real factor;
944
945 row = rows[i];
946
947 /* set factor to store basic entries of ray as = [-BinvL, BinvU]; basic status of rows are flipped! See lpi.h! */
948 assert(SCIProwGetBasisStatus(row) != SCIP_BASESTAT_ZERO);
949 if( SCIProwGetBasisStatus(row) == SCIP_BASESTAT_LOWER )
950 factor = 1.0;
951 else if( SCIProwGetBasisStatus(row) == SCIP_BASESTAT_UPPER )
952 factor =-1.0;
953 else
954 continue;
955
956 SCIP_CALL( insertRayEntries(scip, rays, densetableaucols, rayentry2conspos, raylength, nnonbasic, -1, factor,
957 &nnonz, success) );
958 assert(*success);
959
960 /* if ray is non zero remember who it belongs to */
961 #ifdef DEBUG_INTERSECTIONCUT
962 SCIPinfoMessage(scip, NULL, "looked at ray of row %d, it has %d nonzeros\n-----------------\n", i, nnonz - oldnnonz);
963 #endif
964 assert(oldnnonz <= nnonz);
965 if( oldnnonz < nnonz )
966 {
967 rays->lpposray[rays->nrays] = -SCIProwGetLPPos(row) - 1;
968 (rays->nrays)++;
969 }
970 nnonbasic++;
971 }
972
973 CLEANUP:
974 SCIPfreeBufferArray(scip, &lppos2conspos);
975 SCIPfreeBufferArray(scip, &rayentry2conspos);
976 SCIPfreeBufferArray(scip, &densetableaucols);
977
978 if( ! *success )
979 {
980 freeRays(scip, &rays);
981 }
982
983 return SCIP_OKAY;
984 }
985
986 /* TODO: which function this comment belongs to? */
987 /* this function determines how the maximal S-free set is going to look like
988 *
989 * There are 4 possibilities: after writing the quadratic constraint
990 * \f$q(z) \leq 0\f$
991 * as
992 * \f$\Vert x(z)\Vert^2 - \Vert y\Vert^2 + w(z) + kappa \leq 0\f$,
993 * the cases are determined according to the following:
994 * - Case 1: w = 0 and kappa = 0
995 * - Case 2: w = 0 and kappa > 0
996 * - Case 3: w = 0 and kappa < 0
997 * - Case 4: w != 0
998 */
999
1000 /** compute quantities for intersection cuts
1001 *
1002 * Assume the quadratic is stored as
1003 * \f[ q(z) = z_q^T Q z_q + b_q^T z_q + b_l z_l + c - z_a \f]
1004 * where:
1005 * - \f$z_q\f$ are the quadratic vars
1006 * - \f$z_l\f$ are the linear vars
1007 * - \f$z_a\f$ is the aux var if it exists
1008 *
1009 * We can rewrite it as
1010 * \f[ \Vert x(z)\Vert^2 - \Vert y\Vert^2 + w(z) + \kappa \leq 0. \f]
1011 * To do this transformation and later to compute the actual cut we need to compute and store some quantities.
1012 * Let
1013 * - \f$I_0\f$, \f$I_+\f$, and \f$I_-\f$ be the index set of zero, positive, and negative eigenvalues, respectively
1014 * - \f$v_i\f$ be the i-th eigenvector of \f$Q\f$
1015 * - \f$zlp\f$ be the lp value of the variables \f$z\f$
1016 *
1017 * The quantities we need are:
1018 * - \f$vb_i = v_i^T b\f$ for \f$i \in I_+ \cup I_-\f$
1019 * - \f$vzlp_i = v_i^T zlp_q\f$ for \f$i \in I_+ \cup I_-\f$
1020 * - \f$\kappa = c - 1/4 \sum_{i \in I_+ \cup I_-} (v_i^T b_q)^2 / eigval_i\f$
1021 * - \f$w(z) = (\sum_{i \in I_0} v_i^T b_q v_i^T) z_q + b_l^T z_l - z_a\f$
1022 * - \f$w(zlp)\f$
1023 *
1024 * @return \f$\kappa\f$ and the vector \f$\sum_{i \in I_0} v_i^T b_q v_i^T\f$
1025 *
1026 * @note if the constraint is q(z) ≤ rhs, then the constant when writing the constraint as quad ≤ 0 is c - rhs.
1027 * @note if the quadratic constraint we are separating is q(z) ≥ lhs, then we multiply by -1.
1028 * In practice, what changes is
1029 * - the sign of the eigenvalues
1030 * - the sign of \f$b_q\f$ and \f$b_l\f$
1031 * - the sign of the coefficient of the auxvar (if it exists)
1032 * - the constant of the quadratic written as quad ≤ 0 is lhs - c
1033 * @note The eigenvectors _do not_ change sign!
1034 */
1035 static
1036 SCIP_RETCODE intercutsComputeCommonQuantities(
1037 SCIP* scip, /**< SCIP data structure */
1038 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nlhdlr expression data */
1039 SCIP_VAR* auxvar, /**< aux var of expr or NULL if not needed (e.g. separating real cons) */
1040 SCIP_Real sidefactor, /**< 1.0 if the violated constraint is q ≤ rhs, -1.0 otherwise */
1041 SCIP_SOL* sol, /**< solution to separate */
1042 SCIP_Real* vb, /**< buffer to store \f$v_i^T b\f$ for \f$i \in I_+ \cup I_-\f$ */
1043 SCIP_Real* vzlp, /**< buffer to store \f$v_i^T zlp_q\f$ for \f$i \in I_+ \cup I_-\f$ */
1044 SCIP_Real* wcoefs, /**< buffer to store the coefs of quad vars of w */
1045 SCIP_Real* wzlp, /**< pointer to store the value of w at zlp */
1046 SCIP_Real* kappa /**< pointer to store the value of kappa */
1047 )
1048 {
1049 SCIP_EXPR* qexpr;
1050 SCIP_EXPR** linexprs;
1051 SCIP_Real* eigenvectors;
1052 SCIP_Real* eigenvalues;
1053 SCIP_Real* lincoefs;
1054 SCIP_Real constant; /* constant of the quadratic when written as <= 0 */
1055 int nquadexprs;
1056 int nlinexprs;
1057 int i;
1058 int j;
1059
1060 assert(sidefactor == 1.0 || sidefactor == -1.0);
1061 assert(nlhdlrexprdata->cons != NULL || auxvar != NULL );
1062
1063 qexpr = nlhdlrexprdata->qexpr;
1064 SCIPexprGetQuadraticData(qexpr, &constant, &nlinexprs, &linexprs, &lincoefs, &nquadexprs, NULL, &eigenvalues,
1065 &eigenvectors);
1066
1067 assert( eigenvalues != NULL );
1068
1069 /* first get constant of quadratic when written as quad <= 0 */
1070 if( nlhdlrexprdata->cons != NULL )
1071 constant = (sidefactor == 1.0) ? constant - SCIPgetRhsNonlinear(nlhdlrexprdata->cons) :
1072 SCIPgetLhsNonlinear(nlhdlrexprdata->cons) - constant;
1073 else
1074 constant = (sidefactor * constant);
1075
1076 *kappa = 0.0;
1077 *wzlp = 0.0;
1078 BMSclearMemoryArray(wcoefs, nquadexprs);
1079
1080 for( i = 0; i < nquadexprs; ++i )
1081 {
1082 SCIP_Real vdotb;
1083 SCIP_Real vdotzlp;
1084 int offset;
1085
1086 offset = i * nquadexprs;
1087
1088 /* compute v_i^T b and v_i^T zlp */
1089 vdotb = 0;
1090 vdotzlp = 0;
1091 for( j = 0; j < nquadexprs; ++j )
1092 {
1093 SCIP_EXPR* expr;
1094 SCIP_Real lincoef;
1095
1096 SCIPexprGetQuadraticQuadTerm(qexpr, j, &expr, &lincoef, NULL, NULL, NULL, NULL);
1097
1098 vdotb += (sidefactor * lincoef) * eigenvectors[offset + j];
1099 #ifdef INTERCUT_MOREDEBUG
1100 printf("vdotb: offset %d, eigenvector %d = %g, lincoef quad %g\n", offset, j,
1101 eigenvectors[offset + j], lincoef);
1102 #endif
1103 vdotzlp += SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(expr)) * eigenvectors[offset + j];
1104 }
1105 vb[i] = vdotb;
1106 vzlp[i] = vdotzlp;
1107
1108 if( ! SCIPisZero(scip, eigenvalues[i]) )
1109 {
1110 /* nonzero eigenvalue: compute kappa */
1111 *kappa += SQR(vdotb) / (sidefactor * eigenvalues[i]);
1112 }
1113 else
1114 {
1115 /* compute coefficients of w and compute w at zlp */
1116 for( j = 0; j < nquadexprs; ++j )
1117 wcoefs[j] += vdotb * eigenvectors[offset + j];
1118
1119 *wzlp += vdotb * vdotzlp;
1120 }
1121 }
1122
1123 /* finish kappa computation */
1124 *kappa *= -0.25;
1125 *kappa += constant;
1126
1127 /* finish w(zlp) computation: linear part (including auxvar, if applicable) */
1128 for( i = 0; i < nlinexprs; ++i )
1129 {
1130 *wzlp += (sidefactor * lincoefs[i]) * SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(linexprs[i]));
1131 }
1132 if( auxvar != NULL )
1133 {
1134 *wzlp += (sidefactor * -1.0) * SCIPgetSolVal(scip, sol, auxvar);
1135 }
1136
1137 #ifdef DEBUG_INTERSECTIONCUT
1138 SCIPinfoMessage(scip, NULL, "Computed common quantities needed for intercuts:\n");
1139 SCIPinfoMessage(scip, NULL, " kappa = %g\n quad part w = ", *kappa);
1140 for( i = 0; i < nquadexprs; ++i )
1141 {
1142 SCIPinfoMessage(scip, NULL, "%g ", wcoefs[i]);
1143 }
1144 SCIPinfoMessage(scip, NULL, "\n");
1145 #endif
1146 return SCIP_OKAY;
1147 }
1148
1149 /** computes eigenvec^T ray */
1150 static
1151 SCIP_Real computeEigenvecDotRay(
1152 SCIP_Real* eigenvec, /**< eigenvector */
1153 int nquadvars, /**< number of quadratic vars (length of eigenvec) */
1154 SCIP_Real* raycoefs, /**< coefficients of ray */
1155 int* rayidx, /**< index of consvar the ray coef is associated to */
1156 int raynnonz /**< length of raycoefs and rayidx */
1157 )
1158 {
1159 SCIP_Real retval;
1160 int i;
1161
1162 retval = 0.0;
1163 for( i = 0; i < raynnonz; ++i )
1164 {
1165 /* rays are sorted; the first entries correspond to the quad vars, so we stop after first nonquad var entry */
1166 if( rayidx[i] >= nquadvars )
1167 break;
1168
1169 retval += eigenvec[rayidx[i]] * raycoefs[i];
1170 }
1171
1172 return retval;
1173 }
1174
1175 /** computes linear part of evaluation of w(ray): \f$b_l^T ray_l - ray_a\f$
1176 *
1177 * @note we can know whether the auxiliary variable appears by the entries of the ray
1178 */
1179 static
1180 SCIP_Real computeWRayLinear(
1181 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nlhdlr expression data */
1182 SCIP_Real sidefactor, /**< 1.0 if the violated constraint is q ≤ rhs, -1.0 otherwise */
1183 SCIP_Real* raycoefs, /**< coefficients of ray */
1184 int* rayidx, /**< ray coef[i] affects var at pos rayidx[i] in consvar */
1185 int raynnonz /**< length of raycoefs and rayidx */
1186 )
1187 {
1188 SCIP_EXPR* qexpr;
1189 SCIP_Real* lincoefs;
1190 SCIP_Real retval;
1191 int nquadexprs;
1192 int nlinexprs;
1193
1194 int i;
1195 int start;
1196
1197 #ifdef INTERCUT_MOREDEBUG
1198 printf("Computing w(ray) \n");
1199 #endif
1200 retval = 0.0;
1201 start = raynnonz - 1;
1202
1203 qexpr = nlhdlrexprdata->qexpr;
1204 SCIPexprGetQuadraticData(qexpr, NULL, &nlinexprs, NULL, &lincoefs, &nquadexprs, NULL, NULL, NULL);
1205
1206 /* process ray entry associated to the auxvar if it applies */
1207 if( rayidx[raynnonz - 1] == nquadexprs + nlinexprs )
1208 {
1209 #ifdef INTERCUT_MOREDEBUG
1210 printf("wray auxvar term %g \n", (sidefactor * -1.0) * raycoefs[raynnonz - 1]);
1211 #endif
1212 retval += (sidefactor * -1.0) * raycoefs[raynnonz - 1];
1213 start--;
1214 }
1215
1216 /* process the rest of the entries */
1217 for( i = start; i >= 0; --i )
1218 {
1219 /* rays are sorted; last entries correspond to the lin vars, so we stop after first quad var entry */
1220 if( rayidx[i] < nquadexprs )
1221 break;
1222
1223 #ifdef INTERCUT_MOREDEBUG
1224 printf("wray var in pos %d term %g:: lincoef %g raycoef %g\n", rayidx[i], (sidefactor *
1225 lincoefs[rayidx[i] - nquadexprs]) * raycoefs[i], lincoefs[rayidx[i] - nquadexprs] ,raycoefs[i]);
1226 #endif
1227 retval += (sidefactor * lincoefs[rayidx[i] - nquadexprs]) * raycoefs[i] ;
1228 }
1229
1230 return retval;
1231 }
1232
1233 /** calculate coefficients of restriction of the function to given ray.
1234 *
1235 * The restriction of the function representing the maximal S-free set to zlp + t * ray has the form
1236 * SQRT(A t^2 + B t + C) - (D t + E) for cases 1, 2, and 3.
1237 * For case 4 it is a piecewise defined function and each piece is of the aforementioned form.
1238 *
1239 * This function computes the coefficients A, B, C, D, E for the given ray.
1240 * In case 4, it computes the coefficients for both pieces, in addition to coefficients needed to evaluate the condition
1241 * in the piecewise definition of the function.
1242 *
1243 * The parameter iscase4 tells the function if it is case 4 or not.
1244 */
1245 static
1246 SCIP_RETCODE computeRestrictionToRay(
1247 SCIP* scip, /**< SCIP data structure */
1248 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nlhdlr expression data */
1249 SCIP_Real sidefactor, /**< 1.0 if the violated constraint is q ≤ rhs, -1.0 otherwise */
1250 SCIP_Bool iscase4, /**< whether we are in case 4 */
1251 SCIP_Real* raycoefs, /**< coefficients of ray */
1252 int* rayidx, /**< index of consvar the ray coef is associated to */
1253 int raynnonz, /**< length of raycoefs and rayidx */
1254 SCIP_Real* vb, /**< array containing \f$v_i^T b\f$ for \f$i \in I_+ \cup I_-\f$ */
1255 SCIP_Real* vzlp, /**< array containing \f$v_i^T zlp_q\f$ for \f$i \in I_+ \cup I_-\f$ */
1256 SCIP_Real* wcoefs, /**< coefficients of w for the qud vars or NULL if w is 0 */
1257 SCIP_Real wzlp, /**< value of w at zlp */
1258 SCIP_Real kappa, /**< value of kappa */
1259 SCIP_Real* coefs1234a, /**< buffer to store A, B, C, D, and E of cases 1, 2, 3, or 4a */
1260 SCIP_Real* coefs4b, /**< buffer to store A, B, C, D, and E of case 4b (or NULL if not needed) */
1261 SCIP_Real* coefscondition, /**< buffer to store data to evaluate condition to decide case 4a or 4b */
1262 SCIP_Bool* success /**< did we successfully compute the coefficients? */
1263 )
1264 {
1265 SCIP_EXPR* qexpr;
1266 int nquadexprs;
1267 SCIP_Real* eigenvectors;
1268 SCIP_Real* eigenvalues;
1269 SCIP_Real* a;
1270 SCIP_Real* b;
1271 SCIP_Real* c;
1272 SCIP_Real* d;
1273 SCIP_Real* e;
1274 SCIP_Real wray;
1275 int i;
1276
1277 *success = TRUE;
1278
1279 qexpr = nlhdlrexprdata->qexpr;
1280 SCIPexprGetQuadraticData(qexpr, NULL, NULL, NULL, NULL, &nquadexprs, NULL, &eigenvalues, &eigenvectors);
1281
1282 #ifdef DEBUG_INTERSECTIONCUT
1283 SCIPinfoMessage(scip, NULL, "\n############################################\n");
1284 SCIPinfoMessage(scip, NULL, "Restricting to ray:\n");
1285 for( i = 0; i < raynnonz; ++i )
1286 {
1287 SCIPinfoMessage(scip, NULL, "(%d, %g), ", rayidx[i], raycoefs[i]);
1288 }
1289 SCIPinfoMessage(scip, NULL, "\n");
1290 #endif
1291
1292 assert(coefs1234a != NULL);
1293
1294 /* set all coefficients to zero */
1295 memset(coefs1234a, 0, 5 * sizeof(SCIP_Real));
1296 if( iscase4 )
1297 {
1298 assert(coefs4b != NULL);
1299 assert(coefscondition != NULL);
1300 assert(wcoefs != NULL);
1301
1302 memset(coefs4b, 0, 5 * sizeof(SCIP_Real));
1303 memset(coefscondition, 0, 3 * sizeof(SCIP_Real));
1304 }
1305
1306 a = coefs1234a;
1307 b = coefs1234a + 1;
1308 c = coefs1234a + 2;
1309 d = coefs1234a + 3;
1310 e = coefs1234a + 4;
1311 wray = 0;
1312
1313 for( i = 0; i < nquadexprs; ++i )
1314 {
1315 SCIP_Real dot;
1316 SCIP_Real vdotray;
1317
1318 if( SCIPisZero(scip, eigenvalues[i]) && wcoefs == NULL )
1319 continue;
1320
1321 dot = vzlp[i] + vb[i] / (2.0 * (sidefactor * eigenvalues[i]));
1322 vdotray = computeEigenvecDotRay(&eigenvectors[i * nquadexprs], nquadexprs, raycoefs, rayidx, raynnonz);
1323
1324 if( SCIPisZero(scip, eigenvalues[i]) )
1325 {
1326 /* zero eigenvalue (and wcoefs not null) -> case 4: compute w(r) */
1327 assert(wcoefs != NULL);
1328 wray += vb[i] * vdotray;
1329 #ifdef INTERCUT_MOREDEBUG
1330 printf(" wray += %g, vb[i] %g and vdotray %g\n", vb[i] * vdotray,vb[i],vdotray);
1331 #endif
1332 }
1333 else if( sidefactor * eigenvalues[i] > 0 )
1334 {
1335 /* positive eigenvalue: compute common part of D and E */
1336 *d += (sidefactor * eigenvalues[i]) * dot * vdotray;
1337 *e += (sidefactor * eigenvalues[i]) * SQR( dot );
1338
1339 #ifdef INTERCUT_MOREDEBUG
1340 printf("Positive eigenvalue: computing D: v^T ray %g, v^T( zlp + b/theta ) %g and theta %g \n", vdotray, dot, (sidefactor * eigenvalues[i]));
1341 #endif
1342 }
1343 else
1344 {
1345 /* negative eigenvalue: compute common part of A, B, and C */
1346 *a -= (sidefactor * eigenvalues[i]) * SQR( vdotray );
1347 *b -= 2.0 * (sidefactor * eigenvalues[i]) * dot * vdotray;
1348 *c -= (sidefactor * eigenvalues[i]) * SQR( dot );
1349
1350 #ifdef INTERCUT_MOREDEBUG
1351 printf("Negative eigenvalue: computing A: v^T ray %g, and theta %g \n", vdotray, (sidefactor * eigenvalues[i]));
1352 #endif
1353 }
1354 }
1355
1356 if( ! iscase4 )
1357 {
1358 /* We are in one of the first 3 cases */
1359 *e += MAX(kappa, 0.0);
1360 *c -= MIN(kappa, 0.0);
1361
1362 /* finish computation of D and E */
1363 assert(*e > 0);
1364 *e = SQRT( *e );
1365 *d /= *e;
1366
1367 /* some sanity checks only applicable to these cases (more at the end) */
1368 assert(*c >= 0);
1369
1370 /* In theory, the function at 0 must be negative. Because of bad numerics this might not always hold, so we abort
1371 * the generation of the cut in this case.
1372 */
1373 if( SQRT( *c ) - *e >= 0 )
1374 {
1375 /* check if it's really a numerical problem */
1376 assert(SQRT( *c ) > 10e+15 || *e > 10e+15 || SQRT( *c ) - *e < 10e+9);
1377
1378 INTERLOG(printf("Bad numerics: phi(0) >= 0\n"); )
1379 *success = FALSE;
1380 return SCIP_OKAY;
1381 }
1382 }
1383 else
1384 {
1385 SCIP_Real norm;
1386 SCIP_Real xextra;
1387 SCIP_Real yextra;
1388
1389 norm = SQRT( 1 + SQR( kappa ) );
1390 xextra = wzlp + kappa + norm;
1391 yextra = wzlp + kappa - norm;
1392
1393 /* finish computing w(ray), the linear part is missing */
1394 wray += computeWRayLinear(nlhdlrexprdata, sidefactor, raycoefs, rayidx, raynnonz);
1395
1396 /*
1397 * coefficients of case 4b
1398 */
1399 /* at this point E is \|x(zlp)\|^2, so we can finish A, B, and C */
1400 coefs4b[0] = (*a) * (*e);
1401 coefs4b[1] = (*b) * (*e);
1402 coefs4b[2] = (*c) * (*e);
1403
1404 /* finish D and E */
1405 coefs4b[3] = *d;
1406 coefs4b[4] = (*e) + xextra / 2.0;
1407
1408 /* when \|x(zlp)\|^2 is too large, we can divide everything by \|x(zlp)\| */
1409 if( *e > 100 )
1410 {
1411 coefs4b[0] = (*a);
1412 coefs4b[1] = (*b);
1413 coefs4b[2] = (*c);
1414
1415 /* finish D and E */
1416 coefs4b[3] = *d / SQRT( *e );
1417 coefs4b[4] = SQRT( *e ) + (xextra / (2.0 * SQRT( *e )));
1418 }
1419
1420 /*
1421 * coefficients of case 4a
1422 */
1423 /* finish A, B, and C */
1424 *a += SQR( wray ) / (4.0 * norm);
1425 *b += 2.0 * yextra * (wray) / (4.0 * norm);
1426 *c += SQR( yextra ) / (4.0 * norm);
1427
1428 /* finish D and E */
1429 *e += SQR( xextra ) / (4.0 * norm);
1430 *e = SQRT( *e );
1431
1432 *d += xextra * (wray) / (4.0 * norm);
1433 *d /= *e;
1434
1435 /*
1436 * coefficients of condition: stores -numerator of x_{r+1}/ norm xhat, w(ray), and numerator of y_{s+1} at zlp
1437 *
1438 */
1439 /* at this point E is \| \hat{x} (zlp)\| */
1440 coefscondition[0] = - xextra / (*e);
1441 coefscondition[1] = wray;
1442 coefscondition[2] = yextra;
1443 }
1444
1445 #ifdef DEBUG_INTERSECTIONCUT
1446 if( ! iscase4 )
1447 {
1448 SCIPinfoMessage(scip, NULL, "Restriction yields case 1,2 or 3: a,b,c,d,e %g %g %g %g %g\n", coefs1234a[0], coefs1234a[1], coefs1234a[2],
1449 coefs1234a[3], coefs1234a[4]);
1450 }
1451 else
1452 {
1453 SCIPinfoMessage(scip, NULL, "Restriction yields\n Case 4a: a,b,c,d,e %g %g %g %g %g\n", coefs1234a[0],
1454 coefs1234a[1], coefs1234a[2], coefs1234a[3], coefs1234a[4]);
1455 SCIPinfoMessage(scip, NULL, " Case 4b: a,b,c,d,e %g %g %g %g %g\n", coefs4b[0], coefs4b[1], coefs4b[2],
1456 coefs4b[3], coefs4b[4]);
1457 SCIPinfoMessage(scip, NULL, " Condition: xextra/e, wray, yextra %g %g %g g\n", coefscondition[0],
1458 coefscondition[1], coefscondition[2]);
1459 }
1460 #endif
1461
1462 /* some sanity check applicable to all cases */
1463 assert(*a >= 0); /* the function inside the root is convex */
1464 assert(*c >= 0); /* radicand at zero */
1465
1466 if( iscase4 )
1467 {
1468 assert(coefs4b[0] >= 0); /* the function inside the root is convex */
1469 assert(coefs4b[2] >= 0); /* radicand at zero */
1470 }
1471 /*assert(4.0 * (*a) * (*c) >= SQR( *b ) ); *//* the function is defined everywhere, so minimum of radicand must be nonnegative */
1472
1473 return SCIP_OKAY;
1474 }
1475
1476 /** returns phi(zlp + t * ray) = SQRT(A t^2 + B t + C) - (D t + E) */
1477 static
1478 SCIP_Real evalPhiAtRay(
1479 SCIP_Real t, /**< argument of phi restricted to ray */
1480 SCIP_Real a, /**< value of A */
1481 SCIP_Real b, /**< value of B */
1482 SCIP_Real c, /**< value of C */
1483 SCIP_Real d, /**< value of D */
1484 SCIP_Real e /**< value of E */
1485 )
1486 {
1487 #ifdef INTERCUTS_DBLDBL
1488 SCIP_Real QUAD(lin);
1489 SCIP_Real QUAD(disc);
1490 SCIP_Real QUAD(tmp);
1491 SCIP_Real QUAD(root);
1492
1493 /* d * t + e */
1494 SCIPquadprecProdDD(lin, d, t);
1495 SCIPquadprecSumQD(lin, lin, e);
1496
1497 /* a * t * t */
1498 SCIPquadprecSquareD(disc, t);
1499 SCIPquadprecProdQD(disc, disc, a);
1500
1501 /* b * t */
1502 SCIPquadprecProdDD(tmp, b, t);
1503
1504 /* a * t * t + b * t */
1505 SCIPquadprecSumQQ(disc, disc, tmp);
1506
1507 /* a * t * t + b * t + c */
1508 SCIPquadprecSumQD(disc, disc, c);
1509
1510 /* sqrt(above): can't take sqrt of 0! */
1511 if( QUAD_TO_DBL(disc) == 0 )
1512 {
1513 QUAD_ASSIGN(root, 0.0);
1514 }
1515 else
1516 {
1517 SCIPquadprecSqrtQ(root, disc);
1518 }
1519
1520 /* final result */
1521 QUAD_SCALE(lin, -1.0);
1522 SCIPquadprecSumQQ(tmp, root, lin);
1523
1524 assert(t != 1e20 || QUAD_TO_DBL(tmp) <= 0);
1525
1526 return QUAD_TO_DBL(tmp);
1527 #else
1528 return SQRT( a * t * t + b * t + c ) - ( d * t + e );
1529 #endif
1530 }
1531
1532 /** checks whether case 4a applies
1533 *
1534 * The condition for being in case 4a is
1535 * \f[ -\lambda_{r+1} \Vert \hat y(zlp + tsol\, ray)\Vert + \hat y_{s+1}(zlp + tsol\, ray) \leq 0\f]
1536 *
1537 * This reduces to
1538 * \f[ -num(\hat x_{r+1}(zlp)) \sqrt{A t^2 + B t + C} / E + w(ray) \cdot t + num(\hat y_{s+1}(zlp)) \leq 0\f]
1539 * where num is the numerator.
1540 */
1541 static
1542 SCIP_Real isCase4a(
1543 SCIP_Real tsol, /**< t in the above formula */
1544 SCIP_Real* coefs4a, /**< coefficients A, B, C, D, and E of case 4a */
1545 SCIP_Real* coefscondition /**< extra coefficients needed for the evaluation of the condition:
1546 * \f$num(\hat x_{r+1}(zlp)) / E\f$; \f$w(ray)\f$; \f$num(\hat y_{s+1}(zlp))\f$ */
1547 )
1548 {
1549 return (coefscondition[0] * SQRT( coefs4a[0] * SQR( tsol ) + coefs4a[1] * tsol + coefs4a[2] ) + coefscondition[1] *
1550 tsol + coefscondition[2]) <= 0.0;
1551 }
1552
1553 /** helper function of computeRoot: we want phi to be ≤ 0 */
1554 static
1555 void doBinarySearch(
1556 SCIP* scip, /**< SCIP data structure */
1557 SCIP_Real a, /**< value of A */
1558 SCIP_Real b, /**< value of B */
1559 SCIP_Real c, /**< value of C */
1560 SCIP_Real d, /**< value of D */
1561 SCIP_Real e, /**< value of E */
1562 SCIP_Real* sol /**< buffer to store solution; also gives initial point */
1563 )
1564 {
1565 SCIP_Real lb = 0.0;
1566 SCIP_Real ub = *sol;
1567 SCIP_Real curr;
1568 int i;
1569
1570 for( i = 0; i < BINSEARCH_MAXITERS; ++i )
1571 {
1572 SCIP_Real phival;
1573
1574 curr = (lb + ub) / 2.0;
1575 phival = evalPhiAtRay(curr, a, b, c, d, e);
1576 #ifdef INTERCUT_MOREDEBUG
1577 printf("%d: lb,ub %.10f, %.10f. curr = %g -> phi at curr %g -> phi at lb %g \n", i, lb, ub, curr, phival, evalPhiAtRay(lb, a, b, c, d, e));
1578 #endif
1579
1580 if( phival <= 0.0 )
1581 {
1582 lb = curr;
1583 if( SCIPisFeasZero(scip, phival) || SCIPisFeasEQ(scip, ub, lb) )
1584 break;
1585 }
1586 else
1587 ub = curr;
1588 }
1589
1590 *sol = lb;
1591 }
1592
1593 /** finds smallest positive root phi by finding the smallest positive root of
1594 * (A - D^2) t^2 + (B - 2 D*E) t + (C - E^2) = 0
1595 *
1596 * However, we are conservative and want a solution such that phi is negative, but close to 0.
1597 * Thus, we correct the result with a binary search.
1598 */
1599 static
1600 SCIP_Real computeRoot(
1601 SCIP* scip, /**< SCIP data structure */
1602 SCIP_Real* coefs /**< value of A */
1603 )
1604 {
1605 SCIP_Real sol;
1606 SCIP_INTERVAL bounds;
1607 SCIP_INTERVAL result;
1608 SCIP_Real a = coefs[0];
1609 SCIP_Real b = coefs[1];
1610 SCIP_Real c = coefs[2];
1611 SCIP_Real d = coefs[3];
1612 SCIP_Real e = coefs[4];
1613
1614 /* there is an intersection point if and only if SQRT(A) > D: here we are beliving in math, this might cause
1615 * numerical issues
1616 */
1617 if( SQRT( a ) <= d )
1618 {
1619 sol = SCIPinfinity(scip);
1620 return sol;
1621 }
1622
1623 SCIPintervalSetBounds(&bounds, 0.0, SCIPinfinity(scip));
1624
1625 /* SCIPintervalSolveUnivariateQuadExpressionPositiveAllScalar finds all x such that a x^2 + b x >= c and x in bounds.
1626 * it is known that if tsol is the root we are looking for, then gamma(zlp + t * ray) <= 0 between 0 and tsol, thus
1627 * tsol is the smallest t such that (A - D^2) t^2 + (B - 2 D*E) t + (C - E^2) >= 0
1628 */
1629 SCIPintervalSolveUnivariateQuadExpressionPositiveAllScalar(SCIP_INTERVAL_INFINITY, &result, a - d * d, b - 2.0 * d *
1630 e, -(c - e * e), bounds);
1631
1632 /* it can still be empty because of our infinity, I guess... */
1633 sol = SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, result) ? SCIPinfinity(scip) : SCIPintervalGetInf(result);
1634
1635 #ifdef INTERCUT_MOREDEBUG
1636 {
1637 SCIP_Real binsol;
1638 binsol = SCIPinfinity(scip);
1639 doBinarySearch(scip, a, b, c, d, e, &binsol);
1640 printf("got root %g: with binsearch get %g\n", sol, binsol);
1641 }
1642 #endif
1643
1644 /* check that solution is acceptable, ideally it should be <= 0, however when it is positive, we trigger a binary
1645 * search to make it negative. This binary search might return a solution point that is not at accurately 0 as the
1646 * one obtained from the function above. Thus, it might fail to satisfy the condition of case 4b in some cases, e.g.,
1647 * ex8_3_1, bchoco05, etc
1648 */
1649 if( evalPhiAtRay(sol, a, b, c, d, e) <= 1e-10 )
1650 {
1651 #ifdef INTERCUT_MOREDEBUG
1652 printf("interval solution returned %g -> phival = %g, believe it\n", sol, evalPhiAtRay(sol, a, b, c, d, e));
1653 printf("don't do bin search\n");
1654 #endif
1655 return sol;
1656 }
1657 else
1658 {
1659 /* perform a binary search to make it negative: this might correct a wrong infinity (e.g. crudeoil_lee1_05) */
1660 #ifdef INTERCUT_MOREDEBUG
1661 printf("do bin search because phival is %g\n", evalPhiAtRay(sol, a, b, c, d, e));
1662 #endif
1663 doBinarySearch(scip, a, b, c, d, e, &sol);
1664 }
1665
1666 return sol;
1667 }
1668
1669 /** The maximal S-free set is \f$\gamma(z) \leq 0\f$; we find the intersection point of the ray `ray` starting from zlp with the
1670 * boundary of the S-free set.
1671 * That is, we find \f$t \geq 0\f$ such that \f$\gamma(zlp + t \cdot \text{ray}) = 0\f$.
1672 *
1673 * In cases 1,2, and 3, gamma is of the form
1674 * \f[ \gamma(zlp + t \cdot \text{ray}) = \sqrt{A t^2 + B t + C} - (D t + E) \f]
1675 *
1676 * In the case 4 gamma is of the form
1677 * \f[ \gamma(zlp + t \cdot \text{ray}) =
1678 * \begin{cases}
1679 * \sqrt{A t^2 + B t + C} - (D t + E), & \text{if some condition holds}, \\
1680 * \sqrt{A' t^2 + B' t + C'} - (D' t + E'), & \text{otherwise.}
1681 * \end{cases}
1682 * \f]
1683 *
1684 * It can be shown (given the special properties of \f$\gamma\f$) that the smallest positive root of each function of the form
1685 * \f$\sqrt{a t^2 + b t + c} - (d t + e)\f$
1686 * is the same as the smallest positive root of the quadratic equation:
1687 * \f{align}{
1688 * & \sqrt{a t^2 + b t + c} - (d t + e)) (\sqrt{a t^2 + b t + c} + (d t + e)) = 0 \\ \Leftrightarrow
1689 * & (a - d^2) t^2 + (b - 2 d\,e) t + (c - e^2) = 0
1690 * \f}
1691 *
1692 * So, in cases 1, 2, and 3, this function just returns the solution of the above equation.
1693 * In case 4, it first solves the equation assuming we are in the first piece.
1694 * If there is no solution, then the second piece can't have a solution (first piece ≥ second piece for all t)
1695 * Then we check if the solution satisfies the condition.
1696 * If it doesn't then we solve the equation for the second piece.
1697 * If it has a solution, then it _has_ to be the solution.
1698 */
1699 static
1700 SCIP_Real computeIntersectionPoint(
1701 SCIP* scip, /**< SCIP data structure */
1702 SCIP_NLHDLRDATA* nlhdlrdata, /**< nlhdlr data */
1703 SCIP_Bool iscase4, /**< whether we are in case 4 or not */
1704 SCIP_Real* coefs1234a, /**< values of A, B, C, D, and E of cases 1, 2, 3, or 4a */
1705 SCIP_Real* coefs4b, /**< values of A, B, C, D, and E of case 4b */
1706 SCIP_Real* coefscondition /**< values needed to evaluate condition of case 4 */
1707 )
1708 {
1709 SCIP_Real sol1234a;
1710 SCIP_Real sol4b;
1711
1712 assert(coefs1234a != NULL);
1713
1714 #ifdef DEBUG_INTERSECTIONCUT
1715 SCIPinfoMessage(scip, NULL, "Computing intersection point for case 4? %d\n", iscase4);
1716 #endif
1717 if( ! iscase4 )
1718 return computeRoot(scip, coefs1234a);
1719
1720 assert(coefs4b != NULL);
1721 assert(coefscondition != NULL);
1722
1723 /* compute solution of first piece */
1724 #ifdef DEBUG_INTERSECTIONCUT
1725 SCIPinfoMessage(scip, NULL, "Compute root in 4a\n");
1726 #endif
1727 sol1234a = computeRoot(scip, coefs1234a);
1728
1729 /* if there is no solution --> second piece doesn't have solution */
1730 if( SCIPisInfinity(scip, sol1234a) )
1731 {
1732 /* this assert fails on multiplants_mtg5 the problem is that sqrt(A) <= D in 4a but not in 4b,
1733 * now, this is impossible since the phi4a >= phi4b, so actually sqrt(A) is 10e-15 away from
1734 * D in 4b
1735 */
1736 /* assert(SCIPisInfinity(scip, computeRoot(scip, coefs4b))); */
1737 return sol1234a;
1738 }
1739
1740 /* if solution of 4a is in 4a, then return */
1741 if( isCase4a(sol1234a, coefs1234a, coefscondition) )
1742 return sol1234a;
1743
1744 #ifdef DEBUG_INTERSECTIONCUT
1745 SCIPinfoMessage(scip, NULL, "Root not in 4a -> Compute root in 4b\n");
1746 #endif
1747
1748 /* not on 4a --> then the intersection point is whatever 4b says: as phi4a >= phi4b, the solution of phi4b should
1749 * always be larger (but shouldn't be equal at this point given that isCase4a failed, and the condition function
1750 * evaluates to 0 when phi4a == phi4b) than the solution of phi4a; However, because of numerics (or limits in the
1751 * binary search) we can find a slightly smaller solution; thus, we just keep the larger one
1752 */
1753 sol4b = computeRoot(scip, coefs4b);
1754
1755 /* this assert fails in many instances, e.g. water, because sol4b < sol1234a */
1756 /* assert(SCIPisInfinity(scip, sol4b) || !isCase4a(sol4b, coefs1234a, coefscondition)); */
1757 /* count number of times we could have improved the coefficient by 10% */
1758 if( sol4b < sol1234a && evalPhiAtRay(1.1 * sol1234a, coefs4b[0], coefs4b[1], coefs4b[2], coefs4b[3], coefs4b[4]) <=
1759 0.0 )
1760 nlhdlrdata->ncouldimprovedcoef++;
1761
1762 return MAX(sol1234a, sol4b);
1763 }
1764
1765 /** checks if numerics of the coefficients are not too bad */
1766 static
1767 SCIP_Bool areCoefsNumericsGood(
1768 SCIP* scip, /**< SCIP data structure */
1769 SCIP_NLHDLRDATA* nlhdlrdata, /**< nlhdlr data */
1770 SCIP_Real* coefs1234a, /**< coefficients for case 1-3 and 4a */
1771 SCIP_Real* coefs4b, /**< coefficients for case 4b */
1772 SCIP_Bool iscase4 /**< whether we are in case 4 */
1773 )
1774 {
1775 SCIP_Real max;
1776 SCIP_Real min;
1777 int j;
1778
1779 /* check at phi at 0 is negative (note; this could be checked before restricting to the ray) also, if this
1780 * succeeds for one ray, it should suceed for every ray
1781 */
1782 if( SQRT( coefs1234a[2] ) - coefs1234a[4] >= 0.0 )
1783 {
1784 INTERLOG(printf("Bad numerics: phi(0) >= 0\n"); )
1785 nlhdlrdata->nphinonneg++;
1786 return FALSE;
1787 }
1788
1789 /* maybe we want to avoid a large dynamism between A, B and C */
1790 if( nlhdlrdata->ignorebadrayrestriction )
1791 {
1792 max = 0.0;
1793 min = SCIPinfinity(scip);
1794 for( j = 0; j < 3; ++j )
1795 {
1796 SCIP_Real absval;
1797
1798 absval = REALABS(coefs1234a[j]);
1799 if( max < absval )
1800 max = absval;
1801 if( absval != 0.0 && absval < min )
1802 min = absval;
1803 }
1804
1805 if( SCIPisHugeValue(scip, max / min) )
1806 {
1807 INTERLOG(printf("Bad numerics 1 2 3 or 4a: max(A,B,C)/min(A,B,C) is too large (%g)\n", max / min); )
1808 nlhdlrdata->nbadrayrestriction++;
1809 return FALSE;
1810 }
1811
1812 if( iscase4 )
1813 {
1814 max = 0.0;
1815 min = SCIPinfinity(scip);
1816 for( j = 0; j < 3; ++j )
1817 {
1818 SCIP_Real absval;
1819
1820 absval = ABS(coefs4b[j]);
1821 if( max < absval )
1822 max = absval;
1823 if( absval != 0.0 && absval < min )
1824 min = absval;
1825 }
1826
1827 if( SCIPisHugeValue(scip, max / min) )
1828 {
1829 INTERLOG(printf("Bad numeric 4b: max(A,B,C)/min(A,B,C) is too large (%g)\n", max / min); )
1830 nlhdlrdata->nbadrayrestriction++;
1831 return FALSE;
1832 }
1833 }
1834 }
1835
1836 return TRUE;
1837 }
1838
1839 /** computes intersection cut cuts off sol (because solution sol violates the quadratic constraint cons)
1840 * and stores it in rowprep. Here, we don't use any strengthening.
1841 */
1842 static
1843 SCIP_RETCODE computeIntercut(
1844 SCIP* scip, /**< SCIP data structure */
1845 SCIP_NLHDLRDATA* nlhdlrdata, /**< nlhdlr data */
1846 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nlhdlr expression data */
1847 RAYS* rays, /**< rays */
1848 SCIP_Real sidefactor, /**< 1.0 if the violated constraint is q ≤ rhs, -1.0 otherwise */
1849 SCIP_Bool iscase4, /**< whether we are in case 4 */
1850 SCIP_Real* vb, /**< array containing \f$v_i^T b\f$ for \f$i \in I_+ \cup I_-\f$ */
1851 SCIP_Real* vzlp, /**< array containing \f$v_i^T zlp_q\f$ for \f$i \in I_+ \cup I_-\f$ */
1852 SCIP_Real* wcoefs, /**< coefficients of w for the qud vars or NULL if w is 0 */
1853 SCIP_Real wzlp, /**< value of w at zlp */
1854 SCIP_Real kappa, /**< value of kappa */
1855 SCIP_ROWPREP* rowprep, /**< rowprep for the generated cut */
1856 SCIP_Real* interpoints, /**< array to store intersection points for all rays or NULL if nothing
1857 needs to be stored */
1858 SCIP_SOL* sol, /**< solution we want to separate */
1859 SCIP_Bool* success /**< if a cut candidate could be computed */
1860 )
1861 {
1862 SCIP_COL** cols;
1863 SCIP_ROW** rows;
1864 int i;
1865
1866 cols = SCIPgetLPCols(scip);
1867 rows = SCIPgetLPRows(scip);
1868
1869 /* for every ray: compute cut coefficient and add var associated to ray into cut */
1870 for( i = 0; i < rays->nrays; ++i )
1871 {
1872 SCIP_Real interpoint;
1873 SCIP_Real cutcoef;
1874 int lppos;
1875 SCIP_Real coefs1234a[5];
1876 SCIP_Real coefs4b[5];
1877 SCIP_Real coefscondition[3];
1878
1879 /* restrict phi to ray */
1880 SCIP_CALL( computeRestrictionToRay(scip, nlhdlrexprdata, sidefactor, iscase4,
1881 &rays->rays[rays->raysbegin[i]], &rays->raysidx[rays->raysbegin[i]], rays->raysbegin[i + 1] -
1882 rays->raysbegin[i], vb, vzlp, wcoefs, wzlp, kappa, coefs1234a, coefs4b, coefscondition, success) );
1883
1884 if( ! *success )
1885 return SCIP_OKAY;
1886
1887 /* if restriction to ray is numerically nasty -> abort cut separation */
1888 *success = areCoefsNumericsGood(scip, nlhdlrdata, coefs1234a, coefs4b, iscase4);
1889
1890 if( ! *success )
1891 return SCIP_OKAY;
1892
1893 /* compute intersection point */
1894 interpoint = computeIntersectionPoint(scip, nlhdlrdata, iscase4, coefs1234a, coefs4b, coefscondition);
1895
1896 #ifdef DEBUG_INTERSECTIONCUT
1897 SCIPinfoMessage(scip, NULL, "interpoint for ray %d is %g\n", i, interpoint);
1898 #endif
1899
1900 /* store intersection point */
1901 if( interpoints != NULL )
1902 interpoints[i] = interpoint;
1903
1904 /* compute cut coef */
1905 cutcoef = SCIPisInfinity(scip, interpoint) ? 0.0 : 1.0 / interpoint;
1906
1907 /* add var to cut: if variable is nonbasic at upper we have to flip sign of cutcoef */
1908 lppos = rays->lpposray[i];
1909 if( lppos < 0 )
1910 {
1911 lppos = -lppos - 1;
1912
1913 assert(SCIProwGetBasisStatus(rows[lppos]) == SCIP_BASESTAT_LOWER || SCIProwGetBasisStatus(rows[lppos]) ==
1914 SCIP_BASESTAT_UPPER);
1915
1916 SCIP_CALL( addRowToCut(scip, rowprep, SCIProwGetBasisStatus(rows[lppos]) == SCIP_BASESTAT_UPPER ? cutcoef :
1917 -cutcoef, rows[lppos], success) ); /* rows have flipper base status! */
1918
1919 if( ! *success )
1920 {
1921 INTERLOG(printf("Bad numeric: now not nonbasic enough\n");)
1922 nlhdlrdata->nbadnonbasic++;
1923 return SCIP_OKAY;
1924 }
1925 }
1926 else
1927 {
1928 if( ! nlhdlrdata->useboundsasrays )
1929 {
1930 assert(SCIPcolGetBasisStatus(cols[lppos]) == SCIP_BASESTAT_UPPER || SCIPcolGetBasisStatus(cols[lppos]) ==
1931 SCIP_BASESTAT_LOWER);
1932 SCIP_CALL( addColToCut(scip, rowprep, sol, SCIPcolGetBasisStatus(cols[lppos]) == SCIP_BASESTAT_UPPER ? -cutcoef :
1933 cutcoef, cols[lppos]) );
1934 }
1935 else
1936 {
1937 SCIP_CALL( addColToCut(scip, rowprep, sol, rays->rays[i] == -1 ? -cutcoef : cutcoef, cols[lppos]) );
1938 }
1939 }
1940 }
1941
1942 return SCIP_OKAY;
1943 }
1944
1945 /** combine ray 1 and 2 to obtain new ray coef1 * ray1 - coef2 * ray2 in sparse format */
1946 static
1947 void combineRays(
1948 SCIP_Real* raycoefs1, /**< coefficients of ray 1 */
1949 int* rayidx1, /**< index of consvar of the ray 1 coef is associated to */
1950 int raynnonz1, /**< length of raycoefs1 and rayidx1 */
1951 SCIP_Real* raycoefs2, /**< coefficients of ray 2 */
1952 int* rayidx2, /**< index of consvar of the ray 2 coef is associated to */
1953 int raynnonz2, /**< length of raycoefs2 and rayidx2 */
1954 SCIP_Real* newraycoefs, /**< coefficients of combined ray */
1955 int* newrayidx, /**< index of consvar of the combined ray coef is associated to */
1956 int* newraynnonz, /**< pointer to length of newraycoefs and newrayidx */
1957 SCIP_Real coef1, /**< coef of ray 1 */
1958 SCIP_Real coef2 /**< coef of ray 2 */
1959 )
1960 {
1961 int idx1;
1962 int idx2;
1963
1964 idx1 = 0;
1965 idx2 = 0;
1966 *newraynnonz = 0;
1967
1968 while( idx1 < raynnonz1 || idx2 < raynnonz2 )
1969 {
1970 /* if the pointers look at different variables (or one already arrieved at the end), only one pointer can move
1971 * on
1972 */
1973 if( idx1 >= raynnonz1 || (idx2 < raynnonz2 && rayidx1[idx1] > rayidx2[idx2]) )
1974 {
1975 /*printf("case 1 \n"); */
1976 newraycoefs[*newraynnonz] = - coef2 * raycoefs2[idx2];
1977 newrayidx[*newraynnonz] = rayidx2[idx2];
1978 ++(*newraynnonz);
1979 ++idx2;
1980 }
1981 else if( idx2 >= raynnonz2 || rayidx1[idx1] < rayidx2[idx2] )
1982 {
1983 /*printf("case 2 \n"); */
1984 newraycoefs[*newraynnonz] = coef1 * raycoefs1[idx1];
1985 newrayidx[*newraynnonz] = rayidx1[idx1];
1986 ++(*newraynnonz);
1987 ++idx1;
1988 }
1989 /* if both pointers look at the same variable, just compute the difference and move both pointers */
1990 else if( rayidx1[idx1] == rayidx2[idx2] )
1991 {
1992 /*printf("case 3 \n"); */
1993 newraycoefs[*newraynnonz] = coef1 * raycoefs1[idx1] - coef2 * raycoefs2[idx2];
1994 newrayidx[*newraynnonz] = rayidx1[idx1];
1995 ++(*newraynnonz);
1996 ++idx1;
1997 ++idx2;
1998 }
1999 }
2000 }
2001
2002 /** checks if two rays are linearly dependent */
2003 static
2004 SCIP_Bool raysAreDependent(
2005 SCIP* scip, /**< SCIP data structure */
2006 SCIP_Real* raycoefs1, /**< coefficients of ray 1 */
2007 int* rayidx1, /**< index of consvar of the ray 1 coef is associated to */
2008 int raynnonz1, /**< length of raycoefs1 and rayidx1 */
2009 SCIP_Real* raycoefs2, /**< coefficients of ray 2 */
2010 int* rayidx2, /**< index of consvar of the ray 2 coef is associated to */
2011 int raynnonz2, /**< length of raycoefs2 and rayidx2 */
2012 SCIP_Real* coef /**< pointer to store coef (s.t. r1 = coef * r2) in case rays are
2013 * dependent */
2014 )
2015 {
2016 int i;
2017
2018 /* cannot be dependent if they have different number of non-zero entries */
2019 if( raynnonz1 != raynnonz2 )
2020 return FALSE;
2021
2022 *coef = 0.0;
2023
2024 for( i = 0; i < raynnonz1; ++i )
2025 {
2026 /* cannot be dependent if different variables have non-zero entries */
2027 if( rayidx1[i] != rayidx2[i] ||
2028 (SCIPisZero(scip, raycoefs1[i]) && !SCIPisZero(scip, raycoefs2[i])) ||
2029 (!SCIPisZero(scip, raycoefs1[i]) && SCIPisZero(scip, raycoefs2[i])) )
2030 {
2031 return FALSE;
2032 }
2033
2034 if( *coef != 0.0 )
2035 {
2036 /* cannot be dependent if the coefs aren't equal for all entries */
2037 if( ! SCIPisEQ(scip, *coef, raycoefs1[i] / raycoefs2[i]) )
2038 {
2039 return FALSE;
2040 }
2041 }
2042 else
2043 *coef = raycoefs1[i] / raycoefs2[i];
2044 }
2045
2046 return TRUE;
2047 }
2048
2049 /** checks if the ray alpha * ray_i + (1 - alpha) * ray_j is in the recession cone of the S-free set. To do so,
2050 * we check if phi restricted to the ray has a positive root.
2051 */
2052 static
2053 SCIP_RETCODE rayInRecessionCone(
2054 SCIP* scip, /**< SCIP data structure */
2055 SCIP_NLHDLRDATA* nlhdlrdata, /**< nlhdlr data */
2056 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nlhdlr expression data */
2057 RAYS* rays, /**< rays */
2058 int j, /**< index of current ray in recession cone */
2059 int i, /**< index of current ray not in recession cone */
2060 SCIP_Real sidefactor, /**< 1.0 if the violated constraint is q ≤ rhs, -1.0 otherwise */
2061 SCIP_Bool iscase4, /**< whether we are in case 4 */
2062 SCIP_Real* vb, /**< array containing \f$v_i^T b\f$ for \f$i \in I_+ \cup I_-\f$ */
2063 SCIP_Real* vzlp, /**< array containing \f$v_i^T zlp_q\f$ for \f$i \in I_+ \cup I_-\f$ */
2064 SCIP_Real* wcoefs, /**< coefficients of w for the quad vars or NULL if w is 0 */
2065 SCIP_Real wzlp, /**< value of w at zlp */
2066 SCIP_Real kappa, /**< value of kappa */
2067 SCIP_Real alpha, /**< coef for combining the two rays */
2068 SCIP_Bool* inreccone, /**< pointer to store whether the ray is in the recession cone or not */
2069 SCIP_Bool* success /**< Did numerical troubles occur? */
2070 )
2071 {
2072 SCIP_Real coefs1234a[5];
2073 SCIP_Real coefs4b[5];
2074 SCIP_Real coefscondition[3];
2075 SCIP_Real interpoint;
2076 SCIP_Real* newraycoefs;
2077 int* newrayidx;
2078 int newraynnonz;
2079
2080 *inreccone = FALSE;
2081
2082 /* allocate memory for new ray */
2083 newraynnonz = (rays->raysbegin[i + 1] - rays->raysbegin[i]) + (rays->raysbegin[j + 1] - rays->raysbegin[j]);
2084 SCIP_CALL( SCIPallocBufferArray(scip, &newraycoefs, newraynnonz) );
2085 SCIP_CALL( SCIPallocBufferArray(scip, &newrayidx, newraynnonz) );
2086
2087 /* build the ray alpha * ray_i + (1 - alpha) * ray_j */
2088 combineRays(&rays->rays[rays->raysbegin[i]], &rays->raysidx[rays->raysbegin[i]], rays->raysbegin[i + 1] -
2089 rays->raysbegin[i], &rays->rays[rays->raysbegin[j]], &rays->raysidx[rays->raysbegin[j]],
2090 rays->raysbegin[j + 1] - rays->raysbegin[j], newraycoefs, newrayidx, &newraynnonz, alpha,
2091 -1 + alpha);
2092
2093 /* restrict phi to the "new" ray */
2094 SCIP_CALL( computeRestrictionToRay(scip, nlhdlrexprdata, sidefactor, iscase4, newraycoefs, newrayidx,
2095 newraynnonz, vb, vzlp, wcoefs, wzlp, kappa, coefs1234a, coefs4b, coefscondition, success) );
2096
2097 if( ! *success )
2098 goto CLEANUP;
2099
2100 /* check if restriction to "new" ray is numerically nasty. If so, treat the corresponding rho as if phi is
2101 * positive
2102 */
2103
2104 /* compute intersection point */
2105 interpoint = computeIntersectionPoint(scip, nlhdlrdata, iscase4, coefs1234a, coefs4b, coefscondition);
2106
2107 /* no root exists */
2108 if( SCIPisInfinity(scip, interpoint) )
2109 *inreccone = TRUE;
2110
2111 CLEANUP:
2112 SCIPfreeBufferArray(scip, &newrayidx);
2113 SCIPfreeBufferArray(scip, &newraycoefs);
2114
2115 return SCIP_OKAY;
2116 }
2117
2118 /** finds the smallest negative steplength for the current ray r_idx such that the combination
2119 * of r_idx with all rays not in the recession cone is in the recession cone
2120 */
2121 static
2122 SCIP_RETCODE findRho(
2123 SCIP* scip, /**< SCIP data structure */
2124 SCIP_NLHDLRDATA* nlhdlrdata, /**< nlhdlr data */
2125 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nlhdlr expression data */
2126 RAYS* rays, /**< rays */
2127 int idx, /**< index of current ray we want to find rho for */
2128 SCIP_Real sidefactor, /**< 1.0 if the violated constraint is q ≤ rhs, -1.0 otherwise */
2129 SCIP_Bool iscase4, /**< whether we are in case 4 */
2130 SCIP_Real* vb, /**< array containing \f$v_i^T b\f$ for \f$i \in I_+ \cup I_-\f$ */
2131 SCIP_Real* vzlp, /**< array containing \f$v_i^T zlp_q\f$ for \f$i \in I_+ \cup I_-\f$ */
2132 SCIP_Real* wcoefs, /**< coefficients of w for the quad vars or NULL if w is 0 */
2133 SCIP_Real wzlp, /**< value of w at zlp */
2134 SCIP_Real kappa, /**< value of kappa */
2135 SCIP_Real* interpoints, /**< array to store intersection points for all rays or NULL if nothing
2136 * needs to be stored */
2137 SCIP_Real* rho, /**< pointer to store the optimal rho */
2138 SCIP_Bool* success /**< could we successfully find the right rho? */
2139 )
2140 {
2141 int i;
2142
2143 /* go through all rays not in the recession cone and compute the largest negative steplength possible. The
2144 * smallest of them is then the steplength rho we use for the current ray */
2145 *rho = 0.0;
2146 for( i = 0; i < rays->nrays; ++i )
2147 {
2148 SCIP_Real currentrho;
2149 SCIP_Real coef;
2150
2151 if( SCIPisInfinity(scip, interpoints[i]) )
2152 continue;
2153
2154 /* if we cannot strengthen enough, we don't strengthen at all */
2155 if( SCIPisInfinity(scip, -*rho) )
2156 {
2157 *rho = -SCIPinfinity(scip);
2158 return SCIP_OKAY;
2159 }
2160
2161 /* if the rays are linearly independent, we don't need to search for rho */
2162 if( raysAreDependent(scip, &rays->rays[rays->raysbegin[i]], &rays->raysidx[rays->raysbegin[i]],
2163 rays->raysbegin[i + 1] - rays->raysbegin[i], &rays->rays[rays->raysbegin[idx]],
2164 &rays->raysidx[rays->raysbegin[idx]], rays->raysbegin[idx + 1] - rays->raysbegin[idx], &coef) )
2165 {
2166 currentrho = coef * interpoints[i];
2167 }
2168 else
2169 {
2170 /* since the two rays are linearly independent, we need to find the biggest alpha such that
2171 * alpha * ray_i + (1 - alpha) * ray_idx in the recession cone is. For every ray i, we compute
2172 * such a alpha but take the smallest one of them. We use "maxalpha" to keep track of this.
2173 * Since we know that we can only use alpha < maxalpha, we don't need to do the whole binary search
2174 * for every ray i. We only need to search the intervall [0, maxalpha]. Thereby, we start by checking
2175 * if alpha = maxalpha is already feasable */
2176
2177 SCIP_Bool inreccone;
2178 SCIP_Real alpha;
2179 SCIP_Real lb;
2180 SCIP_Real ub;
2181 int j;
2182
2183 lb = 0.0;
2184 ub = 1.0;
2185 for( j = 0; j < BINSEARCH_MAXITERS; ++j )
2186 {
2187 alpha = (lb + ub) / 2.0;
2188
2189 if( SCIPisZero(scip, alpha) )
2190 {
2191 alpha = 0.0;
2192 break;
2193 }
2194
2195 SCIP_CALL( rayInRecessionCone(scip, nlhdlrdata, nlhdlrexprdata, rays, idx, i, sidefactor, iscase4, vb,
2196 vzlp, wcoefs, wzlp, kappa, alpha, &inreccone, success) );
2197
2198 if( ! *success )
2199 return SCIP_OKAY;
2200
2201 /* no root exists */
2202 if( inreccone )
2203 {
2204 lb = alpha;
2205 if( SCIPisEQ(scip, ub, lb) )
2206 break;
2207 }
2208 else
2209 ub = alpha;
2210 }
2211
2212 /* now we found the best convex combination which we use to derive the corresponding coef. If alpha = 0, we
2213 * cannot move the ray in the recession cone, i.e. strengthening is not possible */
2214 if( SCIPisZero(scip, alpha) )
2215 {
2216 *rho = -SCIPinfinity(scip);
2217 return SCIP_OKAY;
2218 }
2219 else
2220 currentrho = (alpha - 1) * interpoints[i] / alpha; /*lint !e795*/
2221 }
2222
2223 if( currentrho < *rho )
2224 *rho = currentrho;
2225
2226 if( *rho < -10e+06 )
2227 *rho = -SCIPinfinity(scip);
2228
2229 /* if rho is too small, don't add it */
2230 if( SCIPisZero(scip, *rho) )
2231 *success = FALSE;
2232 }
2233
2234 return SCIP_OKAY;
2235 }
2236
2237 /** computes intersection cut using negative edge extension to strengthen rays that do not intersect
2238 * (i.e., rays in the recession cone)
2239 */
2240 static
2241 SCIP_RETCODE computeStrengthenedIntercut(
2242 SCIP* scip, /**< SCIP data structure */
2243 SCIP_NLHDLRDATA* nlhdlrdata, /**< nlhdlr data */
2244 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nlhdlr expression data */
2245 RAYS* rays, /**< rays */
2246 SCIP_Real sidefactor, /**< 1.0 if the violated constraint is q ≤ rhs, -1.0 otherwise */
2247 SCIP_Bool iscase4, /**< whether we are in case 4 */
2248 SCIP_Real* vb, /**< array containing \f$v_i^T b\f$ for \f$i \in I_+ \cup I_-\f$ */
2249 SCIP_Real* vzlp, /**< array containing \f$v_i^T zlp_q\f$ for \f$i \in I_+ \cup I_-\f$ */
2250 SCIP_Real* wcoefs, /**< coefficients of w for the qud vars or NULL if w is 0 */
2251 SCIP_Real wzlp, /**< value of w at zlp */
2252 SCIP_Real kappa, /**< value of kappa */
2253 SCIP_ROWPREP* rowprep, /**< rowprep for the generated cut */
2254 SCIP_SOL* sol, /**< solution to separate */
2255 SCIP_Bool* success, /**< if a cut candidate could be computed */
2256 SCIP_Bool* strengthsuccess /**< if strengthening was successfully applied */
2257 )
2258 {
2259 SCIP_COL** cols;
2260 SCIP_ROW** rows;
2261 SCIP_Real* interpoints;
2262 SCIP_Real avecutcoef;
2263 int counter;
2264 int i;
2265
2266 *success = TRUE;
2267 *strengthsuccess = FALSE;
2268
2269 cols = SCIPgetLPCols(scip);
2270 rows = SCIPgetLPRows(scip);
2271
2272 /* allocate memory for intersection points */
2273 SCIP_CALL( SCIPallocBufferArray(scip, &interpoints, rays->nrays) );
2274
2275 /* compute all intersection points and store them in interpoints; build not-stregthened intersection cut */
2276 SCIP_CALL( computeIntercut(scip, nlhdlrdata, nlhdlrexprdata, rays, sidefactor, iscase4, vb, vzlp, wcoefs, wzlp, kappa,
2277 rowprep, interpoints, sol, success) );
2278
2279 if( ! *success )
2280 goto CLEANUP;
2281
2282 /* keep track of the number of attempted strengthenings and average cutcoef */
2283 counter = 0;
2284 avecutcoef = 0.0;
2285
2286 /* go through all intersection points that are equal to infinity -> these correspond to the rays which are in the
2287 * recession cone of C, i.e. the rays for which we (possibly) can compute a negative steplength */
2288 for( i = 0; i < rays->nrays; ++i )
2289 {
2290 SCIP_Real rho;
2291 SCIP_Real cutcoef;
2292 int lppos;
2293
2294 if( !SCIPisInfinity(scip, interpoints[i]) )
2295 continue;
2296
2297 /* if we reached the limit of strengthenings, we stop */
2298 if( counter >= nlhdlrdata->nstrengthlimit )
2299 break;
2300
2301 /* compute the smallest rho */
2302 SCIP_CALL( findRho(scip, nlhdlrdata, nlhdlrexprdata, rays, i, sidefactor, iscase4, vb, vzlp, wcoefs, wzlp, kappa,
2303 interpoints, &rho, success));
2304
2305 /* compute cut coef */
2306 if( ! *success || SCIPisInfinity(scip, -rho) )
2307 cutcoef = 0.0;
2308 else
2309 cutcoef = 1.0 / rho;
2310
2311 /* track average cut coef */
2312 counter += 1;
2313 avecutcoef += cutcoef;
2314
2315 if( ! SCIPisZero(scip, cutcoef) )
2316 *strengthsuccess = TRUE;
2317
2318 /* add var to cut: if variable is nonbasic at upper we have to flip sign of cutcoef */
2319 lppos = rays->lpposray[i];
2320 if( lppos < 0 )
2321 {
2322 lppos = -lppos - 1;
2323
2324 assert(SCIProwGetBasisStatus(rows[lppos]) == SCIP_BASESTAT_LOWER || SCIProwGetBasisStatus(rows[lppos]) ==
2325 SCIP_BASESTAT_UPPER);
2326
2327 SCIP_CALL( addRowToCut(scip, rowprep, SCIProwGetBasisStatus(rows[lppos]) == SCIP_BASESTAT_UPPER ? cutcoef :
2328 -cutcoef, rows[lppos], success) ); /* rows have flipper base status! */
2329
2330 if( ! *success )
2331 {
2332 INTERLOG(printf("Bad numeric: row not nonbasic enough\n");)
2333 nlhdlrdata->nbadnonbasic++;
2334 return SCIP_OKAY;
2335 }
2336 }
2337 else
2338 {
2339 assert(SCIPcolGetBasisStatus(cols[lppos]) == SCIP_BASESTAT_UPPER || SCIPcolGetBasisStatus(cols[lppos]) ==
2340 SCIP_BASESTAT_LOWER);
2341 SCIP_CALL( addColToCut(scip, rowprep, sol, SCIPcolGetBasisStatus(cols[lppos]) == SCIP_BASESTAT_UPPER ? -cutcoef :
2342 cutcoef, cols[lppos]) );
2343 }
2344 }
2345
2346 if( counter > 0 )
2347 nlhdlrdata->cutcoefsum += avecutcoef / counter;
2348
2349 CLEANUP:
2350 SCIPfreeBufferArray(scip, &interpoints);
2351
2352 return SCIP_OKAY;
2353 }
2354
2355 /** sets variable in solution "vertex" to its nearest bound */
2356 static
2357 SCIP_RETCODE setVarToNearestBound(
2358 SCIP* scip, /**< SCIP data structure */
2359 SCIP_SOL* sol, /**< solution to separate */
2360 SCIP_SOL* vertex, /**< new solution to separate */
2361 SCIP_VAR* var, /**< var we want to find nearest bound to */
2362 SCIP_Real* factor, /**< is vertex for current var at lower or upper? */
2363 SCIP_Bool* success /**< TRUE if no variable is bounded */
2364 )
2365 {
2366 SCIP_Real solval;
2367 SCIP_Real bound;
2368
2369 solval = SCIPgetSolVal(scip, sol, var);
2370 *success = TRUE;
2371
2372 /* find nearest bound */
2373 if( SCIPisInfinity(scip, SCIPvarGetLbLocal(var)) && SCIPisInfinity(scip, SCIPvarGetUbLocal(var)) )
2374 {
2375 *success = FALSE;
2376 return SCIP_OKAY;
2377 }
2378 else if( solval - SCIPvarGetLbLocal(var) < SCIPvarGetUbLocal(var) - solval )
2379 {
2380 bound = SCIPvarGetLbLocal(var);
2381 *factor = 1.0;
2382 }
2383 else
2384 {
2385 bound = SCIPvarGetUbLocal(var);
2386 *factor = -1.0;
2387 }
2388
2389 /* set val to bound in solution */
2390 SCIP_CALL( SCIPsetSolVal(scip, vertex, var, bound) );
2391 return SCIP_OKAY;
2392 }
2393
2394 /** This function finds vertex (w.r.t. bounds of variables appearing in the quadratic) that is closest to the current
2395 * solution we want to separate.
2396 *
2397 * Furthermore, we store the rays corresponding to the unit vectors, i.e.,
2398 * - if \f$x_i\f$ is at its lower bound in vertex --> \f$r_i = e_i\f$
2399 * - if \f$x_i\f$ is at its upper bound in vertex --> \f$r_i = -e_i\f$
2400 */
2401 static
2402 SCIP_RETCODE findVertexAndGetRays(
2403 SCIP* scip, /**< SCIP data structure */
2404 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nlhdlr expression data */
2405 SCIP_SOL* sol, /**< solution to separate */
2406 SCIP_SOL* vertex, /**< new 'vertex' (w.r.t. bounds) solution to separate */
2407 SCIP_VAR* auxvar, /**< aux var of expr or NULL if not needed (e.g. separating real cons) */
2408 RAYS** raysptr, /**< pointer to new bound rays */
2409 SCIP_Bool* success /**< TRUE if no variable is unbounded */
2410 )
2411 {
2412 SCIP_EXPR* qexpr;
2413 SCIP_EXPR** linexprs;
2414 RAYS* rays;
2415 int nquadexprs;
2416 int nlinexprs;
2417 int raylength;
2418 int i;
2419
2420 *success = TRUE;
2421
2422 qexpr = nlhdlrexprdata->qexpr;
2423 SCIPexprGetQuadraticData(qexpr, NULL, &nlinexprs, &linexprs, NULL, &nquadexprs, NULL, NULL, NULL);
2424
2425 raylength = (auxvar == NULL) ? nquadexprs + nlinexprs : nquadexprs + nlinexprs + 1;
2426
2427 /* create rays */
2428 SCIP_CALL( createBoundRays(scip, raysptr, raylength) );
2429 rays = *raysptr;
2430
2431 rays->rayssize = raylength;
2432 rays->nrays = raylength;
2433
2434 /* go through quadratic variables */
2435 for( i = 0; i < nquadexprs; ++i )
2436 {
2437 SCIP_EXPR* expr;
2438 SCIPexprGetQuadraticQuadTerm(qexpr, i, &expr, NULL, NULL, NULL, NULL, NULL);
2439
2440 rays->raysbegin[i] = i;
2441 rays->raysidx[i] = i;
2442 rays->lpposray[i] = SCIPcolGetLPPos(SCIPvarGetCol(SCIPgetExprAuxVarNonlinear(expr)));
2443
2444 SCIP_CALL( setVarToNearestBound(scip, sol, vertex, SCIPgetExprAuxVarNonlinear(expr),
2445 &rays->rays[i], success) );
2446
2447 if( ! *success )
2448 return SCIP_OKAY;
2449 }
2450
2451 /* go through linear variables */
2452 for( i = 0; i < nlinexprs; ++i )
2453 {
2454 rays->raysbegin[i + nquadexprs] = i + nquadexprs;
2455 rays->raysidx[i + nquadexprs] = i + nquadexprs;
2456 rays->lpposray[i + nquadexprs] = SCIPcolGetLPPos(SCIPvarGetCol(SCIPgetExprAuxVarNonlinear(linexprs[i])));
2457
2458 SCIP_CALL( setVarToNearestBound(scip, sol, vertex, SCIPgetExprAuxVarNonlinear(linexprs[i]),
2459 &rays->rays[i + nquadexprs], success) );
2460
2461 if( ! *success )
2462 return SCIP_OKAY;
2463 }
2464
2465 /* consider auxvar if it exists */
2466 if( auxvar != NULL )
2467 {
2468 rays->raysbegin[nquadexprs + nlinexprs] = nquadexprs + nlinexprs;
2469 rays->raysidx[nquadexprs + nlinexprs] = nquadexprs + nlinexprs;
2470 rays->lpposray[nquadexprs + nlinexprs] = SCIPcolGetLPPos(SCIPvarGetCol(auxvar));
2471
2472 SCIP_CALL( setVarToNearestBound(scip, sol, vertex, auxvar, &rays->rays[nquadexprs + nlinexprs], success) );
2473
2474 if( ! *success )
2475 return SCIP_OKAY;
2476 }
2477
2478 rays->raysbegin[raylength] = raylength;
2479
2480 return SCIP_OKAY;
2481 }
2482
2483 /** checks if the quadratic constraint is violated by sol */
2484 static
2485 SCIP_Bool isQuadConsViolated(
2486 SCIP* scip, /**< SCIP data structure */
2487 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nlhdlr expression data */
2488 SCIP_VAR* auxvar, /**< aux var of expr or NULL if not needed (e.g. separating real cons) */
2489 SCIP_SOL* sol, /**< solution to check feasibility for */
2490 SCIP_Real sidefactor /**< 1.0 if the violated constraint is q ≤ rhs, -1.0 otherwise */
2491 )
2492 {
2493 SCIP_EXPR* qexpr;
2494 SCIP_EXPR** linexprs;
2495 SCIP_Real* lincoefs;
2496 SCIP_Real constant;
2497 SCIP_Real val;
2498 int nquadexprs;
2499 int nlinexprs;
2500 int nbilinexprs;
2501 int i;
2502
2503 qexpr = nlhdlrexprdata->qexpr;
2504 SCIPexprGetQuadraticData(qexpr, &constant, &nlinexprs, &linexprs, &lincoefs, &nquadexprs,
2505 &nbilinexprs, NULL, NULL);
2506
2507 val = 0.0;
2508
2509 /* go through quadratic terms */
2510 for( i = 0; i < nquadexprs; i++ )
2511 {
2512 SCIP_EXPR* expr;
2513 SCIP_Real quadlincoef;
2514 SCIP_Real sqrcoef;
2515 SCIP_Real solval;
2516
2517 SCIPexprGetQuadraticQuadTerm(qexpr, i, &expr, &quadlincoef, &sqrcoef, NULL, NULL, NULL);
2518
2519 solval = SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(expr));
2520
2521 /* add square term */
2522 val += sqrcoef * SQR(solval);
2523
2524 /* add linear term */
2525 val += quadlincoef * solval;
2526 }
2527
2528 /* go through bilinear terms */
2529 for( i = 0; i < nbilinexprs; i++ )
2530 {
2531 SCIP_EXPR* expr1;
2532 SCIP_EXPR* expr2;
2533 SCIP_Real bilincoef;
2534
2535 SCIPexprGetQuadraticBilinTerm(qexpr, i, &expr1, &expr2, &bilincoef, NULL, NULL);
2536
2537 val += bilincoef * SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(expr1))
2538 * SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(expr2));
2539 }
2540
2541 /* go through linear terms */
2542 for( i = 0; i < nlinexprs; i++ )
2543 {
2544 val += lincoefs[i] * SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(linexprs[i]));
2545 }
2546
2547 /* add auxvar if exists and get constant */
2548 if( auxvar != NULL )
2549 {
2550 val -= SCIPgetSolVal(scip, sol, auxvar);
2551
2552 constant = (sidefactor == 1.0) ? constant - SCIPgetRhsNonlinear(nlhdlrexprdata->cons) :
2553 SCIPgetLhsNonlinear(nlhdlrexprdata->cons) - constant;
2554 }
2555 else
2556 constant = (sidefactor * constant);
2557
2558 val = (sidefactor * val);
2559
2560 /* now constraint is q(z) <= const */
2561 if( val <= constant )
2562 return FALSE;
2563 else
2564 return TRUE;
2565 }
2566
2567 /** generates intersection cut that cuts off sol (which violates the quadratic constraint cons) */
2568 static
2569 SCIP_RETCODE generateIntercut(
2570 SCIP* scip, /**< SCIP data structure */
2571 SCIP_EXPR* expr, /**< expr */
2572 SCIP_NLHDLRDATA* nlhdlrdata, /**< nlhdlr data */
2573 SCIP_NLHDLREXPRDATA* nlhdlrexprdata, /**< nlhdlr expression data */
2574 SCIP_CONS* cons, /**< violated constraint that contains expr */
2575 SCIP_SOL* sol, /**< solution to separate */
2576 SCIP_ROWPREP* rowprep, /**< rowprep for the generated cut */
2577 SCIP_Bool overestimate, /**< TRUE if viol cons is q(z) ≥ lhs; FALSE if q(z) ≤ rhs */
2578 SCIP_Bool* success /**< whether separation was successfull or not */
2579 )
2580 {
2581 SCIP_EXPR* qexpr;
2582 RAYS* rays;
2583 SCIP_VAR* auxvar;
2584 SCIP_Real sidefactor;
2585 SCIP_Real* vb; /* eigenvectors * b */
2586 SCIP_Real* vzlp; /* eigenvectors * lpsol */
2587 SCIP_Real* wcoefs; /* coefficients affecting quadterms in w */
2588 SCIP_Real wzlp; /* w(lpsol) */
2589 SCIP_Real kappa;
2590 SCIP_Bool iscase4;
2591 SCIP_SOL* vertex;
2592 SCIP_SOL* soltoseparate;
2593 int nquadexprs;
2594 int nlinexprs;
2595 int i;
2596
2597 /* count number of calls */
2598 (nlhdlrdata->ncalls++);
2599
2600 qexpr = nlhdlrexprdata->qexpr;
2601 SCIPexprGetQuadraticData(qexpr, NULL, &nlinexprs, NULL, NULL, &nquadexprs, NULL, NULL, NULL);
2602
2603 #ifdef DEBUG_INTERSECTIONCUT
2604 SCIPinfoMessage(scip, NULL, "Generating intersection cut for quadratic expr %p aka", (void*)expr);
2605 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
2606 SCIPinfoMessage(scip, NULL, "\n");
2607 #endif
2608
2609 *success = TRUE;
2610 iscase4 = TRUE;
2611
2612 /* in nonbasic space cut is >= 1 */
2613 assert(SCIProwprepGetSide(rowprep) == 0.0);
2614 SCIProwprepAddSide(rowprep, 1.0);
2615 SCIProwprepSetSidetype(rowprep, SCIP_SIDETYPE_LEFT);
2616 assert(SCIProwprepGetSide(rowprep) == 1.0);
2617
2618 auxvar = (nlhdlrexprdata->cons != cons) ? SCIPgetExprAuxVarNonlinear(expr) : NULL;
2619 sidefactor = overestimate ? -1.0 : 1.0;
2620
2621 rays = NULL;
2622
2623 /* check if we use tableau or bounds as rays */
2624 if( ! nlhdlrdata->useboundsasrays )
2625 {
2626 SCIP_CALL( createAndStoreSparseRays(scip, nlhdlrexprdata, auxvar, &rays, success) );
2627
2628 if( ! *success )
2629 {
2630 INTERLOG(printf("Failed to get rays: there is a var with base status ZERO!\n"); )
2631 return SCIP_OKAY;
2632 }
2633
2634 soltoseparate = sol;
2635 }
2636 else
2637 {
2638 SCIP_Bool violated;
2639
2640 if( auxvar != NULL )
2641 {
2642 *success = FALSE;
2643 return SCIP_OKAY;
2644 }
2645
2646 /* create new solution */
2647 SCIP_CALL( SCIPcreateSol(scip, &vertex, NULL) );
2648
2649 /* find nearest vertex of the box to separate and compute rays */
2650 SCIP_CALL( findVertexAndGetRays(scip, nlhdlrexprdata, sol, vertex, auxvar, &rays, success) );
2651
2652 if( ! *success )
2653 {
2654 INTERLOG(printf("Failed to use bounds as rays: variable is unbounded!\n"); )
2655 freeRays(scip, &rays);
2656 SCIP_CALL( SCIPfreeSol(scip, &vertex) );
2657 return SCIP_OKAY;
2658 }
2659
2660 /* check if vertex is violated */
2661 violated = isQuadConsViolated(scip, nlhdlrexprdata, auxvar, vertex, sidefactor);
2662
2663 if( ! violated )
2664 {
2665 INTERLOG(printf("Failed to use bounds as rays: nearest vertex is not violated!\n"); )
2666 freeRays(scip, &rays);
2667 SCIP_CALL( SCIPfreeSol(scip, &vertex) );
2668 *success = FALSE;
2669 return SCIP_OKAY;
2670 }
2671
2672 soltoseparate = vertex;
2673 }
2674
2675 SCIP_CALL( SCIPallocBufferArray(scip, &vb, nquadexprs) );
2676 SCIP_CALL( SCIPallocBufferArray(scip, &vzlp, nquadexprs) );
2677 SCIP_CALL( SCIPallocBufferArray(scip, &wcoefs, nquadexprs) );
2678
2679 SCIP_CALL( intercutsComputeCommonQuantities(scip, nlhdlrexprdata, auxvar, sidefactor, soltoseparate, vb, vzlp, wcoefs, &wzlp, &kappa) );
2680
2681 /* check if we are in case 4 */
2682 if( nlinexprs == 0 && auxvar == NULL )
2683 {
2684 for( i = 0; i < nquadexprs; ++i )
2685 if( wcoefs[i] != 0.0 )
2686 break;
2687
2688 if( i == nquadexprs )
2689 {
2690 /* from now on wcoefs is going to be NULL --> case 1, 2 or 3 */
2691 SCIPfreeBufferArray(scip, &wcoefs);
2692 iscase4 = FALSE;
2693 }
2694 }
2695
2696 /* compute (strengthened) intersection cut */
2697 if( nlhdlrdata->usestrengthening )
2698 {
2699 SCIP_Bool strengthsuccess;
2700
2701 SCIP_CALL( computeStrengthenedIntercut(scip, nlhdlrdata, nlhdlrexprdata, rays, sidefactor, iscase4, vb, vzlp, wcoefs,
2702 wzlp, kappa, rowprep, soltoseparate, success, &strengthsuccess) );
2703
2704 if( *success && strengthsuccess )
2705 nlhdlrdata->nstrengthenings++;
2706 }
2707 else
2708 {
2709 SCIP_CALL( computeIntercut(scip, nlhdlrdata, nlhdlrexprdata, rays, sidefactor, iscase4, vb, vzlp, wcoefs, wzlp, kappa,
2710 rowprep, NULL, soltoseparate, success) );
2711 }
2712
2713 SCIPfreeBufferArrayNull(scip, &wcoefs);
2714 SCIPfreeBufferArray(scip, &vzlp);
2715 SCIPfreeBufferArray(scip, &vb);
2716 freeRays(scip, &rays);
2717
2718 if( nlhdlrdata->useboundsasrays )
2719 {
2720 SCIP_CALL( SCIPfreeSol(scip, &vertex) );
2721 }
2722
2723 return SCIP_OKAY;
2724 }
2725
2726 /** returns whether a quadratic form is "propagable"
2727 *
2728 * It is propagable, if a variable (aka child expr) appears at least twice, which is the case if at least two of the following hold:
2729 * - it appears as a linear term (coef*expr)
2730 * - it appears as a square term (coef*expr^2)
2731 * - it appears in a bilinear term
2732 * - it appears in another bilinear term
2733 */
2734 static
2735 SCIP_Bool isPropagable(
2736 SCIP_EXPR* qexpr /**< quadratic representation data */
2737 )
2738 {
2739 int nquadexprs;
2740 int i;
2741
2742 SCIPexprGetQuadraticData(qexpr, NULL, NULL, NULL, NULL, &nquadexprs, NULL, NULL, NULL);
2743
2744 for( i = 0; i < nquadexprs; ++i )
2745 {
2746 SCIP_Real lincoef;
2747 SCIP_Real sqrcoef;
2748 int nadjbilin;
2749
2750 SCIPexprGetQuadraticQuadTerm(qexpr, i, NULL, &lincoef, &sqrcoef, &nadjbilin, NULL, NULL);
2751
2752 if( (lincoef != 0.0) + (sqrcoef != 0.0) + nadjbilin >= 2 ) /*lint !e514*/ /* actually MIN(2, nadjbilin), but we check >= 2 */
2753 return TRUE;
2754 }
2755
2756 return FALSE;
2757 }
2758
2759 /** returns whether a quadratic term is "propagable"
2760 *
2761 * A term is propagable, if its variable (aka child expr) appears at least twice, which is the case if at least two of the following hold:
2762 * - it appears as a linear term (coef*expr)
2763 * - it appears as a square term (coef*expr^2)
2764 * - it appears in a bilinear term
2765 * - it appears in another bilinear term
2766 */
2767 static
2768 SCIP_Bool isPropagableTerm(
2769 SCIP_EXPR* qexpr, /**< quadratic representation data */
2770 int idx /**< index of quadratic term to consider */
2771 )
2772 {
2773 SCIP_Real lincoef;
2774 SCIP_Real sqrcoef;
2775 int nadjbilin;
2776
2777 SCIPexprGetQuadraticQuadTerm(qexpr, idx, NULL, &lincoef, &sqrcoef, &nadjbilin, NULL, NULL);
2778
2779 return (lincoef != 0.0) + (sqrcoef != 0.0) + nadjbilin >= 2; /*lint !e514*/ /* actually MIN(2, nadjbilin), but we check >= 2 */
2780 }
2781
2782 /** solves a quadratic equation \f$ a\, \text{expr}^2 + b\, \text{expr} \in \text{rhs} \f$ (with \f$b\f$ an interval)
2783 * and reduces bounds on `expr` or deduces infeasibility if possible
2784 */
2785 static
2786 SCIP_RETCODE propagateBoundsQuadExpr(
2787 SCIP* scip, /**< SCIP data structure */
2788 SCIP_EXPR* expr, /**< expression for which to solve */
2789 SCIP_Real sqrcoef, /**< square coefficient */
2790 SCIP_INTERVAL b, /**< interval acting as linear coefficient */
2791 SCIP_INTERVAL rhs, /**< interval acting as rhs */
2792 SCIP_Bool* infeasible, /**< buffer to store if propagation produced infeasibility */
2793 int* nreductions /**< buffer to store the number of interval reductions */
2794 )
2795 {
2796 SCIP_INTERVAL a;
2797 SCIP_INTERVAL exprbounds;
2798 SCIP_INTERVAL newrange;
2799
2800 assert(scip != NULL);
2801 assert(expr != NULL);
2802 assert(infeasible != NULL);
2803 assert(nreductions != NULL);
2804
2805 #ifdef DEBUG_PROP
2806 SCIPinfoMessage(scip, NULL, "Propagating <expr> by solving a <expr>^2 + b <expr> in rhs, where <expr> is: ");
2807 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
2808 SCIPinfoMessage(scip, NULL, "\n");
2809 SCIPinfoMessage(scip, NULL, "expr in [%g, %g], a = %g, b = [%g, %g] and rhs = [%g, %g]\n",
2810 SCIPintervalGetInf(SCIPgetExprBoundsNonlinear(scip, expr)),
2811 SCIPintervalGetSup(SCIPgetExprBoundsNonlinear(scip, expr)), sqrcoef, b.inf, b.sup,
2812 rhs.inf, rhs.sup);
2813 #endif
2814
2815 exprbounds = SCIPgetExprBoundsNonlinear(scip, expr);
2816 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, exprbounds) )
2817 {
2818 *infeasible = TRUE;
2819 return SCIP_OKAY;
2820 }
2821
2822 /* compute solution of a*x^2 + b*x in rhs */
2823 SCIPintervalSet(&a, sqrcoef);
2824 SCIPintervalSolveUnivariateQuadExpression(SCIP_INTERVAL_INFINITY, &newrange, a, b, rhs, exprbounds);
2825
2826 #ifdef DEBUG_PROP
2827 SCIPinfoMessage(scip, NULL, "Solution [%g, %g]\n", newrange.inf, newrange.sup);
2828 #endif
2829
2830 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, expr, newrange, infeasible, nreductions) );
2831
2832 return SCIP_OKAY;
2833 }
2834
2835 /** solves a linear equation \f$ b\, \text{expr} \in \text{rhs} \f$ (with \f$b\f$ a scalar) and reduces bounds on `expr` or deduces infeasibility if possible */
2836 static
2837 SCIP_RETCODE propagateBoundsLinExpr(
2838 SCIP* scip, /**< SCIP data structure */
2839 SCIP_EXPR* expr, /**< expression for which to solve */
2840 SCIP_Real b, /**< linear coefficient */
2841 SCIP_INTERVAL rhs, /**< interval acting as rhs */
2842 SCIP_Bool* infeasible, /**< buffer to store if propagation produced infeasibility */
2843 int* nreductions /**< buffer to store the number of interval reductions */
2844 )
2845 {
2846 SCIP_INTERVAL newrange;
2847
2848 assert(scip != NULL);
2849 assert(expr != NULL);
2850 assert(infeasible != NULL);
2851 assert(nreductions != NULL);
2852
2853 #ifdef DEBUG_PROP
2854 SCIPinfoMessage(scip, NULL, "Propagating <expr> by solving %g <expr> in [%g, %g], where <expr> is: ", b, rhs.inf, rhs.sup);
2855 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
2856 SCIPinfoMessage(scip, NULL, "\n");
2857 #endif
2858
2859 /* compute solution of b*x in rhs */
2860 SCIPintervalDivScalar(SCIP_INTERVAL_INFINITY, &newrange, rhs, b);
2861
2862 #ifdef DEBUG_PROP
2863 SCIPinfoMessage(scip, NULL, "Solution [%g, %g]\n", newrange.inf, newrange.sup);
2864 #endif
2865
2866 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, expr, newrange, infeasible, nreductions) );
2867
2868 return SCIP_OKAY;
2869 }
2870
2871 /** returns max of a/x - c*x for x in {x1, x2} with x1, x2 > 0 */
2872 static
2873 SCIP_Real computeMaxBoundaryForBilinearProp(
2874 SCIP_Real a, /**< coefficient a */
2875 SCIP_Real c, /**< coefficient c */
2876 SCIP_Real x1, /**< coefficient x1 > 0 */
2877 SCIP_Real x2 /**< coefficient x2 > 0 */
2878 )
2879 {
2880 SCIP_Real cneg;
2881 SCIP_Real cand1;
2882 SCIP_Real cand2;
2883 SCIP_ROUNDMODE roundmode;
2884
2885 assert(x1 > 0.0);
2886 assert(x2 > 0.0);
2887
2888 cneg = SCIPintervalNegateReal(c);
2889
2890 roundmode = SCIPintervalGetRoundingMode();
2891 SCIPintervalSetRoundingModeUpwards();
2892 cand1 = a/x1 + cneg*x1;
2893 cand2 = a/x2 + cneg*x2;
2894 SCIPintervalSetRoundingMode(roundmode);
2895
2896 return MAX(cand1, cand2);
2897 }
2898
2899 /** returns max of a/x - c*x for x in dom; it assumes that dom is contained in (0, +inf) */
2900 static
2901 SCIP_Real computeMaxForBilinearProp(
2902 SCIP_Real a, /**< coefficient a */
2903 SCIP_Real c, /**< coefficient c */
2904 SCIP_INTERVAL dom /**< domain of x */
2905 )
2906 {
2907 SCIP_ROUNDMODE roundmode;
2908 SCIP_INTERVAL argmax;
2909 SCIP_Real negunresmax;
2910 SCIP_Real boundarymax;
2911 assert(dom.inf > 0);
2912
2913 /* if a >= 0, then the function is convex which means the maximum is at one of the boundaries
2914 *
2915 * if c = 0, then the function is monotone which means the maximum is also at one of the boundaries
2916 *
2917 * if a < 0, then the function is concave. The function then has a maximum if and only if there is a point with derivative 0,
2918 * that is, iff -a/x^2 - c = 0 has a solution; i.e. if -a/c >= 0, i.e. (using a<0 and c != 0), c > 0.
2919 * Otherwise (that is, c<0), the maximum is at one of the boundaries.
2920 */
2921 if( a >= 0.0 || c <= 0.0 )
2922 return computeMaxBoundaryForBilinearProp(a, c, dom.inf, dom.sup);
2923
2924 /* now, the (unrestricted) maximum is at sqrt(-a/c).
2925 * if the argmax is not in the interior of dom then the solution is at a boundary, too
2926 * we check this by computing an interval that contains sqrt(-a/c) first
2927 */
2928 SCIPintervalSet(&argmax, -a);
2929 SCIPintervalDivScalar(SCIP_INTERVAL_INFINITY, &argmax, argmax, c);
2930 SCIPintervalSquareRoot(SCIP_INTERVAL_INFINITY, &argmax, argmax);
2931
2932 /* if the interval containing sqrt(-a/c) does not intersect with the interior of dom, then
2933 * the (restricted) maximum is at a boundary (we could even say at which boundary, but that doesn't save much)
2934 */
2935 if( argmax.sup <= dom.inf || argmax.inf >= dom.sup )
2936 return computeMaxBoundaryForBilinearProp(a, c, dom.inf, dom.sup);
2937
2938 /* the maximum at sqrt(-a/c) is -2*sqrt(-a*c), so we compute an upper bound for that by computing a lower bound for 2*sqrt(-a*c) */
2939 roundmode = SCIPintervalGetRoundingMode();
2940 SCIPintervalSetRoundingModeDownwards();
2941 negunresmax = 2.0*SCIPnextafter(sqrt(SCIPintervalNegateReal(a)*c), 0.0);
2942 SCIPintervalSetRoundingMode(roundmode);
2943
2944 /* if the interval containing sqrt(-a/c) is contained in dom, then we can return -negunresmax */
2945 if( argmax.inf >= dom.inf && argmax.sup <= dom.sup )
2946 return -negunresmax;
2947
2948 /* now what is left is the case where we cannot say for sure whether sqrt(-a/c) is contained in dom or not
2949 * so we are conservative and return the max of both cases, i.e.,
2950 * the max of the upper bounds on -2*sqrt(-a*c), a/dom.inf-c*dom.inf, a/dom.sup-c*dom.sup.
2951 */
2952 boundarymax = computeMaxBoundaryForBilinearProp(a, c, dom.inf, dom.sup);
2953 return MAX(boundarymax, -negunresmax);
2954 }
2955
2956 /** computes the range of rhs/x - coef * x for x in exprdom; this is used for the propagation of bilinear terms
2957 *
2958 * If 0 is in the exprdom, we set range to \f$\mathbb{R}\f$ (even though this is not quite correct, it is correct for the
2959 * intended use of the function).
2960 * TODO: maybe check before calling it whether 0 is in the domain and then just avoid calling it
2961 *
2962 * If rhs is [A,B] and x > 0, then we want the min of A/x - coef*x and max of B/x - coef*x for x in [exprdom].
2963 * If rhs is [A,B] and x < 0, then we want the min of B/x - coef*x and max of A/x - coef*x for x in [exprdom].
2964 * However, this is the same as min of -B/x + coef*x and max of -A/x + coef*x for x in -[exprdom].
2965 * Thus, we can always reduce to x > 0 by multiplying [exprdom], rhs, and coef by -1.
2966 */
2967 static
2968 void computeRangeForBilinearProp(
2969 SCIP_INTERVAL exprdom, /**< expression for which to solve */
2970 SCIP_Real coef, /**< expression for which to solve */
2971 SCIP_INTERVAL rhs, /**< rhs used for computation */
2972 SCIP_INTERVAL* range /**< storage for the resulting range */
2973 )
2974 {
2975 SCIP_Real max;
2976 SCIP_Real min;
2977
2978 if( exprdom.inf <= 0.0 && 0.0 <= exprdom.sup )
2979 {
2980 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, range);
2981 return;
2982 }
2983
2984 /* reduce to positive case */
2985 if( exprdom.sup < 0 )
2986 {
2987 SCIPintervalMulScalar(SCIP_INTERVAL_INFINITY, &exprdom, exprdom, -1.0);
2988 SCIPintervalMulScalar(SCIP_INTERVAL_INFINITY, &rhs, rhs, -1.0);
2989 coef *= -1.0;
2990 }
2991 assert(exprdom.inf > 0.0);
2992
2993 /* compute maximum and minimum */
2994 max = computeMaxForBilinearProp(rhs.sup, coef, exprdom);
2995 min = -computeMaxForBilinearProp(-rhs.inf, -coef, exprdom);
2996
2997 /* set interval */
2998 SCIPintervalSetBounds(range, min, max);
2999 }
3000
3001 /** reverse propagates coef_i expr_i + constant in rhs */
3002 static
3003 SCIP_RETCODE reversePropagateLinearExpr(
3004 SCIP* scip, /**< SCIP data structure */
3005 SCIP_EXPR** linexprs, /**< linear expressions */
3006 int nlinexprs, /**< number of linear expressions */
3007 SCIP_Real* lincoefs, /**< coefficients of linear expressions */
3008 SCIP_Real constant, /**< constant */
3009 SCIP_INTERVAL rhs, /**< rhs */
3010 SCIP_Bool* infeasible, /**< buffer to store whether an exps' bounds were propagated to an empty interval */
3011 int* nreductions /**< buffer to store the number of interval reductions of all exprs */
3012 )
3013 {
3014 SCIP_INTERVAL* oldboundslin;
3015 SCIP_INTERVAL* newboundslin;
3016 int i;
3017
3018 if( nlinexprs == 0 )
3019 return SCIP_OKAY;
3020
3021 SCIP_CALL( SCIPallocBufferArray(scip, &oldboundslin, nlinexprs) );
3022 SCIP_CALL( SCIPallocBufferArray(scip, &newboundslin, nlinexprs) );
3023
3024 for( i = 0; i < nlinexprs; ++i )
3025 oldboundslin[i] = SCIPexprGetActivity(linexprs[i]); /* TODO use SCIPgetExprBoundsNonlinear(scip, linexprs[i]) ? */
3026
3027 *nreductions = SCIPintervalPropagateWeightedSum(SCIP_INTERVAL_INFINITY, nlinexprs,
3028 oldboundslin, lincoefs, constant, rhs, newboundslin, infeasible);
3029
3030 if( *nreductions > 0 && !*infeasible )
3031 {
3032 /* SCIP is more conservative with what constitutes a reduction than interval arithmetic so we follow SCIP */
3033 *nreductions = 0;
3034 for( i = 0; i < nlinexprs && ! (*infeasible); ++i )
3035 {
3036 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, linexprs[i], newboundslin[i], infeasible, nreductions) );
3037 }
3038 }
3039
3040 SCIPfreeBufferArray(scip, &newboundslin);
3041 SCIPfreeBufferArray(scip, &oldboundslin);
3042
3043 return SCIP_OKAY;
3044 }
3045
3046
3047 /*
3048 * Callback methods of nonlinear handler
3049 */
3050
3051 /** callback to free expression specific data */
3052 static
3053 SCIP_DECL_NLHDLRFREEEXPRDATA(nlhdlrFreeexprdataQuadratic)
3054 { /*lint --e{715}*/
3055 assert(nlhdlrexprdata != NULL);
3056 assert(*nlhdlrexprdata != NULL);
3057
3058 if( (*nlhdlrexprdata)->quadactivities != NULL )
3059 {
3060 int nquadexprs;
3061 SCIPexprGetQuadraticData((*nlhdlrexprdata)->qexpr, NULL, NULL, NULL, NULL, &nquadexprs, NULL, NULL, NULL);
3062 SCIPfreeBlockMemoryArray(scip, &(*nlhdlrexprdata)->quadactivities, nquadexprs);
3063 }
3064
3065 SCIPfreeBlockMemory(scip, nlhdlrexprdata);
3066
3067 return SCIP_OKAY;
3068 }
3069
3070 /** callback to detect structure in expression tree
3071 *
3072 * A term is quadratic if
3073 * - it is a product expression of two expressions, or
3074 * - it is power expression of an expression with exponent 2.0.
3075 *
3076 * We define a _propagable_ quadratic expression as a quadratic expression whose termwise propagation does not yield the
3077 * best propagation. In other words, is a quadratic expression that suffers from the dependency problem.
3078 *
3079 * Specifically, a propagable quadratic expression is a sum expression such that there is at least one expr that appears
3080 * at least twice (because of simplification, this means it appears in a quadratic terms and somewhere else).
3081 * For example: \f$x^2 + y^2\f$ is not a propagable quadratic expression; \f$x^2 + x\f$ is a propagable quadratic expression;
3082 * \f$x^2 + x y\f$ is also a propagable quadratic expression
3083 *
3084 * Furthermore, we distinguish between propagable and non-propagable terms. A term is propagable if any of the expressions
3085 * involved in it appear somewhere else. For example, \f$xy + z^2 + z\f$ is a propagable quadratic, the term \f$xy\f$ is
3086 * non-propagable, and \f$z^2\f$ is propagable. For propagation, non-propagable terms are handled as if they were linear
3087 * terms, that is, we do not use the activity of \f$x\f$ and \f$y\f$ to compute the activity of \f$xy\f$ but rather we use directly
3088 * the activity of \f$xy\f$. Similarly, we do not backward propagate to \f$x\f$ and \f$y\f$ (the product expr handler will do this),
3089 * but we backward propagate to \f$x*y\f$. More technically, we register \f$xy\f$ for its activity usage, rather than\f$x\f$ and \f$y\f$.
3090 *
3091 * For propagation, we store the quadratic in our data structure in the following way: We count how often a variable
3092 * appears. Then, a bilinear product expr_i * expr_j is stored as expr_i * expr_j if # expr_i appears > # expr_j
3093 * appears. When # expr_i appears = # expr_j appears, it then it will be stored as expr_i * expr_j if and only if
3094 * expr_i < expr_j, where '<' is the expression order (see \ref EXPR_ORDER "Ordering Rules" in \ref scip_expr.h).
3095 * Heuristically, this should be useful for propagation. The intuition is that by factoring out the variable that
3096 * appears most often we should be able to take care of the dependency problem better.
3097 *
3098 * Simple convex quadratics like \f$x^2 + y^2\f$ are ignored since the default nlhdlr will take care of them.
3099 *
3100 * @note The expression needs to be simplified (in particular, it is assumed to be sorted).
3101 * @note Common subexpressions are also assumed to have been identified, the hashing will fail otherwise!
3102 *
3103 * Sorted implies that:
3104 * - expr < expr^2: bases are the same, but exponent 1 < 2
3105 * - expr < expr * other_expr: u*v < w holds if and only if v < w (OR8), but here w = u < v, since expr comes before
3106 * other_expr in the product
3107 * - expr < other_expr * expr: u*v < w holds if and only if v < w (OR8), but here v = w
3108 *
3109 * Thus, if we see somebody twice, it is a propagable quadratic.
3110 *
3111 * It also implies that
3112 * - expr^2 < expr * other_expr
3113 * - other_expr * expr < expr^2
3114 *
3115 * It also implies that x^-2 < x^-1, but since, so far, we do not interpret x^-2 as (x^-1)^2, it is not a problem.
3116 */
3117 static
3118 SCIP_DECL_NLHDLRDETECT(nlhdlrDetectQuadratic)
3119 { /*lint --e{715,774}*/
3120 SCIP_NLHDLREXPRDATA* nlexprdata;
3121 SCIP_NLHDLRDATA* nlhdlrdata;
3122 SCIP_Real* eigenvalues;
3123 SCIP_Bool isquadratic;
3124 SCIP_Bool propagable;
3125
3126 assert(scip != NULL);
3127 assert(nlhdlr != NULL);
3128 assert(expr != NULL);
3129 assert(enforcing != NULL);
3130 assert(participating != NULL);
3131 assert(nlhdlrexprdata != NULL);
3132
3133 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
3134 assert(nlhdlrdata != NULL);
3135
3136 /* don't check if all enforcement methods are already ensured */
3137 if( (*enforcing & SCIP_NLHDLR_METHOD_ALL) == SCIP_NLHDLR_METHOD_ALL )
3138 return SCIP_OKAY;
3139
3140 /* if it is not a sum of at least two terms, it is not interesting */
3141 /* TODO: constraints of the form l<= x*y <= r ? */
3142 if( ! SCIPisExprSum(scip, expr) || SCIPexprGetNChildren(expr) < 2 )
3143 return SCIP_OKAY;
3144
3145 /* If we are in a subSCIP we don't want to separate intersection cuts */
3146 if( SCIPgetSubscipDepth(scip) > 0 )
3147 nlhdlrdata->useintersectioncuts = FALSE;
3148
3149 #ifdef SCIP_DEBUG
3150 SCIPinfoMessage(scip, NULL, "Nlhdlr quadratic detecting expr %p aka ", (void*)expr);
3151 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
3152 SCIPinfoMessage(scip, NULL, "\n");
3153 SCIPinfoMessage(scip, NULL, "Have to enforce %d\n", *enforcing);
3154 #endif
3155
3156 /* check whether expression is quadratic (a sum with at least one square or bilinear term) */
3157 SCIP_CALL( SCIPcheckExprQuadratic(scip, expr, &isquadratic) );
3158
3159 /* not quadratic -> nothing for us */
3160 if( !isquadratic )
3161 {
3162 SCIPdebugMsg(scip, "expr %p is not quadratic -> abort detect\n", (void*)expr);
3163 return SCIP_OKAY;
3164 }
3165
3166 propagable = isPropagable(expr);
3167
3168 /* if we are not propagable and are in presolving, return */
3169 if( !propagable && SCIPgetStage(scip) == SCIP_STAGE_PRESOLVING )
3170 {
3171 SCIPdebugMsg(scip, "expr %p is not propagable and in presolving -> abort detect\n", (void*)expr);
3172 return SCIP_OKAY;
3173 }
3174
3175 /* if we do not use intersection cuts and are not propagable, then we do not want to handle it at all;
3176 * if not propagable, then we need to check the curvature to decide if we want to generate intersection cuts
3177 */
3178 if( !propagable && !nlhdlrdata->useintersectioncuts )
3179 {
3180 SCIPdebugMsg(scip, "expr %p is not propagable -> abort detect\n", (void*)expr);
3181 return SCIP_OKAY;
3182 }
3183
3184 /* store quadratic in nlhdlrexprdata */
3185 SCIP_CALL( SCIPallocClearBlockMemory(scip, nlhdlrexprdata) );
3186 nlexprdata = *nlhdlrexprdata;
3187 nlexprdata->qexpr = expr;
3188 nlexprdata->cons = cons;
3189
3190 #ifdef DEBUG_DETECT
3191 SCIPinfoMessage(scip, NULL, "Nlhdlr quadratic detected:\n");
3192 SCIP_CALL( SCIPprintExprQuadratic(scip, conshdlr, qexpr) );
3193 #endif
3194
3195 /* every propagable quadratic expression will be handled since we can propagate */
3196 if( propagable )
3197 {
3198 SCIP_EXPR** linexprs;
3199 int nlinexprs;
3200 int nquadexprs;
3201 int nbilin;
3202 int i;
3203
3204 *participating |= SCIP_NLHDLR_METHOD_ACTIVITY;
3205 *enforcing |= SCIP_NLHDLR_METHOD_ACTIVITY;
3206
3207 SCIPexprGetQuadraticData(expr, NULL, &nlinexprs, &linexprs, NULL, &nquadexprs, &nbilin, NULL, NULL);
3208 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &nlexprdata->quadactivities, nquadexprs) );
3209
3210 /* notify children of quadratic that we will need their activity for propagation */
3211 for( i = 0; i < nlinexprs; ++i )
3212 SCIP_CALL( SCIPregisterExprUsageNonlinear(scip, linexprs[i], FALSE, TRUE, FALSE, FALSE) );
3213
3214 for( i = 0; i < nquadexprs; ++i )
3215 {
3216 SCIP_EXPR* argexpr;
3217 if( isPropagableTerm(expr, i) )
3218 {
3219 SCIPexprGetQuadraticQuadTerm(expr, i, &argexpr, NULL, NULL, &nbilin, NULL, NULL);
3220 SCIP_CALL( SCIPregisterExprUsageNonlinear(scip, argexpr, FALSE, TRUE, FALSE, FALSE) );
3221
3222 #ifdef DEBUG_DETECT
3223 SCIPinfoMessage(scip, NULL, "quadterm %d propagable, using %p, unbounded=%d\n", i, (void*)argexpr, nbilin >
3224 0 && SCIPintervalIsEntire(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(scip, argexpr)));
3225 #endif
3226 }
3227 else
3228 {
3229 /* non-propagable quadratic is either a single square term or a single bilinear term
3230 * we should make use nlhdlrs in pow or product for this term, so we register usage of the square or product
3231 * expr instead of argexpr
3232 */
3233 SCIP_EXPR* sqrexpr;
3234 int* adjbilin;
3235
3236 SCIPexprGetQuadraticQuadTerm(expr, i, &argexpr, NULL, NULL, &nbilin, &adjbilin, &sqrexpr);
3237
3238 if( sqrexpr != NULL )
3239 {
3240 SCIP_CALL( SCIPregisterExprUsageNonlinear(scip, sqrexpr, FALSE, TRUE, FALSE, FALSE) );
3241 assert(nbilin == 0);
3242
3243 #ifdef DEBUG_DETECT
3244 SCIPinfoMessage(scip, NULL, "quadterm %d non-propagable square, using %p\n", i, (void*)sqrexpr);
3245 #endif
3246 }
3247 else
3248 {
3249 /* we have expr1 * other_expr or other_expr * expr1; know that expr1 is non propagable, but to decide if
3250 * we want the bounds of expr1 or of the product expr1 * other_expr (or other_expr * expr1), we have to
3251 * decide whether other_expr is also non propagable; due to the way we sort bilinear terms (by
3252 * frequency), we can deduce that other_expr doesn't appear anywhere else (i.e. is non propagable) if the
3253 * product is of the form expr1 * other_expr; however, if we see other_expr * expr1 we need to find
3254 * other_expr and check whether it is propagable
3255 */
3256 SCIP_EXPR* expr1;
3257 SCIP_EXPR* prodexpr;
3258
3259 assert(nbilin == 1);
3260 SCIPexprGetQuadraticBilinTerm(expr, adjbilin[0], &expr1, NULL, NULL, NULL, &prodexpr);
3261
3262 if( expr1 == argexpr )
3263 {
3264 SCIP_CALL( SCIPregisterExprUsageNonlinear(scip, prodexpr, FALSE, TRUE, FALSE, FALSE) );
3265
3266 #ifdef DEBUG_DETECT
3267 SCIPinfoMessage(scip, NULL, "quadterm %d non-propagable product, using %p\n", i, (void*)prodexpr);
3268 #endif
3269 }
3270 else
3271 {
3272 int j;
3273 /* check if other_expr is propagable in which case we need the bounds of expr1; otherwise we just need
3274 * the bounds of the product and this will be (or was) registered when the loop takes us to the
3275 * quadexpr other_expr.
3276 * TODO this should be done faster, maybe store pos1 in bilinexprterm or store quadexprterm's in bilinexprterm
3277 */
3278 for( j = 0; j < nquadexprs; ++j )
3279 {
3280 SCIP_EXPR* exprj;
3281 SCIPexprGetQuadraticQuadTerm(expr, j, &exprj, NULL, NULL, NULL, NULL, NULL);
3282 if( expr1 == exprj )
3283 {
3284 if( isPropagableTerm(expr, j) )
3285 {
3286 SCIP_CALL( SCIPregisterExprUsageNonlinear(scip, argexpr, FALSE, TRUE, FALSE, FALSE) );
3287 #ifdef DEBUG_DETECT
3288 SCIPinfoMessage(scip, NULL, "quadterm %d non-propagable alien product, using %p\n", i, (void*)argexpr);
3289 #endif
3290 }
3291 break;
3292 }
3293 }
3294 }
3295 }
3296 }
3297 }
3298 }
3299
3300 /* check if we are going to separate or not */
3301 nlexprdata->curvature = SCIP_EXPRCURV_UNKNOWN;
3302
3303 /* for now, we do not care about separation if it is not required */
3304 if( (*enforcing & SCIP_NLHDLR_METHOD_SEPABOTH) == SCIP_NLHDLR_METHOD_SEPABOTH )
3305 {
3306 /* if nobody can do anything, remove data */
3307 if( *participating == SCIP_NLHDLR_METHOD_NONE ) /*lint !e845*/
3308 {
3309 SCIP_CALL( nlhdlrFreeexprdataQuadratic(scip, nlhdlr, expr, nlhdlrexprdata) );
3310 }
3311 else
3312 {
3313 SCIPdebugMsg(scip, "expr %p is quadratic and propagable -> propagate\n", (void*)expr);
3314 }
3315 return SCIP_OKAY;
3316 }
3317
3318 assert(SCIPgetStage(scip) >= SCIP_STAGE_INITSOLVE); /* separation should only be required in (init)solving stage */
3319
3320 /* check if we can do something more: check curvature of quadratic function stored in nlexprdata
3321 * this is currently only used to decide whether we want to separate, so it can be skipped if in presolve
3322 */
3323 SCIPdebugMsg(scip, "checking curvature of expr %p\n", (void*)expr);
3324 SCIP_CALL( SCIPcomputeExprQuadraticCurvature(scip, expr, &nlexprdata->curvature, NULL, nlhdlrdata->useintersectioncuts) );
3325
3326 /* get eigenvalues to be able to check whether they were computed */
3327 SCIPexprGetQuadraticData(expr, NULL, NULL, NULL, NULL, NULL, NULL, &eigenvalues, NULL);
3328
3329 /* if we use intersection cuts then we can handle any non-convex quadratic */
3330 if( nlhdlrdata->useintersectioncuts && eigenvalues != NULL && (*enforcing & SCIP_NLHDLR_METHOD_SEPABELOW) ==
3331 FALSE && nlexprdata->curvature != SCIP_EXPRCURV_CONVEX )
3332 {
3333 *participating |= SCIP_NLHDLR_METHOD_SEPABELOW;
3334 }
3335
3336 if( nlhdlrdata->useintersectioncuts && eigenvalues != NULL && (*enforcing & SCIP_NLHDLR_METHOD_SEPAABOVE) == FALSE &&
3337 nlexprdata->curvature != SCIP_EXPRCURV_CONCAVE )
3338 {
3339 *participating |= SCIP_NLHDLR_METHOD_SEPAABOVE;
3340 }
3341
3342 /* if nobody can do anything, remove data */
3343 if( *participating == SCIP_NLHDLR_METHOD_NONE ) /*lint !e845*/
3344 {
3345 SCIP_CALL( nlhdlrFreeexprdataQuadratic(scip, nlhdlr, expr, nlhdlrexprdata) );
3346 return SCIP_OKAY;
3347 }
3348
3349 /* we only need auxiliary variables if we are going to separate */
3350 if( *participating & SCIP_NLHDLR_METHOD_SEPABOTH )
3351 {
3352 SCIP_EXPR** linexprs;
3353 int nquadexprs;
3354 int nlinexprs;
3355 int i;
3356
3357 SCIPexprGetQuadraticData(expr, NULL, &nlinexprs, &linexprs, NULL, &nquadexprs, NULL, NULL, NULL);
3358
3359 for( i = 0; i < nlinexprs; ++i ) /* expressions appearing linearly */
3360 {
3361 SCIP_CALL( SCIPregisterExprUsageNonlinear(scip, linexprs[i], TRUE, FALSE, FALSE, FALSE) );
3362 }
3363 for( i = 0; i < nquadexprs; ++i ) /* expressions appearing quadratically */
3364 {
3365 SCIP_EXPR* quadexpr;
3366 SCIPexprGetQuadraticQuadTerm(expr, i, &quadexpr, NULL, NULL, NULL, NULL, NULL);
3367 SCIP_CALL( SCIPregisterExprUsageNonlinear(scip, quadexpr, TRUE, FALSE, FALSE, FALSE) );
3368 }
3369
3370 SCIPdebugMsg(scip, "expr %p is quadratic and propagable -> propagate and separate\n", (void*)expr);
3371
3372 nlexprdata->separating = TRUE;
3373 }
3374 else
3375 {
3376 SCIPdebugMsg(scip, "expr %p is quadratic and propagable -> propagate only\n", (void*)expr);
3377 }
3378
3379 if( SCIPexprAreQuadraticExprsVariables(expr) )
3380 {
3381 SCIPexprSetCurvature(expr, nlexprdata->curvature);
3382 SCIPdebugMsg(scip, "expr is %s in the original variables\n", nlexprdata->curvature == SCIP_EXPRCURV_CONCAVE ? "concave" : "convex");
3383 nlexprdata->origvars = TRUE;
3384 }
3385
3386 return SCIP_OKAY;
3387 }
3388
3389 /** nonlinear handler auxiliary evaluation callback */
3390 static
3391 SCIP_DECL_NLHDLREVALAUX(nlhdlrEvalauxQuadratic)
3392 { /*lint --e{715}*/
3393 int i;
3394 int nlinexprs;
3395 int nquadexprs;
3396 int nbilinexprs;
3397 SCIP_Real constant;
3398 SCIP_Real* lincoefs;
3399 SCIP_EXPR** linexprs;
3400
3401 assert(scip != NULL);
3402 assert(expr != NULL);
3403 assert(auxvalue != NULL);
3404 assert(nlhdlrexprdata->separating);
3405 assert(nlhdlrexprdata->qexpr == expr);
3406
3407 /* if the quadratic is in the original variable we can just evaluate the expression */
3408 if( nlhdlrexprdata->origvars )
3409 {
3410 *auxvalue = SCIPexprGetEvalValue(expr);
3411 return SCIP_OKAY;
3412 }
3413
3414 /* TODO there was a
3415 *auxvalue = SCIPevalExprQuadratic(scip, nlhdlrexprdata->qexpr, sol);
3416 here; any reason why not using this anymore?
3417 */
3418
3419 SCIPexprGetQuadraticData(expr, &constant, &nlinexprs, &linexprs, &lincoefs, &nquadexprs, &nbilinexprs, NULL, NULL);
3420
3421 *auxvalue = constant;
3422
3423 for( i = 0; i < nlinexprs; ++i ) /* linear exprs */
3424 *auxvalue += lincoefs[i] * SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(linexprs[i]));
3425
3426 for( i = 0; i < nquadexprs; ++i ) /* quadratic terms */
3427 {
3428 SCIP_Real solval;
3429 SCIP_Real lincoef;
3430 SCIP_Real sqrcoef;
3431 SCIP_EXPR* qexpr;
3432
3433 SCIPexprGetQuadraticQuadTerm(expr, i, &qexpr, &lincoef, &sqrcoef, NULL, NULL, NULL);
3434
3435 solval = SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(qexpr));
3436 *auxvalue += (lincoef + sqrcoef * solval) * solval;
3437 }
3438
3439 for( i = 0; i < nbilinexprs; ++i ) /* bilinear terms */
3440 {
3441 SCIP_EXPR* expr1;
3442 SCIP_EXPR* expr2;
3443 SCIP_Real coef;
3444
3445 SCIPexprGetQuadraticBilinTerm(expr, i, &expr1, &expr2, &coef, NULL, NULL);
3446
3447 *auxvalue += coef * SCIPgetSolVal(scip, sol, SCIPgetExprAuxVarNonlinear(expr1)) * SCIPgetSolVal(scip, sol,
3448 SCIPgetExprAuxVarNonlinear(expr2));
3449 }
3450
3451 return SCIP_OKAY;
3452 }
3453
3454 /** nonlinear handler enforcement callback */
3455 static
3456 SCIP_DECL_NLHDLRENFO(nlhdlrEnfoQuadratic)
3457 { /*lint --e{715}*/
3458 SCIP_NLHDLRDATA* nlhdlrdata;
3459 SCIP_ROWPREP* rowprep;
3460 SCIP_Bool success = FALSE;
3461 SCIP_NODE* node;
3462 int depth;
3463 SCIP_Longint nodenumber;
3464 SCIP_Real* eigenvalues;
3465 SCIP_Real violation;
3466
3467 assert(nlhdlrexprdata != NULL);
3468 assert(nlhdlrexprdata->qexpr == expr);
3469
3470 INTERLOG(printf("Starting interesection cuts!\n");)
3471
3472 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
3473 assert(nlhdlrdata != NULL);
3474
3475 assert(result != NULL);
3476 *result = SCIP_DIDNOTRUN;
3477
3478 /* estimate should take care of convex quadratics */
3479 if( ( overestimate && nlhdlrexprdata->curvature == SCIP_EXPRCURV_CONCAVE) ||
3480 (!overestimate && nlhdlrexprdata->curvature == SCIP_EXPRCURV_CONVEX) )
3481 {
3482 INTERLOG(printf("Convex, no need of interesection cuts!\n");)
3483 return SCIP_OKAY;
3484 }
3485
3486 /* nothing to do if we can't use intersection cuts */
3487 if( ! nlhdlrdata->useintersectioncuts )
3488 {
3489 INTERLOG(printf("We don't use intersection cuts!\n");)
3490 return SCIP_OKAY;
3491 }
3492
3493 /* right now can use interesction cuts only if a basic LP solution is at hand; TODO: in principle we can do something
3494 * even if it is not optimal
3495 */
3496 if( sol != NULL || SCIPgetLPSolstat(scip) != SCIP_LPSOLSTAT_OPTIMAL || !SCIPisLPSolBasic(scip) )
3497 {
3498 INTERLOG(printf("LP solutoin not good!\n");)
3499 return SCIP_OKAY;
3500 }
3501
3502 /* only separate at selected nodes */
3503 node = SCIPgetCurrentNode(scip);
3504 depth = SCIPnodeGetDepth(node);
3505 if( (nlhdlrdata->atwhichnodes == -1 && depth != 0) || (nlhdlrdata->atwhichnodes != -1 && depth % nlhdlrdata->atwhichnodes != 0) )
3506 {
3507 INTERLOG(printf("Don't separate at this node\n");)
3508 return SCIP_OKAY;
3509 }
3510
3511 /* do not add more than ncutslimitroot cuts in root node and ncutslimit cuts in the non-root nodes */
3512 nodenumber = SCIPnodeGetNumber(node);
3513 if( nlhdlrdata->lastnodenumber != nodenumber )
3514 {
3515 nlhdlrdata->lastnodenumber = nodenumber;
3516 nlhdlrdata->lastncuts = nlhdlrdata->ncutsadded;
3517 }
3518 /*else if( (depth > 0 && nlhdlrdata->ncutsadded - nlhdlrdata->lastncuts >= nlhdlrdata->ncutslimit) || (depth == 0 &&
3519 nlhdlrdata->ncutsadded - nlhdlrdata->lastncuts >= nlhdlrdata->ncutslimitroot)) */
3520 /* allow the addition of a certain number of cuts per quadratic */
3521 if( (depth > 0 && nlhdlrexprdata->ncutsadded >= nlhdlrdata->ncutslimit) || (depth == 0 &&
3522 nlhdlrexprdata->ncutsadded >= nlhdlrdata->ncutslimitroot) )
3523 {
3524 INTERLOG(printf("Too many cuts added already\n");)
3525 return SCIP_OKAY;
3526 }
3527
3528 /* can't separate if we do not have an eigendecomposition */
3529 SCIPexprGetQuadraticData(expr, NULL, NULL, NULL, NULL, NULL, NULL, &eigenvalues, NULL);
3530 if( eigenvalues == NULL )
3531 {
3532 INTERLOG(printf("No known eigenvalues!\n");)
3533 return SCIP_OKAY;
3534 }
3535
3536 /* if constraint is not sufficiently violated -> do nothing */
3537 if( cons != nlhdlrexprdata->cons )
3538 {
3539 /* constraint is w.r.t auxvar */
3540 violation = auxvalue - SCIPgetSolVal(scip, NULL, SCIPgetExprAuxVarNonlinear(expr));
3541 violation = ABS( violation );
3542 }
3543 else
3544 /* quadratic is a constraint */
3545 violation = MAX( SCIPgetLhsNonlinear(nlhdlrexprdata->cons) - auxvalue, auxvalue -
3546 SCIPgetRhsNonlinear(nlhdlrexprdata->cons)); /*lint !e666*/
3547
3548 if( violation < nlhdlrdata->minviolation )
3549 {
3550 INTERLOG(printf("Violation %g is just too small\n", violation); )
3551 return SCIP_OKAY;
3552 }
3553
3554 /* we can't build an intersection cut when the expr is the root of some constraint and also a subexpression of
3555 * another constraint because we initialize data differently TODO: how differently? */
3556 /* TODO: I don't think this is needed */
3557 if( nlhdlrexprdata->cons != NULL && cons != nlhdlrexprdata->cons )
3558 {
3559 INTERLOG(printf("WARNING!! expr is root of one constraint and subexpr of another!\n"); )
3560 return SCIP_OKAY;
3561 }
3562
3563 /* if we are the root of a constraint and we are feasible w.r.t our auxiliary variables, that is, auxvalue is
3564 * actually feasible for the sides of the constraint, then do not separate
3565 */
3566 if( cons == nlhdlrexprdata->cons && ((overestimate && (SCIPgetLhsNonlinear(cons)) - auxvalue < SCIPfeastol(scip)) ||
3567 (! overestimate && (auxvalue - SCIPgetRhsNonlinear(cons) < SCIPfeastol(scip)))) )
3568 {
3569 INTERLOG(printf("We are actually feasible for the sides of the constraint\n"); )
3570 return SCIP_OKAY;
3571 }
3572
3573 #ifdef DEBUG_INTERSECTIONCUT
3574 SCIPinfoMessage(scip, NULL, "Build intersection cut for \n");
3575 if( cons == nlhdlrexprdata->cons )
3576 {
3577 SCIP_CALL( SCIPprintCons(scip, cons, NULL) );
3578 SCIPinfoMessage(scip, NULL, "\n");
3579 }
3580 else
3581 {
3582 SCIP_CALL( SCIPprintExpr(scip, expr, NULL) );
3583 SCIPinfoMessage(scip, NULL, " == %s\n", SCIPvarGetName(SCIPgetExprAuxVarNonlinear(expr)));
3584 }
3585 SCIPinfoMessage(scip, NULL, "We need to %sestimate\n", overestimate ? "over" : "under" );
3586 SCIPinfoMessage(scip, NULL, "LP sol: \n");
3587 SCIP_CALL( SCIPprintTransSol(scip, NULL, NULL, FALSE) );
3588 #endif
3589 *result = SCIP_DIDNOTFIND;
3590
3591 /* cut (in the nonbasic space) is of the form alpha^T x >= 1 */
3592 SCIP_CALL( SCIPcreateRowprep(scip, &rowprep, SCIP_SIDETYPE_LEFT, TRUE) );
3593 INTERLOG(printf("Generating inter cut\n"); )
3594
3595 SCIP_CALL( generateIntercut(scip, expr, nlhdlrdata, nlhdlrexprdata, cons, sol, rowprep, overestimate, &success) );
3596 INTERLOG(if( !success) printf("Generation failed\n"); )
3597
3598 /* we generated something, let us see if it survives the clean up */
3599 if( success )
3600 {
3601 assert(sol == NULL);
3602 nlhdlrdata->ncutsgenerated += 1;
3603 nlhdlrexprdata->ncutsadded += 1;
3604
3605 /* merge coefficients that belong to same variable */
3606 SCIPmergeRowprepTerms(scip, rowprep);
3607
3608 SCIP_CALL( SCIPcleanupRowprep(scip, rowprep, sol, nlhdlrdata->mincutviolation, &violation, &success) );
3609 INTERLOG(if( !success) printf("Clean up failed\n"); )
3610 }
3611
3612 /* if cut looks good (numerics ok and cutting off solution), then turn into row and add to sepastore */
3613 if( success )
3614 {
3615 SCIP_ROW* row;
3616 SCIP_Bool infeasible;
3617
3618 /* count number of bound cuts */
3619 if( nlhdlrdata->useboundsasrays )
3620 nlhdlrdata->nboundcuts += 1;
3621
3622 (void) SCIPsnprintf(SCIProwprepGetName(rowprep), SCIP_MAXSTRLEN, "%s_intersection_quadratic%p_lp%d",
3623 overestimate ? "over" : "under",
3624 (void*)expr,
(1) Event invalid_type: |
Argument "SCIPgetNLPs(scip)" to format specifier "%d" was expected to have type "int" but has type "long long". [details] |
3625 SCIPgetNLPs(scip));
3626
3627 SCIP_CALL( SCIPgetRowprepRowCons(scip, &row, rowprep, cons) );
3628
3629 /*printf("## New cut\n");
3630 printf(" -> found maxquad-free cut <%s>: act=%f, lhs=%f, norm=%f, eff=%f, min=%f, max=%f (range=%f)\n\n",
3631 SCIProwGetName(row), SCIPgetRowLPActivity(scip, row), SCIProwGetLhs(row), SCIProwGetNorm(row),
3632 SCIPgetCutEfficacy(scip, NULL, row),
3633 SCIPgetRowMinCoef(scip, row), SCIPgetRowMaxCoef(scip, row),
3634 SCIPgetRowMaxCoef(scip, row)/SCIPgetRowMinCoef(scip, row)); */
3635
3636 /* intersection cuts can be numerically nasty; we do some extra numerical checks here */
3637 /*printf("SCIP DEPTH %d got a cut with violation %g, efficacy %g and r/e %g\n", SCIPgetSubscipDepth(scip),
3638 * violation, SCIPgetCutEfficacy(scip, NULL, row), SCIPgetRowMaxCoef(scip, row) / SCIPgetRowMinCoef(scip, row) /
3639 * SCIPgetCutEfficacy(scip, NULL, row));
3640 */
3641 assert(SCIPgetCutEfficacy(scip, NULL, row) > 0.0);
3642 if( ! nlhdlrdata->ignorehighre || SCIPgetRowMaxCoef(scip, row) / SCIPgetRowMinCoef(scip, row) / SCIPgetCutEfficacy(scip, NULL, row) < 1e9 )
3643 {
3644 #ifdef SCIP_DEBUG
3645 SCIPdebugMsg(scip, "adding cut ");
3646 SCIP_CALL( SCIPprintRow(scip, row, NULL) );
3647 #endif
3648
3649 SCIP_CALL( SCIPaddRow(scip, row, FALSE, &infeasible) );
3650
3651 if( infeasible )
3652 {
3653 *result = SCIP_CUTOFF;
3654 }
3655 else
3656 {
3657 *result = SCIP_SEPARATED;
3658 nlhdlrdata->ncutsadded += 1;
3659 nlhdlrdata->densitysum += (SCIP_Real) SCIProwprepGetNVars(rowprep) / (SCIP_Real) SCIPgetNVars(scip);
3660 }
3661 }
3662 else
3663 {
3664 nlhdlrdata->nhighre++;
3665 }
3666 SCIP_CALL( SCIPreleaseRow(scip, &row) );
3667 }
3668
3669 SCIPfreeRowprep(scip, &rowprep);
3670
3671 return SCIP_OKAY;
3672 }
3673
3674 /** nonlinear handler forward propagation callback
3675 *
3676 * This method should solve the problem
3677 * <pre>
3678 * max/min quad expression over box constraints
3679 * </pre>
3680 * However, this problem is difficult so we are satisfied with a proxy.
3681 * Interval arithmetic suffices when no variable appears twice, however this is seldom the case, so we try
3682 * to take care of the dependency problem to some extent:
3683 * Let \f$P_l = \{i : \text{expr}_l \text{expr}_i \,\text{is a bilinear expr}\}\f$.
3684 * 1. partition the quadratic expression as sum of quadratic functions \f$\sum_l q_l\f$
3685 * where \f$q_l = a_l \text{expr}_l^2 + c_l \text{expr}_l + \sum_{i \in P_l} b_{il} \text{expr}_i \text{expr}_l\f$
3686 * 2. build interval quadratic functions, i.e., \f$a x^2 + b x\f$ where \f$b\f$ is an interval, i.e.,
3687 * \f$a_l \text{expr}_l^2 + [\sum_{i \in P_l} b_{il} \text{expr}_i + c_l] \text{expr}_l\f$
3688 * 3. compute \f$\min/\max \{ a x^2 + b x : x \in [x] \}\f$ for each interval quadratic, i.e.,
3689 * \f$\min/\max a_l \text{expr}_l^2 + \text{expr}_l [\sum_{i \in P_l} b_{il} \text{expr}_i + c_l] : \text{expr}_l \in [\text{expr}_l]\f$
3690 *
3691 * Notes:
3692 * 1. The \f$l\f$-th quadratic expr (expressions that appear quadratically) is associated with \f$q_l\f$.
3693 * 2. `nlhdlrdata->quadactivities[l]` is the activity of \f$q_l\f$ as computed in the description above.
3694 * 3. The \f$q_l\f$ of a quadratic term might be empty, in which case `nlhdlrdata->quadactivities[l]` is [0,0].\n
3695 * For example, consider \f$x^2 + xy\f$. There are two quadratic expressions, \f$x\f$ and \f$y\f$.
3696 * The \f$q\f$ associated to \f$x\f$ is \f$x^2 + xy\f$, while the \f$q\f$ associated to \f$y\f$ is empty.
3697 * Thus, `nlhdlrdata->quadactivities[1]` is [0,0] in this case.
3698 * The logic is to avoid considering the term \f$xy\f$ twice.
3699 *
3700 * @note The order matters! If \f$\text{expr}_i\, \text{expr}_l\f$ is a term in the quadratic, then \f$i\f$ is *not* in \f$P_l\f$
3701 */
3702 static
3703 SCIP_DECL_NLHDLRINTEVAL(nlhdlrIntevalQuadratic)
3704 { /*lint --e{715}*/
3705 SCIP_EXPR** linexprs;
3706 SCIP_Real* lincoefs;
3707 SCIP_Real constant;
3708 int nquadexprs;
3709 int nlinexprs;
3710
3711 assert(scip != NULL);
3712 assert(expr != NULL);
3713
3714 assert(nlhdlrexprdata != NULL);
3715 assert(nlhdlrexprdata->quadactivities != NULL);
3716 assert(nlhdlrexprdata->qexpr == expr);
3717
3718 SCIPdebugMsg(scip, "Interval evaluation of quadratic expr\n");
3719
3720 SCIPexprGetQuadraticData(expr, &constant, &nlinexprs, &linexprs, &lincoefs, &nquadexprs, NULL, NULL, NULL);
3721
3722 /*
3723 * compute activity of linear part, if some linear term has changed
3724 */
3725 {
3726 int i;
3727
3728 SCIPdebugMsg(scip, "Computing activity of linear part\n");
3729
3730 SCIPintervalSet(&nlhdlrexprdata->linactivity, constant);
3731 for( i = 0; i < nlinexprs; ++i )
3732 {
3733 SCIP_INTERVAL linterminterval;
3734
3735 linterminterval = SCIPexprGetActivity(linexprs[i]);
3736 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, linterminterval) )
3737 {
3738 SCIPdebugMsg(scip, "Activity of linear part is empty due to child %d\n", i);
3739 SCIPintervalSetEmpty(interval);
3740 return SCIP_OKAY;
3741 }
3742 SCIPintervalMulScalar(SCIP_INTERVAL_INFINITY, &linterminterval, linterminterval, lincoefs[i]);
3743 SCIPintervalAdd(SCIP_INTERVAL_INFINITY, &nlhdlrexprdata->linactivity, nlhdlrexprdata->linactivity, linterminterval);
3744 }
3745
3746 SCIPdebugMsg(scip, "Activity of linear part is [%g, %g]\n", nlhdlrexprdata->linactivity.inf,
3747 nlhdlrexprdata->linactivity.sup);
3748 }
3749
3750 /*
3751 * compute activity of quadratic part
3752 */
3753 {
3754 int i;
3755
3756 SCIPdebugMsg(scip, "Computing activity of quadratic part\n");
3757
3758 nlhdlrexprdata->nneginfinityquadact = 0;
3759 nlhdlrexprdata->nposinfinityquadact = 0;
3760 nlhdlrexprdata->minquadfiniteact = 0.0;
3761 nlhdlrexprdata->maxquadfiniteact = 0.0;
3762 SCIPintervalSet(&nlhdlrexprdata->quadactivity, 0.0);
3763
3764 for( i = 0; i < nquadexprs; ++i )
3765 {
3766 SCIP_Real quadlb;
3767 SCIP_Real quadub;
3768 SCIP_EXPR* qexpr;
3769 SCIP_Real lincoef;
3770 SCIP_Real sqrcoef;
3771 int nadjbilin;
3772 int* adjbilin;
3773 SCIP_EXPR* sqrexpr;
3774
3775 SCIPexprGetQuadraticQuadTerm(expr, i, &qexpr, &lincoef, &sqrcoef, &nadjbilin, &adjbilin, &sqrexpr);
3776
3777 if( !isPropagableTerm(expr, i) )
3778 {
3779 /* term is not propagable, i.e., the exprs involved in term only appear once; thus use the activity of the
3780 * quadratic term directly and not the activity of the exprs involed in the term. See also documentation of
3781 * DETECT
3782 */
3783 SCIP_INTERVAL tmp;
3784
3785 assert(lincoef == 0.0);
3786
3787 if( sqrcoef != 0.0 )
3788 {
3789 assert(sqrexpr != NULL);
3790 assert(nadjbilin == 0);
3791
3792 tmp = SCIPexprGetActivity(sqrexpr);
3793 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, tmp) )
3794 {
3795 SCIPintervalSetEmpty(interval);
3796 return SCIP_OKAY;
3797 }
3798
3799 SCIPintervalMulScalar(SCIP_INTERVAL_INFINITY, &tmp, tmp, sqrcoef);
3800 quadlb = tmp.inf;
3801 quadub = tmp.sup;
3802
3803 #ifdef DEBUG_PROP
3804 SCIPinfoMessage(scip, NULL, "Computing activity for quadratic term %g <expr>, where <expr> is: ", sqrcoef);
3805 SCIP_CALL( SCIPprintExpr(scip, sqrexpr, NULL) );
3806 #endif
3807 }
3808 else
3809 {
3810 SCIP_EXPR* expr1;
3811 SCIP_EXPR* prodexpr;
3812 SCIP_Real prodcoef;
3813
3814 assert(nadjbilin == 1);
3815 SCIPexprGetQuadraticBilinTerm(expr, adjbilin[0], &expr1, NULL, &prodcoef, NULL, &prodexpr);
3816
3817 if( expr1 == qexpr )
3818 {
3819 /* the quadratic expression expr1 appears only as expr1 * expr2, so its 'q' is expr1 * expr2 */
3820 tmp = SCIPexprGetActivity(prodexpr);
3821 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, tmp) )
3822 {
3823 SCIPintervalSetEmpty(interval);
3824 return SCIP_OKAY;
3825 }
3826
3827 SCIPintervalMulScalar(SCIP_INTERVAL_INFINITY, &tmp, tmp, prodcoef);
3828 quadlb = tmp.inf;
3829 quadub = tmp.sup;
3830
3831 #ifdef DEBUG_PROP
3832 SCIPinfoMessage(scip, NULL, "Computing activity for quadratic term %g <expr>, where <expr> is: ", prodcoef);
3833 SCIP_CALL( SCIPprintExpr(scip, prodexpr, NULL) );
3834 #endif
3835 }
3836 else
3837 {
3838 /* the quadratic expression expr1 appears as expr2 * expr1, thus its 'q' is empty, see also the Notes
3839 * in the documentation of the function
3840 */
3841 SCIPintervalSet(&nlhdlrexprdata->quadactivities[i], 0.0);
3842 continue;
3843 }
3844 }
3845 }
3846 else
3847 {
3848 int j;
3849 SCIP_INTERVAL b;
3850
3851 SCIPexprGetQuadraticQuadTerm(expr, i, &qexpr, &lincoef, &sqrcoef, &nadjbilin, &adjbilin, NULL);
3852
3853 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(qexpr)) )
3854 {
3855 SCIPintervalSetEmpty(interval);
3856 return SCIP_OKAY;
3857 }
3858
3859 /* b = [c_l] */
3860 SCIPintervalSet(&b, lincoef);
3861 #ifdef DEBUG_PROP
3862 SCIPinfoMessage(scip, NULL, "b := %g\n", lincoef);
3863 #endif
3864 for( j = 0; j < nadjbilin; ++j )
3865 {
3866 SCIP_INTERVAL bterm;
3867 SCIP_EXPR* expr1;
3868 SCIP_EXPR* expr2;
3869 SCIP_Real bilincoef;
3870
3871 SCIPexprGetQuadraticBilinTerm(expr, adjbilin[j], &expr1, &expr2, &bilincoef, NULL, NULL);
3872
3873 if( expr1 != qexpr )
3874 continue;
3875
3876 bterm = SCIPexprGetActivity(expr2);
3877 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, bterm) )
3878 {
3879 SCIPintervalSetEmpty(interval);
3880 return SCIP_OKAY;
3881 }
3882
3883 /* b += [b_jl * expr_j] for j \in P_l */
3884 SCIPintervalMulScalar(SCIP_INTERVAL_INFINITY, &bterm, bterm, bilincoef);
3885 SCIPintervalAdd(SCIP_INTERVAL_INFINITY, &b, b, bterm);
3886
3887 #ifdef DEBUG_PROP
3888 SCIPinfoMessage(scip, NULL, "b += %g * [expr2], where <expr2> is: ", bilincoef);
3889 SCIP_CALL( SCIPprintExpr(scip, expr2, NULL) );
3890 SCIPinfoMessage(scip, NULL, " [%g,%g]\n", SCIPexprGetActivity(expr2).inf, SCIPexprGetActivity(expr2).sup);
3891 #endif
3892 }
3893
3894 /* TODO: under which assumptions do we know that we just need to compute min or max? its probably the locks that give some information here */
3895 quadub = SCIPintervalQuadUpperBound(SCIP_INTERVAL_INFINITY, sqrcoef, b,
3896 SCIPexprGetActivity(qexpr));
3897
3898 /* TODO: implement SCIPintervalQuadLowerBound */
3899 {
3900 SCIP_INTERVAL minusb;
3901 SCIPintervalSetBounds(&minusb, -SCIPintervalGetSup(b), -SCIPintervalGetInf(b));
3902
3903 quadlb = -SCIPintervalQuadUpperBound(SCIP_INTERVAL_INFINITY, -sqrcoef, minusb,
3904 SCIPexprGetActivity(qexpr));
3905 }
3906
3907 #ifdef DEBUG_PROP
3908 SCIPinfoMessage(scip, NULL, "Computing activity for quadratic term %g <expr>^2 + [%g,%g] <expr>, where <expr> is: ", sqrcoef, b.inf, b.sup);
3909 SCIP_CALL( SCIPprintExpr(scip, qexpr, NULL) );
3910 #endif
3911 }
3912 #ifdef DEBUG_PROP
3913 SCIPinfoMessage(scip, NULL, " -> [%g, %g]\n", quadlb, quadub);
3914 #endif
3915
3916 SCIPintervalSetBounds(&nlhdlrexprdata->quadactivities[i], quadlb, quadub);
3917 SCIPintervalAdd(SCIP_INTERVAL_INFINITY, &nlhdlrexprdata->quadactivity, nlhdlrexprdata->quadactivity, nlhdlrexprdata->quadactivities[i]);
3918
3919 /* get number of +/-infinity contributions and compute finite activity */
3920 if( quadlb <= -SCIP_INTERVAL_INFINITY )
3921 nlhdlrexprdata->nneginfinityquadact++;
3922 else
3923 {
3924 SCIP_ROUNDMODE roundmode;
3925
3926 roundmode = SCIPintervalGetRoundingMode();
3927 SCIPintervalSetRoundingModeDownwards();
3928
3929 nlhdlrexprdata->minquadfiniteact += quadlb;
3930
3931 SCIPintervalSetRoundingMode(roundmode);
3932 }
3933 if( quadub >= SCIP_INTERVAL_INFINITY )
3934 nlhdlrexprdata->nposinfinityquadact++;
3935 else
3936 {
3937 SCIP_ROUNDMODE roundmode;
3938
3939 roundmode = SCIPintervalGetRoundingMode();
3940 SCIPintervalSetRoundingModeUpwards();
3941
3942 nlhdlrexprdata->maxquadfiniteact += quadub;
3943
3944 SCIPintervalSetRoundingMode(roundmode);
3945 }
3946 }
3947
3948 SCIPdebugMsg(scip, "Activity of quadratic part is [%g, %g]\n", nlhdlrexprdata->quadactivity.inf, nlhdlrexprdata->quadactivity.sup);
3949 }
3950
3951 /* interval evaluation is linear activity + quadactivity */
3952 SCIPintervalAdd(SCIP_INTERVAL_INFINITY, interval, nlhdlrexprdata->linactivity, nlhdlrexprdata->quadactivity);
3953
3954 nlhdlrexprdata->activitiestag = SCIPgetCurBoundsTagNonlinear(SCIPfindConshdlr(scip, "nonlinear"));
3955
3956 return SCIP_OKAY;
3957 }
3958
3959 /** nonlinear handler reverse propagation callback
3960 *
3961 * @note the implemented technique is a proxy for solving the problem min/max{ x_i : quad expr in [quad expr] }
3962 * and as such can be improved.
3963 */
3964 static
3965 SCIP_DECL_NLHDLRREVERSEPROP(nlhdlrReversepropQuadratic)
3966 { /*lint --e{715}*/
3967 SCIP_EXPR** linexprs;
3968 SCIP_EXPR** bilinexprs; /* TODO: should this be stored in the nlhdlr expr data? */
3969 SCIP_Real* bilincoefs;
3970 SCIP_Real* lincoefs;
3971 SCIP_Real constant;
3972 int nquadexprs;
3973 int nlinexprs;
3974
3975 SCIP_INTERVAL rhs;
3976 SCIP_INTERVAL quadactivity;
3977 int i;
3978
3979 SCIPdebugMsg(scip, "Reverse propagation of quadratic expr given bounds = [%g,%g]\n", bounds.inf, bounds.sup);
3980
3981 assert(scip != NULL);
3982 assert(expr != NULL);
3983 assert(infeasible != NULL);
3984 assert(nreductions != NULL);
3985 assert(nlhdlrexprdata != NULL);
3986 assert(nlhdlrexprdata->quadactivities != NULL);
3987 assert(nlhdlrexprdata->qexpr == expr);
3988
3989 *nreductions = 0;
3990
3991 /* not possible to conclude finite bounds if the interval of the expression is [-inf,inf] */
3992 if( SCIPintervalIsEntire(SCIP_INTERVAL_INFINITY, bounds) )
3993 {
3994 SCIPdebugMsg(scip, "expr's range is R -> cannot reverse propagate\n");
3995 return SCIP_OKAY;
3996 }
3997
3998 /* ensure that partial activities as stored in nlhdlrexprdata are uptodate
3999 * if the activity stored in expr is more recent than the partial activities stored in this nlhdlrexprdata,
4000 * then we should reevaluate the partial activities
4001 */
4002 if( SCIPexprGetActivityTag(expr) > nlhdlrexprdata->activitiestag )
4003 {
4004 SCIP_CALL( nlhdlrIntevalQuadratic(scip, nlhdlr, expr, nlhdlrexprdata, &quadactivity, NULL, NULL) );
4005 }
4006
4007 SCIPexprGetQuadraticData(expr, &constant, &nlinexprs, &linexprs, &lincoefs, &nquadexprs, NULL, NULL, NULL);
4008
4009 /* propagate linear part in rhs = expr's interval - quadratic activity; first, reconstruct the quadratic activity */
4010 SCIPintervalSetBounds(&quadactivity,
4011 nlhdlrexprdata->nneginfinityquadact > 0 ? -SCIP_INTERVAL_INFINITY : nlhdlrexprdata->minquadfiniteact,
4012 nlhdlrexprdata->nposinfinityquadact > 0 ? SCIP_INTERVAL_INFINITY : nlhdlrexprdata->maxquadfiniteact);
4013
4014 SCIPintervalSub(SCIP_INTERVAL_INFINITY, &rhs, bounds, quadactivity);
4015
4016 SCIP_CALL( reversePropagateLinearExpr(scip, linexprs, nlinexprs, lincoefs, constant, rhs, infeasible, nreductions) );
4017
4018 /* stop if we find infeasibility */
4019 if( *infeasible )
4020 return SCIP_OKAY;
4021
4022 /* propagate quadratic part in expr's interval - linear activity, where linear activity was computed in INTEVAL.
4023 * The idea is basically to write interval quadratics for each expr and then solve for expr.
4024 *
4025 * One way of achieving this is:
4026 * - for each expression expr_i, write the quadratic expression as a_i expr^2_i + expr_i ( \sum_{j \in J_i} b_ij
4027 * expr_j + c_i ) + quadratic expression in expr_k for k \neq i
4028 * - compute the interval b = [\sum_{j \in J_i} b_ij expr_j + c_i], where J_i are all the indices j such that the
4029 * bilinear expression expr_i expr_j appears
4030 * - use some technique (like the one in nlhdlrIntevalQuadratic), to evaluate the activity of rest_i = [quadratic
4031 * expression in expr_k for k \neq i].
4032 * - solve a_i expr_i^2 + b expr_i \in rhs_i := [expr activity] - rest_i
4033 *
4034 * However, this might be expensive, especially computing rest_i. Hence, we implement a simpler version.
4035 * - we use the same partition as in nlhdlrIntevalQuadratic for the bilinear terms. This way, b = [\sum_{j \in P_i}
4036 * b_ij expr_j + c_i], where P_i is the set of indices j such that expr_i * expr_j appears in that order
4037 * - we evaluate the activity of rest_i as sum_{k \neq i} [\min q_k, \max q_k] where q_k = a_k expr_k^2 + [\sum_{j
4038 * \in P_k} b_jk expr_j + c_k] expr_k. The intervals [\min q_k, \max q_k] were already computed in
4039 * nlhdlrIntevalQuadratic, so we just reuse them.
4040 *
4041 * A downside of the above is that we might not deduce any bounds for variables that appear less often. For example,
4042 * consider x^2 + x * y + x * z + y * z + z. This quadratic gets partitioned as (x^2 + x*y + x*z) + (z*y + z). The
4043 * first parenthesis is interpreted as a function of x, while the second one as a function of z.
4044 * To also get bounds on y, after reverse propagating x in x^2 + x*y + x*z \in rhs, we rewrite this as y + z \in rhs/x -
4045 * x and propagate the y + z).
4046 * In general, after reverse propagating expr_i, we consider
4047 * \sum_{j \in J_i} b_ij expr_j in ([expr activity] - quadratic expression in expr_k for k \neq i - c_i) / expr_i - a_i expr_i,
4048 * compute an interval for the right hand side (see computeRangeForBilinearProp) and use that to propagate the
4049 * linear sum on the left hand side.
4050 *
4051 * Note: this last step generalizes a technique that appeared in the classic cons_quadratic.
4052 * The idea of that technique was to borrow a bilinear term expr_k expr_l when propagating expr_l and the quadratic
4053 * function for expr_k was simple enough.
4054 * Since in P_l we only consider the indices of expressions that appear multiplying expr_l as _second_ factor, we
4055 * would lose the bilinear terms expr_k * expr_l, which contributes to the dependency problem.
4056 * The problem is that the contribution of b_kl * expr_k * expr_l to rest_i is not just [b_kl * expr_k * expr_l], but
4057 * rather quadactivities[k] (= max/min of a_k expr_k^2 + expr_k * [c_k + sum_i \in P_k b_ki expr_i]).
4058 * Thus, we _cannot_ just substract [b_kl * expr_k * expr_l] from rest_i.
4059 * But, if expr_k only appears as expr_k * expr_l, then quadactivities[k] = [b_kl * expr_k * expr_l]. So this
4060 * case was handled in old cons_quadratic.
4061 *
4062 *
4063 * TODO: handle simple cases
4064 * TODO: identify early when there is nothing to be gain
4065 */
4066 SCIPintervalSub(SCIP_INTERVAL_INFINITY, &rhs, bounds, nlhdlrexprdata->linactivity);
4067 SCIP_CALL( SCIPallocBufferArray(scip, &bilinexprs, nquadexprs) );
4068 SCIP_CALL( SCIPallocBufferArray(scip, &bilincoefs, nquadexprs) );
4069
4070 for( i = 0; i < nquadexprs; ++i )
4071 {
4072 SCIP_INTERVAL rhs_i;
4073 SCIP_INTERVAL rest_i;
4074 SCIP_EXPR* qexpr;
4075 SCIP_Real lincoef;
4076 SCIP_Real sqrcoef;
4077 int nadjbilin;
4078 int* adjbilin;
4079 SCIP_EXPR* sqrexpr;
4080
4081 SCIPexprGetQuadraticQuadTerm(expr, i, &qexpr, &lincoef, &sqrcoef, &nadjbilin, &adjbilin, &sqrexpr);
4082
4083 /* rhs_i = rhs - rest_i.
4084 * to compute rest_i = [\sum_{k \neq i} q_k] we just have to substract
4085 * the activity of q_i from quadactivity; however, care must be taken about infinities;
4086 * if [q_i].sup = +infinity and there is = 1 contributing +infinity -> rest_i.sup = maxquadfiniteact
4087 * if [q_i].sup = +infinity and there is > 1 contributing +infinity -> rest_i.sup = +infinity
4088 * if [q_i].sup = finite and there is > 0 contributing +infinity -> rest_i.sup = +infinity
4089 * if [q_i].sup = finite and there is = 0 contributing +infinity -> rest_i.sup = maxquadfiniteact - [q_i].sup
4090 *
4091 * the same holds when replacing sup with inf, + with - and max(quadfiniteact) with min(...)
4092 */
4093 /* compute rest_i.sup */
4094 if( SCIPintervalGetSup(nlhdlrexprdata->quadactivities[i]) < SCIP_INTERVAL_INFINITY &&
4095 nlhdlrexprdata->nposinfinityquadact == 0 )
4096 {
4097 SCIP_ROUNDMODE roundmode;
4098
4099 roundmode = SCIPintervalGetRoundingMode();
4100 SCIPintervalSetRoundingModeUpwards();
4101 rest_i.sup = nlhdlrexprdata->maxquadfiniteact - SCIPintervalGetSup(nlhdlrexprdata->quadactivities[i]);
4102
4103 SCIPintervalSetRoundingMode(roundmode);
4104 }
4105 else if( SCIPintervalGetSup(nlhdlrexprdata->quadactivities[i]) >= SCIP_INTERVAL_INFINITY &&
4106 nlhdlrexprdata->nposinfinityquadact == 1 )
4107 rest_i.sup = nlhdlrexprdata->maxquadfiniteact;
4108 else
4109 rest_i.sup = SCIP_INTERVAL_INFINITY;
4110
4111 /* compute rest_i.inf */
4112 if( SCIPintervalGetInf(nlhdlrexprdata->quadactivities[i]) > -SCIP_INTERVAL_INFINITY &&
4113 nlhdlrexprdata->nneginfinityquadact == 0 )
4114 {
4115 SCIP_ROUNDMODE roundmode;
4116
4117 roundmode = SCIPintervalGetRoundingMode();
4118 SCIPintervalSetRoundingModeDownwards();
4119 rest_i.inf = nlhdlrexprdata->minquadfiniteact - SCIPintervalGetInf(nlhdlrexprdata->quadactivities[i]);
4120
4121 SCIPintervalSetRoundingMode(roundmode);
4122 }
4123 else if( SCIPintervalGetInf(nlhdlrexprdata->quadactivities[i]) <= -SCIP_INTERVAL_INFINITY &&
4124 nlhdlrexprdata->nneginfinityquadact == 1 )
4125 rest_i.inf = nlhdlrexprdata->minquadfiniteact;
4126 else
4127 rest_i.inf = -SCIP_INTERVAL_INFINITY;
4128
4129 #ifdef SCIP_DISABLED_CODE /* I (SV) added the following in cons_quadratic to fix/workaround some bug. Maybe we'll need this here, too? */
4130 /* FIXME in theory, rest_i should not be empty here
4131 * what we tried to do here is to remove the contribution of the i'th bilinear term (=bilinterm) to [minquadactivity,maxquadactivity] from rhs
4132 * however, quadactivity is computed differently (as x*(a1*y1+...+an*yn)) than q_i (a*ak*yk) and since interval arithmetics do overestimation,
4133 * it can happen that q_i is actually slightly larger than quadactivity, which results in rest_i being (slightly) empty
4134 * a proper fix could be to compute the quadactivity also as x*a1*y1+...+x*an*yn if sqrcoef=0, but due to taking
4135 * also infinite bounds into account, this complicates the code even further
4136 * instead, I'll just work around this by turning an empty rest_i into a small non-empty one
4137 */
4138 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, rest_i) )
4139 {
4140 assert(SCIPisSumRelEQ(scip, rest_i.inf, rest_i.sup));
4141 SCIPswapReals(&rest_i.inf, &rest_i.sup);
4142 }
4143 #endif
4144 assert(!SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, rest_i));
4145
4146 /* compute rhs_i */
4147 SCIPintervalSub(SCIP_INTERVAL_INFINITY, &rhs_i, rhs, rest_i);
4148
4149 if( SCIPintervalIsEntire(SCIP_INTERVAL_INFINITY, rhs_i) )
4150 continue;
4151
4152 /* try to propagate */
4153 if( !isPropagableTerm(expr, i) )
4154 {
4155 assert(lincoef == 0.0);
4156
4157 if( sqrcoef != 0.0 )
4158 {
4159 assert(sqrexpr != NULL);
4160 assert(nadjbilin == 0);
4161
4162 /* solve sqrcoef sqrexpr in rhs_i */
4163 SCIP_CALL( propagateBoundsLinExpr(scip, sqrexpr, sqrcoef, rhs_i, infeasible, nreductions) );
4164 }
4165 else
4166 {
4167 /* qexpr only appears in a term of the form qexpr * other_expr (or other_expr * qexpr); we only care about
4168 * getting bounds for the product, thus we will compute these bounds when qexpr appears as qexpr *
4169 * other_expr; note that if it appears as other_expr * qexpr, then when we process other_expr bounds for the
4170 * product will be computed
4171 * TODO: we can actually avoid computing rhs_i in the case that qexpr is not propagable and it appears as
4172 * other_expr * qexpr
4173 */
4174 SCIP_EXPR* expr1;
4175 SCIP_EXPR* prodexpr;
4176 SCIP_Real prodcoef;
4177
4178 assert(nadjbilin == 1);
4179 SCIPexprGetQuadraticBilinTerm(expr, adjbilin[0], &expr1, NULL, &prodcoef, NULL, &prodexpr);
4180
4181 if( expr1 == qexpr )
4182 {
4183 /* solve prodcoef prodexpr in rhs_i */
4184 SCIP_CALL( propagateBoundsLinExpr(scip, prodexpr, prodcoef, rhs_i, infeasible, nreductions) );
4185 }
4186 }
4187 }
4188 else
4189 {
4190 SCIP_INTERVAL b;
4191 SCIP_EXPR* expr1 = NULL;
4192 SCIP_EXPR* expr2 = NULL;
4193 SCIP_Real bilincoef = 0.0;
4194 int nbilin = 0;
4195 int pos2 = 0;
4196 int j;
4197
4198 /* set b to [c_l] */
4199 SCIPintervalSet(&b, lincoef);
4200
4201 /* add [\sum_{j \in P_l} b_lj expr_j + c_l] into b */
4202 for( j = 0; j < nadjbilin; ++j )
4203 {
4204 SCIP_INTERVAL bterm;
4205 SCIP_INTERVAL expr2bounds;
4206
4207 SCIPexprGetQuadraticBilinTerm(expr, adjbilin[j], &expr1, &expr2, &bilincoef, &pos2, NULL);
4208
4209 if( expr1 != qexpr )
4210 continue;
4211
4212 expr2bounds = SCIPgetExprBoundsNonlinear(scip, expr2);
4213 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, expr2bounds) )
4214 {
4215 *infeasible = TRUE;
4216 break;
4217 }
4218
4219 /* b += [b_lj * expr_j] for j \in P_l */
4220 SCIPintervalMulScalar(SCIP_INTERVAL_INFINITY, &bterm, expr2bounds, bilincoef);
4221 SCIPintervalAdd(SCIP_INTERVAL_INFINITY, &b, b, bterm);
4222
4223 /* remember b_lj and expr_j to propagate them too */
4224 bilinexprs[nbilin] = expr2;
4225 bilincoefs[nbilin] = bilincoef;
4226 nbilin++;
4227 }
4228
4229 if( !*infeasible )
4230 {
4231 /* solve a_i expr_i^2 + b expr_i in rhs_i */
4232 SCIP_CALL( propagateBoundsQuadExpr(scip, qexpr, sqrcoef, b, rhs_i, infeasible, nreductions) );
4233 }
4234
4235 if( nbilin > 0 && !*infeasible )
4236 {
4237 /* if 0 is not in [expr_i], then propagate bilincoefs^T bilinexpr in rhs_i/expr_i - a_i expr_i - c_i */
4238 SCIP_INTERVAL bilinrhs;
4239 SCIP_INTERVAL qexprbounds;
4240
4241 qexprbounds = SCIPgetExprBoundsNonlinear(scip, qexpr);
4242 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, qexprbounds) )
4243 {
4244 *infeasible = TRUE;
4245 }
4246 else
4247 {
4248 /* compute bilinrhs := [rhs_i/expr_i - a_i expr_i] */
4249 computeRangeForBilinearProp(qexprbounds, sqrcoef, rhs_i, &bilinrhs);
4250
4251 if( !SCIPintervalIsEntire(SCIP_INTERVAL_INFINITY, bilinrhs) )
4252 {
4253 int nreds;
4254
4255 /* propagate \sum_{j \in P_i} b_ij expr_j + c_i in bilinrhs */
4256 SCIP_CALL( reversePropagateLinearExpr(scip, bilinexprs, nbilin, bilincoefs, lincoef, bilinrhs,
4257 infeasible, &nreds) );
4258
4259 /* TODO FIXME: we are overestimating the number of reductions: an expr might be tightened many times! */
4260 *nreductions += nreds;
4261 }
4262 }
4263 }
4264 }
4265
4266 /* stop if we find infeasibility */
4267 if( *infeasible )
4268 break;
4269 }
4270
4271 SCIPfreeBufferArray(scip, &bilincoefs);
4272 SCIPfreeBufferArray(scip, &bilinexprs);
4273
4274 return SCIP_OKAY;
4275 }
4276
4277 /** callback to free data of handler */
4278 static
4279 SCIP_DECL_NLHDLRFREEHDLRDATA(nlhdlrFreehdlrdataQuadratic)
4280 { /*lint --e{715}*/
4281 assert(nlhdlrdata != NULL);
4282
4283 SCIPfreeBlockMemory(scip, nlhdlrdata);
4284
4285 return SCIP_OKAY;
4286 }
4287
4288 /** nonlinear handler copy callback */
4289 static
4290 SCIP_DECL_NLHDLRCOPYHDLR(nlhdlrCopyhdlrQuadratic)
4291 { /*lint --e{715}*/
4292 assert(targetscip != NULL);
4293 assert(sourcenlhdlr != NULL);
4294 assert(strcmp(SCIPnlhdlrGetName(sourcenlhdlr), NLHDLR_NAME) == 0);
4295
4296 SCIP_CALL( SCIPincludeNlhdlrQuadratic(targetscip) );
4297
4298 return SCIP_OKAY;
4299 }
4300
4301 /** includes quadratic nonlinear handler in nonlinear constraint handler */
4302 SCIP_RETCODE SCIPincludeNlhdlrQuadratic(
4303 SCIP* scip /**< SCIP data structure */
4304 )
4305 {
4306 SCIP_NLHDLRDATA* nlhdlrdata;
4307 SCIP_NLHDLR* nlhdlr;
4308
4309 assert(scip != NULL);
4310
4311 /* create nonlinear handler specific data */
4312 SCIP_CALL( SCIPallocBlockMemory(scip, &nlhdlrdata) );
4313 BMSclearMemory(nlhdlrdata);
4314
4315 SCIP_CALL( SCIPincludeNlhdlrNonlinear(scip, &nlhdlr, NLHDLR_NAME, NLHDLR_DESC, NLHDLR_DETECTPRIORITY,
4316 NLHDLR_ENFOPRIORITY, nlhdlrDetectQuadratic, nlhdlrEvalauxQuadratic, nlhdlrdata) );
4317
4318 SCIPnlhdlrSetCopyHdlr(nlhdlr, nlhdlrCopyhdlrQuadratic);
4319 SCIPnlhdlrSetFreeHdlrData(nlhdlr, nlhdlrFreehdlrdataQuadratic);
4320 SCIPnlhdlrSetFreeExprData(nlhdlr, nlhdlrFreeexprdataQuadratic);
4321 SCIPnlhdlrSetSepa(nlhdlr, NULL, nlhdlrEnfoQuadratic, NULL, NULL);
4322 SCIPnlhdlrSetProp(nlhdlr, nlhdlrIntevalQuadratic, nlhdlrReversepropQuadratic);
4323
4324 /* parameters */
4325 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" NLHDLR_NAME "/useintersectioncuts",
4326 "whether to use intersection cuts for quadratic constraints to separate",
4327 &nlhdlrdata->useintersectioncuts, FALSE, DEFAULT_USEINTERCUTS, NULL, NULL) );
4328
4329 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" NLHDLR_NAME "/usestrengthening",
4330 "whether the strengthening should be used",
4331 &nlhdlrdata->usestrengthening, FALSE, DEFAULT_USESTRENGTH, NULL, NULL) );
4332
4333 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" NLHDLR_NAME "/useboundsasrays",
4334 "use bounds of variables in quadratic as rays for intersection cuts",
4335 &nlhdlrdata->useboundsasrays, FALSE, DEFAULT_USEBOUNDS, NULL, NULL) );
4336
4337 SCIP_CALL( SCIPaddIntParam(scip, "nlhdlr/" NLHDLR_NAME "/ncutslimit",
4338 "limit for number of cuts generated consecutively",
4339 &nlhdlrdata->ncutslimit, FALSE, DEFAULT_NCUTS, 0, INT_MAX, NULL, NULL) );
4340
4341 SCIP_CALL( SCIPaddIntParam(scip, "nlhdlr/" NLHDLR_NAME "/ncutslimitroot",
4342 "limit for number of cuts generated at root node",
4343 &nlhdlrdata->ncutslimitroot, FALSE, DEFAULT_NCUTSROOT, 0, INT_MAX, NULL, NULL) );
4344
4345 SCIP_CALL( SCIPaddIntParam(scip, "nlhdlr/" NLHDLR_NAME "/maxrank",
4346 "maximal rank a slackvar can have",
4347 &nlhdlrdata->maxrank, FALSE, INT_MAX, 0, INT_MAX, NULL, NULL) );
4348
4349 SCIP_CALL( SCIPaddRealParam(scip, "nlhdlr/" NLHDLR_NAME "/mincutviolation",
4350 "minimal cut violation the generated cuts must fulfill to be added to the LP",
4351 &nlhdlrdata->mincutviolation, FALSE, 1e-4, 0.0, SCIPinfinity(scip), NULL, NULL) );
4352
4353 SCIP_CALL( SCIPaddRealParam(scip, "nlhdlr/" NLHDLR_NAME "/minviolation",
4354 "minimal violation the constraint must fulfill such that a cut is generated",
4355 &nlhdlrdata->mincutviolation, FALSE, INTERCUTS_MINVIOL, 0.0, SCIPinfinity(scip), NULL, NULL) );
4356
4357 SCIP_CALL( SCIPaddIntParam(scip, "nlhdlr/" NLHDLR_NAME "/atwhichnodes",
4358 "determines at which nodes cut is used (if it's -1, it's used only at the root node, if it's n >= 0, it's used at every multiple of n",
4359 &nlhdlrdata->atwhichnodes, FALSE, 1, -1, INT_MAX, NULL, NULL) );
4360
4361 SCIP_CALL( SCIPaddIntParam(scip, "nlhdlr/" NLHDLR_NAME "/nstrengthlimit",
4362 "limit for number of rays we do the strengthening for",
4363 &nlhdlrdata->nstrengthlimit, FALSE, INT_MAX, 0, INT_MAX, NULL, NULL) );
4364
4365 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" NLHDLR_NAME "/ignorebadrayrestriction",
4366 "should cut be generated even with bad numerics when restricting to ray?",
4367 &nlhdlrdata->ignorebadrayrestriction, FALSE, TRUE, NULL, NULL) );
4368
4369 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" NLHDLR_NAME "/ignorenhighre",
4370 "should cut be added even when range / efficacy is large?",
4371 &nlhdlrdata->ignorehighre, FALSE, TRUE, NULL, NULL) );
4372
4373 /* statistic table */
4374 assert(SCIPfindTable(scip, TABLE_NAME_QUADRATIC) == NULL);
4375 SCIP_CALL( SCIPincludeTable(scip, TABLE_NAME_QUADRATIC, TABLE_DESC_QUADRATIC, FALSE,
4376 NULL, NULL, NULL, NULL, NULL, NULL, tableOutputQuadratic,
4377 NULL, TABLE_POSITION_QUADRATIC, TABLE_EARLIEST_STAGE_QUADRATIC) );
4378 return SCIP_OKAY;
4379 }
4380