SCIP

    Solving Constraint Integer Programs

    sepa_subtour.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/**@file sepa_subtour.c
    25 * @brief If there exists a transition forward along the cycle, then the state that the transition originates from can
    26 * be reached only after another ncluster - 1 transitions. Therefore cycles with a number of transitions smaller than
    27 * that can be separated.
    28 * @author Leon Eifler
    29 */
    30
    31/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
    32
    33#include "sepa_subtour.h"
    34
    35#include "probdata_cyc.h"
    36#include "scip/cons_linear.h"
    37#include "scip/pub_misc.h"
    38
    39#define SEPA_NAME "subtour"
    40#define SEPA_DESC "separator that elininates subtours of length smaller than |NCluster|"
    41#define SEPA_PRIORITY 1000
    42#define SEPA_FREQ 5
    43#define SEPA_MAXBOUNDDIST 0.0
    44#define SEPA_USESSUBSCIP FALSE /**< does the separator use a secondary SCIP instance? */
    45#define SEPA_DELAY FALSE /**< should separation method be delayed, if other separators found cuts? */
    46#define MAXCUTS 2000
    47#define MAXROUNDS 15
    48
    49#ifdef SCIP_DEBUG
    50/** Print a cycle to the command line. For debugging purposes */
    51static
    52void printCycle(
    53 SCIP* scip, /**< SCIP data structure */
    54 int* cycle, /**< The cycle to be printed */
    55 int cyclelength, /**< The length of the cycle */
    56 int nstates /**< The number of states */
    57 )
    58{
    59 int i;
    60
    61 SCIPinfoMessage(scip, NULL, "cycle_l%d_c: %d", cyclelength, cycle[0]);
    62 for( i = 0; i < cyclelength; ++i )
    63 {
    64 SCIPinfoMessage(scip, NULL, " -> %d", cycle[i+1]);
    65 }
    67}
    68#endif
    69
    70/** get distance of longest path between two states with exactly n arcs from the matrix */
    71static
    73 SCIP_Real*** adjacencymatrix, /**< the adjacency-matrices of all paths with 1,...,|Clutster| arcs */
    74 int n, /**< length */
    75 int state1, /**< starting state */
    76 int state2 /**< end state */
    77 )
    78{
    79 assert(adjacencymatrix[n] != NULL);
    80 assert(adjacencymatrix[n][state1] != NULL);
    81
    82 return adjacencymatrix[n][state1][state2];
    83}
    84
    85/** After finding a violation, construct and add all violated subtour cuts to scip */
    86static
    88 SCIP* scip, /**< SCIP data structure. */
    89 SCIP_SEPA* sepa, /**< the subtour separator */
    90 SCIP_Real*** adjacencymatrix, /**< the adjacency-matrices of all paths with 1,...,|Clutster| arcs */
    91 SCIP_DIGRAPH* adjacencygraph, /**< the directed edge-graph */
    92 int** iscontracted, /**< information of intermediate contraction-nodes for contracted arcs */
    93 int cyclelength, /**< the length of the subtours to add */
    94 SCIP_RESULT* result, /**< pointer to store the result of separation */
    95 int* ncuts /**< pointer to store number of cuts */
    96 )
    97{
    98 SCIP_VAR**** edgevars;
    99 char cutname[SCIP_MAXSTRLEN];
    100 SCIP_ROW* cut;
    101 int** subtours;
    102 int* insubtour;
    103 int* successors;
    104 int nsuccessors;
    105 int nstates;
    106 int currentnode;
    107 int successor;
    108 int intermediate;
    109 int anchor;
    110 int ncontractions;
    111 int liftabley;
    112 int liftablez;
    113 int greater;
    114 int smaller;
    115 int c;
    116 int k;
    117 int l;
    118 SCIP_Bool isduplicate;
    119
    120 edgevars = SCIPcycGetEdgevars(scip);
    121 nstates = SCIPdigraphGetNNodes(adjacencygraph);
    122
    123 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &insubtour, nstates) );
    124 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &subtours, nstates) );
    125
    126 for( k = 0; k < nstates; ++k )
    127 {
    128 SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &subtours[k], cyclelength + 1) ); /*lint !e866, !e776*/
    129 insubtour[k] = -1;
    130 }
    131
    132 /* for each state, check if a subtour inequality is violated */
    133 for( anchor = 0; anchor < nstates; ++anchor )
    134 {
    135 /* while reconstructing the subtour, count the number of contractions */
    136 ncontractions = 0;
    137
    138 /* a cycle inequality is violated if the following is true */
    139 if( SCIPisGT(scip, getDist(adjacencymatrix, cyclelength - 1, anchor, anchor), cyclelength - 1.0) )
    140 {
    141 subtours[anchor][0] = anchor;
    142 if( insubtour[anchor] == -1 )
    143 insubtour[anchor] = anchor;
    144
    145 /* traverse the cycle */
    146 for( k = 0; k < cyclelength -1; ++k )
    147 {
    148 currentnode = subtours[anchor][k];
    149
    150 assert(0 <= currentnode && currentnode < nstates);
    151
    152 successors = SCIPdigraphGetSuccessors(adjacencygraph, currentnode);
    153 nsuccessors = SCIPdigraphGetNSuccessors(adjacencygraph, currentnode);
    154
    155 /* find the next state along the subtour */
    156 for( l = 0; l < nsuccessors; l++ )
    157 {
    158 successor = successors[l];
    159
    160 assert(0 <= successor && successor < nstates);
    161
    162 /* check if this successor of the current node is the one in the cycle. If so add it. */
    163 if( SCIPisEQ(scip, getDist(adjacencymatrix, 0, currentnode, successor)
    164 + getDist(adjacencymatrix, cyclelength - (k + 2), successor, anchor),
    165 getDist(adjacencymatrix, cyclelength - (k + 1), currentnode, anchor)) )
    166 {
    167 subtours[anchor][k + 1] = successor;
    168 insubtour[successor] = anchor;
    169
    170 if( iscontracted[currentnode][successor] != -1 )
    171 ncontractions++;
    172
    173 break;
    174 }
    175 }
    176 }
    177
    178 /* start and endnode are always the same in a cycle */
    179 subtours[anchor][cyclelength] = anchor;
    180
    181 /* check last arc for a contraction */
    182 if( iscontracted[subtours[anchor][cyclelength - 1]][anchor] != -1 )
    183 ncontractions++;
    184
    185 isduplicate = FALSE;
    186
    187 /* if this anchor is already in another subtour, we check if the subtour is the same, since we don't want to
    188 * add duplicates
    189 */
    190 if( insubtour[anchor] != anchor )
    191 {
    192 c = 0;
    193 isduplicate = TRUE;
    194
    195 while( subtours[insubtour[anchor]][c] != anchor )
    196 c++;
    197
    198 for( k = 0; k < cyclelength && isduplicate; ++k )
    199 {
    200 if( subtours[insubtour[anchor]][(k + c) % cyclelength] != subtours[anchor][k] )
    201 isduplicate = FALSE;
    202 }
    203 }
    204
    205 if( isduplicate )
    206 continue;
    207
    208 /* set the amount of y and z variables that we can still lift into the inequality */
    209 liftabley = cyclelength - 1;
    210 liftablez = SCIPcycGetNCluster(scip) - cyclelength - 1;
    211
    212 /* Now build the cut and add the subtour inequality */
    213 (void)SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "subtour_%d_length_%d_contracted_%d", anchor,
    214 cyclelength, ncontractions );
    215 SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut,sepa, cutname, -SCIPinfinity(scip),
    216 cyclelength + ncontractions - 1.0, FALSE, FALSE, TRUE) );
    217
    219
    220 for( k = 0; k < cyclelength; ++k )
    221 {
    222 currentnode = subtours[anchor][k];
    223 successor = subtours[anchor][k+1];
    224 intermediate = iscontracted[currentnode][successor];
    225
    226 if( intermediate != -1 )
    227 {
    228 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, currentnode, intermediate, CONSECUTIVE_CLUSTER), 1.0) );
    230 getEdgevar(edgevars, MAX(intermediate, successor), MIN(intermediate, successor), INCLUSTER), 1.0) );
    231
    232 greater = intermediate > currentnode ? intermediate : currentnode;
    233 smaller = intermediate < currentnode ? intermediate : currentnode;
    234
    235 if( liftabley > 0 && SCIPvarGetLPSol(getEdgevar(edgevars, greater, smaller, INCLUSTER)) > 0 )
    236 {
    237 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, greater, smaller, INCLUSTER), 1.0) );
    238 liftabley--;
    239 }
    240 if( liftablez > 0 && SCIPvarGetLPSol(getEdgevar(edgevars, intermediate, successor, CONSECUTIVE_CLUSTER)) > 0 )
    241 {
    242 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, intermediate, successor, CONSECUTIVE_CLUSTER), 1.0) );
    243 liftablez--;
    244 }
    245 }
    246 else
    247 {
    248 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, currentnode, successor, CONSECUTIVE_CLUSTER), 1.0) );
    249 if( SCIPvarGetLPSol(getEdgevar(edgevars, MAX(currentnode, successor), MIN(currentnode, successor), INCLUSTER))
    250 > 0 && liftabley > 0 )
    251 {
    253 getEdgevar(edgevars, MAX(currentnode, successor), MIN(currentnode, successor), INCLUSTER), 1.0) );
    254 liftabley--;
    255 }
    256 }
    257 }
    258
    261
    262 /* print for debugging purposes */
    264
    265 /* release data and increment cut counter */
    266 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
    267
    268 *result = SCIP_SEPARATED;
    269 (*ncuts)++;
    270 }
    271 }
    272
    273 for( k = 0; k < nstates; ++k )
    274 {
    275 SCIPfreeBlockMemoryArray(scip, &(subtours[k]), cyclelength + 1);
    276 }
    277 SCIPfreeBlockMemoryArray(scip, &subtours, nstates);
    278 SCIPfreeBlockMemoryArray(scip, &insubtour, nstates);
    279
    280 return SCIP_OKAY;
    281}
    282
    283/** Detect if path inequalities are violated and if so, add them to scip */
    284static
    286 SCIP* scip, /**< SCIP data structure. */
    287 SCIP_SEPA* sepa, /**< the subtour separator */
    288 SCIP_Real*** adjacencymatrix, /**< the adjacency-matrix of all paths with 1,...,|Clutster| arcs */
    289 SCIP_DIGRAPH* adjacencygraph, /**< the directed edge-graph */
    290 int** iscontracted, /**< information of intermediate contraction-nodes for contracted arcs */
    291 int pathlength, /**< the length of the subtours to add */
    292 SCIP_RESULT* result, /**< pointer to store the result of separation */
    293 int* ncuts /**< pointer to store number of cuts */
    294 )
    295{
    296 SCIP_VAR**** edgevars;
    297 char cutname[SCIP_MAXSTRLEN];
    298 SCIP_ROW* cut;
    299 int* path;
    300 int nstates;
    301 int currentnode;
    302 int successor;
    303 int* successors;
    304 int nsuccessors;
    305 int intermediate;
    306 int start;
    307 int end;
    308 int ncontractions;
    309 int k;
    310 int i;
    311 int j;
    312 int nz;
    313 int ny;
    314
    315 edgevars = SCIPcycGetEdgevars(scip);
    316 nstates = SCIPdigraphGetNNodes(adjacencygraph);
    317
    318 SCIP_CALL( SCIPallocMemoryArray(scip, &path, pathlength + 1) );
    319
    320 for( start = 0; start < nstates; ++start )
    321 {
    322 path[0] = start;
    323
    324 for( j = 0; j < SCIPdigraphGetNSuccessors(adjacencygraph, start); ++j )
    325 {
    326 ncontractions = 0;
    327
    328 end = SCIPdigraphGetSuccessors(adjacencygraph, start)[j];
    329 path[pathlength] = end;
    330
    331 /* check if path-inequality is violated */
    332 if( SCIPisGT(scip, getDist(adjacencymatrix, pathlength - 1, start, end)
    333 + getDist(adjacencymatrix, 0, start, end), (SCIP_Real) pathlength) )
    334 {
    335 /*reconstruct the path */
    336 for( k = 0; k < pathlength - 1; ++k )
    337 {
    338 currentnode = path[k];
    339
    340 assert(0 <= currentnode && currentnode < nstates);
    341
    342 successors = SCIPdigraphGetSuccessors(adjacencygraph, currentnode);
    343 nsuccessors = SCIPdigraphGetNSuccessors(adjacencygraph, currentnode);
    344
    345 for( i = 0; i < nsuccessors; ++i )
    346 {
    347 successor = successors[i];
    348
    349 assert(0 <= successor && successor < nstates);
    350
    351 if( SCIPisEQ(scip, getDist(adjacencymatrix, 0, currentnode, successor)
    352 + getDist(adjacencymatrix, pathlength - (k + 2), successor, end),
    353 getDist(adjacencymatrix, pathlength - (k + 1), currentnode, end)) )
    354 {
    355 path[k + 1] = successor;
    356
    357 if( iscontracted[currentnode][successor] != -1 )
    358 ncontractions++;
    359
    360 break;
    361 }
    362 }
    363 }
    364
    365 /* check the last arc along the path and the direct arc from start to end for contractions */
    366 if( iscontracted[path[pathlength - 1]][end] != -1 )
    367 ncontractions++;
    368
    369 if( iscontracted[start][end] != -1 )
    370 ncontractions++;
    371
    372 nz = pathlength;
    373 ny = 0;
    374
    375 /* construct the corresponding inequality and add it to scip */
    376 (void)SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "path_%d_%d_length_%d_contracted_%d",
    377 start, end, pathlength, ncontractions );
    378 SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut,sepa, cutname, -SCIPinfinity(scip),
    379 (SCIP_Real) pathlength + ncontractions, FALSE, FALSE, TRUE) );
    380
    382
    383 for( k = 0; k < pathlength; ++k )
    384 {
    385 currentnode = path[k];
    386 successor = path[k+1];
    387 intermediate = iscontracted[currentnode][successor];
    388
    389 if( intermediate != -1 )
    390 {
    391 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, currentnode, intermediate, CONSECUTIVE_CLUSTER), 1.0) );
    393 getEdgevar(edgevars, MAX(intermediate, successor), MIN(intermediate, successor), INCLUSTER), 1.0) );
    394
    395 if( nz < SCIPcycGetNCluster(scip)
    396 && SCIPisPositive(scip, SCIPvarGetLPSol(getEdgevar(edgevars, intermediate, successor, CONSECUTIVE_CLUSTER))) )
    397 {
    398 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, intermediate, successor, CONSECUTIVE_CLUSTER), 1.0) );
    399 nz++;
    400 }
    401
    402 if( ny < pathlength - 2 && SCIPisPositive(scip, SCIPvarGetLPSol(
    403 getEdgevar(edgevars, MAX(currentnode, intermediate), MIN(currentnode, intermediate), INCLUSTER))) )
    404 {
    406 getEdgevar(edgevars, MAX(currentnode, intermediate), MIN(currentnode, intermediate), INCLUSTER), 1.0) );
    407 ny++;
    408 }
    409 }
    410 else
    411 {
    412 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, currentnode, successor, CONSECUTIVE_CLUSTER), 1.0) );
    413
    414 if( ny < pathlength - 2 && SCIPisPositive(scip, SCIPvarGetLPSol(
    415 getEdgevar(edgevars, MAX(currentnode, successor), MIN(currentnode, successor), INCLUSTER))) )
    416 {
    418 getEdgevar(edgevars, MAX(currentnode, successor), MIN(currentnode, successor), INCLUSTER), 1.0) );
    419 ny++;
    420 }
    421 }
    422 }
    423
    424 /* add the direct arc from start to end */
    425 intermediate = iscontracted[start][end];
    426
    427 if( iscontracted[start][end] != -1 )
    428 {
    429 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, start, intermediate, CONSECUTIVE_CLUSTER), 1.0) );
    431 MAX(intermediate, end), MIN(intermediate, end), INCLUSTER), 1.0) );
    432 }
    433 else
    434 {
    435 assert( NULL != getEdgevar(edgevars, start, end, CONSECUTIVE_CLUSTER));
    436
    437 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, start, end, CONSECUTIVE_CLUSTER), 1.0) );
    438 }
    439
    441
    442 /* print row if in debug mode */
    444
    445 /* if an arc appears twice then the path inequality should not be used */
    446 if( SCIPisEQ(scip, SCIPgetRowMaxCoef(scip, cut), 1.0) )
    447 {
    449 *result = SCIP_SEPARATED;
    450 (*ncuts)++;
    451 }
    452
    453 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
    454 }
    455 }
    456 }
    457
    459
    460 return SCIP_OKAY;
    461}
    462
    463/** detect if path inequalities are violated and if so, add them to scip */
    464static
    466 SCIP* scip, /**< SCIP data structure. */
    467 SCIP_SEPA* sepa, /**< the subtour separator */
    468 SCIP_Real*** adjacencymatrix, /**< the adjacency-matrix of all paths with 1,...,|Clutster| arcs */
    469 SCIP_DIGRAPH* adjacencygraph, /**< the directed edge-graph */
    470 int** iscontracted, /**< information of intermediate contraction-nodes for contracted arcs */
    471 int tourlength, /**< the length of the subtours to add */
    472 SCIP_RESULT* result, /**< pointer to store the result of separation */
    473 int* ncuts /**< pointer to store number of cuts */
    474 )
    475{
    476 SCIP_VAR**** edgevars;
    477 char cutname[SCIP_MAXSTRLEN];
    478 SCIP_ROW* cut;
    479 int* tour;
    480 int* successors;
    481 int* succerssorsstart;
    482 int nsuccessorsstart;
    483 int nsuccessors;
    484 int nstates;
    485 int currentnode;
    486 int successor;
    487 int intermediate;
    488 int start;
    489 int end;
    490 int ncontractions;
    491 int k;
    492 int i;
    493 int j;
    494
    495 edgevars = SCIPcycGetEdgevars(scip);
    496 nstates = SCIPdigraphGetNNodes(adjacencygraph);
    497
    498 SCIP_CALL( SCIPallocMemoryArray(scip, &tour, tourlength + 1) );
    499
    500 for( start = 0; start < nstates; ++start )
    501 {
    502 tour[0] = start;
    503 succerssorsstart = SCIPdigraphGetSuccessors(adjacencygraph, start);
    504 nsuccessorsstart = SCIPdigraphGetNSuccessors(adjacencygraph, start);
    505
    506 for( j = 0; j < nsuccessorsstart; ++j )
    507 {
    508 ncontractions = 0;
    509
    510 end = succerssorsstart[j];
    511 tour[tourlength] = end;
    512
    513 /* check if tour-inequality is violated */
    514 if( SCIPisGT(scip, getDist(adjacencymatrix, tourlength - 1, start, end)
    515 - getDist(adjacencymatrix, 0, end, start), (SCIP_Real) tourlength - 1) )
    516 {
    517 /*reconstruct the tour */
    518 for( k = 0; k < tourlength - 1; ++k )
    519 {
    520 currentnode = tour[k];
    521 successors = SCIPdigraphGetSuccessors(adjacencygraph, currentnode);
    522 nsuccessors = SCIPdigraphGetNSuccessors(adjacencygraph, currentnode);
    523
    524 for( i = 0; i < nsuccessors; ++i )
    525 {
    526 successor = successors[i];
    527
    528 if( SCIPisEQ(scip, getDist(adjacencymatrix, 0, currentnode, successor)
    529 + getDist(adjacencymatrix, tourlength - (k + 2), successor, end)
    530 , getDist(adjacencymatrix, tourlength - (k + 1), currentnode, end)) )
    531 {
    532 tour[k + 1] = successor;
    533
    534 if( iscontracted[currentnode][successor] != -1 )
    535 ncontractions++;
    536 break;
    537 }
    538 }
    539 }
    540
    541 /* check the last arc along the tour and the direct arc from start to end for contractions */
    542 if( iscontracted[tour[tourlength - 1]][end] != -1 )
    543 ncontractions++;
    544 if( iscontracted[end][start] != -1 )
    545 ncontractions++;
    546
    547 /* construct the corresponding inequality and add it to scip */
    548 (void)SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "tour_%d_%d_length_%d_contracted_%d",
    549 start, end, tourlength, ncontractions );
    550 SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut,sepa, cutname, -SCIPinfinity(scip),
    551 (SCIP_Real) tourlength + ncontractions - 1, FALSE, FALSE, TRUE) );
    552
    554
    555 for( k = 0; k < tourlength; ++k )
    556 {
    557 currentnode = tour[k];
    558 successor = tour[k+1];
    559 intermediate = iscontracted[currentnode][successor];
    560
    561 if( intermediate != -1 )
    562 {
    563 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, currentnode, intermediate, CONSECUTIVE_CLUSTER), 1.0) );
    565 getEdgevar(edgevars, MAX(intermediate, successor), MIN(intermediate, successor), INCLUSTER), 1.0) );
    566 }
    567 else
    568 {
    569 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, currentnode, successor, CONSECUTIVE_CLUSTER), 1.0) );
    570 }
    571 }
    572
    573 /* add the direct arc from start to end */
    574 intermediate = iscontracted[end][start];
    575 if( iscontracted[end][start] != -1 )
    576 {
    577 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, end, intermediate, CONSECUTIVE_CLUSTER), -1.0) );
    579 getEdgevar(edgevars, MAX(intermediate, start), MIN(intermediate, start), INCLUSTER), 1.0) );
    580 }
    581 else
    582 {
    583 SCIP_CALL( SCIPaddVarToRow(scip, cut, getEdgevar(edgevars, end, start, CONSECUTIVE_CLUSTER), -1.0) );
    584 }
    585
    587
    588 /* print row if in debug mode */
    590
    591 /* if an arc appears twice then the tour inequality should not be used */
    592 if( SCIPisEQ(scip, SCIPgetRowMaxCoef(scip, cut), 1.0) )
    593 {
    595 *result = SCIP_SEPARATED;
    596 (*ncuts)++;
    597 }
    598
    599 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
    600 }
    601 }
    602 }
    603
    605
    606 return SCIP_OKAY;
    607}
    608
    609/** compute the next matrix with the weight off all the longest paths with exactly narcs and store it in
    610 * adjacencymatrix[narcs - 1]. For this, simply compute
    611 * \f{align*}{ d^{k}(currentnode,successor) = max_{l=1,\ldots,n} \{d^{k-1}(currentnode,l) + d^1(l,successor) \} \f}.
    612 */
    613static
    615(
    616 SCIP* scip, /**< SCIP data structure */
    617 SCIP_Real*** adjacencymatrix, /**< the max-distance matrices for all number of arcs less than narcs. */
    618 SCIP_DIGRAPH* adjacencygraph, /**< the directed edge-graph */
    619 int narcs /**< the current number of arcs in the paths */
    620)
    621{
    622 int* intermediates;
    623 int nintermediates;
    624 int currentnode;
    625 int intermediate;
    626 int successor;
    627 int l;
    628 int nnodes;
    629 SCIP_Bool foundviolation;
    630
    631 foundviolation = FALSE;
    632 nnodes = SCIPdigraphGetNNodes(adjacencygraph);
    633
    634 for( currentnode = 0; currentnode < nnodes; ++currentnode )
    635 {
    636 intermediates = SCIPdigraphGetSuccessors(adjacencygraph, currentnode);
    637 nintermediates = SCIPdigraphGetNSuccessors(adjacencygraph, currentnode);
    638
    639 for( l = 0; l < nintermediates; ++l )
    640 {
    641 intermediate = intermediates[l];
    642
    643 assert(0 <= intermediate && intermediate < nnodes);
    644
    645 for( successor = 0; successor < nnodes; ++successor )
    646 {
    647 if( SCIPisPositive(scip, getDist(adjacencymatrix, 0, currentnode, intermediate))
    648 && SCIPisPositive(scip, getDist(adjacencymatrix, narcs - 2, intermediate, successor)) )
    649 {
    650 if( SCIPisGT(scip, getDist(adjacencymatrix, 0, currentnode, intermediate)
    651 + getDist(adjacencymatrix, narcs - 2, intermediate, successor),
    652 getDist(adjacencymatrix, narcs - 1, currentnode, successor)) )
    653 {
    654 adjacencymatrix[narcs - 1][currentnode][successor] = getDist(adjacencymatrix, 0, currentnode, intermediate)
    655 + getDist(adjacencymatrix, narcs - 2, intermediate, successor);
    656 }
    657 }
    658 }
    659 }
    660 }
    661
    662 /* check if we have found a violated subtour constraint */
    663 for( currentnode = 0; currentnode < nnodes; ++currentnode )
    664 {
    665 if( SCIPisGT(scip, getDist(adjacencymatrix, narcs - 1, currentnode, currentnode), narcs - 1.0) )
    666 foundviolation = TRUE;
    667 }
    668 return foundviolation;
    669}
    670
    671/** copy method for separator plugins (called when SCIP copies plugins) */
    672static
    673SCIP_DECL_SEPACOPY(sepaCopySubtour)
    674{ /*lint --e{715}*/
    675 assert(scip != NULL);
    676 assert(sepa != NULL);
    677
    679
    680 /* call inclusion method of constraint handler */
    682
    683 return SCIP_OKAY;
    684}
    685
    686/** LP solution separation method of separator */
    687static
    688SCIP_DECL_SEPAEXECLP(sepaExeclpSubtour)
    689{ /*lint --e{715}*/
    690 SCIP_VAR**** edgevars;
    691 SCIP_Real*** adjacencymatrix;
    692 SCIP_DIGRAPH* adjacencygraph;
    693 SCIP_DIGRAPH* edgegraph;
    694 int** iscontracted;
    695 SCIP_Bool violation;
    696 int* successors1;
    697 int* successors2;
    698 int nsuccessors1;
    699 int nsuccessors2;
    700 int ncuts;
    701 int nstates;
    702 int ncluster;
    703 int cyclelength;
    704 int rounds;
    705 int i;
    706 int j;
    707 int k;
    708 int state1;
    709 int state2;
    710 int state3;
    711
    712 /* get problem information */
    713 rounds = SCIPsepaGetNCallsAtNode(sepa);
    714 ncluster = SCIPcycGetNCluster(scip);
    715 edgevars = SCIPcycGetEdgevars(scip);
    716 nstates = SCIPcycGetNBins(scip);
    717 edgegraph = SCIPcycGetEdgeGraph(scip);
    718 ncuts = 0;
    719
    720 if( rounds >= MAXROUNDS )
    721 {
    722 *result = SCIP_DIDNOTRUN;
    723 return SCIP_OKAY;
    724 }
    725
    726 assert(nstates > 0);
    727 assert(ncluster > 0 && ncluster < nstates);
    728 assert(NULL != edgevars);
    729 assert(NULL != edgegraph);
    730
    731 /* allocate memory */
    732 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &adjacencymatrix, ncluster) );
    733
    734 for( k = 0; k < ncluster; ++k )
    735 {
    736 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &adjacencymatrix[k], nstates) ); /*lint !e866*/
    737
    738 for( j = 0; j < nstates; ++j )
    739 {
    740 SCIP_CALL( SCIPallocClearBlockMemoryArray(scip, &adjacencymatrix[k][j], nstates) ); /*lint !e866*/
    741 }
    742 }
    743
    744 /* create Digraph from the current LP-Solution */
    745 SCIP_CALL( SCIPcreateDigraph(scip, &adjacencygraph, nstates) );
    746 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &iscontracted, nstates) );
    747
    748
    749 /* get the values of the lp-solution */
    750 for( i = 0; i < nstates; ++i )
    751 {
    752 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &iscontracted[i], nstates) );
    753
    754 for( j = 0; j < nstates; ++j )
    755 {
    756 iscontracted[i][j] = -1;
    757
    758 if( edgevars[i] != NULL && edgevars[i][j] != NULL && getEdgevar(edgevars, i, j, CONSECUTIVE_CLUSTER) != NULL )
    759 adjacencymatrix[0][i][j] = SCIPvarGetLPSol(getEdgevar(edgevars, i, j, CONSECUTIVE_CLUSTER));
    760 }
    761 }
    762
    763 /* contract the adjacency matrix if it is better to take z_{ij} + y_{jk} rather than z_{ik} directly,
    764 * this stores j at position (i,k)
    765 */
    766 for( i = 0; i < nstates; ++i )
    767 {
    768 state1 = i;
    769
    770 assert( edgevars[state1] != NULL);
    771
    772 successors1 = SCIPdigraphGetSuccessors(edgegraph, state1);
    773 nsuccessors1 = SCIPdigraphGetNSuccessors(edgegraph, state1);
    774
    775 for( j = 0; j < nsuccessors1; ++j )
    776 {
    777 state2 = successors1[j];
    778
    779 assert( edgevars[state2] != NULL);
    780
    781 successors2 = SCIPdigraphGetSuccessors(edgegraph, state2);
    782 nsuccessors2 = SCIPdigraphGetNSuccessors(edgegraph, state2);
    783
    784 for( k = 0 ; k < nsuccessors2; ++k )
    785 {
    786 state3 = successors2[k];
    787
    788 if( edgevars[state1][state2] == NULL || edgevars[state2][state3] == NULL || edgevars[state1][state3] == NULL )
    789 continue;
    790
    791 if( SCIPisLT( scip, getDist(adjacencymatrix, 0, state1, state3),
    792 SCIPvarGetLPSol(getEdgevar(edgevars, state1, state2, CONSECUTIVE_CLUSTER))
    793 + SCIPvarGetLPSol(getEdgevar(edgevars, MAX(state2, state3), MIN(state2, state3), INCLUSTER)) - 1) )
    794 {
    795 adjacencymatrix[0][state1][state3] = SCIPvarGetLPSol(getEdgevar(edgevars, state1, state2, CONSECUTIVE_CLUSTER))
    796 + SCIPvarGetLPSol(getEdgevar(edgevars, MAX(state2, state3), MIN(state2, state3), INCLUSTER)) - 1;
    797
    798 iscontracted[state1][state3] = state2;
    799 }
    800 }
    801 }
    802 }
    803
    804 /* save the contracted matrix as a digraph to be able to reuse it quicker */
    805 for( i = 0; i < nstates; ++i )
    806 {
    807 for( j = 0; j < nstates; ++j )
    808 {
    809 if( !SCIPisZero(scip, getDist(adjacencymatrix, 0, i, j)) )
    810 {
    811 SCIP_CALL( SCIPdigraphAddArc(adjacencygraph, i , j, NULL) );
    812 }
    813 }
    814 }
    815
    816 /* a cyclelength of one does not make sense as there are no loops */
    817 cyclelength = 2;
    818 *result = SCIP_DIDNOTFIND;
    819
    820 /* Iterate until we have found a sufficient number of cuts or until we have checked all possible violations */
    821 while( cyclelength < ncluster )
    822 {
    823 /* Compute the next adjacency matrix */
    824 violation = computeNextAdjacency(scip, adjacencymatrix, adjacencygraph, cyclelength);
    825
    826 /* if we found a violation separate it */
    827 if( violation )
    828 {
    829 SCIP_CALL( addSubtourCuts(scip, sepa, adjacencymatrix, adjacencygraph, iscontracted, cyclelength,
    830 result, &ncuts) );
    831 }
    832
    833 /* check if any path-inequalities are violated and sepatare them */
    834 SCIP_CALL( addPathCuts(scip, sepa, adjacencymatrix, adjacencygraph, iscontracted, cyclelength, result, &ncuts) );
    835
    836 if( cyclelength == ncluster - 1 )
    837 {
    838 SCIP_CALL( addTourCuts(scip, sepa, adjacencymatrix, adjacencygraph, iscontracted, cyclelength,
    839 result, &ncuts) );
    840 }
    841
    842 /* stop if we added maximal number of cuts */
    843 if( ncuts >= MAXCUTS )
    844 break;
    845
    846 cyclelength++;
    847 }
    848
    849 SCIPdigraphFreeComponents(adjacencygraph);
    850 SCIPdigraphFree(&adjacencygraph);
    851
    852 /* free allocated memory */
    853 for( i = 0; i < nstates; ++i )
    854 {
    855 SCIPfreeBlockMemoryArray(scip, &iscontracted[i], nstates);
    856 }
    857 SCIPfreeBlockMemoryArray(scip, &iscontracted, nstates);
    858
    859 for( i = 0; i < ncluster; ++i )
    860 {
    861 for( j = 0; j < nstates; ++j )
    862 {
    863 SCIPfreeBlockMemoryArray(scip, &adjacencymatrix[i][j], nstates);
    864 }
    865 SCIPfreeBlockMemoryArray(scip, &adjacencymatrix[i], nstates);
    866 }
    867 SCIPfreeBlockMemoryArray(scip, &adjacencymatrix, ncluster);
    868
    869 return SCIP_OKAY;
    870}
    871
    872
    873/** creates the Subtour separator and includes it in SCIP */
    875 SCIP* scip /**< SCIP data structure */
    876)
    877{
    878 SCIP_SEPA* sepa;
    879
    880 /* include separator */
    881
    884 sepaExeclpSubtour, NULL,
    885 NULL) );
    886
    887 assert(sepa != NULL);
    888
    889 /* set non fundamental callbacks via setter functions */
    890 SCIP_CALL( SCIPsetSepaCopy(scip, sepa, sepaCopySubtour) );
    891
    892
    893 return SCIP_OKAY;
    894}
    Constraint handler for linear constraints in their most general form, .
    #define NULL
    Definition: def.h:257
    #define SCIP_MAXSTRLEN
    Definition: def.h:278
    #define SCIP_Bool
    Definition: def.h:100
    #define MIN(x, y)
    Definition: def.h:233
    #define SCIP_STRINGEQ(name, reference, retcode)
    Definition: def.h:454
    #define SCIP_Real
    Definition: def.h:165
    #define TRUE
    Definition: def.h:102
    #define FALSE
    Definition: def.h:103
    #define MAX(x, y)
    Definition: def.h:229
    #define SCIP_CALL(x)
    Definition: def.h:364
    #define nnodes
    Definition: gastrans.c:74
    #define narcs
    Definition: gastrans.c:77
    void SCIPdigraphFreeComponents(SCIP_DIGRAPH *digraph)
    Definition: misc.c:8594
    int SCIPdigraphGetNSuccessors(SCIP_DIGRAPH *digraph, int node)
    Definition: misc.c:7881
    int SCIPdigraphGetNNodes(SCIP_DIGRAPH *digraph)
    Definition: misc.c:7823
    SCIP_RETCODE SCIPdigraphAddArc(SCIP_DIGRAPH *digraph, int startnode, int endnode, void *data)
    Definition: misc.c:7739
    void SCIPdigraphFree(SCIP_DIGRAPH **digraph)
    Definition: misc.c:7645
    int * SCIPdigraphGetSuccessors(SCIP_DIGRAPH *digraph, int node)
    Definition: misc.c:7896
    SCIP_RETCODE SCIPcreateDigraph(SCIP *scip, SCIP_DIGRAPH **digraph, int nnodes)
    void SCIPinfoMessage(SCIP *scip, FILE *file, const char *formatstr,...)
    Definition: scip_message.c:208
    SCIP_RETCODE SCIPaddPoolCut(SCIP *scip, SCIP_ROW *row)
    Definition: scip_cut.c:336
    #define SCIPfreeBlockMemoryArray(scip, ptr, num)
    Definition: scip_mem.h:110
    #define SCIPallocMemoryArray(scip, ptr, num)
    Definition: scip_mem.h:64
    #define SCIPallocClearBlockMemoryArray(scip, ptr, num)
    Definition: scip_mem.h:97
    #define SCIPfreeMemoryArray(scip, ptr)
    Definition: scip_mem.h:80
    #define SCIPallocBlockMemoryArray(scip, ptr, num)
    Definition: scip_mem.h:93
    SCIP_Real SCIPgetRowMaxCoef(SCIP *scip, SCIP_ROW *row)
    Definition: scip_lp.c:1886
    SCIP_RETCODE SCIPcacheRowExtensions(SCIP *scip, SCIP_ROW *row)
    Definition: scip_lp.c:1581
    SCIP_RETCODE SCIPflushRowExtensions(SCIP *scip, SCIP_ROW *row)
    Definition: scip_lp.c:1604
    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
    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
    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 SCIPsetSepaCopy(SCIP *scip, SCIP_SEPA *sepa, SCIP_DECL_SEPACOPY((*sepacopy)))
    Definition: scip_sepa.c:157
    SCIP_Real SCIPinfinity(SCIP *scip)
    SCIP_Bool SCIPisPositive(SCIP *scip, SCIP_Real val)
    SCIP_Bool SCIPisGT(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 SCIPisLT(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
    SCIP_Real SCIPvarGetLPSol(SCIP_VAR *var)
    Definition: var.c:24696
    int SCIPsnprintf(char *t, int len, const char *s,...)
    Definition: misc.c:10827
    SCIP_VAR **** SCIPcycGetEdgevars(SCIP *scip)
    int SCIPcycGetNBins(SCIP *scip)
    SCIP_VAR * getEdgevar(SCIP_VAR ****edgevars, int state1, int state2, EDGETYPE edgetype)
    int SCIPcycGetNCluster(SCIP *scip)
    SCIP_DIGRAPH * SCIPcycGetEdgeGraph(SCIP *scip)
    problem data for cycle clustering problem
    @ CONSECUTIVE_CLUSTER
    Definition: probdata_cyc.h:51
    @ INCLUSTER
    Definition: probdata_cyc.h:50
    #define SCIPdebug(x)
    Definition: pub_message.h:93
    public data structures and miscellaneous methods
    #define SEPA_PRIORITY
    Definition: sepa_subtour.c:41
    static SCIP_RETCODE addSubtourCuts(SCIP *scip, SCIP_SEPA *sepa, SCIP_Real ***adjacencymatrix, SCIP_DIGRAPH *adjacencygraph, int **iscontracted, int cyclelength, SCIP_RESULT *result, int *ncuts)
    Definition: sepa_subtour.c:87
    #define SEPA_DELAY
    Definition: sepa_subtour.c:45
    static SCIP_RETCODE addPathCuts(SCIP *scip, SCIP_SEPA *sepa, SCIP_Real ***adjacencymatrix, SCIP_DIGRAPH *adjacencygraph, int **iscontracted, int pathlength, SCIP_RESULT *result, int *ncuts)
    Definition: sepa_subtour.c:285
    #define SEPA_DESC
    Definition: sepa_subtour.c:40
    static SCIP_DECL_SEPACOPY(sepaCopySubtour)
    Definition: sepa_subtour.c:673
    #define SEPA_USESSUBSCIP
    Definition: sepa_subtour.c:44
    static SCIP_DECL_SEPAEXECLP(sepaExeclpSubtour)
    Definition: sepa_subtour.c:688
    #define MAXROUNDS
    Definition: sepa_subtour.c:47
    #define MAXCUTS
    Definition: sepa_subtour.c:46
    #define SEPA_MAXBOUNDDIST
    Definition: sepa_subtour.c:43
    #define SEPA_FREQ
    Definition: sepa_subtour.c:42
    #define SEPA_NAME
    Definition: sepa_subtour.c:39
    static SCIP_Real getDist(SCIP_Real ***adjacencymatrix, int n, int state1, int state2)
    Definition: sepa_subtour.c:72
    static SCIP_RETCODE addTourCuts(SCIP *scip, SCIP_SEPA *sepa, SCIP_Real ***adjacencymatrix, SCIP_DIGRAPH *adjacencygraph, int **iscontracted, int tourlength, SCIP_RESULT *result, int *ncuts)
    Definition: sepa_subtour.c:465
    static SCIP_Bool computeNextAdjacency(SCIP *scip, SCIP_Real ***adjacencymatrix, SCIP_DIGRAPH *adjacencygraph, int narcs)
    Definition: sepa_subtour.c:615
    SCIP_RETCODE SCIPincludeSepaSubtour(SCIP *scip)
    Definition: sepa_subtour.c:874
    Separate Subtours-Elimination inequalities in Cycle-Clustering Applications.
    @ SCIP_DIDNOTRUN
    Definition: type_result.h:42
    @ 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