Scippy

SCIP

Solving Constraint Integer Programs

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