Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
MinSteinerTreeDualAscent.h
Go to the documentation of this file.
1
33#pragma once
34
35#include <ogdf/basic/Graph.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
51namespace ogdf {
52template<typename T>
53class EdgeWeightedGraph;
54
64template<typename T>
66private:
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
142protected:
158 T computeSteinerTree(const EdgeWeightedGraph<T>& G, const List<node>& terminals,
159 const NodeArray<bool>& isTerminal, EdgeWeightedGraphCopy<T>*& finalSteinerTree);
160};
161
162template<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
207template<typename T>
209 OGDF_ASSERT(v->graphOf() == &m_steinerGraph);
210 OGDF_ASSERT(m_componentMapping[v] > -1);
211 return m_componentMapping[v];
212}
213
214template<typename T>
216 strongComponents(m_steinerGraph, m_componentMapping);
217}
218
219template<typename T>
220bool 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
227template<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
265template<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
308template<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
357template<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}
Extends the GraphCopy concept to weighted graphs.
Includes declaration of graph class.
Declaration of graph copy classes.
Decralation of GraphElement and GraphList classes.
Declaration and implementation of HashArray class.
Declaration of doubly linked lists and iterators.
Declaration of ogdf::MinSteinerTreeModule.
Basic declarations, included by all source files.
Class for adjacency list elements.
Definition Graph_d.h:143
Class for the representation of edges.
Definition Graph_d.h:364
node target() const
Returns the target node of the edge.
Definition Graph_d.h:402
node source() const
Returns the source node of the edge.
Definition Graph_d.h:399
edge newEdge(node u, node v, T weight)
void setOriginalGraph(const Graph *wG) override
Re-initializes the copy using G (which might be null), but does not create any nodes or edges.
node newNode(node vOrig)
Creates a new node in the graph copy with original node vOrig.
Definition GraphCopy.h:191
Copies of graphs supporting edge splitting.
Definition GraphCopy.h:390
edge copy(edge e) const override
Returns the first edge in the list of edges corresponding to edge e.
Definition GraphCopy.h:463
Indexed arrays using hashing for element access.
Definition HashArray.h:93
Doubly linked lists (maintaining the length of the list).
Definition List.h:1451
void clear()
Removes all elements from the list.
Definition List.h:1626
iterator pushBack(const E &x)
Adds element x at the end of the list.
Definition List.h:1547
E popFrontRet()
Removes the first element from the list and returns it.
Definition List.h:1591
bool empty() const
Returns true iff the list is empty.
Definition List.h:286
Dual ascent heuristic for the minimum Steiner tree problem.
GraphCopy m_steinerGraph
the to-be constructed "almost" Steiner tree
GraphCopy m_diGraph
the directed graph
void init()
Intializes all relevant variables.
NodeArray< int > m_componentMapping
maps each node to its component
List< edge > * computeCutSet(const node v) const
Returns all incoming cut edges of the component of v.
bool isTerminal(const node v, bool rootIsTerminal) const
Returns whether this node is a terminal.
EdgeArray< T > m_edgeSlacks
slack variables for each directed edge representing the adjusted weight
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.
void updateComponents()
Re-establishes all strongly connected components for the Steiner graph.
const List< node > * m_pTerminals
list of terminals passed to the module
EdgeArray< edge > m_origMapping
maps each directed edge to its undirected original
const EdgeWeightedGraph< T > * m_pOrigGraph
original graph passed to the module
int findActiveComponent(node *terminal) const
Searches for the next active component.
EdgeArray< bool > m_edgeInclusions
stores the resulting Steiner tree
const NodeArray< bool > * m_pIsTerminal
terminal incidence vector passed to the module
int findComponent(const node v) const
Returns the component of node v.
bool isActiveComponent(const node v) const
Determines whether a strongly connected component is active (paper says "is root component").
Serves as an interface for various methods to compute or approximate minimum Steiner trees on undirec...
Class for the representation of nodes.
Definition Graph_d.h:241
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
Definition Graph_d.h:272
const Graph * graphOf() const
Returns the graph containing this node (debug only).
Definition Graph_d.h:345
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
Definition Graph_d.h:717
RegisteredArray for nodes, edges and adjEntries of a graph.
Definition Graph_d.h:659
int strongComponents(const Graph &G, NodeArray< int > &component)
Computes the strongly connected components of the digraph G.
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition basic.h:52
The namespace for all OGDF objects.
Declaration of simple graph algorithms.