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