Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
FullComponentGeneratorDreyfusWagner.h
Go to the documentation of this file.
1
32#pragma once
33
34#include <ogdf/basic/Hashing.h>
35#include <ogdf/basic/Math.h>
38
39namespace ogdf {
40namespace steiner_tree {
41
50template<typename T>
58
60
62 struct DWMData {
66
68
69 explicit DWMData(T _cost = std::numeric_limits<T>::max()) : cost(_cost) { }
70
72 void invalidate() {
74 subgraphs.clear();
75 }
76
78 bool valid() const { return cost == 0 || !(nodepairs.empty() && subgraphs.empty()); }
79
81 void add(const DWMData* other) {
82 if (valid()) {
83 if (other->valid()) {
84 subgraphs.push(other);
85 } else {
86 invalidate();
87 }
88 }
89 cost += other->cost;
90 }
91
93 void clear() {
94 invalidate();
95 cost = 0; // make it valid again
96 }
97
99 void add(NodePair nodepair, T c) {
100 if (valid()) {
102 }
103 cost += c;
104 }
105 };
106
108 struct DWMSplit {
109 T cost = std::numeric_limits<T>::max();
110 const DWMData* subgraph1 = nullptr;
111 const DWMData* subgraph2 = nullptr;
112
114 void set(const DWMData* s1, const DWMData* s2) {
115 subgraph1 = s1;
116 subgraph2 = s2;
117 cost = s1->cost + s2->cost;
118 }
119 };
120
122 class SortedNodeListHashFunc;
123
126
128 const DWMData* dataOf(const List<node>& key) const {
129 OGDF_ASSERT(key.size() > 1);
130 OGDF_ASSERT(m_map.member(key));
131 return &m_map.lookup(key)->info();
132 }
133
135 T costOf(const List<node>& key) const {
136 OGDF_ASSERT(key.size() > 1);
137 if (key.size() == 2) { // a shortcut to avoid using the hash table
138#ifdef OGDF_FULL_COMPONENT_GENERATION_TERMINAL_SSSP_AWARE
139 if (m_isTerminal[key.front()]) {
140 return m_distance[key.front()][key.back()];
141 } else {
143 return m_distance[key.back()][key.front()];
144 }
145#else
146 return m_distance[key.front()][key.back()];
147#endif
148 }
149 return dataOf(key)->cost;
150 }
151
153 bool safeIfSumSmaller(const T summand1, const T summand2, const T compareValue) const {
154#ifdef OGDF_FULL_COMPONENT_GENERATION_ALWAYS_SAFE
155 return summand1 + summand2 < compareValue;
156#else
157 return summand1 < std::numeric_limits<T>::max() && summand2 < std::numeric_limits<T>::max()
159#endif
160 }
161
173 static void sortedInserter(node w, List<node>& list, bool& inserted, node newNode) {
174 if (!inserted && w->index() > newNode->index()) {
175 list.pushBack(newNode);
176 inserted = true;
177 }
178 list.pushBack(w);
179 }
180
183 bool inserted = false;
184 m_terminalSubset.forEachMember([&](node w) { sortedInserter(w, newSubset, inserted, v); });
185 if (!inserted) {
186 newSubset.pushBack(v);
187 }
188 }
189
192 const SubsetEnumerator<node>& subset, node v) const {
193 bool insertedIntoSubset = false;
194 bool insertedIntoComplement = false;
195 // Interestingly std::bind is much slower than using lambdas (at least on g++ 6.3)
196 subset.forEachMemberAndNonmember(
199 if (!insertedIntoSubset) {
200 newSubset.pushBack(v);
201 }
203 newComplement.pushBack(v);
204 }
205 }
206
213 if (split[v].subgraph1 != nullptr) { // already computed
214 return;
215 }
216
217 DWMSplit& best = split[v];
218 for (subset.begin(1, subset.numberOfMembersAndNonmembers() / 2); subset.valid();
219 subset.next()) {
222
225 }
226 }
227 }
228
231 const List<node>& terminals) {
233 makeKey(newSubset, v);
234
235 if (!m_map.member(newSubset)) { // not already defined
236 T oldCost = costOf(terminals);
238 auto addPair = [&](node v1, node v2, T dist) {
240 if (m_pred[v1][v2] == nullptr) {
241 best.invalidate();
242 }
243 };
244 for (node w : m_G.nodes) {
245 T dist = m_distance[v][w];
246 if (m_terminalSubset.hasMember(w)) {
247 // we attach edge vw to tree containing terminal w
248 if (safeIfSumSmaller(oldCost, dist, best.cost)) {
249 best.clear();
250 best.add(dataOf(terminals));
251 addPair(v, w, dist);
252 }
253 } else {
254 // we attach edge vw to tree split[w]
255 OGDF_ASSERT(!m_terminalSubset.hasMember(v));
256 computeSplit(split, w, subset);
257 if (safeIfSumSmaller(split[w].cost, dist, best.cost)) {
258 best.clear();
259 best.add(split[w].subgraph1);
260 best.add(split[w].subgraph2);
261 if (v != w) {
262 addPair(v, w, dist);
263 }
264 }
265 }
266 }
267 m_map.fastInsert(newSubset, best);
268 }
269 }
270
272 template<typename CONTAINER>
274 List<node> terminals;
275 m_terminalSubset.list(terminals);
276 SubsetEnumerator<node> subset(terminals); // done here because of linear running time
278 for (node v : nodeContainer) {
279 if (!m_terminalSubset.hasMember(v)) {
280 computePartialSolution(split, v, subset, terminals);
281 }
282 }
283 }
284
287 for (node v : m_G.nodes) {
288 for (node t : m_terminals) {
289 if (t != v) {
290 List<node> key;
291 key.pushBack(t);
292 if (v->index() < t->index()) {
293 key.pushFront(v);
294 } else {
295 key.pushBack(v);
296 }
297
298 if (!m_map.member(key)) { // not already defined
299 if (m_pred[t][v] == nullptr) {
300 m_map.fastInsert(key, DWMData(m_distance[t][v]));
301 OGDF_ASSERT(!dataOf(key)->valid() || m_distance[t][v] == 0);
302 } else {
303 NodePairs nodepairs;
304 nodepairs.push(NodePair(key.front(), key.back()));
305 m_map.fastInsert(key, DWMData(m_distance[t][v], nodepairs));
306 OGDF_ASSERT(dataOf(key)->valid());
307 }
308 }
309 }
310 }
311 }
312 }
313
316 T cost(0);
317 if (data.valid()) {
318 // add edges
319 for (auto nodepair : data.nodepairs) {
320 node uO = nodepair.source;
321 node vO = nodepair.target;
322 node uC = tree.copy(uO);
323 node vC = tree.copy(vO);
324 if (uC == nullptr) {
325 uC = tree.newNode(uO);
326 }
327 if (vC == nullptr) {
328 vC = tree.newNode(vO);
329 }
330#ifdef OGDF_FULL_COMPONENT_GENERATION_TERMINAL_SSSP_AWARE
331 const T dist = m_isTerminal[uO] ? m_distance[uO][vO] : m_distance[vO][uO];
332#else
333 const T dist = m_distance[uO][vO];
334#endif
335 tree.newEdge(uC, vC, dist);
336 cost += dist;
337 }
338
339 // recurse
340 for (const DWMData* subgraph : data.subgraphs) {
341 cost += getSteinerTreeFor(*subgraph, tree);
342 }
343 }
344
346 return cost;
347 }
348
349public:
354 const NodeArray<bool>& isTerminal, const NodeArray<NodeArray<T>>& distance,
355 const NodeArray<NodeArray<edge>>& pred)
356 : m_G(G)
357 , m_terminals(terminals)
358 , m_isTerminal(isTerminal)
359 , m_distance(distance)
360 , m_pred(pred)
362 , m_map(1 << 22) // we initially allocate 4MB*sizeof(DWMData) for hashing
363 {
365 }
366
367 void call(int restricted) {
370 for (m_terminalSubset.begin(2, restricted - 1); m_terminalSubset.valid();
371 m_terminalSubset.next()) {
372 if (m_terminalSubset.size() != restricted - 1) {
374 } else { // maximal terminal subset
375 // save time by only adding terminals instead of all nodes
377 }
378 }
379 }
380
384 tree.createEmpty(m_G);
385 T cost(getSteinerTreeFor(*dataOf(terminals), tree));
387 return cost;
388 }
389
392 if (graph.empty()) {
393 return false;
394 }
395 for (node v : graph.nodes) {
396 if (m_isTerminal[graph.original(v)] // is a terminal
397 && v->degree() > 1) { // but not a leaf
398 return false;
399 }
400 }
401 return true;
402 }
403};
404
405template<typename T>
407 static const unsigned int c_prime = 0x7fffffff; // mersenne prime 2**31 - 1
408 // would be nicer: 0x1fffffffffffffff; // mersenne prime 2**61 - 1
409 const int m_random;
410
411public:
413 SortedNodeListHashFunc() : m_random(randomNumber(2, c_prime - 1)) { }
414
416 unsigned int hash(const List<node>& key) const {
417 unsigned int hash = 0;
418 for (node v : key) {
419 hash = (hash * m_random + v->index()) % c_prime;
420 }
421 return hash;
422 }
423};
424
425}
426}
Extends the GraphCopy concept to weighted graphs.
Declaration of classes used for hashing.
A class that allows to enumerate k-subsets.
Mathematical Helpers.
void clear()
Clears the buffer.
Definition ArrayBuffer.h:95
void push(E e)
Puts a new element in the buffer.
bool empty() const
Returns true if the buffer is empty, false otherwise.
const Graph & original() const
Returns a reference to the original graph.
Definition GraphCopy.h:289
internal::GraphObjectContainer< NodeElement > nodes
The container containing all node objects.
Definition Graph_d.h:589
bool empty() const
Returns true iff the graph is empty, i.e., contains no nodes.
Definition Graph_d.h:619
Hashing with chaining and table doubling.
Definition Hashing.h:261
Doubly linked lists (maintaining the length of the list).
Definition List.h:1435
int size() const
Returns the number of elements in the list.
Definition List.h:1472
iterator pushBack(const E &x)
Adds element x at the end of the list.
Definition List.h:1531
iterator pushFront(const E &x)
Adds element x at the beginning of the list.
Definition List.h:1518
const_reference front() const
Returns a const reference to the first element.
Definition List.h:289
const_reference back() const
Returns a const reference to the last element.
Definition List.h:307
Dynamic arrays indexed with nodes.
Definition NodeArray.h:125
Class for the representation of nodes.
Definition Graph_d.h:177
int index() const
Returns the (unique) node index.
Definition Graph_d.h:211
Enumerator for k-subsets of a given type.
unsigned int hash(const List< node > &key) const
The actual hash function.
A generator for restricted full components (for Steiner tree approximations) based on the Dreyfus-Wag...
const EdgeWeightedGraph< T > & m_G
A reference to the graph instance.
void computePartialSolution(NodeArray< DWMSplit > &split, node v, SubsetEnumerator< node > &subset, const List< node > &terminals)
Computes a partial solution for given terminals and node v.
void makeKey(List< node > &newSubset, node v) const
Makes a list from the current terminal subset including an correctly inserted node v.
void computePartialSolutions(const CONTAINER &nodeContainer)
Computes all partial solutions for given m_terminalSubset.
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.
bool safeIfSumSmaller(const T summand1, const T summand2, const T compareValue) const
Checks overflow-safe if summand1 plus summand2 is less than compareValue.
FullComponentGeneratorDreyfusWagner(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, const NodeArray< NodeArray< T > > &distance, const NodeArray< NodeArray< edge > > &pred)
The constructor.
void initializeMap()
Initializes the hash array with all node-terminal-pairs.
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.
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...
const List< node > & m_terminals
A reference to the index-sorted list of terminals.
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.
Hashing< List< node >, DWMData, SortedNodeListHashFunc > m_map
A hash array for keys of size > 2.
const DWMData * dataOf(const List< node > &key) const
Returns a pointer to the relevant data of the partial solution given by key.
const NodeArray< NodeArray< T > > & m_distance
A reference to the full distance matrix.
const NodeArray< bool > & m_isTerminal
A reference to the terminal incidence vector.
bool isValidComponent(const EdgeWeightedGraphCopy< T > &graph) const
Checks if a given graph is a valid full component.
const NodeArray< NodeArray< edge > > & m_pred
A reference to the full predecessor matrix.
SubsetEnumerator< node > m_terminalSubset
Handling subsets of terminals.
T costOf(const List< node > &key) const
Returns the cost of the partial solution given by key.
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...
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:41
int randomNumber(int low, int high)
Returns random integer between low and high (including).
static MultilevelBuilder * getDoubleFactoredZeroAdjustedMerger()
void updateMin(T &min, const T &newValue)
Stores the minimum of min and newValue in min.
Definition Math.h:98
The namespace for all OGDF objects.
@ tree
for every hyperedge e a minimal subcubic tree connecting all hypernodes incident with e together is a...
Subgraphs (given by other subgraphs and additional node pairs) and their cost for a partial solution.
void clear()
Remove all subgraphs and edges and set cost to zero.
void set(const DWMData *s1, const DWMData *s2)
Sets subgraph1 and subgraph2 and computes cost.