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