Scippy

    SCIP

    Solving Constraint Integer Programs

    sepa_mcf.c
    Go to the documentation of this file.
    1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    2/* */
    3/* This file is part of the program and library */
    4/* SCIP --- Solving Constraint Integer Programs */
    5/* */
    6/* Copyright (c) 2002-2025 Zuse Institute Berlin (ZIB) */
    7/* */
    8/* Licensed under the Apache License, Version 2.0 (the "License"); */
    9/* you may not use this file except in compliance with the License. */
    10/* You may obtain a copy of the License at */
    11/* */
    12/* http://www.apache.org/licenses/LICENSE-2.0 */
    13/* */
    14/* Unless required by applicable law or agreed to in writing, software */
    15/* distributed under the License is distributed on an "AS IS" BASIS, */
    16/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
    17/* See the License for the specific language governing permissions and */
    18/* limitations under the License. */
    19/* */
    20/* You should have received a copy of the Apache-2.0 license */
    21/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
    22/* */
    23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    24
    25/**@file sepa_mcf.c
    26 * @ingroup DEFPLUGINS_SEPA
    27 * @brief multi-commodity-flow network cut separator
    28 * @author Tobias Achterberg
    29 * @author Christian Raack
    30 *
    31 * We try to identify a multi-commodity flow structure in the LP relaxation of the
    32 * following type:
    33 *
    34 * (1) sum_{a in delta^+(v)} f_a^k - sum_{a in delta^-(v)} f_a^k <= -d_v^k for all v in V and k in K
    35 * (2) sum_{k in K} f_a^k - c_a x_a <= 0 for all a in A
    36 *
    37 * Constraints (1) are flow conservation constraints, which say that for each commodity k and node v the
    38 * outflow (delta^+(v)) minus the inflow (delta^-(v)) of a node v must not exceed the negative of the demand of
    39 * node v in commodity k. To say it the other way around, inflow minus outflow must be at least equal to the demand.
    40 * Constraints (2) are the arc capacity constraints, which say that the sum of all flow over an arc a must not
    41 * exceed its capacity c_a x_a, with x being a binary or integer variable.
    42 * c_a x_a does not need to be a single product of a capacity and an integer variable; we also accept general scalar
    43 * products.
    44 */
    45
    46/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
    47
    48
    49/* #define COUNTNETWORKVARIABLETYPES */
    50/* #define MCF_DEBUG */
    51
    52/* algorithmic defines in testing phase*/
    53/* #define USEFLOWFORTIEBREAKING */
    54/* #define USECAPACITYFORTIEBREAKING */
    55/* #define TIEBREAKING */
    56#define BETTERWEIGHTFORDEMANDNODES
    57
    59#include "scip/cons_knapsack.h"
    60#include "scip/cuts.h"
    61#include "scip/pub_lp.h"
    62#include "scip/pub_message.h"
    63#include "scip/pub_misc.h"
    64#include "scip/pub_misc_sort.h"
    65#include "scip/pub_sepa.h"
    66#include "scip/pub_var.h"
    67#include "scip/scip_branch.h"
    68#include "scip/scip_cut.h"
    69#include "scip/scip_general.h"
    70#include "scip/scip_lp.h"
    71#include "scip/scip_mem.h"
    72#include "scip/scip_message.h"
    73#include "scip/scip_numerics.h"
    74#include "scip/scip_param.h"
    75#include "scip/scip_prob.h"
    76#include "scip/scip_sepa.h"
    77#include "scip/scip_sol.h"
    79#include "scip/scip_tree.h"
    80#include "scip/sepa_mcf.h"
    81#include <string.h>
    82
    83#define SEPA_NAME "mcf"
    84#define SEPA_DESC "multi-commodity-flow network cut separator"
    85#define SEPA_PRIORITY -10000
    86#define SEPA_FREQ 0
    87#define SEPA_MAXBOUNDDIST 0.0
    88#define SEPA_USESSUBSCIP FALSE /**< does the separator use a secondary SCIP instance? */
    89#define SEPA_DELAY FALSE /**< should separation method be delayed, if other separators found cuts? */
    90
    91/* changeable parameters*/
    92#define DEFAULT_NCLUSTERS 5 /**< number of clusters to generate in the shrunken network */
    93#define DEFAULT_MAXWEIGHTRANGE 1e+06 /**< maximal valid range max(|weights|)/min(|weights|) of row weights for CMIR */
    94#define DEFAULT_MAXTESTDELTA 20 /**< maximal number of different deltas to try (-1: unlimited) for CMIR */
    95#define DEFAULT_TRYNEGSCALING FALSE /**< should negative values also be tested in scaling? for CMIR */
    96#define DEFAULT_FIXINTEGRALRHS TRUE /**< should an additional variable be complemented if f0 = 0? for CMIR */
    97#define DEFAULT_DYNAMICCUTS TRUE /**< should generated cuts be removed from the LP if they are no longer tight? */
    98#define DEFAULT_MODELTYPE 0 /**< model type of network (0: auto, 1:directed, 2:undirected) */
    99#define DEFAULT_MAXSEPACUTS 100 /**< maximal number of cuts separated per separation round (-1: unlimited) */
    100#define DEFAULT_MAXSEPACUTSROOT 200 /**< maximal number of cuts separated per separation round in root node (-1: unlimited) */
    101#define DEFAULT_MAXINCONSISTENCYRATIO 0.02 /**< maximum inconsistency ratio (inconsistencies/(arcs*commodities)) at all */
    102#define DEFAULT_MAXARCINCONSISTENCYRATIO 0.5 /**< maximum inconsistency ratio for arcs not to be deleted */
    103#define DEFAULT_CHECKCUTSHORECONNECTIVITY TRUE /**< should we only separate if the cuts shores are connected */
    104#define DEFAULT_SEPARATESINGLENODECUTS TRUE /**< should we separate inequalities based on single node cuts */
    105#define DEFAULT_SEPARATEFLOWCUTSET TRUE /**< should we separate flowcutset inequalities */
    106#define DEFAULT_SEPARATEKNAPSACK TRUE /**< should we separate knapsack cover inequalities */
    107
    108/* fixed parameters */
    109#define BOUNDSWITCH 0.5
    110#define POSTPROCESS TRUE
    111#define VARTYPEUSEVBDS 2 /**< We allow variable bound substitution for variables with continuous vartype only.
    112 * See cuts.c for more information. */
    113#define MINFRAC 0.05
    114#define MAXFRAC 0.999
    115
    116#define MAXCOLS 2000000 /**< maximum number of columns */
    117#define MAXAGGRLEN(nvars) (0.1*(nvars)+1000) /**< maximal length of base inequality */
    118#define MINCOLROWRATIO 0.01 /**< minimum ratio cols/rows for the separator to be switched on */
    119#define MAXCOLROWRATIO 100.0 /**< maximum ratio cols/rows for the separator to be switched on */
    120#define MAXFLOWVARFLOWROWRATIO 100.0 /**< maximum ratio flowvars/flowrows for the separator to be switched on */
    121#define MAXARCNODERATIO 100.0 /**< maximum ratio arcs/nodes for the separator to be switched on */
    122#define MAXNETWORKS 4 /**< maximum number of networks to consider */
    123#define MAXFLOWCANDDENSITY 0.1 /**< maximum density of rows to be accepted as flow candidates*/
    124#define MINCOMNODESFRACTION 0.5 /**< minimal size of commodity relative to largest commodity to keep it in the network */
    125#define MINNODES 3 /**< minimal number of nodes in network to keep it for separation */
    126#define MINARCS 3 /**< minimal number of arcs in network to keep it for separation */
    127#define MAXCAPACITYSLACK 0.1 /**< maximal slack of weighted capacity constraints to use in aggregation */
    128#define UNCAPACITATEDARCSTRESHOLD 0.8 /**< threshold for the percentage of commodities an uncapacitated arc should appear in */
    129#define HASHSIZE_NODEPAIRS 500 /**< minimal size of hash table for nodepairs */
    130
    131/* #define OUTPUTGRAPH should a .gml graph of the network be generated for debugging purposes? */
    132
    133
    134#ifdef SCIP_DEBUG
    135#ifndef MCF_DEBUG
    136#define MCF_DEBUG
    137#endif
    138#endif
    139
    140#ifdef MCF_DEBUG
    141#define MCFdebugMessage printf
    142#else
    143/*lint --e{506}*/
    144/*lint --e{681}*/
    145#define MCFdebugMessage while(FALSE) printf
    146#endif
    147
    148/*
    149 * Data structures
    150 */
    151
    152/** model type of the network */
    154{
    155 SCIP_MCFMODELTYPE_AUTO = 0, /**< model type should be detected automatically */
    156 SCIP_MCFMODELTYPE_DIRECTED = 1, /**< directed network where each arc has its own capacity */
    157 SCIP_MCFMODELTYPE_UNDIRECTED = 2 /**< directed network where anti-parallel arcs share the capacity */
    160
    161/** effort level for separation */
    163{
    164 MCFEFFORTLEVEL_OFF = 0, /**< no separation */
    165 MCFEFFORTLEVEL_DEFAULT = 1, /**< conservative separation */
    166 MCFEFFORTLEVEL_AGGRESSIVE = 2 /**< aggressive separation */
    169
    170/** extracted multi-commodity-flow network */
    172{
    173 SCIP_ROW*** nodeflowrows; /**< nodeflowrows[v][k]: flow conservation constraint for node v and
    174 * commodity k; NULL if this node does not exist in the commodity */
    175 SCIP_Real** nodeflowscales; /**< scaling factors to convert nodeflowrows[v][k] into a +/-1 <= row */
    176 SCIP_Bool** nodeflowinverted; /**< does nodeflowrows[v][k] have to be inverted to fit the network structure? */
    177 SCIP_ROW** arccapacityrows; /**< arccapacity[a]: capacity constraint on arc a;
    178 * NULL if uncapacitated */
    179 SCIP_Real* arccapacityscales; /**< scaling factors to convert arccapacity[a] into a <= row with
    180 * positive entries for the flow variables */
    181 int* arcsources; /**< source node ids of arcs */
    182 int* arctargets; /**< target node ids of arcs */
    183 int* colcommodity; /**< commodity number of each column, or -1 */
    184 int nnodes; /**< number of nodes in the graph */
    185 int narcs; /**< number of arcs in the graph */
    186 int nuncapacitatedarcs; /**< number of uncapacitated arcs in the graph */
    187 int ncommodities; /**< number of commodities */
    188 SCIP_MCFMODELTYPE modeltype; /**< detected model type of the network */
    189};
    191
    192/** separator data */
    193struct SCIP_SepaData
    194{
    195 SCIP_MCFNETWORK** mcfnetworks; /**< array of multi-commodity-flow network structures */
    196 int nmcfnetworks; /**< number of multi-commodity-flow networks (-1: extraction not yet done) */
    197 int nclusters; /**< number of clusters to generate in the shrunken network -- default separation */
    198 SCIP_Real maxweightrange; /**< maximal valid range max(|weights|)/min(|weights|) of row weights */
    199 int maxtestdelta; /**< maximal number of different deltas to try (-1: unlimited) -- default separation */
    200 SCIP_Bool trynegscaling; /**< should negative values also be tested in scaling? */
    201 SCIP_Bool fixintegralrhs; /**< should an additional variable be complemented if f0 = 0? */
    202 SCIP_Bool dynamiccuts; /**< should generated cuts be removed from the LP if they are no longer tight? */
    203 int modeltype; /**< model type of the network */
    204 int maxsepacuts; /**< maximal number of cmir cuts separated per separation round */
    205 int maxsepacutsroot; /**< maximal number of cmir cuts separated per separation round in root node -- default separation */
    206 SCIP_Real maxinconsistencyratio; /**< maximum inconsistency ratio (inconsistencies/(arcs*commodities)) for separation at all*/
    207 SCIP_Real maxarcinconsistencyratio; /**< maximum inconsistency ratio for arcs not to be deleted */
    208 SCIP_Bool checkcutshoreconnectivity;/**< should we only separate if the cuts shores are connected */
    209 SCIP_Bool separatesinglenodecuts; /**< should we separate inequalities based on single node cuts ? */
    210 SCIP_Bool separateflowcutset; /**< should we separate flowcutset inequalities on the network cuts ? */
    211 SCIP_Bool separateknapsack; /**< should we separate knapsack cover inequalities on the network cuts ? */
    212 SCIP_Bool lastroundsuccess; /**< did we find a cut in the last round? */
    213 MCFEFFORTLEVEL effortlevel; /**< effort level of separation (off / aggressive / default) */
    214};
    215
    216/** internal MCF extraction data to pass to subroutines */
    217struct mcfdata
    218{
    219 unsigned char* flowrowsigns; /**< potential or actual sides of rows to be used as flow conservation constraint */
    220 SCIP_Real* flowrowscalars; /**< scalar of rows to transform into +/-1 coefficients */
    221 SCIP_Real* flowrowscores; /**< score value indicating how sure we are that this is indeed a flow conservation constraint */
    222 unsigned char* capacityrowsigns; /**< potential or actual sides of rows to be used as capacity constraint */
    223 SCIP_Real* capacityrowscores; /**< score value indicating how sure we are that this is indeed a capacity constraint */
    224 int* flowcands; /**< list of row indices that are candidates for flow conservation constraints */
    225 int nflowcands; /**< number of elements in flow candidate list */
    226 int* capacitycands; /**< list of row indices that are candidates for capacity constraints */
    227 int ncapacitycands; /**< number of elements in capacity candidate list */
    228 SCIP_Bool* plusflow; /**< is column c member of a flow row with coefficient +1? */
    229 SCIP_Bool* minusflow; /**< is column c member of a flow row with coefficient -1? */
    230 int ncommodities; /**< number of commodities */
    231 int nemptycommodities; /**< number of commodities that have been discarded but still counted in 'ncommodities' */
    232 int* commoditysigns; /**< +1: regular, -1: all arcs have opposite direction; 0: undecided */
    233 int commoditysignssize; /**< size of commoditysigns array */
    234 int* colcommodity; /**< commodity number of each column, or -1 */
    235 int* rowcommodity; /**< commodity number of each row, or -1 */
    236 int* colarcid; /**< arc id of each flow column, or -1 */
    237 int* rowarcid; /**< arc id of each capacity row, or -1 */
    238 int* rownodeid; /**< node id of each flow conservation row, or -1 */
    239 int arcarraysize; /**< size of arrays indexed by arcs */
    240 int* arcsources; /**< source node ids of arcs */
    241 int* arctargets; /**< target node ids of arcs */
    242 int* colsources; /**< source node for flow columns, -1 for non existing, -2 for unknown */
    243 int* coltargets; /**< target node for flow columns, -1 for non existing, -2 for unknown */
    244 int* firstoutarcs; /**< for each node the first arc id for which the node is the source node */
    245 int* firstinarcs; /**< for each node the first arc id for which the node is the target node */
    246 int* nextoutarcs; /**< for each arc the next outgoing arc in the adjacency list */
    247 int* nextinarcs; /**< for each arc the next outgoing arc in the adjacency list */
    248 int* newcols; /**< columns of current commodity that have to be inspected for incident flow conservation rows */
    249 int nnewcols; /**< number of newcols */
    250 int narcs; /**< number of arcs in the extracted graph */
    251 int nnodes; /**< number of nodes in the extracted graph */
    252 SCIP_Real ninconsistencies; /**< number of inconsistencies between the commodity graphs */
    253 SCIP_ROW** capacityrows; /**< capacity row for each arc */
    254 int capacityrowssize; /**< size of array */
    255 SCIP_Bool* colisincident; /**< temporary memory for column collection */
    256 int* zeroarcarray; /**< temporary array of zeros */
    257 SCIP_MCFMODELTYPE modeltype; /**< model type that is used for this network extraction */
    258};
    259typedef struct mcfdata MCFDATA; /**< internal MCF extraction data to pass to subroutines */
    260
    261/** data structure to put a nodepair on the heap -- it holds node1 <= node2 */
    262struct nodepair
    263{
    264 int node1; /**< index of the first node */
    265 int node2; /**< index of the second node */
    266 SCIP_Real weight; /**< weight of the arc in the separation problem */
    267};
    268typedef struct nodepair NODEPAIRENTRY;
    269
    270/** nodepair priority queue */
    271struct nodepairqueue
    272{
    273 SCIP_PQUEUE* pqueue; /**< priority queue of elements */
    274 NODEPAIRENTRY* nodepairs; /**< elements on the heap */
    275};
    276typedef struct nodepairqueue NODEPAIRQUEUE;
    277
    278/** partitioning of the nodes into clusters */
    279struct nodepartition
    280{
    281 int* representatives; /**< mapping of node ids to their representatives within their cluster */
    282 int* nodeclusters; /**< cluster for each node id */
    283 int* clusternodes; /**< node ids sorted by cluster */
    284 int* clusterbegin; /**< first entry in clusternodes for each cluster (size: nclusters+1) */
    285 int nclusters; /**< number of clusters */
    286};
    287typedef struct nodepartition NODEPARTITION;
    288
    289/*
    290 * Local methods
    291 */
    292
    293#define LHSPOSSIBLE 1u /**< we may use the constraint as lhs <= a*x */
    294#define RHSPOSSIBLE 2u /**< we may use the constraint as a*x <= rhs */
    295#define LHSASSIGNED 4u /**< we have chosen to use the constraint as lhs <= a*x */
    296#define RHSASSIGNED 8u /**< we have chosen to use the constraint as a*x <= rhs */
    297#define INVERTED 16u /**< we need to invert the row */
    298#define DISCARDED 32u /**< we have chosen to not use the constraint */
    299#define UNDIRECTED 64u /**< the capacity candidate has two flow variables for a commodity */
    300
    301
    302/** creates an empty MCF network data structure */
    303static
    305 SCIP* scip, /**< SCIP data structure */
    306 SCIP_MCFNETWORK** mcfnetwork /**< MCF network structure */
    307 )
    308{
    309 assert(mcfnetwork != NULL);
    310
    311 SCIP_CALL( SCIPallocBlockMemory(scip, mcfnetwork) );
    312 (*mcfnetwork)->nodeflowrows = NULL;
    313 (*mcfnetwork)->nodeflowscales = NULL;
    314 (*mcfnetwork)->nodeflowinverted = NULL;
    315 (*mcfnetwork)->arccapacityrows = NULL;
    316 (*mcfnetwork)->arccapacityscales = NULL;
    317 (*mcfnetwork)->arcsources = NULL;
    318 (*mcfnetwork)->arctargets = NULL;
    319 (*mcfnetwork)->colcommodity = NULL;
    320 (*mcfnetwork)->nnodes = 0;
    321 (*mcfnetwork)->nuncapacitatedarcs = 0;
    322 (*mcfnetwork)->narcs = 0;
    323 (*mcfnetwork)->ncommodities = 0;
    324
    325 return SCIP_OKAY;
    326}
    327
    328/** frees MCF network data structure */
    329static
    331 SCIP* scip, /**< SCIP data structure */
    332 SCIP_MCFNETWORK** mcfnetwork /**< MCF network structure */
    333 )
    334{
    335 assert(mcfnetwork != NULL);
    336
    337 if( *mcfnetwork != NULL )
    338 {
    339 int v;
    340 int a;
    341
    342 for( v = 0; v < (*mcfnetwork)->nnodes; v++ )
    343 {
    344 int k;
    345
    346 for( k = 0; k < (*mcfnetwork)->ncommodities; k++ )
    347 {
    348 if( (*mcfnetwork)->nodeflowrows[v][k] != NULL )
    349 {
    350 SCIP_CALL( SCIPreleaseRow(scip, &(*mcfnetwork)->nodeflowrows[v][k]) );
    351 }
    352 }
    353 SCIPfreeBlockMemoryArrayNull(scip, &(*mcfnetwork)->nodeflowrows[v], (*mcfnetwork)->ncommodities);
    354 SCIPfreeBlockMemoryArrayNull(scip, &(*mcfnetwork)->nodeflowscales[v], (*mcfnetwork)->ncommodities);
    355 SCIPfreeBlockMemoryArrayNull(scip, &(*mcfnetwork)->nodeflowinverted[v], (*mcfnetwork)->ncommodities);
    356 }
    357 for( a = 0; a < (*mcfnetwork)->narcs; a++ )
    358 {
    359 if( (*mcfnetwork)->arccapacityrows[a] != NULL )
    360 {
    361 SCIP_CALL( SCIPreleaseRow(scip, &(*mcfnetwork)->arccapacityrows[a]) );
    362 }
    363 }
    364 SCIPfreeBlockMemoryArrayNull(scip, &(*mcfnetwork)->nodeflowrows, (*mcfnetwork)->nnodes);
    365 SCIPfreeBlockMemoryArrayNull(scip, &(*mcfnetwork)->nodeflowscales, (*mcfnetwork)->nnodes);
    366 SCIPfreeBlockMemoryArrayNull(scip, &(*mcfnetwork)->nodeflowinverted, (*mcfnetwork)->nnodes);
    367 SCIPfreeBlockMemoryArrayNull(scip, &(*mcfnetwork)->arccapacityrows, (*mcfnetwork)->narcs);
    368 SCIPfreeBlockMemoryArrayNull(scip, &(*mcfnetwork)->arccapacityscales, (*mcfnetwork)->narcs);
    369 SCIPfreeBlockMemoryArrayNull(scip, &(*mcfnetwork)->arcsources, (*mcfnetwork)->narcs);
    370 SCIPfreeBlockMemoryArrayNull(scip, &(*mcfnetwork)->arctargets, (*mcfnetwork)->narcs);
    371 SCIPfreeMemoryArrayNull(scip, &(*mcfnetwork)->colcommodity);
    372
    373 SCIPfreeBlockMemory(scip, mcfnetwork);
    374 }
    375
    376 return SCIP_OKAY;
    377}
    378
    379/** fills the MCF network structure with the MCF data */
    380static
    382 SCIP* scip, /**< SCIP data structure */
    383 SCIP_MCFNETWORK* mcfnetwork, /**< MCF network structure */
    384 MCFDATA* mcfdata, /**< internal MCF extraction data to pass to subroutines */
    385 int* compnodeid, /**< temporary storage for v -> compv mapping; must be set to -1 for all v */
    386 int* compnodes, /**< array of node ids of the component */
    387 int ncompnodes, /**< number of nodes in the component */
    388 int* comparcs, /**< array of arc ids of the component */
    389 int ncomparcs /**< number of arcs in the component */
    390 )
    391{
    392 unsigned char* flowrowsigns = mcfdata->flowrowsigns;
    393 SCIP_Real* flowrowscalars = mcfdata->flowrowscalars;
    394 unsigned char* capacityrowsigns = mcfdata->capacityrowsigns;
    395 int* flowcands = mcfdata->flowcands;
    396 int nflowcands = mcfdata->nflowcands;
    397 int ncommodities = mcfdata->ncommodities;
    398 int* commoditysigns = mcfdata->commoditysigns;
    399 int* colcommodity = mcfdata->colcommodity;
    400 int* rowcommodity = mcfdata->rowcommodity;
    401 int* rownodeid = mcfdata->rownodeid;
    402 SCIP_ROW** capacityrows = mcfdata->capacityrows;
    403 SCIP_MCFMODELTYPE modeltype = mcfdata->modeltype;
    404
    405 SCIP_Real* comdemands;
    406 SCIP_ROW** rows;
    407 SCIP_COL** cols;
    408 int nrows;
    409 int ncols;
    410 int* compcommodity;
    411 int ncompcommodities;
    412 int v;
    413 int a;
    414 int k;
    415 int i;
    416 int c;
    417
    418 assert(mcfnetwork != NULL);
    419 assert(modeltype != SCIP_MCFMODELTYPE_AUTO);
    420 assert(2 <= ncompnodes && ncompnodes <= mcfdata->nnodes);
    421 assert(1 <= ncomparcs && ncomparcs <= mcfdata->narcs);
    422 assert(ncommodities > 0);
    423
    424#ifndef NDEBUG
    425 /* v -> compv mapping must be all -1 */
    426 for( v = 0; v < mcfdata->nnodes; v++ )
    427 assert(compnodeid[v] == -1);
    428#endif
    429
    430 /* allocate temporary memory */
    431 SCIP_CALL( SCIPallocBufferArray(scip, &comdemands, ncommodities) );
    432 SCIP_CALL( SCIPallocBufferArray(scip, &compcommodity, ncommodities) );
    433
    434 /* initialize demand array */
    435 BMSclearMemoryArray(comdemands, ncommodities);
    436
    437 /* initialize k -> compk mapping */
    438 for( k = 0; k < ncommodities; k++ )
    439 compcommodity[k] = -1;
    440
    441 /* get LP rows and cols data */
    442 SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) );
    443 SCIP_CALL( SCIPgetLPColsData(scip, &cols, &ncols) );
    444
    445 /* generate v -> compv mapping */
    446 for( i = 0; i < ncompnodes; i++ )
    447 {
    448 v = compnodes[i];
    449 assert(0 <= v && v < mcfdata->nnodes);
    450 compnodeid[v] = i;
    451 }
    452
    453 /* generate k -> compk mapping */
    454 ncompcommodities = 0;
    455 for( i = 0; i < nflowcands; i++ )
    456 {
    457 int r;
    458 int rv;
    459
    460 r = flowcands[i];
    461 assert(0 <= r && r < nrows);
    462
    463 rv = rownodeid[r];
    464 if( rv >= 0 && compnodeid[rv] >= 0 )
    465 {
    466 k = rowcommodity[r];
    467 assert(0 <= k && k < ncommodities);
    468 if( compcommodity[k] == -1 )
    469 {
    470 compcommodity[k] = ncompcommodities;
    471 ncompcommodities++;
    472 }
    473 }
    474 }
    475
    476 /** @todo model type and flow type may be different for each component */
    477 /* record model and flow type */
    478 mcfnetwork->modeltype = modeltype;
    479
    480 /* record network size */
    481 mcfnetwork->nnodes = ncompnodes;
    482 mcfnetwork->narcs = ncomparcs;
    483 mcfnetwork->nuncapacitatedarcs = 0;
    484 mcfnetwork->ncommodities = ncompcommodities;
    485
    486 /* allocate memory for arrays and initialize with default values */
    487 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &mcfnetwork->nodeflowrows, mcfnetwork->nnodes) );
    488 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &mcfnetwork->nodeflowscales, mcfnetwork->nnodes) );
    489 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &mcfnetwork->nodeflowinverted, mcfnetwork->nnodes) );
    490 for( v = 0; v < mcfnetwork->nnodes; v++ )
    491 {
    492 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &mcfnetwork->nodeflowrows[v], mcfnetwork->ncommodities) ); /*lint !e866*/
    493 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &mcfnetwork->nodeflowscales[v], mcfnetwork->ncommodities) ); /*lint !e866*/
    494 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &mcfnetwork->nodeflowinverted[v], mcfnetwork->ncommodities) ); /*lint !e866*/
    495 for( k = 0; k < mcfnetwork->ncommodities; k++ )
    496 {
    497 mcfnetwork->nodeflowrows[v][k] = NULL;
    498 mcfnetwork->nodeflowscales[v][k] = 0.0;
    499 mcfnetwork->nodeflowinverted[v][k] = FALSE;
    500 }
    501 }
    502
    503 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &mcfnetwork->arccapacityrows, mcfnetwork->narcs) );
    504 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &mcfnetwork->arccapacityscales, mcfnetwork->narcs) );
    505 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &mcfnetwork->arcsources, mcfnetwork->narcs) );
    506 SCIP_CALL( SCIPallocBlockMemoryArray(scip, &mcfnetwork->arctargets, mcfnetwork->narcs) );
    507 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfnetwork->colcommodity, ncols) );
    508 for( a = 0; a < mcfnetwork->narcs; a++ )
    509 {
    510 mcfnetwork->arcsources[a] = -1;
    511 mcfnetwork->arctargets[a] = -1;
    512 }
    513 BMSclearMemoryArray(mcfnetwork->arccapacityrows, mcfnetwork->narcs);
    514 BMSclearMemoryArray(mcfnetwork->arccapacityscales, mcfnetwork->narcs);
    515 BMSclearMemoryArray(mcfnetwork->colcommodity, mcfnetwork->ncommodities);
    516
    517 /* fill in existing node data */
    518 for( i = 0; i < nflowcands; i++ )
    519 {
    520 int r;
    521 int rv;
    522
    523 r = flowcands[i];
    524 assert(0 <= r && r < nrows);
    525
    526 rv = rownodeid[r];
    527 if( rv >= 0 && compnodeid[rv] >= 0 )
    528 {
    529 SCIP_Real scale;
    530 int rk;
    531
    532 v = compnodeid[rv];
    533 rk = rowcommodity[r];
    534 assert(v < mcfnetwork->nnodes);
    535 assert(0 <= rk && rk < ncommodities);
    536 assert((flowrowsigns[r] & (LHSASSIGNED | RHSASSIGNED)) != 0);
    537
    538 k = compcommodity[rk];
    539 assert(0 <= k && k < mcfnetwork->ncommodities);
    540
    541 /* fill in node -> row assignment */
    542 SCIP_CALL( SCIPcaptureRow(scip, rows[r]) );
    543 mcfnetwork->nodeflowrows[v][k] = rows[r];
    544 scale = flowrowscalars[r];
    545 if( (flowrowsigns[r] & LHSASSIGNED) != 0 )
    546 scale *= -1.0;
    547 if( commoditysigns[rk] == -1 )
    548 scale *= -1.0;
    549 mcfnetwork->nodeflowscales[v][k] = scale;
    550 mcfnetwork->nodeflowinverted[v][k] = ((flowrowsigns[r] & INVERTED) != 0);
    551 }
    552 }
    553
    554 /* fill in existing arc data */
    555 for( a = 0; a < mcfnetwork->narcs; a++ )
    556 {
    557 SCIP_ROW* capacityrow;
    558 SCIP_COL** rowcols;
    559 SCIP_Real* rowvals;
    560 int rowlen;
    561 int globala;
    562 int r;
    563 int j;
    564
    565 globala = comparcs[a];
    566 capacityrow = capacityrows[globala];
    567
    568 mcfnetwork->arccapacityscales[a] = 1.0;
    569
    570 /* If arc is capacitated */
    571 if( capacityrow != NULL)
    572 {
    573 r = SCIProwGetLPPos(capacityrow);
    574 assert(0 <= r && r < nrows);
    575 assert((capacityrowsigns[r] & (LHSASSIGNED | RHSASSIGNED)) != 0);
    576 assert((capacityrowsigns[r] & INVERTED) == 0);
    577 assert(mcfdata->rowarcid[r] == globala);
    578
    579 SCIP_CALL( SCIPcaptureRow(scip, capacityrow) );
    580 mcfnetwork->arccapacityrows[a] = capacityrow;
    581
    582 /* Scale constraint such that the coefficients for the flow variables are normalized in such a way that coefficients in
    583 * multiple capacity constraints that belong to the same commodity are (hopefully) equal.
    584 * This is needed for binary flow variable models in which the demand of each commodity is stored as the coefficient in
    585 * the capacity constraints. Since it may happen (e.g., due to presolve) that different capacity constraints are scaled
    586 * differently, we need to find scaling factors to make the demand coefficients of each commodity equal.
    587 * To do this, we scale the first capacity constraint with +1 and then store the coefficients of the flow variables
    588 * as target demands for the commodities. Then, we scale the other constraints in such a way that these demands are hit, if possible.
    589 * Note that for continuous flow variable models, the coefficients in the capacity constraints are usually +1.0.
    590 * This is achieved automatically by our scaling routine.
    591 */
    592 rowcols = SCIProwGetCols(capacityrow);
    593 rowvals = SCIProwGetVals(capacityrow);
    594 rowlen = SCIProwGetNLPNonz(capacityrow);
    595 for( j = 0; j < rowlen; j++ )
    596 {
    597 c = SCIPcolGetLPPos(rowcols[j]);
    598 assert(0 <= c && c < SCIPgetNLPCols(scip));
    599 k = colcommodity[c];
    600 if( k >= 0 )
    601 {
    602 if( comdemands[k] != 0.0 )
    603 {
    604 /* update the scaling factor */
    605 mcfnetwork->arccapacityscales[a] = comdemands[k]/rowvals[j];
    606 break;
    607 }
    608 }
    609 }
    610
    611 /* use negative scaling if we use the left hand side, use positive scaling if we use the right hand side */
    612 mcfnetwork->arccapacityscales[a] = ABS(mcfnetwork->arccapacityscales[a]);
    613 if( (capacityrowsigns[r] & LHSASSIGNED) != 0 )
    614 mcfnetwork->arccapacityscales[a] *= -1.0;
    615
    616 /* record the commodity demands */
    617 for( j = 0; j < rowlen; j++ )
    618 {
    619 c = SCIPcolGetLPPos(rowcols[j]);
    620 assert(0 <= c && c < SCIPgetNLPCols(scip));
    621 k = colcommodity[c];
    622 if( k >= 0 && comdemands[k] == 0.0 )
    623 comdemands[k] = mcfnetwork->arccapacityscales[a] * rowvals[j];
    624 }
    625 }
    626 else
    627 {
    628 /* arc is uncapacitated */
    629 mcfnetwork->arccapacityrows[a] = NULL;
    630 mcfnetwork->nuncapacitatedarcs++;
    631 }
    632
    633 /* copy the source/target node assignment */
    634 if( mcfdata->arcsources[globala] >= 0 )
    635 {
    636 assert(mcfdata->arcsources[globala] < mcfdata->nnodes);
    637 assert(0 <= compnodeid[mcfdata->arcsources[globala]] && compnodeid[mcfdata->arcsources[globala]] < mcfnetwork->nnodes);
    638 mcfnetwork->arcsources[a] = compnodeid[mcfdata->arcsources[globala]];
    639 }
    640 if( mcfdata->arctargets[globala] >= 0 )
    641 {
    642 assert(mcfdata->arctargets[globala] < mcfdata->nnodes);
    643 assert(0 <= compnodeid[mcfdata->arctargets[globala]] && compnodeid[mcfdata->arctargets[globala]] < mcfnetwork->nnodes);
    644 mcfnetwork->arctargets[a] = compnodeid[mcfdata->arctargets[globala]];
    645 }
    646 }
    647
    648 /* translate colcommodity array */
    649 for( c = 0; c < ncols; c++ )
    650 {
    651 k = colcommodity[c];
    652 if( k >= 0 )
    653 mcfnetwork->colcommodity[c] = compcommodity[k];
    654 else
    655 mcfnetwork->colcommodity[c] = -1;
    656 }
    657
    658 /* reset v -> compv mapping */
    659 for( i = 0; i < ncompnodes; i++ )
    660 {
    661 assert(0 <= compnodes[i] && compnodes[i] < mcfdata->nnodes);
    662 assert(compnodeid[compnodes[i]] == i);
    663 compnodeid[compnodes[i]] = -1;
    664 }
    665
    666 /* free temporary memory */
    667 SCIPfreeBufferArray(scip, &compcommodity);
    668 SCIPfreeBufferArray(scip, &comdemands);
    669
    670 return SCIP_OKAY;
    671}
    672
    673#ifdef SCIP_DEBUG
    674/** displays the MCF network */
    675static
    676void mcfnetworkPrint(
    677 SCIP_MCFNETWORK* mcfnetwork /**< MCF network structure */
    678 )
    679{
    680 if( mcfnetwork == NULL )
    681 MCFdebugMessage("MCF network is empty\n");
    682 else
    683 {
    684 int v;
    685 int a;
    686
    687 for( v = 0; v < mcfnetwork->nnodes; v++ )
    688 {
    689 int k;
    690
    691 MCFdebugMessage("node %2d:\n", v);
    692 for( k = 0; k < mcfnetwork->ncommodities; k++ )
    693 {
    694 MCFdebugMessage(" commodity %2d: ", k);
    695 if( mcfnetwork->nodeflowrows[v][k] != NULL )
    696 {
    697 MCFdebugMessage("<%s> [%+g] [inv:%u]\n", SCIProwGetName(mcfnetwork->nodeflowrows[v][k]),
    698 mcfnetwork->nodeflowscales[v][k], mcfnetwork->nodeflowinverted[v][k]);
    699 /*SCIP_CALL( SCIProwPrint(mcfnetwork->nodeflowrows[v][k], NULL) );*/
    700 }
    701 else
    702 MCFdebugMessage("-\n");
    703 }
    704 }
    705
    706 for( a = 0; a < mcfnetwork->narcs; a++ )
    707 {
    708 MCFdebugMessage("arc %2d [%2d -> %2d]: ", a, mcfnetwork->arcsources[a], mcfnetwork->arctargets[a]);
    709 if( mcfnetwork->arccapacityrows[a] != NULL )
    710 {
    711 MCFdebugMessage("<%s> [%+g]\n", SCIProwGetName(mcfnetwork->arccapacityrows[a]), mcfnetwork->arccapacityscales[a]);
    712 /*SCIProwPrint(mcfnetwork->arccapacityrows[a], NULL);*/
    713 }
    714 else
    715 MCFdebugMessage("-\n");
    716 }
    717 }
    718}
    719
    720/** displays commodities and its members */
    721static
    722void printCommodities(
    723 SCIP* scip, /**< SCIP data structure */
    724 MCFDATA* mcfdata /**< internal MCF extraction data to pass to subroutines */
    725 )
    726{
    727 unsigned char* flowrowsigns = mcfdata->flowrowsigns;
    728 unsigned char* capacityrowsigns = mcfdata->capacityrowsigns;
    729 int ncommodities = mcfdata->ncommodities;
    730 int* commoditysigns = mcfdata->commoditysigns;
    731 int* colcommodity = mcfdata->colcommodity;
    732 int* rowcommodity = mcfdata->rowcommodity;
    733 int* colarcid = mcfdata->colarcid;
    734 int* rownodeid = mcfdata->rownodeid;
    735 int nnodes = mcfdata->nnodes;
    736 SCIP_ROW** capacityrows = mcfdata->capacityrows;
    737
    738 SCIP_COL** cols;
    739 SCIP_ROW** rows;
    740 int ncols;
    741 int nrows;
    742 int k;
    743 int c;
    744 int r;
    745 int a;
    746
    747 cols = SCIPgetLPCols(scip);
    748 ncols = SCIPgetNLPCols(scip);
    749 rows = SCIPgetLPRows(scip);
    750 nrows = SCIPgetNLPRows(scip);
    751
    752 for( k = 0; k < ncommodities; k++ )
    753 {
    754 MCFdebugMessage("commodity %d (sign: %+d):\n", k, commoditysigns[k]);
    755
    756 for( c = 0; c < ncols; c++ )
    757 {
    758 if( colcommodity[c] == k )
    759 MCFdebugMessage(" col <%s>: arc %d\n", SCIPvarGetName(SCIPcolGetVar(cols[c])), colarcid != NULL ? colarcid[c] : -1);
    760 }
    761 for( r = 0; r < nrows; r++ )
    762 {
    763 if( rowcommodity[r] == k )
    764 MCFdebugMessage(" row <%s>: node %d [sign:%+d, inv:%+d]\n", SCIProwGetName(rows[r]), rownodeid != NULL ? rownodeid[r] : -1,
    765 (flowrowsigns[r] & RHSASSIGNED) != 0 ? +1 : -1,
    766 (flowrowsigns[r] & INVERTED) != 0 ? -1 : +1);
    767 }
    768 MCFdebugMessage("\n");
    769 }
    770
    771 if( rownodeid != NULL )
    772 {
    773 int v;
    774
    775 for( v = 0; v < nnodes; v++ )
    776 {
    777 MCFdebugMessage("node %d:\n", v);
    778 for( r = 0; r < nrows; r++ )
    779 {
    780 if( rownodeid[r] == v )
    781 MCFdebugMessage(" row <%s> [sign:%+d, inv:%+d]\n", SCIProwGetName(rows[r]),
    782 (flowrowsigns[r] & RHSASSIGNED) != 0 ? +1 : -1,
    783 (flowrowsigns[r] & INVERTED) != 0 ? -1 : +1);
    784 }
    785 MCFdebugMessage("\n");
    786 }
    787 }
    788
    789 assert(capacityrows != NULL || mcfdata->narcs == 0);
    790
    791 MCFdebugMessage("capacities:\n");
    792 for( a = 0; a < mcfdata->narcs; a++ )
    793 {
    794 MCFdebugMessage(" arc %d: ", a);
    795 if( capacityrows[a] != NULL ) /*lint !e613*/
    796 {
    797 r = SCIProwGetLPPos(capacityrows[a]); /*lint !e613*/
    798 assert(0 <= r && r < nrows);
    799 if( (capacityrowsigns[r] & LHSASSIGNED) != 0 )
    800 MCFdebugMessage(" row <%s> [sign:-1]\n", SCIProwGetName(rows[r]));
    801 else if( (capacityrowsigns[r] & RHSASSIGNED) != 0 )
    802 MCFdebugMessage(" row <%s> [sign:+1]\n", SCIProwGetName(rows[r]));
    803 }
    804 else
    805 MCFdebugMessage(" -\n");
    806 }
    807 MCFdebugMessage("\n");
    808
    809 assert(colcommodity != NULL || ncols == 0);
    810
    811 MCFdebugMessage("unused columns:\n");
    812 for( c = 0; c < ncols; c++ )
    813 {
    814 if( colcommodity[c] == -1 ) /*lint !e613*/
    815 {
    816 SCIP_VAR* var = SCIPcolGetVar(cols[c]);
    817 MCFdebugMessage(" col <%s> [%g,%g]\n", SCIPvarGetName(var), SCIPvarGetLbGlobal(var), SCIPvarGetUbGlobal(var));
    818 }
    819 }
    820 MCFdebugMessage("\n");
    821
    822 MCFdebugMessage("unused rows:\n");
    823 for( r = 0; r < nrows; r++ )
    824 {
    825 assert(rowcommodity != NULL);
    826
    827 if( rowcommodity[r] == -1 && (capacityrowsigns == NULL || (capacityrowsigns[r] & (LHSASSIGNED | RHSASSIGNED)) == 0) )
    828 {
    829 MCFdebugMessage(" row <%s>\n", SCIProwGetName(rows[r]));
    830 /*SCIPdebug( SCIP_CALL( SCIPprintRow(scip, rows[r], NULL) ) );*/
    831 }
    832 }
    833 MCFdebugMessage("\n");
    834}
    835#endif
    836
    837/** comparator method for flow and capacity row candidates */
    838static
    840{
    841 SCIP_Real* rowscores = (SCIP_Real*)dataptr;
    842
    843 if( rowscores[ind2] < rowscores[ind1] )
    844 return -1;
    845 else if( rowscores[ind2] > rowscores[ind1] )
    846 return +1;
    847 else
    848 return 0;
    849}
    850
    851/** extracts flow conservation from the LP */
    852static
    854 SCIP* scip, /**< SCIP data structure */
    855 MCFDATA* mcfdata /**< internal MCF extraction data to pass to subroutines */
    856 )
    857{
    858 unsigned char* flowrowsigns;
    859 SCIP_Real* flowrowscalars;
    860 SCIP_Real* flowrowscores;
    861 int* flowcands;
    862
    863 SCIP_ROW** rows;
    864 int nrows;
    865 int ncols;
    866 int r;
    867
    868 SCIP_Real maxdualflow;
    869
    870 SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) );
    871 ncols = SCIPgetNLPCols(scip);
    872
    873 /* allocate temporary memory for extraction data */
    874 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->flowrowsigns, nrows) ); /*lint !e685*/
    875 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->flowrowscalars, nrows) );
    876 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->flowrowscores, nrows) );
    877 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->flowcands, nrows) );
    878 flowrowsigns = mcfdata->flowrowsigns;
    879 flowrowscalars = mcfdata->flowrowscalars;
    880 flowrowscores = mcfdata->flowrowscores;
    881 flowcands = mcfdata->flowcands;
    882
    883 assert(mcfdata->nflowcands == 0);
    884
    885 maxdualflow = 0.0;
    886 for( r = 0; r < nrows; r++ )
    887 {
    888 SCIP_ROW* row;
    889 SCIP_COL** rowcols;
    890 SCIP_Real* rowvals;
    891 SCIP_Real rowlhs;
    892 SCIP_Real rowrhs;
    893 int rowlen;
    894 int nbinvars;
    895 int nintvars;
    896 int nimplintvars;
    897 int ncontvars;
    898 SCIP_Real coef;
    899 SCIP_Bool hasposcoef;
    900 SCIP_Bool hasnegcoef;
    901 SCIP_Real absdualsol;
    902 int i;
    903
    904 row = rows[r];
    905 assert(SCIProwGetLPPos(row) == r);
    906
    907 /* get dual solution, if available */
    908 absdualsol = SCIProwGetDualsol(row);
    909 if( absdualsol == SCIP_INVALID ) /*lint !e777*/
    910 absdualsol = 0.0;
    911 absdualsol = ABS(absdualsol);
    912
    913 flowrowsigns[r] = 0;
    914 flowrowscalars[r] = 0.0;
    915 flowrowscores[r] = 0.0;
    916
    917 /* ignore modifiable rows */
    918 if( SCIProwIsModifiable(row) )
    919 continue;
    920
    921 /* ignore empty rows */
    922 rowlen = SCIProwGetNLPNonz(row);
    923 if( rowlen == 0 )
    924 continue;
    925
    926 /* No dense rows please */
    927 if( rowlen > MAXFLOWCANDDENSITY * ncols )
    928 continue;
    929
    930 rowcols = SCIProwGetCols(row);
    931 rowvals = SCIProwGetVals(row);
    932 rowlhs = SCIProwGetLhs(row) - SCIProwGetConstant(row);
    933 rowrhs = SCIProwGetRhs(row) - SCIProwGetConstant(row);
    934
    935 /* identify flow conservation constraints */
    936 coef = ABS(rowvals[0]);
    937 hasposcoef = FALSE;
    938 hasnegcoef = FALSE;
    939 nbinvars = 0;
    940 nintvars = 0;
    941 nimplintvars = 0;
    942 ncontvars = 0;
    943 for( i = 0; i < rowlen; i++ )
    944 {
    945 SCIP_VAR* var;
    946 SCIP_Real absval = ABS(rowvals[i]);
    947
    948 if( !SCIPisEQ(scip, absval, coef) )
    949 break;
    950
    951 hasposcoef = (hasposcoef || rowvals[i] > 0.0);
    952 hasnegcoef = (hasnegcoef || rowvals[i] < 0.0);
    953 var = SCIPcolGetVar(rowcols[i]);
    954
    955 if ( SCIPvarIsImpliedIntegral(var) )
    956 ++nimplintvars;
    957 else
    958 {
    959 switch( SCIPvarGetType(var) )
    960 {
    962 ++nbinvars;
    963 break;
    965 ++nintvars;
    966 break;
    968 ++ncontvars;
    969 break;
    970 default:
    971 SCIPerrorMessage("unknown variable type\n");
    972 return SCIP_INVALIDDATA;
    973 } /*lint !e788*/
    974 }
    975 }
    976
    977 if( i == rowlen )
    978 {
    979 /* Flow conservation constraints should always be a*x <= -d.
    980 * If lhs and rhs are finite, both sides are still valid candidates.
    981 */
    982 if( !SCIPisInfinity(scip, -rowlhs) )
    983 flowrowsigns[r] |= LHSPOSSIBLE;
    984 if( !SCIPisInfinity(scip, rowrhs) )
    985 flowrowsigns[r] |= RHSPOSSIBLE;
    986 flowrowscalars[r] = 1.0/coef;
    987 flowcands[mcfdata->nflowcands] = r;
    988 mcfdata->nflowcands++;
    989 }
    990
    991 /* calculate flow row score */
    992 if( (flowrowsigns[r] & (LHSPOSSIBLE | RHSPOSSIBLE)) != 0 )
    993 {
    994 /* row does not need to be scaled: score +1000 */
    995 if( SCIPisEQ(scip, flowrowscalars[r], 1.0) )
    996 flowrowscores[r] += 1000.0;
    997
    998 /* row has positive and negative coefficients: score +500 */
    999 if( hasposcoef && hasnegcoef )
    1000 flowrowscores[r] += 500.0;
    1001
    1002 /* all variables are of the same type:
    1003 * continuous: score +1000
    1004 * integer: score +500
    1005 * binary: score +100
    1006 */
    1007 if( ncontvars == rowlen || nimplintvars == rowlen )
    1008 flowrowscores[r] += 1000.0;
    1009 else if( nintvars + nimplintvars == rowlen )
    1010 flowrowscores[r] += 500.0;
    1011 else if( nbinvars == rowlen )
    1012 flowrowscores[r] += 100.0;
    1013
    1014 /* the longer the row, the earlier we want to process it: score +10*len/(len+10) */
    1015 /* value is in [1,10) */
    1016 flowrowscores[r] += 10.0*rowlen/(rowlen+10.0);
    1017
    1018 /* row is an equation: score +50, tie-breaking */
    1019 if( (flowrowsigns[r] & (LHSPOSSIBLE | RHSPOSSIBLE)) == (LHSPOSSIBLE | RHSPOSSIBLE) )
    1020 flowrowscores[r] += 50.0;
    1021
    1022 assert(flowrowscores[r] > 0.0);
    1023
    1024 /* update maximum dual solution value for additional score tie breaking */
    1025 maxdualflow = MAX(maxdualflow, absdualsol);
    1026
    1027 /** @todo go through list of several model types, depending on the current model type throw away invalid constraints
    1028 * instead of assigning a low score
    1029 */
    1030 }
    1031 }
    1032
    1033 /* apply additional score tie breaking using the dual solutions */
    1034 if( SCIPisPositive(scip, maxdualflow) )
    1035 {
    1036 int i;
    1037
    1038 for( i = 0; i < mcfdata->nflowcands; i++ )
    1039 {
    1040 SCIP_Real dualsol;
    1041
    1042 r = flowcands[i];
    1043 assert(0 <= r && r < nrows);
    1044 dualsol = SCIProwGetDualsol(rows[r]);
    1045 if( dualsol == SCIP_INVALID ) /*lint !e777*/
    1046 dualsol = 0.0;
    1047 else if( flowrowsigns[r] == (LHSPOSSIBLE | RHSPOSSIBLE) )
    1048 dualsol = ABS(dualsol);
    1049 else if( flowrowsigns[r] == RHSPOSSIBLE )
    1050 dualsol = -dualsol;
    1051 assert(maxdualflow > 0.0); /*for flexelint*/
    1052 flowrowscores[r] += dualsol/maxdualflow + 1.0;
    1053 assert(flowrowscores[r] > 0.0);
    1054 }
    1055 }
    1056
    1057 /* sort candidates by score */
    1058 SCIPsortInd(mcfdata->flowcands, compCands, (void*)flowrowscores, mcfdata->nflowcands);
    1059
    1060 MCFdebugMessage("flow conservation candidates [%d]\n", mcfdata->nflowcands);
    1061#ifdef SCIP_DEBUG
    1062 for( r = 0; r < mcfdata->nflowcands; r++ )
    1063 {
    1064 /*SCIPdebug( SCIP_CALL( SCIPprintRow(scip, rows[mcfdata->flowcands[r]], NULL) ) );*/
    1065 SCIPdebugMsg(scip, "%4d [score: %2g]: %s\n", mcfdata->flowcands[r], flowrowscores[mcfdata->flowcands[r]],
    1066 SCIProwGetName(rows[mcfdata->flowcands[r]]));
    1067 }
    1068#endif
    1069
    1070 return SCIP_OKAY;
    1071}
    1072
    1073/** extracts capacity rows from the LP */
    1074static
    1076 SCIP* scip, /**< SCIP data structure */
    1077 MCFDATA* mcfdata /**< internal MCF extraction data to pass to subroutines */
    1078 )
    1079{
    1080 unsigned char* flowrowsigns = mcfdata->flowrowsigns;
    1081 int* colcommodity = mcfdata->colcommodity;
    1082 int ncommodities = mcfdata->ncommodities;
    1083 int nactivecommodities = mcfdata->ncommodities - mcfdata->nemptycommodities;
    1084 SCIP_MCFMODELTYPE modeltype = mcfdata->modeltype;
    1085
    1086 unsigned char* capacityrowsigns;
    1087 SCIP_Real* capacityrowscores;
    1088 int* capacitycands;
    1089
    1090 SCIP_ROW** rows;
    1091 int nrows;
    1092 int r;
    1093
    1094 SCIP_Real maxdualcapacity;
    1095 int maxcolspercommoditylimit;
    1096 int *ncolspercommodity;
    1097 int *maxcolspercommodity;
    1098 SCIP_Real directedcandsscore;
    1099 SCIP_Real undirectedcandsscore;
    1100
    1101 SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) );
    1102
    1103 /* allocate temporary memory for extraction data */
    1104 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->capacityrowsigns, nrows) ); /*lint !e685*/
    1105 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->capacityrowscores, nrows) );
    1106 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->capacitycands, nrows) );
    1107 capacityrowsigns = mcfdata->capacityrowsigns;
    1108 capacityrowscores = mcfdata->capacityrowscores;
    1109 capacitycands = mcfdata->capacitycands;
    1110
    1111 assert(mcfdata->ncapacitycands == 0);
    1112
    1113 /* allocate temporary memory for model type identification */
    1114 SCIP_CALL( SCIPallocBufferArray(scip, &ncolspercommodity, ncommodities) );
    1115 SCIP_CALL( SCIPallocBufferArray(scip, &maxcolspercommodity, nrows) );
    1116
    1117 /* identify model type and set the maximal number of flow variables per capacity constraint and commodity */
    1118 switch( modeltype )
    1119 {
    1121 maxcolspercommoditylimit = 2; /* will be set to 1 later if we detect that the network is directed */
    1122 break;
    1124 maxcolspercommoditylimit = 1;
    1125 break;
    1127 maxcolspercommoditylimit = 2;
    1128 break;
    1129 default:
    1130 SCIPerrorMessage("invalid parameter value %d for model type\n", modeltype);
    1131 return SCIP_INVALIDDATA;
    1132 }
    1133
    1134 maxdualcapacity = 0.0;
    1135 directedcandsscore = 0.0;
    1136 undirectedcandsscore = 0.0;
    1137 for( r = 0; r < nrows; r++ )
    1138 {
    1139 SCIP_ROW* row;
    1140 SCIP_COL** rowcols;
    1141 SCIP_Real* rowvals;
    1142 SCIP_Real rowlhs;
    1143 SCIP_Real rowrhs;
    1144 int rowlen;
    1145 int nposflowcoefs;
    1146 int nnegflowcoefs;
    1147 int nposcapacitycoefs;
    1148 int nnegcapacitycoefs;
    1149 int nbadcoefs;
    1150 int ncoveredcommodities;
    1151 SCIP_Real sameflowcoef;
    1152 SCIP_Real sameabsflowcoef;
    1153 SCIP_Real maxabscapacitycoef;
    1154 SCIP_Real absdualsol;
    1155 unsigned char rowsign;
    1156 int i;
    1157
    1158 row = rows[r];
    1159 assert(SCIProwGetLPPos(row) == r);
    1160
    1161 capacityrowsigns[r] = 0;
    1162 capacityrowscores[r] = 0.0;
    1163
    1164 /* ignore modifiable rows */
    1165 if( SCIProwIsModifiable(row) )
    1166 continue;
    1167
    1168 /* ignore empty rows */
    1169 rowlen = SCIProwGetNLPNonz(row);
    1170 if( rowlen == 0 )
    1171 continue;
    1172
    1173 /* ignore rows that have already been used as flow conservation constraints */
    1174 if( (flowrowsigns[r] & (LHSASSIGNED | RHSASSIGNED)) != 0 )
    1175 continue;
    1176
    1177 /* get dual solution, if available */
    1178 absdualsol = SCIProwGetDualsol(row);
    1179 if( absdualsol == SCIP_INVALID ) /*lint !e777*/
    1180 absdualsol = 0.0;
    1181 absdualsol = ABS(absdualsol);
    1182
    1183 rowcols = SCIProwGetCols(row);
    1184 rowvals = SCIProwGetVals(row);
    1185 rowlhs = SCIProwGetLhs(row) - SCIProwGetConstant(row);
    1186 rowrhs = SCIProwGetRhs(row) - SCIProwGetConstant(row);
    1187
    1188 /* reset commodity counting array */
    1189 BMSclearMemoryArray(ncolspercommodity, ncommodities);
    1190 maxcolspercommodity[r] = 0;
    1191
    1192 /* identify capacity constraints */
    1193 nposflowcoefs = 0;
    1194 nnegflowcoefs = 0;
    1195 nposcapacitycoefs = 0;
    1196 nnegcapacitycoefs = 0;
    1197 nbadcoefs = 0;
    1198 ncoveredcommodities = 0;
    1199 sameflowcoef = 0.0;
    1200 sameabsflowcoef = 0.0;
    1201 maxabscapacitycoef = 0.0;
    1202
    1203 rowsign = 0;
    1204 if( !SCIPisInfinity(scip, -rowlhs) )
    1205 rowsign |= LHSPOSSIBLE;
    1206 if( !SCIPisInfinity(scip, rowrhs) )
    1207 rowsign |= RHSPOSSIBLE;
    1208 for( i = 0; i < rowlen; i++ )
    1209 {
    1210 int c;
    1211 int k;
    1212
    1213 c = SCIPcolGetLPPos(rowcols[i]);
    1214 assert(0 <= c && c < SCIPgetNLPCols(scip));
    1215 assert(colcommodity != NULL); /* for lint */
    1216
    1217 /* check if this is a flow variable */
    1218 k = colcommodity[c];
    1219 assert(-1 <= k && k < ncommodities);
    1220 if( k >= 0 )
    1221 {
    1222 SCIP_Real abscoef;
    1223
    1224 abscoef = ABS(rowvals[i]);
    1225 if( sameflowcoef == 0.0 )
    1226 sameflowcoef = rowvals[i];
    1227 else if( !SCIPisEQ(scip, sameflowcoef, rowvals[i]) )
    1228 sameflowcoef = SCIP_REAL_MAX;
    1229 if( sameabsflowcoef == 0.0 )
    1230 sameabsflowcoef = abscoef;
    1231 else if( !SCIPisEQ(scip, sameabsflowcoef, abscoef) )
    1232 sameabsflowcoef = SCIP_REAL_MAX;
    1233
    1234 if( rowvals[i] > 0.0 )
    1235 nposflowcoefs++;
    1236 else
    1237 nnegflowcoefs++;
    1238
    1239 /* count number of covered commodities in capacity candidate */
    1240 if( ncolspercommodity[k] == 0 )
    1241 ncoveredcommodities++;
    1242 ncolspercommodity[k]++;
    1243 maxcolspercommodity[r] = MAX(maxcolspercommodity[r], ncolspercommodity[k]);
    1244
    1245 if( ncolspercommodity[k] >= 2 )
    1246 capacityrowsigns[r] |= UNDIRECTED;
    1247 }
    1248 else
    1249/* if( SCIPvarGetType(SCIPcolGetVar(rowcols[i])) != SCIP_VARTYPE_CONTINUOUS ) */
    1250 {
    1251 SCIP_Real abscoef;
    1252
    1253 /* save maximal capacity coef*/
    1254 abscoef = ABS(rowvals[i]);
    1255 if( abscoef > maxabscapacitycoef )
    1256 maxabscapacitycoef = abscoef;
    1257
    1258 /* a variable which is not a flow variable can be used as capacity variable */
    1259 if( rowvals[i] > 0.0 )
    1260 nposcapacitycoefs++;
    1261 else
    1262 nnegcapacitycoefs++;
    1263
    1264 /* a continuous variable is considered to be not so nice*/
    1265 if( !SCIPvarIsIntegral(SCIPcolGetVar(rowcols[i])) )
    1266 nbadcoefs++;
    1267 }
    1268 }
    1269
    1270 /* check if this is a valid capacity constraint */
    1271 /* it has at least one flow variable */
    1272 if( rowsign != 0 && nposflowcoefs + nnegflowcoefs > 0 )
    1273 {
    1274 SCIP_Real commodityexcessratio;
    1275
    1276 capacityrowsigns[r] |= rowsign;
    1277 capacitycands[mcfdata->ncapacitycands] = r;
    1278 mcfdata->ncapacitycands++;
    1279
    1280 /* calculate capacity row score */
    1281 capacityrowscores[r] = 1.0;
    1282
    1283 /* calculate mean commodity excess: in the (un)directed case there should be exactly */
    1284 /* one (two) flow variable per commodity. in this case commodityexcessratio = 0 */
    1285 assert(ncoveredcommodities > 0);
    1286 /* coverity[divide_by_zero] */
    1287 commodityexcessratio =
    1288 ABS((nposflowcoefs + nnegflowcoefs)/(SCIP_Real)ncoveredcommodities - maxcolspercommoditylimit);
    1289
    1290 capacityrowscores[r] += 1000.0 * MAX(0.0, 2.0 - commodityexcessratio);
    1291
    1292 /* row has at most 'maxcolspercommoditylimit' columns per commodity: score +1000 */
    1293/* if( maxcolspercommodity[r] <= maxcolspercommoditylimit )
    1294 capacityrowscores[r] += 1000.0;*/
    1295
    1296 /* row is of type f - c*x <= b: score +1000 */
    1297 if( (capacityrowsigns[r] & RHSPOSSIBLE) != 0 && nnegflowcoefs == 0 && nposcapacitycoefs == 0 && nnegcapacitycoefs > 0 )
    1298 capacityrowscores[r] += 1000.0;
    1299 if( (capacityrowsigns[r] & LHSPOSSIBLE) != 0 && nposflowcoefs == 0 && nposcapacitycoefs > 0 && nnegcapacitycoefs == 0 )
    1300 capacityrowscores[r] += 1000.0;
    1301
    1302 /* row has no continuous variables that are not flow variables: score +1000 */
    1303/* if( nbadcoefs == 0 )
    1304 capacityrowscores[r] += 1000.0;*/
    1305
    1306 /* almost all commodities are covered: score +2000*ncoveredcommodities/(nactivecommodities+3)
    1307 * use slightly increased denominator in order to not increase score too much for very few commodities
    1308 */
    1309 assert(nactivecommodities + 3 > 0);
    1310 capacityrowscores[r] += 2000.0 * ncoveredcommodities/(SCIP_Real)(nactivecommodities + 3);
    1311
    1312 /* all coefficients of flow variables are +1 or all are -1: score +500 */
    1313 if( SCIPisEQ(scip, ABS(sameflowcoef), 1.0) )
    1314 capacityrowscores[r] += 500.0;
    1315
    1316 /* all coefficients of flow variables are equal: score +250 */
    1317 if( sameflowcoef != 0.0 && sameflowcoef != SCIP_REAL_MAX ) /*lint !e777*/
    1318 capacityrowscores[r] += 250.0;
    1319
    1320 /* all coefficients of flow variables are +1 or -1: score +100 */
    1321 if( SCIPisEQ(scip, sameabsflowcoef, 1.0) )
    1322 capacityrowscores[r] += 100.0;
    1323
    1324 /* there is at least one capacity variable with coefficient not equal to +/-1: score +100 */
    1325 if( maxabscapacitycoef > 0.0 && !SCIPisEQ(scip, maxabscapacitycoef, 1.0) )
    1326 capacityrowscores[r] += 100.0;
    1327
    1328 /* flow coefficients are mostly of the same sign: score +20*max(npos,nneg)/(npos+nneg) */
    1329 capacityrowscores[r] += 20.0 * MAX(nposflowcoefs, nnegflowcoefs)/MAX(1.0,(SCIP_Real)(nposflowcoefs + nnegflowcoefs));
    1330
    1331 /* capacity coefficients are mostly of the same sign: score +10*max(npos,nneg)/(npos+nneg+1) */
    1332 capacityrowscores[r] += 10.0 * MAX(nposcapacitycoefs, nnegcapacitycoefs)/(SCIP_Real)(nposcapacitycoefs+nnegcapacitycoefs+1.0);
    1333
    1334 /* row is a <= row with non-negative right hand side: score +10 */
    1335 if( (capacityrowsigns[r] & RHSPOSSIBLE) != 0 && !SCIPisNegative(scip, rowrhs) )
    1336 capacityrowscores[r] += 10.0;
    1337
    1338 /* row is an inequality: score +10 */
    1339 if( SCIPisInfinity(scip, -rowlhs) != SCIPisInfinity(scip, rowrhs) )
    1340 capacityrowscores[r] += 10.0;
    1341
    1342 assert(capacityrowscores[r] > 0.0);
    1343 SCIPdebugMsg(scip, "row <%s>: maxcolspercommodity=%d capacityrowsign=%d nposflowcoefs=%d nnegflowcoefs=%d nposcapacitycoefs=%d nnegcapacitycoefs=%d nbadcoefs=%d nactivecommodities=%d sameflowcoef=%g -> score=%g\n",
    1344 SCIProwGetName(row), maxcolspercommodity[r], capacityrowsigns[r], nposflowcoefs, nnegflowcoefs, nposcapacitycoefs, nnegcapacitycoefs, nbadcoefs, nactivecommodities, sameflowcoef, capacityrowscores[r]);
    1345
    1346 /* update maximum dual solution value for additional score tie breaking */
    1347 maxdualcapacity = MAX(maxdualcapacity, absdualsol);
    1348
    1349 /* if the model type should be detected automatically, count the number of directed and undirected capacity candidates */
    1350 if( modeltype == SCIP_MCFMODELTYPE_AUTO )
    1351 {
    1352 assert(maxcolspercommoditylimit == 2);
    1353 if( (capacityrowsigns[r] & UNDIRECTED) != 0 )
    1354 undirectedcandsscore += capacityrowscores[r];
    1355 else
    1356 directedcandsscore += capacityrowscores[r];
    1357 }
    1358 }
    1359 else
    1360 {
    1361 SCIPdebugMsg(scip, "row <%s>: rowsign = %d nposflowcoefs = %d nnegflowcoefs = %d -> discard\n",
    1362 SCIProwGetName(row), rowsign, nposflowcoefs, nnegflowcoefs);
    1363 }
    1364 }
    1365
    1366 /* if the model type should be detected automatically, decide it by a majority vote */
    1367 if( modeltype == SCIP_MCFMODELTYPE_AUTO )
    1368 {
    1369 if( directedcandsscore > undirectedcandsscore )
    1370 modeltype = SCIP_MCFMODELTYPE_DIRECTED;
    1371 else
    1372 modeltype = SCIP_MCFMODELTYPE_UNDIRECTED;
    1373
    1374 MCFdebugMessage("detected model type: %s (%g directed score, %g undirected score)\n",
    1375 modeltype == SCIP_MCFMODELTYPE_DIRECTED ? "directed" : "undirected", directedcandsscore, undirectedcandsscore);
    1376
    1377 if( modeltype == SCIP_MCFMODELTYPE_DIRECTED )
    1378 {
    1379 int i;
    1380
    1381 /* discard all undirected arcs */
    1382 for( i = 0; i < mcfdata->ncapacitycands; i++ )
    1383 {
    1384 r = capacitycands[i];
    1385 assert(0 <= r && r < nrows);
    1386 if( (capacityrowsigns[r] & UNDIRECTED) != 0 )
    1387 {
    1388 /* reduce the score of the undirected row in the directed model */
    1389 if( maxcolspercommodity[r] <= maxcolspercommoditylimit )
    1390 capacityrowscores[r] -= 1000.0;
    1391 }
    1392 }
    1393 }
    1394
    1395 /* record the detected model type */
    1396 mcfdata->modeltype = modeltype;
    1397 }
    1398
    1399 /* apply additional score tie breaking using the dual solutions */
    1400 if( SCIPisPositive(scip, maxdualcapacity) )
    1401 {
    1402 int i;
    1403
    1404 for( i = 0; i < mcfdata->ncapacitycands; i++ )
    1405 {
    1406 SCIP_Real dualsol;
    1407
    1408 r = capacitycands[i];
    1409 assert(0 <= r && r < nrows);
    1410 dualsol = SCIProwGetDualsol(rows[r]);
    1411 if( dualsol == SCIP_INVALID ) /*lint !e777*/
    1412 dualsol = 0.0;
    1413 else if( capacityrowsigns[r] == (LHSPOSSIBLE | RHSPOSSIBLE) )
    1414 dualsol = ABS(dualsol);
    1415 else if( capacityrowsigns[r] == RHSPOSSIBLE )
    1416 dualsol = -dualsol;
    1417 assert(maxdualcapacity > 0.0); /*for flexelint*/
    1418 capacityrowscores[r] += MAX(dualsol, 0.0)/maxdualcapacity;
    1419 assert(capacityrowscores[r] > 0.0);
    1420 }
    1421 }
    1422
    1423 /* sort candidates by score */
    1424 SCIPsortInd(mcfdata->capacitycands, compCands, (void*)capacityrowscores, mcfdata->ncapacitycands);
    1425
    1426 MCFdebugMessage("capacity candidates [%d]\n", mcfdata->ncapacitycands);
    1427#ifdef SCIP_DEBUG
    1428 for( r = 0; r < mcfdata->ncapacitycands; r++ )
    1429 {
    1430 SCIPdebugMsg(scip, "row %4d [score: %2g]: %s\n", mcfdata->capacitycands[r],
    1431 capacityrowscores[mcfdata->capacitycands[r]], SCIProwGetName(rows[mcfdata->capacitycands[r]]));
    1432 /*SCIPdebug( SCIP_CALL( SCIPprintRow(scip, rows[mcfdata->capacitycands[r]], NULL) ) );*/
    1433 }
    1434#endif
    1435
    1436 /* free temporary memory */
    1437 SCIPfreeBufferArray(scip, &maxcolspercommodity);
    1438 SCIPfreeBufferArray(scip, &ncolspercommodity);
    1439
    1440 return SCIP_OKAY;
    1441}
    1442
    1443/** creates a new commodity */
    1444static
    1446 SCIP* scip, /**< SCIP data structure */
    1447 MCFDATA* mcfdata /**< internal MCF extraction data to pass to subroutines */
    1448 )
    1449{
    1450 /* get memory for commoditysigns array */
    1451 assert(mcfdata->ncommodities <= mcfdata->commoditysignssize);
    1452 if( mcfdata->ncommodities == mcfdata->commoditysignssize )
    1453 {
    1454 mcfdata->commoditysignssize = MAX(2*mcfdata->commoditysignssize, mcfdata->ncommodities+1);
    1455 SCIP_CALL( SCIPreallocMemoryArray(scip, &mcfdata->commoditysigns, mcfdata->commoditysignssize) );
    1456 }
    1457 assert(mcfdata->ncommodities < mcfdata->commoditysignssize);
    1458
    1459 /* create commodity */
    1460 SCIPdebugMsg(scip, "**** creating new commodity %d ****\n", mcfdata->ncommodities);
    1461 mcfdata->commoditysigns[mcfdata->ncommodities] = 0;
    1462 mcfdata->ncommodities++;
    1463
    1464 return SCIP_OKAY;
    1465}
    1466
    1467/** creates a new arc */
    1468static
    1470 SCIP* scip, /**< SCIP data structure */
    1471 MCFDATA* mcfdata, /**< internal MCF extraction data to pass to subroutines */
    1472 int source, /**< source of new arc */
    1473 int target, /**< target of new arc */
    1474 int* newarcid /**< pointer to store id of new arc */
    1475 )
    1476{
    1477 assert(source != target );
    1478 assert(0 <= source && source < mcfdata->nnodes);
    1479 assert(0 <= target && target < mcfdata->nnodes);
    1480 assert(newarcid != NULL);
    1481
    1482 *newarcid = mcfdata->narcs;
    1483
    1484 /* get memory for arrays indexed by arcs */
    1485 assert(mcfdata->narcs <= mcfdata->arcarraysize);
    1486 if( mcfdata->narcs == mcfdata->arcarraysize )
    1487 {
    1488 mcfdata->arcarraysize = MAX(2*mcfdata->arcarraysize, mcfdata->narcs+1);
    1489 SCIP_CALL( SCIPreallocMemoryArray(scip, &mcfdata->arcsources, mcfdata->arcarraysize) );
    1490 SCIP_CALL( SCIPreallocMemoryArray(scip, &mcfdata->arctargets, mcfdata->arcarraysize) );
    1491 SCIP_CALL( SCIPreallocMemoryArray(scip, &mcfdata->nextinarcs, mcfdata->arcarraysize) );
    1492 SCIP_CALL( SCIPreallocMemoryArray(scip, &mcfdata->nextoutarcs, mcfdata->arcarraysize) );
    1493 }
    1494 assert(mcfdata->narcs < mcfdata->arcarraysize);
    1495
    1496 /* capacityrows is a special case since it is used earlier */
    1497 if( mcfdata->capacityrowssize < mcfdata->arcarraysize )
    1498 {
    1499 mcfdata->capacityrowssize = mcfdata->arcarraysize;
    1500 SCIP_CALL( SCIPreallocMemoryArray(scip, &mcfdata->capacityrows, mcfdata->capacityrowssize) );
    1501 }
    1502 assert(mcfdata->narcs < mcfdata->capacityrowssize);
    1503
    1504 /* create new arc */
    1505 SCIPdebugMsg(scip, "**** creating new arc %d: %d -> %d ****\n", mcfdata->narcs, source, target);
    1506
    1507 mcfdata->arcsources[*newarcid] = source;
    1508 mcfdata->arctargets[*newarcid] = target;
    1509 mcfdata->nextoutarcs[*newarcid] = mcfdata->firstoutarcs[source];
    1510 mcfdata->firstoutarcs[source] = *newarcid;
    1511 mcfdata->nextinarcs[*newarcid] = mcfdata->firstinarcs[target];
    1512 mcfdata->firstinarcs[target] = *newarcid;
    1513 mcfdata->capacityrows[*newarcid] = NULL;
    1514
    1515 mcfdata->narcs++;
    1516
    1517 return SCIP_OKAY;
    1518}
    1519
    1520/** adds the given flow row and all involved columns to the current commodity */
    1521static
    1523 SCIP* scip, /**< SCIP data structure */
    1524 MCFDATA* mcfdata, /**< internal MCF extraction data to pass to subroutines */
    1525 SCIP_ROW* row, /**< flow row to add to current commodity */
    1526 unsigned char rowsign, /**< possible flow row signs to use */
    1527 int* comcolids, /**< array of column indices of columns in commodity */
    1528 int* ncomcolids /**< pointer to number of columns in commodity */
    1529 )
    1530{
    1531 unsigned char* flowrowsigns = mcfdata->flowrowsigns;
    1532 SCIP_Bool* plusflow = mcfdata->plusflow;
    1533 SCIP_Bool* minusflow = mcfdata->minusflow;
    1534 int ncommodities = mcfdata->ncommodities;
    1535 int* commoditysigns = mcfdata->commoditysigns;
    1536 int* colcommodity = mcfdata->colcommodity;
    1537 int* rowcommodity = mcfdata->rowcommodity;
    1538 int* newcols = mcfdata->newcols;
    1539
    1540 SCIP_COL** rowcols;
    1541 SCIP_Real* rowvals;
    1542 int rowlen;
    1543 int rowscale;
    1544 SCIP_Bool invertrow;
    1545 int r;
    1546 int k;
    1547 int i;
    1548
    1549 assert(comcolids != NULL);
    1550 assert(ncomcolids != NULL);
    1551
    1552 k = ncommodities-1;
    1553 assert(k >= 0);
    1554
    1555 r = SCIProwGetLPPos(row);
    1556 assert(r >= 0);
    1557
    1558 /* check if row has to be inverted */
    1559 invertrow = ((rowsign & INVERTED) != 0);
    1560 rowsign &= ~INVERTED;
    1561
    1562 assert(rowcommodity[r] == -1);
    1563 assert((flowrowsigns[r] | rowsign) == flowrowsigns[r]);
    1564 assert((rowsign & (LHSPOSSIBLE | RHSPOSSIBLE)) == rowsign);
    1565 assert(rowsign != 0);
    1566
    1567 /* if the row is only usable as flow row in one direction, we cannot change the sign
    1568 * of the whole commodity anymore
    1569 */
    1570 if( (flowrowsigns[r] & (LHSPOSSIBLE | RHSPOSSIBLE)) != (LHSPOSSIBLE | RHSPOSSIBLE) )
    1571 commoditysigns[k] = +1; /* we cannot switch directions */
    1572
    1573 /* decide the sign (direction) of the row */
    1574 if( rowsign == LHSPOSSIBLE )
    1575 rowsign = LHSASSIGNED;
    1576 else if( rowsign == RHSPOSSIBLE )
    1577 rowsign = RHSASSIGNED;
    1578 else
    1579 {
    1580 SCIP_Real dualsol = SCIProwGetDualsol(row);
    1581
    1582 assert(rowsign == (LHSPOSSIBLE | RHSPOSSIBLE));
    1583
    1584 /* if we have a valid non-zero dual solution, choose the side which is tight */
    1585 if( !SCIPisZero(scip, dualsol) && dualsol != SCIP_INVALID ) /*lint !e777*/
    1586 {
    1587 if( dualsol > 0.0 )
    1588 rowsign = LHSASSIGNED;
    1589 else
    1590 rowsign = RHSASSIGNED;
    1591 }
    1592 else
    1593 {
    1594 SCIP_Real rowlhs = SCIProwGetLhs(row) - SCIProwGetConstant(row);
    1595 SCIP_Real rowrhs = SCIProwGetRhs(row) - SCIProwGetConstant(row);
    1596
    1597 /* choose row sign such that we get a*x <= -d with d non-negative */
    1598 if( rowrhs < 0.0 )
    1599 rowsign = RHSASSIGNED;
    1600 else if( rowlhs > 0.0 )
    1601 rowsign = LHSASSIGNED;
    1602 else
    1603 rowsign = RHSASSIGNED; /* if we are still undecided, choose rhs */
    1604 }
    1605 }
    1606 if( rowsign == RHSASSIGNED )
    1607 rowscale = +1;
    1608 else
    1609 rowscale = -1;
    1610
    1611 /* reintroduce inverted flag */
    1612 if( invertrow )
    1613 {
    1614 rowsign |= INVERTED;
    1615 rowscale *= -1;
    1616 }
    1617 flowrowsigns[r] |= rowsign;
    1618
    1619 SCIPdebugMsg(scip, "adding flow row %d <%s> with sign %+d%s to commodity %d [score:%g]\n",
    1620 r, SCIProwGetName(row), rowscale, (rowsign & INVERTED) != 0 ? " (inverted)" : "",
    1621 k, mcfdata->flowrowscores[r]);
    1622 /*SCIPdebug( SCIP_CALL( SCIPprintRow(scip, row, NULL) ) );*/
    1623
    1624 /* add row to commodity */
    1625 rowcommodity[r] = k;
    1626 rowcols = SCIProwGetCols(row);
    1627 rowvals = SCIProwGetVals(row);
    1628 rowlen = SCIProwGetNLPNonz(row);
    1629 for( i = 0; i < rowlen; i++ )
    1630 {
    1631 SCIP_Real val;
    1632 int c;
    1633
    1634 c = SCIPcolGetLPPos(rowcols[i]);
    1635 assert(0 <= c && c < SCIPgetNLPCols(scip));
    1636
    1637 /* assign column to commodity */
    1638 if( colcommodity[c] == -1 )
    1639 {
    1640 assert(!plusflow[c]);
    1641 assert(!minusflow[c]);
    1642 assert(mcfdata->nnewcols < SCIPgetNLPCols(scip));
    1643 colcommodity[c] = k;
    1644 newcols[mcfdata->nnewcols] = c;
    1645 mcfdata->nnewcols++;
    1646 comcolids[*ncomcolids] = c;
    1647 (*ncomcolids)++;
    1648 }
    1649 assert(colcommodity[c] == k);
    1650
    1651 /* update plusflow/minusflow */
    1652 val = rowscale * rowvals[i];
    1653 if( val > 0.0 )
    1654 {
    1655 assert(!plusflow[c]);
    1656 plusflow[c] = TRUE;
    1657 }
    1658 else
    1659 {
    1660 assert(!minusflow[c]);
    1661 minusflow[c] = TRUE;
    1662 }
    1663 }
    1664}
    1665
    1666/* inverts the lhs/rhs assignment of all rows in the given commodity */
    1667static
    1669 SCIP* scip, /**< SCIP data structure */
    1670 MCFDATA* mcfdata, /**< internal MCF extraction data to pass to subroutines */
    1671 int k, /**< commodity that the flow row should enter */
    1672 SCIP_ROW** comrows, /**< flow rows in commodity k */
    1673 int ncomrows, /**< number of flow rows (number of nodes) in commodity k */
    1674 int* comcolids, /**< column indices of columns in commodity k */
    1675 int ncomcolids /**< number of columns in commodity k */
    1676 )
    1677{
    1678 unsigned char* flowrowsigns = mcfdata->flowrowsigns;
    1679 SCIP_Bool* plusflow = mcfdata->plusflow;
    1680 SCIP_Bool* minusflow = mcfdata->minusflow;
    1681
    1682 int i;
    1683
    1684 assert(mcfdata->commoditysigns[k] == 0);
    1685 assert(comrows != NULL || ncomrows == 0);
    1686 assert(comcolids != NULL);
    1687
    1688 /* switch assignments of rows */
    1689 for( i = 0; i < ncomrows; i++ )
    1690 {
    1691 SCIP_ROW* row;
    1692 int r;
    1693 unsigned char rowsign;
    1694
    1695 assert(comrows != NULL);
    1696 row = comrows[i];
    1697 assert( row != NULL );
    1698 r = SCIProwGetLPPos(row);
    1699 assert(0 <= r && r < SCIPgetNLPRows(scip));
    1700 assert(mcfdata->rowcommodity[r] == k);
    1701 assert(!SCIPisInfinity(scip, -SCIProwGetLhs(row)));
    1702 assert(!SCIPisInfinity(scip, SCIProwGetRhs(row)));
    1703
    1704 rowsign = flowrowsigns[r];
    1705 assert((rowsign & (LHSASSIGNED | RHSASSIGNED)) != 0);
    1706 assert((rowsign & INVERTED) == 0);
    1707
    1708 flowrowsigns[r] &= ~(LHSASSIGNED | RHSASSIGNED);
    1709 if( (rowsign & LHSASSIGNED) != 0 )
    1710 flowrowsigns[r] |= RHSASSIGNED;
    1711 else
    1712 flowrowsigns[r] |= LHSASSIGNED;
    1713 }
    1714
    1715 /* switch plus/minusflow of columns of the given commodity */
    1716 for( i = 0; i < ncomcolids; i++ )
    1717 {
    1718 int c;
    1719 SCIP_Bool tmp;
    1720
    1721 c = comcolids[i];
    1722 assert(0 <= c && c < SCIPgetNLPCols(scip));
    1723 assert(mcfdata->colcommodity[c] == k);
    1724
    1725 tmp = plusflow[c];
    1726 plusflow[c] = minusflow[c];
    1727 minusflow[c] = tmp;
    1728 }
    1729}
    1730
    1731/** deletes a commodity and removes the flow rows again from the system */
    1732static
    1734 SCIP* scip, /**< SCIP data structure */
    1735 MCFDATA* mcfdata, /**< internal MCF extraction data to pass to subroutines */
    1736 int k, /**< commodity to delete */
    1737 SCIP_ROW** comrows, /**< flow rows of the commodity */
    1738 int nrows, /**< number of flow rows in the commodity */
    1739 int* ndelflowrows, /**< pointer to store number of flow rows in deleted commodity */
    1740 int* ndelflowvars /**< pointer to store number of flow vars in deleted commodity */
    1741 )
    1742{
    1743 unsigned char* flowrowsigns = mcfdata->flowrowsigns;
    1744 SCIP_Bool* plusflow = mcfdata->plusflow;
    1745 SCIP_Bool* minusflow = mcfdata->minusflow;
    1746 int ncommodities = mcfdata->ncommodities;
    1747 int* colcommodity = mcfdata->colcommodity;
    1748 int* rowcommodity = mcfdata->rowcommodity;
    1749
    1750 int n;
    1751
    1752 assert(0 <= k && k < ncommodities);
    1753
    1754 assert( ndelflowrows != NULL );
    1755 assert( ndelflowvars != NULL );
    1756
    1757 SCIPdebugMsg(scip, "deleting commodity %d (%d total commodities) with %d flow rows\n", k, ncommodities, nrows);
    1758
    1759 *ndelflowrows = 0;
    1760 *ndelflowvars = 0;
    1761
    1762 for( n = 0; n < nrows; n++ )
    1763 {
    1764 SCIP_ROW* row;
    1765 SCIP_COL** rowcols;
    1766 int rowlen;
    1767 int r;
    1768 int i;
    1769
    1770 row = comrows[n];
    1771 r = SCIProwGetLPPos(row);
    1772 assert(r >= 0);
    1773 assert(rowcommodity[r] == k);
    1774 assert((flowrowsigns[r] & (LHSASSIGNED | RHSASSIGNED)) != 0);
    1775
    1776 SCIPdebugMsg(scip, " -> removing row <%s> from commodity\n", SCIProwGetName(row));
    1777
    1778 /* remove the lhs/rhs assignment and the inverted flag */
    1779 flowrowsigns[r] &= ~(LHSASSIGNED | RHSASSIGNED | INVERTED);
    1780
    1781 /* remove row from commodity */
    1782 rowcommodity[r] = -1;
    1783 rowcols = SCIProwGetCols(row);
    1784 rowlen = SCIProwGetNLPNonz(row);
    1785 for( i = 0; i < rowlen; i++ )
    1786 {
    1787 int c;
    1788
    1789 c = SCIPcolGetLPPos(rowcols[i]);
    1790 assert(0 <= c && c < SCIPgetNLPCols(scip));
    1791
    1792 /* remove column from commodity */
    1793 assert(colcommodity[c] == k || colcommodity[c] == -1);
    1794 if(colcommodity[c] == k)
    1795 (*ndelflowvars)++;
    1796 colcommodity[c] = -1;
    1797
    1798 /* reset plusflow/minusflow */
    1799 plusflow[c] = FALSE;
    1800 minusflow[c] = FALSE;
    1801 }
    1802
    1803 (*ndelflowrows)++;
    1804 }
    1805
    1806 /* get rid of commodity if it is the last one; otherwise, just leave it
    1807 * as an empty commodity which will be discarded later
    1808 */
    1809 if( k == ncommodities-1 )
    1810 mcfdata->ncommodities--;
    1811 else
    1812 mcfdata->nemptycommodities++;
    1813}
    1814
    1815/** checks whether the given row fits into the given commodity and returns the possible flow row signs */
    1816static
    1818 SCIP* scip, /**< SCIP data structure */
    1819 MCFDATA* mcfdata, /**< internal MCF extraction data to pass to subroutines */
    1820 SCIP_ROW* row, /**< flow row to check */
    1821 int k, /**< commodity that the flow row should enter */
    1822 unsigned char* rowsign, /**< pointer to store the possible flow row signs */
    1823 SCIP_Bool* invertcommodity /**< pointer to store whether the commodity has to be inverted to accommodate the row */
    1824 )
    1825{
    1826 unsigned char* flowrowsigns = mcfdata->flowrowsigns;
    1827 SCIP_Bool* plusflow = mcfdata->plusflow;
    1828 SCIP_Bool* minusflow = mcfdata->minusflow;
    1829 int* colcommodity = mcfdata->colcommodity;
    1830 int* rowcommodity = mcfdata->rowcommodity;
    1831 int* commoditysigns = mcfdata->commoditysigns;
    1832
    1833 SCIP_COL** rowcols;
    1834 SCIP_Real* rowvals;
    1835 int rowlen;
    1836 unsigned char flowrowsign;
    1837 unsigned char invflowrowsign;
    1838 int r;
    1839 int j;
    1840
    1841 assert(invertcommodity != NULL);
    1842
    1843 *rowsign = 0;
    1844 *invertcommodity = FALSE;
    1845
    1846 r = SCIProwGetLPPos(row);
    1847 assert(0 <= r && r < SCIPgetNLPRows(scip));
    1848
    1849 /* ignore rows that are already used */
    1850 if( rowcommodity[r] != -1 )
    1851 return;
    1852
    1853 /* check if row is an available flow row */
    1854 flowrowsign = flowrowsigns[r];
    1855 assert((flowrowsign & (LHSPOSSIBLE | RHSPOSSIBLE | DISCARDED)) == flowrowsign);
    1856 if( (flowrowsign & DISCARDED) != 0 )
    1857 return;
    1858 if( (flowrowsign & (LHSPOSSIBLE | RHSPOSSIBLE)) == 0 )
    1859 return;
    1860 invflowrowsign = flowrowsign;
    1861
    1862 /* check whether the row fits w.r.t. the signs of the coefficients */
    1863 rowcols = SCIProwGetCols(row);
    1864 rowvals = SCIProwGetVals(row);
    1865 rowlen = SCIProwGetNLPNonz(row);
    1866 for( j = 0; j < rowlen && (flowrowsign != 0 || invflowrowsign != 0); j++ )
    1867 {
    1868 int rowc;
    1869
    1870 rowc = SCIPcolGetLPPos(rowcols[j]);
    1871 assert(0 <= rowc && rowc < SCIPgetNLPCols(scip));
    1872
    1873 /* check if column already belongs to the same commodity */
    1874 if( colcommodity[rowc] == k )
    1875 {
    1876 /* column only fits if it is not yet present with the same sign */
    1877 if( plusflow[rowc] )
    1878 {
    1879 /* column must not be included with positive sign */
    1880 if( rowvals[j] > 0.0 )
    1881 {
    1882 flowrowsign &= ~RHSPOSSIBLE;
    1883 invflowrowsign &= ~LHSPOSSIBLE;
    1884 }
    1885 else
    1886 {
    1887 flowrowsign &= ~LHSPOSSIBLE;
    1888 invflowrowsign &= ~RHSPOSSIBLE;
    1889 }
    1890 }
    1891 if( minusflow[rowc] )
    1892 {
    1893 /* column must not be included with negative sign */
    1894 if( rowvals[j] > 0.0 )
    1895 {
    1896 flowrowsign &= ~LHSPOSSIBLE;
    1897 invflowrowsign &= ~RHSPOSSIBLE;
    1898 }
    1899 else
    1900 {
    1901 flowrowsign &= ~RHSPOSSIBLE;
    1902 invflowrowsign &= ~LHSPOSSIBLE;
    1903 }
    1904 }
    1905 }
    1906 else if( colcommodity[rowc] != -1 )
    1907 {
    1908 /* column does not fit if it already belongs to a different commodity */
    1909 flowrowsign = 0;
    1910 invflowrowsign = 0;
    1911 }
    1912 }
    1913
    1914 if( flowrowsign != 0 )
    1915 {
    1916 /* flow row fits without inverting anything */
    1917 *rowsign = flowrowsign;
    1918 *invertcommodity = FALSE;
    1919 }
    1920 else if( invflowrowsign != 0 )
    1921 {
    1922 /* this must be an inequality */
    1923 assert((flowrowsigns[r] & (LHSPOSSIBLE | RHSPOSSIBLE)) != (LHSPOSSIBLE | RHSPOSSIBLE));
    1924
    1925 /* flow row fits only if row or commodity is inverted */
    1926 if( commoditysigns == NULL || commoditysigns[k] == 0 )
    1927 {
    1928 /* commodity can be inverted */
    1929 *rowsign = invflowrowsign;
    1930 *invertcommodity = TRUE;
    1931 }
    1932 else
    1933 {
    1934 /* row has to be inverted */
    1935 *rowsign = (invflowrowsign | INVERTED);
    1936 *invertcommodity = FALSE;
    1937 }
    1938 }
    1939 else
    1940 {
    1941 /* we can discard the row, since it can also not be member of a different commodity */
    1942 SCIPdebugMsg(scip, " -> discard flow row %d <%s>, comoditysign=%d\n", r, SCIProwGetName(row), commoditysigns[k]);
    1943 flowrowsigns[r] |= DISCARDED;
    1944 }
    1945}
    1946
    1947/** returns a flow conservation row that fits into the current commodity, or NULL */
    1948static
    1950 SCIP* scip, /**< SCIP data structure */
    1951 MCFDATA* mcfdata, /**< internal MCF extraction data to pass to subroutines */
    1952 SCIP_ROW** nextrow, /**< pointer to store next row */
    1953 unsigned char* nextrowsign, /**< pointer to store possible signs of next row */
    1954 SCIP_Bool* nextinvertcommodity /**< pointer to store whether current commodity has to be inverted to accommodate the next row */
    1955 )
    1956{
    1957 SCIP_Real* flowrowscores = mcfdata->flowrowscores;
    1958 SCIP_Bool* plusflow = mcfdata->plusflow;
    1959 SCIP_Bool* minusflow = mcfdata->minusflow;
    1960 int* newcols = mcfdata->newcols;
    1961 int ncommodities = mcfdata->ncommodities;
    1962
    1963 SCIP_COL** cols;
    1964 int k;
    1965
    1966 assert(nextrow != NULL);
    1967 assert(nextrowsign != NULL);
    1968
    1969 *nextrow = NULL;
    1970 *nextrowsign = 0;
    1971 *nextinvertcommodity = FALSE;
    1972
    1973 k = ncommodities-1;
    1974
    1975 cols = SCIPgetLPCols(scip);
    1976 assert(cols != NULL);
    1977
    1978 /* check if there are any columns left in the commodity that have not yet been inspected for incident flow rows */
    1979 while( mcfdata->nnewcols > 0 )
    1980 {
    1981 SCIP_COL* col;
    1982 SCIP_ROW** colrows;
    1983 int collen;
    1984 SCIP_ROW* bestrow;
    1985 unsigned char bestrowsign;
    1986 SCIP_Bool bestinvertcommodity;
    1987 SCIP_Real bestscore;
    1988 int c;
    1989 int i;
    1990
    1991 /* pop next new column from stack */
    1992 c = newcols[mcfdata->nnewcols-1];
    1993 mcfdata->nnewcols--;
    1994 assert(0 <= c && c < SCIPgetNLPCols(scip));
    1995
    1996 /* check if this columns already as both signs */
    1997 assert(plusflow[c] || minusflow[c]);
    1998 if( plusflow[c] && minusflow[c] )
    1999 continue;
    2000
    2001 /* check whether column is incident to a valid flow row that fits into the current commodity */
    2002 bestrow = NULL;
    2003 bestrowsign = 0;
    2004 bestinvertcommodity = FALSE;
    2005 bestscore = 0.0;
    2006 col = cols[c];
    2007 colrows = SCIPcolGetRows(col);
    2008 collen = SCIPcolGetNLPNonz(col);
    2009 for( i = 0; i < collen; i++ )
    2010 {
    2011 SCIP_ROW* row;
    2012 unsigned char flowrowsign;
    2013 SCIP_Bool invertcommodity;
    2014
    2015 row = colrows[i];
    2016
    2017 /* check if row fits into the current commodity */
    2018 getFlowrowFit(scip, mcfdata, row, k, &flowrowsign, &invertcommodity);
    2019
    2020 /* do we have a winner? */
    2021 if( flowrowsign != 0 )
    2022 {
    2023 int r;
    2024 SCIP_Real score;
    2025
    2026 r = SCIProwGetLPPos(row);
    2027 assert(0 <= r && r < SCIPgetNLPRows(scip));
    2028 score = flowrowscores[r];
    2029 assert(score > 0.0);
    2030
    2031 /* If we have to invert the row, this will lead to a negative slack variable in the MIR cut,
    2032 * which needs to be substituted in the end. We like to avoid this and therefore reduce the
    2033 * score.
    2034 */
    2035 if( (flowrowsign & INVERTED) != 0 )
    2036 score *= 0.75;
    2037
    2038 if( score > bestscore )
    2039 {
    2040 bestrow = row;
    2041 bestrowsign = flowrowsign;
    2042 bestinvertcommodity = invertcommodity;
    2043 bestscore = score;
    2044 }
    2045 }
    2046 }
    2047
    2048 /* if there was a valid row for this column, pick the best one
    2049 * Note: This is not the overall best row, only the one for the first column that has a valid row.
    2050 * However, picking the overall best row seems to be too expensive
    2051 */
    2052 if( bestrow != NULL )
    2053 {
    2054 assert(bestscore > 0.0);
    2055 assert(bestrowsign != 0);
    2056 *nextrow = bestrow;
    2057 *nextrowsign = bestrowsign;
    2058 *nextinvertcommodity = bestinvertcommodity;
    2059 break;
    2060 }
    2061 }
    2062}
    2063
    2064/** extracts flow conservation rows and puts them into commodities */
    2065static
    2067 SCIP* scip, /**< SCIP data structure */
    2068 MCFDATA* mcfdata, /**< internal MCF extraction data to pass to subroutines */
    2069 SCIP_Real maxflowvarflowrowratio, /**< maximum ratio of flowvars and flowrows */
    2070 SCIP_Bool* failed /**< pointer to store whether flowrowflowvarratio exceeded */
    2071 )
    2072{
    2073 int* flowcands = mcfdata->flowcands;
    2074
    2075 SCIP_Bool* plusflow;
    2076 SCIP_Bool* minusflow;
    2077 int* colcommodity;
    2078 int* rowcommodity;
    2079
    2080 SCIP_ROW** comrows;
    2081 int* ncomnodes;
    2082 int* comcolids;
    2083 int ncomcolids;
    2084 SCIP_ROW** rows;
    2085 int nrows;
    2086 int ncols;
    2087 int maxnnodes;
    2088 int nflowrows;
    2089 int nflowvars;
    2090 int i;
    2091 int c;
    2092 int r;
    2093 int k;
    2094
    2095 /* get LP data */
    2096 rows = SCIPgetLPRows(scip);
    2097 nrows = SCIPgetNLPRows(scip);
    2098 ncols = SCIPgetNLPCols(scip);
    2099
    2100 assert(failed != NULL);
    2101 assert(!*failed);
    2102
    2103 /* allocate memory */
    2104 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->plusflow, ncols) );
    2105 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->minusflow, ncols) );
    2106 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->colcommodity, ncols) );
    2107 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->rowcommodity, nrows) );
    2108 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->newcols, ncols) );
    2109 plusflow = mcfdata->plusflow;
    2110 minusflow = mcfdata->minusflow;
    2111 colcommodity = mcfdata->colcommodity;
    2112 rowcommodity = mcfdata->rowcommodity;
    2113
    2114 /* allocate temporary memory */
    2115 SCIP_CALL( SCIPallocBufferArray(scip, &comrows, nrows) );
    2116 SCIP_CALL( SCIPallocBufferArray(scip, &ncomnodes, nrows) );
    2117 SCIP_CALL( SCIPallocBufferArray(scip, &comcolids, ncols) );
    2118
    2119 /* 3. Extract network structure of flow conservation constraints:
    2120 * (a) Initialize plusflow[c] = minusflow[c] = FALSE for all columns c and other local data.
    2121 */
    2122 BMSclearMemoryArray(plusflow, ncols);
    2123 BMSclearMemoryArray(minusflow, ncols);
    2124 for( c = 0; c < ncols; c++ )
    2125 colcommodity[c] = -1;
    2126 for( r = 0; r < nrows; r++ )
    2127 rowcommodity[r] = -1;
    2128
    2129 assert(flowcands != NULL || mcfdata->nflowcands == 0);
    2130
    2131 /* (b) As long as there are flow conservation candidates left:
    2132 * (i) Create new commodity and use first flow conservation constraint as new row.
    2133 * (ii) Add new row to commodity, update pluscom/minuscom accordingly.
    2134 * (iii) For the newly added columns search for an incident flow conservation constraint. Pick the one of highest ranking.
    2135 * (iv) If found, set new row to this row and goto (ii).
    2136 */
    2137 maxnnodes = 0;
    2138 nflowrows = 0;
    2139 nflowvars = 0;
    2140 for( i = 0; i < mcfdata->nflowcands; i++ )
    2141 {
    2142 SCIP_ROW* newrow;
    2143 unsigned char newrowsign;
    2144 SCIP_Bool newinvertcommodity;
    2145 int nnodes;
    2146
    2147 assert(flowcands != NULL);
    2148 r = flowcands[i];
    2149 assert(0 <= r && r < nrows);
    2150 newrow = rows[r];
    2151
    2152 /* check if row fits into a new commodity */
    2153 getFlowrowFit(scip, mcfdata, newrow, mcfdata->ncommodities, &newrowsign, &newinvertcommodity);
    2154 if( newrowsign == 0 )
    2155 continue;
    2156 assert(!newinvertcommodity);
    2157 assert((newrowsign & INVERTED) == 0);
    2158
    2159 /* start new commodity */
    2160 SCIPdebugMsg(scip, " -------------------start new commodity %d--------------------- \n", mcfdata->ncommodities );
    2161 SCIP_CALL( createNewCommodity(scip, mcfdata) );
    2162 nnodes = 0;
    2163 ncomcolids = 0;
    2164
    2165 /* fill commodity with flow conservation constraints */
    2166 do
    2167 {
    2168 /* if next flow row demands an inverting of the commodity, do it now */
    2169 if( newinvertcommodity )
    2170 invertCommodity(scip, mcfdata, mcfdata->ncommodities-1, comrows, nnodes, comcolids, ncomcolids);
    2171
    2172 /* add new row to commodity */
    2173 SCIPdebugMsg(scip, " -> add flow row <%s> \n", SCIProwGetName(newrow));
    2174 addFlowrowToCommodity(scip, mcfdata, newrow, newrowsign, comcolids, &ncomcolids);
    2175 comrows[nnodes] = newrow;
    2176 nnodes++;
    2177 nflowrows++;
    2178
    2179 /* get next row to add */
    2180 getNextFlowrow(scip, mcfdata, &newrow, &newrowsign, &newinvertcommodity);
    2181 }
    2182 while( newrow != NULL );
    2183
    2184 ncomnodes[mcfdata->ncommodities-1] = nnodes;
    2185 maxnnodes = MAX(maxnnodes, nnodes);
    2186 nflowvars += ncomcolids;
    2187 SCIPdebugMsg(scip, " -> finished commodity %d: identified %d nodes, maxnnodes=%d\n", mcfdata->ncommodities-1, nnodes, maxnnodes);
    2188
    2189 /* if the commodity has too few nodes, or if it has much fewer nodes than the largest commodity, discard it */
    2190 if( nnodes < MINNODES || nnodes < MINCOMNODESFRACTION * maxnnodes )
    2191 {
    2192 int ndelflowrows;
    2193 int ndelflowvars;
    2194
    2195 deleteCommodity(scip, mcfdata, mcfdata->ncommodities-1, comrows, nnodes, &ndelflowrows, &ndelflowvars);
    2196 nflowrows -= ndelflowrows;
    2197 nflowvars -= ndelflowvars;
    2198 assert(nflowvars >= 0);
    2199 assert(nflowrows >= 0);
    2200 }
    2201 }
    2202 /* final cleanup of small commodities */
    2203 for( k = 0; k < mcfdata->ncommodities; k++ )
    2204 {
    2205 assert(ncomnodes[k] >= MINNODES);
    2206
    2207 /* if the commodity has much fewer nodes than the largest commodity, discard it */
    2208 if( ncomnodes[k] < MINCOMNODESFRACTION * maxnnodes )
    2209 {
    2210 int nnodes;
    2211 int ndelflowrows;
    2212 int ndelflowvars;
    2213
    2214 nnodes = 0;
    2215 for( i = 0; i < mcfdata->nflowcands; i++ )
    2216 {
    2217 assert(flowcands != NULL);
    2218 r = flowcands[i];
    2219 if( rowcommodity[r] == k )
    2220 {
    2221 comrows[nnodes] = rows[r];
    2222 nnodes++;
    2223#ifdef NDEBUG
    2224 if( nnodes == ncomnodes[k] )
    2225 break;
    2226#endif
    2227 }
    2228 }
    2229 assert(nnodes == ncomnodes[k]);
    2230 deleteCommodity(scip, mcfdata, k, comrows, nnodes, &ndelflowrows, &ndelflowvars);
    2231 nflowrows -= ndelflowrows;
    2232 nflowvars -= ndelflowvars;
    2233 assert(nflowvars >= 0);
    2234 assert(nflowrows >= 0);
    2235 }
    2236 }
    2237
    2238 /* free temporary memory */
    2239 SCIPfreeBufferArray(scip, &comcolids);
    2240 SCIPfreeBufferArray(scip, &ncomnodes);
    2241 SCIPfreeBufferArray(scip, &comrows);
    2242
    2243 MCFdebugMessage("identified %d commodities (%d empty) with a maximum of %d nodes and %d flowrows, %d flowvars \n",
    2244 mcfdata->ncommodities, mcfdata->nemptycommodities, maxnnodes, nflowrows, nflowvars);
    2245
    2246 assert(nflowvars >= 0);
    2247 assert(nflowrows >= 0);
    2248
    2249 /* do not allow flow system exceeding the flowvarflowrowratio (average node degree)*/
    2250 if( nflowrows == 0)
    2251 *failed = TRUE;
    2252 else if( (SCIP_Real)nflowvars / (SCIP_Real)nflowrows > maxflowvarflowrowratio )
    2253 *failed = TRUE;
    2254
    2255 return SCIP_OKAY;
    2256}
    2257
    2258/** Arc-Detection -- identifies capacity constraints for the arcs and assigns arc ids to columns and capacity constraints */
    2259static
    2261 SCIP* scip, /**< SCIP data structure */
    2262 MCFDATA* mcfdata /**< internal MCF extraction data to pass to subroutines */
    2263 )
    2264{
    2265 unsigned char* capacityrowsigns = mcfdata->capacityrowsigns;
    2266 int* colcommodity = mcfdata->colcommodity;
    2267#ifndef NDEBUG
    2268 unsigned char* flowrowsigns = mcfdata->capacityrowsigns;
    2269 int* rowcommodity = mcfdata->rowcommodity;
    2270#endif
    2271
    2272 int* colarcid;
    2273 int* rowarcid;
    2274
    2275 SCIP_ROW** rows;
    2276 SCIP_COL** cols;
    2277 int nrows;
    2278 int ncols;
    2279
    2280 int r;
    2281 int c;
    2282 int i;
    2283
    2284#ifndef NDEBUG
    2285 SCIP_Real* capacityrowscores = mcfdata->capacityrowscores;
    2286#endif
    2287 int *capacitycands = mcfdata->capacitycands;
    2288 int ncapacitycands = mcfdata->ncapacitycands;
    2289
    2290 assert(mcfdata->narcs == 0);
    2291 assert(capacitycands != NULL || ncapacitycands == 0);
    2292
    2293 /* get LP data */
    2294 SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) );
    2295 SCIP_CALL( SCIPgetLPColsData(scip, &cols, &ncols) );
    2296
    2297 /* allocate temporary memory for extraction data */
    2298 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->colarcid, ncols) );
    2299 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->rowarcid, nrows) );
    2300 colarcid = mcfdata->colarcid;
    2301 rowarcid = mcfdata->rowarcid;
    2302
    2303 /* initialize arcid arrays */
    2304 for( c = 0; c < ncols; c++ )
    2305 colarcid[c] = -1;
    2306 for( r = 0; r < nrows; r++ )
    2307 rowarcid[r] = -1;
    2308
    2309 /* -> loop through the list of capacity cands in non-increasing score order */
    2310 for( i = 0; i < ncapacitycands; i++ )
    2311 {
    2312 SCIP_ROW* capacityrow;
    2313 SCIP_COL** rowcols;
    2314 int rowlen;
    2315 int nassignedflowvars;
    2316 int nunassignedflowvars;
    2317 int k;
    2318
    2319 assert(capacitycands != NULL);
    2320 r = capacitycands[i];
    2321 assert(0 <= r && r < nrows );
    2322 capacityrow = rows[r];
    2323
    2324 assert(capacityrowsigns != NULL); /* for lint */
    2325 assert(capacityrowscores != NULL);
    2326 assert(rowcommodity != NULL);
    2327 assert(flowrowsigns != NULL);
    2328
    2329 /* row must be a capacity candidate */
    2330 assert((capacityrowsigns[r] & (LHSPOSSIBLE | RHSPOSSIBLE)) != 0);
    2331 assert((capacityrowsigns[r] & DISCARDED) == 0);
    2332 assert(capacityrowscores[r] > 0.0);
    2333
    2334 /* row must not be already assigned */
    2335 assert((capacityrowsigns[r] & (LHSASSIGNED | RHSASSIGNED)) == 0);
    2336 assert(rowarcid[r] == -1);
    2337
    2338 /* row should not be a flow conservation constraint */
    2339 assert( rowcommodity[r] == -1 );
    2340 assert( (flowrowsigns[r] & (LHSASSIGNED | RHSASSIGNED)) == 0 );
    2341
    2342 /* count the number of already assigned and not yet assigned flow variables */
    2343 rowcols = SCIProwGetCols(capacityrow);
    2344 rowlen = SCIProwGetNLPNonz(capacityrow);
    2345 nassignedflowvars = 0;
    2346 nunassignedflowvars = 0;
    2347 for( k = 0; k < rowlen; k++ )
    2348 {
    2349 c = SCIPcolGetLPPos(rowcols[k]);
    2350 assert(0 <= c && c < ncols);
    2351 assert(colcommodity != NULL); /* for lint */
    2352
    2353 assert(-1 <= colcommodity[c] && colcommodity[c] < mcfdata->ncommodities);
    2354 assert(-1 <= colarcid[c] && colarcid[c] < mcfdata->narcs);
    2355
    2356 /* ignore columns that are not flow variables */
    2357 if( colcommodity[c] == -1 )
    2358 continue;
    2359
    2360 /* check if column is already assigned to an arc */
    2361 if( colarcid[c] >= 0 )
    2362 nassignedflowvars++;
    2363 else
    2364 nunassignedflowvars++;
    2365 }
    2366
    2367 /* Ignore row if all of its flow variables have already been assigned to some other arc.
    2368 * Only accept the row as capacity constraint if at least 1/3 of its flow vars are
    2369 * not yet assigned to some other arc.
    2370 */
    2371 if( nunassignedflowvars == 0 || nassignedflowvars >= nunassignedflowvars * 2 )
    2372 {
    2373 SCIPdebugMsg(scip, "discarding capacity candidate row %d <%s> [score:%g]: %d assigned flowvars, %d unassigned flowvars\n",
    2374 r, SCIProwGetName(capacityrow), mcfdata->capacityrowscores[r], nassignedflowvars, nunassignedflowvars);
    2375 capacityrowsigns[r] |= DISCARDED;
    2376 continue;
    2377 }
    2378
    2379 /* create new arc -- store capacity row */
    2380 assert(mcfdata->narcs <= mcfdata->capacityrowssize);
    2381 if( mcfdata->narcs == mcfdata->capacityrowssize )
    2382 {
    2383 mcfdata->capacityrowssize = MAX(2*mcfdata->capacityrowssize, mcfdata->narcs+1);
    2384 SCIP_CALL( SCIPreallocMemoryArray(scip, &mcfdata->capacityrows, mcfdata->capacityrowssize) );
    2385 }
    2386 assert(mcfdata->narcs < mcfdata->capacityrowssize);
    2387 assert(mcfdata->narcs < nrows);
    2388
    2389 mcfdata->capacityrows[mcfdata->narcs] = capacityrow;
    2390
    2391 /* assign the capacity row to a new arc id */
    2392 assert(0 <= r && r < nrows);
    2393 rowarcid[r] = mcfdata->narcs;
    2394
    2395 /* decide which sign to use */
    2396 if( (capacityrowsigns[r] & RHSPOSSIBLE) != 0 )
    2397 capacityrowsigns[r] |= RHSASSIGNED;
    2398 else
    2399 {
    2400 assert((capacityrowsigns[r] & LHSPOSSIBLE) != 0);
    2401 capacityrowsigns[r] |= LHSASSIGNED;
    2402 }
    2403
    2404 SCIPdebugMsg(scip, "assigning capacity row %d <%s> with sign %+d to arc %d [score:%g]: %d assigned flowvars, %d unassigned flowvars\n",
    2405 r, SCIProwGetName(capacityrow), (capacityrowsigns[r] & RHSASSIGNED) != 0 ? +1 : -1, mcfdata->narcs,
    2406 mcfdata->capacityrowscores[r], nassignedflowvars, nunassignedflowvars);
    2407
    2408 /* assign all involved flow variables to the new arc id */
    2409 for( k = 0; k < rowlen; k++ )
    2410 {
    2411 int rowc = SCIPcolGetLPPos(rowcols[k]);
    2412 assert(0 <= rowc && rowc < ncols);
    2413 assert(colcommodity != NULL); /* for lint */
    2414
    2415 /* due to aggregations in preprocessing it may happen that a flow variable appears in multiple capacity constraints;
    2416 * in this case, assign it to the first that has been found
    2417 */
    2418 if( colcommodity[rowc] >= 0 && colarcid[rowc] == -1 )
    2419 colarcid[rowc] = mcfdata->narcs;
    2420 }
    2421
    2422 /* increase number of arcs */
    2423 mcfdata->narcs++;
    2424 }
    2425 return SCIP_OKAY;
    2426} /* END extractcapacities */
    2427
    2428
    2429/** collects all flow columns of all commodities (except the one of the base row) that are incident to the node described by the given flow row */
    2430static
    2432 SCIP* scip, /**< SCIP data structure */
    2433 MCFDATA* mcfdata, /**< internal MCF extraction data to pass to subroutines */
    2434 SCIP_ROW* flowrow, /**< flow conservation constraint that defines the node */
    2435 int basecommodity /**< commodity of the base row */
    2436 )
    2437{
    2438 int* colcommodity = mcfdata->colcommodity;
    2439 int* colarcid = mcfdata->colarcid;
    2440 int* newcols = mcfdata->newcols;
    2441 SCIP_ROW** capacityrows = mcfdata->capacityrows;
    2442 SCIP_Bool* colisincident = mcfdata->colisincident;
    2443
    2444 SCIP_COL** rowcols;
    2445 int rowlen;
    2446 int i;
    2447
    2448#ifndef NDEBUG
    2449 /* check that the marker array is correctly initialized */
    2450 for( i = 0; i < SCIPgetNLPCols(scip); i++ )
    2451 assert(!colisincident[i]);
    2452#endif
    2453
    2454 /* loop through all flow columns in the flow conservation constraint */
    2455 rowcols = SCIProwGetCols(flowrow);
    2456 rowlen = SCIProwGetNLPNonz(flowrow);
    2457 mcfdata->nnewcols = 0;
    2458
    2459 for( i = 0; i < rowlen; i++ )
    2460 {
    2461 SCIP_COL** capacityrowcols;
    2462 int capacityrowlen;
    2463 int arcid;
    2464 int c;
    2465 int j;
    2466
    2467 c = SCIPcolGetLPPos(rowcols[i]);
    2468 assert(0 <= c && c < SCIPgetNLPCols(scip));
    2469
    2470 /* get arc id of the column in the flow conservation constraint */
    2471 arcid = colarcid[c];
    2472 if( arcid == -1 )
    2473 continue;
    2474 assert(arcid < mcfdata->narcs);
    2475
    2476 /* collect flow variables in the capacity constraint of this arc */
    2477 assert(capacityrows[arcid] != NULL);
    2478 capacityrowcols = SCIProwGetCols(capacityrows[arcid]);
    2479 capacityrowlen = SCIProwGetNLPNonz(capacityrows[arcid]);
    2480
    2481 for( j = 0; j < capacityrowlen; j++ )
    2482 {
    2483 int caprowc;
    2484
    2485 caprowc = SCIPcolGetLPPos(capacityrowcols[j]);
    2486 assert(0 <= caprowc && caprowc < SCIPgetNLPCols(scip));
    2487
    2488 /* ignore columns that do not belong to a commodity, i.e., are not flow variables */
    2489 if( colcommodity[caprowc] == -1 )
    2490 {
    2491 assert(colarcid[caprowc] == -1);
    2492 continue;
    2493 }
    2494 assert(colarcid[caprowc] <= arcid); /* colarcid < arcid if column belongs to multiple arcs, for example, due to an aggregation in presolving */
    2495
    2496 /* ignore columns in the same commodity as the base row */
    2497 if( colcommodity[caprowc] == basecommodity )
    2498 continue;
    2499
    2500 /* if not already done, collect the column */
    2501 if( !colisincident[caprowc] )
    2502 {
    2503 assert(mcfdata->nnewcols < SCIPgetNLPCols(scip));
    2504 colisincident[caprowc] = TRUE;
    2505 newcols[mcfdata->nnewcols] = caprowc;
    2506 mcfdata->nnewcols++;
    2507 }
    2508 }
    2509 }
    2510}
    2511
    2512/** compares given row against a base node flow row and calculates a similarity score;
    2513 * score is 0.0 if the rows are incompatible
    2514 */
    2515static
    2517 SCIP* scip, /**< SCIP data structure */
    2518 MCFDATA* mcfdata, /**< internal MCF extraction data to pass to subroutines */
    2519 int baserowlen, /**< length of base node flow row */
    2520 int* basearcpattern, /**< arc pattern of base node flow row */
    2521 int basenposuncap, /**< number of uncapacitated vars in base node flow row with positive coeff*/
    2522 int basenneguncap, /**< number of uncapacitated vars in base node flow row with negative coeff*/
    2523 SCIP_ROW* row, /**< row to compare against base node flow row */
    2524 SCIP_Real* score, /**< pointer to store the similarity score */
    2525 SCIP_Bool* invertcommodity /**< pointer to store whether the arcs in the commodity of the row have
    2526 * to be inverted for the row to be compatible to the base row */
    2527 )
    2528{
    2529 unsigned char* flowrowsigns = mcfdata->flowrowsigns;
    2530 int* commoditysigns = mcfdata->commoditysigns;
    2531 int narcs = mcfdata->narcs;
    2532 int* rowcommodity = mcfdata->rowcommodity;
    2533 int* colarcid = mcfdata->colarcid;
    2534 int* arcpattern = mcfdata->zeroarcarray;
    2535 SCIP_MCFMODELTYPE modeltype = mcfdata->modeltype;
    2536
    2537 SCIP_COL** rowcols;
    2538 SCIP_Real* rowvals;
    2539 int nposuncap;
    2540 int nneguncap;
    2541 int ncols;
    2542 int rowlen;
    2543 int rowcom;
    2544 int rowcomsign;
    2545 SCIP_Bool incompatible;
    2546 SCIP_Real overlap;
    2547 int* overlappingarcs;
    2548 int noverlappingarcs;
    2549 int r;
    2550 int i;
    2551
    2552 *score = 0.0;
    2553 *invertcommodity = FALSE;
    2554
    2555#ifndef NDEBUG
    2556 for( i = 0; i < narcs; i++ )
    2557 assert(arcpattern[i] == 0);
    2558#endif
    2559
    2560 /* allocate temporary memory */
    2561 SCIP_CALL( SCIPallocBufferArray(scip, &overlappingarcs, narcs) );
    2562
    2563 r = SCIProwGetLPPos(row);
    2564 assert(0 <= r && r < SCIPgetNLPRows(scip));
    2565 assert((flowrowsigns[r] & (LHSASSIGNED | RHSASSIGNED)) != 0);
    2566 rowcom = rowcommodity[r];
    2567 assert(0 <= rowcom && rowcom < mcfdata->ncommodities);
    2568 rowcomsign = commoditysigns[rowcom];
    2569 assert(-1 <= rowcomsign && rowcomsign <= +1);
    2570
    2571 rowcols = SCIProwGetCols(row);
    2572 rowvals = SCIProwGetVals(row);
    2573 rowlen = SCIProwGetNLPNonz(row);
    2574 incompatible = FALSE;
    2575 noverlappingarcs = 0;
    2576 nposuncap=0;
    2577 nneguncap=0;
    2578 ncols = SCIPgetNLPCols(scip);
    2579 for( i = 0; i < rowlen; i++ )
    2580 {
    2581 int c;
    2582 int arcid;
    2583 int valsign;
    2584
    2585 c = SCIPcolGetLPPos(rowcols[i]);
    2586 assert(0 <= c && c < SCIPgetNLPCols(scip));
    2587
    2588 /* get the sign of the coefficient in the flow conservation constraint */
    2589 valsign = (rowvals[i] > 0.0 ? +1 : -1);
    2590 if( (flowrowsigns[r] & LHSASSIGNED) != 0 )
    2591 valsign *= -1;
    2592 if( (flowrowsigns[r] & INVERTED) != 0 )
    2593 valsign *= -1;
    2594
    2595 arcid = colarcid[c];
    2596 if( arcid == -1 )
    2597 {
    2598 if( valsign > 0 )
    2599 nposuncap++;
    2600 else
    2601 nneguncap++;
    2602 continue;
    2603 }
    2604 assert(arcid < narcs);
    2605
    2606 /* check if this arc is also member of the base row */
    2607 if( basearcpattern[arcid] != 0 )
    2608 {
    2609 /* check if the sign of the arc matches in the directed case */
    2610 if( modeltype == SCIP_MCFMODELTYPE_DIRECTED )
    2611 {
    2612 int validcomsign;
    2613
    2614 if( ( valsign * basearcpattern[arcid] ) > 0 )
    2615 validcomsign = +1;
    2616 else
    2617 validcomsign = -1;
    2618
    2619 if( rowcomsign == 0 )
    2620 {
    2621 /* the first entry decides whether we have to invert the commodity */
    2622 rowcomsign = validcomsign;
    2623 }
    2624 else if( rowcomsign != validcomsign )
    2625 {
    2626 /* the signs do not fit: this is incompatible */
    2627 incompatible = TRUE;
    2628 break;
    2629 }
    2630 }
    2631 else
    2632 {
    2633 /* in the undirected case, we ignore the sign of the coefficient */
    2634 valsign = +1;
    2635 }
    2636
    2637 /* store overlapping arc pattern */
    2638 if( arcpattern[arcid] == 0 )
    2639 {
    2640 overlappingarcs[noverlappingarcs] = arcid;
    2641 noverlappingarcs++;
    2642 }
    2643 arcpattern[arcid] += valsign;
    2644 }
    2645 }
    2646
    2647 /* calculate the weighted overlap and reset the zeroarcarray */
    2648 overlap = 0.0;
    2649 for( i = 0; i < noverlappingarcs; i++ )
    2650 {
    2651 SCIP_Real basenum;
    2652 SCIP_Real arcnum;
    2653 int arcid;
    2654
    2655 arcid = overlappingarcs[i];
    2656 assert(0 <= arcid && arcid < narcs);
    2657 assert(modeltype == SCIP_MCFMODELTYPE_UNDIRECTED || rowcomsign * basearcpattern[arcid] * arcpattern[arcid] > 0);
    2658
    2659 basenum = ABS(basearcpattern[arcid]);
    2660 arcnum = ABS(arcpattern[arcid]);
    2661 assert(basenum != 0.0);
    2662 assert(arcnum != 0.0);
    2663
    2664 if( basenum > arcnum )
    2665 overlap += arcnum/basenum;
    2666 else
    2667 overlap += basenum/arcnum;
    2668
    2669 arcpattern[arcid] = 0;
    2670 }
    2671
    2672/* calculate the score: maximize overlap and use minimal number of non-overlapping entries as tie breaker */
    2673 if( !incompatible && overlap > 0.0 )
    2674 {
    2675 /* flow variables with arc-id */
    2676 int rowarcs = rowlen - nposuncap - nneguncap;
    2677 int baserowarcs = baserowlen - basenposuncap - basenneguncap;
    2678
    2679 assert(overlap <= (SCIP_Real) rowlen);
    2680 assert(overlap <= (SCIP_Real) baserowlen);
    2681 assert(noverlappingarcs >= 1);
    2682
    2683 *invertcommodity = (rowcomsign == -1);
    2684
    2685 /* only one overlapping arc is very dangerous,
    2686 since this can also be the other end node of the arc */
    2687 if( noverlappingarcs >= 2 )
    2688 *score += 1000.0;
    2689
    2690 assert(rowarcs >= 0 && baserowarcs >= 0 );
    2691 /* in the ideal undirected case there are two flow variables with the same arc-id */
    2692 if( modeltype == SCIP_MCFMODELTYPE_DIRECTED )
    2693 *score = overlap - (rowarcs + baserowarcs - 2.0 * overlap)/(2.0 * ncols + 1.0);
    2694 else
    2695 *score = overlap - (rowarcs + baserowarcs - 4.0 * overlap)/(2.0 * ncols + 1.0);
    2696
    2697 /* Also use number of uncapacitated flowvars (variables without arcid) as tie-breaker */
    2698 if(*invertcommodity)
    2699 *score += 1.0 - (ABS(nneguncap - basenposuncap) + ABS(nposuncap - basenneguncap))/(2.0 * ncols + 1.0);
    2700 else
    2701 *score += 1.0 - (ABS(nposuncap - basenposuncap) + ABS(nneguncap - basenneguncap))/(2.0 * ncols + 1.0);
    2702
    2703 *score = MAX(*score, 1e-6); /* score may get negative due to many columns having the same arcid */
    2704 }
    2705
    2706 SCIPdebugMsg(scip, " -> node similarity: row <%s>: incompatible=%u overlap=%g rowlen=%d baserowlen=%d score=%g\n",
    2707 SCIProwGetName(row), incompatible, overlap, rowlen, baserowlen, *score);
    2708
    2709 /* free temporary memory */
    2710 SCIPfreeBufferArray(scip, &overlappingarcs);
    2711
    2712 return SCIP_OKAY;
    2713}
    2714
    2715/** assigns node ids to flow conservation constraints */
    2716static
    2718 SCIP* scip, /**< SCIP data structure */
    2719 MCFDATA* mcfdata /**< internal MCF extraction data to pass to subroutines */
    2720 )
    2721{
    2722 unsigned char* flowrowsigns = mcfdata->flowrowsigns;
    2723 int ncommodities = mcfdata->ncommodities;
    2724 int* commoditysigns = mcfdata->commoditysigns;
    2725 int narcs = mcfdata->narcs;
    2726 int* flowcands = mcfdata->flowcands;
    2727 int nflowcands = mcfdata->nflowcands;
    2728 int* rowcommodity = mcfdata->rowcommodity;
    2729 int* colarcid = mcfdata->colarcid;
    2730 int* newcols = mcfdata->newcols;
    2731 SCIP_MCFMODELTYPE modeltype = mcfdata->modeltype;
    2732 int* rownodeid;
    2733 SCIP_Bool* colisincident;
    2734 SCIP_Bool* rowprocessed;
    2735
    2736 SCIP_ROW** rows;
    2737 SCIP_COL** cols;
    2738 int nrows;
    2739 int ncols;
    2740
    2741 int* arcpattern;
    2742 int nposuncap;
    2743 int nneguncap;
    2744 SCIP_ROW** bestflowrows;
    2745 SCIP_Real* bestscores;
    2746 SCIP_Bool* bestinverted;
    2747 int r;
    2748 int c;
    2749 int n;
    2750
    2751 assert(mcfdata->nnodes == 0);
    2752 assert(modeltype != SCIP_MCFMODELTYPE_AUTO);
    2753
    2754 /* get LP data */
    2755 SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) );
    2756 SCIP_CALL( SCIPgetLPColsData(scip, &cols, &ncols) );
    2757
    2758 /* allocate temporary memory */
    2759 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->rownodeid, nrows) );
    2760 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->colisincident, ncols) );
    2761 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->zeroarcarray, narcs) );
    2762 BMSclearMemoryArray(mcfdata->zeroarcarray, narcs);
    2763 rownodeid = mcfdata->rownodeid;
    2764 colisincident = mcfdata->colisincident;
    2765
    2766 /* allocate temporary local memory */
    2767 SCIP_CALL( SCIPallocBufferArray(scip, &arcpattern, narcs) );
    2768 SCIP_CALL( SCIPallocBufferArray(scip, &bestflowrows, ncommodities) );
    2769 SCIP_CALL( SCIPallocBufferArray(scip, &bestscores, ncommodities) );
    2770 SCIP_CALL( SCIPallocBufferArray(scip, &bestinverted, ncommodities) );
    2771 SCIP_CALL( SCIPallocBufferArray(scip, &rowprocessed, nrows) );
    2772
    2773 /* initialize temporary memory */
    2774 for( r = 0; r < nrows; r++ )
    2775 rownodeid[r] = -1;
    2776 for( c = 0; c < ncols; c++ )
    2777 colisincident[c] = FALSE;
    2778
    2779 assert(flowcands != NULL || nflowcands == 0);
    2780
    2781 /* process all flow conservation constraints that have been used */
    2782 for( n = 0; n < nflowcands; n++ )
    2783 {
    2784 SCIP_COL** rowcols;
    2785 SCIP_Real* rowvals;
    2786 int rowlen;
    2787 int rowscale;
    2788 int basecommodity;
    2789 int i;
    2790
    2791 assert(flowcands != NULL);
    2792 r = flowcands[n];
    2793 assert(0 <= r && r < nrows);
    2794 assert(rowcommodity != NULL);
    2795
    2796 /* ignore rows that are not used as flow conservation constraint */
    2797 basecommodity = rowcommodity[r];
    2798 if( basecommodity == -1 )
    2799 continue;
    2800 assert((flowrowsigns[r] & (LHSASSIGNED | RHSASSIGNED)) != 0);
    2801 assert(mcfdata->rowarcid[r] == -1);
    2802
    2803 /* skip rows that are already assigned to a node */
    2804 if( rownodeid[r] >= 0 )
    2805 continue;
    2806
    2807 /* assign row to new node id */
    2808 SCIPdebugMsg(scip, "assigning row %d <%s> of commodity %d to node %d [score: %g]\n",
    2809 r, SCIProwGetName(rows[r]), basecommodity, mcfdata->nnodes, mcfdata->flowrowscores[r]);
    2810 rownodeid[r] = mcfdata->nnodes;
    2811
    2812 /* increase number of nodes */
    2813 mcfdata->nnodes++;
    2814
    2815 /* For single commodity models we are done --
    2816 * no matching flow rows need to be found
    2817 */
    2818 if(ncommodities == 1)
    2819 continue;
    2820
    2821 /* get the arc pattern of the flow row */
    2822 BMSclearMemoryArray(arcpattern, narcs);
    2823 nposuncap=0;
    2824 nneguncap=0;
    2825
    2826 rowcols = SCIProwGetCols(rows[r]);
    2827 rowvals = SCIProwGetVals(rows[r]);
    2828 rowlen = SCIProwGetNLPNonz(rows[r]);
    2829
    2830 assert(commoditysigns != NULL);
    2831
    2832 if( (flowrowsigns[r] & RHSASSIGNED) != 0 )
    2833 rowscale = +1;
    2834 else
    2835 rowscale = -1;
    2836 if( (flowrowsigns[r] & INVERTED) != 0 )
    2837 rowscale *= -1;
    2838 if( commoditysigns[basecommodity] == -1 )
    2839 rowscale *= -1;
    2840
    2841 for( i = 0; i < rowlen; i++ )
    2842 {
    2843 int arcid;
    2844
    2845 c = SCIPcolGetLPPos(rowcols[i]);
    2846 assert(0 <= c && c < ncols);
    2847 arcid = colarcid[c];
    2848 if( arcid >= 0 )
    2849 {
    2850 /* due to presolving we may have multiple flow variables of the same arc in the row */
    2851 if( modeltype == SCIP_MCFMODELTYPE_UNDIRECTED || rowscale * rowvals[i] > 0.0 )
    2852 arcpattern[arcid]++;
    2853 else
    2854 arcpattern[arcid]--;
    2855 }
    2856 /* we also count variables that have no arc -- these have no capacity constraint --> uncapacitated */
    2857 else
    2858 {
    2859 if( modeltype == SCIP_MCFMODELTYPE_UNDIRECTED || rowscale * rowvals[i] > 0.0 )
    2860 nposuncap++;
    2861 else
    2862 nneguncap++;
    2863 }
    2864 }
    2865
    2866 /* initialize arrays to store best flow rows */
    2867 for( i = 0; i < ncommodities; i++ )
    2868 {
    2869 bestflowrows[i] = NULL;
    2870 bestscores[i] = 0.0;
    2871 bestinverted[i] = FALSE;
    2872 }
    2873
    2874 /* collect columns that are member of incident arc capacity constraints */
    2875 collectIncidentFlowCols(scip, mcfdata, rows[r], basecommodity);
    2876
    2877 /* initialize rowprocessed array */
    2878 BMSclearMemoryArray(rowprocessed, nrows);
    2879
    2880 /* identify flow conservation constraints in other commodities that match this node;
    2881 * search for flow rows in the column vectors of the incident columns
    2882 */
    2883 for( i = 0; i < mcfdata->nnewcols; i++ )
    2884 {
    2885 SCIP_ROW** colrows;
    2886 int collen;
    2887 int j;
    2888
    2889 assert(newcols != NULL);
    2890 c = newcols[i];
    2891 assert(0 <= c && c < ncols);
    2892 assert(mcfdata->colcommodity[c] >= 0);
    2893 assert(mcfdata->colcommodity[c] != basecommodity);
    2894
    2895 /* clean up the marker array */
    2896 assert(colisincident[c]);
    2897 colisincident[c] = FALSE;
    2898
    2899 /* scan column vector for flow conservation constraints */
    2900 colrows = SCIPcolGetRows(cols[c]);
    2901 collen = SCIPcolGetNLPNonz(cols[c]);
    2902
    2903 for( j = 0; j < collen; j++ )
    2904 {
    2905 int colr;
    2906 int rowcom;
    2907 SCIP_Real score;
    2908 SCIP_Bool invertcommodity;
    2909
    2910 colr = SCIProwGetLPPos(colrows[j]);
    2911 assert(0 <= colr && colr < nrows);
    2912
    2913 /* ignore rows that have already been processed */
    2914 if( rowprocessed[colr] )
    2915 continue;
    2916 rowprocessed[colr] = TRUE;
    2917
    2918 /* ignore rows that are not flow conservation constraints in the network */
    2919 rowcom = rowcommodity[colr];
    2920 assert(rowcom != basecommodity);
    2921 if( rowcom == -1 )
    2922 continue;
    2923
    2924 assert(rowcom == mcfdata->colcommodity[c]);
    2925 assert((flowrowsigns[colr] & (LHSASSIGNED | RHSASSIGNED)) != 0);
    2926 assert(mcfdata->rowarcid[colr] == -1);
    2927
    2928 /* ignore rows that are already assigned to a node */
    2929 if( rownodeid[colr] >= 0 )
    2930 continue;
    2931
    2932 /* compare row against arc pattern and calculate score */
    2933 SCIP_CALL( getNodeSimilarityScore(scip, mcfdata, rowlen, arcpattern,
    2934 nposuncap, nneguncap, colrows[j], &score, &invertcommodity) );
    2935 assert( !SCIPisNegative(scip, score) );
    2936
    2937 if( score > bestscores[rowcom] )
    2938 {
    2939 bestflowrows[rowcom] = colrows[j];
    2940 bestscores[rowcom] = score;
    2941 bestinverted[rowcom] = invertcommodity;
    2942 }
    2943 }
    2944 }
    2945 assert(bestflowrows[basecommodity] == NULL);
    2946
    2947 /* for each commodity, pick the best flow conservation constraint to define this node */
    2948 for( i = 0; i < ncommodities; i++ )
    2949 {
    2950 int comr;
    2951
    2952 if( bestflowrows[i] == NULL )
    2953 continue;
    2954
    2955 comr = SCIProwGetLPPos(bestflowrows[i]);
    2956 assert(0 <= comr && comr < nrows);
    2957 assert(rowcommodity[comr] == i);
    2958 assert((flowrowsigns[comr] & (LHSASSIGNED | RHSASSIGNED)) != 0);
    2959 assert(rownodeid[comr] == -1);
    2960 assert(mcfdata->nnodes >= 1);
    2961 /* assign flow row to current node */
    2962 SCIPdebugMsg(scip, " -> assigning row %d <%s> of commodity %d to node %d [invert:%u]\n",
    2963 comr, SCIProwGetName(rows[comr]), i, mcfdata->nnodes-1, bestinverted[i]);
    2964 rownodeid[comr] = mcfdata->nnodes-1;
    2965
    2966 /* fix the direction of the arcs of the commodity */
    2967 if( bestinverted[i] )
    2968 {
    2969 assert(commoditysigns[i] != +1);
    2970 commoditysigns[i] = -1;
    2971 }
    2972 else
    2973 {
    2974 assert(commoditysigns[i] != -1);
    2975 commoditysigns[i] = +1;
    2976 }
    2977 }
    2978 }
    2979
    2980 /* free local temporary memory */
    2981
    2982 SCIPfreeBufferArray(scip, &rowprocessed);
    2983 SCIPfreeBufferArray(scip, &bestinverted);
    2984 SCIPfreeBufferArray(scip, &bestscores);
    2985 SCIPfreeBufferArray(scip, &bestflowrows);
    2986 SCIPfreeBufferArray(scip, &arcpattern);
    2987
    2988 return SCIP_OKAY;
    2989}
    2990
    2991/** if there are still undecided commodity signs, fix them to +1 */
    2992static
    2994 MCFDATA* mcfdata /**< internal MCF extraction data to pass to subroutines */
    2995 )
    2996{
    2997 int* commoditysigns = mcfdata->commoditysigns;
    2998 int k;
    2999
    3000 for( k = 0; k < mcfdata->ncommodities; k++ )
    3001 {
    3002 if( commoditysigns[k] == 0 )
    3003 commoditysigns[k] = +1;
    3004 }
    3005}
    3006
    3007
    3008/** identifies the (at most) two nodes which contain the given flow variable */
    3009static
    3011 SCIP* scip, /**< SCIP data structure */
    3012 MCFDATA* mcfdata, /**< internal MCF extraction data to pass to subroutines */
    3013 SCIP_COL* col, /**< flow column */
    3014 int* sourcenode, /**< pointer to store the source node of the flow column */
    3015 int* targetnode /**< pointer to store the target node of the flow column */
    3016 )
    3017{
    3018 unsigned char* flowrowsigns = mcfdata->flowrowsigns;
    3019 int* commoditysigns = mcfdata->commoditysigns;
    3020 int* rowcommodity = mcfdata->rowcommodity;
    3021 int* rownodeid = mcfdata->rownodeid;
    3022 int* colsources = mcfdata->colsources;
    3023 int* coltargets = mcfdata->coltargets;
    3024
    3025 SCIP_ROW** colrows;
    3026 SCIP_Real* colvals;
    3027 int collen;
    3028 int c;
    3029 int i;
    3030
    3031 assert(sourcenode != NULL);
    3032 assert(targetnode != NULL);
    3033 assert(colsources != NULL);
    3034 assert(coltargets != NULL);
    3035
    3036 c = SCIPcolGetLPPos(col);
    3037 assert(0 <= c && c < SCIPgetNLPCols(scip));
    3038
    3039 /* check if we have this column already in cache */
    3040 if( colsources[c] >= -1 )
    3041 {
    3042 assert(coltargets[c] >= -1);
    3043 *sourcenode = colsources[c];
    3044 *targetnode = coltargets[c];
    3045 }
    3046 else
    3047 {
    3048 *sourcenode = -1;
    3049 *targetnode = -1;
    3050
    3051 /* search for flow conservation rows in the column vector */
    3052 colrows = SCIPcolGetRows(col);
    3053 colvals = SCIPcolGetVals(col);
    3054 collen = SCIPcolGetNLPNonz(col);
    3055 for( i = 0; i < collen; i++ )
    3056 {
    3057 int r;
    3058
    3059 r = SCIProwGetLPPos(colrows[i]);
    3060 assert(0 <= r && r < SCIPgetNLPRows(scip));
    3061
    3062 if( rownodeid[r] >= 0 )
    3063 {
    3064 int v;
    3065 int k;
    3066 int scale;
    3067
    3068 v = rownodeid[r];
    3069 k = rowcommodity[r];
    3070 assert(0 <= v && v < mcfdata->nnodes);
    3071 assert(0 <= k && k < mcfdata->ncommodities);
    3072 assert((flowrowsigns[r] & (LHSASSIGNED | RHSASSIGNED)) != 0);
    3073
    3074 /* check whether the flow row is inverted */
    3075 scale = +1;
    3076 if( (flowrowsigns[r] & LHSASSIGNED) != 0 )
    3077 scale *= -1;
    3078 if( (flowrowsigns[r] & INVERTED) != 0 )
    3079 scale *= -1;
    3080 if( commoditysigns[k] == -1 )
    3081 scale *= -1;
    3082
    3083 /* decide whether this node is source or target */
    3084 if( ( scale * colvals[i] ) > 0.0 )
    3085 {
    3086 assert(*sourcenode == -1);
    3087 *sourcenode = v;
    3088 if( *targetnode >= 0 )
    3089 break;
    3090 }
    3091 else
    3092 {
    3093 assert(*targetnode == -1);
    3094 *targetnode = v;
    3095 if( *sourcenode >= 0 )
    3096 break;
    3097 }
    3098 }
    3099 }
    3100
    3101 /* cache result for future use */
    3102 colsources[c] = *sourcenode;
    3103 coltargets[c] = *targetnode;
    3104 }
    3105}
    3106
    3107/** find uncapacitated arcs for flow columns that have no associated arc yet */
    3108static
    3110 SCIP* scip, /**< SCIP data structure */
    3111 MCFDATA* mcfdata /**< internal MCF extraction data to pass to subroutines */
    3112 )
    3113{
    3114 int* flowcands = mcfdata->flowcands;
    3115 int nflowcands = mcfdata->nflowcands;
    3116#ifndef NDEBUG
    3117 unsigned char* flowrowsigns = mcfdata->flowrowsigns;
    3118 int* colcommodity = mcfdata->colcommodity;
    3119 int* rowcommodity = mcfdata->rowcommodity;
    3120#endif
    3121 int* rownodeid = mcfdata->rownodeid;
    3122 int* colarcid = mcfdata->colarcid;
    3123 int nnodes = mcfdata->nnodes;
    3124 int ncommodities = mcfdata->ncommodities;
    3125 SCIP_MCFMODELTYPE modeltype = mcfdata->modeltype;
    3126
    3127 SCIP_ROW** rows;
    3128 SCIP_COL** cols;
    3129 int ncols;
    3130
    3131 int* sortedflowcands;
    3132 int* sortedflowcandnodeid;
    3133 int* sourcecount;
    3134 int* targetcount;
    3135 int* adjnodes;
    3136 int nadjnodes;
    3137 int* inccols;
    3138 int ninccols;
    3139 int arcsthreshold;
    3140
    3141 int v;
    3142 int n;
    3143
    3144 /* there should have been a cleanup already */
    3145 assert(mcfdata->nemptycommodities == 0);
    3146 assert(ncommodities >= 0);
    3147 assert(modeltype == SCIP_MCFMODELTYPE_UNDIRECTED || modeltype == SCIP_MCFMODELTYPE_DIRECTED);
    3148
    3149 /* avoid trivial cases */
    3150 if( ncommodities == 0 || nflowcands == 0 || nnodes == 0 )
    3151 return SCIP_OKAY;
    3152
    3153 SCIPdebugMsg(scip, "finding uncapacitated arcs\n");
    3154
    3155 /* get LP data */
    3156 rows = SCIPgetLPRows(scip);
    3157 cols = SCIPgetLPCols(scip);
    3158 ncols = SCIPgetNLPCols(scip);
    3159 assert(rows != NULL);
    3160 assert(cols != NULL || ncols == 0);
    3161
    3162 /* allocate temporary memory */
    3163 SCIP_CALL( SCIPallocBufferArray(scip, &sortedflowcands, nflowcands) );
    3164 SCIP_CALL( SCIPallocBufferArray(scip, &sortedflowcandnodeid, nflowcands) );
    3165 SCIP_CALL( SCIPallocBufferArray(scip, &sourcecount, nnodes) );
    3166 SCIP_CALL( SCIPallocBufferArray(scip, &targetcount, nnodes) );
    3167 SCIP_CALL( SCIPallocBufferArray(scip, &adjnodes, nnodes) );
    3168 SCIP_CALL( SCIPallocBufferArray(scip, &inccols, ncols) );
    3169
    3170 /* copy flowcands and initialize sortedflowcandnodeid arrays */
    3171 for( n = 0; n < nflowcands; n++ )
    3172 {
    3173 sortedflowcands[n] = flowcands[n];
    3174 sortedflowcandnodeid[n] = rownodeid[flowcands[n]];
    3175 }
    3176
    3177 /* sort flow candidates by node id */
    3178 SCIPsortIntInt(sortedflowcandnodeid, sortedflowcands, nflowcands);
    3179 assert(sortedflowcandnodeid[0] <= 0);
    3180 assert(sortedflowcandnodeid[nflowcands-1] == nnodes-1);
    3181
    3182 /* initialize sourcecount and targetcount arrays */
    3183 for( v = 0; v < nnodes; v++ )
    3184 {
    3185 sourcecount[v] = 0;
    3186 targetcount[v] = 0;
    3187 }
    3188 nadjnodes = 0;
    3189 ninccols = 0;
    3190
    3191 /* we only accept an arc if at least this many flow variables give rise to this arc */
    3192 arcsthreshold = (int) SCIPceil(scip, (SCIP_Real) ncommodities * UNCAPACITATEDARCSTRESHOLD );
    3193
    3194 /* in the undirected case, there are two variables per commodity in each capacity row */
    3195 if( modeltype == SCIP_MCFMODELTYPE_UNDIRECTED )
    3196 arcsthreshold *= 2;
    3197
    3198 /* skip unused flow candidates */
    3199 for( n = 0; n < nflowcands; n++ )
    3200 {
    3201 if( sortedflowcandnodeid[n] >= 0 )
    3202 break;
    3203 assert(0 <= sortedflowcands[n] && sortedflowcands[n] < SCIPgetNLPRows(scip));
    3204 assert(rowcommodity[sortedflowcands[n]] == -1);
    3205 }
    3206 assert(n < nflowcands);
    3207 assert(sortedflowcandnodeid[n] == 0);
    3208
    3209 /* for each node s, count for each other node t the number of flow variables that are not yet assigned
    3210 * to an arc and that give rise to an (s,t) arc or an (t,s) arc
    3211 */
    3212 for( v = 0; n < nflowcands; v++ ) /*lint !e440*/ /* for flexelint: n is used as abort criterion for loop */
    3213 {
    3214 int l;
    3215
    3216 assert(v < nnodes);
    3217 assert(0 <= sortedflowcands[n] && sortedflowcands[n] < SCIPgetNLPRows(scip));
    3218 assert(rowcommodity[sortedflowcands[n]] >= 0);
    3219 assert(rownodeid[sortedflowcands[n]] == sortedflowcandnodeid[n]);
    3220 assert(sortedflowcandnodeid[n] == v); /* we must have at least one row per node */
    3221 assert(nadjnodes == 0);
    3222 assert(ninccols == 0);
    3223
    3224 SCIPdebugMsg(scip, " node %d starts with flowcand %d: <%s>\n", v, n, SCIProwGetName(rows[sortedflowcands[n]]));
    3225
    3226 /* process all flow rows that belong to node v */
    3227 for( ; n < nflowcands && sortedflowcandnodeid[n] == v; n++ )
    3228 {
    3229 SCIP_COL** rowcols;
    3230 int rowlen;
    3231 int r;
    3232 int i;
    3233
    3234 r = sortedflowcands[n];
    3235 assert((flowrowsigns[r] & (LHSASSIGNED | RHSASSIGNED)) != 0);
    3236 assert(mcfdata->rowarcid[r] == -1);
    3237
    3238 /* update sourcecount and targetcount for all flow columns in the row that are not yet assigned to an arc */
    3239 rowcols = SCIProwGetCols(rows[r]);
    3240 rowlen = SCIProwGetNLPNonz(rows[r]);
    3241 for( i = 0; i < rowlen; i++ )
    3242 {
    3243 SCIP_COL* col;
    3244 int arcid;
    3245 int c;
    3246 int s;
    3247 int t;
    3248
    3249 col = rowcols[i];
    3250 c = SCIPcolGetLPPos(col);
    3251 assert(0 <= c && c < SCIPgetNLPCols(scip));
    3252 arcid = colarcid[c];
    3253 assert(-2 <= arcid && arcid < mcfdata->narcs);
    3254 assert(rowcommodity[r] == colcommodity[c]);
    3255
    3256 if( arcid == -2 )
    3257 {
    3258 /* This is the second time we see this column, and we were unable to assign an arc
    3259 * to this column at the first time. So, this time we can ignore it. Just reset the
    3260 * temporary arcid -2 to -1.
    3261 */
    3262 colarcid[c] = -1;
    3263 }
    3264 else if( arcid == -1 )
    3265 {
    3266 int u;
    3267
    3268 /* identify the (at most) two nodes which contain this flow variable */
    3269 getIncidentNodes(scip, mcfdata, col, &s, &t);
    3270
    3271 SCIPdebugMsg(scip, " col <%s> [%g,%g] (s,t):(%i,%i)\n", SCIPvarGetName(SCIPcolGetVar(col)),
    3273
    3274 assert(-1 <= s && s < nnodes);
    3275 assert(-1 <= t && t < nnodes);
    3276 assert(s == v || t == v);
    3277 assert(s != t);
    3278
    3279 /* in the undirected case, always use s as other node */
    3280 if( modeltype == SCIP_MCFMODELTYPE_UNDIRECTED && s == v )
    3281 {
    3282 s = t;
    3283 t = v;
    3284 }
    3285
    3286 /* if there is no other node than v, ignore column */
    3287 if( s < 0 || t < 0 )
    3288 continue;
    3289
    3290 /* remember column in incidence list
    3291 * Note: each column can be collected at most once for node v, because each column can appear in at most one
    3292 * commodity, and in each commodity a column can have at most one +1 and one -1 entry. One of the two +/-1 entries
    3293 * is already used for v.
    3294 */
    3295 assert(ninccols < ncols);
    3296 inccols[ninccols] = c;
    3297 ninccols++;
    3298
    3299 /* update source or target count */
    3300 if( s != v )
    3301 {
    3302 sourcecount[s]++;
    3303 u = s;
    3304 }
    3305 else
    3306 {
    3307 targetcount[t]++;
    3308 u = t;
    3309 }
    3310
    3311 /* if other node has been seen the first time, store it in adjlist for sparse access of count arrays */
    3312 if( sourcecount[u] + targetcount[u] == 1 )
    3313 {
    3314 assert(nadjnodes < nnodes);
    3315 adjnodes[nadjnodes] = u;
    3316 nadjnodes++;
    3317 }
    3318 }
    3319 }
    3320 }
    3321
    3322 /* check if we want to add uncapacitated arcs s -> v or v -> t */
    3323 for( l = 0; l < nadjnodes; l++ )
    3324 {
    3325 int u;
    3326
    3327 u = adjnodes[l];
    3328 assert(0 <= u && u < nnodes);
    3329 assert(sourcecount[u] > 0 || targetcount[u] > 0);
    3330 assert(modeltype != SCIP_MCFMODELTYPE_UNDIRECTED || targetcount[u] == 0);
    3331 assert(ninccols >= 0);
    3332
    3333 /* add arcs u -> v */
    3334 if( sourcecount[u] >= arcsthreshold )
    3335 {
    3336 int arcid;
    3337 int m;
    3338
    3339 /* create new arc */
    3340 SCIP_CALL( createNewArc(scip, mcfdata, u, v, &arcid) );
    3341 SCIPdebugMsg(scip, " -> new arc: <%i> = (%i,%i)\n", arcid, u, v);
    3342
    3343 /* assign arcid to all involved columns */
    3344 for( m = 0; m < ninccols; m++ )
    3345 {
    3346 int c;
    3347 int s;
    3348 int t;
    3349
    3350 c = inccols[m];
    3351 assert(0 <= c && c < ncols);
    3352
    3353 assert(cols != NULL);
    3354 getIncidentNodes(scip, mcfdata, cols[c], &s, &t);
    3355 assert(s == v || t == v);
    3356
    3357 if( s == u || (modeltype == SCIP_MCFMODELTYPE_UNDIRECTED && t == u) )
    3358 {
    3359 SCIPdebugMsg(scip, " -> assign arcid:%i to column <%s>\n", arcid, SCIPvarGetName(SCIPcolGetVar(cols[c])));
    3360 colarcid[c] = arcid;
    3361
    3362 /* remove column from incidence array */
    3363 inccols[m] = inccols[ninccols-1];
    3364 ninccols--;
    3365 m--;
    3366 }
    3367 } /*lint --e{850}*/
    3368 }
    3369
    3370 /* add arcs v -> u */
    3371 if( targetcount[u] >= arcsthreshold )
    3372 {
    3373 int arcid;
    3374 int m;
    3375
    3376 /* create new arc */
    3377 SCIP_CALL( createNewArc(scip, mcfdata, v, u, &arcid) );
    3378 SCIPdebugMsg(scip, " -> new arc: <%i> = (%i,%i)\n", arcid, v, u);
    3379
    3380 /* assign arcid to all involved columns */
    3381 for( m = 0; m < ninccols; m++ )
    3382 {
    3383 int c;
    3384 int s;
    3385 int t;
    3386
    3387 c = inccols[m];
    3388 assert(0 <= c && c < ncols);
    3389
    3390 assert(cols != NULL);
    3391 getIncidentNodes(scip, mcfdata, cols[c], &s, &t);
    3392 assert(s == v || t == v);
    3393
    3394 if( t == u )
    3395 {
    3396 SCIPdebugMsg(scip, " -> assign arcid:%i to column <%s>\n", arcid, SCIPvarGetName(SCIPcolGetVar(cols[c])));
    3397 colarcid[c] = arcid;
    3398
    3399 /* remove column from incidence array */
    3400 inccols[m] = inccols[ninccols-1];
    3401 ninccols--;
    3402 m--;
    3403 }
    3404 } /*lint --e{850}*/
    3405 }
    3406 }
    3407
    3408 /* reset sourcecount and targetcount arrays */
    3409 for( l = 0; l < nadjnodes; l++ )
    3410 {
    3411 sourcecount[l] = 0;
    3412 targetcount[l] = 0;
    3413 }
    3414 nadjnodes = 0;
    3415
    3416 /* mark the incident columns that could not be assigned to a new arc such that we do not inspect them again */
    3417 for( l = 0; l < ninccols; l++ )
    3418 {
    3419 assert(colarcid[inccols[l]] == -1);
    3420 colarcid[inccols[l]] = -2;
    3421 }
    3422 ninccols = 0;
    3423 }
    3424 assert(n == nflowcands);
    3425 assert(v == nnodes);
    3426
    3427#ifdef SCIP_DEBUG
    3428 /* eventually, we must have reset all temporary colarcid[c] = -2 settings to -1 */
    3429 for( n = 0; n < ncols; n++ )
    3430 assert(colarcid[n] >= -1);
    3431#endif
    3432
    3433 /* free temporary memory */
    3434 SCIPfreeBufferArray(scip, &inccols);
    3435 SCIPfreeBufferArray(scip, &adjnodes);
    3436 SCIPfreeBufferArray(scip, &targetcount);
    3437 SCIPfreeBufferArray(scip, &sourcecount);
    3438 SCIPfreeBufferArray(scip, &sortedflowcandnodeid);
    3439 SCIPfreeBufferArray(scip, &sortedflowcands);
    3440
    3441 MCFdebugMessage("network after finding uncapacitated arcs has %d nodes, %d arcs, and %d commodities\n",
    3442 mcfdata->nnodes, mcfdata->narcs, mcfdata->ncommodities);
    3443
    3444 return SCIP_OKAY;
    3445}
    3446
    3447/** cleans up the network: gets rid of commodities without arcs or with at most one node */
    3448static
    3450 SCIP* scip, /**< SCIP data structure */
    3451 MCFDATA* mcfdata /**< internal MCF extraction data to pass to subroutines */
    3452 )
    3453{
    3454 int* flowcands = mcfdata->flowcands;
    3455 int nflowcands = mcfdata->nflowcands;
    3456 int* colcommodity = mcfdata->colcommodity;
    3457 int* rowcommodity = mcfdata->rowcommodity;
    3458 int* colarcid = mcfdata->colarcid;
    3459 int* rowarcid = mcfdata->rowarcid;
    3460 int* rownodeid = mcfdata->rownodeid;
    3461 int ncommodities = mcfdata->ncommodities;
    3462 int* commoditysigns = mcfdata->commoditysigns;
    3463 int narcs = mcfdata->narcs;
    3464 int nnodes = mcfdata->nnodes;
    3465 SCIP_ROW** capacityrows = mcfdata->capacityrows;
    3466
    3467 SCIP_ROW** rows;
    3468 int nrows;
    3469 int ncols;
    3470
    3471 int* nnodespercom;
    3472 int* narcspercom;
    3473 SCIP_Bool* arcisincom;
    3474 int* perm;
    3475 int permsize;
    3476 int maxnnodes;
    3477 int nnodesthreshold;
    3478 int newncommodities;
    3479
    3480 int i;
    3481 int a;
    3482 int k;
    3483
    3484 MCFdebugMessage("network before cleanup has %d nodes, %d arcs, and %d commodities\n", nnodes, narcs, ncommodities);
    3485
    3486 /* get LP data */
    3487 SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) );
    3488 ncols = SCIPgetNLPCols(scip);
    3489
    3490 /* allocate temporary memory */
    3491 permsize = ncommodities;
    3492 permsize = MAX(permsize, narcs);
    3493 permsize = MAX(permsize, nnodes);
    3494 SCIP_CALL( SCIPallocBufferArray(scip, &nnodespercom, ncommodities) );
    3495 SCIP_CALL( SCIPallocBufferArray(scip, &narcspercom, ncommodities) );
    3496 SCIP_CALL( SCIPallocBufferArray(scip, &arcisincom, ncommodities) );
    3497 SCIP_CALL( SCIPallocBufferArray(scip, &perm, permsize) );
    3498 BMSclearMemoryArray(nnodespercom, ncommodities);
    3499 BMSclearMemoryArray(narcspercom, ncommodities);
    3500
    3501 /** @todo remove nodes without any incoming and outgoing arcs */
    3502
    3503 assert(flowcands != NULL || nflowcands == 0);
    3504
    3505 /* count the number of nodes in each commodity */
    3506 for( i = 0; i < nflowcands; i++ )
    3507 {
    3508 int r;
    3509
    3510 assert(flowcands != NULL);
    3511 r = flowcands[i];
    3512 assert(0 <= r && r < nrows);
    3513 assert((rownodeid[r] >= 0) == (rowcommodity[r] >= 0));
    3514 if( rowcommodity[r] >= 0 )
    3515 {
    3516 assert(rowcommodity[r] < ncommodities);
    3517 nnodespercom[rowcommodity[r]]++;
    3518 }
    3519 }
    3520
    3521 assert(capacityrows != NULL || narcs == 0);
    3522
    3523 /* count the number of arcs in each commodity */
    3524 for( a = 0; a < narcs; a++ )
    3525 {
    3526 SCIP_COL** rowcols;
    3527 int rowlen;
    3528 int r;
    3529 int j;
    3530
    3531 assert(capacityrows != NULL);
    3532 r = SCIProwGetLPPos(capacityrows[a]);
    3533 assert(0 <= r && r < nrows);
    3534 assert(rowarcid[r] == a);
    3535
    3536 /* identify commodities which are touched by this arc capacity constraint */
    3537 BMSclearMemoryArray(arcisincom, ncommodities);
    3538 rowcols = SCIProwGetCols(rows[r]);
    3539 rowlen = SCIProwGetNLPNonz(rows[r]);
    3540 for( j = 0; j < rowlen; j++ )
    3541 {
    3542 int c;
    3543
    3544 c = SCIPcolGetLPPos(rowcols[j]);
    3545 assert(0 <= c && c < ncols);
    3546 if( colcommodity[c] >= 0 && colarcid[c] == a )
    3547 {
    3548 assert(colcommodity[c] < ncommodities);
    3549 arcisincom[colcommodity[c]] = TRUE;
    3550 }
    3551 }
    3552
    3553 /* increase arc counters of touched commodities */
    3554 for( k = 0; k < ncommodities; k++ )
    3555 {
    3556 if( arcisincom[k] )
    3557 narcspercom[k]++;
    3558 }
    3559 }
    3560
    3561 /* calculate maximal number of nodes per commodity */
    3562 maxnnodes = 0;
    3563 for( k = 0; k < ncommodities; k++ )
    3564 maxnnodes = MAX(maxnnodes, nnodespercom[k]);
    3565
    3566 /* we want to keep only commodities that have at least a certain size relative
    3567 * to the largest commodity
    3568 */
    3569
    3570 nnodesthreshold = (int)(MINCOMNODESFRACTION * maxnnodes);
    3571 nnodesthreshold = MAX(nnodesthreshold, MINNODES);
    3572 SCIPdebugMsg(scip, " -> node threshold: %d\n", nnodesthreshold);
    3573
    3574 /* discard trivial commodities */
    3575 newncommodities = 0;
    3576 for( k = 0; k < ncommodities; k++ )
    3577 {
    3578 SCIPdebugMsg(scip, " -> commodity %d: %d nodes, %d arcs\n", k, nnodespercom[k], narcspercom[k]);
    3579
    3580 /* only keep commodities of a certain size that have at least one arc */
    3581 if( nnodespercom[k] >= nnodesthreshold && narcspercom[k] >= 1 )
    3582 {
    3583 assert(newncommodities <= k);
    3584 perm[k] = newncommodities;
    3585 commoditysigns[newncommodities] = commoditysigns[k];
    3586 newncommodities++;
    3587 }
    3588 else
    3589 perm[k] = -1;
    3590 }
    3591
    3592 if( newncommodities < ncommodities )
    3593 {
    3594 SCIP_Bool* arcisused;
    3595 SCIP_Bool* nodeisused;
    3596 int newnarcs;
    3597 int newnnodes;
    3598 int c;
    3599 int v;
    3600
    3601 SCIPdebugMsg(scip, " -> discarding %d of %d commodities\n", ncommodities - newncommodities, ncommodities);
    3602
    3603 SCIP_CALL( SCIPallocBufferArray(scip, &arcisused, narcs) );
    3604 SCIP_CALL( SCIPallocBufferArray(scip, &nodeisused, nnodes) );
    3605
    3606 /* update data structures to new commodity ids */
    3607 BMSclearMemoryArray(arcisused, narcs);
    3608 BMSclearMemoryArray(nodeisused, nnodes);
    3609 for( c = 0; c < ncols; c++ )
    3610 {
    3611 if( colcommodity[c] >= 0 )
    3612 {
    3613 assert(-1 <= colarcid[c] && colarcid[c] < narcs);
    3614 assert(colcommodity[c] < mcfdata->ncommodities);
    3615 colcommodity[c] = perm[colcommodity[c]];
    3616 assert(colcommodity[c] < newncommodities);
    3617 if( colcommodity[c] == -1 )
    3618 {
    3619 /* we are lazy and do not update plusflow and minusflow */
    3620 colarcid[c] = -1;
    3621 }
    3622 else if( colarcid[c] >= 0 )
    3623 arcisused[colarcid[c]] = TRUE;
    3624 }
    3625 }
    3626 for( i = 0; i < nflowcands; i++ )
    3627 {
    3628 int r;
    3629
    3630 assert(flowcands != NULL);
    3631 r = flowcands[i];
    3632 assert(0 <= r && r < nrows);
    3633 assert((rownodeid[r] >= 0) == (rowcommodity[r] >= 0));
    3634 if( rowcommodity[r] >= 0 )
    3635 {
    3636 assert(0 <= rownodeid[r] && rownodeid[r] < nnodes);
    3637 assert(rowcommodity[r] < mcfdata->ncommodities);
    3638 rowcommodity[r] = perm[rowcommodity[r]];
    3639 assert(rowcommodity[r] < newncommodities);
    3640 if( rowcommodity[r] == -1 )
    3641 {
    3642 /* we are lazy and do not update flowrowsigns */
    3643 rownodeid[r] = -1;
    3644 }
    3645 else
    3646 nodeisused[rownodeid[r]] = TRUE;
    3647 }
    3648 }
    3649
    3650 mcfdata->ncommodities = newncommodities;
    3651 ncommodities = newncommodities;
    3652
    3653 /* discard unused arcs */
    3654 newnarcs = 0;
    3655 for( a = 0; a < narcs; a++ )
    3656 {
    3657 int r;
    3658
    3659 assert(capacityrows != NULL);
    3660
    3661 if( arcisused[a] )
    3662 {
    3663 assert(newnarcs <= a);
    3664 perm[a] = newnarcs;
    3665 capacityrows[newnarcs] = capacityrows[a];
    3666 newnarcs++;
    3667 }
    3668 else
    3669 {
    3670 /* we are lazy and do not update capacityrowsigns */
    3671 perm[a] = -1;
    3672 }
    3673 r = SCIProwGetLPPos(capacityrows[a]);
    3674 assert(0 <= r && r < nrows);
    3675 assert(rowarcid[r] == a);
    3676 rowarcid[r] = perm[a];
    3677 }
    3678
    3679 /* update remaining data structures to new arc ids */
    3680 if( newnarcs < narcs )
    3681 {
    3682 SCIPdebugMsg(scip, " -> discarding %d of %d arcs\n", narcs - newnarcs, narcs);
    3683
    3684 for( c = 0; c < ncols; c++ )
    3685 {
    3686 if( colarcid[c] >= 0 )
    3687 {
    3688 colarcid[c] = perm[colarcid[c]];
    3689 assert(colarcid[c] >= 0); /* otherwise colarcid[c] was set to -1 in the colcommodity update */
    3690 }
    3691 }
    3692 mcfdata->narcs = newnarcs;
    3693 narcs = newnarcs;
    3694 }
    3695#ifndef NDEBUG
    3696 for( a = 0; a < narcs; a++ )
    3697 {
    3698 int r;
    3699 assert(capacityrows != NULL);
    3700 r = SCIProwGetLPPos(capacityrows[a]);
    3701 assert(0 <= r && r < nrows);
    3702 assert(rowarcid[r] == a);
    3703 }
    3704#endif
    3705
    3706 /* discard unused nodes */
    3707 newnnodes = 0;
    3708 for( v = 0; v < nnodes; v++ )
    3709 {
    3710 if( nodeisused[v] )
    3711 {
    3712 assert(newnnodes <= v);
    3713 perm[v] = newnnodes;
    3714 newnnodes++;
    3715 }
    3716 else
    3717 perm[v] = -1;
    3718 }
    3719
    3720 /* update data structures to new node ids */
    3721 if( newnnodes < nnodes )
    3722 {
    3723 SCIPdebugMsg(scip, " -> discarding %d of %d nodes\n", nnodes - newnnodes, nnodes);
    3724
    3725 for( i = 0; i < nflowcands; i++ )
    3726 {
    3727 int r;
    3728
    3729 assert(flowcands != NULL);
    3730 r = flowcands[i];
    3731 assert(0 <= r && r < nrows);
    3732 assert((rownodeid[r] >= 0) == (rowcommodity[r] >= 0));
    3733 if( rowcommodity[r] >= 0 )
    3734 {
    3735 assert(rowcommodity[r] < ncommodities);
    3736 rownodeid[r] = perm[rownodeid[r]];
    3737 assert(rownodeid[r] >= 0); /* otherwise we would have deleted the commodity in the rowcommodity update above */
    3738 }
    3739 }
    3740 mcfdata->nnodes = newnnodes;
    3741#ifdef MCF_DEBUG
    3742 nnodes = newnnodes;
    3743#endif
    3744 }
    3745
    3746 /* free temporary memory */
    3747 SCIPfreeBufferArray(scip, &nodeisused);
    3748 SCIPfreeBufferArray(scip, &arcisused);
    3749 }
    3750
    3751 /* empty commodities have been removed here */
    3752 mcfdata->nemptycommodities = 0;
    3753
    3754 /* free temporary memory */
    3755 SCIPfreeBufferArray(scip, &perm);
    3756 SCIPfreeBufferArray(scip, &arcisincom);
    3757 SCIPfreeBufferArray(scip, &narcspercom);
    3758 SCIPfreeBufferArray(scip, &nnodespercom);
    3759
    3760 MCFdebugMessage("network after cleanup has %d nodes, %d arcs, and %d commodities\n", nnodes, narcs, ncommodities);
    3761
    3762 return SCIP_OKAY;
    3763}
    3764
    3765/** for each arc identifies a source and target node */
    3766static
    3768 SCIP* scip, /**< SCIP data structure */
    3769 MCFDATA* mcfdata, /**< internal MCF extraction data to pass to subroutines */
    3770 SCIP_SEPADATA* sepadata, /**< separator data */
    3771 MCFEFFORTLEVEL* effortlevel /**< pointer to store effort level of separation */
    3772 )
    3773{
    3774 int* colarcid = mcfdata->colarcid;
    3775 int* colcommodity = mcfdata->colcommodity;
    3776 int narcs = mcfdata->narcs;
    3777 int nnodes = mcfdata->nnodes;
    3778 int ncommodities = mcfdata->ncommodities;
    3779 SCIP_ROW** capacityrows = mcfdata->capacityrows;
    3780 SCIP_MCFMODELTYPE modeltype = mcfdata->modeltype;
    3781 SCIP_Real maxinconsistencyratio = sepadata->maxinconsistencyratio;
    3782 SCIP_Real maxarcinconsistencyratio = sepadata->maxarcinconsistencyratio;
    3783 int* arcsources;
    3784 int* arctargets;
    3785 int* colsources;
    3786 int* coltargets;
    3787 int* firstoutarcs;
    3788 int* firstinarcs;
    3789 int* nextoutarcs;
    3790 int* nextinarcs;
    3791
    3792 SCIP_Real *sourcenodecnt;
    3793 SCIP_Real *targetnodecnt;
    3794 int *flowvarspercom;
    3795 int *comtouched;
    3796 int *touchednodes;
    3797 int ntouchednodes;
    3798
    3799 int ncols;
    3800 SCIP_Real maxninconsistencies;
    3801
    3802 int c;
    3803 int v;
    3804 int a;
    3805
    3806 /* initialize effort level of separation */
    3807 assert(effortlevel != NULL);
    3808 *effortlevel = MCFEFFORTLEVEL_DEFAULT;
    3809
    3810 ncols = SCIPgetNLPCols(scip);
    3811
    3812 /* allocate memory in mcfdata */
    3813 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->arcsources, narcs) );
    3814 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->arctargets, narcs) );
    3815 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->colsources, ncols) );
    3816 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->coltargets, ncols) );
    3817 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->firstoutarcs, nnodes) );
    3818 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->firstinarcs, nnodes) );
    3819 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->nextoutarcs, narcs) );
    3820 SCIP_CALL( SCIPallocMemoryArray(scip, &mcfdata->nextinarcs, narcs) );
    3821 arcsources = mcfdata->arcsources;
    3822 arctargets = mcfdata->arctargets;
    3823 colsources = mcfdata->colsources;
    3824 coltargets = mcfdata->coltargets;
    3825 firstoutarcs = mcfdata->firstoutarcs;
    3826 firstinarcs = mcfdata->firstinarcs;
    3827 nextoutarcs = mcfdata->nextoutarcs;
    3828 nextinarcs = mcfdata->nextinarcs;
    3829
    3830 mcfdata->arcarraysize = narcs;
    3831
    3832 /* initialize colsources and coltargets */
    3833 for( c = 0; c < ncols; c++ )
    3834 {
    3835 colsources[c] = -2;
    3836 coltargets[c] = -2;
    3837 }
    3838
    3839 /* initialize adjacency lists */
    3840 for( v = 0; v < nnodes; v++ )
    3841 {
    3842 firstoutarcs[v] = -1;
    3843 firstinarcs[v] = -1;
    3844 }
    3845 for( a = 0; a < narcs; a++ )
    3846 {
    3847 nextoutarcs[a] = -1;
    3848 nextinarcs[a] = -1;
    3849 }
    3850
    3851 /* allocate temporary memory for source and target node identification */
    3852 SCIP_CALL( SCIPallocBufferArray(scip, &sourcenodecnt, nnodes) );
    3853 SCIP_CALL( SCIPallocBufferArray(scip, &targetnodecnt, nnodes) );
    3854 SCIP_CALL( SCIPallocBufferArray(scip, &flowvarspercom, ncommodities) );
    3855 SCIP_CALL( SCIPallocBufferArray(scip, &comtouched, ncommodities) );
    3856 SCIP_CALL( SCIPallocBufferArray(scip, &touchednodes, nnodes) );
    3857
    3858 BMSclearMemoryArray(sourcenodecnt, nnodes);
    3859 BMSclearMemoryArray(targetnodecnt, nnodes);
    3860
    3861 mcfdata->ninconsistencies = 0.0;
    3862 maxninconsistencies = maxinconsistencyratio * (SCIP_Real)narcs;
    3863
    3864 /* search for source and target nodes */
    3865 for( a = 0; a < narcs; a++ )
    3866 {
    3867 SCIP_COL** rowcols;
    3868 int rowlen;
    3869 int bestsourcev;
    3870 int besttargetv;
    3871 SCIP_Real bestsourcecnt;
    3872 SCIP_Real besttargetcnt;
    3873 SCIP_Real totalsourcecnt;
    3874 SCIP_Real totaltargetcnt;
    3875 SCIP_Real totalnodecnt;
    3876 SCIP_Real nsourceinconsistencies;
    3877 SCIP_Real ntargetinconsistencies;
    3878 int ntouchedcoms;
    3879 int i;
    3880#ifndef NDEBUG
    3881 int r;
    3882
    3883 r = SCIProwGetLPPos(capacityrows[a]);
    3884#endif
    3885 assert(0 <= r && r < SCIPgetNLPRows(scip));
    3886 assert((mcfdata->capacityrowsigns[r] & (LHSASSIGNED | RHSASSIGNED)) != 0);
    3887 assert(mcfdata->rowarcid[r] == a);
    3888
    3889#ifndef NDEBUG
    3890 for( i = 0; i < nnodes; i++ )
    3891 {
    3892 assert(sourcenodecnt[i] == 0);
    3893 assert(targetnodecnt[i] == 0);
    3894 }
    3895#endif
    3896
    3897 rowcols = SCIProwGetCols(capacityrows[a]);
    3898 rowlen = SCIProwGetNLPNonz(capacityrows[a]);
    3899
    3900 /* count number of flow variables per commodity */
    3901 BMSclearMemoryArray(flowvarspercom, ncommodities);
    3902 BMSclearMemoryArray(comtouched, ncommodities);
    3903 ntouchedcoms = 0;
    3904 for( i = 0; i < rowlen; i++ )
    3905 {
    3906 c = SCIPcolGetLPPos(rowcols[i]);
    3907 assert(0 <= c && c < SCIPgetNLPCols(scip));
    3908 if( colarcid[c] >= 0 )
    3909 {
    3910 int k = colcommodity[c];
    3911 assert (0 <= k && k < ncommodities);
    3912 flowvarspercom[k]++;
    3913 if( !comtouched[k] )
    3914 {
    3915 ntouchedcoms++;
    3916 comtouched[k] = TRUE;
    3917 }
    3918 }
    3919 }
    3920
    3921 /* if the row does not have any flow variable, it is not a capacity constraint */
    3922 if( ntouchedcoms == 0 )
    3923 {
    3924 capacityrows[a] = NULL;
    3925 arcsources[a] = -1;
    3926 arctargets[a] = -1;
    3927 continue;
    3928 }
    3929
    3930 /* check the flow variables of the capacity row for flow conservation constraints */
    3931 ntouchednodes = 0;
    3932 totalsourcecnt = 0.0;
    3933 totaltargetcnt = 0.0;
    3934 totalnodecnt = 0.0;
    3935 for( i = 0; i < rowlen; i++ )
    3936 {
    3937 c = SCIPcolGetLPPos(rowcols[i]);
    3938 assert(0 <= c && c < SCIPgetNLPCols(scip));
    3939 if( colarcid[c] >= 0 )
    3940 {
    3941 int k = colcommodity[c];
    3942 int sourcev;
    3943 int targetv;
    3944 SCIP_Real weight;
    3945
    3946 assert (0 <= k && k < ncommodities);
    3947 assert (comtouched[k]);
    3948 assert (flowvarspercom[k] >= 1);
    3949
    3950 /* identify the (at most) two nodes which contain this flow variable */
    3951 getIncidentNodes(scip, mcfdata, rowcols[i], &sourcev, &targetv);
    3952
    3953 /* count the nodes */
    3954 weight = 1.0/flowvarspercom[k];
    3955 if( sourcev >= 0 )
    3956 {
    3957 if( sourcenodecnt[sourcev] == 0.0 && targetnodecnt[sourcev] == 0.0 )
    3958 {
    3959 touchednodes[ntouchednodes] = sourcev;
    3960 ntouchednodes++;
    3961 }
    3962 sourcenodecnt[sourcev] += weight;
    3963 totalsourcecnt += weight;
    3964 }
    3965 if( targetv >= 0 )
    3966 {
    3967 if( sourcenodecnt[targetv] == 0.0 && targetnodecnt[targetv] == 0.0 )
    3968 {
    3969 touchednodes[ntouchednodes] = targetv;
    3970 ntouchednodes++;
    3971 }
    3972 targetnodecnt[targetv] += weight;
    3973 totaltargetcnt += weight;
    3974 }
    3975 if( sourcev >= 0 || targetv >= 0 )
    3976 totalnodecnt += weight;
    3977 }
    3978 }
    3979
    3980 /* perform a majority vote on source and target node */
    3981 bestsourcev = -1;
    3982 besttargetv = -1;
    3983 bestsourcecnt = 0.0;
    3984 besttargetcnt = 0.0;
    3985 for( i = 0; i < ntouchednodes; i++ )
    3986 {
    3987 v = touchednodes[i];
    3988 assert(0 <= v && v < nnodes);
    3989
    3990 if( modeltype == SCIP_MCFMODELTYPE_DIRECTED )
    3991 {
    3992 /* in the directed model, we distinguish between source and target */
    3993 if( sourcenodecnt[v] >= targetnodecnt[v] )
    3994 {
    3995 if( sourcenodecnt[v] > bestsourcecnt )
    3996 {
    3997 bestsourcev = v;
    3998 bestsourcecnt = sourcenodecnt[v];
    3999 }
    4000 }
    4001 else
    4002 {
    4003 if( targetnodecnt[v] > besttargetcnt )
    4004 {
    4005 besttargetv = v;
    4006 besttargetcnt = targetnodecnt[v];
    4007 }
    4008 }
    4009 }
    4010 else
    4011 {
    4012 SCIP_Real nodecnt = sourcenodecnt[v] + targetnodecnt[v];
    4013
    4014 /* in the undirected model, we use source for the maximum and target for the second largest number of total hits */
    4015 assert( modeltype == SCIP_MCFMODELTYPE_UNDIRECTED );
    4016 if( nodecnt > bestsourcecnt )
    4017 {
    4018 besttargetv = bestsourcev;
    4019 besttargetcnt = bestsourcecnt;
    4020 bestsourcev = v;
    4021 bestsourcecnt = nodecnt;
    4022 }
    4023 else if( nodecnt > besttargetcnt )
    4024 {
    4025 besttargetv = v;
    4026 besttargetcnt = nodecnt;
    4027 }
    4028 }
    4029
    4030 /* clear the nodecnt arrays */
    4031 sourcenodecnt[v] = 0;
    4032 targetnodecnt[v] = 0;
    4033 }
    4034
    4035 /* check inconsistency of arcs */
    4036 if( modeltype == SCIP_MCFMODELTYPE_UNDIRECTED )
    4037 {
    4038 totalsourcecnt = totalnodecnt;
    4039 totaltargetcnt = totalnodecnt;
    4040 }
    4041 assert(SCIPisGE(scip,totalsourcecnt,bestsourcecnt));
    4042 assert(SCIPisGE(scip,totaltargetcnt,besttargetcnt));
    4043 nsourceinconsistencies = (totalsourcecnt - bestsourcecnt)/ntouchedcoms;
    4044 ntargetinconsistencies = (totaltargetcnt - besttargetcnt)/ntouchedcoms;
    4045
    4046 /* delete arcs that have to large inconsistency */
    4047 if( nsourceinconsistencies > maxarcinconsistencyratio )
    4048 {
    4049 /* delete source assignment */
    4050 bestsourcev = -1;
    4051 }
    4052
    4053 if( ntargetinconsistencies > maxarcinconsistencyratio )
    4054 {
    4055 /* delete target assignment */
    4056 besttargetv = -1;
    4057 }
    4058
    4059 /* assign the incident nodes */
    4060 assert(bestsourcev == -1 || bestsourcev != besttargetv);
    4061 arcsources[a] = bestsourcev;
    4062 arctargets[a] = besttargetv;
    4063 SCIPdebugMsg(scip, "arc %d: %d -> %d (len=%d, sourcecnt=%g/%g, targetcnt=%g/%g, %g/%g inconsistencies)\n",
    4064 a, bestsourcev, besttargetv, rowlen,
    4065 bestsourcecnt, totalsourcecnt, besttargetcnt, totaltargetcnt,
    4066 nsourceinconsistencies, ntargetinconsistencies);
    4067
    4068 /* update adjacency lists */
    4069 if( bestsourcev != -1 )
    4070 {
    4071 nextoutarcs[a] = firstoutarcs[bestsourcev];
    4072 firstoutarcs[bestsourcev] = a;
    4073 }
    4074 if( besttargetv != -1 )
    4075 {
    4076 nextinarcs[a] = firstinarcs[besttargetv];
    4077 firstinarcs[besttargetv] = a;
    4078 }
    4079
    4080 /* update the number of inconsistencies */
    4081 mcfdata->ninconsistencies += 0.5*(nsourceinconsistencies + ntargetinconsistencies);
    4082
    4083 if( mcfdata->ninconsistencies > maxninconsistencies )
    4084 {
    4085 MCFdebugMessage(" -> reached maximal number of inconsistencies: %g > %g\n",
    4086 mcfdata->ninconsistencies, maxninconsistencies);
    4087 break;
    4088 }
    4089 }
    4090
    4091 /**@todo should we also use an aggressive parameter setting -- this should be done here */
    4092 if( mcfdata->ninconsistencies <= maxninconsistencies && narcs > 0 && ncommodities > 0 )
    4093 *effortlevel = MCFEFFORTLEVEL_DEFAULT;
    4094 else
    4095 *effortlevel = MCFEFFORTLEVEL_OFF;
    4096
    4097 MCFdebugMessage("extracted network has %g inconsistencies (ratio %g) -> separating with effort %d\n",
    4098 mcfdata->ninconsistencies, mcfdata->ninconsistencies/(SCIP_Real)narcs, *effortlevel);
    4099
    4100 /* free temporary memory */
    4101 SCIPfreeBufferArray(scip, &touchednodes);
    4102 SCIPfreeBufferArray(scip, &comtouched);
    4103 SCIPfreeBufferArray(scip, &flowvarspercom);
    4104 SCIPfreeBufferArray(scip, &targetnodecnt);
    4105 SCIPfreeBufferArray(scip, &sourcenodecnt);
    4106
    4107 return SCIP_OKAY;
    4108}
    4109
    4110#define UNKNOWN 0 /**< node has not yet been seen */
    4111#define ONSTACK 1 /**< node is currently on the processing stack */
    4112#define VISITED 2 /**< node has been visited and assigned to some component */
    4113
    4114/** returns lists of nodes and arcs in the connected component of the given startv */
    4115static
    4117 SCIP* scip, /**< SCIP data structure */
    4118 MCFDATA* mcfdata, /**< internal MCF extraction data to pass to subroutines */
    4119 int* nodevisited, /**< array to mark visited nodes */
    4120 int startv, /**< node for which the connected component should be generated */
    4121 int* compnodes, /**< array to store node ids of the component */
    4122 int* ncompnodes, /**< pointer to store the number of nodes in the component */
    4123 int* comparcs, /**< array to store arc ids of the component */
    4124 int* ncomparcs /**< pointer to store the number of arcs in the component */
    4125 )
    4126{
    4127 int* arcsources = mcfdata->arcsources;
    4128 int* arctargets = mcfdata->arctargets;
    4129 int* firstoutarcs = mcfdata->firstoutarcs;
    4130 int* firstinarcs = mcfdata->firstinarcs;
    4131 int* nextoutarcs = mcfdata->nextoutarcs;
    4132 int* nextinarcs = mcfdata->nextinarcs;
    4133 int nnodes = mcfdata->nnodes;
    4134
    4135 int* stacknodes;
    4136 int nstacknodes;
    4137
    4138 assert(nodevisited != NULL);
    4139 assert(0 <= startv && startv < nnodes);
    4140 assert(nodevisited[startv] == UNKNOWN);
    4141 assert(compnodes != NULL);
    4142 assert(ncompnodes != NULL);
    4143 assert(comparcs != NULL);
    4144 assert(ncomparcs != NULL);
    4145
    4146 *ncompnodes = 0;
    4147 *ncomparcs = 0;
    4148
    4149 /* allocate temporary memory for node stack */
    4150 SCIP_CALL( SCIPallocBufferArray(scip, &stacknodes, nnodes) );
    4151
    4152 /* put startv on stack */
    4153 stacknodes[0] = startv;
    4154 nstacknodes = 1;
    4155 nodevisited[startv] = ONSTACK;
    4156
    4157 /* perform depth-first search */
    4158 while( nstacknodes > 0 )
    4159 {
    4160 int v;
    4161 int a;
    4162
    4163 assert(firstoutarcs != NULL);
    4164 assert(firstinarcs != NULL);
    4165 assert(nextoutarcs != NULL);
    4166 assert(nextinarcs != NULL);
    4167
    4168 /* pop first element from stack */
    4169 v = stacknodes[nstacknodes-1];
    4170 nstacknodes--;
    4171 assert(0 <= v && v < nnodes);
    4172 assert(nodevisited[v] == ONSTACK);
    4173 nodevisited[v] = VISITED;
    4174
    4175 /* put node into component */
    4176 assert(*ncompnodes < nnodes);
    4177 compnodes[*ncompnodes] = v;
    4178 (*ncompnodes)++;
    4179
    4180 /* go through the list of outgoing arcs */
    4181 for( a = firstoutarcs[v]; a != -1; a = nextoutarcs[a] )
    4182 {
    4183 int targetv;
    4184
    4185 assert(0 <= a && a < mcfdata->narcs);
    4186 assert(arctargets != NULL);
    4187
    4188 targetv = arctargets[a];
    4189
    4190 /* check if we have already visited the target node */
    4191 if( targetv != -1 && nodevisited[targetv] == VISITED )
    4192 continue;
    4193
    4194 /* put arc to component */
    4195 assert(*ncomparcs < mcfdata->narcs);
    4196 comparcs[*ncomparcs] = a;
    4197 (*ncomparcs)++;
    4198
    4199 /* push target node to stack */
    4200 if( targetv != -1 && nodevisited[targetv] == UNKNOWN )
    4201 {
    4202 assert(nstacknodes < nnodes);
    4203 stacknodes[nstacknodes] = targetv;
    4204 nstacknodes++;
    4205 nodevisited[targetv] = ONSTACK;
    4206 }
    4207 }
    4208
    4209 /* go through the list of ingoing arcs */
    4210 for( a = firstinarcs[v]; a != -1; a = nextinarcs[a] )
    4211 {
    4212 int sourcev;
    4213
    4214 assert(0 <= a && a < mcfdata->narcs);
    4215 assert(arcsources != NULL);
    4216
    4217 sourcev = arcsources[a];
    4218
    4219 /* check if we have already seen the source node */
    4220 if( sourcev != -1 && nodevisited[sourcev] == VISITED )
    4221 continue;
    4222
    4223 /* put arc to component */
    4224 assert(*ncomparcs < mcfdata->narcs);
    4225 comparcs[*ncomparcs] = a;
    4226 (*ncomparcs)++;
    4227
    4228 /* push source node to stack */
    4229 if( sourcev != -1 && nodevisited[sourcev] == UNKNOWN )
    4230 {
    4231 assert(nstacknodes < nnodes);
    4232 stacknodes[nstacknodes] = sourcev;
    4233 nstacknodes++;
    4234 nodevisited[sourcev] = ONSTACK;
    4235 }
    4236 }
    4237 }
    4238
    4239 /* free temporary memory */
    4240 SCIPfreeBufferArray(scip, &stacknodes);
    4241
    4242 return SCIP_OKAY;
    4243}
    4244
    4245/** extracts MCF network structures from the current LP */
    4246static
    4248 SCIP* scip, /**< SCIP data structure */
    4249 SCIP_SEPADATA* sepadata, /**< separator data */
    4250 SCIP_MCFNETWORK*** mcfnetworks, /**< pointer to store array of MCF network structures */
    4251 int* nmcfnetworks, /**< pointer to store number of MCF networks */
    4252 MCFEFFORTLEVEL* effortlevel /**< effort level of separation */
    4253 )
    4254{
    4255 MCFDATA mcfdata;
    4256
    4257 SCIP_MCFMODELTYPE modeltype = (SCIP_MCFMODELTYPE) sepadata->modeltype;
    4258
    4259 SCIP_Bool failed;
    4260
    4261 SCIP_ROW** rows;
    4262 SCIP_COL** cols;
    4263 int nrows;
    4264 int ncols;
    4265 int mcfnetworkssize;
    4266
    4267 assert(mcfnetworks != NULL);
    4268 assert(nmcfnetworks != NULL);
    4269 assert(effortlevel != NULL);
    4270
    4271 failed = FALSE;
    4272 *effortlevel = MCFEFFORTLEVEL_OFF;
    4273 *mcfnetworks = NULL;
    4274 *nmcfnetworks = 0;
    4275 mcfnetworkssize = 0;
    4276
    4277 /* Algorithm to identify multi-commodity-flow network with capacity constraints
    4278 *
    4279 * 1. Identify candidate rows for flow conservation constraints in the LP.
    4280 * 2. Sort flow conservation candidates by a ranking on how sure we are that it is indeed a constraint of the desired type.
    4281 * 3. Extract network structure of flow conservation constraints:
    4282 * (a) Initialize plusflow[c] = minusflow[c] = FALSE for all columns c and other local data.
    4283 * (b) As long as there are flow conservation candidates left:
    4284 * (i) Create new commodity and use first flow conservation constraint as new row.
    4285 * (ii) Add new row to commodity, update pluscom/minuscom accordingly.
    4286 * (iii) For the newly added columns search for an incident flow conservation constraint. Pick the one of highest ranking.
    4287 * Reflect row or commodity if necessary (multiply with -1)
    4288 * (iv) If found, set new row to this row and goto (ii).
    4289 * (v) If only very few flow rows have been used, discard the commodity immediately.
    4290 * 4. Identify candidate rows for capacity constraints in the LP.
    4291 * 5. Sort capacity constraint candidates by a ranking on how sure we are that it is indeed a constraint of the desired type.
    4292 * 6. Identify capacity constraints for the arcs and assign arc ids to columns and capacity constraints.
    4293 * 7. Assign node ids to flow conservation constraints.
    4294 * 8. PostProcessing
    4295 * a if there are still undecided commodity signs, fix them to +1
    4296 * b clean up the network: get rid of commodities without arcs or with at most one node
    4297 * c assign source and target nodes to capacitated arc
    4298 * d find uncapacitated arcs
    4299 */
    4300
    4301 /* get LP data */
    4302 SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) );
    4303 SCIP_CALL( SCIPgetLPColsData(scip, &cols, &ncols) );
    4304
    4305 /* initialize local extraction data */
    4306 mcfdata.flowrowsigns = NULL;
    4307 mcfdata.flowrowscalars = NULL;
    4308 mcfdata.flowrowscores = NULL;
    4309 mcfdata.capacityrowsigns = NULL;
    4310 mcfdata.capacityrowscores = NULL;
    4311 mcfdata.flowcands = NULL;
    4312 mcfdata.nflowcands = 0;
    4313 mcfdata.capacitycands = NULL;
    4314 mcfdata.ncapacitycands = 0;
    4315 mcfdata.plusflow = NULL;
    4316 mcfdata.minusflow = NULL;
    4317 mcfdata.ncommodities = 0;
    4318 mcfdata.nemptycommodities = 0;
    4319 mcfdata.commoditysigns = NULL;
    4320 mcfdata.commoditysignssize = 0;
    4321 mcfdata.colcommodity = NULL;
    4322 mcfdata.rowcommodity = NULL;
    4323 mcfdata.colarcid = NULL;
    4324 mcfdata.rowarcid = NULL;
    4325 mcfdata.rownodeid = NULL;
    4326 mcfdata.arcarraysize = 0;
    4327 mcfdata.arcsources = NULL;
    4328 mcfdata.arctargets = NULL;
    4329 mcfdata.colsources = NULL;
    4330 mcfdata.coltargets = NULL;
    4331 mcfdata.firstoutarcs = NULL;
    4332 mcfdata.firstinarcs = NULL;
    4333 mcfdata.nextoutarcs = NULL;
    4334 mcfdata.nextinarcs = NULL;
    4335 mcfdata.newcols = NULL;
    4336 mcfdata.nnewcols = 0;
    4337 mcfdata.narcs = 0;
    4338 mcfdata.nnodes = 0;
    4339 mcfdata.ninconsistencies = 0.0;
    4340 mcfdata.capacityrows = NULL;
    4341 mcfdata.capacityrowssize = 0;
    4342 mcfdata.colisincident = NULL;
    4343 mcfdata.zeroarcarray = NULL;
    4344 mcfdata.modeltype = modeltype;
    4345
    4346 /* 1. identify candidate rows for flow conservation constraints in the LP
    4347 * 2. Sort flow conservation candidates by a ranking on how sure we are that it is indeed a constraint of the desired type
    4348 */
    4349 SCIP_CALL( extractFlowRows(scip, &mcfdata) );
    4350 assert(mcfdata.flowrowsigns != NULL);
    4351 assert(mcfdata.flowrowscalars != NULL);
    4352 assert(mcfdata.flowrowscores != NULL);
    4353 assert(mcfdata.flowcands != NULL);
    4354
    4355 if( mcfdata.nflowcands == 0 )
    4356 failed = TRUE;
    4357
    4358 if( !failed )
    4359 {
    4360 /* 3. extract network structure of flow conservation constraints. */
    4361 /* coverity[var_deref_model] */
    4362 SCIP_CALL( extractFlow(scip, &mcfdata, MAXFLOWVARFLOWROWRATIO, &failed) );
    4363 assert(mcfdata.plusflow != NULL);
    4364 assert(mcfdata.minusflow != NULL);
    4365 assert(mcfdata.colcommodity != NULL);
    4366 assert(mcfdata.rowcommodity != NULL);
    4367 assert(mcfdata.newcols != NULL);
    4368 }
    4369
    4370 if( !failed )
    4371 {
    4372#ifdef SCIP_DEBUG
    4373 printCommodities(scip, &mcfdata);
    4374#endif
    4375
    4376 /* 4. identify candidate rows for capacity constraints in the LP
    4377 * 5. sort capacity constraint candidates by a ranking on how sure we are that it is indeed a constraint of the desired type
    4378 */
    4379 SCIP_CALL( extractCapacityRows(scip, &mcfdata) );
    4380 assert(mcfdata.capacityrowsigns != NULL);
    4381 assert(mcfdata.capacityrowscores != NULL);
    4382 assert(mcfdata.capacitycands != NULL);
    4383
    4384 if( mcfdata.ncapacitycands == 0 )
    4385 failed = TRUE;
    4386 }
    4387
    4388 if( !failed )
    4389 {
    4390 /* 6. arc-detection -- identify capacity constraints for the arcs and assign arc ids to columns and capacity constraints */
    4391 SCIP_CALL( extractCapacities(scip, &mcfdata) );
    4392 assert(mcfdata.colarcid != NULL);
    4393 assert(mcfdata.rowarcid != NULL);
    4394
    4395 /* 7. node-detection -- assign node ids to flow conservation constraints */
    4396 SCIP_CALL( extractNodes(scip, &mcfdata) );
    4397 assert(mcfdata.rownodeid != NULL);
    4398 assert(mcfdata.colisincident != NULL);
    4399 assert(mcfdata.zeroarcarray != NULL);
    4400
    4401 /* 8. postprocessing */
    4402 /* 8.a if there are still undecided commodity signs, fix them to +1 */
    4403 fixCommoditySigns(&mcfdata);
    4404
    4405 /* 8.b clean up the network: get rid of commodities without arcs or with at most one node */
    4406 SCIP_CALL( cleanupNetwork(scip, &mcfdata) );
    4407
    4408 /* 8.c construct incidence function -- assign source and target nodes to capacitated arcs */
    4409 SCIP_CALL( identifySourcesTargets(scip, &mcfdata, sepadata, effortlevel) );
    4410 assert(mcfdata.arcsources != NULL);
    4411 assert(mcfdata.arctargets != NULL);
    4412 assert(mcfdata.colsources != NULL);
    4413 assert(mcfdata.coltargets != NULL);
    4414 assert(mcfdata.firstoutarcs != NULL);
    4415 assert(mcfdata.firstinarcs != NULL);
    4416 assert(mcfdata.nextoutarcs != NULL);
    4417 assert(mcfdata.nextinarcs != NULL);
    4418 }
    4419
    4420 if( !failed && *effortlevel != MCFEFFORTLEVEL_OFF)
    4421 {
    4422 int* nodevisited;
    4423 int* compnodeid;
    4424 int* compnodes;
    4425 int* comparcs;
    4426 int minnodes;
    4427 int v;
    4428
    4429 /* 8.d find uncapacitated arcs */
    4430 SCIP_CALL( findUncapacitatedArcs(scip, &mcfdata) );
    4431
    4432#ifdef SCIP_DEBUG
    4433 printCommodities(scip, &mcfdata);
    4434#endif
    4435
    4436 minnodes = MINNODES;
    4437
    4438 /* allocate temporary memory for component finding */
    4439 SCIP_CALL( SCIPallocBufferArray(scip, &nodevisited, mcfdata.nnodes) );
    4440 SCIP_CALL( SCIPallocBufferArray(scip, &compnodes, mcfdata.nnodes) );
    4441 SCIP_CALL( SCIPallocBufferArray(scip, &comparcs, mcfdata.narcs) );
    4442 BMSclearMemoryArray(nodevisited, mcfdata.nnodes);
    4443
    4444 /* allocate temporary memory for v -> compv mapping */
    4445 SCIP_CALL( SCIPallocBufferArray(scip, &compnodeid, mcfdata.nnodes) );
    4446 for( v = 0; v < mcfdata.nnodes; v++ )
    4447 compnodeid[v] = -1;
    4448
    4449 /* search components and create a network structure for each of them */
    4450 for( v = 0; v < mcfdata.nnodes; v++ )
    4451 {
    4452 int ncompnodes;
    4453 int ncomparcs;
    4454
    4455 /* ignore nodes that have been already assigned to a component */
    4456 assert(nodevisited[v] == UNKNOWN || nodevisited[v] == VISITED);
    4457 if( nodevisited[v] == VISITED )
    4458 continue;
    4459
    4460 /* identify nodes and arcs of this component */
    4461 SCIP_CALL( identifyComponent(scip, &mcfdata, nodevisited, v, compnodes, &ncompnodes, comparcs, &ncomparcs) );
    4462 assert(ncompnodes >= 1);
    4463 assert(compnodes[0] == v);
    4464 assert(nodevisited[v] == VISITED);
    4465
    4466 /* ignore network component if it is trivial */
    4467 if( ncompnodes >= minnodes && ncomparcs >= MINARCS )
    4468 {
    4469 SCIP_MCFNETWORK* mcfnetwork;
    4470 int i;
    4471
    4472 /* make sure that we have enough memory for the new network pointer */
    4473 assert(*nmcfnetworks <= MAXNETWORKS);
    4474 assert(*nmcfnetworks <= mcfnetworkssize);
    4475 if( *nmcfnetworks == mcfnetworkssize )
    4476 {
    4477 mcfnetworkssize = MAX(2*mcfnetworkssize, *nmcfnetworks+1);
    4478 SCIP_CALL( SCIPreallocMemoryArray(scip, mcfnetworks, mcfnetworkssize) );
    4479 }
    4480 assert(*nmcfnetworks < mcfnetworkssize);
    4481
    4482 /* create network data structure */
    4483 SCIP_CALL( mcfnetworkCreate(scip, &mcfnetwork) );
    4484
    4485 /* fill sparse network structure */
    4486 SCIP_CALL( mcfnetworkFill(scip, mcfnetwork, &mcfdata, compnodeid, compnodes, ncompnodes, comparcs, ncomparcs) );
    4487
    4488 /* insert in sorted network list */
    4489 assert(*mcfnetworks != NULL);
    4490 for( i = *nmcfnetworks; i > 0 && mcfnetwork->nnodes > (*mcfnetworks)[i-1]->nnodes; i-- )
    4491 (*mcfnetworks)[i] = (*mcfnetworks)[i-1];
    4492 (*mcfnetworks)[i] = mcfnetwork;
    4493 (*nmcfnetworks)++;
    4494
    4495 /* if we reached the maximal number of networks, update minnodes */
    4496 if( *nmcfnetworks >= MAXNETWORKS )
    4497 minnodes = MAX(minnodes, (*mcfnetworks)[*nmcfnetworks-1]->nnodes);
    4498
    4499 /* if we exceeded the maximal number of networks, delete the last one */
    4500 if( *nmcfnetworks > MAXNETWORKS )
    4501 {
    4502 SCIPdebugMsg(scip, " -> discarded network with %d nodes and %d arcs due to maxnetworks (minnodes=%d)\n",
    4503 (*mcfnetworks)[*nmcfnetworks-1]->nnodes, (*mcfnetworks)[*nmcfnetworks-1]->narcs, minnodes);
    4504 SCIP_CALL( mcfnetworkFree(scip, &(*mcfnetworks)[*nmcfnetworks-1]) );
    4505 (*nmcfnetworks)--;
    4506 }
    4507 assert(*nmcfnetworks <= MAXNETWORKS);
    4508 }
    4509 else
    4510 {
    4511 SCIPdebugMsg(scip, " -> discarded component with %d nodes and %d arcs\n", ncompnodes, ncomparcs);
    4512 }
    4513 }
    4514
    4515 /* free temporary memory */
    4516 SCIPfreeBufferArray(scip, &compnodeid);
    4517 SCIPfreeBufferArray(scip, &comparcs);
    4518 SCIPfreeBufferArray(scip, &compnodes);
    4519 SCIPfreeBufferArray(scip, &nodevisited);
    4520 }
    4521
    4522 /* free memory */
    4523 SCIPfreeMemoryArrayNull(scip, &mcfdata.arcsources);
    4524 SCIPfreeMemoryArrayNull(scip, &mcfdata.arctargets);
    4525 SCIPfreeMemoryArrayNull(scip, &mcfdata.colsources);
    4526 SCIPfreeMemoryArrayNull(scip, &mcfdata.coltargets);
    4527 SCIPfreeMemoryArrayNull(scip, &mcfdata.firstoutarcs);
    4528 SCIPfreeMemoryArrayNull(scip, &mcfdata.firstinarcs);
    4529 SCIPfreeMemoryArrayNull(scip, &mcfdata.nextoutarcs);
    4530 SCIPfreeMemoryArrayNull(scip, &mcfdata.nextinarcs);
    4531 SCIPfreeMemoryArrayNull(scip, &mcfdata.zeroarcarray);
    4532 SCIPfreeMemoryArrayNull(scip, &mcfdata.colisincident);
    4533 SCIPfreeMemoryArrayNull(scip, &mcfdata.capacityrows);
    4534 SCIPfreeMemoryArrayNull(scip, &mcfdata.rownodeid);
    4535 SCIPfreeMemoryArrayNull(scip, &mcfdata.rowarcid);
    4536 SCIPfreeMemoryArrayNull(scip, &mcfdata.colarcid);
    4537 SCIPfreeMemoryArrayNull(scip, &mcfdata.newcols);
    4538 SCIPfreeMemoryArrayNull(scip, &mcfdata.rowcommodity);
    4539 SCIPfreeMemoryArrayNull(scip, &mcfdata.colcommodity);
    4540 SCIPfreeMemoryArrayNull(scip, &mcfdata.commoditysigns);
    4541 SCIPfreeMemoryArrayNull(scip, &mcfdata.minusflow);
    4542 SCIPfreeMemoryArrayNull(scip, &mcfdata.plusflow);
    4543 SCIPfreeMemoryArrayNull(scip, &mcfdata.capacitycands);
    4544 SCIPfreeMemoryArrayNull(scip, &mcfdata.flowcands);
    4545 SCIPfreeMemoryArrayNull(scip, &mcfdata.capacityrowscores);
    4546 SCIPfreeMemoryArrayNull(scip, &mcfdata.capacityrowsigns);
    4547 SCIPfreeMemoryArrayNull(scip, &mcfdata.flowrowscores);
    4548 SCIPfreeMemoryArrayNull(scip, &mcfdata.flowrowscalars);
    4549 SCIPfreeMemoryArrayNull(scip, &mcfdata.flowrowsigns);
    4550
    4551 return SCIP_OKAY;
    4552}
    4553#ifdef COUNTNETWORKVARIABLETYPES
    4554/** extracts MCF network structures from the current LP */
    4555static
    4556SCIP_RETCODE printFlowSystemInfo(
    4557 SCIP* scip, /**< SCIP data structure */
    4558 SCIP_MCFNETWORK** mcfnetworks, /**< array of MCF network structures */
    4559 int nmcfnetworks /**< number of MCF networks */
    4560 )
    4561{
    4562 SCIP_ROW** rows;
    4563 SCIP_COL** cols;
    4564 SCIP_Bool* colvisited;
    4565 int nrows;
    4566 int ncols;
    4567 int m;
    4568 int c;
    4569 int a;
    4570 int k;
    4571 int v;
    4572 int nflowrows = 0;
    4573 int ncaprows = 0;
    4574 int nflowvars = 0;
    4575 int nintflowvars = 0;
    4576 int nbinflowvars = 0;
    4577 int ncontflowvars = 0;
    4578 int ncapvars = 0;
    4579 int nintcapvars = 0;
    4580 int nbincapvars = 0;
    4581 int ncontcapvars = 0;
    4582
    4583 /* get LP data */
    4584 SCIP_CALL( SCIPgetLPRowsData(scip, &rows, &nrows) );
    4585 SCIP_CALL( SCIPgetLPColsData(scip, &cols, &ncols) );
    4586 SCIP_CALL( SCIPallocBufferArray(scip, &colvisited, ncols) );
    4587
    4588 /* get flow variable types */
    4589 for(c=0; c < ncols; c++)
    4590 colvisited[c]=FALSE;
    4591
    4592 MCFdebugMessage("\n\n****** VAR COUNTING ********* \n");
    4593
    4594 for(m=0; m < nmcfnetworks; m++)
    4595 {
    4596 SCIP_MCFNETWORK* mcfnetwork = mcfnetworks[m];
    4597
    4598 int narcs = mcfnetwork->narcs;
    4599 int nnodes = mcfnetwork->nnodes;
    4600 int ncommodities = mcfnetwork->ncommodities;
    4601 SCIP_ROW** arccapacityrows = mcfnetwork->arccapacityrows;
    4602 SCIP_ROW*** nodeflowrows = mcfnetwork->nodeflowrows;
    4603 int* colcommodity = mcfnetwork->colcommodity;
    4604
    4605 /* get flow variable types */
    4606 for(c=0; c < ncols; c++)
    4607 {
    4608 SCIP_COL* col;
    4609
    4610 if(colcommodity[c] >= 0 && ! colvisited[c])
    4611 {
    4612 /* this is a flow variable */
    4613 nflowvars++;
    4614 col = cols[c];
    4615 colvisited[c] = TRUE;
    4616 if( SCIPcolIsImpliedIntegral(col) )
    4617 ++nintflowvars;
    4618 else
    4619 {
    4620 switch( SCIPvarGetType(SCIPcolGetVar(col)))
    4621 {
    4623 nbinflowvars++;
    4624 break;
    4626 nintflowvars++;
    4627 break;
    4629 ncontflowvars++;
    4630 break;
    4631 default:
    4632 SCIPerrorMessage("unknown variable type\n");
    4633 SCIPABORT();
    4634 return SCIP_INVALIDDATA; /*lint !e527*/
    4635 }
    4636 }
    4637 }
    4638 }
    4639 /* get capacity variable types and number of capacity rows*/
    4640 for( a = 0; a < narcs; a++ )
    4641 {
    4642 SCIP_ROW* row;
    4643 row = arccapacityrows[a];
    4644
    4645 if( row != NULL )
    4646 {
    4647 SCIP_COL** rowcols;
    4648 int rowlen;
    4649 int i;
    4650
    4651 ncaprows++;
    4652 rowcols = SCIProwGetCols(row);
    4653 rowlen = SCIProwGetNLPNonz(row);
    4654
    4655 for( i = 0; i < rowlen; i++ )
    4656 {
    4657 c = SCIPcolGetLPPos(rowcols[i]);
    4658 assert(0 <= c && c < SCIPgetNLPCols(scip));
    4659
    4660 if(colcommodity[c] == -1 && ! colvisited[c] )
    4661 {
    4662 ncapvars++;
    4663 colvisited[c] = TRUE;
    4664 if( SCIPcolIsImpliedIntegral(rowcols[i]) )
    4665 nintcapvars++;
    4666 else
    4667 {
    4668 switch( SCIPvarGetType(SCIPcolGetVar(rowcols[i])))
    4669 {
    4671 nbincapvars++;
    4672 break;
    4674 nintcapvars++;
    4675 break;
    4677 ncontcapvars++;
    4678 break;
    4679 default:
    4680 SCIPerrorMessage("unknown variable type\n");
    4681 SCIPABORT();
    4682 return SCIP_INVALIDDATA; /*lint !e527*/
    4683 }
    4684 }
    4685 }
    4686 }
    4687 }
    4688 }
    4689 /* get number of flow rows */
    4690 for( k = 0; k < ncommodities; k++ )
    4691 {
    4692 for( v = 0; v < nnodes; v++ )
    4693 {
    4694 SCIP_ROW* row;
    4695 row = nodeflowrows[v][k];
    4696
    4697 if( row != NULL )
    4698 nflowrows++;
    4699 }
    4700 }
    4701
    4702 MCFdebugMessage("----- network %i -----\n",m);
    4703 MCFdebugMessage(" nof flowrows: %5d\n", nflowrows);
    4704 MCFdebugMessage(" nof caprows: %5d\n", ncaprows);
    4705 MCFdebugMessage(" nof flowvars: %5d of which [ %d , %d , %d ] are continuous, integer, binary\n",
    4706 nflowvars, ncontflowvars, nintflowvars, nbinflowvars);
    4707 MCFdebugMessage(" nof capvars: %5d of which [ %d , %d , %d ] are continuous, integer, binary\n",
    4708 ncapvars, ncontcapvars, nintcapvars, nbincapvars);
    4709 }
    4710
    4711 MCFdebugMessage("****** END VAR COUNTING ********* \n\n");
    4712
    4713 SCIPfreeBufferArray(scip, &colvisited);
    4714
    4715 return SCIP_OKAY;
    4716}
    4717#endif
    4718/*
    4719 * Union find methods
    4720 * used for generating partitions of node sets and
    4721 * for checking connectivity of cut shores
    4722 */
    4723
    4724/** initializes a union find data structure by putting each element into its own set */
    4725static
    4727 int* representatives, /**< mapping an element v to its representative */
    4728 int nelems /**< number of elements in the ground set */
    4729 )
    4730{
    4731 int v;
    4732
    4733 /* we start with each element being in its own set */
    4734 for( v = 0; v < nelems; v++ )
    4735 representatives[v] = v;
    4736}
    4737
    4738/** applies a union find algorithm to get the representative of v */
    4739static
    4741 int* representatives, /**< mapping an element v to its representative */
    4742 int v /**< element v to get a representative for */
    4743 )
    4744{
    4745 assert(representatives != NULL);
    4746
    4747 while( v != representatives[v] )
    4748 {
    4749 representatives[v] = representatives[representatives[v]];
    4750 v = representatives[v];
    4751 }
    4752
    4753 return v;
    4754}
    4755
    4756/** joins two sets in the union find framework */
    4757static
    4759 int* representatives, /**< mapping an element v to its representative */
    4760 int rep1, /**< representative of first set */
    4761 int rep2 /**< representative of second set */
    4762 )
    4763{
    4764 assert(rep1 != rep2);
    4765 assert(representatives[rep1] == rep1);
    4766 assert(representatives[rep2] == rep2);
    4767
    4768 /* make sure that the smaller representative survives
    4769 * -> element 0 is always a representative
    4770 */
    4771 if( rep1 < rep2 )
    4772 representatives[rep2] = rep1;
    4773 else
    4774 representatives[rep1] = rep2;
    4775}
    4776
    4777/*
    4778 * Node pair methods
    4779 * used for shrinking the network based on nodepair-weights
    4780 * -> creating partition
    4781*/
    4782
    4783/** comparison method for weighted nodepairs */
    4784static
    4786{
    4787 NODEPAIRENTRY* nodepair1 = (NODEPAIRENTRY*)elem1;
    4788 NODEPAIRENTRY* nodepair2 = (NODEPAIRENTRY*)elem2;
    4789
    4790 if( nodepair1->weight > nodepair2->weight )
    4791 return -1;
    4792 else if( nodepair1->weight < nodepair2->weight )
    4793 return +1;
    4794 else
    4795 return 0;
    4796}
    4797
    4798/** NodePair HashTable
    4799 * gets the key of the given element */
    4800static
    4801SCIP_DECL_HASHGETKEY(hashGetKeyNodepairs)
    4802{
    4803 /*lint --e{715}*/
    4804 /* the key is the element itself */
    4805 return elem;
    4806}
    4807
    4808/** NodePair HashTable
    4809 * returns TRUE iff both keys are equal;
    4810 * two nodepairs are equal if both nodes equal
    4811 */
    4812static
    4813SCIP_DECL_HASHKEYEQ(hashKeyEqNodepairs)
    4814{
    4815#ifndef NDEBUG
    4816 SCIP_MCFNETWORK* mcfnetwork;
    4817#endif
    4818 NODEPAIRENTRY* nodepair1;
    4819 NODEPAIRENTRY* nodepair2;
    4820 int source1;
    4821 int source2;
    4822 int target1;
    4823 int target2;
    4824
    4825#ifndef NDEBUG
    4826 mcfnetwork = (SCIP_MCFNETWORK*)userptr;
    4827 assert(mcfnetwork != NULL);
    4828#endif
    4829
    4830 nodepair1 = (NODEPAIRENTRY*)key1;
    4831 nodepair2 = (NODEPAIRENTRY*)key2;
    4832
    4833 assert(nodepair1 != NULL);
    4834 assert(nodepair2 != NULL);
    4835
    4836 source1 = nodepair1->node1;
    4837 source2 = nodepair2->node1;
    4838 target1 = nodepair1->node2;
    4839 target2 = nodepair2->node2;
    4840
    4841 assert(source1 >=0 && source1 < mcfnetwork->nnodes);
    4842 assert(source2 >=0 && source2 < mcfnetwork->nnodes);
    4843 assert(target1 >=0 && target1 < mcfnetwork->nnodes);
    4844 assert(target2 >=0 && target2 < mcfnetwork->nnodes);
    4845 assert(source1 <= target1);
    4846 assert(source2 <= target2);
    4847
    4848 return (source1 == source2 && target1 == target2);
    4849}
    4850
    4851/** NodePair HashTable
    4852 * returns the hash value of the key */
    4853static
    4854SCIP_DECL_HASHKEYVAL(hashKeyValNodepairs)
    4855{
    4856#ifndef NDEBUG
    4857 SCIP_MCFNETWORK* mcfnetwork;
    4858#endif
    4859 NODEPAIRENTRY* nodepair;
    4860 int source;
    4861 int target;
    4862 unsigned int hashval;
    4863
    4864#ifndef NDEBUG
    4865 mcfnetwork = (SCIP_MCFNETWORK*)userptr;
    4866 assert(mcfnetwork != NULL);
    4867#endif
    4868
    4869 nodepair = (NODEPAIRENTRY*)key;
    4870 assert( nodepair != NULL);
    4871
    4872 source = nodepair->node1;
    4873 target = nodepair->node2;
    4874
    4875 assert(source >=0 && source < mcfnetwork->nnodes);
    4876 assert(target >=0 && target < mcfnetwork->nnodes);
    4877 assert(source <= target);
    4878
    4879 hashval = (unsigned)((source << 16) + target); /*lint !e701*/
    4880
    4881 return hashval;
    4882}
    4883
    4884/** creates a priority queue and fills it with the given nodepair entries
    4885 *
    4886 */
    4887static
    4889 SCIP* scip, /**< SCIP data structure */
    4890 SCIP_MCFNETWORK* mcfnetwork, /**< MCF network structure */
    4891 NODEPAIRQUEUE** nodepairqueue /**< pointer to nodepair priority queue */
    4892 )
    4893{
    4894 /* For every nodepair that is used in the network (at least one arc exists having this nodepair as endnodes)
    4895 * we calculate a weight:
    4896 * The weight w_st of a nodepair (s,t) is the minimum of the weights of all s-t and t-s arcs
    4897 * The weight w_a of an arc a is calculated as:
    4898 * w_a : = s_a + pi_a
    4899 * where s_a>=0 is the slack of the capacity constraint and pi_a<=0 its dual.
    4900 * The weight of uncapacitated arcs (without capacity constraints) is infinite.
    4901 */
    4902#ifdef BETTERWEIGHTFORDEMANDNODES
    4903 int ncommodities;
    4904 SCIP_ROW*** nodeflowrows;
    4905 SCIP_Real** nodeflowscales;
    4906 SCIP_Real maxweight;
    4907 SCIP_Real minweight;
    4908#endif
    4909
    4910#ifdef TIEBREAKING
    4911 int* colcommodity;
    4912#endif
    4913
    4914 SCIP_HASHTABLE* hashtable;
    4915 NODEPAIRENTRY* nodepairs;
    4916
    4917 int hashtablesize;
    4918 int a;
    4919 int nnodepairs;
    4920 int n;
    4921
    4922 assert(mcfnetwork != NULL);
    4923
    4924#ifdef BETTERWEIGHTFORDEMANDNODES
    4925 ncommodities = mcfnetwork->ncommodities;
    4926 nodeflowrows = mcfnetwork->nodeflowrows;
    4927 nodeflowscales = mcfnetwork->nodeflowscales;
    4928#endif
    4929
    4930#ifdef TIEBREAKING
    4931 colcommodity = mcfnetwork->colcommodity;
    4932#endif
    4933
    4934 assert(nodepairqueue != NULL);
    4935
    4936 SCIP_CALL( SCIPallocBuffer(scip, nodepairqueue) );
    4937
    4938 /* create a hash table for all used node pairs
    4939 * hash table is only needed to have unique nodepairs (identify arcs using the same nodepair)
    4940 */
    4941 hashtablesize = mcfnetwork->narcs;
    4942 hashtablesize = MAX(hashtablesize, HASHSIZE_NODEPAIRS);
    4943 SCIP_CALL( SCIPhashtableCreate(&hashtable, SCIPblkmem(scip), hashtablesize,
    4944 hashGetKeyNodepairs, hashKeyEqNodepairs, hashKeyValNodepairs, (void*) mcfnetwork) );
    4945
    4946 /* nodepairs will contain all constructed nodepairs and is used to fill the priority queue */
    4947 SCIP_CALL( SCIPallocBufferArray(scip, &(*nodepairqueue)->nodepairs, mcfnetwork->narcs) );
    4948
    4949 /* initialize hash table of all used node pairs and fill nodepairs */
    4950 nnodepairs = 0;
    4951 for( a = 0; a < mcfnetwork->narcs; a++ )
    4952 {
    4953 NODEPAIRENTRY nodepair;
    4954 NODEPAIRENTRY* nodepairptr;
    4955 SCIP_ROW* capacityrow;
    4956
    4957 capacityrow = mcfnetwork->arccapacityrows[a];
    4958
    4959 SCIPdebugMsg(scip, "arc %i = (%i %i)\n", a, mcfnetwork->arcsources[a], mcfnetwork->arctargets[a]);
    4960
    4961 /* construct fresh nodepair: smaller node gets node1 in nodeentry */
    4962 if( mcfnetwork->arcsources[a] <= mcfnetwork->arctargets[a] )
    4963 {
    4964 nodepair.node1 = mcfnetwork->arcsources[a];
    4965 nodepair.node2 = mcfnetwork->arctargets[a];
    4966 }
    4967 else
    4968 {
    4969 nodepair.node2 = mcfnetwork->arcsources[a];
    4970 nodepair.node1 = mcfnetwork->arctargets[a];
    4971 }
    4972
    4973 assert(nodepair.node1 < mcfnetwork->nnodes);
    4974 assert(nodepair.node2 < mcfnetwork->nnodes);
    4975 if( nodepair.node1 == -1 || nodepair.node2 == -1 )
    4976 continue;
    4977
    4978 /* construct arc weight of a */
    4979 if( capacityrow != NULL )
    4980 {
    4981 SCIP_Real maxval;
    4982 SCIP_Real slack;
    4983 SCIP_Real dualsol;
    4984 SCIP_Real scale;
    4985#ifdef TIEBREAKING
    4986 SCIP_Real totalflow;
    4987 SCIP_Real totalcap;
    4988 SCIP_COL** rowcols;
    4989 int rowlen;
    4990 int i;
    4991 int c;
    4992#endif
    4993
    4994 slack = SCIPgetRowFeasibility(scip, mcfnetwork->arccapacityrows[a]);
    4995 slack = MAX(slack, 0.0); /* can only be negative due to numerics */
    4996 dualsol = SCIProwGetDualsol(mcfnetwork->arccapacityrows[a]);
    4997 maxval = SCIPgetRowMaxCoef(scip, mcfnetwork->arccapacityrows[a]);
    4998 scale = ABS(mcfnetwork->arccapacityscales[a])/maxval; /* divide by maxval to normalize rows */
    4999 assert(scale > 0.0);
    5000
    5001#ifdef TIEBREAKING
    5002 /* get flow on arc for tie breaking */
    5003 rowcols = SCIProwGetCols(capacityrow);
    5004 rowlen = SCIProwGetNLPNonz(capacityrow);
    5005 totalflow = 0.0;
    5006 totalcap = 0.0;
    5007 SCIPdebugMsg(scip, " row <%s>: \n", SCIProwGetName(capacityrow));
    5008
    5009 for( i = 0; i < rowlen; i++ )
    5010 {
    5011 c = SCIPcolGetLPPos(rowcols[i]);
    5012 assert(0 <= c && c < SCIPgetNLPCols(scip));
    5013
    5014 SCIPdebugMsg(scip, " col <%s>: %g\n", SCIPvarGetName(SCIPcolGetVar(rowcols[i])), SCIPcolGetPrimsol(rowcols[i]) );
    5015 /* sum up flow on arc a*/
    5016 if(colcommodity[c] >= 0)
    5017 {
    5018 SCIPdebugMsg(scip, " flow col <%s>: %g\n", SCIPvarGetName(SCIPcolGetVar(rowcols[i])), REALABS(SCIPcolGetPrimsol(rowcols[i])) );
    5019 totalflow += REALABS(SCIPcolGetPrimsol(rowcols[i]));
    5020 }
    5021 else
    5022 {
    5023 SCIPdebugMsg(scip, " cap col <%s>: %g\n", SCIPvarGetName(SCIPcolGetVar(rowcols[i])), REALABS(SCIPcolGetPrimsol(rowcols[i])) );
    5024 totalcap += REALABS(SCIPcolGetPrimsol(rowcols[i]));
    5025 }
    5026 }
    5027
    5028 SCIPdebugMsg(scip, "cap arc -- slack:%g -- dual:%g -- flow:%g -- cap:%g \n", scale * slack, dualsol/scale, totalflow * scale, totalcap * scale);
    5029#else
    5030 SCIPdebugMsg(scip, "cap arc -- slack:%g -- dual:%g1\n", scale * slack, dualsol/scale);
    5031#endif
    5032
    5033 /* put the arc weight into a fresh nodepair */
    5034 nodepair.weight = scale * slack - ABS(dualsol)/scale;
    5035#ifdef USEFLOWFORTIEBREAKING
    5036 if( !SCIPisPositive(scip, nodepair.weight) )
    5037 {
    5038 nodepair.weight += totalflow * scale;
    5039 nodepair.weight = MIN( nodepair.weight, -0.0001);
    5040 }
    5041#endif
    5042#ifdef USECAPACITYFORTIEBREAKING
    5043 if( !SCIPisPositive(scip, nodepair.weight) )
    5044 {
    5045 nodepair.weight += totalcap * scale;
    5046 nodepair.weight = MIN( nodepair.weight, -0.0001);
    5047 }
    5048#endif
    5049 }
    5050 else
    5051 {
    5052 /* uncapacitated arc has infinite slack */
    5053 SCIPdebugMsg(scip, "uncap arc ... slack infinite\n");
    5054 nodepair.weight = SCIPinfinity(scip);
    5055 }
    5056
    5057 /* check if nodepair already exists in hash-table */
    5058 nodepairptr = (NODEPAIRENTRY*)(SCIPhashtableRetrieve(hashtable, (void*) (&nodepair) ));
    5059
    5060 /* if nodepair already exists update its weight */
    5061 if( nodepairptr != NULL )
    5062 {
    5063 /* adapt weight */
    5064 SCIPdebugMsg(scip, "nodepair known [%d,%d] -- old weight:%g -- new weight:%g\n", nodepair.node1,nodepair.node2,nodepairptr->weight,
    5065 MIN(nodepair.weight, nodepairptr->weight));
    5066 nodepairptr->weight = MIN(nodepair.weight, nodepairptr->weight);
    5067 }
    5068 else
    5069 {
    5070 /* no such nodepair in current hash table: insert into array and hashtable */
    5071 nodepairs = (*nodepairqueue)->nodepairs;
    5072
    5073 assert(nnodepairs < mcfnetwork->narcs);
    5074 nodepairs[nnodepairs] = nodepair;
    5075 SCIP_CALL( SCIPhashtableInsert(hashtable, (void*) (&nodepairs[nnodepairs]) ) );
    5076
    5077 SCIPdebugMsg(scip, "new nodepair [%d,%d]-- weight:%g\n", nodepair.node1, nodepair.node2, nodepair.weight);
    5078
    5079 nnodepairs++;
    5080 }
    5081 }
    5082
    5083 /* free hash table */
    5084 SCIPhashtableFree(&hashtable);
    5085
    5086 /* we still have to fill the priority queue */
    5087
    5088#ifdef BETTERWEIGHTFORDEMANDNODES
    5089 /* initial weights have been calculated
    5090 * we modify them now depending on the demand emanating at the endnodes of nodepairs
    5091 */
    5092
    5093 /* calculate max and min weight */
    5094 maxweight = +1; /* we want maxweight to be positive */
    5095 minweight = -1; /* we want minweight to be negative */
    5096 nodepairs = (*nodepairqueue)->nodepairs;
    5097 for( n = 0; n < nnodepairs; n++ )
    5098 {
    5099 /* maxweight should not be infinity (uncap arcs have infinity weight)*/
    5100 if(!SCIPisInfinity(scip,nodepairs[n].weight))
    5101 maxweight = MAX(maxweight, nodepairs[n].weight);
    5102
    5103 minweight = MIN(minweight, nodepairs[n].weight);
    5104 }
    5105
    5106 SCIPdebugMsg(scip, "min/max weight:%g / %g\n", minweight, maxweight);
    5107#endif
    5108
    5109 /* initialize priority queue */
    5110 SCIP_CALL( SCIPpqueueCreate(&(*nodepairqueue)->pqueue, nnodepairs, 2.0, compNodepairs, NULL) );
    5111
    5112 /* fill priority queue using array nodepairs */
    5113 for( n = 0; n < nnodepairs; n++ )
    5114 {
    5115 int node1 = nodepairs[n].node1;
    5116 int node2 = nodepairs[n].node2;
    5117
    5118#ifdef BETTERWEIGHTFORDEMANDNODES
    5119 SCIP_Real rhs = 0;
    5120 SCIP_Bool hasdemand1 = FALSE;
    5121 SCIP_Bool hasdemand2 = FALSE;
    5122
    5123 int k; /* commodity */
    5124
    5125 SCIPdebugMsg(scip, "nodepair [%d,%d] weight %g\n", node1,node2,nodepairs[n].weight);
    5126 /* check both nodes for their demand value in all commodities
    5127 * the demand value can be read from the rhs
    5128 * of the flowrows
    5129 */
    5130 /* node1 */
    5131 for( k = 0; k < ncommodities; k++ )
    5132 {
    5133 if( nodeflowrows[node1][k] == NULL )
    5134 continue;
    5135
    5136 if( nodeflowscales[node1][k] > 0.0 )
    5137 rhs = SCIProwGetRhs(nodeflowrows[node1][k]) - SCIProwGetConstant(nodeflowrows[node1][k]);
    5138 else
    5139 rhs = SCIProwGetLhs(nodeflowrows[node1][k]) - SCIProwGetConstant(nodeflowrows[node1][k]);
    5140
    5141 assert( !SCIPisInfinity(scip,ABS(rhs)) );
    5142
    5143 if( ! SCIPisZero(scip, rhs) )
    5144 {
    5145 hasdemand1 = TRUE;
    5146 break;
    5147 }
    5148 }
    5149
    5150 /* node2 */
    5151 for( k = 0; k < ncommodities; k++ )
    5152 {
    5153 if( nodeflowrows[node2][k] == NULL )
    5154 continue;
    5155
    5156 if( nodeflowscales[node2][k] > 0.0 )
    5157 rhs = SCIProwGetRhs(nodeflowrows[node2][k]) - SCIProwGetConstant(nodeflowrows[node2][k]);
    5158 else
    5159 rhs = SCIProwGetLhs(nodeflowrows[node2][k]) - SCIProwGetConstant(nodeflowrows[node2][k]);
    5160
    5161 assert(! SCIPisInfinity(scip, ABS(rhs)));
    5162
    5163 if( ! SCIPisZero(scip, rhs) )
    5164 {
    5165 hasdemand2 = TRUE;
    5166 break;
    5167 }
    5168 }
    5169
    5170 /* if one of the nodes has no demand increase the score
    5171 * (slack arcs are still shrunk first)
    5172 *
    5173 */
    5174 if( SCIPisPositive(scip, nodepairs[n].weight))
    5175 {
    5176 assert(SCIPisPositive(scip, maxweight));
    5177
    5178 if( !hasdemand1 || !hasdemand2 )
    5179 nodepairs[n].weight += maxweight;
    5180 }
    5181 else
    5182 {
    5183 assert( SCIPisNegative(scip, minweight));
    5184
    5185 if( hasdemand1 && hasdemand2)
    5186 nodepairs[n].weight += minweight;
    5187 }
    5188#endif
    5189 SCIPdebugMsg(scip, "nodepair [%d,%d] weight %g\n", node1,node2,nodepairs[n].weight);
    5190
    5191 /* fill priority queue */
    5192 SCIP_CALL( SCIPpqueueInsert((*nodepairqueue)->pqueue, (void*)&(*nodepairqueue)->nodepairs[n]) );
    5193 }
    5194
    5195 return SCIP_OKAY;
    5196}
    5197
    5198
    5199/** frees memory of a nodepair queue */
    5200static
    5202 SCIP* scip, /**< SCIP data structure */
    5203 NODEPAIRQUEUE** nodepairqueue /**< pointer to nodepair priority queue */
    5204 )
    5205{
    5206 assert(nodepairqueue != NULL);
    5207 assert(*nodepairqueue != NULL);
    5208
    5209 SCIPpqueueFree(&(*nodepairqueue)->pqueue);
    5210 SCIPfreeBufferArray(scip, &(*nodepairqueue)->nodepairs);
    5211 SCIPfreeBuffer(scip, nodepairqueue);
    5212}
    5213
    5214
    5215/** returns whether there are any nodepairs left on the queue */
    5216static
    5218 NODEPAIRQUEUE* nodepairqueue /**< nodepair priority queue */
    5219 )
    5220{
    5221 assert(nodepairqueue != NULL);
    5222
    5223 return (SCIPpqueueFirst(nodepairqueue->pqueue) == NULL);
    5224}
    5225
    5226
    5227/** removes the top element from the nodepair priority queue, returns nodepair entry or NULL */
    5228static
    5230 NODEPAIRQUEUE* nodepairqueue /**< nodepair priority queue */
    5231 )
    5232{
    5233 assert(nodepairqueue != NULL);
    5234
    5235 return (NODEPAIRENTRY*)SCIPpqueueRemove(nodepairqueue->pqueue);
    5236}
    5237
    5238/*
    5239 * Node partition methods
    5240 * used for creating partitions of nodes
    5241 * use union find algorithm
    5242*/
    5243
    5244/** returns the representative node in the cluster of the given node */
    5245static
    5247 NODEPARTITION* nodepartition, /**< node partition data structure */
    5248 int v /**< node id to get representative for */
    5249 )
    5250{
    5251 return unionfindGetRepresentative(nodepartition->representatives, v);
    5252}
    5253
    5254/** joins two clusters given by their representative nodes */
    5255static
    5257 NODEPARTITION* nodepartition, /**< node partition data structure */
    5258 int rep1, /**< representative of first cluster */
    5259 int rep2 /**< representative of second cluster */
    5260 )
    5261{
    5262 unionfindJoinSets (nodepartition->representatives, rep1, rep2);
    5263}
    5264
    5265/** partitions nodes into a small number of clusters */
    5266static
    5268 SCIP* scip, /**< SCIP data structure */
    5269 SCIP_MCFNETWORK* mcfnetwork, /**< MCF network structure */
    5270 NODEPARTITION** nodepartition, /**< pointer to node partition data structure */
    5271 int nclusters /**< number of clusters to generate */
    5272 )
    5273{
    5274 NODEPAIRQUEUE* nodepairqueue;
    5275 int* clustersize;
    5276 int nclustersleft;
    5277 int v;
    5278 int c;
    5279 int pos;
    5280
    5281 assert(mcfnetwork != NULL);
    5282 assert(nodepartition != NULL);
    5283 assert(mcfnetwork->nnodes >= 1);
    5284
    5285 /* allocate and initialize memory */
    5286 SCIP_CALL( SCIPallocBuffer(scip, nodepartition) );
    5287 SCIP_CALL( SCIPallocBufferArray(scip, &(*nodepartition)->representatives, mcfnetwork->nnodes) );
    5288 SCIP_CALL( SCIPallocBufferArray(scip, &(*nodepartition)->nodeclusters, mcfnetwork->nnodes) );
    5289 SCIP_CALL( SCIPallocBufferArray(scip, &(*nodepartition)->clusternodes, mcfnetwork->nnodes) );
    5290 SCIP_CALL( SCIPallocBufferArray(scip, &(*nodepartition)->clusterbegin, nclusters+1) );
    5291 (*nodepartition)->nclusters = 0;
    5292
    5293 /* we start with each node being in its own cluster */
    5294 unionfindInitSets((*nodepartition)->representatives, mcfnetwork->nnodes);
    5295
    5296 /* create priority queue for nodepairs */
    5297 SCIP_CALL( nodepairqueueCreate(scip, mcfnetwork, &nodepairqueue) );
    5298
    5299 /* loop over nodepairs in order of their weights */
    5300 nclustersleft = mcfnetwork->nnodes;
    5301 while( !nodepairqueueIsEmpty(nodepairqueue) && nclustersleft > nclusters )
    5302 {
    5303 NODEPAIRENTRY* nodepair;
    5304 int node1;
    5305 int node2;
    5306 int node1rep;
    5307 int node2rep;
    5308
    5309 /* get the next nodepair */
    5310 nodepair = nodepairqueueRemove(nodepairqueue);
    5311 assert(nodepair != NULL);
    5312 node1 = nodepair->node1;
    5313 node2 = nodepair->node2;
    5314
    5315 assert(node1 >= 0 && node1 < mcfnetwork->nnodes);
    5316 assert(node2 >= 0 && node2 < mcfnetwork->nnodes);
    5317
    5318 /* identify the representatives of the two nodes */
    5319 node1rep = nodepartitionGetRepresentative(*nodepartition, node1);
    5320 node2rep = nodepartitionGetRepresentative(*nodepartition, node2);
    5321 assert(0 <= node1rep && node1rep < mcfnetwork->nnodes);
    5322 assert(0 <= node2rep && node2rep < mcfnetwork->nnodes);
    5323
    5324 /* there is nothing to do if the two nodes are already in the same cluster */
    5325 if( node1rep == node2rep )
    5326 continue;
    5327
    5328 /* shrink nodepair by joining the two clusters */
    5329 SCIPdebugMsg(scip, "shrinking nodepair (%d,%d) with weight %g: join representatives %d and %d\n",
    5330 node1, node2, nodepair->weight, node1rep, node2rep);
    5331 nodepartitionJoin(*nodepartition, node1rep, node2rep);
    5332 nclustersleft--;
    5333 }
    5334
    5335 /* node 0 must be a representative due to our join procedure */
    5336 assert((*nodepartition)->representatives[0] == 0);
    5337
    5338 /* if there have been too few arcs to shrink the graph to the required number of clusters, join clusters with first cluster
    5339 * to create a larger disconnected cluster
    5340 */
    5341 if( nclustersleft > nclusters )
    5342 {
    5343 for( v = 1; v < mcfnetwork->nnodes && nclustersleft > nclusters; v++ )
    5344 {
    5345 int rep;
    5346
    5347 rep = nodepartitionGetRepresentative(*nodepartition, v);
    5348 if( rep != 0 )
    5349 {
    5350 nodepartitionJoin(*nodepartition, 0, rep);
    5351 nclustersleft--;
    5352 }
    5353 }
    5354 }
    5355 assert(nclustersleft <= nclusters);
    5356
    5357 /* extract the clusters */
    5358 SCIP_CALL( SCIPallocBufferArray(scip, &clustersize, nclusters) );
    5359 BMSclearMemoryArray(clustersize, nclusters);
    5360 for( v = 0; v < mcfnetwork->nnodes; v++ )
    5361 {
    5362 int rep;
    5363
    5364 /* get cluster of node */
    5365 rep = nodepartitionGetRepresentative(*nodepartition, v);
    5366 assert(rep <= v); /* due to our joining procedure */
    5367 if( rep == v )
    5368 {
    5369 /* node is its own representative: this is a new cluster */
    5370 c = (*nodepartition)->nclusters;
    5371 (*nodepartition)->nclusters++;
    5372 }
    5373 else
    5374 c = (*nodepartition)->nodeclusters[rep];
    5375 assert(0 <= c && c < nclusters);
    5376
    5377 /* assign node to cluster */
    5378 (*nodepartition)->nodeclusters[v] = c;
    5379 clustersize[c]++;
    5380 }
    5381
    5382 /* fill the clusterbegin array */
    5383 pos = 0;
    5384 for( c = 0; c < (*nodepartition)->nclusters; c++ )
    5385 {
    5386 (*nodepartition)->clusterbegin[c] = pos;
    5387 pos += clustersize[c];
    5388 }
    5389 assert(pos == mcfnetwork->nnodes);
    5390 (*nodepartition)->clusterbegin[(*nodepartition)->nclusters] = mcfnetwork->nnodes;
    5391
    5392 /* fill the clusternodes array */
    5393 BMSclearMemoryArray(clustersize, (*nodepartition)->nclusters);
    5394 for( v = 0; v < mcfnetwork->nnodes; v++ )
    5395 {
    5396 c = (*nodepartition)->nodeclusters[v];
    5397 assert(0 <= c && c < (*nodepartition)->nclusters);
    5398 pos = (*nodepartition)->clusterbegin[c] + clustersize[c];
    5399 assert(pos < (*nodepartition)->clusterbegin[c+1]);
    5400 (*nodepartition)->clusternodes[pos] = v;
    5401 clustersize[c]++;
    5402 }
    5403
    5404 /* free temporary memory */
    5405 SCIPfreeBufferArray(scip, &clustersize);
    5406
    5407 /* free nodepair queue */
    5408 nodepairqueueFree(scip, &nodepairqueue);
    5409
    5410 return SCIP_OKAY;
    5411}
    5412
    5413/** frees node partition data */
    5414static
    5416 SCIP* scip, /**< SCIP data structure */
    5417 NODEPARTITION** nodepartition /**< pointer to node partition data structure */
    5418 )
    5419{
    5420 assert(nodepartition != NULL);
    5421 assert(*nodepartition != NULL);
    5422
    5423 SCIPfreeBufferArray(scip, &(*nodepartition)->clusterbegin);
    5424 SCIPfreeBufferArray(scip, &(*nodepartition)->clusternodes);
    5425 SCIPfreeBufferArray(scip, &(*nodepartition)->nodeclusters);
    5426 SCIPfreeBufferArray(scip, &(*nodepartition)->representatives);
    5427 SCIPfreeBuffer(scip, nodepartition);
    5428}
    5429
    5430/** returns whether given node v is in a cluster that belongs to the partition S */
    5431static
    5433 NODEPARTITION* nodepartition, /**< node partition data structure, or NULL */
    5434 unsigned int partition, /**< partition of nodes, or node number in single-node partition */
    5435 SCIP_Bool inverted, /**< should partition be inverted? */
    5436 int v /**< node to check */
    5437 )
    5438{
    5439 /* if the node does not exist, it is not in the partition
    5440 * (and also not in the inverted partition)
    5441 */
    5442 if( v < 0 )
    5443 return FALSE;
    5444
    5445 if( nodepartition == NULL )
    5446 return ((v == (int)partition) == !inverted);
    5447 else
    5448 {
    5449 int cluster;
    5450 unsigned int clusterbit;
    5451
    5452 cluster = nodepartition->nodeclusters[v];
    5453 assert(0 <= cluster && cluster < nodepartition->nclusters);
    5454 clusterbit = (1 << cluster); /*lint !e701*/
    5455
    5456 return (((partition & clusterbit) != 0) == !inverted);
    5457 }
    5458}
    5459
    5460/** returns whether each of the sets S and T defined by the nodepartition is connected */
    5461static
    5463 SCIP* scip, /**< SCIP data structure */
    5464 SCIP_MCFNETWORK* mcfnetwork, /**< MCF network structure */
    5465 NODEPARTITION* nodepartition, /**< node partition data structure */
    5466 unsigned int partition /**< partition of nodes, or node number in single-node partition */
    5467 )
    5468{
    5469 const int* nodeclusters = nodepartition->nodeclusters;
    5470 const int* arcsources = mcfnetwork->arcsources;
    5471 const int* arctargets = mcfnetwork->arctargets;
    5472 int narcs = mcfnetwork->narcs;
    5473 int nclusters;
    5474
    5475 int ncomponents;
    5476 int a;
    5477 int* rep;
    5478
    5479 assert(nodepartition->nodeclusters != NULL);
    5480 nclusters = nodepartition->nclusters;
    5481
    5482 if( SCIPallocBufferArray(scip, &rep, nclusters) != SCIP_OKAY )
    5483 return 0;
    5484
    5485 /* start with each cluster being isolated */
    5486 unionfindInitSets(rep, nclusters);
    5487 ncomponents = nclusters;
    5488 assert(ncomponents >= 2);
    5489
    5490 /* for each arc within S or within T join the connected clusters */
    5491 for( a = 0; a < narcs; a++ )
    5492 {
    5493 int s = arcsources[a];
    5494 int t = arctargets[a];
    5495
    5496 /* ignore arcs that connect the pseudo node -1 */
    5497 if( s == -1 || t == -1 )
    5498 continue;
    5499
    5500 /* check if arc is within one of the components */
    5501 if( nodeInPartition(nodepartition, partition, FALSE, s) == nodeInPartition(nodepartition, partition, FALSE, t) )
    5502 {
    5503 int cs;
    5504 int ct;
    5505 int repcs;
    5506 int repct;
    5507
    5508 assert(0 <= s && s < mcfnetwork->nnodes);
    5509 assert(0 <= t && t < mcfnetwork->nnodes);
    5510
    5511 /* get clusters of the two nodes */
    5512 cs = nodeclusters[s];
    5513 ct = nodeclusters[t];
    5514 assert(0 <= cs && cs < nclusters);
    5515 assert(0 <= ct && ct < nclusters);
    5516
    5517 /* nothing to do if we are already in the same cluster */
    5518 if( cs == ct )
    5519 continue;
    5520
    5521 /* get representatives of clusters in the union structure */
    5522 repcs = unionfindGetRepresentative (rep, cs);
    5523 repct = unionfindGetRepresentative (rep, ct);
    5524 if( repcs == repct )
    5525 continue;
    5526
    5527 /* the arc connects two previously unconnected components of S or T */
    5528
    5529 /* check if we already reached two distinct components */
    5530 ncomponents--;
    5531 if( ncomponents <= 2 )
    5532 break;
    5533
    5534 /* join the two cluster sets and continue */
    5535 unionfindJoinSets (rep, repcs, repct);
    5536 }
    5537 }
    5538
    5539 /* each of the two shores S and T are connected if we were able to shrink the graph
    5540 into two components */
    5541 assert(ncomponents >= 2);
    5543 return (ncomponents == 2);
    5544}
    5545
    5546#ifdef SCIP_DEBUG
    5547static
    5548void nodepartitionPrint(
    5549 NODEPARTITION* nodepartition /**< node partition data structure */
    5550 )
    5551{
    5552 int c;
    5553
    5554 for( c = 0; c < nodepartition->nclusters; c++ )
    5555 {
    5556 int i;
    5557
    5558 MCFdebugMessage("cluster %d:", c);
    5559 for( i = nodepartition->clusterbegin[c]; i < nodepartition->clusterbegin[c+1]; i++ )
    5560 MCFdebugMessage(" %d", nodepartition->clusternodes[i]);
    5561 MCFdebugMessage("\n");
    5562 }
    5563}
    5564#endif
    5565
    5566#ifdef OUTPUTGRAPH
    5567/** generates a GML file to visualize the network graph and LP solution */
    5568static
    5569SCIP_RETCODE outputGraph(
    5570 SCIP* scip, /**< SCIP data structure */
    5571 SCIP_MCFNETWORK* mcfnetwork, /**< MCF network structure */
    5572 NODEPARTITION* nodepartition, /**< node partition data structure, or NULL */
    5573 SCIP_Bool inverted, /**< should partition be inverted? */
    5574 unsigned int partition /**< partition of nodes, or node number */
    5575 )
    5576{
    5577 FILE* file;
    5578 char filename[SCIP_MAXSTRLEN];
    5579 int v;
    5580 int a;
    5581
    5582 /* open file */
    5583 if( nodepartition == NULL )
    5584 (void) SCIPsnprintf(filename, SCIP_MAXSTRLEN, "mcf-node-%d.gml", partition);
    5585 else
    5586 (void) SCIPsnprintf(filename, SCIP_MAXSTRLEN, "mcf-part-%d.gml", partition);
    5587 SCIPinfoMessage(scip, NULL, "creating GML output file <%s>...\n", filename);
    5588 file = fopen(filename, "w");
    5589 if( file == NULL )
    5590 {
    5591 SCIPerrorMessage("cannot create GML output file <%s>\n", filename);
    5592 return SCIP_FILECREATEERROR;
    5593 }
    5594
    5595 /* print GML header */
    5596 fprintf(file, "graph\n");
    5597 fprintf(file, "[\n");
    5598 fprintf(file, " hierarchic 1\n");
    5599 fprintf(file, " label \"\"\n");
    5600 fprintf(file, " directed 1\n");
    5601
    5602 /* nodes */
    5603 for( v = 0; v < mcfnetwork->nnodes; v++ )
    5604 {
    5605 char label[SCIP_MAXSTRLEN];
    5606 SCIP_Bool inpartition;
    5607
    5608 if( mcfnetwork->nodeflowrows[v][0] != NULL )
    5609 (void) SCIPsnprintf(label, SCIP_MAXSTRLEN, "%s", SCIProwGetName(mcfnetwork->nodeflowrows[v][0]));
    5610 else
    5611 (void) SCIPsnprintf(label, SCIP_MAXSTRLEN, "%d", v);
    5612 inpartition = nodeInPartition(nodepartition, partition, inverted, v);
    5613
    5614 fprintf(file, " node\n");
    5615 fprintf(file, " [\n");
    5616 fprintf(file, " id %d\n", v);
    5617 fprintf(file, " label \"%s\"\n", label);
    5618 fprintf(file, " graphics\n");
    5619 fprintf(file, " [\n");
    5620 fprintf(file, " w 30.0\n");
    5621 fprintf(file, " h 30.0\n");
    5622 fprintf(file, " type \"ellipse\"\n");
    5623 if( inpartition )
    5624 fprintf(file, " fill \"#FF0000\"\n");
    5625 else
    5626 fprintf(file, " fill \"#00FF00\"\n");
    5627 fprintf(file, " outline \"#000000\"\n");
    5628 fprintf(file, " ]\n");
    5629 fprintf(file, " LabelGraphics\n");
    5630 fprintf(file, " [\n");
    5631 fprintf(file, " text \"%s\"\n", label);
    5632 fprintf(file, " fontSize 13\n");
    5633 fprintf(file, " fontName \"Dialog\"\n");
    5634 fprintf(file, " anchor \"c\"\n");
    5635 fprintf(file, " ]\n");
    5636 if( inpartition )
    5637 fprintf(file, " gid %d\n", mcfnetwork->nnodes+1);
    5638 else
    5639 fprintf(file, " gid %d\n", mcfnetwork->nnodes+2);
    5640 fprintf(file, " ]\n");
    5641 }
    5642
    5643 /* dummy node for missing arc sources or arc targets */
    5644 fprintf(file, " node\n");
    5645 fprintf(file, " [\n");
    5646 fprintf(file, " id %d\n", mcfnetwork->nnodes);
    5647 fprintf(file, " label \"?\"\n");
    5648 fprintf(file, " graphics\n");
    5649 fprintf(file, " [\n");
    5650 fprintf(file, " w 30.0\n");
    5651 fprintf(file, " h 30.0\n");
    5652 fprintf(file, " type \"ellipse\"\n");
    5653 fprintf(file, " fill \"#FFFFFF\"\n");
    5654 fprintf(file, " outline \"#000000\"\n");
    5655 fprintf(file, " ]\n");
    5656 fprintf(file, " LabelGraphics\n");
    5657 fprintf(file, " [\n");
    5658 fprintf(file, " text \"?\"\n");
    5659 fprintf(file, " fontSize 13\n");
    5660 fprintf(file, " fontName \"Dialog\"\n");
    5661 fprintf(file, " anchor \"c\"\n");
    5662 fprintf(file, " ]\n");
    5663 fprintf(file, " ]\n");
    5664
    5665 /* group node for partition S */
    5666 fprintf(file, " node\n");
    5667 fprintf(file, " [\n");
    5668 fprintf(file, " id %d\n", mcfnetwork->nnodes+1);
    5669 fprintf(file, " label \"Partition S\"\n");
    5670 fprintf(file, " graphics\n");
    5671 fprintf(file, " [\n");
    5672 fprintf(file, " type \"roundrectangle\"\n");
    5673 fprintf(file, " fill \"#CAECFF84\"\n");
    5674 fprintf(file, " outline \"#666699\"\n");
    5675 fprintf(file, " outlineStyle \"dotted\"\n");
    5676 fprintf(file, " topBorderInset 0\n");
    5677 fprintf(file, " bottomBorderInset 0\n");
    5678 fprintf(file, " leftBorderInset 0\n");
    5679 fprintf(file, " rightBorderInset 0\n");
    5680 fprintf(file, " ]\n");
    5681 fprintf(file, " LabelGraphics\n");
    5682 fprintf(file, " [\n");
    5683 fprintf(file, " text \"Partition S\"\n");
    5684 fprintf(file, " fill \"#99CCFF\"\n");
    5685 fprintf(file, " fontSize 15\n");
    5686 fprintf(file, " fontName \"Dialog\"\n");
    5687 fprintf(file, " alignment \"right\"\n");
    5688 fprintf(file, " autoSizePolicy \"node_width\"\n");
    5689 fprintf(file, " anchor \"t\"\n");
    5690 fprintf(file, " borderDistance 0.0\n");
    5691 fprintf(file, " ]\n");
    5692 fprintf(file, " isGroup 1\n");
    5693 fprintf(file, " ]\n");
    5694
    5695 /* group node for partition T */
    5696 fprintf(file, " node\n");
    5697 fprintf(file, " [\n");
    5698 fprintf(file, " id %d\n", mcfnetwork->nnodes+2);
    5699 fprintf(file, " label \"Partition T\"\n");
    5700 fprintf(file, " graphics\n");
    5701 fprintf(file, " [\n");
    5702 fprintf(file, " type \"roundrectangle\"\n");
    5703 fprintf(file, " fill \"#CAECFF84\"\n");
    5704 fprintf(file, " outline \"#666699\"\n");
    5705 fprintf(file, " outlineStyle \"dotted\"\n");
    5706 fprintf(file, " topBorderInset 0\n");
    5707 fprintf(file, " bottomBorderInset 0\n");
    5708 fprintf(file, " leftBorderInset 0\n");
    5709 fprintf(file, " rightBorderInset 0\n");
    5710 fprintf(file, " ]\n");
    5711 fprintf(file, " LabelGraphics\n");
    5712 fprintf(file, " [\n");
    5713 fprintf(file, " text \"Partition T\"\n");
    5714 fprintf(file, " fill \"#99CCFF\"\n");
    5715 fprintf(file, " fontSize 15\n");
    5716 fprintf(file, " fontName \"Dialog\"\n");
    5717 fprintf(file, " alignment \"right\"\n");
    5718 fprintf(file, " autoSizePolicy \"node_width\"\n");
    5719 fprintf(file, " anchor \"t\"\n");
    5720 fprintf(file, " borderDistance 0.0\n");
    5721 fprintf(file, " ]\n");
    5722 fprintf(file, " isGroup 1\n");
    5723 fprintf(file, " ]\n");
    5724
    5725 /* arcs */
    5726 for( a = 0; a < mcfnetwork->narcs; a++ )
    5727 {
    5728 SCIP_ROW* row;
    5729 SCIP_Real slack;
    5730 SCIP_Bool hasfractional;
    5731 char label[SCIP_MAXSTRLEN];
    5732
    5733 if( mcfnetwork->arccapacityrows[a] != NULL )
    5734 (void) SCIPsnprintf(label, SCIP_MAXSTRLEN, "%s", SCIProwGetName(mcfnetwork->arccapacityrows[a]));
    5735 else
    5736 (void) SCIPsnprintf(label, SCIP_MAXSTRLEN, "%d", a);
    5737
    5738 hasfractional = FALSE;
    5739 row = mcfnetwork->arccapacityrows[a];
    5740 if( row != NULL )
    5741 {
    5742 SCIP_COL** rowcols;
    5743 int rowlen;
    5744 int i;
    5745
    5746 slack = ABS(mcfnetwork->arccapacityscales[a]) * SCIPgetRowLPFeasibility(scip, row);
    5747 rowcols = SCIProwGetCols(row);
    5748 rowlen = SCIProwGetNLPNonz(row);
    5749 for( i = 0; i < rowlen; i++ )
    5750 {
    5751 SCIP_VAR* var;
    5752
    5753 var = SCIPcolGetVar(rowcols[i]);
    5755 {
    5756 hasfractional = TRUE;
    5757 break;
    5758 }
    5759 }
    5760 }
    5761 else
    5762 slack = SCIPinfinity(scip);
    5763
    5764 fprintf(file, " edge\n");
    5765 fprintf(file, " [\n");
    5766 fprintf(file, " source %d\n", mcfnetwork->arcsources[a] >= 0 ? mcfnetwork->arcsources[a] : mcfnetwork->nnodes);
    5767 fprintf(file, " target %d\n", mcfnetwork->arctargets[a] >= 0 ? mcfnetwork->arctargets[a] : mcfnetwork->nnodes);
    5768 fprintf(file, " label \"%s\"\n", label);
    5769 fprintf(file, " graphics\n");
    5770 fprintf(file, " [\n");
    5771 if( SCIPisFeasPositive(scip, slack) )
    5772 fprintf(file, " fill \"#000000\"\n");
    5773 else
    5774 fprintf(file, " fill \"#FF0000\"\n");
    5775 if( hasfractional )
    5776 fprintf(file, " style \"dashed\"\n");
    5777 fprintf(file, " width 1\n");
    5778 fprintf(file, " targetArrow \"standard\"\n");
    5779 fprintf(file, " ]\n");
    5780 fprintf(file, " LabelGraphics\n");
    5781 fprintf(file, " [\n");
    5782 fprintf(file, " text \"%s\"\n", label);
    5783 fprintf(file, " ]\n");
    5784 fprintf(file, " ]\n");
    5785 }
    5786
    5787 /* print GML footer */
    5788 fprintf(file, "]\n");
    5789
    5790 /* close file */
    5791 fclose(file);
    5792
    5793 return SCIP_OKAY;
    5794}
    5795#endif
    5796
    5797/** adds given cut to LP if violated */
    5798static
    5800 SCIP* scip, /**< SCIP data structure */
    5801 SCIP_SEPA* sepa, /**< separator */
    5802 SCIP_SEPADATA* sepadata, /**< separator data */
    5803 SCIP_SOL* sol, /**< the solution that should be separated, or NULL for LP solution */
    5804 SCIP_Real* cutcoefs, /**< coefficients of active variables in cut */
    5805 SCIP_Real cutrhs, /**< right hand side of cut */
    5806 int* cutinds, /**< problem indices of variables with non-zero coefficient */
    5807 int cutnnz, /**< number of non-zeros in cut */
    5808 SCIP_Bool cutislocal, /**< is the cut only locally valid? */
    5809 int cutrank, /**< rank of the cut */
    5810 int* ncuts, /**< pointer to count the number of added cuts */
    5811 SCIP_Bool* cutoff /**< pointer to store whether a cutoff was found */
    5812 )
    5813{
    5814 SCIP_ROW* cut;
    5815 char cutname[SCIP_MAXSTRLEN];
    5816 SCIP_VAR** vars;
    5817 int nvars;
    5818 int i;
    5819
    5820 /* variables for knapsack cover separation */
    5821 SCIP_VAR** cutvars;
    5822
    5823 assert(scip != NULL);
    5824 assert(sepadata != NULL);
    5825 assert(cutcoefs != NULL);
    5826 assert(ncuts != NULL);
    5827 assert(cutoff != NULL);
    5828
    5829 /* get active problem variables */
    5830 SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, NULL, NULL, NULL, NULL) );
    5831 assert(nvars == 0 || vars != NULL);
    5832
    5833 *cutoff = FALSE;
    5834
    5835 SCIP_CALL( SCIPallocBufferArray(scip, &cutvars, cutnnz) );
    5836
    5837 for( i = 0; i < cutnnz; ++i )
    5838 {
    5839 cutvars[i] = vars[cutinds[i]];
    5840 }
    5841
    5842 /* create the cut */
    5843 (void) SCIPsnprintf(cutname, SCIP_MAXSTRLEN, "mcf%" SCIP_LONGINT_FORMAT "_%d", SCIPgetNLPs(scip), *ncuts);
    5844 SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut, sepa, cutname, -SCIPinfinity(scip), cutrhs, cutislocal, FALSE,
    5845 sepadata->dynamiccuts) );
    5846
    5847 SCIP_CALL( SCIPaddVarsToRow(scip, cut, cutnnz, cutvars, cutcoefs) );
    5848
    5849 /* set cut rank */
    5850 SCIProwChgRank(cut, cutrank);
    5851
    5852 /* check efficacy */
    5853 assert(SCIPisCutEfficacious(scip, sol, cut));
    5854
    5855 SCIPdebugMsg(scip, " -> found MCF cut <%s>: rhs=%f, act=%f eff=%f rank=%d\n",
    5856 cutname, cutrhs, SCIPgetRowSolActivity(scip, cut, sol), SCIPgetCutEfficacy(scip, sol, cut), SCIProwGetRank(cut));
    5857 /*SCIPdebug( SCIP_CALL( SCIPprintRow(scip, cut, NULL) ) );*/
    5858
    5859 if( !cutislocal )
    5860 {
    5861 SCIP_CALL( SCIPaddPoolCut(scip, cut) );
    5862 }
    5863 else
    5864 {
    5865 SCIP_CALL( SCIPaddRow(scip, cut, FALSE, cutoff) );
    5866 }
    5867 (*ncuts)++;
    5868
    5869 /* release the row */
    5870 SCIP_CALL( SCIPreleaseRow(scip, &cut) );
    5871
    5872 if( !(*cutoff) && sepadata->separateknapsack)
    5873 {
    5874 /* relax cut to knapsack row and separate lifted cover cuts */
    5875 SCIP_CALL( SCIPseparateRelaxedKnapsack(scip, NULL, sepa, cutnnz, cutvars, cutcoefs, +1.0, cutrhs, sol, cutoff, ncuts) );
    5876 }
    5877 SCIPfreeBufferArray(scip, &cutvars);
    5878
    5879 return SCIP_OKAY;
    5880}
    5881
    5882/** enumerates cuts between subsets of the clusters
    5883 * generates single-node cuts if nodepartition == NULL, otherwise generates cluster cuts
    5884 */
    5885static
    5887 SCIP* scip, /**< SCIP data structure */
    5888 SCIP_SEPA* sepa, /**< separator */
    5889 SCIP_SEPADATA* sepadata, /**< separator data */
    5890 SCIP_SOL* sol, /**< the solution that should be separated, or NULL for LP solution */
    5891 SCIP_Bool allowlocal, /**< should local cuts be allowed */
    5892 int depth, /**< current depth */
    5893 SCIP_MCFNETWORK* mcfnetwork, /**< MCF network structure */
    5894 NODEPARTITION* nodepartition, /**< node partition data structure, or NULL */
    5895 int* ncuts, /**< pointer to count the number of added cuts */
    5896 SCIP_Bool* cutoff /**< pointer to store whether a cutoff was found */
    5897 )
    5898{
    5899 SCIP_ROW*** nodeflowrows = mcfnetwork->nodeflowrows;
    5900 SCIP_Real** nodeflowscales = mcfnetwork->nodeflowscales;
    5901 SCIP_Bool** nodeflowinverted = mcfnetwork->nodeflowinverted;
    5902 SCIP_ROW** arccapacityrows = mcfnetwork->arccapacityrows;
    5903 SCIP_Real* arccapacityscales = mcfnetwork->arccapacityscales;
    5904 int* colcommodity = mcfnetwork->colcommodity;
    5905 int* arcsources = mcfnetwork->arcsources;
    5906 int* arctargets = mcfnetwork->arctargets;
    5907 int nnodes = mcfnetwork->nnodes;
    5908 int narcs = mcfnetwork->narcs;
    5909 int ncommodities = mcfnetwork->ncommodities;
    5910 SCIP_MCFMODELTYPE modeltype = mcfnetwork->modeltype;
    5911 MCFEFFORTLEVEL effortlevel = sepadata->effortlevel;
    5912 int maxsepacuts;
    5913
    5914 int nrows;
    5915 int nvars;
    5916
    5917 SCIP_AGGRROW* aggrrow;
    5918 SCIP_Real* cutcoefs;
    5919 SCIP_Real* deltas;
    5920 int* cutinds;
    5921 int deltassize;
    5922 int ndeltas;
    5923 SCIP_Real* rowweights;
    5924 SCIP_Real* comcutdemands;
    5925 SCIP_Real* comdemands;
    5926 unsigned int partition;
    5927 unsigned int allpartitions;
    5928 unsigned int startpartition;
    5929 SCIP_Bool useinverted;
    5930 SCIP_Bool inverted;
    5931 int maxtestdelta;
    5932
    5933 int oldncuts = 0; /* to check success of separation for one nodeset */
    5934 *cutoff = FALSE;
    5935
    5936 assert( effortlevel == MCFEFFORTLEVEL_AGGRESSIVE || effortlevel == MCFEFFORTLEVEL_DEFAULT );
    5937 nrows = SCIPgetNLPRows(scip);
    5938 nvars = SCIPgetNVars(scip);
    5939
    5940 /* get the maximal number of cuts allowed in a separation round */
    5941 if( depth == 0 )
    5942 maxsepacuts = sepadata->maxsepacutsroot;
    5943 else
    5944 maxsepacuts = sepadata->maxsepacuts;
    5945 if( maxsepacuts < 0 )
    5946 maxsepacuts = INT_MAX;
    5947 else if( effortlevel == MCFEFFORTLEVEL_AGGRESSIVE )
    5948 maxsepacuts *= 2;
    5949
    5950 /* get the maximal number of deltas to use for cmir separation */
    5951 maxtestdelta = sepadata->maxtestdelta;
    5952 if( maxtestdelta <= 0 )
    5953 maxtestdelta = INT_MAX;
    5954 else if( effortlevel == MCFEFFORTLEVEL_AGGRESSIVE )
    5955 maxtestdelta *= 2;
    5956
    5957 /* Our system has the following form:
    5958 * (1) \sum_{a \in \delta^+(v)} f_a^k - \sum_{a \in \delta^-(v)} f_a^k <= -d_v^k for all v \in V and k \in K
    5959 * (2) \sum_{k \in K} f_a^k - c_a x_a <= 0 for all a \in A
    5960 *
    5961 * The partitioning yields two clusters:
    5962 *
    5963 * A^+
    5964 * cluster S ------> cluster T
    5965 * <------
    5966 * A^-
    5967 *
    5968 * Now we look at all commodities in which we have to route flow from T to S:
    5969 * K^+ = {k : d^k_S := sum_{v \in S} d_v^k > 0}
    5970 *
    5971 * Then, the base constraint of the c-MIR cut is the sum of those flow conservation constraints and the
    5972 * capacity constraints for arcs A^-:
    5973 *
    5974 * sum_{k \in K^+} sum_{v \in S} (sum_{a \in \delta^+(v)} f_a^k - sum_{a \in \delta^-(v)} f_a^k) <= sum_{k \in K^+} sum_{v \in S} -d_v^k
    5975 * + sum_{a \in A^-} sum_{k \in K} f_a^k - c_a x_a <= 0
    5976 */
    5977
    5978 deltassize = 16;
    5979 SCIP_CALL( SCIPallocBufferArray(scip, &deltas, deltassize) );
    5980 SCIP_CALL( SCIPallocBufferArray(scip, &rowweights, nrows) );
    5981 SCIP_CALL( SCIPallocBufferArray(scip, &comcutdemands, ncommodities) );
    5982 SCIP_CALL( SCIPallocBufferArray(scip, &comdemands, ncommodities) );
    5983 SCIP_CALL( SCIPallocBufferArray(scip, &cutcoefs, nvars) );
    5984 SCIP_CALL( SCIPallocBufferArray(scip, &cutinds, nvars) );
    5985
    5986 /* create empty aggregation row */
    5987 SCIP_CALL( SCIPaggrRowCreate(scip, &aggrrow) );
    5988
    5989 if( nodepartition == NULL )
    5990 {
    5991 /* loop over all nodes and generate single-node cuts */
    5992 startpartition = 0;
    5993 allpartitions = (unsigned int) nnodes;
    5994 SCIPdebugMsg(scip, "maxtestdelta: %d, maxsepacuts: %d, nnodes: %d \n", maxtestdelta, maxsepacuts, nnodes);
    5995 }
    5996 else
    5997 {
    5998 /* loop over all possible partitions of the clusters;
    5999 * cluster i is in S iff bit i of 'partition' is 1
    6000 */
    6001 int nclusters = nodepartition->nclusters;
    6002
    6003 assert((unsigned int)nclusters <= 8*sizeof(unsigned int));
    6004 SCIPdebugMsg(scip, "maxtestdelta: %d, maxsepacuts: %d, nclusters: %d \n", maxtestdelta, maxsepacuts, nclusters);
    6005
    6006 /* We fix the last cluster to belong to partition T. In the undirected case, this is sufficient,
    6007 * because S-T is equivalent to T-S. In the directed case, the loop below is conducted two times,
    6008 * one with regular S-T and one with inverted partitions.
    6009 */
    6010 startpartition = 1;
    6011 allpartitions = (1 << (nclusters-1)); /*lint !e701*/
    6012 }
    6013 useinverted = (mcfnetwork->modeltype == SCIP_MCFMODELTYPE_DIRECTED);
    6014
    6015 for( partition = startpartition; partition <= allpartitions-1 && !SCIPisStopped(scip) && *ncuts < maxsepacuts && !*cutoff; partition++ )
    6016 {
    6017 int v;
    6018 int a;
    6019 int k;
    6020 int d;
    6021 int nnodesinS;
    6022 SCIP_Bool success;
    6023 /* variables for flowcutset separation */
    6024 SCIP_Real baserhs;
    6025 SCIP_Real bestdelta = 1.0;
    6026 SCIP_Real bestefficacy;
    6027 SCIP_Real f0;
    6028
    6029 if( sepadata->checkcutshoreconnectivity )
    6030 {
    6031 if( nodepartition != NULL && !nodepartitionIsConnected(scip, mcfnetwork, nodepartition, partition ) )
    6032 {
    6033 /* if S or T are not connected, it is very likely that there is a cut in our cluster partition
    6034 that gives dominating inequalities
    6035 */
    6036 SCIPdebugMsg(scip, "generating cluster cuts for partition 0x%x \n", partition );
    6037 SCIPdebugMsg(scip, " -> either shore S or shore T is not connected - skip partition.\n");
    6038 continue;
    6039 }
    6040 }
    6041
    6042 for( inverted = FALSE; inverted <= useinverted && !*cutoff; inverted++ )
    6043 {
    6044 if( nodepartition == NULL )
    6045 {
    6046 SCIPdebugMsg(scip, "generating single-node cuts for node %u (inverted: %u)\n", partition, inverted);
    6047 }
    6048 else
    6049 {
    6050 SCIPdebugMsg(scip, "generating cluster cuts for partition 0x%x (inverted: %u)\n", partition, inverted);
    6051 }
    6052
    6053#ifdef OUTPUTGRAPH
    6054 SCIP_CALL( outputGraph(scip, mcfnetwork, nodepartition, inverted, partition) );
    6055#endif
    6056 /* Check if S and T are connected via union find algorithm.
    6057 * We do not check this for single node cuts. First, this can be expensive since
    6058 * in this case the graph still contains all nodes. Second, we may not find a dominating cut,
    6059 * because we may not have the corresponding dominating node set in our cluster partition.
    6060 */
    6061
    6062 /* clear memory */
    6063 BMSclearMemoryArray(rowweights, nrows);
    6064 BMSclearMemoryArray(comcutdemands, ncommodities);
    6065 BMSclearMemoryArray(comdemands, ncommodities);
    6066
    6067 baserhs = 0.0;
    6068 ndeltas = 0;
    6069
    6070 /* Identify commodities with positive T -> S demand */
    6071 nnodesinS = 0;
    6072 for( v = 0; v < nnodes; v++ )
    6073 {
    6074 /* check if node belongs to S */
    6075 if( !nodeInPartition(nodepartition, partition, inverted, v) )
    6076 {
    6077 /* node does not belong to S */
    6078 continue;
    6079 }
    6080 nnodesinS++;
    6081 /* update commodity demand */
    6082 for( k = 0; k < ncommodities; k++ )
    6083 {
    6084 SCIP_Real rhs;
    6085
    6086 if( nodeflowrows[v][k] == NULL )
    6087 continue;
    6088
    6089 if( nodeflowscales[v][k] > 0.0 )
    6090 rhs = SCIProwGetRhs(nodeflowrows[v][k]) - SCIProwGetConstant(nodeflowrows[v][k]);
    6091 else
    6092 rhs = SCIProwGetLhs(nodeflowrows[v][k]) - SCIProwGetConstant(nodeflowrows[v][k]);
    6093 if( nodeflowinverted[v][k] )
    6094 rhs *= -1.0;
    6095
    6096 comcutdemands[k] += rhs * nodeflowscales[v][k];
    6097 }
    6098 }
    6099 assert (1 <= nnodesinS && nnodesinS <= nnodes-1);
    6100
    6101 /* ignore cuts with only a single node in S or in T, since these have
    6102 * already been tried as single node cuts
    6103 */
    6104 if( sepadata->separatesinglenodecuts && nodepartition != NULL && (nnodesinS == 1 || nnodesinS == nnodes-1) )
    6105 {
    6106 SCIPdebugMsg(scip, " -> shore S or T has only one node - skip partition.\n");
    6107 break;
    6108 }
    6109
    6110 /* check if there is at least one useful commodity */
    6111 if( modeltype == SCIP_MCFMODELTYPE_DIRECTED )
    6112 {
    6113 for( k = 0; k < ncommodities; k++ )
    6114 {
    6115 /* in the directed case, use commodities with positive demand (negative -d_k) */
    6116 SCIPdebugMsg(scip, " -> commodity %d: directed cutdemand=%g\n", k, comcutdemands[k]);
    6117 if( SCIPisNegative(scip, comcutdemands[k]) )
    6118 break;
    6119 }
    6120 }
    6121 else
    6122 {
    6123 for( k = 0; k < ncommodities; k++ )
    6124 {
    6125 /* in the undirected case, use commodities with non-zero demand */
    6126 SCIPdebugMsg(scip, " -> commodity %d: undirected cutdemand=%g\n", k, comcutdemands[k]);
    6127 if( !SCIPisZero(scip, comcutdemands[k]) )
    6128 break;
    6129 }
    6130 }
    6131 if( k == ncommodities )
    6132 continue;
    6133
    6134 /* set weights of capacity rows that go from T to S, i.e., a \in A^- */
    6135 for( a = 0; a < narcs; a++ )
    6136 {
    6137 SCIP_COL** rowcols;
    6138 SCIP_Real* rowvals;
    6139 SCIP_Real feasibility;
    6140 int rowlen;
    6141 int r;
    6142 int j;
    6143
    6144 assert(arcsources[a] < nnodes);
    6145 assert(arctargets[a] < nnodes);
    6146
    6147 /* check if this is an arc of our cut */
    6148 if( modeltype == SCIP_MCFMODELTYPE_DIRECTED )
    6149 {
    6150 /* in the directed case, check if arc goes from T to S */
    6151 if( nodeInPartition(nodepartition, partition, inverted, arcsources[a]) ||
    6152 !nodeInPartition(nodepartition, partition, inverted, arctargets[a]) )
    6153 {
    6154 /* arc has source in S or target in T */
    6155 continue;
    6156 }
    6157 }
    6158 else
    6159 {
    6160 /* in the undirected case, check if the arc has endpoints in S and T */
    6161 if( nodeInPartition(nodepartition, partition, inverted, arcsources[a]) &&
    6162 nodeInPartition(nodepartition, partition, inverted, arctargets[a]) )
    6163 {
    6164 /* both endpoints are in S */
    6165 continue;
    6166 }
    6167 if( !nodeInPartition(nodepartition, partition, inverted, arcsources[a]) &&
    6168 !nodeInPartition(nodepartition, partition, inverted, arctargets[a]) )
    6169 {
    6170 /* both endpoints are in T */
    6171 continue;
    6172 }
    6173 }
    6174
    6175 /* arc might be uncapacitated */
    6176 if( arccapacityrows[a] == NULL )
    6177 continue;
    6178
    6179 /* use capacity row in c-MIR cut */
    6180 r = SCIProwGetLPPos(arccapacityrows[a]);
    6181 assert(r < nrows);
    6182 if( r == -1 ) /* row might have been removed from LP in the meantime */
    6183 continue;
    6184 assert(rowweights[r] == 0.0);
    6185
    6186 /* if one of the arc nodes is unknown, we only use the capacity row if it does not have slack,
    6187 * otherwise, we discard it if the slack is too large
    6188 */
    6189 feasibility = SCIPgetRowSolFeasibility(scip, arccapacityrows[a], sol);
    6190 if( arcsources[a] == -1 || arctargets[a] == -1 )
    6191 {
    6192 if( SCIPisFeasPositive(scip, feasibility) )
    6193 continue;
    6194 }
    6195 else
    6196 {
    6197 SCIP_Real maxcoef;
    6198
    6199 maxcoef = SCIPgetRowMaxCoef(scip, arccapacityrows[a]);
    6200 assert(maxcoef > 0.0);
    6201 if( SCIPisFeasGT(scip, feasibility/maxcoef, MAXCAPACITYSLACK) )
    6202 continue;
    6203 }
    6204
    6205 rowweights[r] = arccapacityscales[a];
    6206 SCIPdebugMsg(scip, " -> arc %d, r=%d, capacity row <%s>: weight=%g slack=%g dual=%g\n", a, r, SCIProwGetName(arccapacityrows[a]), rowweights[r],
    6207 SCIPgetRowFeasibility(scip, arccapacityrows[a]), SCIProwGetDualsol(arccapacityrows[a]));
    6208 /*SCIPdebug( SCIP_CALL( SCIPprintRow(scip, arccapacityrows[a], NULL) ) );*/
    6209
    6210 if( sepadata->separateflowcutset )
    6211 {
    6212 if( rowweights[r] > 0.0 )
    6213 baserhs += rowweights[r] * (SCIProwGetRhs(arccapacityrows[a]) - SCIProwGetConstant(arccapacityrows[a]));
    6214 else
    6215 baserhs += rowweights[r] * (SCIProwGetLhs(arccapacityrows[a]) - SCIProwGetConstant(arccapacityrows[a]));
    6216 }
    6217
    6218 /* extract useful deltas for c-MIR scaling and update the demand value for commodities (in binary flow model) */
    6219 rowcols = SCIProwGetCols(arccapacityrows[a]);
    6220 rowvals = SCIProwGetVals(arccapacityrows[a]);
    6221 rowlen = SCIProwGetNLPNonz(arccapacityrows[a]);
    6222 for( j = 0; j < rowlen; j++ )
    6223 {
    6224 SCIP_Real coef;
    6225 int c;
    6226
    6227 coef = rowvals[j] * arccapacityscales[a];
    6228 coef = ABS(coef);
    6229
    6230 /* update commodity demands */
    6231 c = SCIPcolGetLPPos(rowcols[j]);
    6232 assert(0 <= c && c < SCIPgetNLPCols(scip));
    6233 k = colcommodity[c];
    6234 if( k >= 0 )
    6235 comdemands[k] = coef;
    6236
    6237 /* insert coefficients of integer variables into deltas array */
    6238 /* coefficients should not be too small and not be too big */
    6239 if( !SCIPisZero(scip, 1.0 / coef) && !SCIPisFeasZero(scip, coef) && SCIPcolIsIntegral(rowcols[j]) )
    6240 {
    6241 SCIP_Bool exists;
    6242 int left;
    6243 int right;
    6244
    6245 /* binary search if we already know this coefficient */
    6246 exists = FALSE;
    6247 left = 0;
    6248 right = ndeltas-1;
    6249 while( left <= right )
    6250 {
    6251 int mid = (left+right)/2;
    6252 /* take deltas that are not too close */
    6253 if( REALABS( deltas[mid] / coef - 1.0 ) < 1e-03 ) /*lint !e771*/
    6254 {
    6255 exists = TRUE;
    6256 break;
    6257 }
    6258 else if( coef < deltas[mid] )
    6259 right = mid-1;
    6260 else
    6261 left = mid+1;
    6262 }
    6263
    6264 /* insert new candidate value */
    6265 if( !exists )
    6266 {
    6267 assert(right == left-1);
    6268 assert(ndeltas <= deltassize);
    6269 if( ndeltas == deltassize )
    6270 {
    6271 deltassize *= 2;
    6272 SCIP_CALL( SCIPreallocBufferArray(scip, &deltas, deltassize) );
    6273 }
    6274 if( left < ndeltas )
    6275 {
    6276 for( d = ndeltas; d > left; d-- )
    6277 deltas[d] = deltas[d-1];
    6278 }
    6279 deltas[left] = coef;
    6280 ndeltas++;
    6281 SCIPdebugMsg(scip, " -> new capacity %g considered as delta\n", coef);
    6282 }
    6283 }
    6284 }
    6285 }
    6286
    6287 /* set weights of node flow conservation constraints in c-MIR aggregation */
    6288 for( v = 0; v < nnodes; v++ )
    6289 {
    6290 /* aggregate flow conservation constraints of the 'active' commodities */
    6291 for( k = 0; k < ncommodities; k++ )
    6292 {
    6293 SCIP_Real scale;
    6294 int r;
    6295
    6296 /* if commodity was not hit by the capacity constraints of the cut in the graph, ignore the commodity */
    6297 if( comdemands[k] == 0.0 )
    6298 continue;
    6299
    6300 scale = comdemands[k];
    6301 if( modeltype == SCIP_MCFMODELTYPE_DIRECTED )
    6302 {
    6303 /* in the directed case, use rows of commodities with positive demand (negative -d_k) */
    6304 if( !SCIPisNegative(scip, comcutdemands[k]) )
    6305 continue;
    6306
    6307 /* check if node belongs to S */
    6308 if( !nodeInPartition(nodepartition, partition, inverted, v) )
    6309 {
    6310 /* node belongs to T */
    6311 continue;
    6312 }
    6313 }
    6314 else
    6315 {
    6316 /* in the undirected case, use rows of commodities with non-zero demand */
    6317 if( SCIPisZero(scip, comcutdemands[k]) )
    6318 continue;
    6319
    6320 /* If the demand (-d_k) is negative (i.e., points into the wrong direction), we use the flow
    6321 * in the opposite direction, i.e., sum over all nodes in T instead of S.
    6322 */
    6323 if( comcutdemands[k] > 0.0 )
    6324 {
    6325 /* check if node belongs to T */
    6326 if( nodeInPartition(nodepartition, partition, inverted, v) )
    6327 {
    6328 /* node belongs to S */
    6329 continue;
    6330 }
    6331 }
    6332 else
    6333 {
    6334 /* check if node belongs to S */
    6335 if( !nodeInPartition(nodepartition, partition, inverted, v) )
    6336 {
    6337 /* node belongs to T */
    6338 continue;
    6339 }
    6340 }
    6341 }
    6342 if( nodeflowrows[v][k] == NULL )
    6343 continue;
    6344
    6345 r = SCIProwGetLPPos(nodeflowrows[v][k]);
    6346 assert(r < nrows);
    6347 if( r >= 0 ) /* row might have been removed from LP in the meantime */
    6348 {
    6349 SCIP_Real feasibility;
    6350
    6351 assert(rowweights[r] == 0.0);
    6352
    6353 /* ignore rows with slack */
    6354 feasibility = SCIPgetRowSolFeasibility(scip, nodeflowrows[v][k], sol);
    6355 if( !SCIPisFeasPositive(scip, feasibility) )
    6356 {
    6357 rowweights[r] = scale * nodeflowscales[v][k];
    6358 if( nodeflowinverted[v][k] )
    6359 rowweights[r] *= -1.0;
    6360 SCIPdebugMsg(scip, " -> node %d, commodity %d, r=%d, flow row <%s>: scale=%g weight=%g slack=%g dual=%g\n",
    6361 v, k, r, SCIProwGetName(nodeflowrows[v][k]), scale, rowweights[r],
    6362 SCIPgetRowFeasibility(scip, nodeflowrows[v][k]), SCIProwGetDualsol(nodeflowrows[v][k]));
    6363 /*SCIPdebug( SCIP_CALL( SCIPprintRow(scip, nodeflowrows[v][k], NULL) ) );*/
    6364 if( sepadata->separateflowcutset )
    6365 {
    6366 if( nodeflowscales[v][k] > 0.0 )
    6367 baserhs += rowweights[r] * (SCIProwGetRhs(nodeflowrows[v][k]) - SCIProwGetConstant(nodeflowrows[v][k]));
    6368 else
    6369 baserhs += rowweights[r] * (SCIProwGetLhs(nodeflowrows[v][k]) - SCIProwGetConstant(nodeflowrows[v][k]));
    6370 }
    6371 }
    6372 }
    6373 }
    6374 }
    6375
    6376 /* try out deltas to generate c-MIR cuts: use larger deltas first */
    6377 /** @todo use only the best delta instead of generating all cuts ?? */
    6378
    6379 /* use best delta for flowcutset separation */
    6380 bestefficacy = SCIP_REAL_MIN;
    6381
    6382 if( sepadata->separateflowcutset )
    6383 {
    6384 if( ndeltas > 0 )
    6385 bestdelta = deltas[ndeltas-1]; /* if nothing else is found, use maxdelta */
    6386 }
    6387
    6388 oldncuts = *ncuts; /* save number of cuts */
    6389
    6390 SCIP_CALL( SCIPaggrRowSumRows(scip, aggrrow, rowweights, NULL, -1,
    6391 FALSE, allowlocal, 2, (int)MAXAGGRLEN(nvars), &success) );
    6392
    6393 if( success )
    6394 {
    6395 SCIPdebugMsg(scip, " -> found %d different deltas to try\n", ndeltas);
    6396 for( d = ndeltas-1; d >= 0 && d >= ndeltas-maxtestdelta; d-- )
    6397 {
    6398 SCIP_Real cutrhs = 0.0;
    6399 SCIP_Real cutefficacy = 0.0;
    6400 SCIP_Bool cutislocal = FALSE;
    6401 /* variables for flowcutset separation */
    6402 int cutrank;
    6403 int cutnnz;
    6404
    6405 /* we should not have too small deltas */
    6406 assert( !SCIPisFeasZero(scip, deltas[d]) );
    6407 /* we should not have too large deltas */
    6408 assert( !SCIPisZero(scip, 1.0/deltas[d]) );
    6409
    6410 SCIPdebugMsg(scip, "applying MIR with delta = %g\n", deltas[d]);
    6411 SCIP_CALL( SCIPcalcMIR(scip, sol, POSTPROCESS, BOUNDSWITCH, VARTYPEUSEVBDS, allowlocal, sepadata->fixintegralrhs, NULL, NULL, MINFRAC, MAXFRAC,
    6412 1.0/deltas[d], aggrrow, cutcoefs, &cutrhs, cutinds, &cutnnz, &cutefficacy, &cutrank, &cutislocal, &success) );
    6413 assert(allowlocal || !cutislocal);
    6414
    6415 /* // no success means row was too long or empty, there is a free
    6416 * // variable or for numerical reasons, it does not mean that the
    6417 * // cMIR cut was not violated */
    6418 if( ! success )
    6419 continue;
    6420
    6421 if( sepadata->separateflowcutset )
    6422 {
    6423 if( cutefficacy > bestefficacy )
    6424 {
    6425 bestdelta = deltas[d];
    6426 bestefficacy = cutefficacy;
    6427 }
    6428 }
    6429
    6430 if( SCIPisEfficacious(scip, cutefficacy) )
    6431 {
    6432 SCIPdebugMsg(scip, "success -> delta = %g -> rhs: %g, efficacy: %g\n", deltas[d], cutrhs, cutefficacy);
    6433 SCIP_CALL( addCut(scip, sepa, sepadata, sol, cutcoefs, cutrhs, cutinds, cutnnz, cutislocal, cutrank, ncuts, cutoff) );
    6434 if( *cutoff )
    6435 break;
    6436
    6437#ifdef SCIP_DEBUG
    6438 for( a = 0; a < narcs; a++ )
    6439 {
    6440 if( arccapacityrows[a] != NULL )
    6441 {
    6442 int r;
    6443
    6444 r = SCIProwGetLPPos(arccapacityrows[a]);
    6445 assert(r < nrows);
    6446 if( r >= 0 && rowweights[r] != 0.0 )
    6447 {
    6448 MCFdebugMessage(" -> arc %d, capacity row <%s>: weight=%g slack=%g prod=%g dual=%g\n", a,
    6449 SCIProwGetName(arccapacityrows[a]), rowweights[r],
    6450 SCIPgetRowFeasibility(scip, arccapacityrows[a]),
    6451 SCIPgetRowFeasibility(scip, arccapacityrows[a]) * arccapacityscales[a], SCIProwGetDualsol(arccapacityrows[a]));
    6452 }
    6453 }
    6454 }
    6455#endif
    6456 }
    6457 }
    6458 }
    6459
    6460 /* only separate flowcutset inequalities if no cutset inequalities have been found */
    6461 if( sepadata->separateflowcutset && oldncuts == *ncuts && !*cutoff )
    6462 {
    6463 /* try to separate flow cuts for the best delta */
    6464 f0 = SCIPfrac(scip, baserhs/bestdelta);
    6465 if( MINFRAC <= f0 && f0 <= MAXFRAC )
    6466 {
    6467 SCIP_Real onedivoneminsf0;
    6468 SCIP_Real totalviolationdelta;
    6469 totalviolationdelta = 0.0;
    6470 onedivoneminsf0 = 1.0/(1.0 - f0);
    6471 for( a = 0; a < narcs; a++ )
    6472 {
    6473 SCIP_COL** rowcols;
    6474 SCIP_Real* rowvals;
    6475 int rowlen;
    6476 SCIP_Real rowweight;
    6477 SCIP_Real rowlhs;
    6478 SCIP_Real rowrhs;
    6479 SCIP_Real rowconstant;
    6480 SCIP_Real violationdelta;
    6481 int r;
    6482 int j;
    6483
    6484 /* arc might be uncapacitated */
    6485 if( arccapacityrows[a] == NULL )
    6486 continue;
    6487
    6488 r = SCIProwGetLPPos(arccapacityrows[a]);
    6489
    6490 /* row might have been removed from LP in the meantime */
    6491 assert(r < nrows);
    6492 if( r == -1 )
    6493 continue;
    6494
    6495 /* ignore rows that are not in the aggregation */
    6496 if( rowweights[r] == 0.0 )
    6497 continue;
    6498
    6499 /* check if removing the capacity inequality will lead to a more violated MIR inequality:
    6500 * in a "perfect" MCF model, adding the capacity constraints means to cancel the flow
    6501 * variables of the capacity constraints and instead to add the capacity variables.
    6502 * Thus, removing it means to add the flow variables (with negative sign) and to remove
    6503 * the capacity variables.
    6504 * We assume that the right hand side of the scaled capacity inequality is integral (usually 0)
    6505 * and thus, the fractionality of the rhs of the base inequality does not change, hence the
    6506 * cut coefficients of all other involved variables do not change.
    6507 */
    6508 rowcols = SCIProwGetCols(arccapacityrows[a]);
    6509 rowvals = SCIProwGetVals(arccapacityrows[a]);
    6510 rowlen = SCIProwGetNLPNonz(arccapacityrows[a]);
    6511 rowweight = rowweights[r]/bestdelta;
    6512 rowlhs = SCIProwGetLhs(arccapacityrows[a]);
    6513 rowrhs = SCIProwGetRhs(arccapacityrows[a]);
    6514 rowconstant = SCIProwGetConstant(arccapacityrows[a]);
    6515 if( SCIPisInfinity(scip, rowrhs) || (!SCIPisInfinity(scip, -rowlhs) && rowweight < 0.0) )
    6516 violationdelta = rowweight * (rowlhs - rowconstant);
    6517 else
    6518 violationdelta = rowweight * (rowrhs - rowconstant);
    6519
    6520 for( j = 0; j < rowlen; j++ )
    6521 {
    6522 SCIP_VAR* var;
    6523 SCIP_Real coef;
    6524 SCIP_Real mircoef;
    6525 SCIP_Real solval;
    6526 int c;
    6527
    6528 coef = rowvals[j] * rowweight;
    6529
    6530 c = SCIPcolGetLPPos(rowcols[j]);
    6531 assert(0 <= c && c < SCIPgetNLPCols(scip));
    6532 var = SCIPcolGetVar(rowcols[j]);
    6533
    6534 /* variable is flow variable: if we are not using the capacity constraint, this
    6535 * would appear with negative coefficient in the base inequality instead of being canceled.
    6536 * variable is capacity variable: if we are not using the capacity constraint, this
    6537 * would not appear in the base inequality.
    6538 */
    6539 if( colcommodity[c] >= 0 )
    6540 coef *= -1.0;
    6541
    6542 if( SCIPvarIsIntegral(var) )
    6543 {
    6544 SCIP_Real fj;
    6545
    6546 fj = SCIPfrac(scip, coef);
    6547 if( fj <= f0 )
    6548 mircoef = SCIPfloor(scip, coef);
    6549 else
    6550 mircoef = SCIPfloor(scip, coef) + (fj - f0)*onedivoneminsf0;
    6551 }
    6552 else
    6553 {
    6554 if( coef >= 0.0 )
    6555 mircoef = 0.0;
    6556 else
    6557 mircoef = coef * onedivoneminsf0;
    6558 }
    6559
    6560 /* add flow variable MIR coefficients, and subtract capacity variable MIR coefficients */
    6561 solval = SCIPgetSolVal(scip, sol, var);
    6562 if( colcommodity[c] >= 0 )
    6563 violationdelta += mircoef * solval;
    6564 else
    6565 violationdelta -= mircoef * solval;
    6566 }
    6567
    6568 if( SCIPisPositive(scip, violationdelta) )
    6569 {
    6570 SCIPdebugMsg(scip, " -> discarding capacity row <%s> of weight %g and slack %g: increases MIR violation by %g\n",
    6571 SCIProwGetName(arccapacityrows[a]), rowweights[r], SCIPgetRowFeasibility(scip, arccapacityrows[a]),
    6572 violationdelta);
    6573 rowweights[r] = 0.0;
    6574 totalviolationdelta += violationdelta;
    6575 }
    6576 }
    6577
    6578 /* if we removed a capacity constraint from the aggregation, try the new aggregation */
    6579 if( totalviolationdelta > 0.0 )
    6580 {
    6581 SCIP_Real cutrhs;
    6582 SCIP_Real cutefficacy = 0.0;
    6583 SCIP_Bool cutislocal;
    6584 int cutnnz;
    6585 int cutrank;
    6586
    6587 /* we should not have too small deltas */
    6588 assert( !SCIPisFeasZero(scip, bestdelta) );
    6589 /* we should not have too large deltas */
    6590 assert( !SCIPisZero(scip, 1.0/bestdelta) );
    6591
    6592 SCIPdebugMsg(scip, "applying MIR with delta = %g to flowcut inequality (violation improvement: %g)\n", bestdelta, totalviolationdelta);
    6593
    6594 SCIP_CALL( SCIPaggrRowSumRows(scip, aggrrow, rowweights, NULL, -1,
    6595 FALSE, allowlocal, 2, (int)MAXAGGRLEN(nvars), &success) );
    6596
    6597 if( success )
    6598 {
    6599 SCIP_CALL( SCIPcalcMIR(scip, sol, POSTPROCESS, BOUNDSWITCH, VARTYPEUSEVBDS, allowlocal, sepadata->fixintegralrhs, NULL, NULL, MINFRAC, MAXFRAC,
    6600 1.0/bestdelta, aggrrow, cutcoefs, &cutrhs, cutinds, &cutnnz, &cutefficacy, &cutrank, &cutislocal, &success) );
    6601
    6602 assert(allowlocal || !cutislocal);
    6603
    6604 if( success && SCIPisEfficacious(scip, cutefficacy) )
    6605 {
    6606 SCIPdebugMsg(scip, " -> delta = %g -> rhs: %g, efficacy: %g\n", bestdelta, cutrhs, cutefficacy);
    6607 SCIP_CALL( addCut(scip, sepa, sepadata, sol, cutcoefs, cutrhs, cutinds, cutnnz, cutislocal, cutrank, ncuts, cutoff) );
    6608 }
    6609 }
    6610 }
    6611 }
    6612 }
    6613 }
    6614 }
    6615
    6616 /* free local memory */
    6617 SCIPaggrRowFree(scip, &aggrrow);
    6618 SCIPfreeBufferArray(scip, &cutinds);
    6619 SCIPfreeBufferArray(scip, &cutcoefs);
    6620 SCIPfreeBufferArray(scip, &comdemands);
    6621 SCIPfreeBufferArray(scip, &comcutdemands);
    6622 SCIPfreeBufferArray(scip, &rowweights);
    6623 SCIPfreeBufferArray(scip, &deltas);
    6624
    6625 return SCIP_OKAY;
    6626}
    6627
    6628/** searches and adds MCF network cuts that separate the given primal solution */
    6629static
    6631 SCIP* scip, /**< SCIP data structure */
    6632 SCIP_SEPA* sepa, /**< the cut separator itself */
    6633 SCIP_SOL* sol, /**< primal solution that should be separated, or NULL for LP solution */
    6634 SCIP_Bool allowlocal, /**< should local cuts be allowed */
    6635 int depth, /**< current depth */
    6636 SCIP_RESULT* result /**< pointer to store the result of the separation call */
    6637 )
    6638{
    6639 /*lint --e{715}*/
    6640 SCIP_SEPADATA* sepadata;
    6641 SCIP_MCFNETWORK** mcfnetworks;
    6642 int nmcfnetworks;
    6643 int ncuts;
    6644 int ncols;
    6645 int nrows;
    6646 int nvars;
    6647 SCIP_Real colrowratio;
    6648 int i;
    6649 SCIP_Bool cutoff;
    6650
    6651 assert(result != NULL);
    6652 assert(*result == SCIP_DIDNOTRUN);
    6653
    6654 ncuts = 0;
    6655 cutoff = FALSE;
    6656
    6657 /* check for number of columns, number of variables, column/row ratio */
    6658 nrows = SCIPgetNLPRows(scip);
    6659 ncols = SCIPgetNLPCols(scip);
    6660 nvars = SCIPgetNVars(scip);
    6661
    6662 /* exit if not all variables are in the LP (e.g. dynamic columns) */
    6663 /* Notice that in principle we should have most of the columns in the LP to be able to detect a structure */
    6664 if( ncols != nvars )
    6665 {
    6666 MCFdebugMessage("%d variables but %d columns -> exit\n", nvars, ncols );
    6667
    6668 return SCIP_OKAY;
    6669 }
    6670
    6671 /* exit if number of columns is too large (expensive detection) */
    6672 if( ncols > MAXCOLS )
    6673 {
    6674 MCFdebugMessage("%d > %d columns -> exit\n", ncols, MAXCOLS );
    6675
    6676 return SCIP_OKAY;
    6677 }
    6678
    6679 colrowratio = (SCIP_Real)ncols/(nrows+1e-9);
    6680
    6681 /* get separator data */
    6682 sepadata = SCIPsepaGetData(sepa);
    6683 assert(sepadata != NULL);
    6684
    6685 /* if separation was not delayed before and we had no success in previous round then delay the separation*/
    6686 if( !SCIPsepaWasLPDelayed(sepa) && !sepadata->lastroundsuccess )
    6687 {
    6688 *result = SCIP_DELAYED;
    6689 return SCIP_OKAY;
    6690 }
    6691
    6692 if( colrowratio < MINCOLROWRATIO || colrowratio > MAXCOLROWRATIO )
    6693 {
    6694 MCFdebugMessage("%d columns, %d rows, ratio %g is not in [%g,%g] -> exit\n", ncols, nrows, colrowratio, MINCOLROWRATIO, MAXCOLROWRATIO);
    6695
    6696 return SCIP_OKAY;
    6697 }
    6698
    6699 /* ######################## NETWORK DETECTION ##################################### */
    6700 /* get or extract network flow structure */
    6701 if( sepadata->nmcfnetworks == -1 )
    6702 {
    6703 *result = SCIP_DIDNOTFIND;
    6704
    6705 SCIP_CALL( mcfnetworkExtract(scip, sepadata, &sepadata->mcfnetworks, &sepadata->nmcfnetworks, &sepadata->effortlevel) );
    6706
    6707 MCFdebugMessage("extracted %d networks\n", sepadata->nmcfnetworks);
    6708
    6709 for( i = 0; i < sepadata->nmcfnetworks; i++ )
    6710 {
    6711 MCFdebugMessage(" -> extracted network %d has %d nodes, %d (%d) arcs (uncapacitated), and %d commodities (modeltype: %s)\n",
    6712 i, sepadata->mcfnetworks[i]->nnodes, sepadata->mcfnetworks[i]->narcs, sepadata->mcfnetworks[i]->nuncapacitatedarcs,
    6713 sepadata->mcfnetworks[i]->ncommodities,
    6714 sepadata->mcfnetworks[i]->modeltype == SCIP_MCFMODELTYPE_DIRECTED ? "directed" : "undirected");
    6715 SCIPdebug( mcfnetworkPrint(sepadata->mcfnetworks[i]) );
    6716 }
    6717
    6718#ifdef COUNTNETWORKVARIABLETYPES
    6719 SCIP_CALL( printFlowSystemInfo(scip,sepadata->mcfnetworks,sepadata->nmcfnetworks) );
    6720#endif
    6721 }
    6722 assert(sepadata->nmcfnetworks >= 0);
    6723 assert(sepadata->mcfnetworks != NULL || sepadata->nmcfnetworks == 0);
    6724 mcfnetworks = sepadata->mcfnetworks;
    6725 nmcfnetworks = sepadata->nmcfnetworks;
    6726
    6727 /* ######################## SEPARATION ##################################### */
    6728 if( nmcfnetworks > 0 && sepadata->effortlevel != MCFEFFORTLEVEL_OFF )
    6729 {
    6730 /* separate cuts */
    6731 *result = SCIP_DIDNOTFIND;
    6732 sepadata->lastroundsuccess = FALSE;
    6733
    6734 for( i = 0; i < nmcfnetworks && !cutoff; i++ )
    6735 {
    6736 SCIP_MCFNETWORK* mcfnetwork;
    6737 NODEPARTITION* nodepartition;
    6738 SCIP_Real arcnoderatio;
    6739
    6740 mcfnetwork = mcfnetworks[i];
    6741
    6742 /* if the network does not have at least 2 nodes and 1 arc, we did not create it */
    6743 assert(mcfnetwork->nnodes >= 2);
    6744 assert(mcfnetwork->narcs >= 1);
    6745
    6746 arcnoderatio = (SCIP_Real)mcfnetwork->narcs / (SCIP_Real)mcfnetwork->nnodes;
    6747
    6748 /* do not allow networks exceeding the arcs/nodes ratio ( = average node degree / 2 (directed)) */
    6749 if( arcnoderatio > MAXARCNODERATIO )
    6750 {
    6751 MCFdebugMessage("MCF network has %d nodes and %d arcs. arc node ratio %.2f exceed --> exit\n",
    6752 mcfnetwork->nnodes, mcfnetwork->narcs, MAXARCNODERATIO);
    6753 continue;
    6754 }
    6755
    6756 /* enumerate single node cuts */
    6757 if( sepadata->separatesinglenodecuts )
    6758 {
    6759 SCIP_CALL( generateClusterCuts(scip, sepa, sepadata, sol, allowlocal, depth, mcfnetwork, NULL, &ncuts, &cutoff) );
    6760 }
    6761
    6762 if( !cutoff )
    6763 {
    6764 /* partition nodes into a small number of clusters */
    6765 SCIP_CALL( nodepartitionCreate(scip, mcfnetwork, &nodepartition,
    6766 sepadata->effortlevel == MCFEFFORTLEVEL_DEFAULT ? sepadata->nclusters : 2 * sepadata->nclusters) );
    6767#ifdef SCIP_DEBUG
    6768 nodepartitionPrint(nodepartition);
    6769#endif
    6770
    6771 /* enumerate cuts between subsets of the clusters */
    6772 SCIP_CALL( generateClusterCuts(scip, sepa, sepadata, sol, allowlocal, depth, mcfnetwork, nodepartition, &ncuts, &cutoff) );
    6773
    6774 /* free node partition */
    6775 nodepartitionFree(scip, &nodepartition);
    6776 }
    6777
    6778 MCFdebugMessage("MCF network has %d nodes, %d arcs, %d commodities. Found %d MCF network cuts, cutoff = %u.\n",
    6779 mcfnetwork->nnodes, mcfnetwork->narcs, mcfnetwork->ncommodities, ncuts, cutoff);
    6780
    6781 /* adjust result code */
    6782 if( cutoff )
    6783 {
    6784 *result = SCIP_CUTOFF;
    6785 sepadata->lastroundsuccess = TRUE;
    6786 }
    6787 else if( ncuts > 0 )
    6788 {
    6789 *result = SCIP_SEPARATED;
    6790 sepadata->lastroundsuccess = TRUE;
    6791 }
    6792 }
    6793 }
    6794
    6795 return SCIP_OKAY;
    6796}
    6797
    6798
    6799/*
    6800 * Callback methods of separator
    6801 */
    6802
    6803/** copy method for separator plugins (called when SCIP copies plugins) */
    6804static
    6806{ /*lint --e{715}*/
    6807 assert(scip != NULL);
    6808 assert(sepa != NULL);
    6809 assert(strcmp(SCIPsepaGetName(sepa), SEPA_NAME) == 0);
    6810
    6811 /* call inclusion method of constraint handler */
    6813
    6814 return SCIP_OKAY;
    6815}
    6816
    6817/** destructor of separator to free user data (called when SCIP is exiting) */
    6818static
    6820{
    6821 /*lint --e{715}*/
    6822 SCIP_SEPADATA* sepadata;
    6823
    6824 /* free separator data */
    6825 sepadata = SCIPsepaGetData(sepa);
    6826 assert(sepadata != NULL);
    6827 assert(sepadata->mcfnetworks == NULL);
    6828 assert(sepadata->nmcfnetworks == -1);
    6829
    6830 SCIPfreeMemory(scip, &sepadata);
    6831
    6832 SCIPsepaSetData(sepa, NULL);
    6833
    6834 return SCIP_OKAY;
    6835}
    6836
    6837/** solving process initialization method of separator (called when branch and bound process is about to begin) */
    6838static
    6840{
    6841 /*lint --e{715}*/
    6842 SCIP_SEPADATA* sepadata;
    6843
    6844 /* get separator data */
    6845 sepadata = SCIPsepaGetData(sepa);
    6846 assert(sepadata != NULL);
    6847
    6848 sepadata->lastroundsuccess = TRUE;
    6849 sepadata->effortlevel = MCFEFFORTLEVEL_OFF;
    6850
    6851 return SCIP_OKAY;
    6852}
    6853
    6854
    6855/** solving process deinitialization method of separator (called before branch and bound process data is freed) */
    6856static
    6858{
    6859 /*lint --e{715}*/
    6860 SCIP_SEPADATA* sepadata;
    6861 int i;
    6862
    6863 /* get separator data */
    6864 sepadata = SCIPsepaGetData(sepa);
    6865 assert(sepadata != NULL);
    6866
    6867 /* free MCF networks */
    6868 for( i = 0; i < sepadata->nmcfnetworks; i++ )
    6869 {
    6870 SCIP_CALL( mcfnetworkFree(scip, &sepadata->mcfnetworks[i]) );
    6871 }
    6872 SCIPfreeMemoryArrayNull(scip, &sepadata->mcfnetworks);
    6873 sepadata->nmcfnetworks = -1;
    6874
    6875 return SCIP_OKAY;
    6876}
    6877
    6878
    6879/** LP solution separation method of separator */
    6880static
    6882{
    6883 /*lint --e{715}*/
    6884
    6885 *result = SCIP_DIDNOTRUN;
    6886
    6887 /* only call separator, if we are not close to terminating */
    6888 if( SCIPisStopped(scip) )
    6889 return SCIP_OKAY;
    6890
    6891 /* only call separator, if an optimal LP solution is at hand */
    6893 return SCIP_OKAY;
    6894
    6895 /* only call separator, if there are fractional variables */
    6896 if( SCIPgetNLPBranchCands(scip) == 0 )
    6897 return SCIP_OKAY;
    6898
    6899 /* separate cuts on the LP solution */
    6900 SCIP_CALL( separateCuts(scip, sepa, NULL, allowlocal, depth, result) );
    6901
    6902 return SCIP_OKAY;
    6903}
    6904
    6905
    6906/** arbitrary primal solution separation method of separator */
    6907static
    6909{
    6910 /*lint --e{715}*/
    6911
    6912 *result = SCIP_DIDNOTRUN;
    6913
    6914 /* separate cuts on the given primal solution */
    6915 SCIP_CALL( separateCuts(scip, sepa, sol, allowlocal, depth, result) );
    6916
    6917 return SCIP_OKAY;
    6918}
    6919
    6920
    6921/*
    6922 * separator specific interface methods
    6923 */
    6924
    6925/** creates the mcf separator and includes it in SCIP */
    6927 SCIP* scip /**< SCIP data structure */
    6928 )
    6929{
    6930 SCIP_SEPADATA* sepadata;
    6931 SCIP_SEPA* sepa;
    6932
    6933 /* create mcf separator data */
    6934 SCIP_CALL( SCIPallocMemory(scip, &sepadata) );
    6935 sepadata->mcfnetworks = NULL;
    6936 sepadata->nmcfnetworks = -1;
    6937
    6938 sepadata->lastroundsuccess = TRUE;
    6939 sepadata->effortlevel = MCFEFFORTLEVEL_OFF;
    6940
    6941 /* include separator */
    6944 sepaExeclpMcf, sepaExecsolMcf,
    6945 sepadata) );
    6946
    6947 assert(sepa != NULL);
    6948
    6949 /* set non-NULL pointers to callback methods */
    6950 SCIP_CALL( SCIPsetSepaCopy(scip, sepa, sepaCopyMcf) );
    6951 SCIP_CALL( SCIPsetSepaFree(scip, sepa, sepaFreeMcf) );
    6952 SCIP_CALL( SCIPsetSepaInitsol(scip, sepa, sepaInitsolMcf) );
    6953 SCIP_CALL( SCIPsetSepaExitsol(scip, sepa, sepaExitsolMcf) );
    6954
    6955 /** @todo introduce parameters such as maxrounds (see other separators) */
    6956 /* add mcf separator parameters */
    6958 "separating/mcf/nclusters",
    6959 "number of clusters to generate in the shrunken network -- default separation",
    6960 &sepadata->nclusters, TRUE, DEFAULT_NCLUSTERS, 2, (int) (8*sizeof(unsigned int)), NULL, NULL) );
    6962 "separating/mcf/maxweightrange",
    6963 "maximal valid range max(|weights|)/min(|weights|) of row weights",
    6964 &sepadata->maxweightrange, TRUE, DEFAULT_MAXWEIGHTRANGE, 1.0, SCIP_REAL_MAX, NULL, NULL) );
    6966 "separating/mcf/maxtestdelta",
    6967 "maximal number of different deltas to try (-1: unlimited) -- default separation",
    6968 &sepadata->maxtestdelta, TRUE, DEFAULT_MAXTESTDELTA, -1, INT_MAX, NULL, NULL) );
    6970 "separating/mcf/trynegscaling",
    6971 "should negative values also be tested in scaling?",
    6972 &sepadata->trynegscaling, TRUE, DEFAULT_TRYNEGSCALING, NULL, NULL) );
    6974 "separating/mcf/fixintegralrhs",
    6975 "should an additional variable be complemented if f0 = 0?",
    6976 &sepadata->fixintegralrhs, TRUE, DEFAULT_FIXINTEGRALRHS, NULL, NULL) );
    6978 "separating/mcf/dynamiccuts",
    6979 "should generated cuts be removed from the LP if they are no longer tight?",
    6980 &sepadata->dynamiccuts, FALSE, DEFAULT_DYNAMICCUTS, NULL, NULL) );
    6982 "separating/mcf/modeltype",
    6983 "model type of network (0: auto, 1:directed, 2:undirected)",
    6984 &sepadata->modeltype, TRUE, DEFAULT_MODELTYPE, 0, 2, NULL, NULL) );
    6986 "separating/mcf/maxsepacuts",
    6987 "maximal number of mcf cuts separated per separation round",
    6988 &sepadata->maxsepacuts, FALSE, DEFAULT_MAXSEPACUTS, -1, INT_MAX, NULL, NULL) );
    6990 "separating/mcf/maxsepacutsroot",
    6991 "maximal number of mcf cuts separated per separation round in the root node -- default separation",
    6992 &sepadata->maxsepacutsroot, FALSE, DEFAULT_MAXSEPACUTSROOT, -1, INT_MAX, NULL, NULL) );
    6994 "separating/mcf/maxinconsistencyratio",
    6995 "maximum inconsistency ratio for separation at all",
    6996 &sepadata->maxinconsistencyratio, TRUE, DEFAULT_MAXINCONSISTENCYRATIO, 0.0, SCIP_REAL_MAX, NULL, NULL) );
    6998 "separating/mcf/maxarcinconsistencyratio",
    6999 "maximum inconsistency ratio of arcs not to be deleted",
    7000 &sepadata->maxarcinconsistencyratio, TRUE, DEFAULT_MAXARCINCONSISTENCYRATIO, 0.0, SCIP_REAL_MAX, NULL, NULL) );
    7002 "separating/mcf/checkcutshoreconnectivity",
    7003 "should we separate only if the cuts shores are connected?",
    7004 &sepadata->checkcutshoreconnectivity, TRUE, DEFAULT_CHECKCUTSHORECONNECTIVITY, NULL, NULL) );
    7006 "separating/mcf/separatesinglenodecuts",
    7007 "should we separate inequalities based on single-node cuts?",
    7008 &sepadata->separatesinglenodecuts, TRUE, DEFAULT_SEPARATESINGLENODECUTS, NULL, NULL) );
    7010 "separating/mcf/separateflowcutset",
    7011 "should we separate flowcutset inequalities on the network cuts?",
    7012 &sepadata->separateflowcutset, TRUE, DEFAULT_SEPARATEFLOWCUTSET, NULL, NULL) );
    7014 "separating/mcf/separateknapsack",
    7015 "should we separate knapsack cover inequalities on the network cuts?",
    7016 &sepadata->separateknapsack, TRUE, DEFAULT_SEPARATEKNAPSACK, NULL, NULL) );
    7017
    7018 return SCIP_OKAY;
    7019}
    SCIP_VAR * a
    Definition: circlepacking.c:66
    SCIP_Real * r
    Definition: circlepacking.c:59
    Constraint handler for knapsack constraints of the form , x binary and .
    methods for the aggregation rows
    #define NULL
    Definition: def.h:248
    #define SCIP_MAXSTRLEN
    Definition: def.h:269
    #define SCIP_REAL_MAX
    Definition: def.h:158
    #define SCIP_INVALID
    Definition: def.h:178
    #define SCIP_Bool
    Definition: def.h:91
    #define MIN(x, y)
    Definition: def.h:224
    #define SCIP_Real
    Definition: def.h:156
    #define ABS(x)
    Definition: def.h:216
    #define TRUE
    Definition: def.h:93
    #define FALSE
    Definition: def.h:94
    #define MAX(x, y)
    Definition: def.h:220
    #define SCIP_LONGINT_FORMAT
    Definition: def.h:148
    #define SCIPABORT()
    Definition: def.h:327
    #define SCIP_REAL_MIN
    Definition: def.h:159
    #define REALABS(x)
    Definition: def.h:182
    #define SCIP_CALL(x)
    Definition: def.h:355
    #define nnodes
    Definition: gastrans.c:74
    #define narcs
    Definition: gastrans.c:77
    SCIP_RETCODE SCIPseparateRelaxedKnapsack(SCIP *scip, SCIP_CONS *cons, SCIP_SEPA *sepa, int nknapvars, SCIP_VAR **knapvars, SCIP_Real *knapvals, SCIP_Real valscale, SCIP_Real rhs, SCIP_SOL *sol, SCIP_Bool *cutoff, int *ncuts)
    SCIP_Bool SCIPisStopped(SCIP *scip)
    Definition: scip_general.c:759
    SCIP_RETCODE SCIPgetVarsData(SCIP *scip, SCIP_VAR ***vars, int *nvars, int *nbinvars, int *nintvars, int *nimplvars, int *ncontvars)
    Definition: scip_prob.c:2115
    int SCIPgetNVars(SCIP *scip)
    Definition: scip_prob.c:2246
    void SCIPhashtableFree(SCIP_HASHTABLE **hashtable)
    Definition: misc.c:2348
    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
    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 SCIPpqueueFree(SCIP_PQUEUE **pqueue)
    Definition: misc.c:1324
    SCIP_RETCODE SCIPpqueueInsert(SCIP_PQUEUE *pqueue, void *elem)
    Definition: misc.c:1396
    SCIP_RETCODE SCIPpqueueCreate(SCIP_PQUEUE **pqueue, int initsize, SCIP_Real sizefac, SCIP_DECL_SORTPTRCOMP((*ptrcomp)), SCIP_DECL_PQUEUEELEMCHGPOS((*elemchgpos)))
    Definition: misc.c:1297
    void * SCIPpqueueRemove(SCIP_PQUEUE *pqueue)
    Definition: misc.c:1495
    void * SCIPpqueueFirst(SCIP_PQUEUE *pqueue)
    Definition: misc.c:1515
    int SCIPgetNLPBranchCands(SCIP *scip)
    Definition: scip_branch.c:436
    int SCIPcolGetLPPos(SCIP_COL *col)
    Definition: lp.c:17487
    SCIP_VAR * SCIPcolGetVar(SCIP_COL *col)
    Definition: lp.c:17425
    SCIP_Bool SCIPcolIsIntegral(SCIP_COL *col)
    Definition: lp.c:17455
    SCIP_Bool SCIPcolIsImpliedIntegral(SCIP_COL *col)
    Definition: lp.c:17466
    SCIP_Real * SCIPcolGetVals(SCIP_COL *col)
    Definition: lp.c:17555
    SCIP_ROW ** SCIPcolGetRows(SCIP_COL *col)
    Definition: lp.c:17545
    SCIP_Real SCIPcolGetPrimsol(SCIP_COL *col)
    Definition: lp.c:17379
    int SCIPcolGetNLPNonz(SCIP_COL *col)
    Definition: lp.c:17534
    SCIP_RETCODE SCIPaddPoolCut(SCIP *scip, SCIP_ROW *row)
    Definition: scip_cut.c:336
    SCIP_Real SCIPgetCutEfficacy(SCIP *scip, SCIP_SOL *sol, SCIP_ROW *cut)
    Definition: scip_cut.c:94
    SCIP_RETCODE SCIPcalcMIR(SCIP *scip, SCIP_SOL *sol, SCIP_Bool postprocess, SCIP_Real boundswitch, int vartypeusevbds, SCIP_Bool allowlocal, SCIP_Bool fixintegralrhs, int *boundsfortrans, SCIP_BOUNDTYPE *boundtypesfortrans, SCIP_Real minfrac, SCIP_Real maxfrac, SCIP_Real scale, SCIP_AGGRROW *aggrrow, SCIP_Real *cutcoefs, SCIP_Real *cutrhs, int *cutinds, int *cutnnz, SCIP_Real *cutefficacy, int *cutrank, SCIP_Bool *cutislocal, SCIP_Bool *success)
    Definition: cuts.c:7923
    SCIP_RETCODE SCIPaggrRowCreate(SCIP *scip, SCIP_AGGRROW **aggrrow)
    Definition: cuts.c:2668
    SCIP_Bool SCIPisCutEfficacious(SCIP *scip, SCIP_SOL *sol, SCIP_ROW *cut)
    Definition: scip_cut.c:117
    SCIP_Bool SCIPisEfficacious(SCIP *scip, SCIP_Real efficacy)
    Definition: scip_cut.c:135
    void SCIPaggrRowFree(SCIP *scip, SCIP_AGGRROW **aggrrow)
    Definition: cuts.c:2700
    SCIP_RETCODE SCIPaddRow(SCIP *scip, SCIP_ROW *row, SCIP_Bool forcecut, SCIP_Bool *infeasible)
    Definition: scip_cut.c:225
    SCIP_RETCODE SCIPaggrRowSumRows(SCIP *scip, SCIP_AGGRROW *aggrrow, SCIP_Real *weights, int *rowinds, int nrowinds, SCIP_Bool sidetypebasis, SCIP_Bool allowlocal, int negslack, int maxaggrlen, SCIP_Bool *valid)
    Definition: cuts.c:3523
    SCIP_RETCODE SCIPgetLPColsData(SCIP *scip, SCIP_COL ***cols, int *ncols)
    Definition: scip_lp.c:477
    SCIP_RETCODE SCIPgetLPRowsData(SCIP *scip, SCIP_ROW ***rows, int *nrows)
    Definition: scip_lp.c:576
    SCIP_ROW ** SCIPgetLPRows(SCIP *scip)
    Definition: scip_lp.c:611
    int SCIPgetNLPRows(SCIP *scip)
    Definition: scip_lp.c:632
    SCIP_LPSOLSTAT SCIPgetLPSolstat(SCIP *scip)
    Definition: scip_lp.c:174
    SCIP_COL ** SCIPgetLPCols(SCIP *scip)
    Definition: scip_lp.c:512
    int SCIPgetNLPCols(SCIP *scip)
    Definition: scip_lp.c:533
    #define SCIPfreeBuffer(scip, ptr)
    Definition: scip_mem.h:134
    #define SCIPfreeMemoryArrayNull(scip, ptr)
    Definition: scip_mem.h:81
    #define SCIPreallocMemoryArray(scip, ptr, newnum)
    Definition: scip_mem.h:70
    BMS_BLKMEM * SCIPblkmem(SCIP *scip)
    Definition: scip_mem.c:57
    #define SCIPallocMemoryArray(scip, ptr, num)
    Definition: scip_mem.h:64
    #define SCIPallocBufferArray(scip, ptr, num)
    Definition: scip_mem.h:124
    #define SCIPreallocBufferArray(scip, ptr, num)
    Definition: scip_mem.h:128
    #define SCIPallocMemory(scip, ptr)
    Definition: scip_mem.h:60
    #define SCIPfreeBufferArray(scip, ptr)
    Definition: scip_mem.h:136
    #define SCIPfreeMemory(scip, ptr)
    Definition: scip_mem.h:78
    #define SCIPallocBlockMemoryArray(scip, ptr, num)
    Definition: scip_mem.h:93
    #define SCIPallocBuffer(scip, ptr)
    Definition: scip_mem.h:122
    #define SCIPfreeBlockMemory(scip, ptr)
    Definition: scip_mem.h:108
    #define SCIPfreeBlockMemoryArrayNull(scip, ptr, num)
    Definition: scip_mem.h:111
    #define SCIPallocBlockMemory(scip, ptr)
    Definition: scip_mem.h:89
    SCIP_Real SCIPgetRowMaxCoef(SCIP *scip, SCIP_ROW *row)
    Definition: scip_lp.c:1886
    SCIP_Real SCIProwGetLhs(SCIP_ROW *row)
    Definition: lp.c:17686
    SCIP_Bool SCIProwIsModifiable(SCIP_ROW *row)
    Definition: lp.c:17805
    SCIP_Real SCIPgetRowFeasibility(SCIP *scip, SCIP_ROW *row)
    Definition: scip_lp.c:2088
    SCIP_COL ** SCIProwGetCols(SCIP_ROW *row)
    Definition: lp.c:17632
    SCIP_Real SCIProwGetRhs(SCIP_ROW *row)
    Definition: lp.c:17696
    int SCIProwGetNLPNonz(SCIP_ROW *row)
    Definition: lp.c:17621
    int SCIProwGetLPPos(SCIP_ROW *row)
    Definition: lp.c:17895
    const char * SCIProwGetName(SCIP_ROW *row)
    Definition: lp.c:17745
    SCIP_RETCODE SCIPcaptureRow(SCIP *scip, SCIP_ROW *row)
    Definition: scip_lp.c:1486
    SCIP_Real SCIPgetRowSolFeasibility(SCIP *scip, SCIP_ROW *row, SCIP_SOL *sol)
    Definition: scip_lp.c:2131
    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 SCIProwGetRank(SCIP_ROW *row)
    Definition: lp.c:17775
    void SCIProwChgRank(SCIP_ROW *row, int rank)
    Definition: lp.c:17928
    SCIP_Real SCIPgetRowLPFeasibility(SCIP *scip, SCIP_ROW *row)
    Definition: scip_lp.c:1974
    SCIP_Real SCIProwGetConstant(SCIP_ROW *row)
    Definition: lp.c:17652
    SCIP_RETCODE SCIPaddVarsToRow(SCIP *scip, SCIP_ROW *row, int nvars, SCIP_VAR **vars, SCIP_Real *vals)
    Definition: scip_lp.c:1672
    SCIP_Real SCIProwGetDualsol(SCIP_ROW *row)
    Definition: lp.c:17706
    SCIP_Real * SCIProwGetVals(SCIP_ROW *row)
    Definition: lp.c:17642
    SCIP_Real SCIPgetRowSolActivity(SCIP *scip, SCIP_ROW *row, SCIP_SOL *sol)
    Definition: scip_lp.c:2108
    SCIP_RETCODE SCIPsetSepaInitsol(SCIP *scip, SCIP_SEPA *sepa, SCIP_DECL_SEPAINITSOL((*sepainitsol)))
    Definition: scip_sepa.c:221
    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
    SCIP_RETCODE SCIPsetSepaFree(SCIP *scip, SCIP_SEPA *sepa, SCIP_DECL_SEPAFREE((*sepafree)))
    Definition: scip_sepa.c:173
    SCIP_Bool SCIPsepaWasLPDelayed(SCIP_SEPA *sepa)
    Definition: sepa.c:1112
    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:1765
    SCIP_Longint SCIPgetNLPs(SCIP *scip)
    SCIP_Real SCIPinfinity(SCIP *scip)
    SCIP_Bool SCIPisGE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
    SCIP_Bool SCIPisPositive(SCIP *scip, SCIP_Real val)
    SCIP_Bool SCIPisFeasZero(SCIP *scip, SCIP_Real val)
    SCIP_Real SCIPfloor(SCIP *scip, SCIP_Real val)
    SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
    SCIP_Bool SCIPisFeasIntegral(SCIP *scip, SCIP_Real val)
    SCIP_Real SCIPfrac(SCIP *scip, SCIP_Real val)
    SCIP_Bool SCIPisNegative(SCIP *scip, SCIP_Real val)
    SCIP_Real SCIPceil(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)
    SCIP_Bool SCIPvarIsImpliedIntegral(SCIP_VAR *var)
    Definition: var.c:23498
    SCIP_VARTYPE SCIPvarGetType(SCIP_VAR *var)
    Definition: var.c:23453
    SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
    Definition: var.c:24142
    const char * SCIPvarGetName(SCIP_VAR *var)
    Definition: var.c:23267
    SCIP_Bool SCIPvarIsIntegral(SCIP_VAR *var)
    Definition: var.c:23490
    SCIP_Real SCIPvarGetLPSol(SCIP_VAR *var)
    Definition: var.c:24664
    SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
    Definition: var.c:24120
    SCIP_RETCODE SCIPincludeSepaMcf(SCIP *scip)
    Definition: sepa_mcf.c:6926
    void SCIPsortInd(int *indarray, SCIP_DECL_SORTINDCOMP((*indcomp)), void *dataptr, int len)
    void SCIPsortIntInt(int *intarray1, int *intarray2, int len)
    int SCIPsnprintf(char *t, int len, const char *s,...)
    Definition: misc.c:10827
    memory allocation routines
    #define BMSclearMemoryArray(ptr, num)
    Definition: memory.h:130
    public methods for LP management
    public methods for message output
    #define SCIPerrorMessage
    Definition: pub_message.h:64
    #define SCIPdebug(x)
    Definition: pub_message.h:93
    public data structures and miscellaneous methods
    methods for sorting joint arrays of various types
    public methods for separators
    public methods for problem variables
    public methods for branching rule plugins and branching
    public methods for cuts and aggregation rows
    general public methods
    public methods for the LP relaxation, rows and columns
    public methods for memory management
    public methods for message handling
    public methods for numerical tolerances
    public methods for SCIP parameter handling
    public methods for global and local (sub)problems
    public methods for separator plugins
    public methods for solutions
    public methods for querying solving statistics
    public methods for the branch-and-bound tree
    static void addFlowrowToCommodity(SCIP *scip, MCFDATA *mcfdata, SCIP_ROW *row, unsigned char rowsign, int *comcolids, int *ncomcolids)
    Definition: sepa_mcf.c:1522
    static SCIP_RETCODE extractFlow(SCIP *scip, MCFDATA *mcfdata, SCIP_Real maxflowvarflowrowratio, SCIP_Bool *failed)
    Definition: sepa_mcf.c:2066
    #define SEPA_PRIORITY
    Definition: sepa_mcf.c:85
    #define MINNODES
    Definition: sepa_mcf.c:125
    static SCIP_DECL_SORTINDCOMP(compCands)
    Definition: sepa_mcf.c:839
    static SCIP_RETCODE nodepartitionCreate(SCIP *scip, SCIP_MCFNETWORK *mcfnetwork, NODEPARTITION **nodepartition, int nclusters)
    Definition: sepa_mcf.c:5267
    static SCIP_RETCODE mcfnetworkFree(SCIP *scip, SCIP_MCFNETWORK **mcfnetwork)
    Definition: sepa_mcf.c:330
    static SCIP_RETCODE mcfnetworkFill(SCIP *scip, SCIP_MCFNETWORK *mcfnetwork, MCFDATA *mcfdata, int *compnodeid, int *compnodes, int ncompnodes, int *comparcs, int ncomparcs)
    Definition: sepa_mcf.c:381
    #define BOUNDSWITCH
    Definition: sepa_mcf.c:109
    #define SEPA_DELAY
    Definition: sepa_mcf.c:89
    struct nodepair NODEPAIRENTRY
    Definition: sepa_mcf.c:268
    #define DEFAULT_MODELTYPE
    Definition: sepa_mcf.c:98
    #define DEFAULT_MAXWEIGHTRANGE
    Definition: sepa_mcf.c:93
    #define VISITED
    Definition: sepa_mcf.c:4112
    #define DEFAULT_MAXARCINCONSISTENCYRATIO
    Definition: sepa_mcf.c:102
    struct nodepartition NODEPARTITION
    Definition: sepa_mcf.c:287
    #define MAXFRAC
    Definition: sepa_mcf.c:114
    #define DEFAULT_DYNAMICCUTS
    Definition: sepa_mcf.c:97
    static SCIP_DECL_SEPAEXECSOL(sepaExecsolMcf)
    Definition: sepa_mcf.c:6908
    #define MAXCAPACITYSLACK
    Definition: sepa_mcf.c:127
    enum McfEffortLevel MCFEFFORTLEVEL
    Definition: sepa_mcf.c:168
    static void getNextFlowrow(SCIP *scip, MCFDATA *mcfdata, SCIP_ROW **nextrow, unsigned char *nextrowsign, SCIP_Bool *nextinvertcommodity)
    Definition: sepa_mcf.c:1949
    static SCIP_RETCODE findUncapacitatedArcs(SCIP *scip, MCFDATA *mcfdata)
    Definition: sepa_mcf.c:3109
    #define POSTPROCESS
    Definition: sepa_mcf.c:110
    static SCIP_RETCODE getNodeSimilarityScore(SCIP *scip, MCFDATA *mcfdata, int baserowlen, int *basearcpattern, int basenposuncap, int basenneguncap, SCIP_ROW *row, SCIP_Real *score, SCIP_Bool *invertcommodity)
    Definition: sepa_mcf.c:2516
    #define SEPA_DESC
    Definition: sepa_mcf.c:84
    static void nodepartitionJoin(NODEPARTITION *nodepartition, int rep1, int rep2)
    Definition: sepa_mcf.c:5256
    static SCIP_RETCODE identifySourcesTargets(SCIP *scip, MCFDATA *mcfdata, SCIP_SEPADATA *sepadata, MCFEFFORTLEVEL *effortlevel)
    Definition: sepa_mcf.c:3767
    #define DEFAULT_SEPARATEFLOWCUTSET
    Definition: sepa_mcf.c:105
    #define HASHSIZE_NODEPAIRS
    Definition: sepa_mcf.c:129
    static SCIP_DECL_SEPAEXITSOL(sepaExitsolMcf)
    Definition: sepa_mcf.c:6857
    static SCIP_RETCODE mcfnetworkCreate(SCIP *scip, SCIP_MCFNETWORK **mcfnetwork)
    Definition: sepa_mcf.c:304
    static void nodepairqueueFree(SCIP *scip, NODEPAIRQUEUE **nodepairqueue)
    Definition: sepa_mcf.c:5201
    static SCIP_RETCODE mcfnetworkExtract(SCIP *scip, SCIP_SEPADATA *sepadata, SCIP_MCFNETWORK ***mcfnetworks, int *nmcfnetworks, MCFEFFORTLEVEL *effortlevel)
    Definition: sepa_mcf.c:4247
    #define UNCAPACITATEDARCSTRESHOLD
    Definition: sepa_mcf.c:128
    static SCIP_RETCODE extractNodes(SCIP *scip, MCFDATA *mcfdata)
    Definition: sepa_mcf.c:2717
    static void unionfindJoinSets(int *representatives, int rep1, int rep2)
    Definition: sepa_mcf.c:4758
    struct mcfdata MCFDATA
    Definition: sepa_mcf.c:259
    static SCIP_DECL_SEPAFREE(sepaFreeMcf)
    Definition: sepa_mcf.c:6819
    #define SEPA_USESSUBSCIP
    Definition: sepa_mcf.c:88
    #define DISCARDED
    Definition: sepa_mcf.c:298
    static void deleteCommodity(SCIP *scip, MCFDATA *mcfdata, int k, SCIP_ROW **comrows, int nrows, int *ndelflowrows, int *ndelflowvars)
    Definition: sepa_mcf.c:1733
    McfEffortLevel
    Definition: sepa_mcf.c:163
    @ MCFEFFORTLEVEL_OFF
    Definition: sepa_mcf.c:164
    @ MCFEFFORTLEVEL_DEFAULT
    Definition: sepa_mcf.c:165
    @ MCFEFFORTLEVEL_AGGRESSIVE
    Definition: sepa_mcf.c:166
    static SCIP_RETCODE nodepairqueueCreate(SCIP *scip, SCIP_MCFNETWORK *mcfnetwork, NODEPAIRQUEUE **nodepairqueue)
    Definition: sepa_mcf.c:4888
    #define MAXCOLS
    Definition: sepa_mcf.c:116
    static int nodepartitionIsConnected(SCIP *scip, SCIP_MCFNETWORK *mcfnetwork, NODEPARTITION *nodepartition, unsigned int partition)
    Definition: sepa_mcf.c:5462
    #define DEFAULT_CHECKCUTSHORECONNECTIVITY
    Definition: sepa_mcf.c:103
    #define UNDIRECTED
    Definition: sepa_mcf.c:299
    #define RHSPOSSIBLE
    Definition: sepa_mcf.c:294
    #define MCFdebugMessage
    Definition: sepa_mcf.c:145
    #define VARTYPEUSEVBDS
    Definition: sepa_mcf.c:111
    #define DEFAULT_TRYNEGSCALING
    Definition: sepa_mcf.c:95
    static SCIP_RETCODE extractFlowRows(SCIP *scip, MCFDATA *mcfdata)
    Definition: sepa_mcf.c:853
    enum SCIP_McfModeltype SCIP_MCFMODELTYPE
    Definition: sepa_mcf.c:159
    #define MAXCOLROWRATIO
    Definition: sepa_mcf.c:119
    struct nodepairqueue NODEPAIRQUEUE
    Definition: sepa_mcf.c:276
    #define DEFAULT_SEPARATESINGLENODECUTS
    Definition: sepa_mcf.c:104
    #define DEFAULT_MAXSEPACUTSROOT
    Definition: sepa_mcf.c:100
    static void getFlowrowFit(SCIP *scip, MCFDATA *mcfdata, SCIP_ROW *row, int k, unsigned char *rowsign, SCIP_Bool *invertcommodity)
    Definition: sepa_mcf.c:1817
    #define MAXFLOWVARFLOWROWRATIO
    Definition: sepa_mcf.c:120
    #define DEFAULT_FIXINTEGRALRHS
    Definition: sepa_mcf.c:96
    static void getIncidentNodes(SCIP *scip, MCFDATA *mcfdata, SCIP_COL *col, int *sourcenode, int *targetnode)
    Definition: sepa_mcf.c:3010
    static SCIP_Bool nodeInPartition(NODEPARTITION *nodepartition, unsigned int partition, SCIP_Bool inverted, int v)
    Definition: sepa_mcf.c:5432
    static SCIP_RETCODE extractCapacities(SCIP *scip, MCFDATA *mcfdata)
    Definition: sepa_mcf.c:2260
    static SCIP_RETCODE createNewArc(SCIP *scip, MCFDATA *mcfdata, int source, int target, int *newarcid)
    Definition: sepa_mcf.c:1469
    static SCIP_RETCODE identifyComponent(SCIP *scip, MCFDATA *mcfdata, int *nodevisited, int startv, int *compnodes, int *ncompnodes, int *comparcs, int *ncomparcs)
    Definition: sepa_mcf.c:4116
    static SCIP_RETCODE generateClusterCuts(SCIP *scip, SCIP_SEPA *sepa, SCIP_SEPADATA *sepadata, SCIP_SOL *sol, SCIP_Bool allowlocal, int depth, SCIP_MCFNETWORK *mcfnetwork, NODEPARTITION *nodepartition, int *ncuts, SCIP_Bool *cutoff)
    Definition: sepa_mcf.c:5886
    #define DEFAULT_MAXTESTDELTA
    Definition: sepa_mcf.c:94
    static SCIP_RETCODE cleanupNetwork(SCIP *scip, MCFDATA *mcfdata)
    Definition: sepa_mcf.c:3449
    static SCIP_DECL_SORTPTRCOMP(compNodepairs)
    Definition: sepa_mcf.c:4785
    static SCIP_DECL_HASHGETKEY(hashGetKeyNodepairs)
    Definition: sepa_mcf.c:4801
    #define DEFAULT_NCLUSTERS
    Definition: sepa_mcf.c:92
    #define SEPA_MAXBOUNDDIST
    Definition: sepa_mcf.c:87
    static void nodepartitionFree(SCIP *scip, NODEPARTITION **nodepartition)
    Definition: sepa_mcf.c:5415
    static SCIP_RETCODE extractCapacityRows(SCIP *scip, MCFDATA *mcfdata)
    Definition: sepa_mcf.c:1075
    static SCIP_Bool nodepairqueueIsEmpty(NODEPAIRQUEUE *nodepairqueue)
    Definition: sepa_mcf.c:5217
    static int nodepartitionGetRepresentative(NODEPARTITION *nodepartition, int v)
    Definition: sepa_mcf.c:5246
    #define INVERTED
    Definition: sepa_mcf.c:297
    #define SEPA_FREQ
    Definition: sepa_mcf.c:86
    static SCIP_RETCODE addCut(SCIP *scip, SCIP_SEPA *sepa, SCIP_SEPADATA *sepadata, SCIP_SOL *sol, SCIP_Real *cutcoefs, SCIP_Real cutrhs, int *cutinds, int cutnnz, SCIP_Bool cutislocal, int cutrank, int *ncuts, SCIP_Bool *cutoff)
    Definition: sepa_mcf.c:5799
    #define MINFRAC
    Definition: sepa_mcf.c:113
    static void unionfindInitSets(int *representatives, int nelems)
    Definition: sepa_mcf.c:4726
    #define MAXAGGRLEN(nvars)
    Definition: sepa_mcf.c:117
    #define LHSASSIGNED
    Definition: sepa_mcf.c:295
    #define UNKNOWN
    Definition: sepa_mcf.c:4110
    #define DEFAULT_MAXSEPACUTS
    Definition: sepa_mcf.c:99
    #define SEPA_NAME
    Definition: sepa_mcf.c:83
    static void collectIncidentFlowCols(SCIP *scip, MCFDATA *mcfdata, SCIP_ROW *flowrow, int basecommodity)
    Definition: sepa_mcf.c:2431
    #define MAXNETWORKS
    Definition: sepa_mcf.c:122
    #define MINCOLROWRATIO
    Definition: sepa_mcf.c:118
    #define MINARCS
    Definition: sepa_mcf.c:126
    #define MAXARCNODERATIO
    Definition: sepa_mcf.c:121
    #define MINCOMNODESFRACTION
    Definition: sepa_mcf.c:124
    static void invertCommodity(SCIP *scip, MCFDATA *mcfdata, int k, SCIP_ROW **comrows, int ncomrows, int *comcolids, int ncomcolids)
    Definition: sepa_mcf.c:1668
    SCIP_McfModeltype
    Definition: sepa_mcf.c:154
    @ SCIP_MCFMODELTYPE_AUTO
    Definition: sepa_mcf.c:155
    @ SCIP_MCFMODELTYPE_UNDIRECTED
    Definition: sepa_mcf.c:157
    @ SCIP_MCFMODELTYPE_DIRECTED
    Definition: sepa_mcf.c:156
    #define LHSPOSSIBLE
    Definition: sepa_mcf.c:293
    static NODEPAIRENTRY * nodepairqueueRemove(NODEPAIRQUEUE *nodepairqueue)
    Definition: sepa_mcf.c:5229
    static SCIP_DECL_HASHKEYVAL(hashKeyValNodepairs)
    Definition: sepa_mcf.c:4854
    static void fixCommoditySigns(MCFDATA *mcfdata)
    Definition: sepa_mcf.c:2993
    static SCIP_DECL_SEPAEXECLP(sepaExeclpMcf)
    Definition: sepa_mcf.c:6881
    static SCIP_DECL_SEPAINITSOL(sepaInitsolMcf)
    Definition: sepa_mcf.c:6839
    static SCIP_DECL_HASHKEYEQ(hashKeyEqNodepairs)
    Definition: sepa_mcf.c:4813
    #define RHSASSIGNED
    Definition: sepa_mcf.c:296
    static SCIP_DECL_SEPACOPY(sepaCopyMcf)
    Definition: sepa_mcf.c:6805
    static SCIP_RETCODE separateCuts(SCIP *scip, SCIP_SEPA *sepa, SCIP_SOL *sol, SCIP_Bool allowlocal, int depth, SCIP_RESULT *result)
    Definition: sepa_mcf.c:6630
    static SCIP_RETCODE createNewCommodity(SCIP *scip, MCFDATA *mcfdata)
    Definition: sepa_mcf.c:1445
    #define DEFAULT_SEPARATEKNAPSACK
    Definition: sepa_mcf.c:106
    #define DEFAULT_MAXINCONSISTENCYRATIO
    Definition: sepa_mcf.c:101
    #define MAXFLOWCANDDENSITY
    Definition: sepa_mcf.c:123
    #define ONSTACK
    Definition: sepa_mcf.c:4111
    static int unionfindGetRepresentative(int *representatives, int v)
    Definition: sepa_mcf.c:4740
    multi-commodity-flow network cut separator
    SCIP_ROW *** nodeflowrows
    Definition: sepa_mcf.c:173
    SCIP_MCFMODELTYPE modeltype
    Definition: sepa_mcf.c:188
    SCIP_Real * arccapacityscales
    Definition: sepa_mcf.c:179
    int * arctargets
    Definition: sepa_mcf.c:182
    SCIP_Real ** nodeflowscales
    Definition: sepa_mcf.c:175
    SCIP_Bool ** nodeflowinverted
    Definition: sepa_mcf.c:176
    int * arcsources
    Definition: sepa_mcf.c:181
    int nuncapacitatedarcs
    Definition: sepa_mcf.c:186
    SCIP_ROW ** arccapacityrows
    Definition: sepa_mcf.c:177
    int * colcommodity
    Definition: sepa_mcf.c:183
    @ SCIP_LPSOLSTAT_OPTIMAL
    Definition: type_lp.h:44
    @ SCIP_DIDNOTRUN
    Definition: type_result.h:42
    @ SCIP_CUTOFF
    Definition: type_result.h:48
    @ SCIP_DELAYED
    Definition: type_result.h:43
    @ 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_FILECREATEERROR
    Definition: type_retcode.h:48
    @ SCIP_INVALIDDATA
    Definition: type_retcode.h:52
    @ SCIP_OKAY
    Definition: type_retcode.h:42
    enum SCIP_Retcode SCIP_RETCODE
    Definition: type_retcode.h:63
    struct SCIP_SepaData SCIP_SEPADATA
    Definition: type_sepa.h:52
    @ SCIP_VARTYPE_INTEGER
    Definition: type_var.h:65
    @ SCIP_VARTYPE_CONTINUOUS
    Definition: type_var.h:71
    @ SCIP_VARTYPE_BINARY
    Definition: type_var.h:64