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
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>
41
42#include <limits>
43
44namespace ogdf {
45template<typename T>
46class EdgeWeightedGraph;
47template<typename T>
48class EdgeWeightedGraphCopy;
49
50namespace steiner_tree {
51
60template<typename T>
68
70
72 struct DWMData {
76
77 DWMData(T _cost, NodePairs _nodepairs) : cost(_cost), nodepairs(_nodepairs) { }
78
79 explicit DWMData(T _cost = std::numeric_limits<T>::max()) : cost(_cost) { }
80
82 void invalidate() {
84 subgraphs.clear();
85 }
86
88 bool valid() const { return cost == 0 || !(nodepairs.empty() && subgraphs.empty()); }
89
91 void add(const DWMData* other) {
92 if (valid()) {
93 if (other->valid()) {
94 subgraphs.push(other);
95 } else {
96 invalidate();
97 }
98 }
99 cost += other->cost;
100 }
101
103 void clear() {
104 invalidate();
105 cost = 0; // make it valid again
106 }
107
109 void add(NodePair nodepair, T c) {
110 if (valid()) {
111 nodepairs.push(nodepair);
112 }
113 cost += c;
114 }
115 };
116
118 struct DWMSplit {
119 T cost = std::numeric_limits<T>::max();
120 const DWMData* subgraph1 = nullptr;
121 const DWMData* subgraph2 = nullptr;
122
124 void set(const DWMData* s1, const DWMData* s2) {
125 subgraph1 = s1;
126 subgraph2 = s2;
127 cost = s1->cost + s2->cost;
128 }
129 };
130
132 class SortedNodeListHashFunc;
133
136
138 const DWMData* dataOf(const List<node>& key) const {
139 OGDF_ASSERT(key.size() > 1);
140 OGDF_ASSERT(m_map.member(key));
141 return &m_map.lookup(key)->info();
142 }
143
145 T costOf(const List<node>& key) const {
146 OGDF_ASSERT(key.size() > 1);
147 if (key.size() == 2) { // a shortcut to avoid using the hash table
148#ifdef OGDF_FULL_COMPONENT_GENERATION_TERMINAL_SSSP_AWARE
149 if (m_isTerminal[key.front()]) {
150 return m_distance[key.front()][key.back()];
151 } else {
153 return m_distance[key.back()][key.front()];
154 }
155#else
156 return m_distance[key.front()][key.back()];
157#endif
158 }
159 return dataOf(key)->cost;
160 }
161
163 bool safeIfSumSmaller(const T summand1, const T summand2, const T compareValue) const {
164#ifdef OGDF_FULL_COMPONENT_GENERATION_ALWAYS_SAFE
165 return summand1 + summand2 < compareValue;
166#else
167 return summand1 < std::numeric_limits<T>::max() && summand2 < std::numeric_limits<T>::max()
168 && summand1 + summand2 < compareValue;
169#endif
170 }
171
183 static void sortedInserter(node w, List<node>& list, bool& inserted, node newNode) {
184 if (!inserted && w->index() > newNode->index()) {
185 list.pushBack(newNode);
186 inserted = true;
187 }
188 list.pushBack(w);
189 }
190
192 void makeKey(List<node>& newSubset, node v) const {
193 bool inserted = false;
194 m_terminalSubset.forEachMember([&](node w) { sortedInserter(w, newSubset, inserted, v); });
195 if (!inserted) {
196 newSubset.pushBack(v);
197 }
198 }
199
201 void makeKey(List<node>& newSubset, List<node>& newComplement,
202 const SubsetEnumerator<node>& subset, node v) const {
203 bool insertedIntoSubset = false;
204 bool insertedIntoComplement = false;
205 // Interestingly std::bind is much slower than using lambdas (at least on g++ 6.3)
207 [&](node w) { sortedInserter(w, newSubset, insertedIntoSubset, v); },
208 [&](node w) { sortedInserter(w, newComplement, insertedIntoComplement, v); });
209 if (!insertedIntoSubset) {
210 newSubset.pushBack(v);
211 }
212 if (!insertedIntoComplement) {
213 newComplement.pushBack(v);
214 }
215 }
216
223 if (split[v].subgraph1 != nullptr) { // already computed
224 return;
225 }
226
227 DWMSplit& best = split[v];
228 for (subset.begin(1, subset.numberOfMembersAndNonmembers() / 2); subset.valid();
229 subset.next()) {
230 List<node> newSubset, newComplement;
231 makeKey(newSubset, newComplement, subset, v);
232
233 if (safeIfSumSmaller(costOf(newSubset), costOf(newComplement), best.cost)) {
234 best.set(dataOf(newSubset), dataOf(newComplement));
235 }
236 }
237 }
238
241 const List<node>& terminals) {
242 List<node> newSubset;
243 makeKey(newSubset, v);
244
245 if (!m_map.member(newSubset)) { // not already defined
246 T oldCost = costOf(terminals);
247 DWMData best;
248 auto addPair = [&](node v1, node v2, T dist) {
249 best.add(NodePair(v1, v2), dist);
250 if (m_pred[v1][v2] == nullptr) {
251 best.invalidate();
252 }
253 };
254 for (node w : m_G.nodes) {
255 T dist = m_distance[v][w];
256 if (m_terminalSubset.hasMember(w)) {
257 // we attach edge vw to tree containing terminal w
258 if (safeIfSumSmaller(oldCost, dist, best.cost)) {
259 best.clear();
260 best.add(dataOf(terminals));
261 addPair(v, w, dist);
262 }
263 } else {
264 // we attach edge vw to tree split[w]
265 OGDF_ASSERT(!m_terminalSubset.hasMember(v));
266 computeSplit(split, w, subset);
267 if (safeIfSumSmaller(split[w].cost, dist, best.cost)) {
268 best.clear();
269 best.add(split[w].subgraph1);
270 best.add(split[w].subgraph2);
271 if (v != w) {
272 addPair(v, w, dist);
273 }
274 }
275 }
276 }
277 m_map.fastInsert(newSubset, best);
278 }
279 }
280
282 template<typename CONTAINER>
283 void computePartialSolutions(const CONTAINER& nodeContainer) {
284 List<node> terminals;
285 m_terminalSubset.list(terminals);
286 SubsetEnumerator<node> subset(terminals); // done here because of linear running time
288 for (node v : nodeContainer) {
289 if (!m_terminalSubset.hasMember(v)) {
290 computePartialSolution(split, v, subset, terminals);
291 }
292 }
293 }
294
297 for (node v : m_G.nodes) {
298 for (node t : m_terminals) {
299 if (t != v) {
300 List<node> key;
301 key.pushBack(t);
302 if (v->index() < t->index()) {
303 key.pushFront(v);
304 } else {
305 key.pushBack(v);
306 }
307
308 if (!m_map.member(key)) { // not already defined
309 if (m_pred[t][v] == nullptr) {
310 m_map.fastInsert(key, DWMData(m_distance[t][v]));
311 OGDF_ASSERT(!dataOf(key)->valid() || m_distance[t][v] == 0);
312 } else {
313 NodePairs nodepairs;
314 nodepairs.push(NodePair(key.front(), key.back()));
315 m_map.fastInsert(key, DWMData(m_distance[t][v], nodepairs));
316 OGDF_ASSERT(dataOf(key)->valid());
317 }
318 }
319 }
320 }
321 }
322 }
323
326 T cost(0);
327 if (data.valid()) {
328 // add edges
329 for (auto nodepair : data.nodepairs) {
330 node uO = nodepair.source;
331 node vO = nodepair.target;
332 node uC = tree.copy(uO);
333 node vC = tree.copy(vO);
334 if (uC == nullptr) {
335 uC = tree.newNode(uO);
336 }
337 if (vC == nullptr) {
338 vC = tree.newNode(vO);
339 }
340#ifdef OGDF_FULL_COMPONENT_GENERATION_TERMINAL_SSSP_AWARE
341 const T dist = m_isTerminal[uO] ? m_distance[uO][vO] : m_distance[vO][uO];
342#else
343 const T dist = m_distance[uO][vO];
344#endif
345 tree.newEdge(uC, vC, dist);
346 cost += dist;
347 }
348
349 // recurse
350 for (const DWMData* subgraph : data.subgraphs) {
351 cost += getSteinerTreeFor(*subgraph, tree);
352 }
353 }
354
356 return cost;
357 }
358
359public:
364 const NodeArray<bool>& isTerminal, const NodeArray<NodeArray<T>>& distance,
365 const NodeArray<NodeArray<edge>>& pred)
366 : m_G(G)
367 , m_terminals(terminals)
368 , m_isTerminal(isTerminal)
369 , m_distance(distance)
370 , m_pred(pred)
372 , m_map(1 << 22) // we initially allocate 4MB*sizeof(DWMData) for hashing
373 {
375 }
376
377 void call(int restricted) {
378 OGDF_ASSERT(restricted >= 2);
379 Math::updateMin(restricted, m_terminals.size());
380 for (m_terminalSubset.begin(2, restricted - 1); m_terminalSubset.valid();
381 m_terminalSubset.next()) {
382 if (m_terminalSubset.size() != restricted - 1) {
384 } else { // maximal terminal subset
385 // save time by only adding terminals instead of all nodes
387 }
388 }
389 }
390
394 tree.setOriginalGraph(m_G);
395 T cost(getSteinerTreeFor(*dataOf(terminals), tree));
397 return cost;
398 }
399
402 if (graph.empty()) {
403 return false;
404 }
405 for (node v : graph.nodes) {
406 if (m_isTerminal[graph.original(v)] // is a terminal
407 && v->degree() > 1) { // but not a leaf
408 return false;
409 }
410 }
411 return true;
412 }
413};
414
415template<typename T>
417 static const unsigned int c_prime = 0x7fffffff; // mersenne prime 2**31 - 1
418 // would be nicer: 0x1fffffffffffffff; // mersenne prime 2**61 - 1
419 const int m_random;
420
421public:
423 SortedNodeListHashFunc() : m_random(randomNumber(2, c_prime - 1)) { }
424
426 unsigned int hash(const List<node>& key) const {
427 unsigned int hash = 0;
428 for (node v : key) {
429 hash = (hash * m_random + v->index()) % c_prime;
430 }
431 return hash;
432 }
433};
434
435}
436}
Declaration and implementation of ArrayBuffer class.
Includes declaration of graph class.
Declaration of classes used for hashing.
Declaration of doubly linked lists and iterators.
A class that allows to enumerate k-subsets.
Mathematical Helpers.
Basic declarations, included by all source files.
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.
const Graph & original() const
Returns a reference to the original graph.
Definition GraphCopy.h:104
internal::GraphObjectContainer< NodeElement > nodes
The container containing all node objects.
Definition Graph_d.h:929
bool empty() const
Returns true iff the graph is empty, i.e., contains no nodes.
Definition Graph_d.h:976
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
const_reference front() const
Returns a const reference to the first element.
Definition List.h:305
const_reference back() const
Returns a const reference to the last element.
Definition List.h:323
Class for the representation of nodes.
Definition Graph_d.h:241
int index() const
Returns the (unique) node index.
Definition Graph_d.h:275
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 nodes, edges and adjEntries of a graph.
Definition Graph_d.h:659
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: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...
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.