Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
FullComponentGeneratorDreyfusWagnerWithoutMatrix.h
Go to the documentation of this file.
1
32#pragma once
33
35#include <ogdf/basic/Graph.h>
36#include <ogdf/basic/Hashing.h>
37#include <ogdf/basic/List.h>
38#include <ogdf/basic/Math.h>
40#include <ogdf/basic/basic.h>
44
45#include <limits>
46
47namespace ogdf {
48template<typename T>
49class EdgeWeightedGraphCopy;
50
51namespace steiner_tree {
52
64template<typename T>
69
78
80
81 public:
83 AuxiliaryGraph(const EdgeWeightedGraph<T>& orig, const List<node>& terminals)
84 : m_original(orig)
86 , m_origOfNode(m_copy, nullptr)
87 , m_origOfEdge(m_copy, nullptr)
88 , m_isTerminal(m_copy, false) {
89 // copy nodes and edges
90 for (node v : m_original.nodes) {
91 node vCopy = m_copy.newNode();
92 m_copyOfNode[v] = vCopy;
93 m_origOfNode[vCopy] = v;
94 }
95 for (edge e : m_original.edges) {
96 edge eCopy =
97 m_copy.newEdge(copy(e->source()), copy(e->target()), m_original.weight(e));
98 m_origOfEdge[eCopy] = e;
99 }
100
101 // set terminals
102 for (node v : terminals) {
103 m_isTerminal[copy(v)] = true;
104 }
105
106 // initialize source node and its edges to every other node
107 m_source = m_copy.newNode();
108 for (node w : m_original.nodes) {
109 m_copy.newEdge(m_source, copy(w), std::numeric_limits<T>::max());
110 }
111 }
112
114 node copy(node v) const {
116 return m_copyOfNode[v];
117 }
118
120 node original(node v) const {
121 OGDF_ASSERT(v->graphOf() == &m_copy);
122 return m_origOfNode[v];
123 }
124
126 edge original(edge e) const {
127 OGDF_ASSERT(e->graphOf() == &m_copy);
128 return m_origOfEdge[e];
129 }
130
132 node source() const { return m_source; }
133
135 const EdgeWeightedGraph<T>& graph() const { return m_copy; }
136
138 const NodeArray<bool>& terminalArray() const { return m_isTerminal; }
139
141 T weight(edge e) const {
142 OGDF_ASSERT(e->graphOf() == &m_copy);
143 return m_copy.weight(e);
144 }
145
147 void setWeight(edge e, T value) {
148 OGDF_ASSERT(e->graphOf() == &m_copy);
149 m_copy.setWeight(e, value);
150 }
151 };
152
155
157 struct DWMData {
161
162 DWMData(T _cost, ArrayBuffer<edge> _edges) : cost(_cost), edges(_edges) { }
163
164 explicit DWMData(T _cost = std::numeric_limits<T>::max()) : cost(_cost) { }
165
167 void invalidate() {
168 edges.clear();
169 subgraphs.clear();
170 }
171
173 bool valid() const { return cost == 0 || !(edges.empty() && subgraphs.empty()); }
174
176 void add(const DWMData* other) {
177 if (valid()) {
178 if (other->valid()) {
179 subgraphs.push(other);
180 } else {
181 invalidate();
182 }
183 }
184 cost += other->cost;
185 }
186
188 void clear() {
189 invalidate();
190 cost = 0; // make it valid again
191 }
192
194 void add(edge e, T c) {
195 if (valid()) {
196 edges.push(e);
197 }
198 cost += c;
199 }
200 };
201
203 struct DWMSplit {
204 T cost = std::numeric_limits<T>::max();
205 const DWMData* subgraph1 = nullptr;
206 const DWMData* subgraph2 = nullptr;
207
209 void set(const DWMData* s1, const DWMData* s2) {
210 subgraph1 = s1;
211 subgraph2 = s2;
212 cost = s1->cost + s2->cost;
213 }
214 };
215
217 class SortedNodeListHashFunc;
218
221
223 const DWMData* dataOf(const List<node>& key) const {
224 OGDF_ASSERT(key.size() > 1);
225 OGDF_ASSERT(m_map.member(key));
226 return &m_map.lookup(key)->info();
227 }
228
230 T costOf(const List<node>& key) const {
231 OGDF_ASSERT(key.size() > 1);
232 return dataOf(key)->cost;
233 }
234
236 bool safeIfSumSmaller(const T summand1, const T summand2, const T compareValue) const {
237#ifdef OGDF_FULL_COMPONENT_GENERATION_ALWAYS_SAFE
238 return summand1 + summand2 < compareValue;
239#else
240 return summand1 < std::numeric_limits<T>::max() && summand2 < std::numeric_limits<T>::max()
241 && summand1 + summand2 < compareValue;
242#endif
243 }
244
256 static void sortedInserter(node w, List<node>& list, bool& inserted, node newNode) {
257 if (!inserted && w->index() > newNode->index()) {
258 list.pushBack(newNode);
259 inserted = true;
260 }
261 list.pushBack(w);
262 }
263
265 void makeKey(List<node>& newSubset, node v) const {
266 bool inserted = false;
267 m_terminalSubset.forEachMember([&](node w) { sortedInserter(w, newSubset, inserted, v); });
268 if (!inserted) {
269 newSubset.pushBack(v);
270 }
271 }
272
274 void makeKey(List<node>& newSubset, List<node>& newComplement,
275 const SubsetEnumerator<node>& subset, node v) const {
276 bool insertedIntoSubset = false;
277 bool insertedIntoComplement = false;
278 // Interestingly std::bind is much slower than using lambdas (at least on g++ 6.3)
280 [&](node w) { sortedInserter(w, newSubset, insertedIntoSubset, v); },
281 [&](node w) { sortedInserter(w, newComplement, insertedIntoComplement, v); });
282 if (!insertedIntoSubset) {
283 newSubset.pushBack(v);
284 }
285 if (!insertedIntoComplement) {
286 newComplement.pushBack(v);
287 }
288 }
289
296 OGDF_ASSERT(v->graphOf() == &m_G);
297 OGDF_ASSERT(split[v].subgraph1 == nullptr);
298 OGDF_ASSERT(split[v].subgraph2 == nullptr);
299
300 DWMSplit& best = split[v];
301 for (subset.begin(1, subset.numberOfMembersAndNonmembers() / 2); subset.valid();
302 subset.next()) {
303 List<node> newSubset, newComplement;
304 makeKey(newSubset, newComplement, subset, v);
305
306 if (safeIfSumSmaller(costOf(newSubset), costOf(newComplement), best.cost)) {
307 best.set(dataOf(newSubset), dataOf(newComplement));
308 }
309 }
310 }
311
313 template<typename CONTAINER>
314 void computePartialSolutions(const CONTAINER& targets) {
315 OGDF_ASSERT(m_terminalSubset.size() >= 2);
316
317 List<node> terminals;
318 m_terminalSubset.list(terminals);
319 SubsetEnumerator<node> subset(terminals); // done here because of linear running time
321
322 // update auxiliary graph
323 updateAuxGraph(split, subset, costOf(terminals));
324
325 // compute shortest-paths tree on graph
326 NodeArray<T> distance;
327 NodeArray<edge> pred;
329 m_auxG.terminalArray(), distance, pred);
330
331 // insert best subtrees
332 insertBestSubtrees(targets, split, pred, distance, terminals);
333 }
334
337 for (node t : m_terminals) {
338 NodeArray<T> distance;
339 NodeArray<edge> pred;
341
342 for (node v : m_G.nodes) {
343 // make key
344 List<node> key;
345 key.pushBack(t);
346 if (v->index() < t->index()) {
347 key.pushFront(v);
348 } else {
349 key.pushBack(v);
350 }
351
352 // insert if not already defined
353 if (!m_map.member(key)) {
354 T dist = distance[v];
355 ArrayBuffer<edge> edges;
356 for (node curr = v; pred[curr] != nullptr; curr = pred[curr]->opposite(curr)) {
357 edges.push(pred[curr]);
358 }
359 m_map.fastInsert(key, DWMData(dist, edges));
360 }
361 }
362 }
363 }
364
367 for (adjEntry adj : m_auxG.source()->adjEntries) {
368 node w = m_auxG.original(adj->twinNode());
369 T cost = oldCost;
370 if (!m_terminalSubset.hasMember(w)) {
371 computeSplit(split, w, subset);
372 cost = split[w].cost;
373 }
374 m_auxG.setWeight(adj->theEdge(), cost);
375 }
376 }
377
380 edge addNewPath(DWMData& result, node curr, const NodeArray<edge>& pred) const {
381 edge e = nullptr;
382 while (curr != m_auxG.source()) {
383 e = pred[curr];
384 OGDF_ASSERT(e != nullptr);
385 node prev = e->opposite(curr);
386 OGDF_ASSERT(prev != curr);
387 if (prev != m_auxG.source()) {
388 edge eO = m_auxG.original(e);
389 OGDF_ASSERT(eO != nullptr);
390 result.add(eO, m_auxG.weight(e));
391 }
392 curr = prev;
393 }
394 OGDF_ASSERT(e != nullptr);
395 OGDF_ASSERT(e->source() == curr);
396 OGDF_ASSERT(curr == m_auxG.source());
397
398 return e;
399 }
400
402 void insertInvalidBestSubtree(node v, const NodeArray<T>& distance, const List<node>& newSubset) {
403 OGDF_ASSERT(v->graphOf() == &m_auxG.graph());
404 OGDF_ASSERT(v != m_auxG.source());
405 DWMData best(distance[v]);
406 best.invalidate();
407 m_map.fastInsert(newSubset, best);
408 }
409
412 const NodeArray<edge>& pred, const List<node>& newSubset, const List<node>& terminals) {
413 OGDF_ASSERT(v->graphOf() == &m_auxG.graph());
414 OGDF_ASSERT(v != m_auxG.source());
415 DWMData best(0);
416
417 edge e = addNewPath(best, v, pred);
418 // add best solution so far
419 node tO = m_auxG.original(e->target());
420 if (m_terminalSubset.hasMember(tO)) {
421 best.add(dataOf(terminals));
422 } else {
423 best.add(split[tO].subgraph1);
424 best.add(split[tO].subgraph2);
425 }
426
427 m_map.fastInsert(newSubset, best);
428 }
429
431 template<typename CONTAINER>
432 void insertBestSubtrees(const CONTAINER& targets, const NodeArray<DWMSplit>& split,
433 const NodeArray<edge>& pred, const NodeArray<T>& distance, const List<node>& terminals) {
434 for (node v : targets) {
435 if (!m_terminalSubset.hasMember(v)) {
436 List<node> newSubset;
437 makeKey(newSubset, v);
438
439 if (!m_map.member(newSubset)) { // not already defined
440 node vCopy = m_auxG.copy(v);
441 if (pred[m_auxG.copy(v)] == nullptr) { // path over terminal
442 insertInvalidBestSubtree(vCopy, distance, newSubset);
443 } else {
444 insertValidBestSubtree(vCopy, split, pred, newSubset, terminals);
445 }
446 }
447 }
448 }
449 }
450
453 T cost(0);
454
455 if (data.valid()) {
456 // add edges
457 for (edge e : data.edges) {
458 node uO = e->source();
459 node vO = e->target();
460 OGDF_ASSERT(uO != nullptr);
461 OGDF_ASSERT(vO != nullptr);
462 node uC = tree.copy(uO);
463 node vC = tree.copy(vO);
464 if (uC == nullptr) {
465 uC = tree.newNode(uO);
466 }
467 if (vC == nullptr) {
468 vC = tree.newNode(vO);
469 }
470 T dist = m_G.weight(e);
471 tree.newEdge(e, dist);
472 cost += dist;
473 }
474
475 // recurse
476 for (const DWMData* subgraph : data.subgraphs) {
477 cost += getSteinerTreeFor(*subgraph, tree);
478 }
479
481 }
482
483 return cost;
484 }
485
486public:
492 const List<node>& terminals, const NodeArray<bool>& isTerminal)
493 : m_G(G)
494 , m_terminals(terminals)
495 , m_isTerminal(isTerminal)
498 , m_map(1 << 22) { // we initially allocate 4MB*sizeof(DWMData) for hashing
499 }
500
501 void call(int restricted) {
502 OGDF_ASSERT(restricted >= 2);
503 Math::updateMin(restricted, m_terminals.size());
505 for (m_terminalSubset.begin(2, restricted - 2); m_terminalSubset.valid();
506 m_terminalSubset.next()) {
508 }
509 // save time by only adding terminals instead of all nodes
510 for (m_terminalSubset.begin(restricted - 1); m_terminalSubset.valid();
511 m_terminalSubset.next()) {
513 }
514 }
515
519 tree.setOriginalGraph(m_G);
520 T cost(getSteinerTreeFor(*dataOf(terminals), tree));
522 return cost;
523 }
524
527 if (tree.empty()) {
528 return false;
529 }
530 for (node v : tree.nodes) {
531 OGDF_ASSERT(v->degree() > 1 || m_isTerminal[tree.original(v)]);
532 if (m_isTerminal[tree.original(v)] // is a terminal
533 && v->degree() > 1) { // but not a leaf
534 return false;
535 }
536 }
537 return true;
538 }
539};
540
541template<typename T>
543 static const unsigned int c_prime = 0x7fffffff; // mersenne prime 2**31 - 1
544 // would be nicer: 0x1fffffffffffffff; // mersenne prime 2**61 - 1
545 const int m_random;
546
547public:
549 SortedNodeListHashFunc() : m_random(randomNumber(2, c_prime - 1)) { }
550
552 unsigned int hash(const List<node>& key) const {
553 unsigned int hash = 0;
554 for (node v : key) {
555 hash = (hash * m_random + v->index()) % c_prime;
556 }
557 return hash;
558 }
559};
560
561}
562}
Declaration and implementation of ArrayBuffer class.
Declaration of class EdgeWeightedGraph.
Includes declaration of graph class.
Declaration of classes used for hashing.
Declaration of doubly linked lists and iterators.
Declaration of ogdf::MinSteinerTreeModule.
A class that allows to enumerate k-subsets.
Mathematical Helpers.
Basic declarations, included by all source files.
Class for adjacency list elements.
Definition Graph_d.h:143
An array that keeps track of the number of inserted elements; also usable as an efficient stack.
Definition ArrayBuffer.h:64
void clear()
Clears the buffer.
void push(E e)
Puts a new element in the buffer.
bool empty() const
Returns true if the buffer is empty, false otherwise.
Class for the representation of edges.
Definition Graph_d.h:364
node opposite(node v) const
Returns the adjacent node different from v.
Definition Graph_d.h:414
const Graph * graphOf() const
Returns the graph containing this node (debug only).
Definition Graph_d.h:441
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
Hashing with chaining and table doubling.
Definition Hashing.h:264
Doubly linked lists (maintaining the length of the list).
Definition List.h:1451
int size() const
Returns the number of elements in the list.
Definition List.h:1488
iterator pushBack(const E &x)
Adds element x at the end of the list.
Definition List.h:1547
iterator pushFront(const E &x)
Adds element x at the beginning of the list.
Definition List.h:1534
static void singleSourceShortestPaths(const EdgeWeightedGraph< T > &G, node source, const NodeArray< bool > &isTerminal, NodeArray< T > &distance, NodeArray< edge > &pred)
The default single-source-shortest-paths algorithm.
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
int index() const
Returns the (unique) node index.
Definition Graph_d.h:275
const Graph * graphOf() const
Returns the graph containing this node (debug only).
Definition Graph_d.h:345
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.
void forEachMemberAndNonmember(std::function< void(const T &)> funcIn, std::function< void(const T &)> funcNotIn) const
Calls funcIn for each subset member and funcNotIn for each other element of the set.
bool valid() const
Checks if the current subset is valid. If not, the subset is either not initialized or all subsets ha...
int numberOfMembersAndNonmembers() const
Returns the cardinality of the (super-)set. This is the maximum size that can be used for a subset.
void next()
Obtains the next subset if possible. The result should be checked using the valid() method.
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
AuxiliaryGraph(const EdgeWeightedGraph< T > &orig, const List< node > &terminals)
Constructs a copy of the original graph with an added source node having edges to all other nodes.
A generator for restricted full components (for Steiner tree approximations) based on the Dreyfus-Wag...
static void sortedInserter(node w, List< node > &list, bool &inserted, node newNode)
Is being used as a callback to ogdf::SubsetEnumerator's forEach* methods to get the subset plus a cor...
T getSteinerTreeFor(const List< node > &terminals, EdgeWeightedGraphCopy< T > &tree) const
Constructs a Steiner tree for the given set of terminals if it is valid, otherwise an empty tree is r...
void insertValidBestSubtree(node v, const NodeArray< DWMSplit > &split, const NodeArray< edge > &pred, const List< node > &newSubset, const List< node > &terminals)
Inserts the valid best subtree (based on the SSSP computation) into the hash map.
const NodeArray< bool > & m_isTerminal
A reference to the terminal incidence vector.
bool isValidComponent(const EdgeWeightedGraphCopy< T > &tree) const
Checks if a given tree is a valid full component.
const DWMData * dataOf(const List< node > &key) const
Returns a pointer to the relevant data of the partial solution given by key.
void computePartialSolutions(const CONTAINER &targets)
Computes all partial solutions for given m_terminalSubset.
T costOf(const List< node > &key) const
Returns the cost of the partial solution given by key.
edge addNewPath(DWMData &result, node curr, const NodeArray< edge > &pred) const
Adds the shortest path from the source to curr into result.
T getSteinerTreeFor(const DWMData &data, EdgeWeightedGraphCopy< T > &tree) const
Adds edges to construct a Steiner tree from the given data (recursively) if the data is valid.
void makeKey(List< node > &newSubset, List< node > &newComplement, const SubsetEnumerator< node > &subset, node v) const
Makes a list from subset and its complement, each including an correctly inserted node v.
FullComponentGeneratorDreyfusWagnerWithoutMatrix(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal)
The constructor.
void insertBestSubtrees(const CONTAINER &targets, const NodeArray< DWMSplit > &split, const NodeArray< edge > &pred, const NodeArray< T > &distance, const List< node > &terminals)
Inserts the best subtrees into the hash map.
bool safeIfSumSmaller(const T summand1, const T summand2, const T compareValue) const
Checks overflow-safe if summand1 plus summand2 is less than compareValue.
void insertInvalidBestSubtree(node v, const NodeArray< T > &distance, const List< node > &newSubset)
Inserts the invalid best subtree (based on the SSSP computation) into the hash map.
void makeKey(List< node > &newSubset, node v) const
Makes a list from the current terminal subset including an correctly inserted node v.
void updateAuxGraph(NodeArray< DWMSplit > &split, SubsetEnumerator< node > &subset, T oldCost)
Updates the auxiliary graph.
void computeSplit(NodeArray< DWMSplit > &split, node v, SubsetEnumerator< node > &subset) const
Populates split that contains a partial solution for an included nonterminal v in m_G.
Hashing< List< node >, DWMData, SortedNodeListHashFunc > m_map
A hash array for keys of size > 2.
const List< node > & m_terminals
A reference to the index-sorted list of terminals.
bool isAcyclicUndirected(const Graph &G, List< edge > &backedges)
Returns true iff the undirected graph G is acyclic.
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
int randomNumber(int low, int high)
Returns random integer between low and high (including).
void updateMin(T &min, const T &newValue)
Stores the minimum of min and newValue in min.
Definition Math.h:102
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.
Subgraphs (given by other subgraphs and additional edges) and their cost for a partial solution.
void set(const DWMData *s1, const DWMData *s2)
Sets subgraph1 and subgraph2 and computes cost.