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_bilinear.c
17 * @ingroup DEFPLUGINS_NLHDLR
18 * @brief bilinear nonlinear handler
19 * @author Benjamin Mueller
20 */
21
22 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
23
24 #include <string.h>
25
26 #include "scip/nlhdlr_bilinear.h"
27 #include "scip/cons_nonlinear.h"
28 #include "scip/expr_product.h"
29 #include "scip/expr_var.h"
30
31 /* fundamental nonlinear handler properties */
32 #define NLHDLR_NAME "bilinear"
33 #define NLHDLR_DESC "bilinear handler for expressions"
34 #define NLHDLR_DETECTPRIORITY -10 /**< it is important that the nlhdlr runs after the default nldhlr */
35 #define NLHDLR_ENFOPRIORITY -10
36
37 #define MIN_INTERIORITY 0.01 /**< minimum interiority for a reference point for applying separation */
38 #define MIN_ABSBOUNDSIZE 0.1 /**< minimum size of variable bounds for applying separation */
39
40 /* properties of the bilinear nlhdlr statistics table */
41 #define TABLE_NAME_BILINEAR "nlhdlr_bilinear"
42 #define TABLE_DESC_BILINEAR "bilinear nlhdlr statistics table"
43 #define TABLE_POSITION_BILINEAR 14800 /**< the position of the statistics table */
44 #define TABLE_EARLIEST_STAGE_BILINEAR SCIP_STAGE_INITSOLVE /**< output of the statistics table is only printed from this stage onwards */
45
46
47 /*
48 * Data structures
49 */
50
51 /** nonlinear handler expression data */
52 struct SCIP_NlhdlrExprData
53 {
54 SCIP_Real underineqs[6]; /**< inequalities for underestimation */
55 int nunderineqs; /**< total number of inequalities for underestimation */
56 SCIP_Real overineqs[6]; /**< inequalities for overestimation */
57 int noverineqs; /**< total number of inequalities for overestimation */
58 SCIP_Longint lastnodeid; /**< id of the last node that has been used for separation */
59 int nseparoundslastnode; /**< number of separation calls of the last node */
60 };
61
62 /** nonlinear handler data */
63 struct SCIP_NlhdlrData
64 {
65 SCIP_EXPR** exprs; /**< expressions that have been detected by the nlhdlr */
66 int nexprs; /**< total number of expression that have been detected */
67 int exprsize; /**< size of exprs array */
68 SCIP_HASHMAP* exprmap; /**< hashmap to store the position of each expression in the exprs array */
69
70 /* parameter */
71 SCIP_Bool useinteval; /**< whether to use the interval evaluation callback of the nlhdlr */
72 SCIP_Bool usereverseprop; /**< whether to use the reverse propagation callback of the nlhdlr */
73 int maxseparoundsroot; /**< maximum number of separation rounds in the root node */
74 int maxseparounds; /**< maximum number of separation rounds in a local node */
75 int maxsepadepth; /**< maximum depth to apply separation */
76 };
77
78 /*
79 * Local methods
80 */
81
82 /** helper function to compute the violation of an inequality of the form xcoef * x <= ycoef * y + constant for two
83 * corner points of the domain [lbx,ubx] x [lby,uby]
84 */
85 static
86 void getIneqViol(
87 SCIP_VAR* x, /**< first variable */
88 SCIP_VAR* y, /**< second variable */
89 SCIP_Real xcoef, /**< x-coefficient */
90 SCIP_Real ycoef, /**< y-coefficient */
91 SCIP_Real constant, /**< constant */
92 SCIP_Real* viol1, /**< buffer to store the violation of the first corner point */
93 SCIP_Real* viol2 /**< buffer to store the violation of the second corner point */
94 )
95 {
96 SCIP_Real norm;
97 assert(viol1 != NULL);
98 assert(viol2 != NULL);
99
100 norm = SQRT(SQR(xcoef) + SQR(ycoef));
101
102 /* inequality can be used for underestimating xy if and only if xcoef * ycoef > 0 */
103 if( xcoef * ycoef >= 0 )
104 {
105 /* violation for top-left and bottom-right corner */
106 *viol1 = MAX(0, (xcoef * SCIPvarGetLbLocal(x) - ycoef * SCIPvarGetUbLocal(y) - constant) / norm); /*lint !e666*/
107 *viol2 = MAX(0, (xcoef * SCIPvarGetUbLocal(x) - ycoef * SCIPvarGetLbLocal(y) - constant) / norm); /*lint !e666*/
108 }
109 else
110 {
111 /* violation for top-right and bottom-left corner */
112 *viol1 = MAX(0, (xcoef * SCIPvarGetUbLocal(x) - ycoef * SCIPvarGetUbLocal(y) - constant) / norm); /*lint !e666*/
113 *viol2 = MAX(0, (xcoef * SCIPvarGetLbLocal(x) - ycoef * SCIPvarGetLbLocal(y) - constant) / norm); /*lint !e666*/
114 }
115 }
116
117 /** auxiliary function to decide whether to use inequalities for a strong relaxation of bilinear terms or not */
118 static
119 SCIP_Bool useBilinIneqs(
120 SCIP* scip, /**< SCIP data structure */
121 SCIP_VAR* x, /**< x variable */
122 SCIP_VAR* y, /**< y variable */
123 SCIP_Real refx, /**< reference point for x */
124 SCIP_Real refy /**< reference point for y */
125 )
126 {
127 SCIP_Real lbx;
128 SCIP_Real ubx;
129 SCIP_Real lby;
130 SCIP_Real uby;
131 SCIP_Real interiorityx;
132 SCIP_Real interiorityy;
133 SCIP_Real interiority;
134
135 assert(x != NULL);
136 assert(y != NULL);
137 assert(x != y);
138
139 /* get variable bounds */
140 lbx = SCIPvarGetLbLocal(x);
141 ubx = SCIPvarGetUbLocal(x);
142 lby = SCIPvarGetLbLocal(y);
143 uby = SCIPvarGetUbLocal(y);
144
145 /* compute interiority */
146 interiorityx = MIN(refx-lbx, ubx-refx) / MAX(ubx-lbx, SCIPepsilon(scip)); /*lint !e666*/
147 interiorityy = MIN(refy-lby, uby-refy) / MAX(uby-lby, SCIPepsilon(scip)); /*lint !e666*/
148 interiority = 2.0*MIN(interiorityx, interiorityy);
149
150 return ubx - lbx >= MIN_ABSBOUNDSIZE && uby - lby >= MIN_ABSBOUNDSIZE && interiority >= MIN_INTERIORITY;
151 }
152
153 /** helper function to update the best relaxation for a bilinear term when using valid linear inequalities */
154 static
155 void updateBilinearRelaxation(
156 SCIP* scip, /**< SCIP data structure */
157 SCIP_VAR* RESTRICT x, /**< first variable */
158 SCIP_VAR* RESTRICT y, /**< second variable */
159 SCIP_Real bilincoef, /**< coefficient of the bilinear term */
160 SCIP_SIDETYPE violside, /**< side of quadratic constraint that is violated */
161 SCIP_Real refx, /**< reference point for the x variable */
162 SCIP_Real refy, /**< reference point for the y variable */
163 SCIP_Real* RESTRICT ineqs, /**< coefficients of each linear inequality; stored as triple (xcoef,ycoef,constant) */
164 int nineqs, /**< total number of inequalities */
165 SCIP_Real mccormickval, /**< value of the McCormick relaxation at the reference point */
166 SCIP_Real* RESTRICT bestcoefx, /**< pointer to update the x coefficient */
167 SCIP_Real* RESTRICT bestcoefy, /**< pointer to update the y coefficient */
168 SCIP_Real* RESTRICT bestconst, /**< pointer to update the constant */
169 SCIP_Real* RESTRICT bestval, /**< value of the best relaxation that have been found so far */
170 SCIP_Bool* success /**< buffer to store whether we found a better relaxation */
171 )
172 {
173 SCIP_Real constshift[2] = {0.0, 0.0};
174 SCIP_Real constant;
175 SCIP_Real xcoef;
176 SCIP_Real ycoef;
177 SCIP_Real lbx;
178 SCIP_Real ubx;
179 SCIP_Real lby;
180 SCIP_Real uby;
181 SCIP_Bool update;
182 SCIP_Bool overestimate;
183 int i;
184
185 assert(x != y);
186 assert(!SCIPisZero(scip, bilincoef));
187 assert(nineqs >= 0 && nineqs <= 2);
188 assert(bestcoefx != NULL);
189 assert(bestcoefy != NULL);
190 assert(bestconst != NULL);
191 assert(bestval != NULL);
192
193 /* no inequalities available */
194 if( nineqs == 0 )
195 return;
196 assert(ineqs != NULL);
197
198 lbx = SCIPvarGetLbLocal(x);
199 ubx = SCIPvarGetUbLocal(x);
200 lby = SCIPvarGetLbLocal(y);
201 uby = SCIPvarGetUbLocal(y);
202 overestimate = (violside == SCIP_SIDETYPE_LEFT);
203
204 /* check cases for which we can't compute a tighter relaxation */
205 if( SCIPisFeasLE(scip, refx, lbx) || SCIPisFeasGE(scip, refx, ubx)
206 || SCIPisFeasLE(scip, refy, lby) || SCIPisFeasGE(scip, refy, uby) )
207 return;
208
209 /* due to the feasibility tolerances of the LP and NLP solver, it might possible that the reference point is
210 * violating the linear inequalities; to ensure that we compute a valid underestimate, we relax the linear
211 * inequality by changing its constant part
212 */
213 for( i = 0; i < nineqs; ++i )
214 {
215 constshift[i] = MAX(0.0, ineqs[3*i] * refx - ineqs[3*i+1] * refy - ineqs[3*i+2]);
216 SCIPdebugMsg(scip, "constant shift of inequality %d = %.16f\n", i, constshift[i]);
217 }
218
219 /* try to use both inequalities */
220 if( nineqs == 2 )
221 {
222 SCIPcomputeBilinEnvelope2(scip, bilincoef, lbx, ubx, refx, lby, uby, refy, overestimate, ineqs[0], ineqs[1],
223 ineqs[2] + constshift[0], ineqs[3], ineqs[4], ineqs[5] + constshift[1], &xcoef, &ycoef, &constant, &update);
224
225 if( update )
226 {
227 SCIP_Real val = xcoef * refx + ycoef * refy + constant;
228 SCIP_Real relimpr = 1.0 - (REALABS(val - bilincoef * refx * refy) + 1e-4) / (REALABS(*bestval - bilincoef * refx * refy) + 1e-4);
229 SCIP_Real absimpr = REALABS(val - (*bestval));
230
231 /* update relaxation if possible */
232 if( relimpr > 0.05 && absimpr > 1e-3 && ((overestimate && SCIPisRelLT(scip, val, *bestval))
233 || (!overestimate && SCIPisRelGT(scip, val, *bestval))) )
234 {
235 *bestcoefx = xcoef;
236 *bestcoefy = ycoef;
237 *bestconst = constant;
238 *bestval = val;
239 *success = TRUE;
240 }
241 }
242 }
243
244 /* use inequalities individually */
245 for( i = 0; i < nineqs; ++i )
246 {
247 SCIPcomputeBilinEnvelope1(scip, bilincoef, lbx, ubx, refx, lby, uby, refy, overestimate, ineqs[3*i], ineqs[3*i+1],
248 ineqs[3*i+2] + constshift[i], &xcoef, &ycoef, &constant, &update);
249
250 if( update )
251 {
252 SCIP_Real val = xcoef * refx + ycoef * refy + constant;
253 SCIP_Real relimpr = 1.0 - (REALABS(val - bilincoef * refx * refy) + 1e-4)
254 / (REALABS(mccormickval - bilincoef * refx * refy) + 1e-4);
255 SCIP_Real absimpr = REALABS(val - (*bestval));
256
257 /* update relaxation if possible */
258 if( relimpr > 0.05 && absimpr > 1e-3 && ((overestimate && SCIPisRelLT(scip, val, *bestval))
259 || (!overestimate && SCIPisRelGT(scip, val, *bestval))) )
260 {
261 *bestcoefx = xcoef;
262 *bestcoefy = ycoef;
263 *bestconst = constant;
264 *bestval = val;
265 *success = TRUE;
266 }
267 }
268 }
269 }
270
271 /** helper function to determine whether a given point satisfy given inequalities */
272 static
273 SCIP_Bool isPointFeasible(
274 SCIP* scip, /**< SCIP data structure */
275 SCIP_Real x, /**< x-coordinate */
276 SCIP_Real y, /**< y-coordinate */
277 SCIP_Real lbx, /**< lower bound of x */
278 SCIP_Real ubx, /**< upper bound of x */
279 SCIP_Real lby, /**< lower bound of y */
280 SCIP_Real uby, /**< upper bound of y */
281 SCIP_Real* ineqs, /**< inequalities of the form coefx x <= coefy y + constant */
282 int nineqs /**< total number of inequalities */
283 )
284 {
285 int i;
286
287 assert(ineqs != NULL);
288 assert(nineqs > 0);
289
290 /* check whether point satisfies the bounds */
291 if( SCIPisLT(scip, x, lbx) || SCIPisGT(scip, x, ubx)
292 || SCIPisLT(scip, y, lby) || SCIPisGT(scip, y, uby) )
293 return FALSE;
294
295 /* check whether point satisfy the linear inequalities */
296 for( i = 0; i < nineqs; ++i )
297 {
298 SCIP_Real coefx = ineqs[3*i];
299 SCIP_Real coefy = ineqs[3*i+1];
300 SCIP_Real constant = ineqs[3*i+2];
301
302 /* TODO check with an absolute comparison? */
303 if( SCIPisGT(scip, coefx*x - coefy*y - constant, 0.0) )
304 return FALSE;
305 }
306
307 return TRUE;
308 }
309
310 /** helper function for computing all vertices of the polytope described by the linear inequalities and the local
311 * extrema of the bilinear term along each inequality
312 *
313 * @note there are at most 22 points where the min/max can be achieved (given that there are at most 4 inequalities)
314 * - corners of [lbx,ubx]x[lby,uby] (4)
315 * - two intersection points for each inequality with the box (8)
316 * - global maximum / minimum on each inequality (4)
317 * - intersection between two inequalities (6)
318 */
319 static
320 void getFeasiblePointsBilinear(
321 SCIP* scip, /**< SCIP data structure */
322 SCIP_CONSHDLR* conshdlr, /**< constraint handler, if levelset == TRUE, otherwise can be NULL */
323 SCIP_EXPR* expr, /**< product expression */
324 SCIP_INTERVAL exprbounds, /**< bounds on product expression, only used if levelset == TRUE */
325 SCIP_Real* underineqs, /**< inequalities for underestimation */
326 int nunderineqs, /**< total number of inequalities for underestimation */
327 SCIP_Real* overineqs, /**< inequalities for overestimation */
328 int noverineqs, /**< total number of inequalities for overestimation */
329 SCIP_Bool levelset, /**< should the level set be considered? */
330 SCIP_Real* xs, /**< array to store x-coordinates of computed points */
331 SCIP_Real* ys, /**< array to store y-coordinates of computed points */
332 int* npoints /**< buffer to store the total number of computed points */
333 )
334 {
335 SCIP_EXPR* child1;
336 SCIP_EXPR* child2;
337 SCIP_Real ineqs[12];
338 SCIP_INTERVAL boundsx;
339 SCIP_INTERVAL boundsy;
340 SCIP_Real lbx;
341 SCIP_Real ubx;
342 SCIP_Real lby;
343 SCIP_Real uby;
344 int nineqs = 0;
345 int i;
346
347 assert(scip != NULL);
348 assert(conshdlr != NULL || !levelset);
349 assert(expr != NULL);
350 assert(xs != NULL);
351 assert(ys != NULL);
352 assert(SCIPexprGetNChildren(expr) == 2);
353 assert(noverineqs + nunderineqs > 0);
354 assert(noverineqs + nunderineqs <= 4);
355
356 *npoints = 0;
357
358 /* collect inequalities */
359 for( i = 0; i < noverineqs; ++i )
360 {
361 SCIPdebugMsg(scip, "over-inequality %d: %g*x <= %g*y + %g\n", i, overineqs[3*i], overineqs[3*i+1], overineqs[3*i+2]);
362 ineqs[3*nineqs] = overineqs[3*i];
363 ineqs[3*nineqs+1] = overineqs[3*i+1];
364 ineqs[3*nineqs+2] = overineqs[3*i+2];
365 ++nineqs;
366 }
367 for( i = 0; i < nunderineqs; ++i )
368 {
369 SCIPdebugMsg(scip, "under-inequality %d: %g*x <= %g*y + %g 0\n", i, underineqs[3*i], underineqs[3*i+1], underineqs[3*i+2]);
370 ineqs[3*nineqs] = underineqs[3*i];
371 ineqs[3*nineqs+1] = underineqs[3*i+1];
372 ineqs[3*nineqs+2] = underineqs[3*i+2];
373 ++nineqs;
374 }
375 assert(nineqs == noverineqs + nunderineqs);
376
377 /* collect children */
378 child1 = SCIPexprGetChildren(expr)[0];
379 child2 = SCIPexprGetChildren(expr)[1];
380 assert(child1 != NULL && child2 != NULL);
381 assert(child1 != child2);
382
383 /* collect bounds of children */
384 if( !levelset )
385 {
386 /* if called from inteval, then use activity */
387 boundsx = SCIPexprGetActivity(child1);
388 boundsy = SCIPexprGetActivity(child2);
389 }
390 else
391 {
392 /* if called from reverseprop, then use bounds */
393 boundsx = SCIPgetExprBoundsNonlinear(scip, child1);
394 boundsy = SCIPgetExprBoundsNonlinear(scip, child2);
395
396 /* if children bounds are empty, then returning with *npoints==0 is the way to go */
397 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, boundsx)
398 || SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, boundsy) )
399 return;
400 }
401 lbx = boundsx.inf;
402 ubx = boundsx.sup;
403 lby = boundsy.inf;
404 uby = boundsy.sup;
405 SCIPdebugMsg(scip, "x = [%g,%g], y=[%g,%g]\n", lbx, ubx, lby, uby);
406
407 /* corner points that satisfy all inequalities */
408 for( i = 0; i < 4; ++i )
409 {
410 SCIP_Real cx = i < 2 ? lbx : ubx;
411 SCIP_Real cy = (i % 2) == 0 ? lby : uby;
412
413 SCIPdebugMsg(scip, "corner point (%g,%g) feasible? %u\n", cx, cy, isPointFeasible(scip, cx, cy, lbx, ubx, lby, uby, ineqs, nineqs));
414
415 if( isPointFeasible(scip, cx, cy, lbx, ubx, lby, uby, ineqs, nineqs) )
416 {
417 xs[*npoints] = cx;
418 ys[*npoints] = cy;
419 ++(*npoints);
420 }
421 }
422
423 /* intersection point of inequalities with [lbx,ubx] x [lby,uby] and extremum of xy on each inequality */
424 for( i = 0; i < nineqs; ++i )
425 {
426 SCIP_Real coefx = ineqs[3*i];
427 SCIP_Real coefy = ineqs[3*i+1];
428 SCIP_Real constant = ineqs[3*i+2];
429 SCIP_Real px[5] = {lbx, ubx, (coefy*lby + constant)/coefx, (coefy*uby + constant)/coefx, 0.0};
430 SCIP_Real py[5] = {(coefx*lbx - constant)/coefy, (coefx*ubx - constant)/coefy, lby, uby, 0.0};
431 int j;
432
433 /* the last entry corresponds to the extremum of xy on the line */
434 py[4] = (-constant) / (2.0 * coefy);
435 px[4] = constant / (2.0 * coefx);
436
437 for( j = 0; j < 5; ++j )
438 {
439 SCIPdebugMsg(scip, "intersection point (%g,%g) feasible? %u\n", px[j], py[j], isPointFeasible(scip, px[j], py[j], lbx, ubx, lby, uby, ineqs, nineqs));
440 if( isPointFeasible(scip, px[j], py[j], lbx, ubx, lby, uby, ineqs, nineqs) )
441 {
442 xs[*npoints] = px[j];
443 ys[*npoints] = py[j];
444 ++(*npoints);
445 }
446 }
447 }
448
449 /* intersection point between two inequalities */
450 for( i = 0; i < nineqs - 1; ++i )
451 {
452 SCIP_Real coefx1 = ineqs[3*i];
453 SCIP_Real coefy1 = ineqs[3*i+1];
454 SCIP_Real constant1 = ineqs[3*i+2];
455 int j;
456
457 for( j = i + 1; j < nineqs; ++j )
458 {
459 SCIP_Real coefx2 = ineqs[3*j];
460 SCIP_Real coefy2 = ineqs[3*j+1];
461 SCIP_Real constant2 = ineqs[3*j+2];
462 SCIP_Real px;
463 SCIP_Real py;
464
465 /* no intersection point -> skip */
466 if( SCIPisZero(scip, coefx2*coefy1 - coefx1 * coefy2) )
467 continue;
468
469 py = (constant2 * coefx1 - constant1 * coefx2)/ (coefx2 * coefy1 - coefx1 * coefy2);
470 px = (coefy1 * py + constant1) / coefx1;
471 assert(SCIPisRelEQ(scip, px, (coefy2 * py + constant2) / coefx2));
472
473 if( isPointFeasible(scip, px, py, lbx, ubx, lby, uby, ineqs, nineqs) )
474 {
475 xs[*npoints] = px;
476 ys[*npoints] = py;
477 ++(*npoints);
478 }
479 }
480 }
481
482 assert(*npoints <= 22);
483
484 /* consider the intersection of the level set with
485 *
486 * 1. the boundary of the box
487 * 2. the linear inequalities
488 *
489 * this adds at most for 4 (level set curves) * 4 (inequalities) * 2 (intersection points) for all linear
490 * inequalities and 4 (level set curves) * 2 (intersection points) with the boundary of the box
491 */
492 if( !levelset )
493 return;
494
495 /* compute intersection of level sets with the boundary */
496 for( i = 0; i < 2; ++i )
497 {
498 SCIP_Real vals[4] = {lbx, ubx, lby, uby};
499 SCIP_Real val;
500 int k;
501
502 /* fix auxiliary variable to its lower or upper bound and consider the coefficient of the product */
503 val = (i == 0) ? exprbounds.inf : exprbounds.sup;
504 val /= SCIPgetCoefExprProduct(expr);
505
506 for( k = 0; k < 4; ++k )
507 {
508 if( !SCIPisZero(scip, vals[k]) )
509 {
510 SCIP_Real res = val / vals[k];
511
512 assert(SCIPisRelGE(scip, SCIPgetCoefExprProduct(expr)*res*vals[k], exprbounds.inf));
513 assert(SCIPisRelLE(scip, SCIPgetCoefExprProduct(expr)*res*vals[k], exprbounds.sup));
514
515 /* fix x to lbx or ubx */
516 if( k < 2 && isPointFeasible(scip, vals[k], res, lbx, ubx, lby, uby, ineqs, nineqs) )
517 {
518 xs[*npoints] = vals[k];
519 ys[*npoints] = res;
520 ++(*npoints);
521 }
522 /* fix y to lby or uby */
523 else if( k >= 2 && isPointFeasible(scip, res, vals[k], lbx, ubx, lby, uby, ineqs, nineqs) )
524 {
525 xs[*npoints] = res;
526 ys[*npoints] = vals[k];
527 ++(*npoints);
528 }
529 }
530 }
531 }
532
533 /* compute intersection points of level sets with the linear inequalities */
534 for( i = 0; i < nineqs; ++i )
535 {
536 SCIP_INTERVAL result;
537 SCIP_Real coefx = ineqs[3*i];
538 SCIP_Real coefy = ineqs[3*i+1];
539 SCIP_Real constant = ineqs[3*i+2];
540 SCIP_INTERVAL sqrcoef;
541 SCIP_INTERVAL lincoef;
542 SCIP_Real px;
543 SCIP_Real py;
544 int k;
545
546 /* solve system of coefx x = coefy y + constant and X = xy which is the same as computing the solutions of
547 *
548 * (coefy / coefx) y^2 + (constant / coefx) y = inf(X) or sup(X)
549 */
550 SCIPintervalSet(&sqrcoef, coefy / coefx);
551 SCIPintervalSet(&lincoef, constant / coefx);
552
553 for( k = 0; k < 2; ++k )
554 {
555 SCIP_INTERVAL rhs;
556 SCIP_INTERVAL ybnds;
557
558 /* set right-hand side */
559 if( k == 0 )
560 SCIPintervalSet(&rhs, exprbounds.inf);
561 else
562 SCIPintervalSet(&rhs, exprbounds.sup);
563
564 SCIPintervalSetBounds(&ybnds, lby, uby);
565 SCIPintervalSolveUnivariateQuadExpression(SCIP_INTERVAL_INFINITY, &result, sqrcoef, lincoef, rhs, ybnds);
566
567 /* interval is empty -> no solution available */
568 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, result) )
569 continue;
570
571 /* compute and check point */
572 py = SCIPintervalGetInf(result);
573 px = (coefy * py + constant) / coefx;
574
575 if( isPointFeasible(scip, px, py, lbx, ubx, lby, uby, ineqs, nineqs) )
576 {
577 xs[*npoints] = px;
578 ys[*npoints] = py;
579 ++(*npoints);
580 }
581
582 /* check for a second solution */
583 if( SCIPintervalGetInf(result) != SCIPintervalGetSup(result) ) /*lint !e777*/
584 {
585 py = SCIPintervalGetSup(result);
586 px = (coefy * py + constant) / coefx;
587
588 if( isPointFeasible(scip, px, py, lbx, ubx, lby, uby, ineqs, nineqs) )
589 {
590 xs[*npoints] = px;
591 ys[*npoints] = py;
592 ++(*npoints);
593 }
594 }
595 }
596 }
597
598 assert(*npoints <= 62);
599 }
600
601 /** computes interval for a bilinear term when using at least one inequality */
602 static
603 SCIP_INTERVAL intevalBilinear(
604 SCIP* scip, /**< SCIP data structure */
605 SCIP_EXPR* expr, /**< product expression */
606 SCIP_Real* underineqs, /**< inequalities for underestimation */
607 int nunderineqs, /**< total number of inequalities for underestimation */
608 SCIP_Real* overineqs, /**< inequalities for overestimation */
609 int noverineqs /**< total number of inequalities for overestimation */
610 )
611 {
612 SCIP_INTERVAL interval = {0., 0.};
613 SCIP_Real xs[22];
614 SCIP_Real ys[22];
615 SCIP_Real inf;
616 SCIP_Real sup;
617 int npoints;
618 int i;
619
620 assert(scip != NULL);
621 assert(expr != NULL);
622 assert(SCIPexprGetNChildren(expr) == 2);
623 assert(noverineqs + nunderineqs <= 4);
624
625 /* no inequalities available -> skip computation */
626 if( noverineqs == 0 && nunderineqs == 0 )
627 {
628 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, &interval);
629 return interval;
630 }
631
632 /* x or y has empty interval -> empty */
633 if( SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(SCIPexprGetChildren(expr)[0])) ||
634 SCIPintervalIsEmpty(SCIP_INTERVAL_INFINITY, SCIPexprGetActivity(SCIPexprGetChildren(expr)[1])) )
635 {
636 SCIPintervalSetEmpty(&interval);
637 return interval;
638 }
639
640 /* compute all feasible points (since we use levelset == FALSE, the value of interval doesn't matter) */
641 getFeasiblePointsBilinear(scip, NULL, expr, interval, underineqs, nunderineqs, overineqs,
642 noverineqs, FALSE, xs, ys, &npoints);
643
644 /* no feasible point left -> return an empty interval */
645 if( npoints == 0 )
646 {
647 SCIPintervalSetEmpty(&interval);
648 return interval;
649 }
650
651 /* compute the minimum and maximum over all computed points */
652 inf = xs[0] * ys[0];
653 sup = inf;
654 SCIPdebugMsg(scip, "point 0: (%g,%g) -> inf = sup = %g\n", xs[0], ys[0], inf);
655 for( i = 1; i < npoints; ++i )
656 {
657 inf = MIN(inf, xs[i] * ys[i]);
658 sup = MAX(sup, xs[i] * ys[i]);
659 SCIPdebugMsg(scip, "point %d: (%g,%g) -> inf = %g, sup = %g\n", i, xs[i], ys[i], inf, sup);
660 }
661 assert(inf <= sup);
662
663 /* adjust infinite values */
664 inf = MAX(inf, -SCIP_INTERVAL_INFINITY);
665 sup = MIN(sup, SCIP_INTERVAL_INFINITY);
666
667 /* multiply resulting interval with coefficient of the product expression */
668 SCIPintervalSetBounds(&interval, inf, sup);
669 if( SCIPgetCoefExprProduct(expr) != 1.0 )
670 SCIPintervalMulScalar(SCIP_INTERVAL_INFINITY, &interval, interval, SCIPgetCoefExprProduct(expr));
671
672 return interval;
673 }
674
675 /** uses inequalities for bilinear terms to get stronger bounds during reverse propagation */
676 static
677 void reversePropBilinear(
678 SCIP* scip, /**< SCIP data structure */
679 SCIP_CONSHDLR* conshdlr, /**< constraint handler */
680 SCIP_EXPR* expr, /**< product expression */
681 SCIP_INTERVAL exprbounds, /**< bounds on product expression */
682 SCIP_Real* underineqs, /**< inequalities for underestimation */
683 int nunderineqs, /**< total number of inequalities for underestimation */
684 SCIP_Real* overineqs, /**< inequalities for overestimation */
685 int noverineqs, /**< total number of inequalities for overestimation */
686 SCIP_INTERVAL* intervalx, /**< buffer to store the new interval for x */
687 SCIP_INTERVAL* intervaly /**< buffer to store the new interval for y */
688 )
689 {
690 SCIP_Real xs[62];
691 SCIP_Real ys[62];
692 SCIP_Real exprinf;
693 SCIP_Real exprsup;
694 SCIP_Bool first = TRUE;
695 int npoints;
696 int i;
697
698 assert(scip != NULL);
699 assert(conshdlr != NULL);
700 assert(expr != NULL);
701 assert(intervalx != NULL);
702 assert(intervaly != NULL);
703 assert(SCIPexprGetNChildren(expr) == 2);
704
705 assert(noverineqs + nunderineqs > 0);
706
707 /* set intervals to be empty */
708 SCIPintervalSetEmpty(intervalx);
709 SCIPintervalSetEmpty(intervaly);
710
711 /* compute feasible points */
712 getFeasiblePointsBilinear(scip, conshdlr, expr, exprbounds, underineqs, nunderineqs, overineqs,
713 noverineqs, TRUE, xs, ys, &npoints);
714
715 /* no feasible points left -> problem is infeasible */
716 if( npoints == 0 )
717 return;
718
719 /* get bounds of the product expression */
720 exprinf = exprbounds.inf;
721 exprsup = exprbounds.sup;
722
723 /* update intervals with the computed points */
724 for( i = 0; i < npoints; ++i )
725 {
726 SCIP_Real val = SCIPgetCoefExprProduct(expr) * xs[i] * ys[i];
727
728 #ifndef NDEBUG
729 {
730 SCIP_Real lbx = SCIPexprGetActivity(SCIPexprGetChildren(expr)[0]).inf;
731 SCIP_Real ubx = SCIPexprGetActivity(SCIPexprGetChildren(expr)[0]).sup;
732 SCIP_Real lby = SCIPexprGetActivity(SCIPexprGetChildren(expr)[1]).inf;
733 SCIP_Real uby = SCIPexprGetActivity(SCIPexprGetChildren(expr)[1]).sup;
734
735 assert(nunderineqs == 0 || isPointFeasible(scip, xs[i], ys[i], lbx, ubx, lby, uby, underineqs, nunderineqs));
736 assert(noverineqs == 0 || isPointFeasible(scip, xs[i], ys[i], lbx, ubx, lby, uby, overineqs, noverineqs));
737 }
738 #endif
739
740 /* only accept points for which the value of x*y is in the interval of the product expression
741 *
742 * NOTE: in order to consider all relevant points, we are a bit conservative here and relax the interval of
743 * the expression by SCIPfeastol()
744 */
745 if( SCIPisRelGE(scip, val, exprinf - SCIPfeastol(scip)) && SCIPisRelLE(scip, val, exprsup + SCIPfeastol(scip)) )
746 {
747 if( first )
748 {
749 SCIPintervalSet(intervalx, xs[i]);
750 SCIPintervalSet(intervaly, ys[i]);
751 first = FALSE;
752 }
753 else
754 {
755 (*intervalx).inf = MIN((*intervalx).inf, xs[i]);
756 (*intervalx).sup = MAX((*intervalx).sup, xs[i]);
757 (*intervaly).inf = MIN((*intervaly).inf, ys[i]);
758 (*intervaly).sup = MAX((*intervaly).sup, ys[i]);
759 }
760
761 SCIPdebugMsg(scip, "consider points (%g,%g)=%g for reverse propagation\n", xs[i], ys[i], val);
762 }
763 }
764 }
765
766 /** helper function to compute the convex envelope of a bilinear term when two linear inequalities are given; we
767 * use the same notation and formulas as in Locatelli 2016
768 */
769 static
770 void computeBilinEnvelope2(
771 SCIP* scip, /**< SCIP data structure */
772 SCIP_Real x, /**< reference point for x */
773 SCIP_Real y, /**< reference point for y */
774 SCIP_Real mi, /**< coefficient of x in the first linear inequality */
775 SCIP_Real qi, /**< constant in the first linear inequality */
776 SCIP_Real mj, /**< coefficient of x in the second linear inequality */
777 SCIP_Real qj, /**< constant in the second linear inequality */
778 SCIP_Real* RESTRICT xi, /**< buffer to store x coordinate of the first point */
779 SCIP_Real* RESTRICT yi, /**< buffer to store y coordinate of the first point */
780 SCIP_Real* RESTRICT xj, /**< buffer to store x coordinate of the second point */
781 SCIP_Real* RESTRICT yj, /**< buffer to store y coordinate of the second point */
782 SCIP_Real* RESTRICT xcoef, /**< buffer to store the x coefficient of the envelope */
783 SCIP_Real* RESTRICT ycoef, /**< buffer to store the y coefficient of the envelope */
784 SCIP_Real* RESTRICT constant /**< buffer to store the constant of the envelope */
785 )
786 {
787 SCIP_Real QUAD(xiq);
788 SCIP_Real QUAD(yiq);
789 SCIP_Real QUAD(xjq);
790 SCIP_Real QUAD(yjq);
791 SCIP_Real QUAD(xcoefq);
792 SCIP_Real QUAD(ycoefq);
793 SCIP_Real QUAD(constantq);
794 SCIP_Real QUAD(tmpq);
795
796 assert(xi != NULL);
797 assert(yi != NULL);
798 assert(xj != NULL);
799 assert(yj != NULL);
800 assert(xcoef != NULL);
801 assert(ycoef != NULL);
802 assert(constant != NULL);
803
804 if( SCIPisEQ(scip, mi, mj) )
805 {
806 /* xi = (x + mi * y - qi) / (2.0*mi) */
807 SCIPquadprecProdDD(xiq, mi, y);
808 SCIPquadprecSumQD(xiq, xiq, x);
809 SCIPquadprecSumQD(xiq, xiq, -qi);
810 SCIPquadprecDivQD(xiq, xiq, 2.0 * mi);
811 assert(EPSEQ((x + mi * y - qi) / (2.0*mi), QUAD_TO_DBL(xiq), 1e-3));
812
813 /* yi = mi*(*xi) + qi */
814 SCIPquadprecProdQD(yiq, xiq, mi);
815 SCIPquadprecSumQD(yiq, yiq, qi);
816 assert(EPSEQ(mi*QUAD_TO_DBL(xiq) + qi, QUAD_TO_DBL(yiq), 1e-3));
817
818 /* xj = (*xi) + (qi - qj)/ (2.0*mi) */
819 SCIPquadprecSumDD(xjq, qi, -qj);
820 SCIPquadprecDivQD(xjq, xjq, 2.0 * mi);
821 SCIPquadprecSumQQ(xjq, xjq, xiq);
822 assert(EPSEQ(QUAD_TO_DBL(xiq) + (qi - qj)/ (2.0*mi), QUAD_TO_DBL(xjq), 1e-3));
823
824 /* yj = mj * (*xj) + qj */
825 SCIPquadprecProdQD(yjq, xjq, mj);
826 SCIPquadprecSumQD(yjq, yjq, qj);
827 assert(EPSEQ(mj * QUAD_TO_DBL(xjq) + qj, QUAD_TO_DBL(yjq), 1e-3));
828
829 /* ycoef = (*xi) + (qi - qj) / (4.0*mi) note that this is wrong in Locatelli 2016 */
830 SCIPquadprecSumDD(ycoefq, qi, -qj);
831 SCIPquadprecDivQD(ycoefq, ycoefq, 4.0 * mi);
832 SCIPquadprecSumQQ(ycoefq, ycoefq, xiq);
833 assert(EPSEQ(QUAD_TO_DBL(xiq) + (qi - qj) / (4.0*mi), QUAD_TO_DBL(ycoefq), 1e-3));
834
835 /* xcoef = 2.0*mi*(*xi) - mi * (*ycoef) + qi */
836 SCIPquadprecProdQD(xcoefq, xiq, 2.0 * mi);
837 SCIPquadprecProdQD(tmpq, ycoefq, -mi);
838 SCIPquadprecSumQQ(xcoefq, xcoefq, tmpq);
839 SCIPquadprecSumQD(xcoefq, xcoefq, qi);
840 assert(EPSEQ(2.0*mi*QUAD_TO_DBL(xiq) - mi * QUAD_TO_DBL(ycoefq) + qi, QUAD_TO_DBL(xcoefq), 1e-3));
841
842 /* constant = -mj*SQR(*xj) - (*ycoef) * qj */
843 SCIPquadprecSquareQ(constantq, xjq);
844 SCIPquadprecProdQD(constantq, constantq, -mj);
845 SCIPquadprecProdQD(tmpq, ycoefq, -qj);
846 SCIPquadprecSumQQ(constantq, constantq, tmpq);
847 /* assert(EPSEQ(-mj*SQR(QUAD_TO_DBL(xjq)) - QUAD_TO_DBL(ycoefq) * qj, QUAD_TO_DBL(constantq), 1e-3)); */
848
849 *xi = QUAD_TO_DBL(xiq);
850 *yi = QUAD_TO_DBL(yiq);
851 *xj = QUAD_TO_DBL(xjq);
852 *yj = QUAD_TO_DBL(yjq);
853 *ycoef = QUAD_TO_DBL(ycoefq);
854 *xcoef = QUAD_TO_DBL(xcoefq);
855 *constant = QUAD_TO_DBL(constantq);
856 }
857 else if( mi > 0.0 )
858 {
859 assert(mj > 0.0);
860
861 /* xi = (y + SQRT(mi*mj)*x - qi) / (REALABS(mi) + SQRT(mi*mj)) */
862 SCIPquadprecProdDD(xiq, mi, mj);
863 SCIPquadprecSqrtQ(xiq, xiq);
864 SCIPquadprecProdQD(xiq, xiq, x);
865 SCIPquadprecSumQD(xiq, xiq, y);
866 SCIPquadprecSumQD(xiq, xiq, -qi); /* (y + SQRT(mi*mj)*x - qi) */
867 SCIPquadprecProdDD(tmpq, mi, mj);
868 SCIPquadprecSqrtQ(tmpq, tmpq);
869 SCIPquadprecSumQD(tmpq, tmpq, REALABS(mi)); /* REALABS(mi) + SQRT(mi*mj) */
870 SCIPquadprecDivQQ(xiq, xiq, tmpq);
871 assert(EPSEQ((y + SQRT(mi*mj)*x - qi) / (REALABS(mi) + SQRT(mi*mj)), QUAD_TO_DBL(xiq), 1e-3));
872
873 /* yi = mi*(*xi) + qi */
874 SCIPquadprecProdQD(yiq, xiq, mi);
875 SCIPquadprecSumQD(yiq, yiq, qi);
876 assert(EPSEQ(mi*(QUAD_TO_DBL(xiq)) + qi, QUAD_TO_DBL(yiq), 1e-3));
877
878 /* xj = (y + SQRT(mi*mj)*x - qj) / (REALABS(mj) + SQRT(mi*mj)) */
879 SCIPquadprecProdDD(xjq, mi, mj);
880 SCIPquadprecSqrtQ(xjq, xjq);
881 SCIPquadprecProdQD(xjq, xjq, x);
882 SCIPquadprecSumQD(xjq, xjq, y);
883 SCIPquadprecSumQD(xjq, xjq, -qj); /* (y + SQRT(mi*mj)*x - qj) */
884 SCIPquadprecProdDD(tmpq, mi, mj);
885 SCIPquadprecSqrtQ(tmpq, tmpq);
886 SCIPquadprecSumQD(tmpq, tmpq, REALABS(mj)); /* REALABS(mj) + SQRT(mi*mj) */
887 SCIPquadprecDivQQ(xjq, xjq, tmpq);
888 assert(EPSEQ((y + SQRT(mi*mj)*x - qj) / (REALABS(mj) + SQRT(mi*mj)), QUAD_TO_DBL(xjq), 1e-3));
889
890 /* yj = mj*(*xj) + qj */
891 SCIPquadprecProdQD(yjq, xjq, mj);
892 SCIPquadprecSumQD(yjq, yjq, qj);
893 assert(EPSEQ(mj*QUAD_TO_DBL(xjq) + qj, QUAD_TO_DBL(yjq), 1e-3));
894
895 /* ycoef = (2.0*mj*(*xj) + qj - 2.0*mi*(*xi) - qi) / (mj - mi) */
896 SCIPquadprecProdQD(ycoefq, xjq, 2.0 * mj);
897 SCIPquadprecSumQD(ycoefq, ycoefq, qj);
898 SCIPquadprecProdQD(tmpq, xiq, -2.0 * mi);
899 SCIPquadprecSumQQ(ycoefq, ycoefq, tmpq);
900 SCIPquadprecSumQD(ycoefq, ycoefq, -qi);
901 SCIPquadprecSumDD(tmpq, mj, -mi);
902 SCIPquadprecDivQQ(ycoefq, ycoefq, tmpq);
903 assert(EPSEQ((2.0*mj*QUAD_TO_DBL(xjq) + qj - 2.0*mi*QUAD_TO_DBL(xiq) - qi) / (mj - mi), QUAD_TO_DBL(ycoefq), 1e-3));
904
905 /* xcoef = 2.0*mj*(*xj) + qj - mj*(*ycoef) */
906 SCIPquadprecProdQD(xcoefq, xjq, 2.0 * mj);
907 SCIPquadprecSumQD(xcoefq, xcoefq, qj);
908 SCIPquadprecProdQD(tmpq, ycoefq, -mj);
909 SCIPquadprecSumQQ(xcoefq, xcoefq, tmpq);
910 assert(EPSEQ(2.0*mj*QUAD_TO_DBL(xjq) + qj - mj*QUAD_TO_DBL(ycoefq), QUAD_TO_DBL(xcoefq), 1e-3));
911
912 /* constant = -mj*SQR(*xj) - (*ycoef) * qj */
913 SCIPquadprecSquareQ(constantq, xjq);
914 SCIPquadprecProdQD(constantq, constantq, -mj);
915 SCIPquadprecProdQD(tmpq, ycoefq, -qj);
916 SCIPquadprecSumQQ(constantq, constantq, tmpq);
917 /* assert(EPSEQ(-mj*SQR(QUAD_TO_DBL(xjq)) - QUAD_TO_DBL(ycoefq) * qj, QUAD_TO_DBL(constantq), 1e-3)); */
918
919 *xi = QUAD_TO_DBL(xiq);
920 *yi = QUAD_TO_DBL(yiq);
921 *xj = QUAD_TO_DBL(xjq);
922 *yj = QUAD_TO_DBL(yjq);
923 *ycoef = QUAD_TO_DBL(ycoefq);
924 *xcoef = QUAD_TO_DBL(xcoefq);
925 *constant = QUAD_TO_DBL(constantq);
926 }
927 else
928 {
929 assert(mi < 0.0 && mj < 0.0);
930
931 /* apply variable transformation x = -x in case for overestimation */
932 computeBilinEnvelope2(scip, -x, y, -mi, qi, -mj, qj, xi, yi, xj, yj, xcoef, ycoef, constant);
933
934 /* revert transformation; multiply cut by -1 and change -x by x */
935 *xi = -(*xi);
936 *xj = -(*xj);
937 *ycoef = -(*ycoef);
938 *constant = -(*constant);
939 }
940 }
941
942 /** output method of statistics table to output file stream 'file' */
943 static
944 SCIP_DECL_TABLEOUTPUT(tableOutputBilinear)
945 { /*lint --e{715}*/
946 SCIP_NLHDLR* nlhdlr;
947 SCIP_NLHDLRDATA* nlhdlrdata;
948 SCIP_CONSHDLR* conshdlr;
949 SCIP_HASHMAP* hashmap;
950 SCIP_EXPRITER* it;
951 int resfound = 0;
952 int restotal = 0;
953 int c;
954
955 conshdlr = SCIPfindConshdlr(scip, "nonlinear");
956 assert(conshdlr != NULL);
957 nlhdlr = SCIPfindNlhdlrNonlinear(conshdlr, NLHDLR_NAME);
958 assert(nlhdlr != NULL);
959 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
960 assert(nlhdlrdata != NULL);
961
962 /* allocate memory */
963 SCIP_CALL( SCIPhashmapCreate(&hashmap, SCIPblkmem(scip), nlhdlrdata->nexprs) );
964 SCIP_CALL( SCIPcreateExpriter(scip, &it) );
965
966 for( c = 0; c < nlhdlrdata->nexprs; ++c )
967 {
968 assert(!SCIPhashmapExists(hashmap, nlhdlrdata->exprs[c]));
969 SCIP_CALL( SCIPhashmapInsertInt(hashmap, nlhdlrdata->exprs[c], 0) );
970 }
971
972 /* count in how many constraints each expression is contained */
973 for( c = 0; c < SCIPconshdlrGetNConss(conshdlr); ++c )
974 {
975 SCIP_CONS* cons = SCIPconshdlrGetConss(conshdlr)[c];
976 SCIP_EXPR* expr;
977
978 SCIP_CALL( SCIPexpriterInit(it, SCIPgetExprNonlinear(cons), SCIP_EXPRITER_DFS, FALSE) );
979
980 for( expr = SCIPexpriterGetCurrent(it); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) ) /*lint !e441*//*lint !e440*/
981 {
982 if( SCIPhashmapExists(hashmap, expr) )
983 {
984 int nuses = SCIPhashmapGetImageInt(hashmap, expr);
985 SCIP_CALL( SCIPhashmapSetImageInt(hashmap, expr, nuses + 1) );
986 }
987 }
988 }
989
990 /* compute success ratio */
991 for( c = 0; c < nlhdlrdata->nexprs; ++c )
992 {
993 SCIP_NLHDLREXPRDATA* nlhdlrexprdata;
994 int nuses;
995
996 nuses = SCIPhashmapGetImageInt(hashmap, nlhdlrdata->exprs[c]);
997 assert(nuses > 0);
998
999 nlhdlrexprdata = SCIPgetNlhdlrExprDataNonlinear(nlhdlr, nlhdlrdata->exprs[c]);
1000 assert(nlhdlrexprdata != NULL);
1001
1002 if( nlhdlrexprdata->nunderineqs > 0 || nlhdlrexprdata->noverineqs > 0 )
1003 resfound += nuses;
1004 restotal += nuses;
1005 }
1006
1007 /* print statistics */
1008 SCIPinfoMessage(scip, file, "Bilinear Nlhdlr : %10s %10s\n", "#found", "#total");
1009 SCIPinfoMessage(scip, file, " %-17s:", "");
1010 SCIPinfoMessage(scip, file, " %10d", resfound);
1011 SCIPinfoMessage(scip, file, " %10d", restotal);
1012 SCIPinfoMessage(scip, file, "\n");
1013
1014 /* free memory */
1015 SCIPfreeExpriter(&it);
1016 SCIPhashmapFree(&hashmap);
1017
1018 return SCIP_OKAY;
1019 }
1020
1021
1022 /*
1023 * Callback methods of nonlinear handler
1024 */
1025
1026 /** nonlinear handler copy callback */
1027 static
1028 SCIP_DECL_NLHDLRCOPYHDLR(nlhdlrCopyhdlrBilinear)
1029 { /*lint --e{715}*/
1030 assert(targetscip != NULL);
1031 assert(sourcenlhdlr != NULL);
1032 assert(strcmp(SCIPnlhdlrGetName(sourcenlhdlr), NLHDLR_NAME) == 0);
1033
1034 SCIP_CALL( SCIPincludeNlhdlrBilinear(targetscip) );
1035
1036 return SCIP_OKAY;
1037 }
1038
1039 /** callback to free data of handler */
1040 static
1041 SCIP_DECL_NLHDLRFREEHDLRDATA(nlhdlrFreehdlrdataBilinear)
1042 { /*lint --e{715}*/
1043 assert(nlhdlrdata != NULL);
1044 assert((*nlhdlrdata)->nexprs == 0);
1045
1046 if( (*nlhdlrdata)->exprmap != NULL )
1047 {
1048 assert(SCIPhashmapGetNElements((*nlhdlrdata)->exprmap) == 0);
1049 SCIPhashmapFree(&(*nlhdlrdata)->exprmap);
1050 }
1051
1052 SCIPfreeBlockMemoryArrayNull(scip, &(*nlhdlrdata)->exprs, (*nlhdlrdata)->exprsize);
1053 SCIPfreeBlockMemory(scip, nlhdlrdata);
1054
1055 return SCIP_OKAY;
1056 }
1057
1058 /** callback to free expression specific data */
1059 static
1060 SCIP_DECL_NLHDLRFREEEXPRDATA(nlhdlrFreeExprDataBilinear)
1061 { /*lint --e{715}*/
1062 SCIP_NLHDLRDATA* nlhdlrdata;
1063 int pos;
1064
1065 assert(expr != NULL);
1066
1067 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
1068 assert(nlhdlrdata != NULL);
1069 assert(nlhdlrdata->nexprs > 0);
1070 assert(nlhdlrdata->exprs != NULL);
1071 assert(nlhdlrdata->exprmap != NULL);
1072 assert(SCIPhashmapExists(nlhdlrdata->exprmap, (void*)expr));
1073
1074 pos = SCIPhashmapGetImageInt(nlhdlrdata->exprmap, (void*)expr);
1075 assert(pos >= 0 && pos < nlhdlrdata->nexprs);
1076 assert(nlhdlrdata->exprs[pos] == expr);
1077
1078 /* move the last expression to the free position */
1079 if( nlhdlrdata->nexprs > 0 && pos != nlhdlrdata->nexprs - 1 )
1080 {
1081 SCIP_EXPR* lastexpr = nlhdlrdata->exprs[nlhdlrdata->nexprs - 1];
1082 assert(expr != lastexpr);
1083 assert(SCIPhashmapExists(nlhdlrdata->exprmap, (void*)lastexpr));
1084
1085 nlhdlrdata->exprs[pos] = lastexpr;
1086 nlhdlrdata->exprs[nlhdlrdata->nexprs - 1] = NULL;
1087 SCIP_CALL( SCIPhashmapSetImageInt(nlhdlrdata->exprmap, (void*)lastexpr, pos) );
1088 }
1089
1090 /* remove expression from the nonlinear handler data */
1091 SCIP_CALL( SCIPhashmapRemove(nlhdlrdata->exprmap, (void*)expr) );
1092 SCIP_CALL( SCIPreleaseExpr(scip, &expr) );
1093 --nlhdlrdata->nexprs;
1094
1095 /* free nonlinear handler expression data */
1096 SCIPfreeBlockMemoryNull(scip, nlhdlrexprdata);
1097
1098 return SCIP_OKAY;
1099 }
1100
1101 /** callback to be called in initialization */
1102 #define nlhdlrInitBilinear NULL
1103
1104 /** callback to be called in deinitialization */
1105 static
1106 SCIP_DECL_NLHDLREXIT(nlhdlrExitBilinear)
1107 { /*lint --e{715}*/
1108 assert(SCIPnlhdlrGetData(nlhdlr) != NULL);
1109 assert(SCIPnlhdlrGetData(nlhdlr)->nexprs == 0);
1110
1111 return SCIP_OKAY;
1112 }
1113
1114 /** callback to detect structure in expression tree */
1115 static
1116 SCIP_DECL_NLHDLRDETECT(nlhdlrDetectBilinear)
1117 { /*lint --e{715}*/
1118 SCIP_NLHDLRDATA* nlhdlrdata;
1119
1120 assert(expr != NULL);
1121 assert(participating != NULL);
1122
1123 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
1124 assert(nlhdlrdata);
1125
1126 /* only during solving will we have the extra inequalities that we rely on so much here */
1127 if( SCIPgetStage(scip) < SCIP_STAGE_INITSOLVE )
1128 return SCIP_OKAY;
1129
1130 /* check for product expressions with two children */
1131 if( SCIPisExprProduct(scip, expr) && SCIPexprGetNChildren(expr) == 2
1132 && (nlhdlrdata->exprmap == NULL || !SCIPhashmapExists(nlhdlrdata->exprmap, (void*)expr)) )
1133 {
1134 SCIP_EXPR** children;
1135 SCIP_Bool valid;
1136 int c;
1137
1138 children = SCIPexprGetChildren(expr);
1139 assert(children != NULL);
1140
1141 /* detection is only successful if both children will have auxiliary variable or are variables
1142 * that are not binary variables */
1143 valid = TRUE;
1144 for( c = 0; c < 2; ++c )
1145 {
1146 assert(children[c] != NULL);
1147 if( SCIPgetExprNAuxvarUsesNonlinear(children[c]) == 0 &&
1148 (!SCIPisExprVar(scip, children[c]) || SCIPvarIsBinary(SCIPgetVarExprVar(children[c]))) )
1149 {
1150 valid = FALSE;
1151 break;
1152 }
1153 }
1154
1155 if( valid )
1156 {
1157 /* create expression data for the nonlinear handler */
1158 SCIP_CALL( SCIPallocClearBlockMemory(scip, nlhdlrexprdata) );
1159 (*nlhdlrexprdata)->lastnodeid = -1;
1160
1161 /* ensure that there is enough memory to store the detected expression */
1162 if( nlhdlrdata->exprsize < nlhdlrdata->nexprs + 1 )
1163 {
1164 int newsize = SCIPcalcMemGrowSize(scip, nlhdlrdata->nexprs + 1);
1165 assert(newsize > nlhdlrdata->exprsize);
1166
1167 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &nlhdlrdata->exprs, nlhdlrdata->exprsize, newsize) );
1168 nlhdlrdata->exprsize = newsize;
1169 }
1170
1171 /* create expression map, if not done so far */
1172 if( nlhdlrdata->exprmap == NULL )
1173 {
1174 SCIP_CALL( SCIPhashmapCreate(&nlhdlrdata->exprmap, SCIPblkmem(scip), SCIPgetNVars(scip)) );
1175 }
1176
1177 #ifndef NDEBUG
1178 {
1179 int i;
1180
1181 for( i = 0; i < nlhdlrdata->nexprs; ++i )
1182 assert(nlhdlrdata->exprs[i] != expr);
1183 }
1184 #endif
1185
1186 /* add expression to nlhdlrdata and capture it */
1187 nlhdlrdata->exprs[nlhdlrdata->nexprs] = expr;
1188 SCIPcaptureExpr(expr);
1189 SCIP_CALL( SCIPhashmapInsertInt(nlhdlrdata->exprmap, (void*)expr, nlhdlrdata->nexprs) );
1190 ++nlhdlrdata->nexprs;
1191
1192 /* tell children that we will use their auxvar and use its activity for both estimate and domain propagation */
1193 SCIP_CALL( SCIPregisterExprUsageNonlinear(scip, children[0], TRUE, nlhdlrdata->useinteval
1194 || nlhdlrdata->usereverseprop, TRUE, TRUE) );
1195 SCIP_CALL( SCIPregisterExprUsageNonlinear(scip, children[1], TRUE, nlhdlrdata->useinteval
1196 || nlhdlrdata->usereverseprop, TRUE, TRUE) );
1197 }
1198 }
1199
1200 if( *nlhdlrexprdata != NULL )
1201 {
1202 /* we want to join separation and domain propagation, if not disabled by parameter */
1203 *participating = SCIP_NLHDLR_METHOD_SEPABOTH;
1204 if( nlhdlrdata->useinteval || nlhdlrdata->usereverseprop )
1205 *participating |= SCIP_NLHDLR_METHOD_ACTIVITY;
1206 }
1207
1208 #ifdef SCIP_DEBUG
1209 if( *participating )
1210 {
1211 SCIPdebugMsg(scip, "detected expr ");
1212 SCIPprintExpr(scip, expr, NULL);
1213 SCIPinfoMessage(scip, NULL, " participating: %d\n", *participating);
1214 }
1215 #endif
1216
1217 return SCIP_OKAY;
1218 }
1219
1220 /** auxiliary evaluation callback of nonlinear handler */
1221 static
1222 SCIP_DECL_NLHDLREVALAUX(nlhdlrEvalauxBilinear)
1223 { /*lint --e{715}*/
1224 SCIP_VAR* var1;
1225 SCIP_VAR* var2;
1226 SCIP_Real coef;
1227
1228 assert(SCIPisExprProduct(scip, expr));
1229 assert(SCIPexprGetNChildren(expr) == 2);
1230
1231 var1 = SCIPgetExprAuxVarNonlinear(SCIPexprGetChildren(expr)[0]);
1232 assert(var1 != NULL);
1233 var2 = SCIPgetExprAuxVarNonlinear(SCIPexprGetChildren(expr)[1]);
1234 assert(var2 != NULL);
1235 coef = SCIPgetCoefExprProduct(expr);
1236
1237 *auxvalue = coef * SCIPgetSolVal(scip, sol, var1) * SCIPgetSolVal(scip, sol, var2);
1238
1239 return SCIP_OKAY;
1240 }
1241
1242 /** separation initialization method of a nonlinear handler (called during CONSINITLP) */
1243 #define nlhdlrInitSepaBilinear NULL
1244
1245 /** separation deinitialization method of a nonlinear handler (called during CONSEXITSOL) */
1246 #define nlhdlrExitSepaBilinear NULL
1247
1248 /** nonlinear handler separation callback */
1249 #define nlhdlrEnfoBilinear NULL
1250
1251 /** nonlinear handler under/overestimation callback */
1252 static
1253 SCIP_DECL_NLHDLRESTIMATE(nlhdlrEstimateBilinear)
1254 { /*lint --e{715}*/
1255 SCIP_NLHDLRDATA* nlhdlrdata;
1256 SCIP_VAR* x;
1257 SCIP_VAR* y;
1258 SCIP_VAR* auxvar;
1259 SCIP_Real lincoefx = 0.0;
1260 SCIP_Real lincoefy = 0.0;
1261 SCIP_Real linconstant = 0.0;
1262 SCIP_Real refpointx;
1263 SCIP_Real refpointy;
1264 SCIP_Real violation;
1265 SCIP_Longint nodeid;
1266 SCIP_Bool mccsuccess = TRUE;
1267 SCIP_ROWPREP* rowprep;
1268
1269 assert(rowpreps != NULL);
1270
1271 *success = FALSE;
1272 *addedbranchscores = FALSE;
1273
1274 /* check whether an inequality is available */
1275 if( nlhdlrexprdata->noverineqs == 0 && nlhdlrexprdata->nunderineqs == 0 )
1276 return SCIP_OKAY;
1277
1278 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
1279 assert(nlhdlrdata != NULL);
1280
1281 nodeid = SCIPnodeGetNumber(SCIPgetCurrentNode(scip));
1282
1283 /* update last node */
1284 if( nlhdlrexprdata->lastnodeid != nodeid )
1285 {
1286 nlhdlrexprdata->lastnodeid = nodeid;
1287 nlhdlrexprdata->nseparoundslastnode = 0;
1288 }
1289
1290 /* update separation round */
1291 ++nlhdlrexprdata->nseparoundslastnode;
1292
1293 /* check working limits */
1294 if( (SCIPgetDepth(scip) == 0 && nlhdlrexprdata->nseparoundslastnode > nlhdlrdata->maxseparoundsroot)
1295 || (SCIPgetDepth(scip) > 0 && nlhdlrexprdata->nseparoundslastnode > nlhdlrdata->maxseparounds)
1296 || SCIPgetDepth(scip) > nlhdlrdata->maxsepadepth )
1297 return SCIP_OKAY;
1298
1299 /* collect variables */
1300 x = SCIPgetExprAuxVarNonlinear(SCIPexprGetChildren(expr)[0]);
1301 assert(x != NULL);
1302 y = SCIPgetExprAuxVarNonlinear(SCIPexprGetChildren(expr)[1]);
1303 assert(y != NULL);
1304 auxvar = SCIPgetExprAuxVarNonlinear(expr);
1305 assert(auxvar != NULL);
1306
1307 /* get and adjust the reference points */
1308 refpointx = MIN(MAX(SCIPgetSolVal(scip, sol, x), SCIPvarGetLbLocal(x)),SCIPvarGetUbLocal(x)); /*lint !e666*/
1309 refpointy = MIN(MAX(SCIPgetSolVal(scip, sol, y), SCIPvarGetLbLocal(y)),SCIPvarGetUbLocal(y)); /*lint !e666*/
1310 assert(SCIPisLE(scip, refpointx, SCIPvarGetUbLocal(x)) && SCIPisGE(scip, refpointx, SCIPvarGetLbLocal(x)));
1311 assert(SCIPisLE(scip, refpointy, SCIPvarGetUbLocal(y)) && SCIPisGE(scip, refpointy, SCIPvarGetLbLocal(y)));
1312
1313 /* use McCormick inequalities to decide whether we want to separate or not */
1314 SCIPaddBilinMcCormick(scip, SCIPgetCoefExprProduct(expr), SCIPvarGetLbLocal(x), SCIPvarGetUbLocal(x), refpointx,
1315 SCIPvarGetLbLocal(y), SCIPvarGetUbLocal(y), refpointy, overestimate, &lincoefx, &lincoefy, &linconstant,
1316 &mccsuccess);
1317
1318 /* too large values in McCormick inequalities -> skip */
1319 if( !mccsuccess )
1320 return SCIP_OKAY;
1321
1322 /* compute violation for the McCormick relaxation */
1323 violation = lincoefx * refpointx + lincoefy * refpointy + linconstant - SCIPgetSolVal(scip, sol, auxvar);
1324 if( overestimate )
1325 violation = -violation;
1326
1327 /* only use a tighter relaxations if McCormick does not separate the reference point */
1328 if( SCIPisFeasLE(scip, violation, 0.0) && useBilinIneqs(scip, x, y, refpointx, refpointy) )
1329 {
1330 SCIP_Bool useoverestineq = SCIPgetCoefExprProduct(expr) > 0.0 ? overestimate : !overestimate;
1331 SCIP_Real mccormickval = lincoefx * refpointx + lincoefy * refpointy + linconstant;
1332 SCIP_Real* ineqs;
1333 SCIP_Real bestval;
1334 int nineqs;
1335
1336 /* McCormick relaxation is too weak */
1337 bestval = mccormickval;
1338
1339 /* get the inequalities that might lead to a tighter relaxation */
1340 if( useoverestineq )
1341 {
1342 ineqs = nlhdlrexprdata->overineqs;
1343 nineqs = nlhdlrexprdata->noverineqs;
1344 }
1345 else
1346 {
1347 ineqs = nlhdlrexprdata->underineqs;
1348 nineqs = nlhdlrexprdata->nunderineqs;
1349 }
1350
1351 /* use linear inequalities to update relaxation */
1352 updateBilinearRelaxation(scip, x, y, SCIPgetCoefExprProduct(expr),
1353 overestimate ? SCIP_SIDETYPE_LEFT : SCIP_SIDETYPE_RIGHT,
1354 refpointx, refpointy, ineqs, nineqs, mccormickval,
1355 &lincoefx, &lincoefy, &linconstant, &bestval,
1356 success);
1357
1358 #ifndef NDEBUG
1359 /* check whether cut is really valid */
1360 if( *success )
1361 {
1362 assert(!overestimate || SCIPisLE(scip, bestval, mccormickval));
1363 assert(overestimate || SCIPisGE(scip, bestval, mccormickval));
1364 }
1365 #endif
1366 }
1367
1368 if( *success )
1369 {
1370 SCIP_CALL( SCIPcreateRowprep(scip, &rowprep, overestimate ? SCIP_SIDETYPE_LEFT : SCIP_SIDETYPE_RIGHT, TRUE) );
1371 SCIProwprepAddConstant(rowprep, linconstant);
1372 SCIP_CALL( SCIPensureRowprepSize(scip, rowprep, 2) );
1373 SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, x, lincoefx) );
1374 SCIP_CALL( SCIPaddRowprepTerm(scip, rowprep, y, lincoefy) );
1375 SCIP_CALL( SCIPsetPtrarrayVal(scip, rowpreps, 0, rowprep) );
1376 }
1377
1378 return SCIP_OKAY;
1379 }
1380
1381 /** nonlinear handler interval evaluation callback */
1382 static
1383 SCIP_DECL_NLHDLRINTEVAL(nlhdlrIntevalBilinear)
1384 { /*lint --e{715}*/
1385 SCIP_NLHDLRDATA* nlhdlrdata;
1386 assert(nlhdlrexprdata != NULL);
1387
1388 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
1389 assert(nlhdlrdata != NULL);
1390
1391 if( nlhdlrdata->useinteval && nlhdlrexprdata->nunderineqs + nlhdlrexprdata->noverineqs > 0 )
1392 {
1393 SCIP_INTERVAL tmp = intevalBilinear(scip, expr, nlhdlrexprdata->underineqs, nlhdlrexprdata->nunderineqs,
1394 nlhdlrexprdata->overineqs, nlhdlrexprdata->noverineqs);
1395
1396 /* intersect intervals if we have learned a tighter interval */
1397 if( SCIPisGT(scip, tmp.inf, (*interval).inf) || SCIPisLT(scip, tmp.sup, (*interval).sup) )
1398 SCIPintervalIntersect(interval, *interval, tmp);
1399 }
1400
1401 return SCIP_OKAY;
1402 }
1403
1404 /** nonlinear handler callback for reverse propagation */
1405 static
1406 SCIP_DECL_NLHDLRREVERSEPROP(nlhdlrReversepropBilinear)
1407 { /*lint --e{715}*/
1408 SCIP_NLHDLRDATA* nlhdlrdata;
1409
1410 assert(nlhdlrexprdata != NULL);
1411
1412 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
1413 assert(nlhdlrdata != NULL);
1414
1415 if( nlhdlrdata->usereverseprop && nlhdlrexprdata->nunderineqs + nlhdlrexprdata->noverineqs > 0 )
1416 {
1417 SCIP_EXPR* childx;
1418 SCIP_EXPR* childy;
1419 SCIP_INTERVAL intervalx;
1420 SCIP_INTERVAL intervaly;
1421
1422 assert(SCIPexprGetNChildren(expr) == 2);
1423 childx = SCIPexprGetChildren(expr)[0];
1424 childy = SCIPexprGetChildren(expr)[1];
1425 assert(childx != NULL && childy != NULL);
1426
1427 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, &intervalx);
1428 SCIPintervalSetEntire(SCIP_INTERVAL_INFINITY, &intervaly);
1429
1430 /* compute bounds on x and y */
1431 reversePropBilinear(scip, conshdlr, expr, bounds, nlhdlrexprdata->underineqs, nlhdlrexprdata->nunderineqs,
1432 nlhdlrexprdata->overineqs, nlhdlrexprdata->noverineqs, &intervalx, &intervaly);
1433
1434 /* tighten bounds of x */
1435 SCIPdebugMsg(scip, "try to tighten bounds of x: [%g,%g] -> [%g,%g]\n",
1436 SCIPgetExprBoundsNonlinear(scip, childx).inf, SCIPgetExprBoundsNonlinear(scip, childx).sup,
1437 intervalx.inf, intervalx.sup);
1438
1439 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, SCIPexprGetChildren(expr)[0], intervalx, infeasible,
1440 nreductions) );
1441
1442 if( !(*infeasible) )
1443 {
1444 /* tighten bounds of y */
1445 SCIPdebugMsg(scip, "try to tighten bounds of y: [%g,%g] -> [%g,%g]\n",
1446 SCIPgetExprBoundsNonlinear(scip, childy).inf, SCIPgetExprBoundsNonlinear(scip, childy).sup,
1447 intervaly.inf, intervaly.sup);
1448 SCIP_CALL( SCIPtightenExprIntervalNonlinear(scip, SCIPexprGetChildren(expr)[1], intervaly,
1449 infeasible, nreductions) );
1450 }
1451 }
1452
1453 return SCIP_OKAY;
1454 }
1455
1456 /*
1457 * nonlinear handler specific interface methods
1458 */
1459
1460 /** includes bilinear nonlinear handler in nonlinear constraint handler */
1461 SCIP_RETCODE SCIPincludeNlhdlrBilinear(
1462 SCIP* scip /**< SCIP data structure */
1463 )
1464 {
1465 SCIP_NLHDLRDATA* nlhdlrdata;
1466 SCIP_NLHDLR* nlhdlr;
1467
1468 assert(scip != NULL);
1469
1470 /**! [SnippetIncludeNlhdlrBilinear] */
1471 /* create nonlinear handler specific data */
(1) Event cond_false: |
Condition "(nlhdlrdata = BMSallocBlockMemory_call(SCIPblkmem(scip), 48UL /* sizeof (*nlhdlrdata) */, "/sapmnt/home1/d029903/my_work/SCIPSoPlex_coverity/scipoptsuite-8.0.1/scip/src/scip/nlhdlr_bilinear.c", 1472)) == NULL", taking false branch. |
(2) Event cond_false: |
Condition "(_restat_ = (((nlhdlrdata = BMSallocBlockMemory_call(SCIPblkmem(scip), 48UL /* sizeof (*nlhdlrdata) */, "/sapmnt/home1/d029903/my_work/SCIPSoPlex_coverity/scipoptsuite-8.0.1/scip/src/scip/nlhdlr_bilinear.c", 1472)) == NULL) ? SCIP_NOMEMORY : SCIP_OKAY)) != SCIP_OKAY", taking false branch. |
(3) Event if_end: |
End of if statement. |
1472 SCIP_CALL( SCIPallocBlockMemory(scip, &nlhdlrdata) );
1473 BMSclearMemory(nlhdlrdata);
1474
(4) Event alloc_arg: |
"SCIPincludeNlhdlrNonlinear" allocates memory that is stored into "nlhdlr". [details] |
(5) Event cond_true: |
Condition "(_restat_ = SCIPincludeNlhdlrNonlinear(scip, &nlhdlr, "bilinear", "bilinear handler for expressions", -10, -10, nlhdlrDetectBilinear, nlhdlrEvalauxBilinear, nlhdlrdata)) != SCIP_OKAY", taking true branch. |
(6) Event leaked_storage: |
Variable "nlhdlr" going out of scope leaks the storage it points to. |
1475 SCIP_CALL( SCIPincludeNlhdlrNonlinear(scip, &nlhdlr, NLHDLR_NAME, NLHDLR_DESC, NLHDLR_DETECTPRIORITY,
1476 NLHDLR_ENFOPRIORITY, nlhdlrDetectBilinear, nlhdlrEvalauxBilinear, nlhdlrdata) );
1477 assert(nlhdlr != NULL);
1478
1479 SCIPnlhdlrSetCopyHdlr(nlhdlr, nlhdlrCopyhdlrBilinear);
1480 SCIPnlhdlrSetFreeHdlrData(nlhdlr, nlhdlrFreehdlrdataBilinear);
1481 SCIPnlhdlrSetFreeExprData(nlhdlr, nlhdlrFreeExprDataBilinear);
1482 SCIPnlhdlrSetInitExit(nlhdlr, nlhdlrInitBilinear, nlhdlrExitBilinear);
1483 SCIPnlhdlrSetSepa(nlhdlr, nlhdlrInitSepaBilinear, nlhdlrEnfoBilinear, nlhdlrEstimateBilinear, nlhdlrExitSepaBilinear);
1484 SCIPnlhdlrSetProp(nlhdlr, nlhdlrIntevalBilinear, nlhdlrReversepropBilinear);
1485
1486 /* parameters */
1487 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" NLHDLR_NAME "/useinteval",
1488 "whether to use the interval evaluation callback of the nlhdlr",
1489 &nlhdlrdata->useinteval, FALSE, TRUE, NULL, NULL) );
1490
1491 SCIP_CALL( SCIPaddBoolParam(scip, "nlhdlr/" NLHDLR_NAME "/usereverseprop",
1492 "whether to use the reverse propagation callback of the nlhdlr",
1493 &nlhdlrdata->usereverseprop, FALSE, TRUE, NULL, NULL) );
1494
1495 SCIP_CALL( SCIPaddIntParam(scip, "nlhdlr/" NLHDLR_NAME "/maxseparoundsroot",
1496 "maximum number of separation rounds in the root node",
1497 &nlhdlrdata->maxseparoundsroot, FALSE, 10, 0, INT_MAX, NULL, NULL) );
1498
1499 SCIP_CALL( SCIPaddIntParam(scip, "nlhdlr/" NLHDLR_NAME "/maxseparounds",
1500 "maximum number of separation rounds in a local node",
1501 &nlhdlrdata->maxseparounds, FALSE, 1, 0, INT_MAX, NULL, NULL) );
1502
1503 SCIP_CALL( SCIPaddIntParam(scip, "nlhdlr/" NLHDLR_NAME "/maxsepadepth",
1504 "maximum depth to apply separation",
1505 &nlhdlrdata->maxsepadepth, FALSE, INT_MAX, 0, INT_MAX, NULL, NULL) );
1506
1507 /* statistic table */
1508 assert(SCIPfindTable(scip, TABLE_NAME_BILINEAR) == NULL);
1509 SCIP_CALL( SCIPincludeTable(scip, TABLE_NAME_BILINEAR, TABLE_DESC_BILINEAR, FALSE,
1510 NULL, NULL, NULL, NULL, NULL, NULL, tableOutputBilinear,
1511 NULL, TABLE_POSITION_BILINEAR, TABLE_EARLIEST_STAGE_BILINEAR) );
1512 /**! [SnippetIncludeNlhdlrBilinear] */
1513
1514 return SCIP_OKAY;
1515 }
1516
1517 /** returns an array of expressions that have been detected by the bilinear nonlinear handler */
1518 SCIP_EXPR** SCIPgetExprsBilinear(
1519 SCIP_NLHDLR* nlhdlr /**< nonlinear handler */
1520 )
1521 {
1522 SCIP_NLHDLRDATA* nlhdlrdata;
1523
1524 assert(nlhdlr != NULL);
1525 assert(strcmp(SCIPnlhdlrGetName(nlhdlr), NLHDLR_NAME) == 0);
1526
1527 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
1528 assert(nlhdlrdata);
1529
1530 return nlhdlrdata->exprs;
1531 }
1532
1533 /** returns the total number of expressions that have been detected by the bilinear nonlinear handler */
1534 int SCIPgetNExprsBilinear(
1535 SCIP_NLHDLR* nlhdlr /**< nonlinear handler */
1536 )
1537 {
1538 SCIP_NLHDLRDATA* nlhdlrdata;
1539
1540 assert(nlhdlr != NULL);
1541 assert(strcmp(SCIPnlhdlrGetName(nlhdlr), NLHDLR_NAME) == 0);
1542
1543 nlhdlrdata = SCIPnlhdlrGetData(nlhdlr);
1544 assert(nlhdlrdata);
1545
1546 return nlhdlrdata->nexprs;
1547 }
1548
1549 /** adds a globally valid inequality of the form \f$\text{xcoef}\cdot x \leq \text{ycoef} \cdot y + \text{constant}\f$ to a product expression of the form \f$x\cdot y\f$ */
1550 SCIP_RETCODE SCIPaddIneqBilinear(
1551 SCIP* scip, /**< SCIP data structure */
1552 SCIP_NLHDLR* nlhdlr, /**< nonlinear handler */
1553 SCIP_EXPR* expr, /**< product expression */
1554 SCIP_Real xcoef, /**< x coefficient */
1555 SCIP_Real ycoef, /**< y coefficient */
1556 SCIP_Real constant, /**< constant part */
1557 SCIP_Bool* success /**< buffer to store whether inequality has been accepted */
1558 )
1559 {
1560 SCIP_NLHDLREXPRDATA* nlhdlrexprdata;
1561 SCIP_VAR* x;
1562 SCIP_VAR* y;
1563 SCIP_Real* ineqs;
1564 SCIP_Real viol1;
1565 SCIP_Real viol2;
1566 SCIP_Bool underestimate;
1567 int nineqs;
1568 int i;
1569
1570 assert(scip != NULL);
1571 assert(nlhdlr != NULL);
1572 assert(strcmp(SCIPnlhdlrGetName(nlhdlr), NLHDLR_NAME) == 0);
1573 assert(expr != NULL);
1574 assert(SCIPexprGetNChildren(expr) == 2);
1575 assert(xcoef != SCIP_INVALID); /*lint !e777 */
1576 assert(ycoef != SCIP_INVALID); /*lint !e777 */
1577 assert(constant != SCIP_INVALID); /*lint !e777 */
1578 assert(success != NULL);
1579
1580 *success = FALSE;
1581
1582 /* find nonlinear handler expression handler data */
1583 nlhdlrexprdata = SCIPgetNlhdlrExprDataNonlinear(nlhdlr, expr);
1584
1585 if( nlhdlrexprdata == NULL )
1586 {
1587 SCIPwarningMessage(scip, "nonlinear expression data has not been found. "
1588 "Skip SCIPaddConsExprExprProductBilinearIneq()\n");
1589 return SCIP_OKAY;
1590 }
1591
1592 /* ignore inequalities that only yield to a (possible) bound tightening */
1593 if( SCIPisFeasZero(scip, xcoef) || SCIPisFeasZero(scip, ycoef) )
1594 return SCIP_OKAY;
1595
1596 /* collect variables */
1597 x = SCIPgetExprAuxVarNonlinear(SCIPexprGetChildren(expr)[0]);
1598 y = SCIPgetExprAuxVarNonlinear(SCIPexprGetChildren(expr)[1]);
1599 assert(x != NULL);
1600 assert(y != NULL);
1601 assert(x != y);
1602
1603 /* normalize inequality s.t. xcoef in {-1,1} */
1604 if( !SCIPisEQ(scip, REALABS(xcoef), 1.0) )
1605 {
1606 constant /= REALABS(xcoef);
1607 ycoef /= REALABS(xcoef);
1608 xcoef /= REALABS(xcoef);
1609 }
1610
1611 /* coefficients of the inequality determine whether the inequality can be used for under- or overestimation */
1612 underestimate = xcoef * ycoef > 0;
1613
1614 SCIPdebugMsg(scip, "add inequality for a bilinear term: %g %s <= %g %s + %g (underestimate=%u)\n", xcoef,
1615 SCIPvarGetName(x), ycoef, SCIPvarGetName(y), constant, underestimate);
1616
1617 /* compute violation of the inequality of the important corner points */
1618 getIneqViol(x, y, xcoef, ycoef, constant, &viol1, &viol2);
1619 SCIPdebugMsg(scip, "violations of inequality = (%g,%g)\n", viol1, viol2);
1620
1621 /* inequality does not cutoff one of the important corner points -> skip */
1622 if( SCIPisFeasLE(scip, MAX(viol1, viol2), 0.0) )
1623 return SCIP_OKAY;
1624
1625 if( underestimate )
1626 {
1627 ineqs = nlhdlrexprdata->underineqs;
1628 nineqs = nlhdlrexprdata->nunderineqs;
1629 }
1630 else
1631 {
1632 ineqs = nlhdlrexprdata->overineqs;
1633 nineqs = nlhdlrexprdata->noverineqs;
1634 }
1635
1636 /* check for a duplicate */
1637 for( i = 0; i < nineqs; ++i )
1638 {
1639 if( SCIPisFeasEQ(scip, xcoef, ineqs[3*i]) && SCIPisFeasEQ(scip, ycoef, ineqs[3*i+1])
1640 && SCIPisFeasEQ(scip, constant, ineqs[3*i+2]) )
1641 {
1642 SCIPdebugMsg(scip, "inequality already found -> skip\n");
1643 return SCIP_OKAY;
1644 }
1645 }
1646
1647 {
1648 SCIP_Real viols1[2] = {0.0, 0.0};
1649 SCIP_Real viols2[2] = {0.0, 0.0};
1650
1651 /* compute violations of existing inequalities */
1652 for( i = 0; i < nineqs; ++i )
1653 {
1654 getIneqViol(x, y, ineqs[3*i], ineqs[3*i+1], ineqs[3*i+2], &viols1[i], &viols2[i]);
1655
1656 /* check whether an existing inequality is dominating the candidate */
1657 if( SCIPisGE(scip, viols1[i], viol1) && SCIPisGE(scip, viols2[i], viol2) )
1658 {
1659 SCIPdebugMsg(scip, "inequality is dominated by %d -> skip\n", i);
1660 return SCIP_OKAY;
1661 }
1662
1663 /* replace inequality if candidate is dominating it */
1664 if( SCIPisLT(scip, viols1[i], viol1) && SCIPisLT(scip, viols2[i], viol2) )
1665 {
1666 SCIPdebugMsg(scip, "inequality dominates %d -> replace\n", i);
1667 ineqs[3*i] = xcoef;
1668 ineqs[3*i+1] = ycoef;
1669 ineqs[3*i+2] = constant;
1670 *success = TRUE;
1671 }
1672 }
1673
1674 /* inequality is not dominated by other inequalities -> add if we have less than 2 inequalities */
1675 if( nineqs < 2 )
1676 {
1677 ineqs[3*nineqs] = xcoef;
1678 ineqs[3*nineqs + 1] = ycoef;
1679 ineqs[3*nineqs + 2] = constant;
1680 *success = TRUE;
1681 SCIPdebugMsg(scip, "add inequality\n");
1682
1683 /* increase number of inequalities */
1684 if( underestimate )
1685 ++(nlhdlrexprdata->nunderineqs);
1686 else
1687 ++(nlhdlrexprdata->noverineqs);
1688 }
1689 }
1690
1691 if( *success )
1692 {
1693 /* With the added inequalities, we can potentially compute tighter activities for the expression,
1694 * so constraints that contain this expression should be propagated again.
1695 * We don't have a direct expression to constraint mapping, though. This call marks all expr-constraints
1696 * which include any of the variables that this expression depends on for propagation.
1697 */
1698 SCIP_CALL( SCIPmarkExprPropagateNonlinear(scip, expr) );
1699 }
1700
1701 return SCIP_OKAY;
1702 }
1703
1704 /** computes coefficients of linearization of a bilinear term in a reference point */
1705 void SCIPaddBilinLinearization(
1706 SCIP* scip, /**< SCIP data structure */
1707 SCIP_Real bilincoef, /**< coefficient of bilinear term */
1708 SCIP_Real refpointx, /**< point where to linearize first variable */
1709 SCIP_Real refpointy, /**< point where to linearize second variable */
1710 SCIP_Real* lincoefx, /**< buffer to add coefficient of first variable in linearization */
1711 SCIP_Real* lincoefy, /**< buffer to add coefficient of second variable in linearization */
1712 SCIP_Real* linconstant, /**< buffer to add constant of linearization */
1713 SCIP_Bool* success /**< buffer to set to FALSE if linearization has failed due to large numbers */
1714 )
1715 {
1716 SCIP_Real constant;
1717
1718 assert(scip != NULL);
1719 assert(lincoefx != NULL);
1720 assert(lincoefy != NULL);
1721 assert(linconstant != NULL);
1722 assert(success != NULL);
1723
1724 if( bilincoef == 0.0 )
1725 return;
1726
1727 if( SCIPisInfinity(scip, REALABS(refpointx)) || SCIPisInfinity(scip, REALABS(refpointy)) )
1728 {
1729 *success = FALSE;
1730 return;
1731 }
1732
1733 /* bilincoef * x * y -> bilincoef * (refpointx * refpointy + refpointy * (x - refpointx) + refpointx * (y - refpointy))
1734 * = -bilincoef * refpointx * refpointy + bilincoef * refpointy * x + bilincoef * refpointx * y
1735 */
1736
1737 constant = -bilincoef * refpointx * refpointy;
1738
1739 if( SCIPisInfinity(scip, REALABS(bilincoef * refpointx)) || SCIPisInfinity(scip, REALABS(bilincoef * refpointy))
1740 || SCIPisInfinity(scip, REALABS(constant)) )
1741 {
1742 *success = FALSE;
1743 return;
1744 }
1745
1746 *lincoefx += bilincoef * refpointy;
1747 *lincoefy += bilincoef * refpointx;
1748 *linconstant += constant;
1749 }
1750
1751 /** computes coefficients of McCormick under- or overestimation of a bilinear term */
1752 void SCIPaddBilinMcCormick(
1753 SCIP* scip, /**< SCIP data structure */
1754 SCIP_Real bilincoef, /**< coefficient of bilinear term */
1755 SCIP_Real lbx, /**< lower bound on first variable */
1756 SCIP_Real ubx, /**< upper bound on first variable */
1757 SCIP_Real refpointx, /**< reference point for first variable */
1758 SCIP_Real lby, /**< lower bound on second variable */
1759 SCIP_Real uby, /**< upper bound on second variable */
1760 SCIP_Real refpointy, /**< reference point for second variable */
1761 SCIP_Bool overestimate, /**< whether to compute an overestimator instead of an underestimator */
1762 SCIP_Real* lincoefx, /**< buffer to add coefficient of first variable in linearization */
1763 SCIP_Real* lincoefy, /**< buffer to add coefficient of second variable in linearization */
1764 SCIP_Real* linconstant, /**< buffer to add constant of linearization */
1765 SCIP_Bool* success /**< buffer to set to FALSE if linearization has failed due to large numbers */
1766 )
1767 {
1768 SCIP_Real constant;
1769 SCIP_Real coefx;
1770 SCIP_Real coefy;
1771
1772 assert(scip != NULL);
1773 assert(!SCIPisInfinity(scip, lbx));
1774 assert(!SCIPisInfinity(scip, -ubx));
1775 assert(!SCIPisInfinity(scip, lby));
1776 assert(!SCIPisInfinity(scip, -uby));
1777 assert(SCIPisInfinity(scip, -lbx) || SCIPisLE(scip, lbx, ubx));
1778 assert(SCIPisInfinity(scip, -lby) || SCIPisLE(scip, lby, uby));
1779 assert(SCIPisInfinity(scip, -lbx) || SCIPisLE(scip, lbx, refpointx));
1780 assert(SCIPisInfinity(scip, -lby) || SCIPisLE(scip, lby, refpointy));
1781 assert(SCIPisInfinity(scip, ubx) || SCIPisGE(scip, ubx, refpointx));
1782 assert(SCIPisInfinity(scip, uby) || SCIPisGE(scip, uby, refpointy));
1783 assert(lincoefx != NULL);
1784 assert(lincoefy != NULL);
1785 assert(linconstant != NULL);
1786 assert(success != NULL);
1787
1788 if( bilincoef == 0.0 )
1789 return;
1790
1791 if( overestimate )
1792 bilincoef = -bilincoef;
1793
1794 if( SCIPisRelEQ(scip, lbx, ubx) && SCIPisRelEQ(scip, lby, uby) )
1795 {
1796 /* both x and y are mostly fixed */
1797 SCIP_Real cand1;
1798 SCIP_Real cand2;
1799 SCIP_Real cand3;
1800 SCIP_Real cand4;
1801
1802 coefx = 0.0;
1803 coefy = 0.0;
1804
1805 /* estimate x * y by constant */
1806 cand1 = lbx * lby;
1807 cand2 = lbx * uby;
1808 cand3 = ubx * lby;
1809 cand4 = ubx * uby;
1810
1811 /* take most conservative value for underestimator */
1812 if( bilincoef < 0.0 )
1813 constant = bilincoef * MAX( MAX(cand1, cand2), MAX(cand3, cand4) );
1814 else
1815 constant = bilincoef * MIN( MIN(cand1, cand2), MIN(cand3, cand4) );
1816 }
1817 else if( bilincoef > 0.0 )
1818 {
1819 /* either x or y is not fixed and coef > 0.0 */
1820 if( !SCIPisInfinity(scip, -lbx) && !SCIPisInfinity(scip, -lby) &&
1821 (SCIPisInfinity(scip, ubx) || SCIPisInfinity(scip, uby)
1822 || (uby - refpointy) * (ubx - refpointx) >= (refpointy - lby) * (refpointx - lbx)) )
1823 {
1824 if( SCIPisRelEQ(scip, lbx, ubx) )
1825 {
1826 /* x*y = lbx * y + (x-lbx) * y >= lbx * y + (x-lbx) * lby >= lbx * y + min{(ubx-lbx) * lby, 0 * lby} */
1827 coefx = 0.0;
1828 coefy = bilincoef * lbx;
1829 constant = bilincoef * (lby < 0.0 ? (ubx-lbx) * lby : 0.0);
1830 }
1831 else if( SCIPisRelEQ(scip, lby, uby) )
1832 {
1833 /* x*y = lby * x + (y-lby) * x >= lby * x + (y-lby) * lbx >= lby * x + min{(uby-lby) * lbx, 0 * lbx} */
1834 coefx = bilincoef * lby;
1835 coefy = 0.0;
1836 constant = bilincoef * (lbx < 0.0 ? (uby-lby) * lbx : 0.0);
1837 }
1838 else
1839 {
1840 coefx = bilincoef * lby;
1841 coefy = bilincoef * lbx;
1842 constant = -bilincoef * lbx * lby;
1843 }
1844 }
1845 else if( !SCIPisInfinity(scip, ubx) && !SCIPisInfinity(scip, uby) )
1846 {
1847 if( SCIPisRelEQ(scip, lbx, ubx) )
1848 {
1849 /* x*y = ubx * y + (x-ubx) * y >= ubx * y + (x-ubx) * uby >= ubx * y + min{(lbx-ubx) * uby, 0 * uby} */
1850 coefx = 0.0;
1851 coefy = bilincoef * ubx;
1852 constant = bilincoef * (uby > 0.0 ? (lbx - ubx) * uby : 0.0);
1853 }
1854 else if( SCIPisRelEQ(scip, lby, uby) )
1855 {
1856 /* x*y = uby * x + (y-uby) * x >= uby * x + (y-uby) * ubx >= uby * x + min{(lby-uby) * ubx, 0 * ubx} */
1857 coefx = bilincoef * uby;
1858 coefy = 0.0;
1859 constant = bilincoef * (ubx > 0.0 ? (lby - uby) * ubx : 0.0);
1860 }
1861 else
1862 {
1863 coefx = bilincoef * uby;
1864 coefy = bilincoef * ubx;
1865 constant = -bilincoef * ubx * uby;
1866 }
1867 }
1868 else
1869 {
1870 *success = FALSE;
1871 return;
1872 }
1873 }
1874 else
1875 {
1876 /* either x or y is not fixed and coef < 0.0 */
1877 if( !SCIPisInfinity(scip, ubx) && !SCIPisInfinity(scip, -lby) &&
1878 (SCIPisInfinity(scip, -lbx) || SCIPisInfinity(scip, uby)
1879 || (ubx - lbx) * (refpointy - lby) <= (uby - lby) * (refpointx - lbx)) )
1880 {
1881 if( SCIPisRelEQ(scip, lbx, ubx) )
1882 {
1883 /* x*y = ubx * y + (x-ubx) * y <= ubx * y + (x-ubx) * lby <= ubx * y + max{(lbx-ubx) * lby, 0 * lby} */
1884 coefx = 0.0;
1885 coefy = bilincoef * ubx;
1886 constant = bilincoef * (lby < 0.0 ? (lbx - ubx) * lby : 0.0);
1887 }
1888 else if( SCIPisRelEQ(scip, lby, uby) )
1889 {
1890 /* x*y = lby * x + (y-lby) * x <= lby * x + (y-lby) * ubx <= lby * x + max{(uby-lby) * ubx, 0 * ubx} */
1891 coefx = bilincoef * lby;
1892 coefy = 0.0;
1893 constant = bilincoef * (ubx > 0.0 ? (uby - lby) * ubx : 0.0);
1894 }
1895 else
1896 {
1897 coefx = bilincoef * lby;
1898 coefy = bilincoef * ubx;
1899 constant = -bilincoef * ubx * lby;
1900 }
1901 }
1902 else if( !SCIPisInfinity(scip, -lbx) && !SCIPisInfinity(scip, uby) )
1903 {
1904 if( SCIPisRelEQ(scip, lbx, ubx) )
1905 {
1906 /* x*y = lbx * y + (x-lbx) * y <= lbx * y + (x-lbx) * uby <= lbx * y + max{(ubx-lbx) * uby, 0 * uby} */
1907 coefx = 0.0;
1908 coefy = bilincoef * lbx;
1909 constant = bilincoef * (uby > 0.0 ? (ubx - lbx) * uby : 0.0);
1910 }
1911 else if( SCIPisRelEQ(scip, lby, uby) )
1912 {
1913 /* x*y = uby * x + (y-uby) * x <= uby * x + (y-uby) * lbx <= uby * x + max{(lby-uby) * lbx, 0 * lbx} */
1914 coefx = bilincoef * uby;
1915 coefy = 0.0;
1916 constant = bilincoef * (lbx < 0.0 ? (lby - uby) * lbx : 0.0);
1917 }
1918 else
1919 {
1920 coefx = bilincoef * uby;
1921 coefy = bilincoef * lbx;
1922 constant = -bilincoef * lbx * uby;
1923 }
1924 }
1925 else
1926 {
1927 *success = FALSE;
1928 return;
1929 }
1930 }
1931
1932 if( SCIPisInfinity(scip, REALABS(coefx)) || SCIPisInfinity(scip, REALABS(coefy))
1933 || SCIPisInfinity(scip, REALABS(constant)) )
1934 {
1935 *success = FALSE;
1936 return;
1937 }
1938
1939 if( overestimate )
1940 {
1941 coefx = -coefx;
1942 coefy = -coefy;
1943 constant = -constant;
1944 }
1945
1946 SCIPdebugMsg(scip, "%.15g * x[%.15g,%.15g] * y[%.15g,%.15g] %c= %.15g * x %+.15g * y %+.15g\n", bilincoef, lbx, ubx,
1947 lby, uby, overestimate ? '<' : '>', coefx, coefy, constant);
1948
1949 *lincoefx += coefx;
1950 *lincoefy += coefy;
1951 *linconstant += constant;
1952 }
1953
1954 /** computes coefficients of linearization of a bilinear term in a reference point when given a linear inequality
1955 * involving only the variables of the bilinear term
1956 *
1957 * @note the formulas are extracted from "Convex envelopes of bivariate functions through the solution of KKT systems"
1958 * by Marco Locatelli
1959 */
1960 void SCIPcomputeBilinEnvelope1(
1961 SCIP* scip, /**< SCIP data structure */
1962 SCIP_Real bilincoef, /**< coefficient of bilinear term */
1963 SCIP_Real lbx, /**< lower bound on first variable */
1964 SCIP_Real ubx, /**< upper bound on first variable */
1965 SCIP_Real refpointx, /**< reference point for first variable */
1966 SCIP_Real lby, /**< lower bound on second variable */
1967 SCIP_Real uby, /**< upper bound on second variable */
1968 SCIP_Real refpointy, /**< reference point for second variable */
1969 SCIP_Bool overestimate, /**< whether to compute an overestimator instead of an underestimator */
1970 SCIP_Real xcoef, /**< x coefficient of linear inequality; must be in {-1,0,1} */
1971 SCIP_Real ycoef, /**< y coefficient of linear inequality */
1972 SCIP_Real constant, /**< constant of linear inequality */
1973 SCIP_Real* RESTRICT lincoefx, /**< buffer to store coefficient of first variable in linearization */
1974 SCIP_Real* RESTRICT lincoefy, /**< buffer to store coefficient of second variable in linearization */
1975 SCIP_Real* RESTRICT linconstant, /**< buffer to store constant of linearization */
1976 SCIP_Bool* RESTRICT success /**< buffer to store whether linearization was successful */
1977 )
1978 {
1979 SCIP_Real xs[2] = {lbx, ubx};
1980 SCIP_Real ys[2] = {lby, uby};
1981 SCIP_Real minx;
1982 SCIP_Real maxx;
1983 SCIP_Real miny;
1984 SCIP_Real maxy;
1985 SCIP_Real QUAD(lincoefyq);
1986 SCIP_Real QUAD(lincoefxq);
1987 SCIP_Real QUAD(linconstantq);
1988 SCIP_Real QUAD(denomq);
1989 SCIP_Real QUAD(mjq);
1990 SCIP_Real QUAD(qjq);
1991 SCIP_Real QUAD(xjq);
1992 SCIP_Real QUAD(yjq);
1993 SCIP_Real QUAD(tmpq);
1994 SCIP_Real vx;
1995 SCIP_Real vy;
1996 int n;
1997 int i;
1998
1999 assert(scip != NULL);
2000 assert(!SCIPisInfinity(scip, lbx));
2001 assert(!SCIPisInfinity(scip, -ubx));
2002 assert(!SCIPisInfinity(scip, lby));
2003 assert(!SCIPisInfinity(scip, -uby));
2004 assert(SCIPisLE(scip, lbx, ubx));
2005 assert(SCIPisLE(scip, lby, uby));
2006 assert(SCIPisLE(scip, lbx, refpointx));
2007 assert(SCIPisGE(scip, ubx, refpointx));
2008 assert(SCIPisLE(scip, lby, refpointy));
2009 assert(SCIPisGE(scip, uby, refpointy));
2010 assert(lincoefx != NULL);
2011 assert(lincoefy != NULL);
2012 assert(linconstant != NULL);
2013 assert(success != NULL);
2014 assert(xcoef == 0.0 || xcoef == -1.0 || xcoef == 1.0); /*lint !e777*/
2015 assert(ycoef != SCIP_INVALID && ycoef != 0.0); /*lint !e777*/
2016 assert(constant != SCIP_INVALID); /*lint !e777*/
2017
2018 *success = FALSE;
2019 *lincoefx = SCIP_INVALID;
2020 *lincoefy = SCIP_INVALID;
2021 *linconstant = SCIP_INVALID;
2022
2023 /* reference point does not satisfy linear inequality */
2024 if( SCIPisFeasGT(scip, xcoef * refpointx - ycoef * refpointy - constant, 0.0) )
2025 return;
2026
2027 /* compute minimal and maximal bounds on x and y for accepting the reference point */
2028 minx = lbx + 0.01 * (ubx-lbx);
2029 maxx = ubx - 0.01 * (ubx-lbx);
2030 miny = lby + 0.01 * (uby-lby);
2031 maxy = uby - 0.01 * (uby-lby);
2032
2033 /* check whether the reference point is in [minx,maxx]x[miny,maxy] */
2034 if( SCIPisLE(scip, refpointx, minx) || SCIPisGE(scip, refpointx, maxx)
2035 || SCIPisLE(scip, refpointy, miny) || SCIPisGE(scip, refpointy, maxy) )
2036 return;
2037
2038 /* always consider xy without the bilinear coefficient */
2039 if( bilincoef < 0.0 )
2040 overestimate = !overestimate;
2041
2042 /* we use same notation as in "Convex envelopes of bivariate functions through the solution of KKT systems", 2016 */
2043 /* mj = xcoef / ycoef */
2044 SCIPquadprecDivDD(mjq, xcoef, ycoef);
2045
2046 /* qj = -constant / ycoef */
2047 SCIPquadprecDivDD(qjq, -constant, ycoef);
2048
2049 /* mj > 0 => underestimate; mj < 0 => overestimate */
2050 if( SCIPisNegative(scip, QUAD_TO_DBL(mjq)) != overestimate )
2051 return;
2052
2053 /* get the corner point that satisfies the linear inequality xcoef*x <= ycoef*y + constant */
2054 if( !overestimate )
2055 {
2056 ys[0] = uby;
2057 ys[1] = lby;
2058 }
2059
2060 vx = SCIP_INVALID;
2061 vy = SCIP_INVALID;
2062 n = 0;
2063 for( i = 0; i < 2; ++i )
2064 {
2065 SCIP_Real activity = xcoef * xs[i] - ycoef * ys[i] - constant;
2066 if( SCIPisLE(scip, activity, 0.0) )
2067 {
2068 /* corner point is satisfies inequality */
2069 vx = xs[i];
2070 vy = ys[i];
2071 }
2072 else if( SCIPisFeasGT(scip, activity, 0.0) )
2073 /* corner point is clearly cut off */
2074 ++n;
2075 }
2076
2077 /* skip if no corner point satisfies the inequality or if no corner point is cut off
2078 * (that is, all corner points satisfy the inequality almost [1e-9..1e-6]) */
2079 if( n != 1 || vx == SCIP_INVALID || vy == SCIP_INVALID ) /*lint !e777*/
2080 return;
2081
2082 /* denom = mj*(refpointx - vx) + vy - refpointy */
2083 SCIPquadprecSumDD(denomq, refpointx, -vx); /* refpoint - vx */
2084 SCIPquadprecProdQQ(denomq, denomq, mjq); /* mj * (refpoint - vx) */
2085 SCIPquadprecSumQD(denomq, denomq, vy); /* mj * (refpoint - vx) + vy */
2086 SCIPquadprecSumQD(denomq, denomq, -refpointy); /* mj * (refpoint - vx) + vy - refpointy */
2087
2088 if( SCIPisZero(scip, QUAD_TO_DBL(denomq)) )
2089 return;
2090
2091 /* (xj,yj) is the projection onto the line xcoef*x = ycoef*y + constant */
2092 /* xj = (refpointx*(vy - qj) - vx*(refpointy - qj)) / denom */
2093 SCIPquadprecProdQD(xjq, qjq, -1.0); /* - qj */
2094 SCIPquadprecSumQD(xjq, xjq, vy); /* vy - qj */
2095 SCIPquadprecProdQD(xjq, xjq, refpointx); /* refpointx * (vy - qj) */
2096 SCIPquadprecProdQD(tmpq, qjq, -1.0); /* - qj */
2097 SCIPquadprecSumQD(tmpq, tmpq, refpointy); /* refpointy - qj */
2098 SCIPquadprecProdQD(tmpq, tmpq, -vx); /* - vx * (refpointy - qj) */
2099 SCIPquadprecSumQQ(xjq, xjq, tmpq); /* refpointx * (vy - qj) - vx * (refpointy - qj) */
2100 SCIPquadprecDivQQ(xjq, xjq, denomq); /* (refpointx * (vy - qj) - vx * (refpointy - qj)) / denom */
2101
2102 /* yj = mj * xj + qj */
2103 SCIPquadprecProdQQ(yjq, mjq, xjq);
2104 SCIPquadprecSumQQ(yjq, yjq, qjq);
2105
2106 assert(SCIPisFeasEQ(scip, xcoef*QUAD_TO_DBL(xjq) - ycoef*QUAD_TO_DBL(yjq) - constant, 0.0));
2107
2108 /* check whether the projection is in [minx,maxx] x [miny,maxy]; this avoids numerical difficulties when the
2109 * projection is close to the variable bounds
2110 */
2111 if( SCIPisLE(scip, QUAD_TO_DBL(xjq), minx) || SCIPisGE(scip, QUAD_TO_DBL(xjq), maxx)
2112 || SCIPisLE(scip, QUAD_TO_DBL(yjq), miny) || SCIPisGE(scip, QUAD_TO_DBL(yjq), maxy) )
2113 return;
2114
2115 assert(vy - QUAD_TO_DBL(mjq)*vx - QUAD_TO_DBL(qjq) != 0.0);
2116
2117 /* lincoefy = (mj*SQR(xj) - 2.0*mj*vx*xj - qj*vx + vx*vy) / (vy - mj*vx - qj) */
2118 SCIPquadprecSquareQ(lincoefyq, xjq); /* xj^2 */
2119 SCIPquadprecProdQQ(lincoefyq, lincoefyq, mjq); /* mj * xj^2 */
2120 SCIPquadprecProdQQ(tmpq, mjq, xjq); /* mj * xj */
2121 SCIPquadprecProdQD(tmpq, tmpq, -2.0 * vx); /* -2 * vx * mj * xj */
2122 SCIPquadprecSumQQ(lincoefyq, lincoefyq, tmpq); /* mj * xj^2 -2 * vx * mj * xj */
2123 SCIPquadprecProdQD(tmpq, qjq, -vx); /* -qj * vx */
2124 SCIPquadprecSumQQ(lincoefyq, lincoefyq, tmpq); /* mj * xj^2 -2 * vx * mj * xj -qj * vx */
2125 SCIPquadprecProdDD(tmpq, vx, vy); /* vx * vy */
2126 SCIPquadprecSumQQ(lincoefyq, lincoefyq, tmpq); /* mj * xj^2 -2 * vx * mj * xj -qj * vx + vx * vy */
2127 SCIPquadprecProdQD(tmpq, mjq, vx); /* mj * vx */
2128 SCIPquadprecSumQD(tmpq, tmpq, -vy); /* -vy + mj * vx */
2129 SCIPquadprecSumQQ(tmpq, tmpq, qjq); /* -vy + mj * vx + qj */
2130 QUAD_SCALE(tmpq, -1.0); /* vy - mj * vx - qj */
2131 SCIPquadprecDivQQ(lincoefyq, lincoefyq, tmpq); /* (mj * xj^2 -2 * vx * mj * xj -qj * vx + vx * vy) / (vy - mj * vx - qj) */
2132
2133 /* lincoefx = 2.0*mj*xj + qj - mj*(*lincoefy) */
2134 SCIPquadprecProdQQ(lincoefxq, mjq, xjq); /* mj * xj */
2135 QUAD_SCALE(lincoefxq, 2.0); /* 2 * mj * xj */
2136 SCIPquadprecSumQQ(lincoefxq, lincoefxq, qjq); /* 2 * mj * xj + qj */
2137 SCIPquadprecProdQQ(tmpq, mjq, lincoefyq); /* mj * lincoefy */
2138 QUAD_SCALE(tmpq, -1.0); /* - mj * lincoefy */
2139 SCIPquadprecSumQQ(lincoefxq, lincoefxq, tmpq); /* 2 * mj * xj + qj - mj * lincoefy */
2140
2141 /* linconstant = -mj*SQR(xj) - (*lincoefy)*qj */
2142 SCIPquadprecSquareQ(linconstantq, xjq); /* xj^2 */
2143 SCIPquadprecProdQQ(linconstantq, linconstantq, mjq); /* mj * xj^2 */
2144 QUAD_SCALE(linconstantq, -1.0); /* - mj * xj^2 */
2145 SCIPquadprecProdQQ(tmpq, lincoefyq, qjq); /* lincoefy * qj */
2146 QUAD_SCALE(tmpq, -1.0); /* - lincoefy * qj */
2147 SCIPquadprecSumQQ(linconstantq, linconstantq, tmpq); /* - mj * xj^2 - lincoefy * qj */
2148
2149 /* consider the bilinear coefficient */
2150 SCIPquadprecProdQD(lincoefxq, lincoefxq, bilincoef);
2151 SCIPquadprecProdQD(lincoefyq, lincoefyq, bilincoef);
2152 SCIPquadprecProdQD(linconstantq, linconstantq, bilincoef);
2153 *lincoefx = QUAD_TO_DBL(lincoefxq);
2154 *lincoefy = QUAD_TO_DBL(lincoefyq);
2155 *linconstant = QUAD_TO_DBL(linconstantq);
2156
2157 /* cut needs to be tight at (vx,vy) and (xj,yj); otherwise we consider the cut to be numerically bad */
2158 *success = SCIPisFeasEQ(scip, (*lincoefx)*vx + (*lincoefy)*vy + (*linconstant), bilincoef*vx*vy)
2159 && SCIPisFeasEQ(scip, (*lincoefx)*QUAD_TO_DBL(xjq) + (*lincoefy)*QUAD_TO_DBL(yjq) + (*linconstant),
2160 bilincoef*QUAD_TO_DBL(xjq)*QUAD_TO_DBL(yjq));
2161
2162 #ifndef NDEBUG
2163 {
2164 SCIP_Real activity = (*lincoefx)*refpointx + (*lincoefy)*refpointy + (*linconstant);
2165
2166 /* cut needs to under- or overestimate the bilinear term at the reference point */
2167 if( bilincoef < 0.0 )
2168 overestimate = !overestimate;
2169
2170 if( overestimate )
2171 assert(SCIPisFeasGE(scip, activity, bilincoef*refpointx*refpointy));
2172 else
2173 assert(SCIPisFeasLE(scip, activity, bilincoef*refpointx*refpointy));
2174 }
2175 #endif
2176 }
2177
2178 /** computes coefficients of linearization of a bilinear term in a reference point when given two linear inequalities
2179 * involving only the variables of the bilinear term
2180 *
2181 * @note the formulas are extracted from "Convex envelopes of bivariate functions through the solution of KKT systems"
2182 * by Marco Locatelli
2183 */
2184 void SCIPcomputeBilinEnvelope2(
2185 SCIP* scip, /**< SCIP data structure */
2186 SCIP_Real bilincoef, /**< coefficient of bilinear term */
2187 SCIP_Real lbx, /**< lower bound on first variable */
2188 SCIP_Real ubx, /**< upper bound on first variable */
2189 SCIP_Real refpointx, /**< reference point for first variable */
2190 SCIP_Real lby, /**< lower bound on second variable */
2191 SCIP_Real uby, /**< upper bound on second variable */
2192 SCIP_Real refpointy, /**< reference point for second variable */
2193 SCIP_Bool overestimate, /**< whether to compute an overestimator instead of an underestimator */
2194 SCIP_Real xcoef1, /**< x coefficient of linear inequality; must be in {-1,0,1} */
2195 SCIP_Real ycoef1, /**< y coefficient of linear inequality */
2196 SCIP_Real constant1, /**< constant of linear inequality */
2197 SCIP_Real xcoef2, /**< x coefficient of linear inequality; must be in {-1,0,1} */
2198 SCIP_Real ycoef2, /**< y coefficient of linear inequality */
2199 SCIP_Real constant2, /**< constant of linear inequality */
2200 SCIP_Real* RESTRICT lincoefx, /**< buffer to store coefficient of first variable in linearization */
2201 SCIP_Real* RESTRICT lincoefy, /**< buffer to store coefficient of second variable in linearization */
2202 SCIP_Real* RESTRICT linconstant, /**< buffer to store constant of linearization */
2203 SCIP_Bool* RESTRICT success /**< buffer to store whether linearization was successful */
2204 )
2205 {
2206 SCIP_Real mi, mj, qi, qj, xi, xj, yi, yj;
2207 SCIP_Real xcoef, ycoef, constant;
2208 SCIP_Real minx, maxx, miny, maxy;
2209
2210 assert(scip != NULL);
2211 assert(!SCIPisInfinity(scip, lbx));
2212 assert(!SCIPisInfinity(scip, -ubx));
2213 assert(!SCIPisInfinity(scip, lby));
2214 assert(!SCIPisInfinity(scip, -uby));
2215 assert(SCIPisLE(scip, lbx, ubx));
2216 assert(SCIPisLE(scip, lby, uby));
2217 assert(SCIPisLE(scip, lbx, refpointx));
2218 assert(SCIPisGE(scip, ubx, refpointx));
2219 assert(SCIPisLE(scip, lby, refpointy));
2220 assert(SCIPisGE(scip, uby, refpointy));
2221 assert(lincoefx != NULL);
2222 assert(lincoefy != NULL);
2223 assert(linconstant != NULL);
2224 assert(success != NULL);
2225 assert(xcoef1 != 0.0 && xcoef1 != SCIP_INVALID); /*lint !e777*/
2226 assert(ycoef1 != SCIP_INVALID && ycoef1 != 0.0); /*lint !e777*/
2227 assert(constant1 != SCIP_INVALID); /*lint !e777*/
2228 assert(xcoef2 != 0.0 && xcoef2 != SCIP_INVALID); /*lint !e777*/
2229 assert(ycoef2 != SCIP_INVALID && ycoef2 != 0.0); /*lint !e777*/
2230 assert(constant2 != SCIP_INVALID); /*lint !e777*/
2231
2232 *success = FALSE;
2233 *lincoefx = SCIP_INVALID;
2234 *lincoefy = SCIP_INVALID;
2235 *linconstant = SCIP_INVALID;
2236
2237 /* reference point does not satisfy linear inequalities */
2238 if( SCIPisFeasGT(scip, xcoef1 * refpointx - ycoef1 * refpointy - constant1, 0.0)
2239 || SCIPisFeasGT(scip, xcoef2 * refpointx - ycoef2 * refpointy - constant2, 0.0) )
2240 return;
2241
2242 /* compute minimal and maximal bounds on x and y for accepting the reference point */
2243 minx = lbx + 0.01 * (ubx-lbx);
2244 maxx = ubx - 0.01 * (ubx-lbx);
2245 miny = lby + 0.01 * (uby-lby);
2246 maxy = uby - 0.01 * (uby-lby);
2247
2248 /* check the reference point is in the interior of the domain */
2249 if( SCIPisLE(scip, refpointx, minx) || SCIPisGE(scip, refpointx, maxx)
2250 || SCIPisLE(scip, refpointy, miny) || SCIPisFeasGE(scip, refpointy, maxy) )
2251 return;
2252
2253 /* the sign of the x-coefficients of the two inequalities must be different; otherwise the convex or concave
2254 * envelope can be computed via SCIPcomputeBilinEnvelope1 for each inequality separately
2255 */
2256 if( (xcoef1 > 0) == (xcoef2 > 0) )
2257 return;
2258
2259 /* always consider xy without the bilinear coefficient */
2260 if( bilincoef < 0.0 )
2261 overestimate = !overestimate;
2262
2263 /* we use same notation as in "Convex envelopes of bivariate functions through the solution of KKT systems", 2016 */
2264 mi = xcoef1 / ycoef1;
2265 qi = -constant1 / ycoef1;
2266 mj = xcoef2 / ycoef2;
2267 qj = -constant2 / ycoef2;
2268
2269 /* mi, mj > 0 => underestimate; mi, mj < 0 => overestimate */
2270 if( SCIPisNegative(scip, mi) != overestimate || SCIPisNegative(scip, mj) != overestimate )
2271 return;
2272
2273 /* compute cut according to Locatelli 2016 */
2274 computeBilinEnvelope2(scip, refpointx, refpointy, mi, qi, mj, qj, &xi, &yi, &xj, &yj, &xcoef, &ycoef, &constant);
2275 assert(SCIPisRelEQ(scip, mi*xi + qi, yi));
2276 assert(SCIPisRelEQ(scip, mj*xj + qj, yj));
2277
2278 /* it might happen that (xi,yi) = (xj,yj) if the two lines intersect */
2279 if( SCIPisEQ(scip, xi, xj) && SCIPisEQ(scip, yi, yj) )
2280 return;
2281
2282 /* check whether projected points are in the interior */
2283 if( SCIPisLE(scip, xi, minx) || SCIPisGE(scip, xi, maxx) || SCIPisLE(scip, yi, miny) || SCIPisGE(scip, yi, maxy) )
2284 return;
2285 if( SCIPisLE(scip, xj, minx) || SCIPisGE(scip, xj, maxx) || SCIPisLE(scip, yj, miny) || SCIPisGE(scip, yj, maxy) )
2286 return;
2287
2288 *lincoefx = bilincoef * xcoef;
2289 *lincoefy = bilincoef * ycoef;
2290 *linconstant = bilincoef * constant;
2291
2292 /* cut needs to be tight at (vx,vy) and (xj,yj) */
2293 *success = SCIPisFeasEQ(scip, (*lincoefx)*xi + (*lincoefy)*yi + (*linconstant), bilincoef*xi*yi)
2294 && SCIPisFeasEQ(scip, (*lincoefx)*xj + (*lincoefy)*yj + (*linconstant), bilincoef*xj*yj);
2295
2296 #ifndef NDEBUG
2297 {
2298 SCIP_Real activity = (*lincoefx)*refpointx + (*lincoefy)*refpointy + (*linconstant);
2299
2300 /* cut needs to under- or overestimate the bilinear term at the reference point */
2301 if( bilincoef < 0.0 )
2302 overestimate = !overestimate;
2303
2304 if( overestimate )
2305 assert(SCIPisFeasGE(scip, activity, bilincoef*refpointx*refpointy));
2306 else
2307 assert(SCIPisFeasLE(scip, activity, bilincoef*refpointx*refpointy));
2308 }
2309 #endif
2310 }
2311