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