All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
sepa_oddcycle.c
Go to the documentation of this file.
21 * We separate odd cycle inequalities in the implication graph. Implemented are the classic method
22 * by Groetschel, Lovasz, and Schrijver (GLS) and the levelgraph method by Hoffman and Padberg (HP)
31 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
47 #define SEPA_DELAY FALSE /**< should separation method be delayed, if other separators found cuts? */
51 #define DEFAULT_SCALEFACTOR 1000 /**< factor for scaling of the arc-weights in the Dijkstra algorithm */
54 #define DEFAULT_REPAIRCYCLES TRUE /**< try to repair violated cycles in which a variable and its negated appear */
56 #define DEFAULT_INCLUDETRIANGLES TRUE /**< separate triangles (3-cliques) found as 3-cycles or repaired larger cycles */
57 #define DEFAULT_MULTIPLECUTS FALSE /**< still try variable as start, even if it is already covered by a cut */
58 #define DEFAULT_ALLOWMULTIPLECUTS TRUE /**< allow another inequality to use variable, even if it is already covered */
59 #define DEFAULT_LPLIFTCOEF FALSE /**< TRUE: choose lifting candidate with highest value of coefficient*lpvalue
62 #define DEFAULT_MAXSEPACUTS 5000 /**< maximal number of oddcycle cuts separated per separation round */
63 #define DEFAULT_MAXSEPACUTSROOT 5000 /**< maximal number of oddcycle cuts separated per separation round in root node */
64 #define DEFAULT_PERCENTTESTVARS 0 /**< percent of variables to try the chosen method on [0-100] */
66 #define DEFAULT_MAXCUTSROOT 1 /**< maximal number of oddcycle cuts generated per root of the levelgraph */
67 #define DEFAULT_SORTSWITCH 3 /**< unsorted (0), maxlp (1), minlp (2), maxfrac (3), minfrac (4) */
68 #define DEFAULT_MAXREFERENCE 0 /**< minimal weight on an edge (in level graph or Dijkstra graph) */
72 #define DEFAULT_MAXPERNODESLEVEL 100 /**< maximal percentage of nodes allowed in one level of the levelgraph [0-100] */
73 #define DEFAULT_OFFSETNODESLEVEL 10 /**< additional offset of nodes allowed in one level of the levelgraph */
77 #define DEFAULT_CUTTHRESHOLD -1 /**< maximal number of other cuts s.t. separation is applied (-1 for direct call) */
88 * This undirected graph is represented by a directed graph with forward and backward arcs. Arcs are
105 unsigned int lastF; /**< last storage element index in targetForward, weightForward - forward direction */
106 unsigned int lastB; /**< last storage element index in targetBackward, weightBackward - backward direction */
107 int* beginForward; /**< forward adjacency list index in targetForward, weightForward for each node */
108 int* beginBackward; /**< backward adjacency list index in targetBackward, weightBackward for each node */
132 * processed start node or root node and continues from this position in the next separation round.
149 unsigned int ncuts; /**< number of cuts, added by the separator so far (in current and past calls) */
151 int nliftedcuts; /**< number of lifted cuts, added by the separator so far (in current and past calls) */
153 SCIP_Bool multiplecuts; /**< an odd cycle cut of length L can be generated L times; forbidding multiple cuts
157 SCIP_Bool addselfarcs; /**< add arcs between the nodes of a variable and its negated; since not all implications
159 SCIP_Bool repaircycles; /**< if a variable and its negated appear in a cycle, we can repair the cycle
161 SCIP_Bool includetriangles; /**< handle triangles found as 3-cycles or repaired larger cycles */
164 unsigned int* mapping; /**< mapping for getting the index of a variable in the sorted variable array */
165 SCIP_Bool lpliftcoef; /**< TRUE: choose lifting candidate with highest value of coefficient*lpvalue
169 int maxsepacutsroot; /**< max. number of oddcycle cuts separated per separation round in the root node */
170 int maxsepacutsround; /**< max. number of oddcycle cuts separated per separation round in the current node */
171 SORTTYPE sortswitch; /**< sorted type: unsorted (0), maxlp (1), minlp (2), maxfrac (3), minfrac (4) */
176 int maxpernodeslevel; /**< percentage of nodes allowed in the same level of the level graph [0-100] */
178 unsigned int maxlevelsize; /**< maximal number of nodes allowed in the same level of the level graph */
180 int maxcutslevel; /**< maximal number of oddcycle cuts generated per level of the level graph */
182 int maxroundsroot; /**< maximal number of oddcycle separation rounds in the root node (-1: unlimited) */
187 int cutthreshold; /**< maximal number of other cuts s.t. separation is applied (-1 for direct call) */
198 /** displays cycle of pred data structure w.r.t. variable names of the original problem (including
294 for( i = sepadata->dijkstragraph->outbeg[a]; i < sepadata->dijkstragraph->outbeg[a] + sepadata->dijkstragraph->outcnt[a]; ++i )
302 /* if a and b are contained in the level graph (with their arcs), we can check inside the level graph structure */
303 if( (sepadata->levelgraph->beginForward[a] != -1 || sepadata->levelgraph->beginBackward[a] != -1)
304 && (sepadata->levelgraph->beginForward[b] != -1 || sepadata->levelgraph->beginBackward[b] != -1) )
332 else if( sepadata->levelgraph->level[a] == sepadata->levelgraph->level[b]-1 ) /* second case of adjacent level */
345 else /* same level (note that an edge between a and b is stored for a if a < b, otherwise it is stored for b) */
355 while( sepadata->levelgraph->sourceAdj[i] == a && i < sepadata->levelgraph->levelAdj[sepadata->levelgraph->level[a]+1] )
373 while( sepadata->levelgraph->sourceAdj[i] == b && i < sepadata->levelgraph->levelAdj[sepadata->levelgraph->level[b]+1])
431 if( ( SCIPvarGetNBinImpls(vars[b], originalb) == 0 && SCIPvarGetNCliques(vars[a], originala) == 0 )
432 || ( SCIPvarGetNBinImpls(vars[a], originala) == 0 && SCIPvarGetNCliques(vars[b], originalb) == 0 ) )
435 /* @todo later: possible improvement: do this test for implications and cliques separately if this here is time consuming */
436 /* one of the nodes seems to have more arcs than the other, we swap them (since adjacency is symmetric) */
502 if( (cliquevals[j] == FALSE && originalb == TRUE) || ( cliquevals[j] == TRUE && originalb == FALSE ) )
516 * since we have to exclude all chains adjacent to an already lifted node which is not adjacent to
517 * the current lifting candidate we check all chains of the cycle of length three and block them if
569 /** determine the heuristic lifting coefficient by counting the length of the adjacent chains of the
608 myi[j] = isNeighbor(vars, nbinvars, sepadata, i, cycle[j-1]) && isNeighbor(vars, nbinvars, sepadata, i, cycle[j])
613 myi[0] = isNeighbor(vars, nbinvars, sepadata, i, cycle[ncyclevars-1]) && isNeighbor(vars, nbinvars, sepadata, i, cycle[0])
624 checkBlocking((unsigned int) (j-1), (unsigned int) j, (unsigned int) (j+1), i, cycle, ncyclevars, vars, nbinvars, lifted, nlifted, sepadata, myi);
626 checkBlocking(ncyclevars-2, ncyclevars-1, 0, i, cycle, ncyclevars, vars, nbinvars, lifted, nlifted, sepadata, myi);
627 checkBlocking(ncyclevars-1, 0, 1, i, cycle, ncyclevars, vars, nbinvars, lifted, nlifted, sepadata, myi);
693 * This method is based on the observation, that a non-cycle node can be lifted into the inequality
700 * If the node is connected to several chains, the coefficients of the chains can be summed up, resulting
703 * Additionally further variables can be lifted by considering chains connected to the additional lifting node
707 * (Furthermore the first lifting coefficient is always smaller or equal to the largest possible lifting coefficient.)
802 /* in case we use a lifting rule, which does not require the first lifting coefficient of all variables: REMOVE this */
841 coef[i] = getCoef(scip, (unsigned int) bestcand, cycle, ncyclevars, vars, nbinvars, lifted, nlifted, sepadata, myi);
960 SCIP_CALL( liftOddCycleCut(scip, &nlifted, lifted, liftcoef, sepadata, vars, nbinvars, startnode, pred, ncyclevars, vals, result) );
966 SCIP_CALL( SCIPcreateEmptyRowSepa(scip, &cut, sepa, cutname, -SCIPinfinity(scip), (ncyclevars-1)/2.0, FALSE, FALSE, TRUE) );
1076 * calculated by the GLS algorithm in case there is no violated odd cycle inequality) and removes
1107 /* skip variable if it is already covered by a cut and we do not allow multiple cuts per node */
1139 /* delete original and negated variable and cross-link their neighbors the following way, if possible:
1150 * in addition to that, in our linked list structure we need to relink the chain c1-...-cn in reverse order.
1240 /** memory reallocation method (the graph is normally very dense, so we dynamically allocate only the memory we need)
1242 * Since the array sizes differ the method can be called for each of the three data structure types:
1252 int** targetArray, /**< given target array (or NULL if sourceAdjArray and targetAdjArray given) */
1271 additional = MIN(graph->maxarcs + graph->maxnodes - *size, *size) * ((int) sizeof(**weightArray));
1274 additional += MIN(graph->maxarcs + graph->maxnodes - *size, *size) * ((int) sizeof(**targetArray));
1278 additional += MIN(graph->maxarcs + graph->maxnodes - *size, *size) * ((int) sizeof(**sourceAdjArray));
1279 additional += MIN(graph->maxarcs + graph->maxnodes - *size, *size) * ((int) sizeof(**targetAdjArray));
1300 SCIP_CALL( SCIPreallocBufferArray(scip, weightArray, (int) MIN(graph->maxarcs + graph->maxnodes, *size)) );
1303 SCIP_CALL( SCIPreallocBufferArray(scip, targetArray, (int) MIN(graph->maxarcs + graph->maxnodes, *size)) );
1515 /* calculate arc weight and add arc, if the neighbor node is on the same or a neighbor level */
1516 if( inlevelgraph[v] && (graph->level[v] == level+1 || graph->level[v] == level || graph->level[v] == level-1))
1520 /* the computation of 1.0 - vals[v] if v is negated is ensured by the fact that v > nbinvars in this case */
1667 /* calculate arc weight and add arc, if the neighbor node is on the same or a neighbor level */
1668 if( inlevelgraph[v] && (graph->level[v] == level+1 || graph->level[v] == level || graph->level[v] == level-1))
1672 /* the computation of 1.0 - vals[v] if v is negated is ensured by the fact that v > nbinvars in this case */
1700 * If we limit the size of nodes of a level, we want to add the best neighbors to the next level.
1701 * Since sorting every level is too expensive, we sort the neighbors of the root (if requested).
1705 * - iterate over the implication and clique neighbors of the root and set their flag array values to TRUE
1708 * - add variables from sorted array to levelgraph until first level is full (or all variables are inserted)
1710 * Even inserting all variables might help for the following creation of further levels since the neighbors
1711 * of nodes with high fractionality often have high fractionalities themselves and would be inserted first
1905 /* insert sorted neighbors until level size limit is reached (or all neighbors are inserted) */
1927 /* the computation of 1.0 - vals[v] if v is negated is ensured by the fact that v > nbinvars in this case */
1961 * node, P stores the partent nodes in the shortest path tree (-1 if node has not been reached).
2037 /* it is not necessary to stop if we found the root (in this case there are no arcs left) and we stop anyway */
2047 * We traverse the shortest path found by findShortestPathToRoot() and block all neighbors (in the
2048 * original graph) of nodes in the path, i.e., we set blocked to TRUE. We do not block neighbors of
2232 /* it is not necessary to stop if we found the root (in this case there are no arcs left) and we stop anyway */
2434 * All neighbors of nodes in level i that are assigned to level i+1, if they do not already appear in levels <= i.
2499 SCIP_CALL( SCIPgetVarsData(scip, &scipvars, NULL, &nscipbinvars, &nscipintvars, &nscipimplvars, NULL) );
2586 /* create mapping for getting the index of a variable via its probindex to the index in the sorted variable array */
2592 assert( 0 <= SCIPvarGetProbindex(vars[i]) && SCIPvarGetProbindex(vars[i]) < nintegral); /* since binary, integer, and implicit variables are first */
2594 vals[i] = SCIPgetSolVal(scip, sol, vars[i]); /* need to get new values, since they might be corrupted */
2603 /* the implication graph is redundant and therefore more implications and clique arcs may occur than should be possible
2618 SCIP_CALL( SCIPallocBufferArray(scip, &graph.targetForward, (int) MIN(graph.sizeForward, graph.maxarcs)) );
2619 SCIP_CALL( SCIPallocBufferArray(scip, &graph.targetBackward, (int) MIN(graph.sizeBackward, graph.maxarcs)) );
2620 SCIP_CALL( SCIPallocBufferArray(scip, &graph.weightForward, (int) MIN(graph.sizeForward, graph.maxarcs)) );
2621 SCIP_CALL( SCIPallocBufferArray(scip, &graph.weightBackward, (int) MIN(graph.sizeBackward, graph.maxarcs)) );
2626 SCIP_CALL( SCIPallocBufferArray(scip, &graph.sourceAdj, (int) MIN(graph.sizeAdj, graph.maxarcs)) );
2627 SCIP_CALL( SCIPallocBufferArray(scip, &graph.targetAdj, (int) MIN(graph.sizeAdj, graph.maxarcs)) );
2628 SCIP_CALL( SCIPallocBufferArray(scip, &graph.weightAdj, (int) MIN(graph.sizeAdj, graph.maxarcs)) );
2645 maxroots = (unsigned int) SCIPceil(scip, sepadata->offsettestvars + (0.02 * nbinvars * sepadata->percenttestvars));
2646 sepadata->maxlevelsize = (unsigned int) SCIPceil(scip, sepadata->offsetnodeslevel + 0.01 * sepadata->maxpernodeslevel * graph.maxnodes);
2663 /* consider original and negated variable pair and skip variable if there is only one edge leaving the pair */
2664 if( (SCIPvarGetNBinImpls(vars[i % nbinvars], TRUE) + SCIPvarGetNBinImpls(vars[i % nbinvars], FALSE) < 2)
2665 && (SCIPvarGetNCliques(vars[i % nbinvars], TRUE) + SCIPvarGetNCliques(vars[i % nbinvars], FALSE) < 1) )
2671 if( SCIPvarGetNBinImpls(vars[i % nbinvars], TRUE ) < 1 && SCIPvarGetNCliques(vars[i % nbinvars], TRUE ) < 1 )
2680 if( SCIPvarGetNBinImpls(vars[i % nbinvars], FALSE) < 1 && SCIPvarGetNCliques(vars[i % nbinvars], FALSE) < 1 )
2738 /* calculate maximal cuts in this level due to cut limitations (per level, per root, per separation round) */
2741 maxcutslevel = (unsigned int) MIN(maxcutslevel, sepadata->maxsepacutsround + sepadata->oldncuts - sepadata->ncuts);
2743 /* for each cross edge in this level find both shortest paths to root (as long as no limits are reached) */
2757 SCIP_CALL( findShortestPathToRoot(scip, sepadata->scale, &graph, graph.sourceAdj[j], distance, queue, inQueue, parentTree) );
2774 SCIP_CALL( blockRootPath(scip, &graph, graph.sourceAdj[j], inlevelgraph, blocked, parentTree, i) );
2812 SCIP_CALL( cleanCycle(scip, pred, incycle, incut, u, graph.targetAdj[j], nbinvars, &ncyclevars,
2828 SCIP_CALL( cleanCycle(scip, pred, incycle, incut, u, graph.targetAdj[j], nbinvars, &ncyclevars,
2842 SCIP_CALL( cleanCycle(scip, pred, incycle, incut, u, graph.targetAdj[j], nbinvars, &ncyclevars,
2852 SCIP_CALL( generateOddCycleCut(scip, sepa, sol, vars, nbinvars, graph.targetAdj[j], pred, ncyclevars,
2938 /** memory reallocation method (the graph is normally very dense, so we dynamically allocate only the memory we need) */
2963 additional += (MIN(maxarcs, 2 * (*arraysize)) - (*arraysize)) * ((int) sizeof(*(graph->weight)));
3031 SCIP_Bool* emptygraph, /**< TRUE, iff there is no arc in the implication graph of the binary variables of SCIP */
3083 /* forward direction (the backward is created at the occurrence of the current variable in the cliquevars of the neighbor) */
3101 tmp = (int) SCIPfeasCeil(scip, sepadata->scale * (1.0 - vals[varsidx] - (1.0 - vals[neighindex])) );
3113 tmp = (int) SCIPfeasCeil(scip, sepadata->scale * (1.0 - (1.0 - vals[varsidx]) - vals[neighindex]) );
3122 tmp = (int) SCIPfeasCeil(scip, sepadata->scale * (1.0 - (1.0 - vals[varsidx]) - (1.0 - vals[neighindex])) );
3170 SCIP_Bool* emptygraph, /**< TRUE, iff there is no arc in the implication graph of the binary variables of SCIP */
3230 /* forward direction (the backward is created at the occurrence of the current variable in the cliquevars of the neighbor) */
3244 tmp = (int) SCIPfeasCeil(scip, sepadata->scale * (1 - vals[varsidx] - (1 - vals[neighindex]) ));
3255 tmp = (int) SCIPfeasCeil(scip, sepadata->scale * (1 - (1 - vals[varsidx]) - vals[neighindex] ));
3262 tmp = (int) SCIPfeasCeil(scip, sepadata->scale * (1 - (1 - vals[varsidx]) - (1-vals[neighindex]) )) ;
3293 /** The classical method for finding odd cycles by Groetschel, Lovasz, Schrijver uses a bipartite graph
3295 * All arcs uv of the original graph are copied to arcs from u of the first partition to v' of the second partition
3373 SCIP_CALL( SCIPgetVarsData(scip, &scipvars, NULL, &nscipbinvars, &nscipintvars, &nscipimplvars, NULL) );
3460 /* create mapping for getting the index of a variable via its probindex to the index in the sorted variable array */
3468 assert( 0 <= SCIPvarGetProbindex(vars[i]) && SCIPvarGetProbindex(vars[i]) < nintegral); /* since binary, integer, and implicit variables are first */
3470 vals[i] = SCIPgetSolVal(scip, sol, vars[i]); /* need to get new values, since they might be corrupted */
3476 /* initialize number of nodes in Dijkstra graph (2*2*n nodes in a mirrored bipartite graph with negated variables) */
3479 /* Initialize number of arcs in Dijkstra graph, should be (nbinvars+1) * graph.nodes, but might deviate, because
3493 /* the implication graph is redundant and therefore more implications and clique arcs may occur than should be possible
3494 * @todo later: filtering of edges which were already added, maxarcs should be graph.arcs rather than INT_MAX;
3527 /* add arcs from first to second partition to Dijkstra graph (based on the original fractional implication graph) */
3552 /* insert arcs for binary implications (take var => getImpl(Bin) => getImplVar => add forward-arc) */
3557 SCIP_CALL( addGLSBinImpls(scip, sepadata, vars, i, dijkindex, vals, nbinvars, nbinimpls, &graph,
3577 /* add link to copy of negated variable (useful if/because the implication graph is incomplete) */
3674 SCIPdebugMessage("--- graph successfully created (%u nodes, %u arcs) ---\n", graph.nodes, narcs);
3689 SCIPverbMessage(scip, SCIP_VERBLEVEL_HIGH, NULL, "Wrote clique/implication graph to <%s>.\n", filename);
3694 maxstarts = (unsigned int) SCIPceil(scip, sepadata->offsettestvars + (0.02 * nbinvars * sepadata->percenttestvars));
3709 SCIP_Bool edgedirection; /* partitionindicator for backprojection from bipartite graph to original graph:
3735 (void) dijkstraPairCutoffIgnore(&graph, startnode, endnode, incut, cutoff, dist, pred, entry, order);
3763 for( dijkindex = endnode; dijkindex != startnode && success; dijkindex = pred[dijkindex], edgedirection = !edgedirection )
3777 SCIP_CALL( cleanCycle(scip, pred2, incycle, incut, dijkindex-2*nbinvars, endnode-2*nbinvars, nbinvars,
3792 SCIP_CALL( cleanCycle(scip, pred2, incycle, incut, dijkindex, endnode-2*nbinvars, nbinvars, &ncyclevars,
3801 SCIP_CALL( generateOddCycleCut(scip, sepa, sol, vars, nbinvars, startnode, pred2, ncyclevars, incut, vals, sepadata, result) );
3897 /** solving process initialization method of separator (called when branch and bound process is about to begin) */
3946 if( SCIPgetNLPBranchCands(scip) < 3 || (!(sepadata->includetriangles) && SCIPgetNLPBranchCands(scip) < 5))
3952 /* only call separator if enough implications (but not all implications should come from cliques) are present */
3973 SCIPdebugMessage("skipping separator: number of unsucessfull calls = %d.\n", sepadata->nunsucessfull);
4001 SCIPdebugMessage("added %u cuts (%d allowed), %d lifted.\n", sepadata->ncuts - sepadata->oldncuts,
4010 SCIPdebugMessage("total sepatime: %.2f - total number of added cuts: %u\n", SCIPsepaGetTime(sepa), sepadata->ncuts);
4034 SCIP_CALL( SCIPincludeSepaBasic(scip, &sepa, SEPA_NAME, SEPA_DESC, SEPA_PRIORITY, SEPA_FREQ, SEPA_MAXBOUNDDIST,
4049 "should the search method by Groetschel, Lovasz, Schrijver be used? Otherwise use levelgraph method by Hoffman, Padberg.",
4081 "even if a variable is already covered by a cut, still try it as start node for a cycle search",
|