SCIP

    Solving Constraint Integer Programs

    sepa_rlt.c
    Go to the documentation of this file.
    1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    2/* */
    3/* This file is part of the program and library */
    4/* SCIP --- Solving Constraint Integer Programs */
    5/* */
    6/* Copyright (c) 2002-2026 Zuse Institute Berlin (ZIB) */
    7/* */
    8/* Licensed under the Apache License, Version 2.0 (the "License"); */
    9/* you may not use this file except in compliance with the License. */
    10/* You may obtain a copy of the License at */
    11/* */
    12/* http://www.apache.org/licenses/LICENSE-2.0 */
    13/* */
    14/* Unless required by applicable law or agreed to in writing, software */
    15/* distributed under the License is distributed on an "AS IS" BASIS, */
    16/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
    17/* See the License for the specific language governing permissions and */
    18/* limitations under the License. */
    19/* */
    20/* You should have received a copy of the Apache-2.0 license */
    21/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
    22/* */
    23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    24
    25/**@file sepa_rlt.c
    26 * @ingroup DEFPLUGINS_SEPA
    27 * @brief separator for cuts generated by Reformulation-Linearization-Technique (RLT)
    28 * @author Fabian Wegscheider
    29 * @author Ksenia Bestuzheva
    30 *
    31 * @todo implement the possibility to add extra auxiliary variables for RLT (like in DOI 10.1080/10556788.2014.916287)
    32 * @todo add RLT cuts for the product of equality constraints
    33 * @todo implement dynamic addition of RLT cuts during branching (see DOI 10.1007/s10898-012-9874-7)
    34 * @todo use SCIPvarIsBinary instead of SCIPvarGetType() == SCIP_VARTYPE_BINARY ?
    35 * @todo parameter maxusedvars seems arbitrary (too large for small problems; too small for large problems); something more adaptive we can do? (e.g., all variables with priority >= x% of highest prio)
    36 */
    37
    38/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
    39
    40#include "scip/sepa_rlt.h"
    41#include "scip/cons_nonlinear.h"
    42#include "scip/pub_lp.h"
    43#include "scip/expr_pow.h"
    45#include "scip/cutsel_hybrid.h"
    46
    47
    48#define SEPA_NAME "rlt"
    49#define SEPA_DESC "reformulation-linearization-technique separator"
    50#define SEPA_PRIORITY 10 /**< priority for separation */
    51#define SEPA_FREQ 0 /**< frequency for separating cuts; zero means to separate only in the root node */
    52#define SEPA_MAXBOUNDDIST 1.0 /**< maximal relative distance from the current node's dual bound to primal bound
    53 * compared to best node's dual bound for applying separation.*/
    54#define SEPA_USESSUBSCIP FALSE /**< does the separator use a secondary SCIP instance? */
    55#define SEPA_DELAY FALSE /**< should separation method be delayed, if other separators found cuts? */
    56
    57#define DEFAULT_MAXUNKNOWNTERMS 0 /**< maximum number of unknown bilinear terms a row can have to be used */
    58#define DEFAULT_MAXUSEDVARS 100 /**< maximum number of variables that will be used to compute rlt cuts */
    59#define DEFAULT_MAXNCUTS -1 /**< maximum number of cuts that will be added per round */
    60#define DEFAULT_MAXROUNDS 1 /**< maximum number of separation rounds per node (-1: unlimited) */
    61#define DEFAULT_MAXROUNDSROOT 10 /**< maximum number of separation rounds in the root node (-1: unlimited) */
    62#define DEFAULT_ONLYEQROWS FALSE /**< whether only equality rows should be used for rlt cuts */
    63#define DEFAULT_ONLYCONTROWS FALSE /**< whether only continuous rows should be used for rlt cuts */
    64#define DEFAULT_ONLYORIGINAL TRUE /**< whether only original variables and rows should be used for rlt cuts */
    65#define DEFAULT_USEINSUBSCIP FALSE /**< whether the separator should also be used in sub-scips */
    66#define DEFAULT_USEPROJECTION FALSE /**< whether the separator should first check projected rows */
    67#define DEFAULT_DETECTHIDDEN FALSE /**< whether implicit products should be detected and separated by McCormick */
    68#define DEFAULT_HIDDENRLT FALSE /**< whether RLT cuts should be added for hidden products */
    69#define DEFAULT_ADDTOPOOL TRUE /**< whether globally valid RLT cuts are added to the global cut pool */
    70
    71#define DEFAULT_GOODSCORE 1.0 /**< threshold for score of cut relative to best score to be considered good,
    72 * so that less strict filtering is applied */
    73#define DEFAULT_BADSCORE 0.5 /**< threshold for score of cut relative to best score to be discarded */
    74#define DEFAULT_OBJPARALWEIGHT 0.0 /**< weight of objective parallelism in cut score calculation */
    75#define DEFAULT_EFFICACYWEIGHT 1.0 /**< weight of efficacy in cut score calculation */
    76#define DEFAULT_DIRCUTOFFDISTWEIGHT 0.0 /**< weight of directed cutoff distance in cut score calculation */
    77#define DEFAULT_GOODMAXPARALL 0.1 /**< maximum parallelism for good cuts */
    78#define DEFAULT_MAXPARALL 0.1 /**< maximum parallelism for non-good cuts */
    79
    80#define MAXVARBOUND 1e+5 /**< maximum allowed variable bound for computing an RLT-cut */
    81
    82/*
    83 * Data structures
    84 */
    85
    86/** data object for pairs and triples of variables */
    87struct HashData
    88{
    89 SCIP_VAR* vars[3]; /**< variables in the pair or triple, used for hash comparison */
    90 int nvars; /**< number of variables */
    91 int nrows; /**< number of rows */
    92 int firstrow; /**< beginning of the corresponding row linked list */
    93};
    94typedef struct HashData HASHDATA;
    95
    96/** data structure representing an array of variables together with number of elements and size;
    97 * used for storing variables that are in some sense adjacent to a given variable
    98 */
    100{
    101 SCIP_VAR** adjacentvars; /**< adjacent vars */
    102 int nadjacentvars; /**< number of vars in adjacentvars */
    103 int sadjacentvars; /**< size of adjacentvars */
    104};
    106
    107/** separator data */
    108struct SCIP_SepaData
    109{
    110 SCIP_CONSHDLR* conshdlr; /**< nonlinear constraint handler */
    111 SCIP_Bool iscreated; /**< indicates whether the sepadata has been initialized yet */
    112 SCIP_Bool isinitialround; /**< indicates that this is the first round and original rows are used */
    113
    114 /* bilinear variables */
    115 SCIP_VAR** varssorted; /**< variables that occur in bilinear terms sorted by priority */
    116 SCIP_HASHMAP* bilinvardatamap; /**< maps each bilinear var to ADJACENTVARDATA containing vars appearing
    117 together with it in bilinear products */
    118 int* varpriorities; /**< priorities of variables */
    119 int nbilinvars; /**< total number of variables occurring in bilinear terms */
    120 int sbilinvars; /**< size of arrays for variables occurring in bilinear terms */
    121
    122 /* information about bilinear terms */
    123 int* eqauxexpr; /**< position of the auxexpr that is equal to the product (-1 if none) */
    124 int nbilinterms; /**< total number of bilinear terms */
    125
    126 /* parameters */
    127 int maxunknownterms; /**< maximum number of unknown bilinear terms a row can have to be used (-1: unlimited) */
    128 int maxusedvars; /**< maximum number of variables that will be used to compute rlt cuts (-1: unlimited) */
    129 int maxncuts; /**< maximum number of cuts that will be added per round (-1: unlimited) */
    130 int maxrounds; /**< maximum number of separation rounds per node (-1: unlimited) */
    131 int maxroundsroot; /**< maximum number of separation rounds in the root node (-1: unlimited) */
    132 SCIP_Bool onlyeqrows; /**< whether only equality rows should be used for rlt cuts */
    133 SCIP_Bool onlycontrows; /**< whether only continuous rows should be used for rlt cuts */
    134 SCIP_Bool onlyoriginal; /**< whether only original rows and variables should be used for rlt cuts */
    135 SCIP_Bool useinsubscip; /**< whether the separator should also be used in sub-scips */
    136 SCIP_Bool useprojection; /**< whether the separator should first check projected rows */
    137 SCIP_Bool detecthidden; /**< whether implicit products should be detected and separated by McCormick */
    138 SCIP_Bool hiddenrlt; /**< whether RLT cuts should be added for hidden products */
    139 SCIP_Bool addtopool; /**< whether globally valid RLT cuts are added to the global cut pool */
    140
    141 /* cut selection parameters */
    142 SCIP_Real goodscore; /**< threshold for score of cut relative to best score to be considered good,
    143 * so that less strict filtering is applied */
    144 SCIP_Real badscore; /**< threshold for score of cut relative to best score to be discarded */
    145 SCIP_Real objparalweight; /**< weight of objective parallelism in cut score calculation */
    146 SCIP_Real efficacyweight; /**< weight of efficacy in cut score calculation */
    147 SCIP_Real dircutoffdistweight;/**< weight of directed cutoff distance in cut score calculation */
    148 SCIP_Real goodmaxparall; /**< maximum parallelism for good cuts */
    149 SCIP_Real maxparall; /**< maximum parallelism for non-good cuts */
    150};
    151
    152/* a simplified representation of an LP row */
    154{
    155 const char* name; /**< name of the row */
    156 SCIP_Real* coefs; /**< coefficients */
    157 SCIP_VAR** vars; /**< variables */
    158 SCIP_Real rhs; /**< right hand side */
    159 SCIP_Real lhs; /**< left hand side */
    160 SCIP_Real cst; /**< constant */
    161 int nnonz; /**< number of nonzeroes */
    162 int size; /**< size of the coefs and vars arrays */
    163};
    165
    166/*
    167 * Local methods
    168 */
    169
    170/** returns TRUE iff both keys are equal
    171 *
    172 * two variable pairs/triples are equal if the variables are equal
    173 */
    174static
    175SCIP_DECL_HASHKEYEQ(hashdataKeyEqConss)
    176{ /*lint --e{715}*/
    177 HASHDATA* hashdata1;
    178 HASHDATA* hashdata2;
    179 int v;
    180
    181 hashdata1 = (HASHDATA*)key1;
    182 hashdata2 = (HASHDATA*)key2;
    183
    184 /* check data structure */
    185 assert(hashdata1->nvars == hashdata2->nvars);
    186 assert(hashdata1->firstrow != -1 || hashdata2->firstrow != -1);
    187
    188 for( v = hashdata1->nvars-1; v >= 0; --v )
    189 {
    190 /* tests if variables are equal */
    191 if( hashdata1->vars[v] != hashdata2->vars[v] )
    192 return FALSE;
    193
    194 assert(SCIPvarCompare(hashdata1->vars[v], hashdata2->vars[v]) == 0);
    195 }
    196
    197 /* if two hashdata objects have the same variables, then either one of them doesn't have a row list yet
    198 * (firstrow == -1) or they both point to the same row list
    199 */
    200 assert(hashdata1->firstrow == -1 || hashdata2->firstrow == -1 || hashdata1->firstrow == hashdata2->firstrow);
    201
    202 return TRUE;
    203}
    204
    205/** returns the hash value of the key */
    206static
    207SCIP_DECL_HASHKEYVAL(hashdataKeyValConss)
    208{ /*lint --e{715}*/
    209 HASHDATA* hashdata;
    210 int minidx;
    211 int mididx;
    212 int maxidx;
    213 int idx[3];
    214
    215 hashdata = (HASHDATA*)key;
    216 assert(hashdata != NULL);
    217 assert(hashdata->nvars == 3 || hashdata->nvars == 2);
    218
    219 idx[0] = SCIPvarGetIndex(hashdata->vars[0]);
    220 idx[1] = SCIPvarGetIndex(hashdata->vars[1]);
    221 idx[2] = SCIPvarGetIndex(hashdata->vars[hashdata->nvars - 1]);
    222
    223 minidx = MIN3(idx[0], idx[1], idx[2]);
    224 maxidx = MAX3(idx[0], idx[1], idx[2]);
    225 if( idx[0] == maxidx )
    226 mididx = MAX(idx[1], idx[2]);
    227 else
    228 mididx = MAX(idx[0], MIN(idx[1], idx[2]));
    229
    230 /* vars should already be sorted by index */
    231 assert(minidx <= mididx && mididx <= maxidx);
    232
    233 return SCIPhashFour(hashdata->nvars, minidx, mididx, maxidx);
    234}
    235
    236/** store a pair of adjacent variables */
    237static
    239 SCIP* scip, /**< SCIP data structure */
    240 SCIP_HASHMAP* adjvarmap, /**< hashmap mapping variables to their ADJACENTVARDATAs */
    241 SCIP_VAR** vars /**< variable pair to be stored */
    242 )
    243{
    244 int v1;
    245 int v2;
    246 int i;
    247 ADJACENTVARDATA* adjacentvardata;
    248
    249 assert(adjvarmap != NULL);
    250
    251 /* repeat for each variable of the new pair */
    252 for( v1 = 0; v1 < 2; ++v1 )
    253 {
    254 v2 = 1 - v1;
    255
    256 /* look for data of the first variable */
    257 adjacentvardata = (ADJACENTVARDATA*) SCIPhashmapGetImage(adjvarmap, (void*)(size_t) SCIPvarGetIndex(vars[v1]));
    258
    259 /* if the first variable has not been added to adjvarmap yet, add it here */
    260 if( adjacentvardata == NULL )
    261 {
    262 SCIP_CALL( SCIPallocClearBlockMemory(scip, &adjacentvardata) );
    263 SCIP_CALL( SCIPhashmapInsert(adjvarmap, (void*)(size_t) SCIPvarGetIndex(vars[v1]), adjacentvardata) );
    264 }
    265
    266 assert(adjacentvardata != NULL);
    267
    268 /* look for second variable in adjacentvars of the first variable */
    269 if( adjacentvardata->adjacentvars == NULL )
    270 {
    271 /* we don't know how many adjacent vars there will be - take a guess */
    272 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &adjacentvardata->adjacentvars, 4) );
    273 adjacentvardata->adjacentvars[0] = vars[v2];
    274 ++adjacentvardata->nadjacentvars;
    275 adjacentvardata->sadjacentvars = 4;
    276 }
    277 else
    278 {
    279 SCIP_Bool found;
    280 int pos2;
    281
    282 found = SCIPsortedvecFindPtr((void**) adjacentvardata->adjacentvars, SCIPvarComp, vars[v2],
    283 adjacentvardata->nadjacentvars, &pos2);
    284
    285 /* add second var to adjacentvardata->adjacentvars, if not already added */
    286 if( !found )
    287 {
    288 /* ensure size of adjacentvardata->adjacentvars */
    289 SCIP_CALL( SCIPensureBlockMemoryArray(scip, &adjacentvardata->adjacentvars, &adjacentvardata->sadjacentvars,
    290 adjacentvardata->nadjacentvars + 1) );
    291
    292 /* insert second var at the correct position */
    293 for( i = adjacentvardata->nadjacentvars; i > pos2; --i )
    294 {
    295 adjacentvardata->adjacentvars[i] = adjacentvardata->adjacentvars[i-1];
    296 }
    297 adjacentvardata->adjacentvars[pos2] = vars[v2];
    298 ++adjacentvardata->nadjacentvars;
    299 }
    300 }
    301
    302 /* if this is a self-adjacent var, only need to add the connection once */
    303 if( vars[v1] == vars[v2] )
    304 break;
    305 }
    306
    307 return SCIP_OKAY;
    308}
    309
    310/** returns the array of adjacent variables for a given variable */
    311static
    313 SCIP_HASHMAP* adjvarmap, /**< hashmap mapping variables to their ADJACENTVARDATAs */
    314 SCIP_VAR* var, /**< variable */
    315 int* nadjacentvars /**< buffer to store the number of variables in the returned array */
    316 )
    317{
    318 ADJACENTVARDATA* adjacentvardata;
    319
    320 assert(adjvarmap != NULL);
    321
    322 *nadjacentvars = 0;
    323 adjacentvardata = (ADJACENTVARDATA*) SCIPhashmapGetImage(adjvarmap, (void*)(size_t) SCIPvarGetIndex(var));
    324
    325 if( adjacentvardata == NULL )
    326 return NULL;
    327
    328 *nadjacentvars = adjacentvardata->nadjacentvars;
    329
    330 return adjacentvardata->adjacentvars;
    331}
    332
    333/** frees all ADJACENTVARDATAs stored in a hashmap */
    334static
    336 SCIP* scip, /**< SCIP data structure */
    337 SCIP_HASHMAP* adjvarmap /**< hashmap mapping variables to their ADJACENTVARDATAs */
    338 )
    339{
    340 int i;
    341 SCIP_HASHMAPENTRY* entry;
    342 ADJACENTVARDATA* adjacentvardata;
    343
    344 assert(adjvarmap != NULL);
    345
    346 for( i = 0; i < SCIPhashmapGetNEntries(adjvarmap); ++i )
    347 {
    348 entry = SCIPhashmapGetEntry(adjvarmap, i);
    349
    350 if( entry == NULL )
    351 continue;
    352
    353 adjacentvardata = (ADJACENTVARDATA*) SCIPhashmapEntryGetImage(entry);
    354
    355 /* if adjacentvardata has been added to the hashmap, it can't be empty */
    356 assert(adjacentvardata->adjacentvars != NULL);
    357
    358 SCIPfreeBlockMemoryArray(scip, &adjacentvardata->adjacentvars, adjacentvardata->sadjacentvars);
    359 SCIPfreeBlockMemory(scip, &adjacentvardata);
    360 }
    361}
    362
    363/** free separator data */
    364static
    366 SCIP* scip, /**< SCIP data structure */
    367 SCIP_SEPADATA* sepadata /**< separation data */
    368 )
    369{ /*lint --e{715}*/
    370 int i;
    371
    372 assert(sepadata->iscreated);
    373
    374 if( sepadata->nbilinvars != 0 )
    375 {
    376 /* release bilinvars that were captured for rlt and free all related arrays */
    377
    378 /* if there are bilinear vars, some of them must also participate in the same product */
    379 assert(sepadata->bilinvardatamap != NULL);
    380
    381 clearVarAdjacency(scip, sepadata->bilinvardatamap);
    382
    383 for( i = 0; i < sepadata->nbilinvars; ++i )
    384 {
    385 assert(sepadata->varssorted[i] != NULL);
    386 SCIP_CALL( SCIPreleaseVar(scip, &(sepadata->varssorted[i])) );
    387 }
    388
    389 SCIPhashmapFree(&sepadata->bilinvardatamap);
    390 SCIPfreeBlockMemoryArray(scip, &sepadata->varssorted, sepadata->sbilinvars);
    391 SCIPfreeBlockMemoryArray(scip, &sepadata->varpriorities, sepadata->sbilinvars);
    392 sepadata->nbilinvars = 0;
    393 sepadata->sbilinvars = 0;
    394 }
    395
    396 /* free the remaining array */
    397 if( sepadata->nbilinterms > 0 )
    398 {
    399 SCIPfreeBlockMemoryArray(scip, &sepadata->eqauxexpr, sepadata->nbilinterms);
    400 }
    401
    402 sepadata->iscreated = FALSE;
    403
    404 return SCIP_OKAY;
    405}
    406
    407/** creates and returns rows of original linear constraints */
    408static
    410 SCIP* scip, /**< SCIP data structure */
    411 SCIP_ROW*** rows, /**< buffer to store the rows */
    412 int* nrows /**< buffer to store the number of linear rows */
    413 )
    414{
    415 SCIP_CONS** conss;
    416 int nconss;
    417 int i;
    418
    419 assert(rows != NULL);
    420 assert(nrows != NULL);
    421
    422 conss = SCIPgetConss(scip);
    423 nconss = SCIPgetNConss(scip);
    424 *nrows = 0;
    425
    426 SCIP_CALL( SCIPallocBufferArray(scip, rows, nconss) );
    427
    428 for( i = 0; i < nconss; ++i )
    429 {
    430 SCIP_ROW *row;
    431
    432 row = SCIPconsGetRow(scip, conss[i]);
    433
    434 if( row != NULL )
    435 {
    436 (*rows)[*nrows] = row;
    437 ++*nrows;
    438 }
    439 }
    440
    441 return SCIP_OKAY;
    442}
    443
    444/** fills an array of rows suitable for RLT cut generation */
    445static
    447 SCIP* scip, /**< SCIP data structure */
    448 SCIP_SEPA* sepa, /**< separator */
    449 SCIP_SEPADATA* sepadata, /**< separator data */
    450 SCIP_ROW** prob_rows, /**< problem rows */
    451 SCIP_ROW** rows, /**< an array to be filled with suitable rows */
    452 int* nrows, /**< buffer to store the number of suitable rows */
    453 SCIP_HASHMAP* row_to_pos, /**< hashmap linking row indices to positions in rows */
    454 SCIP_Bool allowlocal /**< are local rows allowed? */
    455 )
    456{
    457 int new_nrows;
    458 int r;
    459 int j;
    460 SCIP_Bool iseqrow;
    461 SCIP_COL** cols;
    462 SCIP_Bool iscontrow;
    463
    464 new_nrows = 0;
    465
    466 for( r = 0; r < *nrows; ++r )
    467 {
    468 iseqrow = SCIPisEQ(scip, SCIProwGetLhs(prob_rows[r]), SCIProwGetRhs(prob_rows[r]));
    469
    470 /* if equality rows are requested, only those can be used */
    471 if( sepadata->onlyeqrows && !iseqrow )
    472 continue;
    473
    474 /* if global cuts are requested, only globally valid rows can be used */
    475 if( !allowlocal && SCIProwIsLocal(prob_rows[r]) )
    476 continue;
    477
    478 /* if continuous rows are requested, only those can be used */
    479 if( sepadata->onlycontrows )
    480 {
    481 cols = SCIProwGetCols(prob_rows[r]);
    482 iscontrow = TRUE;
    483
    484 /* check row for integral variables */
    485 for( j = 0; j < SCIProwGetNNonz(prob_rows[r]); ++j )
    486 {
    487 if( SCIPcolIsIntegral(cols[j]) )
    488 {
    489 iscontrow = FALSE;
    490 break;
    491 }
    492 }
    493
    494 if( !iscontrow )
    495 continue;
    496 }
    497
    498 /* don't try to use rows that have been generated by the RLT separator */
    499 if( SCIProwGetOriginSepa(prob_rows[r]) == sepa )
    500 continue;
    501
    502 /* if we are here, the row has passed all checks and should be added to rows */
    503 rows[new_nrows] = prob_rows[r];
    504 SCIP_CALL( SCIPhashmapSetImageInt(row_to_pos, (void*)(size_t)SCIProwGetIndex(prob_rows[r]), new_nrows) ); /*lint !e571 */
    505 ++new_nrows;
    506 }
    507
    508 *nrows = new_nrows;
    509
    510 return SCIP_OKAY;
    511}
    512
    513/** make sure that the arrays in sepadata are large enough to store information on n variables */
    514static
    516 SCIP* scip, /**< SCIP data structure */
    517 SCIP_SEPADATA* sepadata, /**< separator data */
    518 int n /**< number of variables that we need to store */
    519 )
    520{
    521 int newsize;
    522
    523 /* check whether array is large enough */
    524 if( n <= sepadata->sbilinvars )
    525 return SCIP_OKAY;
    526
    527 /* compute new size */
    528 newsize = SCIPcalcMemGrowSize(scip, n);
    529 assert(n <= newsize);
    530
    531 /* realloc arrays */
    532 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &sepadata->varssorted, sepadata->sbilinvars, newsize) );
    533 SCIP_CALL( SCIPreallocBlockMemoryArray(scip, &sepadata->varpriorities, sepadata->sbilinvars, newsize) );
    534
    535 sepadata->sbilinvars = newsize;
    536
    537 return SCIP_OKAY;
    538}
    539
    540/** saves variables x and y to separator data and stores information about their connection
    541 *
    542 * variables must be captured separately
    543 */
    544static
    546 SCIP* scip, /**< SCIP data structure */
    547 SCIP_SEPADATA* sepadata, /**< separator data */
    548 SCIP_VAR* x, /**< x variable */
    549 SCIP_VAR* y, /**< y variable */
    550 SCIP_HASHMAP* varmap, /**< hashmap linking var index to position */
    551 int nlocks /**< number of locks */
    552 )
    553{
    554 int xpos;
    555 int ypos;
    556 int xidx;
    557 int yidx;
    558 SCIP_VAR* vars[2];
    559
    560 if( sepadata->bilinvardatamap == NULL )
    561 {
    562 int varmapsize;
    563 int nvars;
    564
    565 /* the number of variables participating in bilinear products cannot exceed twice the number of bilinear terms;
    566 * however, if we detect hidden products, the number of terms is yet unknown, so use the number of variables
    567 */
    568 nvars = SCIPgetNVars(scip);
    569 varmapsize = sepadata->detecthidden ? nvars : MIN(nvars, sepadata->nbilinterms * 2);
    570
    571 SCIP_CALL( SCIPhashmapCreate(&sepadata->bilinvardatamap, SCIPblkmem(scip), varmapsize) );
    572 }
    573
    574 xidx = SCIPvarGetIndex(x);
    575 yidx = SCIPvarGetIndex(y);
    576
    577 xpos = SCIPhashmapGetImageInt(varmap, (void*)(size_t) xidx); /*lint !e571 */
    578
    579 if( xpos == INT_MAX )
    580 {
    581 /* add x to sepadata and initialise its priority */
    582 SCIP_CALL( SCIPhashmapInsertInt(varmap, (void*)(size_t) xidx, sepadata->nbilinvars) ); /*lint !e571*/
    583 SCIP_CALL( ensureVarsSize(scip, sepadata, sepadata->nbilinvars + 1) );
    584 sepadata->varssorted[sepadata->nbilinvars] = x;
    585 sepadata->varpriorities[sepadata->nbilinvars] = 0;
    586 xpos = sepadata->nbilinvars;
    587 ++sepadata->nbilinvars;
    588 }
    589
    590 assert(xpos >= 0 && xpos < sepadata->nbilinvars );
    591 assert(xpos == SCIPhashmapGetImageInt(varmap, (void*)(size_t) xidx)); /*lint !e571 */
    592
    593 /* add locks to priority of x */
    594 sepadata->varpriorities[xpos] += nlocks;
    595
    596 if( xidx != yidx )
    597 {
    598 ypos = SCIPhashmapGetImageInt(varmap, (void*)(size_t) yidx); /*lint !e571 */
    599
    600 if( ypos == INT_MAX )
    601 {
    602 /* add y to sepadata and initialise its priority */
    603 SCIP_CALL( SCIPhashmapInsertInt(varmap, (void*)(size_t) yidx, sepadata->nbilinvars) ); /*lint !e571*/
    604 SCIP_CALL( ensureVarsSize(scip, sepadata, sepadata->nbilinvars + 1) );
    605 sepadata->varssorted[sepadata->nbilinvars] = y;
    606 sepadata->varpriorities[sepadata->nbilinvars] = 0;
    607 ypos = sepadata->nbilinvars;
    608 ++sepadata->nbilinvars;
    609 }
    610
    611 assert(ypos >= 0 && ypos < sepadata->nbilinvars);
    612 assert(ypos == SCIPhashmapGetImageInt(varmap, (void*)(size_t) yidx)); /*lint !e571 */
    613
    614 /* add locks to priority of y */
    615 sepadata->varpriorities[ypos] += nlocks;
    616 }
    617
    618 /* remember the connection between x and y */
    619 vars[0] = x;
    620 vars[1] = y;
    621 SCIP_CALL( addAdjacentVars(scip, sepadata->bilinvardatamap, vars) );
    622
    623 return SCIP_OKAY;
    624}
    625
    626/** extract a bilinear product from two linear relations, if possible
    627 *
    628 * First, the two given rows are brought to the form:
    629 * \f[
    630 * a_1x + b_1w + c_1y \leq/\geq d_1,\\
    631 * a_2x + b_2w + c_2y \leq/\geq d_2,
    632 * \f]
    633 * where \f$ a_1a_2 \leq 0 \f$ and the first implied relation is enabled when \f$ x = 1 \f$
    634 * and the second when \f$ x = 0 \f$, and \f$ b_1, b_2 > 0 \f$, the product relation can be written as:
    635 * \f[
    636 * \frac{b_1b_2w + (b_2(a_1 - d_1) + b_1d_2)x + b_1c_2y - b_1d_2}{b_1c_2 - c_1b_2} \leq/\geq xy.
    637 * \f]
    638 * The inequality sign in the product relation is similar to that in the given linear relations if
    639 * \f$ b_1c_2 - c_1b_2 > 0 \f$ and opposite if \f$ b_1c_2 - c_1b_2 > 0 \f$.
    640 *
    641 * To obtain this formula, the given relations are first multiplied by scaling factors \f$ \alpha \f$
    642 * and \f$ \beta \f$, which is necessary in order for the solution to always exist, and written as
    643 * implications:
    644 * \f{align}{
    645 * x = 1 & ~\Rightarrow~ \alpha b_1w + \alpha c_1y \leq/\geq \alpha(d_1 - a_1), \\
    646 * x = 0 & ~\Rightarrow~ \beta b_2w + \beta c_2y \leq/\geq \beta d_2.
    647 * \f}
    648 * Then a linear system is solved which ensures that the coefficients of the two implications of the product
    649 * relation are equal to the corresponding coefficients in the linear relations.
    650 * If the product relation is written as:
    651 * \f[
    652 * Ax + Bw + Cy + D \leq/\geq xy,
    653 * \f]
    654 * then the system is
    655 * \f[
    656 * B = \alpha b_1, ~C - 1 = \alpha c_1, ~D+A = \alpha(a_1-d_1),\\
    657 * B = \beta b_2, ~C = \beta c_2, ~D = -\beta d_2.
    658 * \f]
    659 */
    660static
    662 SCIP* scip, /**< SCIP data structure */
    663 SCIP_SEPADATA* sepadata, /**< separator data */
    664 SCIP_VAR** vars_xwy, /**< 3 variables involved in the inequalities in the order x,w,y */
    665 SCIP_Real* coefs1, /**< coefficients of the first inequality (always implied, i.e. has x) */
    666 SCIP_Real* coefs2, /**< coefficients of the second inequality (can be unconditional) */
    667 SCIP_Real d1, /**< side of the first inequality */
    668 SCIP_Real d2, /**< side of the second inequality */
    669 SCIP_SIDETYPE sidetype1, /**< side type (lhs or rls) in the first inequality */
    670 SCIP_SIDETYPE sidetype2, /**< side type (lhs or rhs) in the second inequality */
    671 SCIP_HASHMAP* varmap, /**< variable map */
    672 SCIP_Bool f /**< the first relation is an implication x == f */
    673 )
    674{
    675 SCIP_Real mult;
    676
    677 /* coefficients and constant of the auxexpr */
    678 SCIP_Real A; /* coefficient of x */
    679 SCIP_Real B; /* coefficient of w */
    680 SCIP_Real C; /* coefficient of y */
    681 SCIP_Real D; /* constant */
    682
    683 /* variables */
    684 SCIP_VAR* w;
    685 SCIP_VAR* x;
    686 SCIP_VAR* y;
    687
    688 /* does auxexpr overestimate the product? */
    689 SCIP_Bool overestimate;
    690
    691 /* coefficients in given relations: a for x, b for w, c for y; 1 and 2 for 1st and 2nd relation, respectively */
    692 SCIP_Real a1 = coefs1[0];
    693 SCIP_Real b1 = coefs1[1];
    694 SCIP_Real c1 = coefs1[2];
    695 SCIP_Real a2 = coefs2[0];
    696 SCIP_Real b2 = coefs2[1];
    697 SCIP_Real c2 = coefs2[2];
    698
    699 x = vars_xwy[0];
    700 w = vars_xwy[1];
    701 y = vars_xwy[2];
    702
    703 /* check given linear relations and decide if to continue */
    704
    705 assert(SCIPvarGetType(x) == SCIP_VARTYPE_BINARY); /* x must be binary */
    706 assert(a1 != 0.0); /* the first relation is always conditional */
    707 assert(b1 != 0.0 || b2 != 0.0); /* at least one w coefficient must be nonzero */
    708
    709 SCIPdebugMsg(scip, "Extracting product from two implied relations:\n");
    710 SCIPdebugMsg(scip, "Relation 1: <%s> == %u => %g<%s> + %g<%s> %s %g\n", SCIPvarGetName(x), f, b1,
    711 SCIPvarGetName(w), c1, SCIPvarGetName(y), sidetype1 == SCIP_SIDETYPE_LEFT ? ">=" : "<=",
    712 f ? d1 - a1 : d1);
    713 SCIPdebugMsg(scip, "Relation 2: <%s> == %d => %g<%s> + %g<%s> %s %g\n", SCIPvarGetName(x), !f, b2,
    714 SCIPvarGetName(w), c2, SCIPvarGetName(y), sidetype2 == SCIP_SIDETYPE_LEFT ? ">=" : "<=",
    715 f ? d2 : d2 - a2);
    716
    717 /* cannot use a global bound on x to detect a product */
    718 if( (b1 == 0.0 && c1 == 0.0) || (b2 == 0.0 && c2 == 0.0) )
    719 return SCIP_OKAY;
    720
    721 /* cannot use a global bound on y to detect a non-redundant product relation */
    722 if( a2 == 0.0 && b2 == 0.0 ) /* only check the 2nd relation because the 1st at least has x */
    723 {
    724 SCIPdebugMsg(scip, "Ignoring a global bound on y\n");
    725 return SCIP_OKAY;
    726 }
    727
    728 SCIPdebugMsg(scip, "binary var = <%s>, product of its coefs: %g\n", SCIPvarGetName(x), a1*a2);
    729
    730 /* rewrite the linear relations in a standard form:
    731 * a1x + b1w + c1y <=/>= d1,
    732 * a2x + b2w + c2y <=/>= d2,
    733 * where b1 > 0, b2 > 0 and first implied relation is activated when x == 1
    734 */
    735
    736 /* if needed, multiply the rows by -1 so that coefs of w are positive */
    737 if( b1 < 0 )
    738 {
    739 a1 *= -1.0;
    740 b1 *= -1.0;
    741 c1 *= -1.0;
    742 d1 *= -1.0;
    743 sidetype1 = sidetype1 == SCIP_SIDETYPE_LEFT ? SCIP_SIDETYPE_RIGHT : SCIP_SIDETYPE_LEFT;
    744 }
    745 if( b2 < 0 )
    746 {
    747 a2 *= -1.0;
    748 b2 *= -1.0;
    749 c2 *= -1.0;
    750 d2 *= -1.0;
    751 sidetype2 = sidetype2 == SCIP_SIDETYPE_LEFT ? SCIP_SIDETYPE_RIGHT : SCIP_SIDETYPE_LEFT;
    752 }
    753
    754 /* the linear relations imply a product only if the inequality signs are similar */
    755 if( sidetype1 != sidetype2 )
    756 return SCIP_OKAY;
    757
    758 /* when b1c2 = b2c1, the linear relations do not imply a product relation */
    759 if( SCIPisRelEQ(scip, b2*c1, c2*b1) )
    760 {
    761 SCIPdebugMsg(scip, "Ignoring a pair of linear relations because b1c2 = b2c1\n");
    762 return SCIP_OKAY;
    763 }
    764
    765 if( !f )
    766 {
    767 /* swap the linear relations so that the relation implied by x == TRUE goes first */
    768 SCIPswapReals(&a1, &a2);
    769 SCIPswapReals(&b1, &b2);
    770 SCIPswapReals(&c1, &c2);
    771 SCIPswapReals(&d1, &d2);
    772 }
    773
    774 /* all conditions satisfied, we can extract the product and write it as:
    775 * (1/(b1c2 - c1b2))*(b1b2w + (b2(a1 - d1) + b1d2)x + b1c2y - b1d2) >=/<= xy,
    776 * where the inequality sign in the product relation is similar to that in the given linear relations
    777 * if b1c2 - c1b2 > 0 and opposite if b1c2 - c1b2 > 0
    778 */
    779
    780 /* compute the multiplier */
    781 mult = 1/(b1*c2 - c1*b2);
    782
    783 /* determine the inequality sign; only check sidetype1 because sidetype2 is equal to it */
    784 overestimate = (sidetype1 == SCIP_SIDETYPE_LEFT && mult > 0.0) || (sidetype1 == SCIP_SIDETYPE_RIGHT && mult < 0.0);
    785
    786 SCIPdebugMsg(scip, "found suitable implied rels (w,x,y): %g<%s> + %g<%s> + %g<%s> <= %g\n", a1,
    788 SCIPdebugMsg(scip, " and %g<%s> + %g<%s> + %g<%s> <= %g\n", a2, SCIPvarGetName(x),
    789 b2, SCIPvarGetName(w), c2, SCIPvarGetName(y), d2);
    790
    791 /* compute the coefficients for x, w and y and the constant in auxexpr */
    792 A = (b2*a1 - d1*b2 + d2*b1)*mult;
    793 B = b1*b2*mult;
    794 C = b1*c2*mult;
    795 D = -b1*d2*mult;
    796
    797 SCIPdebugMsg(scip, "product: <%s><%s> %s %g<%s> + %g<%s> + %g<%s> + %g\n", SCIPvarGetName(x), SCIPvarGetName(y),
    798 overestimate ? "<=" : ">=", A, SCIPvarGetName(x), B, SCIPvarGetName(w), C, SCIPvarGetName(y), D);
    799
    800 SCIP_CALL( addProductVars(scip, sepadata, x, y, varmap, 1) );
    801 SCIP_CALL( SCIPinsertBilinearTermImplicitNonlinear(scip, sepadata->conshdlr, x, y, w, A, C, B, D, overestimate) );
    802
    803 return SCIP_OKAY;
    804}
    805
    806/** convert an implied bound: `binvar` = `binval` &rArr; `implvar` &le;/&ge; `implbnd` into a big-M constraint */
    807static
    809 SCIP* scip, /**< SCIP data structure */
    810 SCIP_VAR** vars_xwy, /**< variables in order x,w,y */
    811 int binvarpos, /**< position of binvar in vars_xwy */
    812 int implvarpos, /**< position of implvar in vars_xwy */
    813 SCIP_BOUNDTYPE bndtype, /**< type of implied bound */
    814 SCIP_Bool binval, /**< value of binvar which implies the bound */
    815 SCIP_Real implbnd, /**< value of the implied bound */
    816 SCIP_Real* coefs, /**< coefficients of the big-M constraint */
    817 SCIP_Real* side /**< side of the big-M constraint */
    818 )
    819{
    820 SCIP_VAR* implvar;
    821 SCIP_Real globbnd;
    822
    823 assert(vars_xwy != NULL);
    824 assert(coefs != NULL);
    825 assert(side != NULL);
    826 assert(binvarpos != implvarpos);
    827
    828 implvar = vars_xwy[implvarpos];
    829 globbnd = bndtype == SCIP_BOUNDTYPE_LOWER ? SCIPvarGetLbGlobal(implvar) : SCIPvarGetUbGlobal(implvar);
    830
    831 /* Depending on the bound type and binval, there are four possibilities:
    832 * binvar == 1 => implvar >= implbnd <=> (implvar^l - implbnd)binvar + implvar >= implvar^l;
    833 * binvar == 0 => implvar >= implbnd <=> (implbnd - implvar^l)binvar + implvar >= implbnd;
    834 * binvar == 1 => implvar <= implbnd <=> (implvar^u - implbnd)binvar + implvar <= implvar^u;
    835 * binvar == 0 => implvar <= implbnd <=> (implbnd - implvar^u)binvar + implvar <= implbnd.
    836 */
    837
    838 coefs[0] = 0.0;
    839 coefs[1] = 0.0;
    840 coefs[2] = 0.0;
    841 coefs[binvarpos] = binval ? globbnd - implbnd : implbnd - globbnd;
    842 coefs[implvarpos] = 1.0;
    843 *side = binval ? globbnd : implbnd;
    844
    845 SCIPdebugMsg(scip, "Got an implied relation with binpos = %d, implpos = %d, implbnd = %g, "
    846 "bnd type = %s, binval = %u, glbbnd = %g\n", binvarpos, implvarpos, implbnd,
    847 bndtype == SCIP_BOUNDTYPE_LOWER ? "lower" : "upper", binval, globbnd);
    848 SCIPdebugMsg(scip, "Constructed big-M: %g*bvar + implvar %s %g\n", coefs[binvarpos],
    849 bndtype == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", *side);
    850}
    851
    852/** extract products from a relation given by coefs1, vars, side1 and sidetype1 and
    853 * implied bounds of the form `binvar` = `!f` &rArr; `implvar` &ge;/&le; `implbnd`
    854 */
    855static
    857 SCIP* scip, /**< SCIP data structure */
    858 SCIP_SEPADATA* sepadata, /**< separator data */
    859 SCIP_Real* coefs1, /**< coefficients of the first linear relation */
    860 SCIP_VAR** vars_xwy, /**< variables in the order x, w, y */
    861 SCIP_Real side1, /**< side of the first relation */
    862 SCIP_SIDETYPE sidetype1, /**< is the left or right hand side given for the first relation? */
    863 int binvarpos, /**< position of the indicator variable in the vars_xwy array */
    864 int implvarpos, /**< position of the variable that is bounded */
    865 SCIP_HASHMAP* varmap, /**< variable map */
    866 SCIP_Bool f /**< the value of x that activates the first relation */
    867 )
    868{
    869 SCIP_Real coefs2[3] = { 0., 0., 0. };
    870 SCIP_Real impllb;
    871 SCIP_Real implub;
    872 SCIP_VAR* binvar;
    873 SCIP_VAR* implvar;
    874 SCIP_Real side2;
    875 int i;
    876 SCIP_Bool binvals[2] = {!f, f};
    877
    878 assert(binvarpos != implvarpos);
    879 assert(implvarpos != 0); /* implied variable must be continuous, therefore it can't be x */
    880
    881 binvar = vars_xwy[binvarpos];
    882 implvar = vars_xwy[implvarpos];
    883
    884 assert(SCIPvarGetType(binvar) == SCIP_VARTYPE_BINARY);
    885 assert(SCIPvarGetType(implvar) != SCIP_VARTYPE_BINARY);
    886
    887 /* loop over binvals; if binvar is x (case binvarpos == 0), then we want to use only implications from
    888 * binvar == !f (which is the option complementing the first relation, which is implied from f); if
    889 * binvar is not x, this doesn't matter since the implbnd doesn't depend on x, therefore try both !f and f
    890 */
    891 for( i = 0; i < (binvarpos == 0 ? 1 : 2); ++i )
    892 {
    893 /* get implications binvar == binval => implvar <=/>= implbnd */
    894 SCIPvarGetImplicVarBounds(binvar, binvals[i], implvar, &impllb, &implub);
    895
    896 if( impllb != SCIP_INVALID ) /*lint !e777*/
    897 {
    898 /* write the implied bound as a big-M constraint */
    899 implBndToBigM(scip, vars_xwy, binvarpos, implvarpos, SCIP_BOUNDTYPE_LOWER, binvals[i], impllb, coefs2, &side2);
    900
    901 SCIP_CALL( extractProducts(scip, sepadata, vars_xwy, coefs1, coefs2, side1, side2, sidetype1,
    902 SCIP_SIDETYPE_LEFT, varmap, f) );
    903 }
    904
    905 if( implub != SCIP_INVALID ) /*lint !e777*/
    906 {
    907 /* write the implied bound as a big-M constraint */
    908 implBndToBigM(scip, vars_xwy, binvarpos, implvarpos, SCIP_BOUNDTYPE_UPPER, binvals[i], implub, coefs2, &side2);
    909
    910 SCIP_CALL( extractProducts(scip, sepadata, vars_xwy, coefs1, coefs2, side1, side2, sidetype1,
    911 SCIP_SIDETYPE_RIGHT, varmap, f) );
    912 }
    913 }
    914
    915 return SCIP_OKAY;
    916}
    917
    918/** extract products from a relation given by `coefs1`, `vars_xwy`, `side1` and `sidetype1` and
    919 * cliques containing `vars_xwy[varpos1]` and `vars_xwy[varpos2]`
    920 */
    921static
    923 SCIP* scip, /**< SCIP data structure */
    924 SCIP_SEPADATA* sepadata, /**< separator data */
    925 SCIP_Real* coefs1, /**< coefficients of the first linear relation */
    926 SCIP_VAR** vars_xwy, /**< variables of the first relation in the order x, w, y */
    927 SCIP_Real side1, /**< side of the first relation */
    928 SCIP_SIDETYPE sidetype1, /**< is the left or right hand side given for the first relation? */
    929 int varpos1, /**< position of the first variable in the vars_xwy array */
    930 int varpos2, /**< position of the second variable in the vars_xwy array */
    931 SCIP_HASHMAP* varmap, /**< variable map */
    932 SCIP_Bool f /**< the value of x that activates the first relation */
    933 )
    934{
    935 SCIP_Real coefs2[3] = { 0., 0., 0. };
    936 SCIP_VAR* var1;
    937 SCIP_VAR* var2;
    938 SCIP_Real side2;
    939 int i;
    940 int imax;
    941 SCIP_Bool binvals[2] = {!f, f};
    942
    943 var1 = vars_xwy[varpos1];
    944 var2 = vars_xwy[varpos2];
    945
    946 /* this decides whether we do one or two iterations of the loop for binvals: if var1
    947 * or var2 is x, we only want cliques with x = !f (which is the option complementing
    948 * the first relation, which is implied from f); otherwise this doesn't matter since
    949 * the clique doesn't depend on x, therefore try both !f and f
    950 */
    951 imax = (varpos1 == 0 || varpos2 == 0) ? 1 : 2;
    952
    953 assert(SCIPvarGetType(var1) == SCIP_VARTYPE_BINARY);
    954 assert(SCIPvarGetType(var2) == SCIP_VARTYPE_BINARY);
    955
    956 for( i = 0; i < imax; ++i )
    957 {
    958 /* if var1=TRUE and var2=TRUE are in a clique (binvals[i] == TRUE), the relation var1 + var2 <= 1 is implied
    959 * if var1=FALSE and var2=TRUE are in a clique (binvals[i] == FALSE), the relation (1 - var1) + var2 <= 1 is implied
    960 */
    961 if( SCIPvarsHaveCommonClique(var1, binvals[i], var2, TRUE, TRUE) )
    962 {
    963 SCIPdebugMsg(scip, "vars %s<%s> and <%s> are in a clique\n", binvals[i] ? "" : "!", SCIPvarGetName(var1), SCIPvarGetName(var2));
    964 coefs2[varpos1] = binvals[i] ? 1.0 : -1.0;
    965 coefs2[varpos2] = 1.0;
    966 side2 = binvals[i] ? 1.0 : 0.0;
    967
    968 SCIP_CALL( extractProducts(scip, sepadata, vars_xwy, coefs1, coefs2, side1, side2, sidetype1,
    969 SCIP_SIDETYPE_RIGHT, varmap, f) );
    970 }
    971
    972 /* if var1=TRUE and var2=FALSE are in the same clique, the relation var1 + (1-var2) <= 1 is implied
    973 * if var1=FALSE and var2=FALSE are in the same clique, the relation (1-var1) + (1-var2) <= 1 is implied
    974 */
    975 if( SCIPvarsHaveCommonClique(var1, binvals[i], var2, FALSE, TRUE) )
    976 {
    977 SCIPdebugMsg(scip, "vars %s<%s> and !<%s> are in a clique\n", binvals[i] ? "" : "!", SCIPvarGetName(var1), SCIPvarGetName(var2));
    978 coefs2[varpos1] = binvals[i] ? 1.0 : -1.0;
    979 coefs2[varpos2] = -1.0;
    980 side2 = binvals[i] ? 0.0 : -1.0;
    981
    982 SCIP_CALL( extractProducts(scip, sepadata, vars_xwy, coefs1, coefs2, side1, side2, sidetype1,
    983 SCIP_SIDETYPE_RIGHT, varmap, f) );
    984 }
    985 }
    986
    987 return SCIP_OKAY;
    988}
    989
    990
    991/** extract products from a relation given by `coefs1`, `vars`, `side1` and `sidetype1` and unconditional relations
    992 * (inequalities with 2 nonzeros) containing `vars[varpos1]` and `vars[varpos2]`
    993 */
    994static
    996 SCIP* scip, /**< SCIP data structure */
    997 SCIP_SEPADATA* sepadata, /**< separator data */
    998 SCIP_ROW** rows, /**< problem rows */
    999 int* row_list, /**< linked list of rows corresponding to 2 or 3 var sets */
    1000 SCIP_HASHTABLE* hashtable, /**< hashtable storing unconditional relations */
    1001 SCIP_Real* coefs1, /**< coefficients of the first linear relation */
    1002 SCIP_VAR** vars_xwy, /**< variables of the first relation in the order x, w, y */
    1003 SCIP_Real side1, /**< side of the first relation */
    1004 SCIP_SIDETYPE sidetype1, /**< is the left or right hand side given for the first relation? */
    1005 int varpos1, /**< position of the first unconditional variable in the vars_xwy array */
    1006 int varpos2, /**< position of the second unconditional variable in the vars_xwy array */
    1007 SCIP_HASHMAP* varmap, /**< variable map */
    1008 SCIP_Bool f /**< the value of x that activates the first relation */
    1009 )
    1010{
    1011 HASHDATA hashdata;
    1012 HASHDATA* foundhashdata;
    1013 SCIP_ROW* row2;
    1014 int r2;
    1015 int pos1;
    1016 int pos2;
    1017 SCIP_Real coefs2[3] = { 0., 0., 0. };
    1018 SCIP_VAR* var1;
    1019 SCIP_VAR* var2;
    1020
    1021 /* always unconditional, therefore x must not be one of the two variables */
    1022 assert(varpos1 != 0);
    1023 assert(varpos2 != 0);
    1024
    1025 var1 = vars_xwy[varpos1];
    1026 var2 = vars_xwy[varpos2];
    1027
    1028 hashdata.nvars = 2;
    1029 hashdata.firstrow = -1;
    1030 if( SCIPvarGetIndex(var1) < SCIPvarGetIndex(var2) )
    1031 {
    1032 pos1 = 0;
    1033 pos2 = 1;
    1034 }
    1035 else
    1036 {
    1037 pos1 = 1;
    1038 pos2 = 0;
    1039 }
    1040
    1041 hashdata.vars[pos1] = var1;
    1042 hashdata.vars[pos2] = var2;
    1043
    1044 foundhashdata = (HASHDATA*)SCIPhashtableRetrieve(hashtable, &hashdata);
    1045
    1046 if( foundhashdata != NULL )
    1047 {
    1048 /* if the var pair exists, use all corresponding rows */
    1049 r2 = foundhashdata->firstrow;
    1050
    1051 while( r2 != -1 )
    1052 {
    1053 row2 = rows[r2];
    1054 assert(SCIProwGetNNonz(row2) == 2);
    1055 assert(var1 == SCIPcolGetVar(SCIProwGetCols(row2)[pos1]));
    1056 assert(var2 == SCIPcolGetVar(SCIProwGetCols(row2)[pos2]));
    1057
    1058 coefs2[varpos1] = SCIProwGetVals(row2)[pos1];
    1059 coefs2[varpos2] = SCIProwGetVals(row2)[pos2];
    1060
    1061 SCIPdebugMsg(scip, "Unconditional:\n");
    1062 if( !SCIPisInfinity(scip, -SCIProwGetLhs(row2)) )
    1063 {
    1064 SCIP_CALL( extractProducts(scip, sepadata, vars_xwy, coefs1, coefs2, side1,
    1065 SCIProwGetLhs(row2) - SCIProwGetConstant(row2), sidetype1, SCIP_SIDETYPE_LEFT, varmap, f) );
    1066 }
    1067 if( !SCIPisInfinity(scip, SCIProwGetRhs(row2)) )
    1068 {
    1069 SCIP_CALL( extractProducts(scip, sepadata, vars_xwy, coefs1, coefs2, side1,
    1070 SCIProwGetRhs(row2) - SCIProwGetConstant(row2), sidetype1, SCIP_SIDETYPE_RIGHT, varmap, f) );
    1071 }
    1072
    1073 r2 = row_list[r2];
    1074 }
    1075 }
    1076
    1077 return SCIP_OKAY;
    1078}
    1079
    1080/** finds and stores implied relations (x = f &rArr; ay + bw &le; c, f can be 0 or 1) and 2-variable relations
    1081 *
    1082 * Fills the following:
    1083 *
    1084 * - An array of variables that participate in two variable relations; for each such variable, ADJACENTVARDATA
    1085 * containing an array of variables that participate in two variable relations together with it; and a hashmap
    1086 * mapping variables to ADJACENTVARDATAs.
    1087 *
    1088 * - Hashtables storing hashdata objects with the two or three variables and the position of the first row in the
    1089 * `prob_rows` array, which in combination with the linked list (described below) will allow access to all rows that
    1090 * depend only on the corresponding variables.
    1091 *
    1092 * - Linked lists of row indices. Each list corresponds to a pair or triple of variables and contains positions of rows
    1093 * which depend only on those variables. All lists are stored in `row_list`, an array of length `nrows`, which is
    1094 * possible because each row belongs to at most one list. The array indices of `row_list` represent the positions of
    1095 * rows in `prob_rows`, and a value in the `row_list` array represents the next index in the list (-1 if there is no next
    1096 * list element). The first index of each list is stored in one of the hashdata objects as firstrow.
    1097 */
    1098static
    1100 SCIP* scip, /**< SCIP data structure */
    1101 SCIP_ROW** prob_rows, /**< linear rows of the problem */
    1102 int nrows, /**< number of rows */
    1103 SCIP_HASHTABLE* hashtable2, /**< hashtable to store 2-variable relations */
    1104 SCIP_HASHTABLE* hashtable3, /**< hashtable to store implied relations */
    1105 SCIP_HASHMAP* vars_in_2rels, /**< connections between variables that appear in 2-variable relations */
    1106 int* row_list /**< linked lists of row positions for each 2 or 3 variable set */
    1107 )
    1108{
    1109 int r;
    1110 SCIP_COL** cols;
    1111 HASHDATA searchhashdata;
    1112 HASHDATA* elementhashdata;
    1113
    1114 assert(prob_rows != NULL);
    1115 assert(nrows > 0);
    1116 assert(hashtable2 != NULL);
    1117 assert(hashtable3 != NULL);
    1118 assert(vars_in_2rels != NULL);
    1119 assert(row_list != NULL);
    1120
    1121 for( r = 0; r < nrows; ++r )
    1122 {
    1123 assert(prob_rows[r] != NULL);
    1124
    1125 cols = SCIProwGetCols(prob_rows[r]);
    1126 assert(cols != NULL);
    1127
    1128 /* initialise with the "end of list" value */
    1129 row_list[r] = -1;
    1130
    1131 /* look for unconditional relations with 2 variables */
    1132 if( SCIProwGetNNonz(prob_rows[r]) == 2 )
    1133 {
    1134 /* if at least one of the variables is binary, this is either an implied bound
    1135 * or a clique; these are covered separately */
    1138 {
    1139 SCIPdebugMsg(scip, "ignoring relation <%s> because a var is binary\n", SCIProwGetName(prob_rows[r]));
    1140 continue;
    1141 }
    1142
    1143 /* fill in searchhashdata so that to search for the two variables in hashtable2 */
    1144 searchhashdata.nvars = 2;
    1145 searchhashdata.firstrow = -1;
    1146 searchhashdata.vars[0] = SCIPcolGetVar(cols[0]);
    1147 searchhashdata.vars[1] = SCIPcolGetVar(cols[1]);
    1148
    1149 /* get the element corresponding to the two variables */
    1150 elementhashdata = (HASHDATA*)SCIPhashtableRetrieve(hashtable2, &searchhashdata);
    1151
    1152 if( elementhashdata != NULL )
    1153 {
    1154 /* if element exists, update it by adding the row */
    1155 row_list[r] = elementhashdata->firstrow;
    1156 elementhashdata->firstrow = r;
    1157 ++elementhashdata->nrows;
    1158 }
    1159 else
    1160 {
    1161 /* create an element for the combination of two variables */
    1162 SCIP_CALL( SCIPallocBuffer(scip, &elementhashdata) );
    1163
    1164 elementhashdata->nvars = 2;
    1165 elementhashdata->nrows = 1;
    1166 elementhashdata->vars[0] = searchhashdata.vars[0];
    1167 elementhashdata->vars[1] = searchhashdata.vars[1];
    1168 elementhashdata->firstrow = r;
    1169
    1170 SCIP_CALL( SCIPhashtableInsert(hashtable2, (void*)elementhashdata) );
    1171
    1172 /* hashdata.vars are two variables participating together in a two variable relation, therefore update
    1173 * these variables' adjacency data
    1174 */
    1175 SCIP_CALL( addAdjacentVars(scip, vars_in_2rels, searchhashdata.vars) );
    1176 }
    1177 }
    1178
    1179 /* look for implied relations (three variables, at least one binary variable) */
    1180 if( SCIProwGetNNonz(prob_rows[r]) == 3 )
    1181 {
    1182 /* an implied relation contains at least one binary variable */
    1186 continue;
    1187
    1188 /* fill in hashdata so that to search for the three variables in hashtable3 */
    1189 searchhashdata.nvars = 3;
    1190 searchhashdata.firstrow = -1;
    1191 searchhashdata.vars[0] = SCIPcolGetVar(cols[0]);
    1192 searchhashdata.vars[1] = SCIPcolGetVar(cols[1]);
    1193 searchhashdata.vars[2] = SCIPcolGetVar(cols[2]);
    1194
    1195 /* get the element corresponding to the three variables */
    1196 elementhashdata = (HASHDATA*)SCIPhashtableRetrieve(hashtable3, &searchhashdata);
    1197
    1198 if( elementhashdata != NULL )
    1199 {
    1200 /* if element exists, update it by adding the row */
    1201 row_list[r] = elementhashdata->firstrow;
    1202 elementhashdata->firstrow = r;
    1203 ++elementhashdata->nrows;
    1204 }
    1205 else
    1206 {
    1207 /* create an element for the combination of three variables */
    1208 SCIP_CALL( SCIPallocBuffer(scip, &elementhashdata) );
    1209
    1210 elementhashdata->nvars = 3;
    1211 elementhashdata->nrows = 1;
    1212 elementhashdata->vars[0] = searchhashdata.vars[0];
    1213 elementhashdata->vars[1] = searchhashdata.vars[1];
    1214 elementhashdata->vars[2] = searchhashdata.vars[2];
    1215 elementhashdata->firstrow = r;
    1216
    1217 SCIP_CALL( SCIPhashtableInsert(hashtable3, (void*)elementhashdata) );
    1218 }
    1219 }
    1220 }
    1221
    1222 return SCIP_OKAY;
    1223}
    1224
    1225/** detect bilinear products encoded in linear constraints */
    1226static
    1228 SCIP* scip, /**< SCIP data structure */
    1229 SCIP_SEPADATA* sepadata, /**< separation data */
    1230 SCIP_HASHMAP* varmap /**< variable map */
    1231 )
    1232{
    1233 int r1; /* first relation index */
    1234 int r2; /* second relation index */
    1235 int i; /* outer loop counter */
    1236 int permwy; /* index for permuting w and y */
    1237 int nrows;
    1238 SCIP_ROW** prob_rows;
    1239 SCIP_HASHTABLE* hashtable3;
    1240 SCIP_HASHTABLE* hashtable2;
    1241 HASHDATA* foundhashdata;
    1242 SCIP_VAR* vars_xwy[3];
    1243 SCIP_Real coefs1[3];
    1244 SCIP_Real coefs2[3];
    1245 SCIP_ROW* row1;
    1246 SCIP_ROW* row2;
    1247 int xpos;
    1248 int ypos;
    1249 int wpos;
    1250 int f; /* value of the binary variable */
    1251 SCIP_VAR** relatedvars;
    1252 int nrelatedvars;
    1253 SCIP_Bool xfixing;
    1254 SCIP_SIDETYPE sidetype1;
    1255 SCIP_SIDETYPE sidetype2;
    1256 SCIP_Real side1;
    1257 SCIP_Real side2;
    1258 int* row_list;
    1259 SCIP_HASHMAP* vars_in_2rels;
    1260 int nvars;
    1261
    1262 /* get the (original) rows */
    1263 SCIP_CALL( getOriginalRows(scip, &prob_rows, &nrows) );
    1264
    1265 if( nrows == 0 )
    1266 {
    1267 SCIPfreeBufferArray(scip, &prob_rows);
    1268 return SCIP_OKAY;
    1269 }
    1270
    1271 /* create tables of implied and unconditional relations */
    1272 SCIP_CALL( SCIPhashtableCreate(&hashtable3, SCIPblkmem(scip), nrows, SCIPhashGetKeyStandard,
    1273 hashdataKeyEqConss, hashdataKeyValConss, NULL) );
    1274 SCIP_CALL( SCIPhashtableCreate(&hashtable2, SCIPblkmem(scip), nrows, SCIPhashGetKeyStandard,
    1275 hashdataKeyEqConss, hashdataKeyValConss, NULL) );
    1276 SCIP_CALL( SCIPallocBufferArray(scip, &row_list, nrows) );
    1277
    1278 /* allocate the adjacency data map for variables that appear in 2-var relations */
    1279 nvars = SCIPgetNVars(scip);
    1280 SCIP_CALL( SCIPhashmapCreate(&vars_in_2rels, SCIPblkmem(scip), MIN(nvars, nrows * 2)) );
    1281
    1282 /* fill the data structures that will be used for product detection: hashtables and linked lists allowing to access
    1283 * two and three variable relations by the variables; and the hashmap for accessing variables participating in two
    1284 * variable relations with each given variable */
    1285 SCIP_CALL( fillRelationTables(scip, prob_rows, nrows, hashtable2, hashtable3, vars_in_2rels, row_list) );
    1286
    1287 /* start actually looking for products */
    1288 /* go through all sets of three variables */
    1289 for( i = 0; i < SCIPhashtableGetNEntries(hashtable3); ++i )
    1290 {
    1291 foundhashdata = (HASHDATA*)SCIPhashtableGetEntry(hashtable3, i);
    1292 if( foundhashdata == NULL )
    1293 continue;
    1294
    1295 SCIPdebugMsg(scip, "(<%s>, <%s>, <%s>): ", SCIPvarGetName(foundhashdata->vars[0]),
    1296 SCIPvarGetName(foundhashdata->vars[1]), SCIPvarGetName(foundhashdata->vars[2]));
    1297
    1298 /* An implied relation has the form: x == f => l(w,y) <=/>= side (f is 0 or 1, l is a linear function). Given
    1299 * a linear relation with three variables, any binary var can be x: we try them all here because this can
    1300 * produce different products.
    1301 */
    1302 for( xpos = 0; xpos < 3; ++xpos )
    1303 {
    1304 /* in vars_xwy, the order of variables is always as in the name: x, w, y */
    1305 vars_xwy[0] = foundhashdata->vars[xpos];
    1306
    1307 /* x must be binary */
    1308 if( SCIPvarGetType(vars_xwy[0]) != SCIP_VARTYPE_BINARY )
    1309 continue;
    1310
    1311 /* the first row might be an implication from f == 0 or f == 1: try both */
    1312 for( f = 0; f <= 1; ++f )
    1313 {
    1314 xfixing = f == 1;
    1315
    1316 /* go through implied relations for the corresponding three variables */
    1317 for( r1 = foundhashdata->firstrow; r1 != -1; r1 = row_list[r1] )
    1318 {
    1319 /* get the implied relation */
    1320 row1 = prob_rows[r1];
    1321
    1322 assert(SCIProwGetNNonz(row1) == 3);
    1323 /* the order of variables in all rows should be the same, and similar to the order in hashdata->vars,
    1324 * therefore the x variable from vars_xwy should be similar to the column variable at xpos
    1325 */
    1326 assert(vars_xwy[0] == SCIPcolGetVar(SCIProwGetCols(row1)[xpos]));
    1327
    1328 coefs1[0] = SCIProwGetVals(row1)[xpos];
    1329
    1330 /* use the side for which the inequality becomes tighter when x == xfixing than when x == !xfixing */
    1331 if( (!xfixing && coefs1[0] > 0.0) || (xfixing && coefs1[0] < 0.0) )
    1332 {
    1333 sidetype1 = SCIP_SIDETYPE_LEFT;
    1334 side1 = SCIProwGetLhs(row1);
    1335 }
    1336 else
    1337 {
    1338 sidetype1 = SCIP_SIDETYPE_RIGHT;
    1339 side1 = SCIProwGetRhs(row1);
    1340 }
    1341
    1342 if( SCIPisInfinity(scip, REALABS(side1)) )
    1343 continue;
    1344
    1345 side1 -= SCIProwGetConstant(row1);
    1346
    1347 /* permute w and y */
    1348 for( permwy = 1; permwy <= 2; ++permwy )
    1349 {
    1350 wpos = (xpos + permwy) % 3;
    1351 ypos = (xpos - permwy + 3) % 3;
    1352 vars_xwy[1] = foundhashdata->vars[wpos];
    1353 vars_xwy[2] = foundhashdata->vars[ypos];
    1354
    1355 assert(vars_xwy[1] == SCIPcolGetVar(SCIProwGetCols(row1)[wpos]));
    1356 assert(vars_xwy[2] == SCIPcolGetVar(SCIProwGetCols(row1)[ypos]));
    1357
    1358 coefs1[1] = SCIProwGetVals(row1)[wpos];
    1359 coefs1[2] = SCIProwGetVals(row1)[ypos];
    1360
    1361 /* look for the second relation: it should be tighter when x == !xfixing than when x == xfixing
    1362 * and can be either another implied relation or one of several types of two and one variable
    1363 * relations
    1364 */
    1365
    1366 /* go through the remaining rows (implied relations) for these three variables */
    1367 for( r2 = row_list[r1]; r2 != -1; r2 = row_list[r2] )
    1368 {
    1369 /* get the second implied relation */
    1370 row2 = prob_rows[r2];
    1371
    1372 assert(SCIProwGetNNonz(row2) == 3);
    1373 assert(vars_xwy[0] == SCIPcolGetVar(SCIProwGetCols(row2)[xpos]));
    1374 assert(vars_xwy[1] == SCIPcolGetVar(SCIProwGetCols(row2)[wpos]));
    1375 assert(vars_xwy[2] == SCIPcolGetVar(SCIProwGetCols(row2)[ypos]));
    1376
    1377 coefs2[0] = SCIProwGetVals(row2)[xpos];
    1378 coefs2[1] = SCIProwGetVals(row2)[wpos];
    1379 coefs2[2] = SCIProwGetVals(row2)[ypos];
    1380
    1381 /* use the side for which the inequality becomes tighter when x == !xfixing than when x == xfixing */
    1382 if( (!xfixing && coefs2[0] > 0.0) || (xfixing && coefs2[0] < 0.0) )
    1383 {
    1384 sidetype2 = SCIP_SIDETYPE_RIGHT;
    1385 side2 = SCIProwGetRhs(row2);
    1386 }
    1387 else
    1388 {
    1389 sidetype2 = SCIP_SIDETYPE_LEFT;
    1390 side2 = SCIProwGetLhs(row2);
    1391 }
    1392
    1393 if( SCIPisInfinity(scip, REALABS(side2)) )
    1394 continue;
    1395
    1396 side2 -= SCIProwGetConstant(row2);
    1397
    1398 SCIPdebugMsg(scip, "Two implied relations:\n");
    1399 SCIP_CALL( extractProducts(scip, sepadata, vars_xwy, coefs1, coefs2, side1, side2, sidetype1,
    1400 sidetype2, varmap, xfixing) );
    1401 }
    1402
    1403 /* use global bounds on w */
    1404 coefs2[0] = 0.0;
    1405 coefs2[1] = 1.0;
    1406 coefs2[2] = 0.0;
    1407 SCIPdebugMsg(scip, "w global bounds:\n");
    1408 if( !SCIPisInfinity(scip, -SCIPvarGetLbGlobal(vars_xwy[1])) )
    1409 {
    1410 SCIP_CALL( extractProducts(scip, sepadata, vars_xwy, coefs1, coefs2, side1,
    1411 SCIPvarGetLbGlobal(vars_xwy[1]), sidetype1, SCIP_SIDETYPE_LEFT, varmap, xfixing) );
    1412 }
    1413
    1414 if( !SCIPisInfinity(scip, SCIPvarGetUbGlobal(vars_xwy[1])) )
    1415 {
    1416 SCIP_CALL( extractProducts(scip, sepadata, vars_xwy, coefs1, coefs2, side1,
    1417 SCIPvarGetUbGlobal(vars_xwy[1]), sidetype1, SCIP_SIDETYPE_RIGHT, varmap, xfixing) );
    1418 }
    1419
    1420 /* use implied bounds and cliques with w */
    1421 if( SCIPvarGetType(vars_xwy[1]) != SCIP_VARTYPE_BINARY )
    1422 {
    1423 /* w is non-binary - look for implied bounds x == !f => w >=/<= bound */
    1424 SCIPdebugMsg(scip, "Implied relation + implied bounds on w:\n");
    1425 SCIP_CALL( detectProductsImplbnd(scip, sepadata, coefs1, vars_xwy, side1, sidetype1, 0, 1,
    1426 varmap, xfixing) );
    1427 }
    1428 else
    1429 {
    1430 /* w is binary - look for cliques containing x and w */
    1431 SCIPdebugMsg(scip, "Implied relation + cliques with x and w:\n");
    1432 SCIP_CALL( detectProductsClique(scip, sepadata, coefs1, vars_xwy, side1, sidetype1, 0, 1,
    1433 varmap, xfixing) );
    1434 }
    1435
    1436 /* use unconditional relations (i.e. relations of w and y) */
    1437
    1438 /* implied bound w == 0/1 => y >=/<= bound */
    1439 if( SCIPvarGetType(vars_xwy[1]) == SCIP_VARTYPE_BINARY && SCIPvarGetType(vars_xwy[2]) != SCIP_VARTYPE_BINARY )
    1440 {
    1441 SCIPdebugMsg(scip, "Implied relation + implied bounds with w and y:\n");
    1442 SCIP_CALL( detectProductsImplbnd(scip, sepadata, coefs1, vars_xwy, side1, sidetype1, 1, 2, varmap, xfixing) );
    1443 }
    1444
    1445 /* implied bound y == 0/1 => w >=/<= bound */
    1446 if( SCIPvarGetType(vars_xwy[2]) == SCIP_VARTYPE_BINARY && SCIPvarGetType(vars_xwy[1]) != SCIP_VARTYPE_BINARY )
    1447 {
    1448 SCIPdebugMsg(scip, "Implied relation + implied bounds with y and w:\n");
    1449 SCIP_CALL( detectProductsImplbnd(scip, sepadata, coefs1, vars_xwy, side1, sidetype1, 2, 1, varmap, xfixing) );
    1450 }
    1451
    1452 /* cliques containing w and y */
    1453 if( SCIPvarGetType(vars_xwy[1]) == SCIP_VARTYPE_BINARY && SCIPvarGetType(vars_xwy[2]) == SCIP_VARTYPE_BINARY )
    1454 {
    1455 SCIPdebugMsg(scip, "Implied relation + cliques with w and y:\n");
    1456 SCIP_CALL( detectProductsClique(scip, sepadata, coefs1, vars_xwy, side1, sidetype1, 1, 2, varmap, xfixing) );
    1457 }
    1458
    1459 /* inequalities containing w and y */
    1460 if( SCIPvarGetType(vars_xwy[1]) != SCIP_VARTYPE_BINARY && SCIPvarGetType(vars_xwy[2]) != SCIP_VARTYPE_BINARY )
    1461 {
    1462 SCIPdebugMsg(scip, "Implied relation + unconditional with w and y:\n");
    1463 SCIP_CALL( detectProductsUnconditional(scip, sepadata, prob_rows, row_list, hashtable2, coefs1,
    1464 vars_xwy, side1, sidetype1, 1, 2, varmap, xfixing) );
    1465 }
    1466 }
    1467 }
    1468 }
    1469 }
    1470 SCIPfreeBuffer(scip, &foundhashdata);
    1471 }
    1472
    1473 /* also loop through implied bounds to look for products */
    1474 for( i = 0; i < SCIPgetNBinVars(scip); ++i )
    1475 {
    1476 /* first choose the x variable: it can be any binary variable in the problem */
    1477 vars_xwy[0] = SCIPgetVars(scip)[i];
    1478
    1479 assert(SCIPvarGetType(vars_xwy[0]) == SCIP_VARTYPE_BINARY);
    1480
    1481 /* consider both possible values of x */
    1482 for( f = 0; f <= 1; ++f )
    1483 {
    1484 xfixing = f == 1;
    1485
    1486 /* go through implications of x */
    1487 for( r1 = 0; r1 < SCIPvarGetNImpls(vars_xwy[0], xfixing); ++r1 )
    1488 {
    1489 /* w is the implication var */
    1490 vars_xwy[1] = SCIPvarGetImplVars(vars_xwy[0], xfixing)[r1];
    1491 assert(SCIPvarGetType(vars_xwy[1]) != SCIP_VARTYPE_BINARY);
    1492
    1493 /* write the implication as a big-M constraint */
    1494 implBndToBigM(scip, vars_xwy, 0, 1, SCIPvarGetImplTypes(vars_xwy[0], xfixing)[r1], xfixing,
    1495 SCIPvarGetImplBounds(vars_xwy[0], xfixing)[r1], coefs1, &side1);
    1496 sidetype1 = SCIPvarGetImplTypes(vars_xwy[0], xfixing)[r1] == SCIP_BOUNDTYPE_LOWER ?
    1498
    1499 /* if the global bound is equal to the implied bound, there is nothing to do */
    1500 if( SCIPisZero(scip, coefs1[0]) )
    1501 continue;
    1502
    1503 SCIPdebugMsg(scip, "Implication %s == %u => %s %s %g\n", SCIPvarGetName(vars_xwy[0]), xfixing,
    1504 SCIPvarGetName(vars_xwy[1]), sidetype1 == SCIP_SIDETYPE_LEFT ? ">=" : "<=",
    1505 SCIPvarGetImplBounds(vars_xwy[0], xfixing)[r1]);
    1506 SCIPdebugMsg(scip, "Written as big-M: %g%s + %s %s %g\n", coefs1[0], SCIPvarGetName(vars_xwy[0]),
    1507 SCIPvarGetName(vars_xwy[1]), sidetype1 == SCIP_SIDETYPE_LEFT ? ">=" : "<=", side1);
    1508
    1509 /* the second relation is in w and y (y could be anything, but must be in relation with w) */
    1510
    1511 /* x does not participate in the second relation, so we immediately set its coefficient to 0.0 */
    1512 coefs2[0] = 0.0;
    1513
    1514 SCIPdebugMsg(scip, "Implic of x = <%s> + implied lb on w = <%s>:\n", SCIPvarGetName(vars_xwy[0]), SCIPvarGetName(vars_xwy[1]));
    1515
    1516 /* use implied lower bounds on w: w >= b*y + d */
    1517 for( r2 = 0; r2 < SCIPvarGetNVlbs(vars_xwy[1]); ++r2 )
    1518 {
    1519 vars_xwy[2] = SCIPvarGetVlbVars(vars_xwy[1])[r2];
    1520 if( vars_xwy[2] == vars_xwy[0] )
    1521 continue;
    1522
    1523 coefs2[1] = 1.0;
    1524 coefs2[2] = -SCIPvarGetVlbCoefs(vars_xwy[1])[r2];
    1525
    1526 SCIP_CALL( extractProducts(scip, sepadata, vars_xwy, coefs1, coefs2, side1,
    1527 SCIPvarGetVlbConstants(vars_xwy[1])[r2], sidetype1, SCIP_SIDETYPE_LEFT, varmap, xfixing) );
    1528 }
    1529
    1530 SCIPdebugMsg(scip, "Implic of x = <%s> + implied ub on w = <%s>:\n", SCIPvarGetName(vars_xwy[0]), SCIPvarGetName(vars_xwy[1]));
    1531
    1532 /* use implied upper bounds on w: w <= b*y + d */
    1533 for( r2 = 0; r2 < SCIPvarGetNVubs(vars_xwy[1]); ++r2 )
    1534 {
    1535 vars_xwy[2] = SCIPvarGetVubVars(vars_xwy[1])[r2];
    1536 if( vars_xwy[2] == vars_xwy[0] )
    1537 continue;
    1538
    1539 coefs2[1] = 1.0;
    1540 coefs2[2] = -SCIPvarGetVubCoefs(vars_xwy[1])[r2];
    1541
    1542 SCIP_CALL( extractProducts(scip, sepadata, vars_xwy, coefs1, coefs2, side1,
    1543 SCIPvarGetVubConstants(vars_xwy[1])[r2], sidetype1, SCIP_SIDETYPE_RIGHT, varmap, xfixing) );
    1544 }
    1545
    1546 /* use unconditional relations containing w */
    1547 relatedvars = getAdjacentVars(vars_in_2rels, vars_xwy[1], &nrelatedvars);
    1548 if( relatedvars == NULL )
    1549 continue;
    1550
    1551 for( r2 = 0; r2 < nrelatedvars; ++r2 )
    1552 {
    1553 vars_xwy[2] = relatedvars[r2];
    1554 SCIPdebugMsg(scip, "Implied bound + unconditional with w and y:\n");
    1555 SCIP_CALL( detectProductsUnconditional(scip, sepadata, prob_rows, row_list, hashtable2, coefs1,
    1556 vars_xwy, side1, sidetype1, 1, 2, varmap, xfixing) );
    1557 }
    1558 }
    1559 }
    1560 }
    1561
    1562 /* free memory */
    1563 clearVarAdjacency(scip, vars_in_2rels);
    1564 SCIPhashmapFree(&vars_in_2rels);
    1565
    1566 SCIPdebugMsg(scip, "Unconditional relations table:\n");
    1567 for( i = 0; i < SCIPhashtableGetNEntries(hashtable2); ++i )
    1568 {
    1569 foundhashdata = (HASHDATA*)SCIPhashtableGetEntry(hashtable2, i);
    1570 if( foundhashdata == NULL )
    1571 continue;
    1572
    1573 SCIPdebugMsg(scip, "(%s, %s): ", SCIPvarGetName(foundhashdata->vars[0]),
    1574 SCIPvarGetName(foundhashdata->vars[1]));
    1575
    1576 SCIPfreeBuffer(scip, &foundhashdata);
    1577 }
    1578
    1579 SCIPfreeBufferArray(scip, &row_list);
    1580
    1581 SCIPhashtableFree(&hashtable2);
    1582 SCIPhashtableFree(&hashtable3);
    1583
    1584 SCIPfreeBufferArray(scip, &prob_rows);
    1585
    1586 return SCIP_OKAY;
    1587}
    1588
    1589/** helper method to create separation data */
    1590static
    1592 SCIP* scip, /**< SCIP data structure */
    1593 SCIP_SEPADATA* sepadata /**< separation data */
    1594 )
    1595{
    1596 SCIP_HASHMAP* varmap;
    1597 int i;
    1598 SCIP_CONSNONLINEAR_BILINTERM* bilinterms;
    1599 int varmapsize;
    1600 int nvars;
    1601
    1602 assert(sepadata != NULL);
    1603
    1604 /* initialize some fields of sepadata */
    1605 sepadata->varssorted = NULL;
    1606 sepadata->varpriorities = NULL;
    1607 sepadata->bilinvardatamap = NULL;
    1608 sepadata->eqauxexpr = NULL;
    1609 sepadata->nbilinvars = 0;
    1610 sepadata->sbilinvars = 0;
    1611
    1612 /* get total number of bilinear terms */
    1613 sepadata->nbilinterms = SCIPgetNBilinTermsNonlinear(sepadata->conshdlr);
    1614
    1615 /* skip if there are no bilinear terms and implicit product detection is off */
    1616 if( sepadata->nbilinterms == 0 && !sepadata->detecthidden )
    1617 return SCIP_OKAY;
    1618
    1619 /* the number of variables participating in bilinear products cannot exceed twice the number of bilinear terms;
    1620 * however, if we detect hidden products, the number of terms is yet unknown, so use the number of variables
    1621 */
    1622 nvars = SCIPgetNVars(scip);
    1623 varmapsize = sepadata->detecthidden ? nvars : MIN(nvars, sepadata->nbilinterms * 2);
    1624
    1625 /* create variable map */
    1626 SCIP_CALL( SCIPhashmapCreate(&varmap, SCIPblkmem(scip), varmapsize) );
    1627
    1628 /* get all bilinear terms from the nonlinear constraint handler */
    1629 bilinterms = SCIPgetBilinTermsNonlinear(sepadata->conshdlr);
    1630
    1631 /* store the information of all variables that appear bilinearly */
    1632 for( i = 0; i < sepadata->nbilinterms; ++i )
    1633 {
    1634 assert(bilinterms[i].x != NULL);
    1635 assert(bilinterms[i].y != NULL);
    1636 assert(bilinterms[i].nlockspos + bilinterms[i].nlocksneg > 0);
    1637
    1638 /* skip bilinear term if it does not have an auxiliary variable */
    1639 if( bilinterms[i].aux.var == NULL )
    1640 continue;
    1641
    1642 /* if only original variables should be used, skip products that contain at least one auxiliary variable */
    1643 if( sepadata->onlyoriginal && (SCIPvarIsRelaxationOnly(bilinterms[i].x) ||
    1644 SCIPvarIsRelaxationOnly(bilinterms[i].y)) )
    1645 continue;
    1646
    1647 /* coverity[var_deref_model] */
    1648 SCIP_CALL( addProductVars(scip, sepadata, bilinterms[i].x, bilinterms[i].y, varmap,
    1649 bilinterms[i].nlockspos + bilinterms[i].nlocksneg) );
    1650 }
    1651
    1652 if( sepadata->detecthidden )
    1653 {
    1654 int oldnterms = sepadata->nbilinterms;
    1655
    1656 /* coverity[var_deref_model] */
    1657 SCIP_CALL( detectHiddenProducts(scip, sepadata, varmap) );
    1658
    1659 /* update nbilinterms and bilinterms, as detectHiddenProducts might have found new terms */
    1660 sepadata->nbilinterms = SCIPgetNBilinTermsNonlinear(sepadata->conshdlr);
    1661 bilinterms = SCIPgetBilinTermsNonlinear(sepadata->conshdlr);
    1662
    1663 if( sepadata->nbilinterms > oldnterms )
    1664 {
    1665 SCIPstatisticMessage(" Number of hidden products: %d\n", sepadata->nbilinterms - oldnterms);
    1666 }
    1667 }
    1668
    1669 SCIPhashmapFree(&varmap);
    1670
    1671 if( sepadata->nbilinterms == 0 )
    1672 {
    1673 return SCIP_OKAY;
    1674 }
    1675
    1676 /* mark positions of aux.exprs that must be equal to the product */
    1677 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &sepadata->eqauxexpr, sepadata->nbilinterms) );
    1678
    1679 for( i = 0; i < sepadata->nbilinterms; ++i )
    1680 {
    1681 int j;
    1682
    1683 sepadata->eqauxexpr[i] = -1;
    1684 for( j = 0; j < bilinterms[i].nauxexprs; ++j )
    1685 {
    1686 assert(bilinterms[i].aux.exprs[j] != NULL);
    1687
    1688 if( bilinterms[i].aux.exprs[j]->underestimate && bilinterms[i].aux.exprs[j]->overestimate )
    1689 {
    1690 sepadata->eqauxexpr[i] = j;
    1691 break;
    1692 }
    1693 }
    1694 }
    1695
    1696 /* find maxnumber of variables that occur most often and sort them by number of occurrences
    1697 * (same as normal sort, except that entries at positions maxusedvars..nbilinvars may be unsorted at end)
    1698 */
    1699 SCIPselectDownIntPtr(sepadata->varpriorities, (void**) sepadata->varssorted, MIN(sepadata->maxusedvars,sepadata->nbilinvars-1),
    1700 sepadata->nbilinvars);
    1701
    1702 /* capture all variables */
    1703 for( i = 0; i < sepadata->nbilinvars; ++i )
    1704 {
    1705 assert(sepadata->varssorted[i] != NULL);
    1706 SCIP_CALL( SCIPcaptureVar(scip, sepadata->varssorted[i]) );
    1707 }
    1708
    1709 /* mark that separation data has been created */
    1710 sepadata->iscreated = TRUE;
    1711 sepadata->isinitialround = TRUE;
    1712
    1713 if( SCIPgetNBilinTermsNonlinear(sepadata->conshdlr) > 0 )
    1714 SCIPstatisticMessage(" Found bilinear terms\n");
    1715 else
    1716 SCIPstatisticMessage(" No bilinear terms\n");
    1717
    1718 return SCIP_OKAY;
    1719}
    1720
    1721/** get the positions of the most violated auxiliary under- and overestimators for each product
    1722 *
    1723 * -1 means no relation with given product is violated
    1724 */
    1725static
    1727 SCIP* scip, /**< SCIP data structure */
    1728 SCIP_SEPADATA* sepadata, /**< separator data */
    1729 SCIP_SOL* sol, /**< solution at which to evaluate the expressions */
    1730 int* bestunderestimators,/**< array of indices of best underestimators for each term */
    1731 int* bestoverestimators /**< array of indices of best overestimators for each term */
    1732 )
    1733{
    1734 SCIP_Real prodval;
    1735 SCIP_Real auxval;
    1736 SCIP_Real prodviol;
    1737 SCIP_Real viol_below;
    1738 SCIP_Real viol_above;
    1739 int i;
    1740 int j;
    1742
    1743 assert(bestunderestimators != NULL);
    1744 assert(bestoverestimators != NULL);
    1745
    1746 terms = SCIPgetBilinTermsNonlinear(sepadata->conshdlr);
    1747
    1748 for( j = 0; j < SCIPgetNBilinTermsNonlinear(sepadata->conshdlr); ++j )
    1749 {
    1750 viol_below = 0.0;
    1751 viol_above = 0.0;
    1752
    1753 /* evaluate the product expression */
    1754 prodval = SCIPgetSolVal(scip, sol, terms[j].x) * SCIPgetSolVal(scip, sol, terms[j].y);
    1755
    1756 bestunderestimators[j] = -1;
    1757 bestoverestimators[j] = -1;
    1758
    1759 /* if there are any auxexprs, look there */
    1760 for( i = 0; i < terms[j].nauxexprs; ++i )
    1761 {
    1762 auxval = SCIPevalBilinAuxExprNonlinear(scip, terms[j].x, terms[j].y, terms[j].aux.exprs[i], sol);
    1763 prodviol = auxval - prodval;
    1764
    1765 if( terms[j].aux.exprs[i]->underestimate && SCIPisFeasGT(scip, auxval, prodval) && prodviol > viol_below )
    1766 {
    1767 viol_below = prodviol;
    1768 bestunderestimators[j] = i;
    1769 }
    1770 if( terms[j].aux.exprs[i]->overestimate && SCIPisFeasGT(scip, prodval, auxval) && -prodviol > viol_above )
    1771 {
    1772 viol_above = -prodviol;
    1773 bestoverestimators[j] = i;
    1774 }
    1775 }
    1776
    1777 /* if the term has a plain auxvar, it will be treated differently - do nothing here */
    1778 }
    1779}
    1780
    1781/** tests if a row contains too many unknown bilinear terms w.r.t. the parameters */
    1782static
    1784 SCIP_SEPADATA* sepadata, /**< separation data */
    1785 SCIP_ROW* row, /**< the row to be tested */
    1786 SCIP_VAR* var, /**< the variable that is to be multiplied with row */
    1787 int* currentnunknown, /**< buffer to store number of unknown terms in current row if acceptable */
    1788 SCIP_Bool* acceptable /**< buffer to store the result */
    1789 )
    1790{
    1791 int i;
    1792 int idx;
    1794
    1795 assert(row != NULL);
    1796 assert(var != NULL);
    1797
    1798 *currentnunknown = 0;
    1799 terms = SCIPgetBilinTermsNonlinear(sepadata->conshdlr);
    1800
    1801 for( i = 0; (i < SCIProwGetNNonz(row)) && (sepadata->maxunknownterms < 0 || *currentnunknown <= sepadata->maxunknownterms); ++i )
    1802 {
    1803 idx = SCIPgetBilinTermIdxNonlinear(sepadata->conshdlr, var, SCIPcolGetVar(SCIProwGetCols(row)[i]));
    1804
    1805 /* if the product hasn't been found, no auxiliary expressions for it are known */
    1806 if( idx < 0 )
    1807 {
    1808 ++(*currentnunknown);
    1809 continue;
    1810 }
    1811
    1812 /* known terms are only those that have an aux.var or equality estimators */
    1813 if( sepadata->eqauxexpr[idx] == -1 && !(terms[idx].nauxexprs == 0 && terms[idx].aux.var != NULL) )
    1814 {
    1815 ++(*currentnunknown);
    1816 }
    1817 }
    1818
    1819 *acceptable = sepadata->maxunknownterms < 0 || *currentnunknown <= sepadata->maxunknownterms;
    1820
    1821 return SCIP_OKAY;
    1822}
    1823
    1824/** adds coefficients and constant of an auxiliary expression
    1825 *
    1826 * the variables the pointers are pointing to must already be initialized
    1827 */
    1828static
    1830 SCIP_VAR* var1, /**< first product variable */
    1831 SCIP_VAR* var2, /**< second product variable */
    1832 SCIP_CONSNONLINEAR_AUXEXPR* auxexpr, /**< auxiliary expression to be added */
    1833 SCIP_Real coef, /**< coefficient of the auxiliary expression */
    1834 SCIP_Real* coefaux, /**< pointer to add the coefficient of the auxiliary variable */
    1835 SCIP_Real* coef1, /**< pointer to add the coefficient of the first variable */
    1836 SCIP_Real* coef2, /**< pointer to add the coefficient of the second variable */
    1837 SCIP_Real* cst /**< pointer to add the constant */
    1838 )
    1839{
    1840 assert(auxexpr != NULL);
    1841 assert(auxexpr->auxvar != NULL);
    1842 assert(coefaux != NULL);
    1843 assert(coef1 != NULL);
    1844 assert(coef2 != NULL);
    1845 assert(cst != NULL);
    1846
    1847 *coefaux += auxexpr->coefs[0] * coef;
    1848
    1849 /* in auxexpr, x goes before y and has the smaller index,
    1850 * so compare vars to figure out which one is x and which is y
    1851 */
    1852 if( SCIPvarCompare(var1, var2) < 1 )
    1853 {
    1854 *coef1 += auxexpr->coefs[1] * coef;
    1855 *coef2 += auxexpr->coefs[2] * coef;
    1856 }
    1857 else
    1858 {
    1859 *coef1 += auxexpr->coefs[2] * coef;
    1860 *coef2 += auxexpr->coefs[1] * coef;
    1861 }
    1862 *cst += coef * auxexpr->cst;
    1863}
    1864
    1865/** add a linear term `coef`*`colvar` multiplied by a bound factor (var - lb(var)) or (ub(var) - var)
    1866 *
    1867 * adds the linear term with `colvar` to `cut` and updates `coefvar` and `cst`
    1868 */
    1869static
    1871 SCIP* scip, /**< SCIP data structure */
    1872 SCIP_SEPADATA* sepadata, /**< separator data */
    1873 SCIP_SOL* sol, /**< the point to be separated (can be NULL) */
    1874 int* bestunderest, /**< positions of most violated underestimators for each product term */
    1875 int* bestoverest, /**< positions of most violated overestimators for each product term */
    1876 SCIP_ROW* cut, /**< cut to which the term is to be added */
    1877 SCIP_VAR* var, /**< multiplier variable */
    1878 SCIP_VAR* colvar, /**< row variable to be multiplied */
    1879 SCIP_Real coef, /**< coefficient of the bilinear term */
    1880 SCIP_Bool uselb, /**< whether we multiply with (var - lb) or (ub - var) */
    1881 SCIP_Bool uselhs, /**< whether to create a cut for the lhs or rhs */
    1882 SCIP_Bool local, /**< whether local or global cuts should be computed */
    1883 SCIP_Bool computeEqCut, /**< whether conditions are fulfilled to compute equality cuts */
    1884 SCIP_Real* coefvar, /**< coefficient of var */
    1885 SCIP_Real* cst, /**< buffer to store the constant part of the cut */
    1886 SCIP_Bool* success /**< buffer to store whether cut was updated successfully */
    1887 )
    1888{
    1889 SCIP_Real lbvar;
    1890 SCIP_Real ubvar;
    1891 SCIP_Real refpointvar;
    1892 SCIP_Real signfactor;
    1893 SCIP_Real boundfactor;
    1894 SCIP_Real coefauxvar;
    1895 SCIP_Real coefcolvar;
    1896 SCIP_Real coefterm;
    1897 int auxpos;
    1898 int idx;
    1900 SCIP_VAR* auxvar;
    1901
    1902 terms = SCIPgetBilinTermsNonlinear(sepadata->conshdlr);
    1903
    1904 if( computeEqCut )
    1905 {
    1906 lbvar = 0.0;
    1907 ubvar = 0.0;
    1908 }
    1909 else
    1910 {
    1911 lbvar = local ? SCIPvarGetLbLocal(var) : SCIPvarGetLbGlobal(var);
    1912 ubvar = local ? SCIPvarGetUbLocal(var) : SCIPvarGetUbGlobal(var);
    1913 }
    1914
    1915 refpointvar = MAX(lbvar, MIN(ubvar, SCIPgetSolVal(scip, sol, var))); /*lint !e666*/
    1916
    1917 signfactor = (uselb ? 1.0 : -1.0);
    1918 boundfactor = (uselb ? -lbvar : ubvar);
    1919
    1920 coefterm = coef * signfactor; /* coefficient of the bilinear term */
    1921 coefcolvar = coef * boundfactor; /* coefficient of the linear term */
    1922 coefauxvar = 0.0; /* coefficient of the auxiliary variable corresponding to the bilinear term */
    1923 auxvar = NULL;
    1924
    1925 assert(!SCIPisInfinity(scip, REALABS(coefterm)));
    1926
    1927 /* first, add the linearisation of the bilinear term */
    1928
    1929 idx = SCIPgetBilinTermIdxNonlinear(sepadata->conshdlr, var, colvar);
    1930 auxpos = -1;
    1931
    1932 /* for an implicit term, get the position of the best estimator */
    1933 if( idx >= 0 && terms[idx].nauxexprs > 0 )
    1934 {
    1935 if( computeEqCut )
    1936 {
    1937 /* use an equality auxiliary expression (which should exist for computeEqCut to be TRUE) */
    1938 assert(sepadata->eqauxexpr[idx] >= 0);
    1939 auxpos = sepadata->eqauxexpr[idx];
    1940 }
    1941 else if( (uselhs && coefterm > 0.0) || (!uselhs && coefterm < 0.0) )
    1942 {
    1943 /* use an overestimator */
    1944 auxpos = bestoverest[idx];
    1945 }
    1946 else
    1947 {
    1948 /* use an underestimator */
    1949 auxpos = bestunderest[idx];
    1950 }
    1951 }
    1952
    1953 /* if the term is implicit and a suitable auxiliary expression for var*colvar exists, add the coefficients
    1954 * of the auxiliary expression for coefterm*var*colvar to coefauxvar, coefcolvar, coefvar and cst
    1955 */
    1956 if( auxpos >= 0 )
    1957 {
    1958 SCIPdebugMsg(scip, "auxiliary expression for <%s> and <%s> found, will be added to cut:\n",
    1959 SCIPvarGetName(colvar), SCIPvarGetName(var));
    1960 addAuxexprCoefs(var, colvar, terms[idx].aux.exprs[auxpos], coefterm, &coefauxvar, coefvar, &coefcolvar, cst);
    1961 auxvar = terms[idx].aux.exprs[auxpos]->auxvar;
    1962 }
    1963 /* for an existing term, use the auxvar if there is one */
    1964 else if( idx >= 0 && terms[idx].nauxexprs == 0 && terms[idx].aux.var != NULL )
    1965 {
    1966 SCIPdebugMsg(scip, "auxvar for <%s> and <%s> found, will be added to cut:\n",
    1967 SCIPvarGetName(colvar), SCIPvarGetName(var));
    1968 coefauxvar += coefterm;
    1969 auxvar = terms[idx].aux.var;
    1970 }
    1971
    1972 /* otherwise, use clique information or the McCormick estimator in place of the bilinear term */
    1973 else if( colvar != var )
    1974 {
    1975 SCIP_Bool found_clique = FALSE;
    1976 SCIP_Real lbcolvar = local ? SCIPvarGetLbLocal(colvar) : SCIPvarGetLbGlobal(colvar);
    1977 SCIP_Real ubcolvar = local ? SCIPvarGetUbLocal(colvar) : SCIPvarGetUbGlobal(colvar);
    1978 SCIP_Real refpointcolvar = MAX(lbcolvar, MIN(ubcolvar, SCIPgetSolVal(scip, sol, colvar))); /*lint !e666*/
    1979
    1980 assert(!computeEqCut);
    1981
    1982 if( REALABS(lbcolvar) > MAXVARBOUND || REALABS(ubcolvar) > MAXVARBOUND )
    1983 {
    1984 *success = FALSE;
    1985 return SCIP_OKAY;
    1986 }
    1987
    1988 SCIPdebugMsg(scip, "auxvar for <%s> and <%s> not found, will linearize the product\n", SCIPvarGetName(colvar), SCIPvarGetName(var));
    1989
    1990 /* if both variables are binary, check if they are contained together in some clique */
    1992 {
    1993 int c;
    1994 SCIP_CLIQUE** varcliques;
    1995
    1996 varcliques = SCIPvarGetCliques(var, TRUE);
    1997
    1998 /* look through cliques containing var */
    1999 for( c = 0; c < SCIPvarGetNCliques(var, TRUE); ++c )
    2000 {
    2001 if( SCIPcliqueHasVar(varcliques[c], colvar, TRUE) ) /* var + colvar <= 1 => var*colvar = 0 */
    2002 {
    2003 /* product is zero, add nothing */
    2004 found_clique = TRUE;
    2005 break;
    2006 }
    2007
    2008 if( SCIPcliqueHasVar(varcliques[c], colvar, FALSE) ) /* var + (1-colvar) <= 1 => var*colvar = var */
    2009 {
    2010 *coefvar += coefterm;
    2011 found_clique = TRUE;
    2012 break;
    2013 }
    2014 }
    2015
    2016 if( !found_clique )
    2017 {
    2018 varcliques = SCIPvarGetCliques(var, FALSE);
    2019
    2020 /* look through cliques containing complement of var */
    2021 for( c = 0; c < SCIPvarGetNCliques(var, FALSE); ++c )
    2022 {
    2023 if( SCIPcliqueHasVar(varcliques[c], colvar, TRUE) ) /* (1-var) + colvar <= 1 => var*colvar = colvar */
    2024 {
    2025 coefcolvar += coefterm;
    2026 found_clique = TRUE;
    2027 break;
    2028 }
    2029
    2030 if( SCIPcliqueHasVar(varcliques[c], colvar, FALSE) ) /* (1-var) + (1-colvar) <= 1 => var*colvar = var + colvar - 1 */
    2031 {
    2032 *coefvar += coefterm;
    2033 coefcolvar += coefterm;
    2034 *cst -= coefterm;
    2035 found_clique = TRUE;
    2036 break;
    2037 }
    2038 }
    2039 }
    2040 }
    2041
    2042 if( !found_clique )
    2043 {
    2044 SCIPdebugMsg(scip, "clique for <%s> and <%s> not found or at least one of them is not binary, will use McCormick\n", SCIPvarGetName(colvar), SCIPvarGetName(var));
    2045 SCIPaddBilinMcCormick(scip, coefterm, lbvar, ubvar, refpointvar, lbcolvar,
    2046 ubcolvar, refpointcolvar, uselhs, coefvar, &coefcolvar, cst, success);
    2047 if( !*success )
    2048 return SCIP_OKAY;
    2049 }
    2050 }
    2051
    2052 /* or, if it's a quadratic term, use a secant for overestimation and a gradient for underestimation */
    2053 else
    2054 {
    2055 SCIPdebugMsg(scip, "auxvar for <%s>^2 not found, will use gradient and secant estimators\n", SCIPvarGetName(colvar));
    2056
    2057 assert(!computeEqCut);
    2058
    2059 /* for a binary var, var^2 = var */
    2061 {
    2062 *coefvar += coefterm;
    2063 }
    2064 else
    2065 {
    2066 /* depending on over-/underestimation and the sign of the column variable, compute secant or tangent */
    2067 if( (uselhs && coefterm > 0.0) || (!uselhs && coefterm < 0.0) )
    2068 SCIPaddSquareSecant(scip, coefterm, lbvar, ubvar, coefvar, cst, success);
    2069 else
    2070 SCIPaddSquareLinearization(scip, coefterm, refpointvar, SCIPvarIsIntegral(var), coefvar, cst, success);
    2071
    2072 if( !*success )
    2073 return SCIP_OKAY;
    2074 }
    2075 }
    2076
    2077 /* add the auxiliary variable if its coefficient is nonzero */
    2078 if( !SCIPisZero(scip, coefauxvar) )
    2079 {
    2080 assert(auxvar != NULL);
    2081 /* coverity[var_deref_model] */
    2082 SCIP_CALL( SCIPaddVarToRow(scip, cut, auxvar, coefauxvar) );
    2083 }
    2084
    2085 /* we are done with the product linearisation, now add the term which comes from multiplying
    2086 * coef*colvar by the constant part of the bound factor
    2087 */
    2088
    2089 if( colvar != var )
    2090 {
    2091 assert(!SCIPisInfinity(scip, REALABS(coefcolvar)));
    2092 SCIP_CALL( SCIPaddVarToRow(scip, cut, colvar, coefcolvar) );
    2093 }
    2094 else
    2095 *coefvar += coefcolvar;
    2096
    2097 return SCIP_OKAY;
    2098}
    2099
    2100/** creates the RLT cut formed by multiplying a given row with (x - lb) or (ub - x)
    2101 *
    2102 * In detail:
    2103 * - The row is multiplied either with (x - lb(x)) or with (ub(x) - x), depending on parameter `uselb`, or by x if
    2104 * this is an equality cut
    2105 * - The (inequality) cut is computed either for lhs or rhs, depending on parameter `uselhs`.
    2106 * - Terms for which no auxiliary variable and no clique relation exists are replaced by either McCormick, secants,
    2107 * or gradient linearization cuts.
    2108 */
    2109static
    2111 SCIP* scip, /**< SCIP data structure */
    2112 SCIP_SEPA* sepa, /**< separator */
    2113 SCIP_SEPADATA* sepadata, /**< separation data */
    2114 SCIP_ROW** cut, /**< buffer to store the cut */
    2115 SCIP_ROW* row, /**< the row that is used for the rlt cut (NULL if using projected row) */
    2116 RLT_SIMPLEROW* projrow, /**< projected row that is used for the rlt cut (NULL if using row) */
    2117 SCIP_SOL* sol, /**< the point to be separated (can be NULL) */
    2118 int* bestunderest, /**< positions of most violated underestimators for each product term */
    2119 int* bestoverest, /**< positions of most violated overestimators for each product term */
    2120 SCIP_VAR* var, /**< the variable that is used for the rlt cuts */
    2121 SCIP_Bool* success, /**< buffer to store whether cut was created successfully */
    2122 SCIP_Bool uselb, /**< whether we multiply with (var - lb) or (ub - var) */
    2123 SCIP_Bool uselhs, /**< whether to create a cut for the lhs or rhs */
    2124 SCIP_Bool local, /**< whether local or global cuts should be computed */
    2125 SCIP_Bool computeEqCut, /**< whether conditions are fulfilled to compute equality cuts */
    2126 SCIP_Bool useprojrow /**< whether to use projected row instead of normal row */
    2127 )
    2128{ /*lint --e{413}*/
    2129 SCIP_Real signfactor;
    2130 SCIP_Real boundfactor;
    2131 SCIP_Real lbvar;
    2132 SCIP_Real ubvar;
    2133 SCIP_Real coefvar;
    2134 SCIP_Real consside;
    2135 SCIP_Real finalside;
    2136 SCIP_Real cstterm;
    2137 SCIP_Real lhs;
    2138 SCIP_Real rhs;
    2139 SCIP_Real rowcst;
    2140 int i;
    2141 const char* rowname;
    2142 char cutname[SCIP_MAXSTRLEN];
    2143
    2144 assert(sepadata != NULL);
    2145 assert(cut != NULL);
    2146 assert(useprojrow || row != NULL);
    2147 assert(!useprojrow || projrow != NULL);
    2148 assert(var != NULL);
    2149 assert(success != NULL);
    2150
    2151 lhs = useprojrow ? projrow->lhs : SCIProwGetLhs(row);
    2152 rhs = useprojrow ? projrow->rhs : SCIProwGetRhs(row);
    2153 rowname = useprojrow ? projrow->name : SCIProwGetName(row);
    2154 rowcst = useprojrow ? projrow ->cst : SCIProwGetConstant(row);
    2155
    2156 assert(!computeEqCut || SCIPisEQ(scip, lhs, rhs));
    2157
    2158 *cut = NULL;
    2159
    2160 /* get data for given variable */
    2161 if( computeEqCut )
    2162 {
    2163 lbvar = 0.0;
    2164 ubvar = 0.0;
    2165 }
    2166 else
    2167 {
    2168 lbvar = local ? SCIPvarGetLbLocal(var) : SCIPvarGetLbGlobal(var);
    2169 ubvar = local ? SCIPvarGetUbLocal(var) : SCIPvarGetUbGlobal(var);
    2170 }
    2171
    2172 /* get row side */
    2173 consside = uselhs ? lhs : rhs;
    2174
    2175 /* if the bounds are too large or the respective side is infinity, skip this cut */
    2176 if( (uselb && REALABS(lbvar) > MAXVARBOUND) || (!uselb && REALABS(ubvar) > MAXVARBOUND)
    2177 || SCIPisInfinity(scip, REALABS(consside)) )
    2178 {
    2179 SCIPdebugMsg(scip, "cut generation for %srow <%s>, %s, and variable <%s> with its %s %g not possible\n",
    2180 useprojrow ? "projected " : "", rowname, uselhs ? "lhs" : "rhs", SCIPvarGetName(var),
    2181 uselb ? "lower bound" : "upper bound", uselb ? lbvar : ubvar);
    2182
    2183 if( REALABS(lbvar) > MAXVARBOUND )
    2184 SCIPdebugMsg(scip, " because of lower bound\n");
    2185 if( REALABS(ubvar) > MAXVARBOUND )
    2186 SCIPdebugMsg(scip, " because of upper bound\n");
    2187 if( SCIPisInfinity(scip, REALABS(consside)) )
    2188 SCIPdebugMsg(scip, " because of side %g\n", consside);
    2189
    2190 *success = FALSE;
    2191 return SCIP_OKAY;
    2192 }
    2193
    2194 /* initialize some factors needed for computation */
    2195 coefvar = 0.0;
    2196 cstterm = 0.0;
    2197 signfactor = (uselb ? 1.0 : -1.0);
    2198 boundfactor = (uselb ? -lbvar : ubvar);
    2199 *success = TRUE;
    2200
    2201 /* create an empty row which we then fill with variables step by step */
    2202 (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "rlt_%scut_%s_%s_%s_%s_%" SCIP_LONGINT_FORMAT, useprojrow ? "proj" : "", rowname,
    2203 uselhs ? "lhs" : "rhs", SCIPvarGetName(var), uselb ? "lb" : "ub", SCIPgetNLPs(scip));
    2205 SCIPgetDepth(scip) > 0 && local, FALSE, FALSE) ); /* TODO SCIPgetDepth() should be replaced by depth that is passed on to the SEPAEXEC calls (?) */
    2206
    2208
    2209 /* iterate over all variables in the row and add the corresponding terms coef*colvar*(bound factor) to the cuts */
    2210 for( i = 0; i < (useprojrow ? projrow->nnonz : SCIProwGetNNonz(row)); ++i )
    2211 {
    2212 SCIP_VAR* colvar;
    2213
    2214 colvar = useprojrow ? projrow->vars[i] : SCIPcolGetVar(SCIProwGetCols(row)[i]);
    2215 SCIP_CALL( addRltTerm(scip, sepadata, sol, bestunderest, bestoverest, *cut, var, colvar,
    2216 useprojrow ? projrow->coefs[i] : SCIProwGetVals(row)[i], uselb, uselhs, local, computeEqCut,
    2217 &coefvar, &cstterm, success) );
    2218 }
    2219
    2220 if( REALABS(cstterm) > MAXVARBOUND )
    2221 {
    2222 *success = FALSE;
    2223 return SCIP_OKAY;
    2224 }
    2225
    2226 /* multiply (x-lb) or (ub -x) with the lhs and rhs of the row */
    2227 coefvar += signfactor * (rowcst - consside);
    2228 finalside = boundfactor * (consside - rowcst) - cstterm;
    2229
    2230 assert(!SCIPisInfinity(scip, REALABS(coefvar)));
    2231 assert(!SCIPisInfinity(scip, REALABS(finalside)));
    2232
    2233 /* set the coefficient of var and update the side */
    2234 SCIP_CALL( SCIPaddVarToRow(scip, *cut, var, coefvar) );
    2236 if( uselhs || computeEqCut )
    2237 {
    2238 SCIP_CALL( SCIPchgRowLhs(scip, *cut, finalside) );
    2239 }
    2240 if( !uselhs || computeEqCut )
    2241 {
    2242 SCIP_CALL( SCIPchgRowRhs(scip, *cut, finalside) );
    2243 }
    2244
    2245 SCIPdebugMsg(scip, "%scut was generated successfully:\n", useprojrow ? "projected " : "");
    2246#ifdef SCIP_DEBUG
    2247 SCIP_CALL( SCIPprintRow(scip, *cut, NULL) );
    2248#endif
    2249
    2250 return SCIP_OKAY;
    2251}
    2252
    2253/** store a row projected by fixing all variables that are at bound at sol; the result is a simplified row */
    2254static
    2256 SCIP* scip, /**< SCIP data structure */
    2257 RLT_SIMPLEROW* simplerow, /**< pointer to the simplified row */
    2258 SCIP_ROW* row, /**< row to be projected */
    2259 SCIP_SOL* sol, /**< the point to be separated (can be NULL) */
    2260 SCIP_Bool local /**< whether local bounds should be checked */
    2261 )
    2262{
    2263 int i;
    2264 SCIP_VAR* var;
    2265 SCIP_Real val;
    2266 SCIP_Real vlb;
    2267 SCIP_Real vub;
    2268
    2269 assert(simplerow != NULL);
    2270
    2272 strlen(SCIProwGetName(row))+1) ); /*lint !e666*/
    2273 simplerow->nnonz = 0;
    2274 simplerow->size = 0;
    2275 simplerow->vars = NULL;
    2276 simplerow->coefs = NULL;
    2277 simplerow->lhs = SCIProwGetLhs(row);
    2278 simplerow->rhs = SCIProwGetRhs(row);
    2279 simplerow->cst = SCIProwGetConstant(row);
    2280
    2281 for( i = 0; i < SCIProwGetNNonz(row); ++i )
    2282 {
    2283 var = SCIPcolGetVar(SCIProwGetCols(row)[i]);
    2284 val = SCIPgetSolVal(scip, sol, var);
    2285 vlb = local ? SCIPvarGetLbLocal(var) : SCIPvarGetLbGlobal(var);
    2286 vub = local ? SCIPvarGetUbLocal(var) : SCIPvarGetUbGlobal(var);
    2287 if( SCIPisFeasEQ(scip, vlb, val) || SCIPisFeasEQ(scip, vub, val) )
    2288 {
    2289 /* if we are projecting and the var is at bound, add var as a constant to simplerow */
    2290 if( !SCIPisInfinity(scip, -simplerow->lhs) )
    2291 simplerow->lhs -= SCIProwGetVals(row)[i]*val;
    2292 if( !SCIPisInfinity(scip, simplerow->rhs) )
    2293 simplerow->rhs -= SCIProwGetVals(row)[i]*val;
    2294 }
    2295 else
    2296 {
    2297 if( simplerow->nnonz + 1 > simplerow->size )
    2298 {
    2299 int newsize;
    2300
    2301 newsize = SCIPcalcMemGrowSize(scip, simplerow->nnonz + 1);
    2302 SCIP_CALL( SCIPreallocBufferArray(scip, &simplerow->coefs, newsize) );
    2303 SCIP_CALL( SCIPreallocBufferArray(scip, &simplerow->vars, newsize) );
    2304 simplerow->size = newsize;
    2305 }
    2306
    2307 /* add the term to simplerow */
    2308 simplerow->vars[simplerow->nnonz] = var;
    2309 simplerow->coefs[simplerow->nnonz] = SCIProwGetVals(row)[i];
    2310 ++(simplerow->nnonz);
    2311 }
    2312 }
    2313
    2314 return SCIP_OKAY;
    2315}
    2316
    2317/** free the projected row */
    2318static
    2320 SCIP* scip, /**< SCIP data structure */
    2321 RLT_SIMPLEROW* simplerow /**< simplified row to be freed */
    2322 )
    2323{
    2324 assert(simplerow != NULL);
    2325
    2326 if( simplerow->size > 0 )
    2327 {
    2328 assert(simplerow->vars != NULL);
    2329 assert(simplerow->coefs != NULL);
    2330
    2331 SCIPfreeBufferArray(scip, &simplerow->vars);
    2332 SCIPfreeBufferArray(scip, &simplerow->coefs);
    2333 }
    2334 SCIPfreeBlockMemoryArray(scip, &simplerow->name, strlen(simplerow->name)+1);
    2335}
    2336
    2337/** creates the projected problem
    2338 *
    2339 * All variables that are at their bounds at the current solution are added
    2340 * to left and/or right hand sides as constant values.
    2341 */
    2342static
    2344 SCIP* scip, /**< SCIP data structure */
    2345 SCIP_ROW** rows, /**< problem rows */
    2346 int nrows, /**< number of rows */
    2347 SCIP_SOL* sol, /**< the point to be separated (can be NULL) */
    2348 RLT_SIMPLEROW** projrows, /**< the projected rows to be filled */
    2349 SCIP_Bool local, /**< are local cuts allowed? */
    2350 SCIP_Bool* allcst /**< buffer to store whether all projected rows have only constants */
    2351 )
    2352{
    2353 int i;
    2354
    2355 assert(scip != NULL);
    2356 assert(rows != NULL);
    2357 assert(projrows != NULL);
    2358 assert(allcst != NULL);
    2359
    2360 *allcst = TRUE;
    2361 SCIP_CALL( SCIPallocBufferArray(scip, projrows, nrows) );
    2362
    2363 for( i = 0; i < nrows; ++i )
    2364 {
    2365 /* get a simplified and projected row */
    2366 SCIP_CALL( createProjRow(scip, &(*projrows)[i], rows[i], sol, local) );
    2367 if( (*projrows)[i].nnonz > 0 )
    2368 *allcst = FALSE;
    2369 }
    2370
    2371 return SCIP_OKAY;
    2372}
    2373
    2374#ifdef SCIP_DEBUG
    2375/* prints the projected LP */
    2376static
    2377void printProjRows(
    2378 SCIP* scip, /**< SCIP data structure */
    2379 RLT_SIMPLEROW* projrows, /**< the projected rows */
    2380 int nrows, /**< number of projected rows */
    2381 FILE* file /**< output file (or NULL for standard output) */
    2382 )
    2383{
    2384 int i;
    2385 int j;
    2386
    2387 assert(projrows != NULL);
    2388
    2389 for( i = 0; i < nrows; ++i )
    2390 {
    2391 SCIPinfoMessage(scip, file, "\nproj_row[%d]: ", i);
    2392 if( !SCIPisInfinity(scip, -projrows[i].lhs) )
    2393 SCIPinfoMessage(scip, file, "%.15g <= ", projrows[i].lhs);
    2394 for( j = 0; j < projrows[i].nnonz; ++j )
    2395 {
    2396 if( j == 0 )
    2397 {
    2398 if( projrows[i].coefs[j] < 0 )
    2399 SCIPinfoMessage(scip, file, "-");
    2400 }
    2401 else
    2402 {
    2403 if( projrows[i].coefs[j] < 0 )
    2404 SCIPinfoMessage(scip, file, " - ");
    2405 else
    2406 SCIPinfoMessage(scip, file, " + ");
    2407 }
    2408
    2409 if( projrows[i].coefs[j] != 1.0 )
    2410 SCIPinfoMessage(scip, file, "%.15g*", REALABS(projrows[i].coefs[j]));
    2411 SCIPinfoMessage(scip, file, "<%s>", SCIPvarGetName(projrows[i].vars[j]));
    2412 }
    2413 if( projrows[i].cst > 0 )
    2414 SCIPinfoMessage(scip, file, " + %.15g", projrows[i].cst);
    2415 else if( projrows[i].cst < 0 )
    2416 SCIPinfoMessage(scip, file, " - %.15g", REALABS(projrows[i].cst));
    2417
    2418 if( !SCIPisInfinity(scip, projrows[i].rhs) )
    2419 SCIPinfoMessage(scip, file, " <= %.15g", projrows[i].rhs);
    2420 }
    2421 SCIPinfoMessage(scip, file, "\n");
    2422}
    2423#endif
    2424
    2425/** frees the projected rows */
    2426static
    2428 SCIP* scip, /**< SCIP data structure */
    2429 RLT_SIMPLEROW** projrows, /**< the projected LP */
    2430 int nrows /**< number of rows in projrows */
    2431 )
    2432{
    2433 int i;
    2434
    2435 for( i = 0; i < nrows; ++i )
    2436 freeProjRow(scip, &(*projrows)[i]);
    2437
    2438 SCIPfreeBufferArray(scip, projrows);
    2439}
    2440
    2441/** mark a row for rlt cut selection
    2442 *
    2443 * depending on the sign of the coefficient and violation, set or update mark which cut is required:
    2444 * - 1 - cuts for axy < aw case,
    2445 * - 2 - cuts for axy > aw case,
    2446 * - 3 - cuts for both cases
    2447 */
    2448static
    2450 int ridx, /**< row index */
    2451 SCIP_Real a, /**< coefficient of x in the row */
    2452 SCIP_Bool violatedbelow, /**< whether the relation auxexpr <= xy is violated */
    2453 SCIP_Bool violatedabove, /**< whether the relation xy <= auxexpr is violated */
    2454 int* row_idcs, /**< sparse array with indices of marked rows */
    2455 unsigned int* row_marks, /**< sparse array to store the marks */
    2456 int* nmarked /**< number of marked rows */
    2457 )
    2458{
    2459 unsigned int newmark;
    2460 int pos;
    2461 SCIP_Bool exists;
    2462
    2463 assert(a != 0.0);
    2464
    2465 if( (a > 0.0 && violatedbelow) || (a < 0.0 && violatedabove) )
    2466 newmark = 1; /* axy < aw case */
    2467 else
    2468 newmark = 2; /* axy > aw case */
    2469
    2470 /* find row idx in row_idcs */
    2471 exists = SCIPsortedvecFindInt(row_idcs, ridx, *nmarked, &pos);
    2472
    2473 if( exists )
    2474 {
    2475 /* we found the row index: update the mark at pos */
    2476 row_marks[pos] |= newmark;
    2477 }
    2478 else /* the given row index does not yet exist in row_idcs */
    2479 {
    2480 int i;
    2481
    2482 /* insert row index at the correct position */
    2483 for( i = *nmarked; i > pos; --i )
    2484 {
    2485 row_idcs[i] = row_idcs[i-1];
    2486 row_marks[i] = row_marks[i-1];
    2487 }
    2488 row_idcs[pos] = ridx;
    2489 row_marks[pos] = newmark;
    2490 (*nmarked)++;
    2491 }
    2492}
    2493
    2494/** mark all rows that should be multiplied by xj */
    2495static
    2497 SCIP* scip, /**< SCIP data structure */
    2498 SCIP_SEPADATA* sepadata, /**< separator data */
    2499 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
    2500 SCIP_SOL* sol, /**< point to be separated (can be NULL) */
    2501 int j, /**< index of the multiplier variable in sepadata */
    2502 SCIP_Bool local, /**< are local cuts allowed? */
    2503 SCIP_HASHMAP* row_to_pos, /**< hashmap linking row indices to positions in array */
    2504 int* bestunderest, /**< positions of most violated underestimators for each product term */
    2505 int* bestoverest, /**< positions of most violated overestimators for each product term */
    2506 unsigned int* row_marks, /**< sparse array storing the row marks */
    2507 int* row_idcs, /**< sparse array storing the marked row positions */
    2508 int* nmarked /**< number of marked rows */
    2509 )
    2510{
    2511 int i;
    2512 int idx;
    2513 int ncolrows;
    2514 int r;
    2515 int ridx;
    2516 SCIP_VAR* xi;
    2517 SCIP_VAR* xj;
    2518 SCIP_Real vlb;
    2519 SCIP_Real vub;
    2520 SCIP_Real vali;
    2521 SCIP_Real valj;
    2522 SCIP_Real a;
    2523 SCIP_COL* coli;
    2524 SCIP_Real* colvals;
    2525 SCIP_ROW** colrows;
    2527 SCIP_Bool violatedbelow;
    2528 SCIP_Bool violatedabove;
    2529 SCIP_VAR** bilinadjvars;
    2530 int nbilinadjvars;
    2531
    2532 *nmarked = 0;
    2533
    2534 xj = sepadata->varssorted[j];
    2535 assert(xj != NULL);
    2536
    2537 valj = SCIPgetSolVal(scip, sol, xj);
    2538 vlb = local ? SCIPvarGetLbLocal(xj) : SCIPvarGetLbGlobal(xj);
    2539 vub = local ? SCIPvarGetUbLocal(xj) : SCIPvarGetUbGlobal(xj);
    2540
    2541 if( sepadata->useprojection && (SCIPisFeasEQ(scip, vlb, valj) || SCIPisFeasEQ(scip, vub, valj)) )
    2542 {
    2543 /* we don't want to multiply by variables that are at bound */
    2544 SCIPdebugMsg(scip, "Rejected multiplier <%s> in [%g,%g] because it is at bound (current value %g)\n", SCIPvarGetName(xj), vlb, vub, valj);
    2545 return SCIP_OKAY;
    2546 }
    2547
    2548 terms = SCIPgetBilinTermsNonlinear(conshdlr);
    2549 bilinadjvars = getAdjacentVars(sepadata->bilinvardatamap, xj, &nbilinadjvars);
    2550 assert(bilinadjvars != NULL);
    2551
    2552 /* for each var which appears in a bilinear product together with xj, mark rows */
    2553 for( i = 0; i < nbilinadjvars; ++i )
    2554 {
    2555 xi = bilinadjvars[i];
    2556
    2558 continue;
    2559
    2560 vali = SCIPgetSolVal(scip, sol, xi);
    2561 vlb = local ? SCIPvarGetLbLocal(xi) : SCIPvarGetLbGlobal(xi);
    2562 vub = local ? SCIPvarGetUbLocal(xi) : SCIPvarGetUbGlobal(xi);
    2563
    2564 /* if we use projection, we aren't interested in products with variables that are at bound */
    2565 if( sepadata->useprojection && (SCIPisFeasEQ(scip, vlb, vali) || SCIPisFeasEQ(scip, vub, vali)) )
    2566 continue;
    2567
    2568 /* get the index of the bilinear product */
    2569 idx = SCIPgetBilinTermIdxNonlinear(conshdlr, xj, xi);
    2570 assert(idx >= 0 && idx < SCIPgetNBilinTermsNonlinear(conshdlr));
    2571
    2572 /* skip implicit products if we don't want to add RLT cuts for them */
    2573 if( !sepadata->hiddenrlt && !terms[idx].existing )
    2574 continue;
    2575
    2576 /* use the most violated under- and overestimators for this product;
    2577 * if equality cuts are computed, we might end up using a different auxiliary expression;
    2578 * so this is an optimistic (i.e. taking the largest possible violation) estimation
    2579 */
    2580 if( bestunderest == NULL || bestunderest[idx] == -1 )
    2581 { /* no violated implicit underestimation relations -> either use auxvar or set violatedbelow to FALSE */
    2582 if( terms[idx].nauxexprs == 0 && terms[idx].aux.var != NULL )
    2583 {
    2584 assert(terms[idx].existing);
    2585 violatedbelow = SCIPisFeasPositive(scip, SCIPgetSolVal(scip, sol, terms[idx].aux.var) - valj * vali);
    2586 }
    2587 else
    2588 {
    2589 assert(bestunderest != NULL);
    2590 violatedbelow = FALSE;
    2591 }
    2592 }
    2593 else
    2594 {
    2595 assert(bestunderest[idx] >= 0 && bestunderest[idx] < terms[idx].nauxexprs);
    2596
    2597 /* if we are here, the relation with the best underestimator must be violated */
    2598 assert(SCIPisFeasPositive(scip, SCIPevalBilinAuxExprNonlinear(scip, terms[idx].x, terms[idx].y,
    2599 terms[idx].aux.exprs[bestunderest[idx]], sol) - valj * vali));
    2600 violatedbelow = TRUE;
    2601 }
    2602
    2603 if( bestoverest == NULL || bestoverest[idx] == -1 )
    2604 { /* no violated implicit overestimation relations -> either use auxvar or set violatedabove to FALSE */
    2605 if( terms[idx].nauxexprs == 0 && terms[idx].aux.var != NULL )
    2606 {
    2607 assert(terms[idx].existing);
    2608 violatedabove = SCIPisFeasPositive(scip, valj * vali - SCIPgetSolVal(scip, sol, terms[idx].aux.var));
    2609 }
    2610 else
    2611 {
    2612 assert(bestoverest != NULL);
    2613 violatedabove = FALSE;
    2614 }
    2615 }
    2616 else
    2617 {
    2618 assert(bestoverest[idx] >= 0 && bestoverest[idx] < terms[idx].nauxexprs);
    2619
    2620 /* if we are here, the relation with the best overestimator must be violated */
    2621 assert(SCIPisFeasPositive(scip, valj * vali - SCIPevalBilinAuxExprNonlinear(scip, terms[idx].x, terms[idx].y,
    2622 terms[idx].aux.exprs[bestoverest[idx]], sol)));
    2623 violatedabove = TRUE;
    2624 }
    2625
    2626 /* only violated products contribute to row marks */
    2627 if( !violatedbelow && !violatedabove )
    2628 {
    2629 SCIPdebugMsg(scip, "the product for vars <%s> and <%s> is not violated\n", SCIPvarGetName(xj), SCIPvarGetName(xi));
    2630 continue;
    2631 }
    2632
    2633 /* get the column of xi */
    2634 coli = SCIPvarGetCol(xi);
    2635 colvals = SCIPcolGetVals(coli);
    2636 ncolrows = SCIPcolGetNNonz(coli);
    2637 colrows = SCIPcolGetRows(coli);
    2638
    2639 SCIPdebugMsg(scip, "marking rows for xj = <%s>, xi = <%s>\n", SCIPvarGetName(xj), SCIPvarGetName(xi));
    2640
    2641 /* mark the rows */
    2642 for( r = 0; r < ncolrows; ++r )
    2643 {
    2644 ridx = SCIProwGetIndex(colrows[r]);
    2645
    2646 if( !SCIPhashmapExists(row_to_pos, (void*)(size_t)ridx) )
    2647 continue; /* if row index is not in row_to_pos, it means that storeSuitableRows decided to ignore this row */
    2648
    2649 a = colvals[r];
    2650 if( a == 0.0 )
    2651 continue;
    2652
    2653 SCIPdebugMsg(scip, "Marking row %d\n", ridx);
    2654 addRowMark(ridx, a, violatedbelow, violatedabove, row_idcs, row_marks, nmarked);
    2655 }
    2656 }
    2657
    2658 return SCIP_OKAY;
    2659}
    2660
    2661/** adds McCormick inequalities for implicit products */
    2662static
    2664 SCIP* scip, /**< SCIP data structure */
    2665 SCIP_SEPA* sepa, /**< separator */
    2666 SCIP_SEPADATA* sepadata, /**< separator data */
    2667 SCIP_SOL* sol, /**< the point to be separated (can be NULL) */
    2668 int* bestunderestimators,/**< indices of auxiliary underestimators with largest violation in sol */
    2669 int* bestoverestimators, /**< indices of auxiliary overestimators with largest violation in sol */
    2670 SCIP_RESULT* result /**< pointer to store the result */
    2671 )
    2672{
    2673 int i;
    2674 int j;
    2676 SCIP_ROW* cut;
    2677 char name[SCIP_MAXSTRLEN];
    2678 SCIP_Bool underestimate;
    2679 SCIP_Real xcoef;
    2680 SCIP_Real ycoef;
    2681 SCIP_Real auxcoef;
    2682 SCIP_Real constant;
    2683 SCIP_Bool success;
    2685 SCIP_Bool cutoff;
    2686 SCIP_Real refpointx;
    2687 SCIP_Real refpointy;
    2688 SCIP_INTERVAL bndx;
    2689 SCIP_INTERVAL bndy;
    2690#ifndef NDEBUG
    2691 SCIP_Real productval;
    2692 SCIP_Real auxval;
    2693#endif
    2694
    2695 assert(sepadata->nbilinterms == SCIPgetNBilinTermsNonlinear(sepadata->conshdlr));
    2696 assert(bestunderestimators != NULL && bestoverestimators != NULL);
    2697
    2698 cutoff = FALSE;
    2699 terms = SCIPgetBilinTermsNonlinear(sepadata->conshdlr);
    2700
    2701 for( i = 0; i < sepadata->nbilinterms; ++i )
    2702 {
    2703 if( terms[i].existing )
    2704 continue;
    2705
    2706 assert(terms[i].nauxexprs > 0);
    2707
    2708 bndx.inf = SCIPvarGetLbLocal(terms[i].x);
    2709 bndx.sup = SCIPvarGetUbLocal(terms[i].x);
    2710 bndy.inf = SCIPvarGetLbLocal(terms[i].y);
    2711 bndy.sup = SCIPvarGetUbLocal(terms[i].y);
    2712 refpointx = SCIPgetSolVal(scip, sol, terms[i].x);
    2713 refpointy = SCIPgetSolVal(scip, sol, terms[i].y);
    2714
    2715 /* adjust the reference points */
    2716 refpointx = MIN(MAX(refpointx, bndx.inf), bndx.sup); /*lint !e666*/
    2717 refpointy = MIN(MAX(refpointy, bndy.inf), bndy.sup); /*lint !e666*/
    2718
    2719 /* one iteration for underestimation and one for overestimation */
    2720 for( j = 0; j < 2; ++j )
    2721 {
    2722 /* if underestimate, separate xy <= auxexpr; if !underestimate, separate xy >= auxexpr;
    2723 * the cuts will be:
    2724 * if underestimate: McCormick_under(xy) - auxexpr <= 0,
    2725 * if !underestimate: McCormick_over(xy) - auxexpr >= 0
    2726 */
    2727 underestimate = j == 0;
    2728 if( underestimate && bestoverestimators[i] != -1 )
    2729 auxexpr = terms[i].aux.exprs[bestoverestimators[i]];
    2730 else if( !underestimate && bestunderestimators[i] != -1 )
    2731 auxexpr = terms[i].aux.exprs[bestunderestimators[i]];
    2732 else
    2733 continue;
    2734 assert(!underestimate || auxexpr->overestimate);
    2735 assert(underestimate || auxexpr->underestimate);
    2736
    2737#ifndef NDEBUG
    2738 /* make sure that the term is violated */
    2739 productval = SCIPgetSolVal(scip, sol, terms[i].x) * SCIPgetSolVal(scip, sol, terms[i].y);
    2740 auxval = SCIPevalBilinAuxExprNonlinear(scip, terms[i].x, terms[i].y, auxexpr, sol);
    2741
    2742 /* if underestimate, then xy <= aux must be violated; otherwise aux <= xy must be violated */
    2743 assert((underestimate && SCIPisFeasLT(scip, auxval, productval)) ||
    2744 (!underestimate && SCIPisFeasLT(scip, productval, auxval)));
    2745#endif
    2746
    2747 /* create an empty row */
    2748 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "mccormick_%sestimate_implicit_%s*%s_%" SCIP_LONGINT_FORMAT,
    2749 underestimate ? "under" : "over", SCIPvarGetName(terms[i].x), SCIPvarGetName(terms[i].y),
    2750 SCIPgetNLPs(scip));
    2751
    2753 FALSE, FALSE) );
    2754
    2755 xcoef = 0.0;
    2756 ycoef = 0.0;
    2757 auxcoef = 0.0;
    2758 constant = 0.0;
    2759 success = TRUE;
    2760
    2761 /* subtract auxexpr from the cut */
    2762 addAuxexprCoefs(terms[i].x, terms[i].y, auxexpr, -1.0, &auxcoef, &xcoef, &ycoef, &constant);
    2763
    2764 /* add McCormick terms: ask for an underestimator if relation is xy <= auxexpr, and vice versa */
    2765 SCIPaddBilinMcCormick(scip, 1.0, bndx.inf, bndx.sup, refpointx, bndy.inf, bndy.sup, refpointy, !underestimate,
    2766 &xcoef, &ycoef, &constant, &success);
    2767
    2768 if( REALABS(constant) > MAXVARBOUND )
    2769 success = FALSE;
    2770
    2771 if( success )
    2772 {
    2773 assert(!SCIPisInfinity(scip, REALABS(xcoef)));
    2774 assert(!SCIPisInfinity(scip, REALABS(ycoef)));
    2775 assert(!SCIPisInfinity(scip, REALABS(constant)));
    2776
    2777 SCIP_CALL( SCIPaddVarToRow(scip, cut, terms[i].x, xcoef) );
    2778 SCIP_CALL( SCIPaddVarToRow(scip, cut, terms[i].y, ycoef) );
    2779 SCIP_CALL( SCIPaddVarToRow(scip, cut, auxexpr->auxvar, auxcoef) );
    2780
    2781 /* set side */
    2782 if( underestimate )
    2783 SCIP_CALL( SCIPchgRowRhs(scip, cut, -constant) );
    2784 else
    2785 SCIP_CALL( SCIPchgRowLhs(scip, cut, -constant) );
    2786
    2787 /* if the cut is violated, add it to SCIP */
    2789 {
    2790 SCIP_CALL( SCIPaddRow(scip, cut, FALSE, &cutoff) );
    2791 *result = SCIP_SEPARATED;
    2792 }
    2793 else
    2794 {
    2795 SCIPdebugMsg(scip, "\nMcCormick cut for hidden product <%s>*<%s> was created successfully, but is not violated",
    2796 SCIPvarGetName(terms[i].x), SCIPvarGetName(terms[i].y));
    2797 }
    2798 }
    2799
    2800 /* release the cut */
    2801 if( cut != NULL )
    2802 {
    2803 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
    2804 }
    2805
    2806 if( cutoff )
    2807 {
    2808 *result = SCIP_CUTOFF;
    2809 SCIPdebugMsg(scip, "exit separator because we found a cutoff -> skip\n");
    2810 return SCIP_OKAY;
    2811 }
    2812 }
    2813 }
    2814
    2815 return SCIP_OKAY;
    2816}
    2817
    2818/** builds and adds the RLT cuts */
    2819static
    2821 SCIP* scip, /**< SCIP data structure */
    2822 SCIP_SEPA* sepa, /**< separator */
    2823 SCIP_SEPADATA* sepadata, /**< separator data */
    2824 SCIP_CONSHDLR* conshdlr, /**< nonlinear constraint handler */
    2825 SCIP_SOL* sol, /**< the point to be separated (can be NULL) */
    2826 SCIP_HASHMAP* row_to_pos, /**< hashmap linking row indices to positions in array */
    2827 RLT_SIMPLEROW* projrows, /**< projected rows */
    2828 SCIP_ROW** rows, /**< problem rows */
    2829 int nrows, /**< number of problem rows */
    2830 SCIP_Bool allowlocal, /**< are local cuts allowed? */
    2831 int* bestunderestimators,/**< indices of auxiliary underestimators with largest violation in sol */
    2832 int* bestoverestimators, /**< indices of auxiliary overestimators with largest violation in sol */
    2833 SCIP_RESULT* result /**< buffer to store whether separation was successful */
    2834 )
    2835{
    2836 int j;
    2837 int r;
    2838 int k;
    2839 int nmarked;
    2840 int cutssize;
    2841 int ncuts;
    2842 SCIP_VAR* xj;
    2843 unsigned int* row_marks;
    2844 int* row_idcs;
    2845 SCIP_ROW* cut;
    2846 SCIP_ROW** cuts;
    2847 SCIP_Bool uselb[4] = {TRUE, TRUE, FALSE, FALSE};
    2848 SCIP_Bool uselhs[4] = {TRUE, FALSE, TRUE, FALSE};
    2849 SCIP_Bool success;
    2850 SCIP_Bool infeasible;
    2851 SCIP_Bool accepted;
    2852 SCIP_Bool buildeqcut;
    2853 SCIP_Bool iseqrow;
    2854
    2855 assert(!sepadata->useprojection || projrows != NULL);
    2856 assert(!sepadata->detecthidden || (bestunderestimators != NULL && bestoverestimators != NULL));
    2857
    2858 ncuts = 0;
    2859 cutssize = 0;
    2860 cuts = NULL;
    2861 *result = SCIP_DIDNOTFIND;
    2862
    2863 SCIP_CALL( SCIPallocCleanBufferArray(scip, &row_marks, nrows) );
    2864 SCIP_CALL( SCIPallocBufferArray(scip, &row_idcs, nrows) );
    2865
    2866 /* loop through all variables that appear in bilinear products */
    2867 for( j = 0; j < sepadata->nbilinvars && (sepadata->maxusedvars < 0 || j < sepadata->maxusedvars); ++j )
    2868 {
    2869 xj = sepadata->varssorted[j];
    2870
    2871 /* mark all rows for multiplier xj */
    2872 SCIP_CALL( markRowsXj(scip, sepadata, conshdlr, sol, j, allowlocal, row_to_pos, bestunderestimators,
    2873 bestoverestimators, row_marks, row_idcs, &nmarked) );
    2874
    2875 assert(nmarked <= nrows);
    2876
    2877 /* generate the projected cut and if it is violated, generate the actual cut */
    2878 for( r = 0; r < nmarked; ++r )
    2879 {
    2880 int pos;
    2881 int currentnunknown;
    2882 SCIP_ROW* row;
    2883
    2884 assert(row_marks[r] != 0);
    2885 assert(SCIPhashmapExists(row_to_pos, (void*)(size_t) row_idcs[r])); /*lint !e571 */
    2886
    2887 pos = SCIPhashmapGetImageInt(row_to_pos, (void*)(size_t) row_idcs[r]); /*lint !e571 */
    2888 row = rows[pos];
    2889 assert(SCIProwGetIndex(row) == row_idcs[r]);
    2890
    2891 /* check whether this row and var fulfill the conditions */
    2892 SCIP_CALL( isAcceptableRow(sepadata, row, xj, &currentnunknown, &accepted) );
    2893 if( !accepted )
    2894 {
    2895 SCIPdebugMsg(scip, "rejected row <%s> for variable <%s> (introduces too many new products)\n", SCIProwGetName(row), SCIPvarGetName(xj));
    2896 row_marks[r] = 0;
    2897 continue;
    2898 }
    2899
    2900 SCIPdebugMsg(scip, "accepted row <%s> for variable <%s>\n", SCIProwGetName(rows[r]), SCIPvarGetName(xj));
    2901#ifdef SCIP_DEBUG
    2902 SCIP_CALL( SCIPprintRow(scip, rows[r], NULL) );
    2903#endif
    2904 iseqrow = SCIPisEQ(scip, SCIProwGetLhs(row), SCIProwGetRhs(row));
    2905
    2906 /* if all terms are known and it is an equality row, compute equality cut, that is, multiply row with (x-lb) and/or (ub-x) (but see also @todo at top)
    2907 * otherwise, multiply row w.r.t. lhs and/or rhs with (x-lb) and/or (ub-x) and estimate product terms that have no aux.var or aux.expr
    2908 */
    2909 buildeqcut = (currentnunknown == 0 && iseqrow);
    2910
    2911 /* go over all suitable combinations of sides and bounds and compute the respective cuts */
    2912 for( k = 0; k < 4; ++k )
    2913 {
    2914 /* if equality cuts are possible, lhs and rhs cuts are equal so skip rhs */
    2915 if( buildeqcut )
    2916 {
    2917 if( k != 1 )
    2918 continue;
    2919 }
    2920 /* otherwise which cuts are generated depends on the marks */
    2921 else
    2922 {
    2923 if( row_marks[r] == 1 && uselb[k] == uselhs[k] )
    2924 continue;
    2925
    2926 if( row_marks[r] == 2 && uselb[k] != uselhs[k] )
    2927 continue;
    2928 }
    2929
    2930 success = TRUE;
    2931 cut = NULL;
    2932
    2933 SCIPdebugMsg(scip, "row <%s>, uselb = %u, uselhs = %u\n", SCIProwGetName(row), uselb[k], uselhs[k]);
    2934
    2935 if( sepadata->useprojection )
    2936 {
    2937 /* if no variables are left in the projected row, the RLT cut will not be violated */
    2938 if( projrows[pos].nnonz == 0 )
    2939 continue;
    2940
    2941 /* compute the rlt cut for a projected row first */
    2942 SCIP_CALL( computeRltCut(scip, sepa, sepadata, &cut, NULL, &(projrows[pos]), sol, bestunderestimators,
    2943 bestoverestimators, xj, &success, uselb[k], uselhs[k], allowlocal, buildeqcut, TRUE) );
    2944
    2945 /* if the projected cut is not violated, set success to FALSE */
    2946 if( cut != NULL )
    2947 {
    2948 SCIPdebugMsg(scip, "proj cut viol = %g\n", -SCIPgetRowFeasibility(scip, cut));
    2949 }
    2950 if( cut != NULL && !SCIPisFeasPositive(scip, -SCIPgetRowFeasibility(scip, cut)) )
    2951 {
    2952 SCIPdebugMsg(scip, "projected cut is not violated, feasibility = %g\n", SCIPgetRowFeasibility(scip, cut));
    2953 success = FALSE;
    2954 }
    2955
    2956 /* release the projected cut */
    2957 if( cut != NULL )
    2958 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
    2959 }
    2960
    2961 /* if we don't use projection or if the projected cut was generated successfully and is violated,
    2962 * generate the actual cut */
    2963 if( success )
    2964 {
    2965 SCIP_CALL( computeRltCut(scip, sepa, sepadata, &cut, row, NULL, sol, bestunderestimators,
    2966 bestoverestimators, xj, &success, uselb[k], uselhs[k], allowlocal, buildeqcut, FALSE) );
    2967 }
    2968
    2969 if( success )
    2970 {
    2971 success = SCIPisFeasNegative(scip, SCIPgetRowFeasibility(scip, cut)) || (sepadata->addtopool &&
    2972 !SCIProwIsLocal(cut));
    2973 }
    2974
    2975 /* if the cut was created successfully and is violated or (if addtopool == TRUE) globally valid,
    2976 * it is added to the cuts array */
    2977 if( success )
    2978 {
    2979 if( ncuts + 1 > cutssize )
    2980 {
    2981 int newsize;
    2982
    2983 newsize = SCIPcalcMemGrowSize(scip, ncuts + 1);
    2984 SCIP_CALL( SCIPreallocBufferArray(scip, &cuts, newsize) );
    2985 cutssize = newsize;
    2986 }
    2987 cuts[ncuts] = cut;
    2988 (ncuts)++;
    2989 }
    2990 else
    2991 {
    2992 SCIPdebugMsg(scip, "the generation of the cut failed or cut not violated and not added to cutpool\n");
    2993 /* release the cut */
    2994 if( cut != NULL )
    2995 {
    2996 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
    2997 }
    2998 }
    2999 }
    3000
    3001 /* clear row_marks[r] since it will be used for the next multiplier */
    3002 row_marks[r] = 0;
    3003 }
    3004 }
    3005
    3006 /* if cuts were found, we apply an additional filtering procedure, which is similar to sepastore */
    3007 if( ncuts > 0 )
    3008 {
    3009 int nselectedcuts;
    3010 int i;
    3011
    3012 assert(cuts != NULL);
    3013
    3014 SCIP_CALL( SCIPselectCutsHybrid(scip, cuts, NULL, NULL, sepadata->goodscore, sepadata->badscore, sepadata->goodmaxparall,
    3015 sepadata->maxparall, sepadata->dircutoffdistweight, sepadata->efficacyweight, sepadata->objparalweight,
    3016 0.0, ncuts, 0, sepadata->maxncuts == -1 ? ncuts : sepadata->maxncuts, &nselectedcuts) );
    3017
    3018 for( i = 0; i < ncuts; ++i )
    3019 {
    3020 assert(cuts[i] != NULL);
    3021
    3022 if( i < nselectedcuts )
    3023 {
    3024 /* if selected, add global cuts to the pool and local cuts to the sepastore */
    3025 if( SCIProwIsLocal(cuts[i]) || !sepadata->addtopool )
    3026 {
    3027 SCIP_CALL( SCIPaddRow(scip, cuts[i], FALSE, &infeasible) );
    3028
    3029 if( infeasible )
    3030 {
    3031 SCIPdebugMsg(scip, "CUTOFF! The cut <%s> revealed infeasibility\n", SCIProwGetName(cuts[i]));
    3032 *result = SCIP_CUTOFF;
    3033 }
    3034 else
    3035 {
    3036 SCIPdebugMsg(scip, "SEPARATED: added cut to scip\n");
    3037 *result = SCIP_SEPARATED;
    3038 }
    3039 }
    3040 else
    3041 {
    3042 SCIP_CALL( SCIPaddPoolCut(scip, cuts[i]) );
    3043 }
    3044 }
    3045
    3046 /* release current cut */
    3047 SCIP_CALL( SCIPreleaseRow(scip, &cuts[i]) );
    3048 }
    3049 }
    3050
    3051 SCIPdebugMsg(scip, "exit separator because cut calculation is finished\n");
    3052
    3054 SCIPfreeBufferArray(scip, &row_idcs);
    3055 SCIPfreeCleanBufferArray(scip, &row_marks);
    3056
    3057 return SCIP_OKAY;
    3058}
    3059
    3060/*
    3061 * Callback methods of separator
    3062 */
    3063
    3064/** copy method for separator plugins (called when SCIP copies plugins) */
    3065static
    3067{ /*lint --e{715}*/
    3068 assert(scip != NULL);
    3069 assert(sepa != NULL);
    3070
    3072
    3073 /* call inclusion method of separator */
    3075
    3076 return SCIP_OKAY;
    3077}
    3078
    3079/** destructor of separator to free user data (called when SCIP is exiting) */
    3080static
    3082{ /*lint --e{715}*/
    3083 SCIP_SEPADATA* sepadata;
    3084
    3086
    3087 sepadata = SCIPsepaGetData(sepa);
    3088 assert(sepadata != NULL);
    3089
    3090 /* free separator data */
    3091 SCIPfreeBlockMemory(scip, &sepadata);
    3092
    3093 SCIPsepaSetData(sepa, NULL);
    3094
    3095 return SCIP_OKAY;
    3096}
    3097
    3098/** solving process deinitialization method of separator (called before branch and bound process data is freed) */
    3099static
    3101{ /*lint --e{715}*/
    3102 SCIP_SEPADATA* sepadata;
    3103
    3105
    3106 sepadata = SCIPsepaGetData(sepa);
    3107 assert(sepadata != NULL);
    3108
    3109 if( sepadata->iscreated )
    3110 {
    3111 SCIP_CALL( freeSepaData(scip, sepadata) );
    3112 }
    3113
    3114 return SCIP_OKAY;
    3115}
    3116
    3117/** LP solution separation method of separator */
    3118static
    3120{ /*lint --e{715}*/
    3121 SCIP_ROW** prob_rows;
    3122 SCIP_ROW** rows;
    3123 SCIP_SEPADATA* sepadata;
    3124 int ncalls;
    3125 int nrows;
    3126 SCIP_HASHMAP* row_to_pos;
    3127 RLT_SIMPLEROW* projrows;
    3128
    3130
    3131 sepadata = SCIPsepaGetData(sepa);
    3132 *result = SCIP_DIDNOTRUN;
    3133
    3134 if( sepadata->maxncuts == 0 )
    3135 {
    3136 SCIPdebugMsg(scip, "exit separator because maxncuts is set to 0\n");
    3137 return SCIP_OKAY;
    3138 }
    3139
    3140 /* don't run in a sub-SCIP or in probing */
    3141 if( SCIPgetSubscipDepth(scip) > 0 && !sepadata->useinsubscip )
    3142 {
    3143 SCIPdebugMsg(scip, "exit separator because in sub-SCIP\n");
    3144 return SCIP_OKAY;
    3145 }
    3146
    3147 /* don't run in probing */
    3148 if( SCIPinProbing(scip) )
    3149 {
    3150 SCIPdebugMsg(scip, "exit separator because in probing\n");
    3151 return SCIP_OKAY;
    3152 }
    3153
    3154 /* only call separator a given number of times at each node */
    3155 ncalls = SCIPsepaGetNCallsAtNode(sepa);
    3156 if( (depth == 0 && sepadata->maxroundsroot >= 0 && ncalls >= sepadata->maxroundsroot)
    3157 || (depth > 0 && sepadata->maxrounds >= 0 && ncalls >= sepadata->maxrounds) )
    3158 {
    3159 SCIPdebugMsg(scip, "exit separator because round limit for this node is reached\n");
    3160 return SCIP_OKAY;
    3161 }
    3162
    3163 /* if this is called for the first time, create the sepadata and start the initial separation round */
    3164 if( !sepadata->iscreated )
    3165 {
    3166 *result = SCIP_DIDNOTFIND;
    3167 SCIP_CALL( createSepaData(scip, sepadata) );
    3168 }
    3169 assert(sepadata->iscreated || (sepadata->nbilinvars == 0 && sepadata->nbilinterms == 0));
    3170 assert(sepadata->nbilinterms == SCIPgetNBilinTermsNonlinear(sepadata->conshdlr));
    3171
    3172 /* no bilinear terms available -> skip */
    3173 if( sepadata->nbilinvars == 0 )
    3174 {
    3175 SCIPdebugMsg(scip, "exit separator because there are no known bilinear terms\n");
    3176 return SCIP_OKAY;
    3177 }
    3178
    3179 /* only call separator, if we are not close to terminating */
    3180 if( SCIPisStopped(scip) )
    3181 {
    3182 SCIPdebugMsg(scip, "exit separator because we are too close to terminating\n");
    3183 return SCIP_OKAY;
    3184 }
    3185
    3186 /* only call separator, if an optimal LP solution is at hand */
    3188 {
    3189 SCIPdebugMsg(scip, "exit separator because there is no LP solution at hand\n");
    3190 return SCIP_OKAY;
    3191 }
    3192
    3193 /* get the rows, depending on settings */
    3194 if( sepadata->isinitialround || sepadata->onlyoriginal )
    3195 {
    3196 SCIP_CALL( getOriginalRows(scip, &prob_rows, &nrows) );
    3197 }
    3198 else
    3199 {
    3200 SCIP_CALL( SCIPgetLPRowsData(scip, &prob_rows, &nrows) );
    3201 }
    3202
    3203 /* save the suitable rows */
    3204 SCIP_CALL( SCIPallocBufferArray(scip, &rows, nrows) );
    3205 SCIP_CALL( SCIPhashmapCreate(&row_to_pos, SCIPblkmem(scip), nrows) );
    3206
    3207 SCIP_CALL( storeSuitableRows(scip, sepa, sepadata, prob_rows, rows, &nrows, row_to_pos, allowlocal) );
    3208
    3209 if( nrows == 0 ) /* no suitable rows found, free memory and exit */
    3210 {
    3211 SCIPhashmapFree(&row_to_pos);
    3212 SCIPfreeBufferArray(scip, &rows);
    3213 if( sepadata->isinitialround || sepadata->onlyoriginal )
    3214 {
    3215 SCIPfreeBufferArray(scip, &prob_rows);
    3216 sepadata->isinitialround = FALSE;
    3217 }
    3218 return SCIP_OKAY;
    3219 }
    3220
    3221 /* create the projected problem */
    3222 if( sepadata->useprojection )
    3223 {
    3224 SCIP_Bool allcst;
    3225
    3226 SCIP_CALL( createProjRows(scip, rows, nrows, NULL, &projrows, allowlocal, &allcst) );
    3227
    3228 /* if all projected rows have only constants left, quit */
    3229 if( allcst )
    3230 goto TERMINATE;
    3231
    3232#ifdef SCIP_DEBUG
    3233 printProjRows(scip, projrows, nrows, NULL);
    3234#endif
    3235 }
    3236 else
    3237 {
    3238 projrows = NULL;
    3239 }
    3240
    3241 /* separate the cuts */
    3242 if( sepadata->detecthidden )
    3243 {
    3244 int* bestunderestimators;
    3245 int* bestoverestimators;
    3246
    3247 /* if we detect implicit products, a term might have more than one estimator in each direction;
    3248 * save the indices of the most violated estimators
    3249 */
    3250 SCIP_CALL( SCIPallocBufferArray(scip, &bestunderestimators, sepadata->nbilinterms) );
    3251 SCIP_CALL( SCIPallocBufferArray(scip, &bestoverestimators, sepadata->nbilinterms) );
    3252 getBestEstimators(scip, sepadata, NULL, bestunderestimators, bestoverestimators);
    3253
    3254 /* also separate McCormick cuts for implicit products */
    3255 SCIP_CALL( separateMcCormickImplicit(scip, sepa, sepadata, NULL, bestunderestimators, bestoverestimators,
    3256 result) );
    3257
    3258 if( *result != SCIP_CUTOFF )
    3259 {
    3260 SCIP_CALL( separateRltCuts(scip, sepa, sepadata, sepadata->conshdlr, NULL, row_to_pos, projrows, rows, nrows,
    3261 allowlocal, bestunderestimators, bestoverestimators, result) );
    3262 }
    3263
    3264 SCIPfreeBufferArray(scip, &bestoverestimators);
    3265 SCIPfreeBufferArray(scip, &bestunderestimators);
    3266 }
    3267 else
    3268 {
    3269 SCIP_CALL( separateRltCuts(scip, sepa, sepadata, sepadata->conshdlr, NULL, row_to_pos, projrows, rows, nrows,
    3270 allowlocal, NULL, NULL, result) );
    3271 }
    3272
    3273 TERMINATE:
    3274 /* free the projected problem */
    3275 if( sepadata->useprojection )
    3276 {
    3277 freeProjRows(scip, &projrows, nrows);
    3278 }
    3279
    3280 SCIPhashmapFree(&row_to_pos);
    3281 SCIPfreeBufferArray(scip, &rows);
    3282
    3283 if( sepadata->isinitialround || sepadata->onlyoriginal )
    3284 {
    3285 SCIPfreeBufferArray(scip, &prob_rows);
    3286 sepadata->isinitialround = FALSE;
    3287 }
    3288
    3289 return SCIP_OKAY;
    3290}
    3291
    3292/*
    3293 * separator specific interface methods
    3294 */
    3295
    3296/** creates the RLT separator and includes it in SCIP */
    3298 SCIP* scip /**< SCIP data structure */
    3299 )
    3300{
    3301 SCIP_SEPADATA* sepadata;
    3302 SCIP_SEPA* sepa;
    3303
    3304 /* create RLT separator data */
    3306 sepadata->conshdlr = SCIPfindConshdlr(scip, "nonlinear");
    3307 assert(sepadata->conshdlr != NULL);
    3308
    3309 /* include separator */
    3311 SEPA_USESSUBSCIP, SEPA_DELAY, sepaExeclpRlt, NULL, sepadata) );
    3312
    3313 /* set non fundamental callbacks via setter functions */
    3314 SCIP_CALL( SCIPsetSepaCopy(scip, sepa, sepaCopyRlt) );
    3315 SCIP_CALL( SCIPsetSepaFree(scip, sepa, sepaFreeRlt) );
    3316 SCIP_CALL( SCIPsetSepaExitsol(scip, sepa, sepaExitsolRlt) );
    3317
    3318 /* add RLT separator parameters */
    3320 "separating/" SEPA_NAME "/maxncuts",
    3321 "maximal number of rlt-cuts that are added per round (-1: unlimited)",
    3322 &sepadata->maxncuts, FALSE, DEFAULT_MAXNCUTS, -1, INT_MAX, NULL, NULL) );
    3323
    3325 "separating/" SEPA_NAME "/maxunknownterms",
    3326 "maximal number of unknown bilinear terms a row is still used with (-1: unlimited)",
    3327 &sepadata->maxunknownterms, FALSE, DEFAULT_MAXUNKNOWNTERMS, -1, INT_MAX, NULL, NULL) );
    3328
    3330 "separating/" SEPA_NAME "/maxusedvars",
    3331 "maximal number of variables used to compute rlt cuts (-1: unlimited)",
    3332 &sepadata->maxusedvars, FALSE, DEFAULT_MAXUSEDVARS, -1, INT_MAX, NULL, NULL) );
    3333
    3335 "separating/" SEPA_NAME "/maxrounds",
    3336 "maximal number of separation rounds per node (-1: unlimited)",
    3337 &sepadata->maxrounds, FALSE, DEFAULT_MAXROUNDS, -1, INT_MAX, NULL, NULL) );
    3338
    3340 "separating/" SEPA_NAME "/maxroundsroot",
    3341 "maximal number of separation rounds in the root node (-1: unlimited)",
    3342 &sepadata->maxroundsroot, FALSE, DEFAULT_MAXROUNDSROOT, -1, INT_MAX, NULL, NULL) );
    3343
    3345 "separating/" SEPA_NAME "/onlyeqrows",
    3346 "if set to true, only equality rows are used for rlt cuts",
    3347 &sepadata->onlyeqrows, FALSE, DEFAULT_ONLYEQROWS, NULL, NULL) );
    3348
    3350 "separating/" SEPA_NAME "/onlycontrows",
    3351 "if set to true, only continuous rows are used for rlt cuts",
    3352 &sepadata->onlycontrows, FALSE, DEFAULT_ONLYCONTROWS, NULL, NULL) );
    3353
    3355 "separating/" SEPA_NAME "/onlyoriginal",
    3356 "if set to true, only original rows and variables are used",
    3357 &sepadata->onlyoriginal, FALSE, DEFAULT_ONLYORIGINAL, NULL, NULL) );
    3358
    3360 "separating/" SEPA_NAME "/useinsubscip",
    3361 "if set to true, rlt is also used in sub-scips",
    3362 &sepadata->useinsubscip, FALSE, DEFAULT_USEINSUBSCIP, NULL, NULL) );
    3363
    3365 "separating/" SEPA_NAME "/useprojection",
    3366 "if set to true, projected rows are checked first",
    3367 &sepadata->useprojection, FALSE, DEFAULT_USEPROJECTION, NULL, NULL) );
    3368
    3370 "separating/" SEPA_NAME "/detecthidden",
    3371 "if set to true, hidden products are detected and separated by McCormick cuts",
    3372 &sepadata->detecthidden, FALSE, DEFAULT_DETECTHIDDEN, NULL, NULL) );
    3373
    3375 "separating/" SEPA_NAME "/hiddenrlt",
    3376 "whether RLT cuts (TRUE) or only McCormick inequalities (FALSE) should be added for hidden products",
    3377 &sepadata->hiddenrlt, FALSE, DEFAULT_HIDDENRLT, NULL, NULL) );
    3378
    3380 "separating/" SEPA_NAME "/addtopool",
    3381 "if set to true, globally valid RLT cuts are added to the global cut pool",
    3382 &sepadata->addtopool, FALSE, DEFAULT_ADDTOPOOL, NULL, NULL) );
    3383
    3385 "separating/" SEPA_NAME "/goodscore",
    3386 "threshold for score of cut relative to best score to be considered good, so that less strict filtering is applied",
    3387 &sepadata->goodscore, TRUE, DEFAULT_GOODSCORE, 0.0, 1.0, NULL, NULL) );
    3388
    3390 "separating/" SEPA_NAME "/badscore",
    3391 "threshold for score of cut relative to best score to be discarded",
    3392 &sepadata->badscore, TRUE, DEFAULT_BADSCORE, 0.0, 1.0, NULL, NULL) );
    3393
    3395 "separating/" SEPA_NAME "/objparalweight",
    3396 "weight of objective parallelism in cut score calculation",
    3397 &sepadata->objparalweight, TRUE, DEFAULT_OBJPARALWEIGHT, 0.0, 1.0, NULL, NULL) );
    3398
    3400 "separating/" SEPA_NAME "/efficacyweight",
    3401 "weight of efficacy in cut score calculation",
    3402 &sepadata->efficacyweight, TRUE, DEFAULT_EFFICACYWEIGHT, 0.0, 1.0, NULL, NULL) );
    3403
    3405 "separating/" SEPA_NAME "/dircutoffdistweight",
    3406 "weight of directed cutoff distance in cut score calculation",
    3407 &sepadata->dircutoffdistweight, TRUE, DEFAULT_DIRCUTOFFDISTWEIGHT, 0.0, 1.0, NULL, NULL) );
    3408
    3410 "separating/" SEPA_NAME "/goodmaxparall",
    3411 "maximum parallelism for good cuts",
    3412 &sepadata->goodmaxparall, TRUE, DEFAULT_GOODMAXPARALL, 0.0, 1.0, NULL, NULL) );
    3413
    3415 "separating/" SEPA_NAME "/maxparall",
    3416 "maximum parallelism for non-good cuts",
    3417 &sepadata->maxparall, TRUE, DEFAULT_MAXPARALL, 0.0, 1.0, NULL, NULL) );
    3418
    3419 return SCIP_OKAY;
    3420}
    SCIP_VAR * w
    Definition: circlepacking.c:67
    SCIP_VAR * a
    Definition: circlepacking.c:66
    SCIP_VAR ** y
    Definition: circlepacking.c:64
    SCIP_Real * r
    Definition: circlepacking.c:59
    SCIP_VAR ** x
    Definition: circlepacking.c:63
    constraint handler for nonlinear constraints specified by algebraic expressions
    hybrid cut selector
    #define NULL
    Definition: def.h:257
    #define SCIP_MAXSTRLEN
    Definition: def.h:278
    #define SCIP_INVALID
    Definition: def.h:187
    #define SCIP_Bool
    Definition: def.h:100
    #define MIN(x, y)
    Definition: def.h:233
    #define MAX3(x, y, z)
    Definition: def.h:237
    #define SCIP_STRINGEQ(name, reference, retcode)
    Definition: def.h:454
    #define SCIP_Real
    Definition: def.h:165
    #define TRUE
    Definition: def.h:102
    #define FALSE
    Definition: def.h:103
    #define MAX(x, y)
    Definition: def.h:229
    #define SCIP_LONGINT_FORMAT
    Definition: def.h:157
    #define MIN3(x, y, z)
    Definition: def.h:241
    #define REALABS(x)
    Definition: def.h:191
    #define SCIP_CALL(x)
    Definition: def.h:364
    void SCIPaddSquareLinearization(SCIP *scip, SCIP_Real sqrcoef, SCIP_Real refpoint, SCIP_Bool isint, SCIP_Real *lincoef, SCIP_Real *linconstant, SCIP_Bool *success)
    Definition: expr_pow.c:3246
    void SCIPaddSquareSecant(SCIP *scip, SCIP_Real sqrcoef, SCIP_Real lb, SCIP_Real ub, SCIP_Real *lincoef, SCIP_Real *linconstant, SCIP_Bool *success)
    Definition: expr_pow.c:3314
    power and signed power expression handlers
    SCIP_Real SCIPevalBilinAuxExprNonlinear(SCIP *scip, SCIP_VAR *x, SCIP_VAR *y, SCIP_CONSNONLINEAR_AUXEXPR *auxexpr, SCIP_SOL *sol)
    SCIP_RETCODE SCIPinsertBilinearTermImplicitNonlinear(SCIP *scip, SCIP_CONSHDLR *conshdlr, SCIP_VAR *x, SCIP_VAR *y, SCIP_VAR *auxvar, SCIP_Real coefx, SCIP_Real coefy, SCIP_Real coefaux, SCIP_Real cst, SCIP_Bool overestimate)
    int SCIPgetBilinTermIdxNonlinear(SCIP_CONSHDLR *conshdlr, SCIP_VAR *x, SCIP_VAR *y)
    SCIP_CONSNONLINEAR_BILINTERM * SCIPgetBilinTermsNonlinear(SCIP_CONSHDLR *conshdlr)
    int SCIPgetNBilinTermsNonlinear(SCIP_CONSHDLR *conshdlr)
    SCIP_RETCODE SCIPselectCutsHybrid(SCIP *scip, SCIP_ROW **cuts, SCIP_ROW **forcedcuts, SCIP_RANDNUMGEN *randnumgen, SCIP_Real goodscorefac, SCIP_Real badscorefac, SCIP_Real goodmaxparall, SCIP_Real maxparall, SCIP_Real dircutoffdistweight, SCIP_Real efficacyweight, SCIP_Real objparalweight, SCIP_Real intsupportweight, int ncuts, int nforcedcuts, int maxselectedcuts, int *nselectedcuts)
    int SCIPgetSubscipDepth(SCIP *scip)
    Definition: scip_copy.c:2589
    SCIP_Bool SCIPisStopped(SCIP *scip)
    Definition: scip_general.c:767
    SCIP_CONS ** SCIPgetConss(SCIP *scip)
    Definition: scip_prob.c:3666
    int SCIPgetNVars(SCIP *scip)
    Definition: scip_prob.c:2246
    int SCIPgetNConss(SCIP *scip)
    Definition: scip_prob.c:3620
    SCIP_VAR ** SCIPgetVars(SCIP *scip)
    Definition: scip_prob.c:2201
    int SCIPgetNBinVars(SCIP *scip)
    Definition: scip_prob.c:2293
    void SCIPhashmapFree(SCIP_HASHMAP **hashmap)
    Definition: misc.c:3095
    void * SCIPhashmapEntryGetImage(SCIP_HASHMAPENTRY *entry)
    Definition: misc.c:3613
    int SCIPhashmapGetImageInt(SCIP_HASHMAP *hashmap, void *origin)
    Definition: misc.c:3304
    void * SCIPhashmapGetImage(SCIP_HASHMAP *hashmap, void *origin)
    Definition: misc.c:3284
    SCIP_RETCODE SCIPhashmapInsert(SCIP_HASHMAP *hashmap, void *origin, void *image)
    Definition: misc.c:3143
    int SCIPhashmapGetNEntries(SCIP_HASHMAP *hashmap)
    Definition: misc.c:3584
    SCIP_HASHMAPENTRY * SCIPhashmapGetEntry(SCIP_HASHMAP *hashmap, int entryidx)
    Definition: misc.c:3592
    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 SCIPhashmapSetImageInt(SCIP_HASHMAP *hashmap, void *origin, int image)
    Definition: misc.c:3400
    void SCIPhashtableFree(SCIP_HASHTABLE **hashtable)
    Definition: misc.c:2348
    int SCIPhashtableGetNEntries(SCIP_HASHTABLE *hashtable)
    Definition: misc.c:2765
    #define SCIPhashFour(a, b, c, d)
    Definition: pub_misc.h:573
    void * SCIPhashtableGetEntry(SCIP_HASHTABLE *hashtable, int entryidx)
    Definition: misc.c:2773
    SCIP_RETCODE SCIPhashtableCreate(SCIP_HASHTABLE **hashtable, BMS_BLKMEM *blkmem, int tablesize, SCIP_DECL_HASHGETKEY((*hashgetkey)), SCIP_DECL_HASHKEYEQ((*hashkeyeq)), SCIP_DECL_HASHKEYVAL((*hashkeyval)), void *userptr)
    Definition: misc.c:2298
    void * SCIPhashtableRetrieve(SCIP_HASHTABLE *hashtable, void *key)
    Definition: misc.c:2596
    SCIP_RETCODE SCIPhashtableInsert(SCIP_HASHTABLE *hashtable, void *element)
    Definition: misc.c:2535
    void SCIPinfoMessage(SCIP *scip, FILE *file, const char *formatstr,...)
    Definition: scip_message.c:208
    #define SCIPdebugMsg
    Definition: scip_message.h:78
    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)
    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 SCIPaddRealParam(SCIP *scip, const char *name, const char *desc, SCIP_Real *valueptr, SCIP_Bool isadvanced, SCIP_Real defaultvalue, SCIP_Real minvalue, SCIP_Real maxvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
    Definition: scip_param.c:139
    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
    void SCIPswapReals(SCIP_Real *value1, SCIP_Real *value2)
    Definition: misc.c:10498
    SCIP_VAR * SCIPcolGetVar(SCIP_COL *col)
    Definition: lp.c:17425
    SCIP_Bool SCIPcolIsIntegral(SCIP_COL *col)
    Definition: lp.c:17455
    int SCIPcolGetNNonz(SCIP_COL *col)
    Definition: lp.c:17520
    SCIP_Real * SCIPcolGetVals(SCIP_COL *col)
    Definition: lp.c:17555
    SCIP_ROW ** SCIPcolGetRows(SCIP_COL *col)
    Definition: lp.c:17545
    SCIP_CONSHDLR * SCIPfindConshdlr(SCIP *scip, const char *name)
    Definition: scip_cons.c:940
    SCIP_RETCODE SCIPaddPoolCut(SCIP *scip, SCIP_ROW *row)
    Definition: scip_cut.c:336
    SCIP_RETCODE SCIPaddRow(SCIP *scip, SCIP_ROW *row, SCIP_Bool forcecut, SCIP_Bool *infeasible)
    Definition: scip_cut.c:225
    SCIP_RETCODE SCIPgetLPRowsData(SCIP *scip, SCIP_ROW ***rows, int *nrows)
    Definition: scip_lp.c:576
    SCIP_LPSOLSTAT SCIPgetLPSolstat(SCIP *scip)
    Definition: scip_lp.c:174
    #define SCIPfreeCleanBufferArray(scip, ptr)
    Definition: scip_mem.h:146
    #define SCIPfreeBuffer(scip, ptr)
    Definition: scip_mem.h:134
    #define SCIPallocCleanBufferArray(scip, ptr, num)
    Definition: scip_mem.h:142
    #define SCIPfreeBlockMemoryArray(scip, ptr, num)
    Definition: scip_mem.h:110
    #define SCIPallocClearBlockMemory(scip, ptr)
    Definition: scip_mem.h:91
    BMS_BLKMEM * SCIPblkmem(SCIP *scip)
    Definition: scip_mem.c:57
    #define SCIPensureBlockMemoryArray(scip, ptr, arraysizeptr, minsize)
    Definition: scip_mem.h:107
    int SCIPcalcMemGrowSize(SCIP *scip, int num)
    Definition: scip_mem.c:139
    #define SCIPallocBufferArray(scip, ptr, num)
    Definition: scip_mem.h:124
    #define SCIPreallocBufferArray(scip, ptr, num)
    Definition: scip_mem.h:128
    #define SCIPfreeBufferArray(scip, ptr)
    Definition: scip_mem.h:136
    #define SCIPallocBlockMemoryArray(scip, ptr, num)
    Definition: scip_mem.h:93
    #define SCIPallocBuffer(scip, ptr)
    Definition: scip_mem.h:122
    #define SCIPreallocBlockMemoryArray(scip, ptr, oldnum, newnum)
    Definition: scip_mem.h:99
    #define SCIPfreeBlockMemory(scip, ptr)
    Definition: scip_mem.h:108
    #define SCIPfreeBufferArrayNull(scip, ptr)
    Definition: scip_mem.h:137
    #define SCIPduplicateBlockMemoryArray(scip, ptr, source, num)
    Definition: scip_mem.h:105
    SCIP_Bool SCIPinProbing(SCIP *scip)
    Definition: scip_probing.c:98
    SCIP_Real SCIProwGetLhs(SCIP_ROW *row)
    Definition: lp.c:17686
    SCIP_RETCODE SCIPcacheRowExtensions(SCIP *scip, SCIP_ROW *row)
    Definition: scip_lp.c:1581
    SCIP_RETCODE SCIPchgRowLhs(SCIP *scip, SCIP_ROW *row, SCIP_Real lhs)
    Definition: scip_lp.c:1529
    SCIP_Real SCIPgetRowFeasibility(SCIP *scip, SCIP_ROW *row)
    Definition: scip_lp.c:2088
    int SCIProwGetNNonz(SCIP_ROW *row)
    Definition: lp.c:17607
    SCIP_COL ** SCIProwGetCols(SCIP_ROW *row)
    Definition: lp.c:17632
    SCIP_Real SCIProwGetRhs(SCIP_ROW *row)
    Definition: lp.c:17696
    SCIP_RETCODE SCIPflushRowExtensions(SCIP *scip, SCIP_ROW *row)
    Definition: scip_lp.c:1604
    SCIP_Bool SCIProwIsLocal(SCIP_ROW *row)
    Definition: lp.c:17795
    SCIP_RETCODE SCIPaddVarToRow(SCIP *scip, SCIP_ROW *row, SCIP_VAR *var, SCIP_Real val)
    Definition: scip_lp.c:1646
    SCIP_RETCODE SCIPprintRow(SCIP *scip, SCIP_ROW *row, FILE *file)
    Definition: scip_lp.c:2176
    const char * SCIProwGetName(SCIP_ROW *row)
    Definition: lp.c:17745
    SCIP_SEPA * SCIProwGetOriginSepa(SCIP_ROW *row)
    Definition: lp.c:17870
    SCIP_RETCODE SCIPreleaseRow(SCIP *scip, SCIP_ROW **row)
    Definition: scip_lp.c:1508
    SCIP_RETCODE SCIPcreateEmptyRowSepa(SCIP *scip, SCIP_ROW **row, SCIP_SEPA *sepa, const char *name, SCIP_Real lhs, SCIP_Real rhs, SCIP_Bool local, SCIP_Bool modifiable, SCIP_Bool removable)
    Definition: scip_lp.c:1429
    int SCIProwGetIndex(SCIP_ROW *row)
    Definition: lp.c:17755
    SCIP_RETCODE SCIPchgRowRhs(SCIP *scip, SCIP_ROW *row, SCIP_Real rhs)
    Definition: scip_lp.c:1553
    SCIP_Real SCIProwGetConstant(SCIP_ROW *row)
    Definition: lp.c:17652
    SCIP_Real * SCIProwGetVals(SCIP_ROW *row)
    Definition: lp.c:17642
    SCIP_RETCODE SCIPincludeSepaBasic(SCIP *scip, SCIP_SEPA **sepa, const char *name, const char *desc, int priority, int freq, SCIP_Real maxbounddist, SCIP_Bool usessubscip, SCIP_Bool delay, SCIP_DECL_SEPAEXECLP((*sepaexeclp)), SCIP_DECL_SEPAEXECSOL((*sepaexecsol)), SCIP_SEPADATA *sepadata)
    Definition: scip_sepa.c:115
    const char * SCIPsepaGetName(SCIP_SEPA *sepa)
    Definition: sepa.c:746
    int SCIPsepaGetNCallsAtNode(SCIP_SEPA *sepa)
    Definition: sepa.c:893
    SCIP_RETCODE SCIPsetSepaFree(SCIP *scip, SCIP_SEPA *sepa, SCIP_DECL_SEPAFREE((*sepafree)))
    Definition: scip_sepa.c:173
    SCIP_RETCODE SCIPsetSepaExitsol(SCIP *scip, SCIP_SEPA *sepa, SCIP_DECL_SEPAEXITSOL((*sepaexitsol)))
    Definition: scip_sepa.c:237
    SCIP_SEPADATA * SCIPsepaGetData(SCIP_SEPA *sepa)
    Definition: sepa.c:636
    void SCIPsepaSetData(SCIP_SEPA *sepa, SCIP_SEPADATA *sepadata)
    Definition: sepa.c:646
    SCIP_RETCODE SCIPsetSepaCopy(SCIP *scip, SCIP_SEPA *sepa, SCIP_DECL_SEPACOPY((*sepacopy)))
    Definition: scip_sepa.c:157
    SCIP_Real SCIPgetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var)
    Definition: scip_sol.c:1763
    SCIP_Longint SCIPgetNLPs(SCIP *scip)
    SCIP_Bool SCIPisRelEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
    SCIP_Real SCIPinfinity(SCIP *scip)
    SCIP_Bool SCIPisFeasEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
    SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
    SCIP_Bool SCIPisFeasLT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
    SCIP_Bool SCIPisFeasNegative(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_Bool SCIPisFeasPositive(SCIP *scip, SCIP_Real val)
    int SCIPgetDepth(SCIP *scip)
    Definition: scip_tree.c:672
    int SCIPvarGetNVlbs(SCIP_VAR *var)
    Definition: var.c:24514
    SCIP_COL * SCIPvarGetCol(SCIP_VAR *var)
    Definition: var.c:23715
    SCIP_Real * SCIPvarGetVlbCoefs(SCIP_VAR *var)
    Definition: var.c:24536
    int SCIPvarGetNImpls(SCIP_VAR *var, SCIP_Bool varfixing)
    Definition: var.c:24600
    SCIP_VARSTATUS SCIPvarGetStatus(SCIP_VAR *var)
    Definition: var.c:23418
    SCIP_Real SCIPvarGetUbLocal(SCIP_VAR *var)
    Definition: var.c:24300
    void SCIPvarGetImplicVarBounds(SCIP_VAR *var, SCIP_Bool varfixing, SCIP_VAR *implvar, SCIP_Real *lb, SCIP_Real *ub)
    Definition: var.c:16521
    SCIP_VARTYPE SCIPvarGetType(SCIP_VAR *var)
    Definition: var.c:23485
    SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
    Definition: var.c:24174
    SCIP_VAR ** SCIPvarGetImplVars(SCIP_VAR *var, SCIP_Bool varfixing)
    Definition: var.c:24617
    int SCIPvarGetIndex(SCIP_VAR *var)
    Definition: var.c:23684
    const char * SCIPvarGetName(SCIP_VAR *var)
    Definition: var.c:23299
    SCIP_RETCODE SCIPreleaseVar(SCIP *scip, SCIP_VAR **var)
    Definition: scip_var.c:1887
    SCIP_Real * SCIPvarGetVlbConstants(SCIP_VAR *var)
    Definition: var.c:24546
    int SCIPvarGetNVubs(SCIP_VAR *var)
    Definition: var.c:24556
    SCIP_Bool SCIPvarIsIntegral(SCIP_VAR *var)
    Definition: var.c:23522
    SCIP_Real * SCIPvarGetImplBounds(SCIP_VAR *var, SCIP_Bool varfixing)
    Definition: var.c:24646
    int SCIPvarGetNCliques(SCIP_VAR *var, SCIP_Bool varfixing)
    Definition: var.c:24674
    SCIP_Real SCIPvarGetLbLocal(SCIP_VAR *var)
    Definition: var.c:24266
    SCIP_Bool SCIPvarIsRelaxationOnly(SCIP_VAR *var)
    Definition: var.c:23632
    SCIP_VAR ** SCIPvarGetVlbVars(SCIP_VAR *var)
    Definition: var.c:24526
    SCIP_CLIQUE ** SCIPvarGetCliques(SCIP_VAR *var, SCIP_Bool varfixing)
    Definition: var.c:24685
    SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
    Definition: var.c:24152
    int SCIPvarCompare(SCIP_VAR *var1, SCIP_VAR *var2)
    Definition: var.c:17319
    SCIP_Real * SCIPvarGetVubConstants(SCIP_VAR *var)
    Definition: var.c:24588
    SCIP_VAR ** SCIPvarGetVubVars(SCIP_VAR *var)
    Definition: var.c:24568
    SCIP_Bool SCIPvarsHaveCommonClique(SCIP_VAR *var1, SCIP_Bool value1, SCIP_VAR *var2, SCIP_Bool value2, SCIP_Bool regardimplics)
    Definition: var.c:16852
    SCIP_Real * SCIPvarGetVubCoefs(SCIP_VAR *var)
    Definition: var.c:24578
    SCIP_BOUNDTYPE * SCIPvarGetImplTypes(SCIP_VAR *var, SCIP_Bool varfixing)
    Definition: var.c:24632
    SCIP_RETCODE SCIPcaptureVar(SCIP *scip, SCIP_VAR *var)
    Definition: scip_var.c:1853
    void SCIPselectDownIntPtr(int *intarray, void **ptrarray, int k, int len)
    SCIP_RETCODE SCIPincludeSepaRlt(SCIP *scip)
    Definition: sepa_rlt.c:3297
    SCIP_Bool SCIPsortedvecFindPtr(void **ptrarray, SCIP_DECL_SORTPTRCOMP((*ptrcomp)), void *val, int len, int *pos)
    SCIP_Bool SCIPsortedvecFindInt(int *intarray, int val, int len, int *pos)
    int SCIPsnprintf(char *t, int len, const char *s,...)
    Definition: misc.c:10827
    SCIP_Bool SCIPcliqueHasVar(SCIP_CLIQUE *clique, SCIP_VAR *var, SCIP_Bool value)
    Definition: implics.c:1141
    SCIP_ROW * SCIPconsGetRow(SCIP *scip, SCIP_CONS *cons)
    Definition: misc_linear.c:549
    bilinear nonlinear handler
    public methods for LP management
    #define SCIPstatisticMessage
    Definition: pub_message.h:123
    static SCIP_RETCODE extractProducts(SCIP *scip, SCIP_SEPADATA *sepadata, SCIP_VAR **vars_xwy, SCIP_Real *coefs1, SCIP_Real *coefs2, SCIP_Real d1, SCIP_Real d2, SCIP_SIDETYPE sidetype1, SCIP_SIDETYPE sidetype2, SCIP_HASHMAP *varmap, SCIP_Bool f)
    Definition: sepa_rlt.c:661
    #define SEPA_PRIORITY
    Definition: sepa_rlt.c:50
    #define DEFAULT_BADSCORE
    Definition: sepa_rlt.c:73
    static SCIP_RETCODE isAcceptableRow(SCIP_SEPADATA *sepadata, SCIP_ROW *row, SCIP_VAR *var, int *currentnunknown, SCIP_Bool *acceptable)
    Definition: sepa_rlt.c:1783
    static SCIP_RETCODE separateMcCormickImplicit(SCIP *scip, SCIP_SEPA *sepa, SCIP_SEPADATA *sepadata, SCIP_SOL *sol, int *bestunderestimators, int *bestoverestimators, SCIP_RESULT *result)
    Definition: sepa_rlt.c:2663
    static SCIP_DECL_HASHKEYVAL(hashdataKeyValConss)
    Definition: sepa_rlt.c:207
    #define DEFAULT_GOODSCORE
    Definition: sepa_rlt.c:71
    #define DEFAULT_MAXUSEDVARS
    Definition: sepa_rlt.c:58
    #define SEPA_DELAY
    Definition: sepa_rlt.c:55
    #define DEFAULT_EFFICACYWEIGHT
    Definition: sepa_rlt.c:75
    static SCIP_DECL_SEPAEXECLP(sepaExeclpRlt)
    Definition: sepa_rlt.c:3119
    #define DEFAULT_OBJPARALWEIGHT
    Definition: sepa_rlt.c:74
    static SCIP_DECL_SEPACOPY(sepaCopyRlt)
    Definition: sepa_rlt.c:3066
    static SCIP_VAR ** getAdjacentVars(SCIP_HASHMAP *adjvarmap, SCIP_VAR *var, int *nadjacentvars)
    Definition: sepa_rlt.c:312
    #define DEFAULT_USEPROJECTION
    Definition: sepa_rlt.c:66
    #define DEFAULT_DETECTHIDDEN
    Definition: sepa_rlt.c:67
    #define SEPA_DESC
    Definition: sepa_rlt.c:49
    static SCIP_RETCODE detectProductsClique(SCIP *scip, SCIP_SEPADATA *sepadata, SCIP_Real *coefs1, SCIP_VAR **vars_xwy, SCIP_Real side1, SCIP_SIDETYPE sidetype1, int varpos1, int varpos2, SCIP_HASHMAP *varmap, SCIP_Bool f)
    Definition: sepa_rlt.c:922
    static void implBndToBigM(SCIP *scip, SCIP_VAR **vars_xwy, int binvarpos, int implvarpos, SCIP_BOUNDTYPE bndtype, SCIP_Bool binval, SCIP_Real implbnd, SCIP_Real *coefs, SCIP_Real *side)
    Definition: sepa_rlt.c:808
    #define MAXVARBOUND
    Definition: sepa_rlt.c:80
    #define DEFAULT_HIDDENRLT
    Definition: sepa_rlt.c:68
    static SCIP_RETCODE separateRltCuts(SCIP *scip, SCIP_SEPA *sepa, SCIP_SEPADATA *sepadata, SCIP_CONSHDLR *conshdlr, SCIP_SOL *sol, SCIP_HASHMAP *row_to_pos, RLT_SIMPLEROW *projrows, SCIP_ROW **rows, int nrows, SCIP_Bool allowlocal, int *bestunderestimators, int *bestoverestimators, SCIP_RESULT *result)
    Definition: sepa_rlt.c:2820
    #define DEFAULT_MAXPARALL
    Definition: sepa_rlt.c:78
    static void freeProjRow(SCIP *scip, RLT_SIMPLEROW *simplerow)
    Definition: sepa_rlt.c:2319
    #define DEFAULT_MAXROUNDSROOT
    Definition: sepa_rlt.c:61
    static SCIP_RETCODE addRltTerm(SCIP *scip, SCIP_SEPADATA *sepadata, SCIP_SOL *sol, int *bestunderest, int *bestoverest, SCIP_ROW *cut, SCIP_VAR *var, SCIP_VAR *colvar, SCIP_Real coef, SCIP_Bool uselb, SCIP_Bool uselhs, SCIP_Bool local, SCIP_Bool computeEqCut, SCIP_Real *coefvar, SCIP_Real *cst, SCIP_Bool *success)
    Definition: sepa_rlt.c:1870
    #define SEPA_USESSUBSCIP
    Definition: sepa_rlt.c:54
    static SCIP_RETCODE detectProductsImplbnd(SCIP *scip, SCIP_SEPADATA *sepadata, SCIP_Real *coefs1, SCIP_VAR **vars_xwy, SCIP_Real side1, SCIP_SIDETYPE sidetype1, int binvarpos, int implvarpos, SCIP_HASHMAP *varmap, SCIP_Bool f)
    Definition: sepa_rlt.c:856
    static SCIP_RETCODE getOriginalRows(SCIP *scip, SCIP_ROW ***rows, int *nrows)
    Definition: sepa_rlt.c:409
    #define DEFAULT_ONLYORIGINAL
    Definition: sepa_rlt.c:64
    static SCIP_RETCODE fillRelationTables(SCIP *scip, SCIP_ROW **prob_rows, int nrows, SCIP_HASHTABLE *hashtable2, SCIP_HASHTABLE *hashtable3, SCIP_HASHMAP *vars_in_2rels, int *row_list)
    Definition: sepa_rlt.c:1099
    static SCIP_DECL_HASHKEYEQ(hashdataKeyEqConss)
    Definition: sepa_rlt.c:175
    static SCIP_RETCODE createProjRow(SCIP *scip, RLT_SIMPLEROW *simplerow, SCIP_ROW *row, SCIP_SOL *sol, SCIP_Bool local)
    Definition: sepa_rlt.c:2255
    static SCIP_RETCODE createProjRows(SCIP *scip, SCIP_ROW **rows, int nrows, SCIP_SOL *sol, RLT_SIMPLEROW **projrows, SCIP_Bool local, SCIP_Bool *allcst)
    Definition: sepa_rlt.c:2343
    #define DEFAULT_MAXNCUTS
    Definition: sepa_rlt.c:59
    static SCIP_RETCODE detectHiddenProducts(SCIP *scip, SCIP_SEPADATA *sepadata, SCIP_HASHMAP *varmap)
    Definition: sepa_rlt.c:1227
    static SCIP_RETCODE markRowsXj(SCIP *scip, SCIP_SEPADATA *sepadata, SCIP_CONSHDLR *conshdlr, SCIP_SOL *sol, int j, SCIP_Bool local, SCIP_HASHMAP *row_to_pos, int *bestunderest, int *bestoverest, unsigned int *row_marks, int *row_idcs, int *nmarked)
    Definition: sepa_rlt.c:2496
    static void addAuxexprCoefs(SCIP_VAR *var1, SCIP_VAR *var2, SCIP_CONSNONLINEAR_AUXEXPR *auxexpr, SCIP_Real coef, SCIP_Real *coefaux, SCIP_Real *coef1, SCIP_Real *coef2, SCIP_Real *cst)
    Definition: sepa_rlt.c:1829
    #define DEFAULT_USEINSUBSCIP
    Definition: sepa_rlt.c:65
    static SCIP_RETCODE computeRltCut(SCIP *scip, SCIP_SEPA *sepa, SCIP_SEPADATA *sepadata, SCIP_ROW **cut, SCIP_ROW *row, RLT_SIMPLEROW *projrow, SCIP_SOL *sol, int *bestunderest, int *bestoverest, SCIP_VAR *var, SCIP_Bool *success, SCIP_Bool uselb, SCIP_Bool uselhs, SCIP_Bool local, SCIP_Bool computeEqCut, SCIP_Bool useprojrow)
    Definition: sepa_rlt.c:2110
    #define DEFAULT_ADDTOPOOL
    Definition: sepa_rlt.c:69
    static SCIP_RETCODE createSepaData(SCIP *scip, SCIP_SEPADATA *sepadata)
    Definition: sepa_rlt.c:1591
    #define DEFAULT_GOODMAXPARALL
    Definition: sepa_rlt.c:77
    #define SEPA_MAXBOUNDDIST
    Definition: sepa_rlt.c:52
    static void clearVarAdjacency(SCIP *scip, SCIP_HASHMAP *adjvarmap)
    Definition: sepa_rlt.c:335
    #define DEFAULT_DIRCUTOFFDISTWEIGHT
    Definition: sepa_rlt.c:76
    static SCIP_RETCODE freeSepaData(SCIP *scip, SCIP_SEPADATA *sepadata)
    Definition: sepa_rlt.c:365
    #define SEPA_FREQ
    Definition: sepa_rlt.c:51
    static SCIP_RETCODE storeSuitableRows(SCIP *scip, SCIP_SEPA *sepa, SCIP_SEPADATA *sepadata, SCIP_ROW **prob_rows, SCIP_ROW **rows, int *nrows, SCIP_HASHMAP *row_to_pos, SCIP_Bool allowlocal)
    Definition: sepa_rlt.c:446
    static SCIP_RETCODE addProductVars(SCIP *scip, SCIP_SEPADATA *sepadata, SCIP_VAR *x, SCIP_VAR *y, SCIP_HASHMAP *varmap, int nlocks)
    Definition: sepa_rlt.c:545
    #define SEPA_NAME
    Definition: sepa_rlt.c:48
    #define DEFAULT_MAXUNKNOWNTERMS
    Definition: sepa_rlt.c:57
    static SCIP_RETCODE addAdjacentVars(SCIP *scip, SCIP_HASHMAP *adjvarmap, SCIP_VAR **vars)
    Definition: sepa_rlt.c:238
    static SCIP_RETCODE detectProductsUnconditional(SCIP *scip, SCIP_SEPADATA *sepadata, SCIP_ROW **rows, int *row_list, SCIP_HASHTABLE *hashtable, SCIP_Real *coefs1, SCIP_VAR **vars_xwy, SCIP_Real side1, SCIP_SIDETYPE sidetype1, int varpos1, int varpos2, SCIP_HASHMAP *varmap, SCIP_Bool f)
    Definition: sepa_rlt.c:995
    static void addRowMark(int ridx, SCIP_Real a, SCIP_Bool violatedbelow, SCIP_Bool violatedabove, int *row_idcs, unsigned int *row_marks, int *nmarked)
    Definition: sepa_rlt.c:2449
    #define DEFAULT_MAXROUNDS
    Definition: sepa_rlt.c:60
    static void freeProjRows(SCIP *scip, RLT_SIMPLEROW **projrows, int nrows)
    Definition: sepa_rlt.c:2427
    static SCIP_RETCODE ensureVarsSize(SCIP *scip, SCIP_SEPADATA *sepadata, int n)
    Definition: sepa_rlt.c:515
    static SCIP_DECL_SEPAFREE(sepaFreeRlt)
    Definition: sepa_rlt.c:3081
    #define DEFAULT_ONLYEQROWS
    Definition: sepa_rlt.c:62
    static SCIP_DECL_SEPAEXITSOL(sepaExitsolRlt)
    Definition: sepa_rlt.c:3100
    static void getBestEstimators(SCIP *scip, SCIP_SEPADATA *sepadata, SCIP_SOL *sol, int *bestunderestimators, int *bestoverestimators)
    Definition: sepa_rlt.c:1726
    #define DEFAULT_ONLYCONTROWS
    Definition: sepa_rlt.c:63
    reformulation-linearization technique separator
    SCIP_VAR ** adjacentvars
    Definition: sepa_rlt.c:101
    SCIP_VAR ** vars
    int nrows
    Definition: sepa_rlt.c:91
    int firstrow
    Definition: sepa_rlt.c:92
    SCIP_Real rhs
    Definition: sepa_rlt.c:158
    SCIP_VAR ** vars
    Definition: sepa_rlt.c:157
    SCIP_Real cst
    Definition: sepa_rlt.c:160
    const char * name
    Definition: sepa_rlt.c:155
    SCIP_Real * coefs
    Definition: sepa_rlt.c:156
    SCIP_Real lhs
    Definition: sepa_rlt.c:159
    SCIP_CONSNONLINEAR_AUXEXPR ** exprs
    union SCIP_ConsNonlinear_BilinTerm::@6 aux
    SCIP_Real sup
    Definition: intervalarith.h:57
    SCIP_Real inf
    Definition: intervalarith.h:56
    @ SCIP_BOUNDTYPE_UPPER
    Definition: type_lp.h:58
    @ SCIP_BOUNDTYPE_LOWER
    Definition: type_lp.h:57
    enum SCIP_BoundType SCIP_BOUNDTYPE
    Definition: type_lp.h:60
    @ SCIP_SIDETYPE_RIGHT
    Definition: type_lp.h:66
    @ SCIP_SIDETYPE_LEFT
    Definition: type_lp.h:65
    @ SCIP_LPSOLSTAT_OPTIMAL
    Definition: type_lp.h:44
    enum SCIP_SideType SCIP_SIDETYPE
    Definition: type_lp.h:68
    @ SCIP_DIDNOTRUN
    Definition: type_result.h:42
    @ SCIP_CUTOFF
    Definition: type_result.h:48
    @ SCIP_DIDNOTFIND
    Definition: type_result.h:44
    @ SCIP_SEPARATED
    Definition: type_result.h:49
    enum SCIP_Result SCIP_RESULT
    Definition: type_result.h:61
    @ SCIP_OKAY
    Definition: type_retcode.h:42
    @ SCIP_INVALIDCALL
    Definition: type_retcode.h:51
    enum SCIP_Retcode SCIP_RETCODE
    Definition: type_retcode.h:63
    struct SCIP_SepaData SCIP_SEPADATA
    Definition: type_sepa.h:52
    @ SCIP_VARTYPE_BINARY
    Definition: type_var.h:64
    @ SCIP_VARSTATUS_COLUMN
    Definition: type_var.h:53