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