Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

MinSteinerTreeDualAscent.h
Go to the documentation of this file.
1 
33 #pragma once
34 
35 #include <ogdf/basic/Graph.h>
36 #include <ogdf/basic/GraphCopy.h>
37 #include <ogdf/basic/GraphList.h>
38 #include <ogdf/basic/HashArray.h>
39 #include <ogdf/basic/List.h>
40 #include <ogdf/basic/basic.h>
44 
45 #include <iostream>
46 #include <limits>
47 
48 // enable this to print log
49 //#define OGDF_DUAL_ASCENT_LOGGING
50 
51 namespace ogdf {
52 template<typename T>
53 class EdgeWeightedGraph;
54 
64 template<typename T>
66 private:
67  const T MAX_VALUE = std::numeric_limits<T>::max();
68 
72 
76 
79 
81 
82  // components for the Steiner graph
84 
89  void init();
90 
99  bool isTerminal(const node v, bool rootIsTerminal) const;
100 
107  int findActiveComponent(node* terminal) const;
108 
115  int findComponent(const node v) const;
116 
123  List<edge>* computeCutSet(const node v) const;
124 
134  bool isActiveComponent(const node v) const;
135 
140  void updateComponents();
141 
142 protected:
158  T computeSteinerTree(const EdgeWeightedGraph<T>& G, const List<node>& terminals,
159  const NodeArray<bool>& isTerminal, EdgeWeightedGraphCopy<T>*& finalSteinerTree);
160 };
161 
162 template<typename T>
164  // local auxilary lists
165  List<node> nodes;
166  m_pOrigGraph->allNodes(nodes);
167  List<edge> edges;
168  m_pOrigGraph->allEdges(edges);
169 
170  // create directed graph
171  // and initialize slack variables
172  m_diGraph.setOriginalGraph(*m_pOrigGraph);
173  m_diGraph.clear();
174  m_edgeSlacks.init(m_diGraph);
175  m_origMapping.init(m_diGraph);
176 
177  // create resulting Steiner tree
178  m_steinerGraph.setOriginalGraph(m_diGraph);
179  m_steinerGraph.clear();
180  m_componentMapping.init(m_steinerGraph);
181 
182  // TODO: Better choices possible?
183  m_rootTerminal = m_pTerminals->chooseElement();
184 
185  for (node v : nodes) {
186  node w = m_diGraph.newNode(v);
187  m_steinerGraph.newNode(w);
188  }
189 
190  for (edge e : edges) {
191  node source = m_diGraph.copy(e->source());
192  node target = m_diGraph.copy(e->target());
193  edge copiedEdgeS = m_diGraph.newEdge(source, target);
194  edge copiedEdgeT = m_diGraph.newEdge(target, source);
195  m_edgeSlacks[copiedEdgeS] = m_edgeSlacks[copiedEdgeT] = m_pOrigGraph->weight(e);
196  m_origMapping[copiedEdgeS] = m_origMapping[copiedEdgeT] = e;
197  }
198  updateComponents();
199 
200 #ifdef OGDF_DUAL_ASCENT_LOGGING
201  std::cout << "directed graph has " << m_diGraph.numberOfNodes() << " nodes "
202  << "and " << m_diGraph.numberOfEdges() << " edges." << std::endl
203  << "root terminal is node " << m_rootTerminal << "." << std::endl;
204 #endif
205 }
206 
207 template<typename T>
209  OGDF_ASSERT(v->graphOf() == &m_steinerGraph);
210  OGDF_ASSERT(m_componentMapping[v] > -1);
211  return m_componentMapping[v];
212 }
213 
214 template<typename T>
216  strongComponents(m_steinerGraph, m_componentMapping);
217 }
218 
219 template<typename T>
220 bool MinSteinerTreeDualAscent<T>::isTerminal(const node v, bool rootIsTerminal) const {
221  OGDF_ASSERT(v->graphOf() == &m_steinerGraph);
222 
223  node w = m_diGraph.original(m_steinerGraph.original(v));
224  return (m_rootTerminal != w || rootIsTerminal) && (*m_pIsTerminal)[w];
225 }
226 
227 template<typename T>
229  int result = -1;
230 
231 #ifdef OGDF_DUAL_ASCENT_LOGGING
232  std::cout << " searching for active component.." << std::endl;
233 #endif
234 
235  HashArray<int, bool> checked(false);
236  for (auto it = m_pTerminals->begin(); it != m_pTerminals->end() && result == -1; it++) {
237  if (*it != m_rootTerminal) {
238  node v = m_steinerGraph.copy(m_diGraph.copy(*it));
239  int candidate = findComponent(v);
240  if (!checked[candidate]) {
241  checked[candidate] = true;
242  bool isRoot = isActiveComponent(v);
243 
244  if (isRoot) {
245  result = candidate;
246  *terminal = *it;
247 #ifdef OGDF_DUAL_ASCENT_LOGGING
248  std::cout << " active component found: component " << result << ", terminal "
249  << *terminal << std::endl;
250 #endif
251  }
252  }
253  }
254  }
255 
256 #ifdef OGDF_DUAL_ASCENT_LOGGING
257  if (result == -1) {
258  std::cout << " could not find an active component" << std::endl;
259  }
260 #endif
261 
262  return result;
263 }
264 
265 template<typename T>
267  OGDF_ASSERT(root->graphOf() == &m_steinerGraph);
268 
269  // establish "weakly connected" component of v (non-standard definition, see paper for details)
270  NodeArray<bool> visited(m_steinerGraph, false);
271  List<node> weakComp;
272  visited[root] = true;
273 
274  List<node> queue;
275  queue.pushBack(root);
276 
277  // determine all nodes connected to root
278  // meaning all nodes from which a directed path to root exists
279  while (!queue.empty()) {
280  node v = queue.popFrontRet();
281  weakComp.pushBack(v);
282  for (adjEntry adj : v->adjEntries) {
283  edge e = adj->theEdge();
284  node w = e->source();
285  if (!visited[w]) {
286  visited[w] = true;
287  queue.pushBack(w);
288  }
289  }
290  }
291 
292  // identify (incoming) cut edges
293  List<edge>* result = new List<edge>();
294 
295  for (node v : weakComp) {
296  node w = m_steinerGraph.original(v);
297  for (adjEntry adj : w->adjEntries) {
298  edge e = adj->theEdge();
299  if (!visited[m_steinerGraph.copy(e->source())]) {
300  result->pushBack(e);
301  OGDF_ASSERT(m_steinerGraph.copy(e) == nullptr);
302  }
303  }
304  }
305  return result;
306 }
307 
308 template<typename T>
310  OGDF_ASSERT(source->graphOf() == &m_steinerGraph);
311 
312  int comp = findComponent(source);
313  bool danglingTerminalFound = false;
314  bool hasTerminal = false;
315 
316 #ifdef OGDF_DUAL_ASCENT_LOGGING
317  std::cout << " checking whether component of node " << source << " is active.. " << std::endl;
318  std::cout << " component has id " << comp << std::endl;
319 #endif
320 
321  List<node> queue;
322  NodeArray<bool> visited(m_steinerGraph, false);
323  queue.pushBack(source);
324  visited[source] = true;
325 #ifdef OGDF_DUAL_ASCENT_LOGGING
326  while (!queue.empty()) {
327 #else
328  while (!queue.empty() && !danglingTerminalFound) {
329 #endif
330  node v = queue.popFrontRet();
331  hasTerminal |= isTerminal(v, false) && findComponent(v) == comp;
332  for (adjEntry adj : v->adjEntries) {
333  edge e = adj->theEdge();
334  node w = e->source();
335  if (!visited[w]) {
336  danglingTerminalFound |= isTerminal(w, true) && findComponent(w) != comp;
337  visited[w] = true;
338  queue.pushBack(w);
339  }
340  }
341  }
342 
343 #ifdef OGDF_DUAL_ASCENT_LOGGING
344  if (hasTerminal) {
345  std::cout << " component includes a terminal" << std::endl;
346  }
347  if (danglingTerminalFound) {
348  std::cout << " component has dangling terminal" << std::endl;
349  }
350  if (hasTerminal && !danglingTerminalFound) {
351  std::cout << " component is active!" << std::endl;
352  }
353 #endif
354  return hasTerminal && !danglingTerminalFound;
355 }
356 
357 template<typename T>
359  const List<node>& terminals, const NodeArray<bool>& isTerminal,
360  EdgeWeightedGraphCopy<T>*& finalSteinerTree) {
361 #ifdef OGDF_DUAL_ASCENT_LOGGING
362  std::cout << "MinSteinerTreeDualAscent called." << std::endl;
363  std::cout << "terminals are: ";
364 
365  for (node v : terminals) {
366  std::cout << v << " ";
367  }
368  std::cout << std::endl;
369 #endif
370  m_pOrigGraph = &G;
371  m_pTerminals = &terminals;
372  m_pIsTerminal = &isTerminal;
373 
374  init();
375 
376  // create resulting Steiner tree
377  finalSteinerTree = new EdgeWeightedGraphCopy<T>();
378  finalSteinerTree->setOriginalGraph(*m_pOrigGraph);
379  T result = 0;
380 
381  int comp = -1;
382  node terminal = nullptr;
383 
384 #ifdef OGDF_DUAL_ASCENT_LOGGING
385  std::cout << "main loop starting.." << std::endl;
386 #endif
387  while ((comp = findActiveComponent(&terminal)) != -1) {
388  // if active comonents exists we
389  // have one of its terminals
390  OGDF_ASSERT(terminal != nullptr);
391 
392  // find minimal cut edge
393  List<edge>* cutEdges = computeCutSet(m_steinerGraph.copy(m_diGraph.copy(terminal)));
394  edge minEdge = nullptr;
395  T minSlack = MAX_VALUE;
396 #ifdef OGDF_DUAL_ASCENT_LOGGING
397  std::cout << " cut edges:";
398 #endif
399  for (edge e : *cutEdges) {
400 #ifdef OGDF_DUAL_ASCENT_LOGGING
401  std::cout << " " << e;
402 #endif
403  if (m_edgeSlacks[e] < MAX_VALUE || minEdge == nullptr) {
404  minEdge = e;
405  minSlack = m_edgeSlacks[e];
406  }
407  }
408 #ifdef OGDF_DUAL_ASCENT_LOGGING
409  std::cout << std::endl << " next edge: " << minEdge << std::endl;
410 #endif
411  OGDF_ASSERT(minEdge != nullptr);
412 
413  // update slack variables
414  for (edge e : *cutEdges) {
415  m_edgeSlacks[e] -= minSlack;
416  }
417  delete cutEdges;
418 
419  // insert edge
420  m_steinerGraph.newEdge(minEdge);
421  updateComponents();
422 
423  // insert edge into final Steiner tree
424  edge origEdge = m_origMapping[minEdge];
425  T cost = m_pOrigGraph->weight(origEdge);
426  if (finalSteinerTree->copy(origEdge->source()) == nullptr) {
427  finalSteinerTree->newNode(origEdge->source());
428  }
429  if (finalSteinerTree->copy(origEdge->target()) == nullptr) {
430  finalSteinerTree->newNode(origEdge->target());
431  }
432  if (finalSteinerTree->copy(origEdge) == nullptr) {
433  finalSteinerTree->newEdge(origEdge, cost);
434  result += cost;
435  }
436  }
437 
438 #ifdef OGDF_DUAL_ASCENT_LOGGING
439  std::cout << "removing expendable edges" << std::endl;
440 #endif
441  result -= this->pruneAllDanglingSteinerPaths(*finalSteinerTree, *m_pIsTerminal);
442  result -= this->removeCyclesFrom(*finalSteinerTree, *m_pIsTerminal);
443 
444 #ifdef OGDF_DUAL_ASCENT_LOGGING
445  std::cout << "algorithm terminated." << std::endl;
446 #endif
447  return result;
448 }
449 
450 }
ogdf::MinSteinerTreeDualAscent::init
void init()
Intializes all relevant variables.
Definition: MinSteinerTreeDualAscent.h:163
HashArray.h
Declaration and implementation of HashArray class.
ogdf
The namespace for all OGDF objects.
Definition: multilevelmixer.cpp:39
Graph.h
Includes declaration of graph class.
ogdf::MinSteinerTreeDualAscent::m_origMapping
EdgeArray< edge > m_origMapping
maps each directed edge to its undirected original
Definition: MinSteinerTreeDualAscent.h:75
OGDF_ASSERT
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition: basic.h:66
ogdf::MinSteinerTreeDualAscent::m_edgeInclusions
EdgeArray< bool > m_edgeInclusions
stores the resulting Steiner tree
Definition: MinSteinerTreeDualAscent.h:77
ogdf::MinSteinerTreeDualAscent::m_steinerGraph
GraphCopy m_steinerGraph
the to-be constructed "almost" Steiner tree
Definition: MinSteinerTreeDualAscent.h:74
ogdf::EdgeWeightedGraphCopy::setOriginalGraph
void setOriginalGraph(const Graph *wG) override
Associates the graph copy with G, but does not create any nodes or edges.
Definition: EdgeWeightedGraphCopy.h:162
ogdf::List::popFrontRet
E popFrontRet()
Removes the first element from the list and returns it.
Definition: List.h:1591
ogdf::GraphCopy
Copies of graphs supporting edge splitting.
Definition: GraphCopy.h:391
ogdf::EdgeWeightedGraphCopy::newEdge
edge newEdge(node u, node v, T weight)
Definition: EdgeWeightedGraphCopy.h:169
ogdf::MinSteinerTreeDualAscent::m_edgeSlacks
EdgeArray< T > m_edgeSlacks
slack variables for each directed edge representing the adjusted weight
Definition: MinSteinerTreeDualAscent.h:78
ogdf::MinSteinerTreeDualAscent::findComponent
int findComponent(const node v) const
Returns the component of node v.
Definition: MinSteinerTreeDualAscent.h:208
ogdf::AdjElement
Class for adjacency list elements.
Definition: Graph_d.h:142
EdgeWeightedGraphCopy.h
Extends the GraphCopy concept to weighted graphs.
ogdf::MinSteinerTreeModule
Serves as an interface for various methods to compute or approximate minimum Steiner trees on undirec...
Definition: MinSteinerTreeModule.h:72
MinSteinerTreeModule.h
Declaration of ogdf::MinSteinerTreeModule.
ogdf::MinSteinerTreeDualAscent::computeSteinerTree
T computeSteinerTree(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, EdgeWeightedGraphCopy< T > *&finalSteinerTree)
Creates a minimum Steiner tree given a weighted graph and a list of terminals.
Definition: MinSteinerTreeDualAscent.h:358
ogdf::AdjElement::theEdge
edge theEdge() const
Returns the edge associated with this adjacency entry.
Definition: Graph_d.h:160
ogdf::EdgeWeightedGraph
Definition: GraphIO.h:56
GraphList.h
Decralation of GraphElement and GraphList classes.
ogdf::NodeElement::adjEntries
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
Definition: Graph_d.h:271
ogdf::HashArray
Indexed arrays using hashing for element access.
Definition: HashArray.h:93
ogdf::strongComponents
int strongComponents(const Graph &G, NodeArray< int > &component)
Computes the strongly connected components of the digraph G.
ogdf::MinSteinerTreeDualAscent::m_pOrigGraph
const EdgeWeightedGraph< T > * m_pOrigGraph
original graph passed to the module
Definition: MinSteinerTreeDualAscent.h:69
ogdf::EdgeElement::source
node source() const
Returns the source node of the edge.
Definition: Graph_d.h:398
ogdf::MinSteinerTreeDualAscent::m_pIsTerminal
const NodeArray< bool > * m_pIsTerminal
terminal incidence vector passed to the module
Definition: MinSteinerTreeDualAscent.h:71
ogdf::GraphCopyBase::newNode
node newNode(node vOrig)
Creates a new node in the graph copy with original node vOrig.
Definition: GraphCopy.h:192
GraphCopy.h
Declaration of graph copy classes.
ogdf::List
Doubly linked lists (maintaining the length of the list).
Definition: DfsMakeBiconnected.h:40
ogdf::internal::GraphRegisteredArray
RegisteredArray for nodes, edges and adjEntries of a graph.
Definition: Graph_d.h:658
ogdf::MinSteinerTreeDualAscent::m_diGraph
GraphCopy m_diGraph
the directed graph
Definition: MinSteinerTreeDualAscent.h:73
ogdf::MinSteinerTreeDualAscent::findActiveComponent
int findActiveComponent(node *terminal) const
Searches for the next active component.
Definition: MinSteinerTreeDualAscent.h:228
ogdf::MinSteinerTreeDualAscent
Dual ascent heuristic for the minimum Steiner tree problem.
Definition: MinSteinerTreeDualAscent.h:65
ogdf::GraphCopy::copy
edge copy(edge e) const override
Returns the first edge in the list of edges corresponding to edge e.
Definition: GraphCopy.h:464
ogdf::MinSteinerTreeDualAscent::m_rootTerminal
node m_rootTerminal
root node
Definition: MinSteinerTreeDualAscent.h:80
ogdf::MinSteinerTreeDualAscent::m_pTerminals
const List< node > * m_pTerminals
list of terminals passed to the module
Definition: MinSteinerTreeDualAscent.h:70
ogdf::MinSteinerTreeDualAscent::updateComponents
void updateComponents()
Re-establishes all strongly connected components for the Steiner graph.
Definition: MinSteinerTreeDualAscent.h:215
ogdf::graphics::init
void init()
Definition: graphics.h:450
basic.h
Basic declarations, included by all source files.
ogdf::EdgeWeightedGraphCopy
Definition: EdgeWeightedGraphCopy.h:44
ogdf::MinSteinerTreeDualAscent::isTerminal
bool isTerminal(const node v, bool rootIsTerminal) const
Returns whether this node is a terminal.
Definition: MinSteinerTreeDualAscent.h:220
ogdf::MinSteinerTreeDualAscent::isActiveComponent
bool isActiveComponent(const node v) const
Determines whether a strongly connected component is active (paper says "is root component").
Definition: MinSteinerTreeDualAscent.h:309
ogdf::EdgeElement
Class for the representation of edges.
Definition: Graph_d.h:363
List.h
Declaration of doubly linked lists and iterators.
ogdf::NodeElement::graphOf
const Graph * graphOf() const
Returns the graph containing this node (debug only).
Definition: Graph_d.h:344
ogdf::MinSteinerTreeDualAscent::MAX_VALUE
const T MAX_VALUE
Definition: MinSteinerTreeDualAscent.h:67
ogdf::EdgeElement::target
node target() const
Returns the target node of the edge.
Definition: Graph_d.h:401
ogdf::MinSteinerTreeDualAscent::m_componentMapping
NodeArray< int > m_componentMapping
maps each node to its component
Definition: MinSteinerTreeDualAscent.h:83
ogdf::NodeElement
Class for the representation of nodes.
Definition: Graph_d.h:240
simple_graph_alg.h
Declaration of simple graph algorithms.
ogdf::MinSteinerTreeDualAscent::computeCutSet
List< edge > * computeCutSet(const node v) const
Returns all incoming cut edges of the component of v.
Definition: MinSteinerTreeDualAscent.h:266
ogdf::List::pushBack
iterator pushBack(const E &x)
Adds element x at the end of the list.
Definition: List.h:1547
ogdf::internal::EdgeArrayBase2
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
Definition: Graph_d.h:716
ogdf::List::clear
void clear()
Removes all elements from the list.
Definition: List.h:1626