|
Open Graph Drawing Framework |
v. 2023.09 (Elderberry)
|
|
|
Go to the documentation of this file.
53 template<
typename T,
typename ExtraDataType>
54 class FullComponentWithExtraStore;
59 class EdgeWeightedGraph;
64 #define OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_CONNECTED_COMPONENTS // this is faster
65 #define OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_YVAR_CONSTRAINTS // if not defined: generate all yvar constraints in the beginning
68 namespace steiner_tree {
87 #ifdef OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_CONNECTED_COMPONENTS
122 G.setOriginalGraph(
m_G);
124 source = G.newNode();
128 target = G.newNode();
129 for (
int j = 0; j < activeComponents.
size(); ++j) {
130 const int i = activeComponents[j];
135 const auto it0 = terminals.
begin();
136 const node rC = G.copy(*it0);
137 capacity[G.newEdge(source, rC)] = cap;
138 if (terminals.
size() > 2) {
139 const node v = G.newNode();
140 capacity[G.newEdge(rC, v)] = cap;
141 for (
auto it = it0 + 1; it != terminals.
end(); ++it) {
142 const node w = G.copy(*it);
143 capacity[G.newEdge(v, w)] = cap;
146 capacity[G.newEdge(rC, G.copy(*terminals.
rbegin()))] = cap;
153 const node v = G.copy(t);
159 y_v += capacity[adj->
theEdge()];
163 #ifdef OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_YVAR_CONSTRAINTS
170 capacity[G.newEdge(v, target)] = y_v;
176 for (
edge e : G.edges) {
177 if (G.original(e->
source())) {
178 std::cout <<
" T:" << G.original(e->
source());
180 std::cout <<
" " << e->
source();
183 if (G.original(e->
target())) {
184 std::cout <<
"T:" << G.original(e->
target());
188 std::cout <<
" " << capacity[e] <<
"\n";
208 T upperBound = 0,
int cliqueSize = 0,
double eps = 1e-8)
227 #ifndef OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_YVAR_CONSTRAINTS
249 const int n = m_fullCompStore.size();
251 m_matrix->setDimensions(0, n);
252 for (
int i = 0; i < n; ++i) {
253 m_lowerBounds[i] = 0;
254 m_upperBounds[i] = 1;
256 for (
int i = 0; i < n; ++i) {
257 m_objective[i] = m_fullCompStore.cost(i);
259 m_osiSolver->loadProblem(*m_matrix, m_lowerBounds, m_upperBounds, m_objective,
nullptr,
nullptr);
261 if (m_upperBound > 0) {
262 CoinPackedVector row;
263 row.setFull(m_fullCompStore.size(), m_objective);
264 m_osiSolver->addRow(row, 0, m_upperBound);
270 CoinPackedVector row;
272 for (
int i = 0; i < m_fullCompStore.size(); ++i) {
273 if (m_fullCompStore.isTerminal(i, t)) {
278 m_osiSolver->addRow(row, 1, m_osiSolver->getInfinity());
284 CoinPackedVector row;
287 for (
int i = 0; i < m_fullCompStore.size(); ++i) {
289 int intersectionCard = 0;
290 auto terminals = m_fullCompStore.terminals(i);
291 node* it1 = terminals.begin();
292 auto it2 = subset.
begin();
293 while (it1 != terminals.end() && it2 != subset.
end()) {
294 if ((*it1)->index() < (*it2)->index()) {
296 }
else if ((*it1)->index() > (*it2)->index()) {
305 if (intersectionCard > 1) {
306 row.insert(i, intersectionCard - 1);
307 test += (intersectionCard - 1) * m_fullCompStore.extra(i);
310 if (test > subset.
size() - 1) {
311 m_osiSolver->addRow(row, 0, subset.
size() - 1);
321 CoinPackedVector row;
323 for (
int i = 0; i < m_fullCompStore.size(); ++i) {
324 row.insert(i, m_fullCompStore.terminals(i).size() - 1);
327 int value = m_terminals.size() - 1;
328 m_osiSolver->addRow(row, value, value);
333 bool initialIteration =
true;
336 if (initialIteration) {
337 m_osiSolver->initialSolve();
338 #ifdef OGDF_STEINERTREE_LPRELAXATIONSER_LOGGING
339 std::cout <<
"Objective value " << m_osiSolver->getObjValue() <<
" of initial solution."
342 initialIteration =
false;
344 m_osiSolver->resolve();
345 #ifdef OGDF_STEINERTREE_LPRELAXATIONSER_LOGGING
346 std::cout <<
"Objective value " << m_osiSolver->getObjValue() <<
" after resolve."
351 if (!m_osiSolver->isProvenOptimal()) {
352 if (m_upperBound > 0) {
355 std::cerr <<
"Failed to optimize LP!" << std::endl;
359 }
while (separate());
361 const double* constSol = m_osiSolver->getColSolution();
362 const int numberOfColumns = m_osiSolver->getNumCols();
364 for (
int i = 0; i < numberOfColumns; ++i) {
365 m_fullCompStore.extra(i) = constSol[i];
368 #ifdef OGDF_STEINERTREE_LPRELAXATIONSER_OUTPUT_LP
369 m_osiSolver->writeLp(stderr);
377 const double* constSol = m_osiSolver->getColSolution();
380 for (
int i = 0; i < m_fullCompStore.size(); ++i) {
381 m_fullCompStore.extra(i) = constSol[i];
382 if (m_fullCompStore.extra(i) > m_eps) {
383 activeComponents.
push(i);
387 #ifdef OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_CONNECTED_COMPONENTS
388 if (!m_separationStage) {
389 if (separateConnected(activeComponents)) {
392 m_separationStage = 1;
395 if (separateMinCut(activeComponents)) {
398 return m_separateCliqueSize > 2 ? separateCycles(activeComponents) :
false;
405 for (
node t : m_terminals) {
410 for (
int j = 0; j < activeComponents.
size(); ++j) {
411 auto terminals = m_fullCompStore.terminals(activeComponents[j]);
412 auto it = terminals.begin();
413 const int s1 = setID[*it];
414 for (++it; it != terminals.end(); ++it) {
425 for (
node t : m_terminals) {
426 const int k = uf.
find(setID[t]);
427 if (components[k].empty()) {
430 components[k].push(t);
433 for (
const int k : usedComp) {
434 cutsFound += addSubsetCoverConstraint(components[k]);
446 double y_R = generateMinCutSeparationGraph(activeComponents, source, pseudotarget, auxG,
447 capacity, cutsFound);
449 #ifdef OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_YVAR_CONSTRAINTS
456 capacity[auxG.
newEdge(pseudotarget, target)] = y_R;
461 for (
node t : m_terminals) {
465 capacity[v_to_target] = std::numeric_limits<double>::max();
467 maxFlow.
init(auxG, &flow);
469 const double cutVal = maxFlow.
computeValue(capacity, source, target);
470 if (cutVal - y_R < 1 - m_eps) {
471 minSTCut.
call(auxG, capacity, flow, source, target);
473 for (
node tOrig : m_terminals) {
474 const node tCopy = auxG.
copy(tOrig);
480 cutsFound += addSubsetCoverConstraint(subset);
485 return cutsFound != 0;
495 for (
int i : activeComponents) {
498 for (
node u1 : G.nodes) {
499 const int i1 =
id[u1];
500 const Array<node>& terminals1 = m_fullCompStore.terminals(i1);
502 const int i2 =
id[u2];
503 const Array<node>& terminals2 = m_fullCompStore.terminals(i2);
505 int intersectionCard = 0;
508 while (it1 != terminals1.
end() && it2 != terminals2.
end()) {
509 if ((*it1)->index() < (*it2)->index()) {
511 }
else if ((*it1)->index() > (*it2)->index()) {
515 if (intersectionCard == 2) {
526 if (G.numberOfEdges() == 0) {
532 for (
node v : G.nodes) {
537 if (idx >= m_separateCliqueSize) {
538 idx = m_separateCliqueSize - 1;
541 degrees[idx].pushBack(v);
544 for (
int k = degrees.
size(); k >= 2; --k) {
545 degrees[k - 2].conc(degrees[k - 1]);
546 if (degrees[k - 2].size() >= k) {
548 for (nodeSubset.
begin(k); nodeSubset.
valid(); nodeSubset.
next()) {
549 int countEdges = (k * (k - 1)) / 2;
550 for (
int j = 0; j < nodeSubset.
size(); ++j) {
551 test[nodeSubset[j]] =
true;
553 for (
edge e : G.edges) {
559 if (countEdges == 0) {
562 CoinPackedVector row;
564 for (
int j = 0; j < nodeSubset.
size(); ++j) {
565 int i =
id[nodeSubset[j]];
566 val += m_fullCompStore.extra(i);
569 if (val >= 1 + m_eps) {
570 m_osiSolver->addRow(row, 0, 1);
574 for (
int j = 0; j < nodeSubset.
size(); ++j) {
575 test[nodeSubset[j]] =
false;
An array that keeps track of the number of inserted elements; also usable as an efficient stack.
The namespace for all OGDF objects.
int size() const
Returns the cardinality of the subset.
Declaration and implementation of ArrayBuffer class.
double generateMinCutSeparationGraph(const ArrayBuffer< int > &activeComponents, node &source, node &target, GraphCopy &G, EdgeArray< double > &capacity, int &cutsFound)
Generates an auxiliary multi-graph for separation (during LP solving): directed, with special source ...
Includes declaration of graph class.
Enumerator for k-subsets of a given type.
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
const Array< node > & terminals(int id) const
Returns the list of terminals in the full component with given id.
const NodeArray< bool > & m_isTerminal
int find(disjoint_sets::CompressionOption< CompressionOptions::PathCompression >, int set)
void generateProblem()
Generate the basic LP model.
void begin(int low, int high)
Initializes the SubsetEnumerator to enumerate subsets of cardinalities from low to high.
bool valid() const
Checks if the current subset is valid. If not, the subset is either not initialized or all subsets ha...
bool separateMinCut(const ArrayBuffer< int > &activeComponents)
Perform the general cut-based separation algorithm.
Copies of graphs supporting edge splitting.
LPRelaxationSER(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, FullComponentWithExtraStore< T, double > &fullCompStore, T upperBound=0, int cliqueSize=0, double eps=1e-8)
Initialize the LP.
int makeSet()
Initializes a singleton set.
const int m_separateCliqueSize
bool addSubsetCoverConstraint(const ArrayBuffer< node > &subset)
Add subset cover constraints to the LP for a given subset of terminals.
If you use COIN-OR, you should use this class.
Computes a max flow via Preflow-Push (global relabeling and gap relabeling heuristic).
bool separate()
Perform all available separation algorithms.
edge newEdge(edge eOrig)
Creates a new edge (v,w) with original edge eOrig.
Class for adjacency list elements.
Definition of ogdf::CoinManager.
edge theEdge() const
Returns the edge associated with this adjacency entry.
virtual bool call(const Graph &graph, const EdgeArray< TCost > &weight, node s, node t, List< edge > &edgeList, edge e_st=nullptr) override
The actual algorithm call.
Class managing the component-based subtour elimination LP relaxation for the Steiner tree problem and...
int link(disjoint_sets::LinkOption< LinkOptions::Naive >, int set1, int set2)
void addTerminalCoverConstraint()
Add terminal cover constraints to the LP.
bool solve()
Solve the LP. The solution will be written to the extra data of the full component store.
int degree() const
Returns the degree of the node (indegree + outdegree).
Declaration and implementation of Goldberg-Tarjan max-flow algorithm with global relabeling and gap r...
bool separateConnected(const ArrayBuffer< int > &activeComponents)
Separate to ensure that the solution is connected.
FullComponentWithExtraStore< T, double > & m_fullCompStore
all enumerated full components, with solution
A Union/Find data structure for maintaining disjoint sets.
const List< node > & m_terminals
List of terminals.
Decralation of GraphElement and GraphList classes.
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
reverse_iterator rbegin()
Returns an reverse iterator to the last element.
int getNumberOfSets()
Returns the current number of disjoint sets.
INDEX size() const
Returns number of elements in the buffer.
void push(E e)
Puts a new element in the buffer.
iterator end()
Returns an iterator to one past the last element.
iterator begin()
Returns an iterator to the first element.
OsiSolverInterface * m_osiSolver
node source() const
Returns the source node of the edge.
node newNode(node vOrig)
Creates a new node in the graph copy with original node vOrig.
const double m_eps
epsilon for double operations
Declaration of graph copy classes.
const EdgeWeightedGraph< T > & m_G
Doubly linked lists (maintaining the length of the list).
RegisteredArray for nodes, edges and adjEntries of a graph.
Data type for general directed graphs (adjacency list representation).
iterator end()
Returns an iterator to one past the last element.
node twinNode() const
Returns the associated node of the corresponding adjacency entry (shorthand for twin()->theNode()).
bool separateCycles(const ArrayBuffer< int > &activeComponents)
Perform the separation algorithm for cycle constraints (to obtain stronger LP solution)
edge copy(edge e) const override
Returns the first edge in the list of edges corresponding to edge e.
iterator begin()
Returns an iterator to the first element.
void delEdge(edge e) override
Removes edge e and clears the list of edges corresponding to e's original edge.
virtual void init(const Graph &graph, EdgeArray< TCap > *flow=nullptr)
Initialize the problem with a graph and optional flow array. If no flow array is given,...
void addYConstraint(const node t)
Add constraint that the sum of x_C over all components C spanning terminal t is at least 1 to ensure ...
Basic declarations, included by all source files.
CoinPackedMatrix * m_matrix
node succ() const
Returns the successor in the list of all nodes.
Implementation of disjoint sets data structures (union-find functionality).
Declaration and implementation of Array class and Array algorithms.
Declaration of min-st-cut algorithms parameterized by max-flow alorithm.
#define OGDF_STEINERTREE_LPRELAXATIONSER_SEPARATE_CONNECTED_COMPONENTS
Class for the representation of edges.
Declaration of doubly linked lists and iterators.
void next()
Obtains the next subset if possible. The result should be checked using the valid() method.
void init(const Graph *base=nullptr)
Reinitializes the array. Associates the array with the matching registry of base.
INDEX size() const
Returns the size (number of elements) of the array.
TCap computeValue(const EdgeArray< TCap > &cap, const node &s, const node &t)
Compute only the value of the flow.
bool isInBackCut(const node v) const
Returns whether this node is part of the back cut.
node target() const
Returns the target node of the edge.
Class for the representation of nodes.
Min-st-cut algorithm, that calculates the cut via maxflow.
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
A class that allows to enumerate k-subsets.