Loading [MathJax]/extensions/tex2jax.js

Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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.