Scippy

SCIP

Solving Constraint Integer Programs

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