Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
MinSteinerTreeRZLoss.h
Go to the documentation of this file.
1
33#pragma once
34
35#include <ogdf/basic/Graph.h>
36#include <ogdf/basic/List.h>
38#include <ogdf/basic/basic.h>
52
53#include <algorithm>
54#include <memory>
55#include <set>
56#include <vector>
57
58namespace ogdf {
59template<typename T>
60class EdgeWeightedGraph;
61} // namespace ogdf
62
63#define OGDF_STEINERTREE_RZLOSS_REDUCE_ON
64
65namespace ogdf {
66
78template<typename T>
81
82 class Main;
83
84 std::unique_ptr<Main> m_alg;
85
86public:
88
90
92
97 void setMaxComponentSize(int k) { m_restricted = k; }
98
101 if (m_alg == nullptr) {
102 return 0;
103 }
104 return m_alg->numberOfGeneratedComponents();
105 }
106
109 if (m_alg == nullptr) {
110 return 0;
111 }
112 return m_alg->numberOfContractedComponents();
113 }
114
117 if (m_alg == nullptr) {
118 return 0;
119 }
120 return m_alg->numberOfComponentLookUps();
121 }
122
123protected:
132 virtual T computeSteinerTree(const EdgeWeightedGraph<T>& G, const List<node>& terminals,
133 const NodeArray<bool>& isTerminal, EdgeWeightedGraphCopy<T>*& finalSteinerTree) override {
134 m_alg.reset(new Main(G, terminals, isTerminal, m_restricted));
135 return m_alg->getApproximation(finalSteinerTree);
136 }
137};
138
139template<typename T>
141 template<typename TYPE>
143
146
153 void findFullComponentsDW(const EdgeWeightedGraphCopy<T>& tree,
154 const NodeArray<NodeArray<T>>& distance, const NodeArray<NodeArray<edge>>& pred);
156 void findFullComponentsEMV(const EdgeWeightedGraphCopy<T>& tree);
158 void findFull3Components(const EdgeWeightedGraphCopy<T>& tree,
159 const NodeArray<NodeArray<T>>& distance, const NodeArray<NodeArray<edge>>& pred);
161 template<typename FCG>
162 void retrieveComponents(const FCG& fcg, const EdgeWeightedGraphCopy<T>& tree);
163
165
172 void generateInitialTerminalSpanningTree(EdgeWeightedGraphCopy<T>& steinerTree,
173 const NodeArray<NodeArray<T>>& distance, const NodeArray<NodeArray<edge>>& pred);
174
177 tree.setOriginalGraph(m_G);
178
179 if (m_restricted >= 4
181 m_G.numberOfEdges())) {
183 m_save.reset(new steiner_tree::SaveStatic<T>(tree));
184 findFullComponentsEMV(tree);
185 } else {
186 NodeArray<NodeArray<T>> distance;
188
190 m_G, m_terminals, m_isTerminal, m_restricted);
191
192 generateInitialTerminalSpanningTree(tree, distance, pred);
193 m_save.reset(new steiner_tree::SaveStatic<T>(tree));
194
195 if (m_restricted >= 4) { // use Dreyfus-Wagner based full component generation
196 findFullComponentsDW(tree, distance, pred);
197 } else {
198 findFull3Components(tree, distance, pred);
199 }
200 }
201
202 m_fullCompStore.computeAllLosses();
203 m_componentsGenerated = m_fullCompStore.size();
204 }
205
210 void multiPass(EdgeWeightedGraphCopy<T>& steinerTree);
211
218 double extractMaxComponent(const EdgeWeightedGraphCopy<T>& steinerTree, int& maxCompId);
219
226 template<typename TERMINAL_CONTAINER>
227 T gain(const TERMINAL_CONTAINER& terminals, const EdgeWeightedGraphCopy<T>& steinerTree);
228
234 void contractLoss(EdgeWeightedGraphCopy<T>& steinerTree, int compId);
235
240 std::unique_ptr<steiner_tree::SaveStatic<T>> m_save;
243
247
248public:
249 Main(const EdgeWeightedGraph<T>& G, const List<node>& terminals,
250 const NodeArray<bool>& isTerminal, int restricted);
251
252 T getApproximation(EdgeWeightedGraphCopy<T>*& finalSteinerTree) const {
253 // obtain final Steiner Tree using (MST-based) Steiner tree approximation algorithm
254 return steiner_tree::obtainFinalSteinerTree(m_G, m_isNewTerminal, m_isTerminal,
255 finalSteinerTree);
256 }
257
259 long numberOfGeneratedComponents() { return m_componentsGenerated; }
260
262 long numberOfContractedComponents() { return m_componentsContracted; }
263
265 long numberOfComponentLookUps() { return m_componentsLookUps; }
266};
267
268template<typename T>
270 const NodeArray<bool>& isTerminal, int restricted)
271 : m_G(G)
272 , m_isTerminal(isTerminal)
273 , m_terminals(terminals)
274 , // copy
275 m_restricted(min(restricted, terminals.size()))
276 , m_fullCompStore(m_G, m_terminals, m_isTerminal)
277 , m_isNewTerminal(m_G, false)
278 , m_componentsGenerated(0)
279 , m_componentsContracted(0)
280 , m_componentsLookUps(0) {
282
283 for (node v : terminals) {
284 m_isNewTerminal[v] = true;
285 }
286
287 // init terminal-spanning tree and its save-edge data structure
288 EdgeWeightedGraphCopy<T> steinerTree; // the terminal-spanning tree to be modified
289
290 setup(steinerTree);
291
292 // contraction phase
293 multiPass(steinerTree);
294
295 m_save.reset();
296}
297
298template<typename T>
300 EdgeWeightedGraphCopy<T>& steinerTree, const NodeArray<NodeArray<T>>& distance,
301 const NodeArray<NodeArray<edge>>& pred) {
302 // generate complete graph
303 for (node v : m_terminals) {
304 steinerTree.newNode(v);
305 }
306 for (node u : steinerTree.nodes) {
307 const node uO = steinerTree.original(u);
308 const NodeArray<T>& dist = distance[uO];
309 const NodeArray<edge>& p = pred[uO];
310 for (node v = u->succ(); v; v = v->succ()) {
311 const node vO = steinerTree.original(v);
312 if (p[vO] != nullptr) {
313 steinerTree.newEdge(u, v, dist[vO]);
314 }
315 }
316 }
317 // compute MST
318 makeMinimumSpanningTree(steinerTree, steinerTree.edgeWeights());
319 OGDF_ASSERT(steinerTree.numberOfNodes() == steinerTree.numberOfEdges() + 1);
320}
321
322template<typename T>
324 const NodeArray<NodeArray<T>>& distance, const NodeArray<NodeArray<edge>>& pred) {
326 fcg.call(m_G, m_terminals, m_isTerminal, distance, pred,
327 [&](node t0, node t1, node t2, node minCenter, T minCost) {
328 // create a full 3-component
330 minComp.setOriginalGraph(m_G);
331 node minCenterC = minComp.newNode(minCenter);
332 minComp.newEdge(minComp.newNode(t0), minCenterC, distance[t0][minCenter]);
333 minComp.newEdge(minComp.newNode(t1), minCenterC, distance[t1][minCenter]);
334 minComp.newEdge(minComp.newNode(t2), minCenterC, distance[t2][minCenter]);
335 OGDF_ASSERT(isTree(minComp));
336#ifdef OGDF_STEINERTREE_RZLOSS_REDUCE_ON
337 if (gain(std::vector<node> {t0, t1, t2}, tree) > minCost) { // reduction
338 m_fullCompStore.insert(minComp);
339 }
340#else
341 m_fullCompStore.insert(minComp);
342#endif
343 });
344}
345
346template<typename T>
347template<typename FCG>
350 SubsetEnumerator<node> terminalSubset(m_terminals);
351 for (terminalSubset.begin(3, m_restricted); terminalSubset.valid(); terminalSubset.next()) {
352 EdgeWeightedGraphCopy<T> component;
353 List<node> terminals;
354 terminalSubset.list(terminals);
355#ifdef OGDF_STEINERTREE_RZLOSS_REDUCE_ON
356 T cost =
357#endif
358 fcg.getSteinerTreeFor(terminals, component);
359 if (
361 gain(terminals, tree) > cost &&
362#endif
363 fcg.isValidComponent(component)) {
364 m_fullCompStore.insert(component);
365 }
366 }
367}
368
369template<typename T>
371 const NodeArray<NodeArray<T>>& distance, const NodeArray<NodeArray<edge>>& pred) {
372 steiner_tree::FullComponentGeneratorDreyfusWagner<T> fcg(m_G, m_terminals, m_isTerminal,
373 distance, pred);
374 fcg.call(m_restricted);
375 retrieveComponents(fcg, tree);
376}
377
378template<typename T>
385
386template<typename T>
388 while (!m_fullCompStore.isEmpty()) {
389 int maxCompId;
390 double r = extractMaxComponent(steinerTree, maxCompId);
391 if (r > 1e-9) {
392 ++m_componentsContracted;
393
394 // convert nodes of component to terminals
395 m_fullCompStore.foreachNode(maxCompId, [&](node v) { m_isNewTerminal[v] = true; });
396
397 contractLoss(steinerTree, maxCompId);
398 m_fullCompStore.remove(maxCompId);
399
400 if (!m_fullCompStore.isEmpty()) {
401 m_save->rebuild();
402 }
403 } else {
404 return;
405 }
406 }
407}
408
409template<typename T>
411 const EdgeWeightedGraphCopy<T>& steinerTree, int& maxCompId) {
412 maxCompId = -1;
413 double max(0);
414 for (int i = 0; i < m_fullCompStore.size();) {
415 ++m_componentsLookUps;
416 const double winAbs =
417 gain(m_fullCompStore.terminals(i), steinerTree) - m_fullCompStore.cost(i);
418 if (winAbs > 1e-9) {
419 const double r = winAbs / m_fullCompStore.loss(i);
420 if (r > max) {
421 max = r;
422 maxCompId = i;
423 }
424 ++i;
425 }
426#ifdef OGDF_STEINERTREE_RZLOSS_REDUCE_ON
427 else {
428 // reduction
429 m_fullCompStore.remove(i);
430 }
431#endif
432 }
433 return max;
434}
435
436template<typename T>
437template<typename TERMINAL_CONTAINER>
438T MinSteinerTreeRZLoss<T>::Main::gain(const TERMINAL_CONTAINER& terminals,
439 const EdgeWeightedGraphCopy<T>& steinerTree) {
440 std::set<edge> saveEdges;
441 T result(0);
442
443 // extract edges and compute their sum (result value)
444 for (auto it1 = terminals.begin(); it1 != terminals.end(); ++it1) {
445 auto next = it1;
446 ++next;
447 for (auto it2 = next; it2 != terminals.end(); ++it2) {
448 const edge e = m_save->saveEdge(*it1, *it2);
449 saveEdges.insert(e);
450 }
451 }
452
453 for (edge e : saveEdges) {
454 result += steinerTree.weight(e);
455 }
456 return result;
457}
458
459template<typename T>
461 // for every non-loss edge {st} in the component,
462 // where s belongs to the loss component of terminal u
463 // and t belongs to the loss component of terminal v,
464 // we insert edges from u to v in the terminal-spanning tree
465 for (edge e : m_fullCompStore.lossBridges(compId)) {
466 const node u = m_fullCompStore.lossTerminal(e->source());
467 OGDF_ASSERT(u);
468 const node v = m_fullCompStore.lossTerminal(e->target());
469 OGDF_ASSERT(v);
470 steinerTree.newEdge(steinerTree.copy(u), steinerTree.copy(v),
471 m_fullCompStore.graph().weight(e));
472 // parallel edges are ok, will be removed by MST
473 }
474 if (steinerTree.numberOfNodes() != steinerTree.numberOfEdges() + 1) {
475 makeMinimumSpanningTree(steinerTree, steinerTree.edgeWeights());
476 }
477}
478
479}
Extends the GraphCopy concept to weighted graphs.
Definition of ogdf::steiner_tree::Full3ComponentGeneratorVoronoi class template.
Definition of the FullComponentDecisions class.
Definition of the FullComponentGeneratorCaller class template.
Definition of the ogdf::steiner_tree::FullComponentGeneratorDreyfusWagner class template.
Definition of the ogdf::steiner_tree::FullComponentGeneratorDreyfusWagnerWithoutMatrix class template...
Definition of the FullComponentStore class template.
Includes declaration of graph class.
Declaration of doubly linked lists and iterators.
Implementation of Mehlhorn's minimum Steiner tree 2(1-1/l)-approximation algorithm.
Declaration of ogdf::MinSteinerTreeModule.
#define OGDF_STEINERTREE_RZLOSS_REDUCE_ON
Implementation of the staticLCATree option for calculating save edges in Zelikovsky's 11/6-approximat...
A class that allows to enumerate k-subsets.
Basic declarations, included by all source files.
Class for the representation of edges.
Definition Graph_d.h:364
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.
const EdgeArray< T > & edgeWeights() const
node newNode(node vOrig)
Creates a new node in the graph copy with original node vOrig.
Definition GraphCopy.h:191
const Graph & original() const
Returns a reference to the original graph.
Definition GraphCopy.h:104
edge copy(edge e) const override
Returns the first edge in the list of edges corresponding to edge e.
Definition GraphCopy.h:463
int numberOfNodes() const
Returns the number of nodes in the graph.
Definition Graph_d.h:979
int numberOfEdges() const
Returns the number of edges in the graph.
Definition Graph_d.h:982
internal::GraphObjectContainer< NodeElement > nodes
The container containing all node objects.
Definition Graph_d.h:929
Doubly linked lists (maintaining the length of the list).
Definition List.h:1451
Serves as an interface for various methods to compute or approximate minimum Steiner trees on undirec...
static void sortTerminals(List< node > &terminals)
Sort terminals by index.
double extractMaxComponent(const EdgeWeightedGraphCopy< T > &steinerTree, int &maxCompId)
Traverses over all full components and finds the one with the highest win-objective.
std::unique_ptr< steiner_tree::SaveStatic< T > > m_save
The save data structure.
const EdgeWeightedGraph< T > & m_G
The original edge-weighted graph.
NodeArray< bool > m_isNewTerminal
Incidence vector for nonterminal nodes marked as terminals for improvement.
Main(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, int restricted)
long m_componentsGenerated
Number of generated components.
long m_componentsContracted
Number of contracted components.
long numberOfContractedComponents()
Returns the number of contracted components.
const NodeArray< bool > & m_isTerminal
Incidence vector for terminal nodes.
void findFullComponentsEMV(const EdgeWeightedGraphCopy< T > &tree)
Find full components using algorithm by Erickson et al.
void retrieveComponents(const FCG &fcg, const EdgeWeightedGraphCopy< T > &tree)
Auxiliary function to retrieve components by Dreyfus-Wagner/Erickson et al implementation.
long numberOfComponentLookUps()
Returns the number of components lookups during execution time.
T gain(const TERMINAL_CONTAINER &terminals, const EdgeWeightedGraphCopy< T > &steinerTree)
void generateInitialTerminalSpanningTree(EdgeWeightedGraphCopy< T > &steinerTree, const NodeArray< NodeArray< T > > &distance, const NodeArray< NodeArray< edge > > &pred)
Builds a minimum terminal spanning tree (via a MST call on the complete terminal graph)
long numberOfGeneratedComponents()
Returns the number of generated components.
void findFullComponentsDW(const EdgeWeightedGraphCopy< T > &tree, const NodeArray< NodeArray< T > > &distance, const NodeArray< NodeArray< edge > > &pred)
Find full components using algorithm by Dreyfus-Wagner.
steiner_tree::FullComponentWithLossStore< T > m_fullCompStore
All generated full components.
long m_componentsLookUps
Number of components lookups.
void findFull3Components(const EdgeWeightedGraphCopy< T > &tree, const NodeArray< NodeArray< T > > &distance, const NodeArray< NodeArray< edge > > &pred)
Find full components of size 3.
void multiPass(EdgeWeightedGraphCopy< T > &steinerTree)
Contraction phase of the algorithm.
List< node > m_terminals
List of terminal nodes (will be copied and sorted)
void contractLoss(EdgeWeightedGraphCopy< T > &steinerTree, int compId)
Contracts the loss of a full component and integrates it into the given terminal-spanning tree.
void setup(EdgeWeightedGraphCopy< T > &tree)
Setup (build initial terminal-spanning tree, its save data structure, and find all components)
T getApproximation(EdgeWeightedGraphCopy< T > *&finalSteinerTree) const
int m_restricted
Parameter for the number of terminals in a full component.
This class implements the loss-contracting (1.55+epsilon)-approximation algorithm for the Steiner tre...
long numberOfComponentLookUps()
Returns the number of components lookups during execution time.
virtual T computeSteinerTree(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, EdgeWeightedGraphCopy< T > *&finalSteinerTree) override
Builds a minimum Steiner tree given a weighted graph and a list of terminals.
long numberOfContractedComponents()
Returns the number of contracted components.
long numberOfGeneratedComponents()
Returns the number of generated components.
void setMaxComponentSize(int k)
Sets the maximal number of terminals in a full component.
std::unique_ptr< Main > m_alg
Class for the representation of nodes.
Definition Graph_d.h:241
node succ() const
Returns the successor in the list of all nodes.
Definition Graph_d.h:293
Enumerator for k-subsets of a given type.
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...
void list(List< T > &subset) const
Obtains (appends) a list of the subset members.
void next()
Obtains the next subset if possible. The result should be checked using the valid() method.
RegisteredArray for nodes, edges and adjEntries of a graph.
Definition Graph_d.h:659
Full 3-component generation using Voronoi regions.
void call(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, const NodeArray< NodeArray< T > > &distance, const NodeArray< NodeArray< edge > > &pred, std::function< void(node, node, node, node, T)> generateFunction) const
Generate full components and call generateFunction for each full component.
static void computeDistanceMatrix(NodeArray< NodeArray< T > > &distance, NodeArray< NodeArray< edge > > &pred, const EdgeWeightedGraph< T > &graph, const List< node > &terminals, const NodeArray< bool > &isTerminal, int restricted)
Computes distance and predecessor matrix.
A generator for restricted full components (for Steiner tree approximations) based on the Dreyfus-Wag...
A generator for restricted full components (for Steiner tree approximations) based on the Dreyfus-Wag...
A data structure to store full components with additional "loss" functionality.
This class behaves basically the same as SaveDynamic except that the update of the weighted graph is ...
Definition SaveStatic.h:57
Algorithms used by at least two functions of Steiner tree code or its internal helpers.
Declaration of extended graph algorithms.
T makeMinimumSpanningTree(Graph &G, const EdgeArray< T > &weight)
Reduce a graph to its minimum spanning tree (MST) using Kruskal's algorithm.
bool isTree(const Graph &G)
Returns true iff G is a tree, i.e. contains no undirected cycle and is connected.
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition basic.h:52
T obtainFinalSteinerTree(const EdgeWeightedGraph< T > &G, const NodeArray< bool > &isTerminal, const NodeArray< bool > &isOriginalTerminal, EdgeWeightedGraphCopy< T > *&finalSteinerTree)
T constructTerminalSpanningTreeUsingVoronoiRegions(EdgeWeightedGraphCopy< T > &terminalSpanningTree, const EdgeWeightedGraph< T > &graph, const List< node > &terminals)
The namespace for all OGDF objects.
@ tree
for every hyperedge e a minimal subcubic tree connecting all hypernodes incident with e together is a...
Declaration of simple graph algorithms.
static bool shouldUseErickson(int n, int m)
Returns true iff the rule of thumb predicts to use the algorithm by Erickson et al instead of the Dre...