Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
FullComponentStore.h
Go to the documentation of this file.
1
32#pragma once
33
34#include <ogdf/basic/Array.h>
36#include <ogdf/basic/Graph.h>
38#include <ogdf/basic/List.h>
39#include <ogdf/basic/basic.h>
40#include <ogdf/basic/comparer.h>
42
43#include <type_traits>
44
45namespace ogdf {
46template<typename T>
47class EdgeWeightedGraphCopy;
48
49namespace steiner_tree {
50
51#define OGDF_FULLCOMPONENTSTORE_REMOVE_IN_GRAPH_REPRESENTATION_ALSO // unnecessary but may save memory
52
54template<typename T, typename ExtraDataType = void>
56protected:
63
64 template<class Y, class Enable = void> // Metadata without extra data
65 struct Metadata {
68 T cost;
69
70 Metadata() : start(nullptr), terminals(0), cost(0) { }
71 };
72
73 template<class Y> // Metadata with extra data
74 struct Metadata<Y, typename std::enable_if<!std::is_void<Y>::value>::type> {
77 T cost;
79
80 Metadata() : start(nullptr), terminals(0), cost(0) { }
81 };
82
84
87 const EdgeWeightedGraphCopy<T>& comp) const {
88 while (m_nodeCopy[uO] == nullptr && !m_isTerminal[uO]) {
89 OGDF_ASSERT(comp.copy(uO)->degree() == 2);
90 adj = adj->twin()->cyclicSucc();
91 OGDF_ASSERT(comp.original(adj->theNode()) == uO);
92 edge e2 = adj->theEdge();
93 marked[e2] = true;
94 weight += comp.weight(e2);
95 uO = comp.original(adj->twinNode());
96 }
97 }
98
101 const EdgeWeightedGraphCopy<T>& comp, const ArrayBuffer<node>& nonterminals) {
102 EdgeArray<bool> marked(comp, false);
103 for (node v : nonterminals) {
104 for (adjEntry adj : comp.copy(m_nodeOrig[v])->adjEntries) {
105 edge e = adj->theEdge();
106 if (!marked[e]) {
107 marked[e] = true;
108 node vO = comp.original(adj->theNode());
109 OGDF_ASSERT(m_nodeCopy[vO] != nullptr);
110 node uO = comp.original(adj->twinNode());
111 T weight(comp.weight(e));
112
113 traverseOverDegree2Nonterminals(uO, weight, marked, adj, comp);
114
115 edge eC = m_graph.newEdge(m_nodeCopy[uO], m_nodeCopy[vO], weight);
116 data.cost += weight;
117 if (m_isTerminal[uO]) {
118 data.start = eC->adjSource();
119 }
120 }
121 }
122 }
123#ifdef OGDF_DEBUG
124 for (edge e : comp.edges) {
125 OGDF_ASSERT(marked[e] == true);
126 }
127#endif
128 }
129
132 for (edge e : comp.edges) {
133 node uO = comp.original(e->source());
134 node vO = comp.original(e->target());
135 T weight = comp.weight(e);
136 edge eC = m_graph.newEdge(m_nodeCopy[uO], m_nodeCopy[vO], weight);
137 data.cost += weight;
138 if (m_isTerminal[uO]) {
139 data.start = eC->adjSource();
140 } else if (m_isTerminal[vO]) {
141 data.start = eC->adjTarget();
142 }
143 }
144 }
145
146public:
149 : m_originalGraph(G)
152 , m_nodeCopy(G, nullptr)
154 for (node v : m_terminals) {
155 node u = m_graph.newNode();
156 m_nodeCopy[v] = u;
157 m_nodeOrig[u] = v;
158 }
159 }
160
163 OGDF_ASSERT(!comp.empty());
164 OGDF_ASSERT(isTree(comp));
165
166 // we temporarily use m_nodeCopy for nonterminals (with degree > 2) also
167 ArrayBuffer<node> nonterminals(comp.numberOfNodes() / 2);
168
169 // add all nonterminals with degree > 2 of comp to m_graph
170 // and find terminals
172 bool existNoncritical = false;
173 for (node v : comp.nodes) {
174 node vO = comp.original(v);
175 if (m_nodeCopy[vO] == nullptr) {
176 OGDF_ASSERT(v->degree() >= 2);
177 if (v->degree() > 2) {
178 node vC = m_graph.newNode();
179 m_nodeCopy[vO] = vC;
180 m_nodeOrig[vC] = vO;
181 nonterminals.push(vC);
182 } else {
183 existNoncritical = true;
184 }
185 } else {
186 data.terminals.grow(1, vO);
187 }
188 }
189 data.terminals.quicksort(GenericComparer<node, int>([](node v) { return v->index(); }));
190
191 // add all edges of comp to m_graph
192 // and find start adjEntry
193 if (existNoncritical) {
194 if (nonterminals.empty()) {
195 OGDF_ASSERT(data.terminals.size() == 2);
196 OGDF_ASSERT(data.cost == 0);
197 for (edge e : comp.edges) {
198 data.cost += comp.weight(e);
199 }
200 edge eC = m_graph.newEdge(m_nodeCopy[data.terminals[0]],
201 m_nodeCopy[data.terminals[1]], data.cost);
202 data.start = eC->adjSource();
203 } else {
204 copyEdgesWithSimplifiedPaths(data, comp, nonterminals);
205 }
206 } else {
207 copyEdges(data, comp);
208 }
209 OGDF_ASSERT(data.start != nullptr);
210
211 // cleanup m_nodeCopy (only terminals should be set)
212 for (node vC : nonterminals) {
213 m_nodeCopy[m_nodeOrig[vC]] = nullptr;
214 }
215
216 m_components.push(data);
217 }
218
220 void remove(int id) {
221#ifdef OGDF_FULLCOMPONENTSTORE_REMOVE_IN_GRAPH_REPRESENTATION_ALSO
222 auto& comp = m_components[id];
223 if (comp.terminals.size() == 2) {
224 m_graph.delEdge(comp.start->theEdge());
225 } else {
226 ArrayBuffer<node> stack(2 * comp.terminals.size() - 3);
227 stack.push(comp.start->twinNode());
228 m_graph.delEdge(comp.start->theEdge());
229 while (!stack.empty()) {
230 const node v = stack.popRet();
231 if (!isTerminal(v)) {
232 for (adjEntry adj : v->adjEntries) {
233 stack.push(adj->twinNode());
234 }
235 m_graph.delNode(v);
236 }
237 }
238 }
239#endif
240 if (m_components.size() == id + 1) {
241 m_components.pop();
242 } else {
243 m_components[id] = m_components.popRet();
244 }
245 }
246
248 int size() const { return m_components.size(); }
249
251 bool isEmpty() const { return m_components.empty(); }
252
254 const Array<node>& terminals(int id) const {
255 OGDF_ASSERT(id >= 0);
256 OGDF_ASSERT(id < m_components.size());
257 return m_components[id].terminals;
258 }
259
261 bool isTerminal(int id, node t) const {
262 OGDF_ASSERT(id >= 0);
263 OGDF_ASSERT(id < m_components.size());
264 return m_components[id].terminals.linearSearch(t) != -1;
265 }
266
267 bool isTerminal(node v) const { return m_isTerminal[m_nodeOrig[v]]; }
268
270 T cost(int i) const {
271 OGDF_ASSERT(i >= 0);
272 OGDF_ASSERT(i < m_components.size());
273 return m_components[i].cost;
274 }
275
276 adjEntry start(int i) const {
277 OGDF_ASSERT(i >= 0);
278 OGDF_ASSERT(i < m_components.size());
279 return m_components[i].start;
280 }
281
282 const EdgeWeightedGraph<T>& graph() const { return m_graph; }
283
284 node original(node v) const {
285 OGDF_ASSERT(m_nodeOrig[v] != nullptr);
286 return m_nodeOrig[v];
287 }
288
289 template<typename Fun>
290 void foreachAdjEntry(int i, Fun f) const {
291 adjEntry start = m_components[i].start;
292 int size = m_components[i].terminals.size();
293 if (size == 2) {
294 f(start->twin());
295 return;
296 }
297 // size >= 3: do DFS over nonterminals (terminals are only leaves)
298 ArrayBuffer<adjEntry> stack(2 * size - 2);
299 stack.push(start);
300 while (!stack.empty()) {
301 const adjEntry back = stack.popRet()->twin();
302 f(back);
303 if (!this->isTerminal(back->theNode())) {
304 for (adjEntry adj = back->cyclicSucc(); adj != back; adj = adj->cyclicSucc()) {
305 stack.push(adj);
306 }
307 }
308 }
309 }
310
311 // \brief Do f(v) for each (original) node v of degree at least 3 in component with given id
312 template<typename Fun>
313 void foreachNode(int id, Fun f) const {
314 f(original(start(id)->theNode()));
315 foreachAdjEntry(id, [&](adjEntry back) { f(original(back->theNode())); });
316 }
317
318 // \brief Do f(e) for each (original) edge e in component with given id
319 template<typename Fun>
320 void foreachEdge(int id, const NodeArray<NodeArray<edge>>& pred, Fun f) const {
321 foreachAdjEntry(id, [&](adjEntry back) {
322 const node u = original(back->twinNode());
323 for (node v = original(back->theNode()); pred[u][v]; v = pred[u][v]->opposite(v)) {
324 f(pred[u][v]);
325 }
326 });
327 }
328
329 // \brief Do f(v) for each node v (also of degree 2) in component with given id
330 template<typename Fun>
331 void foreachNode(int id, const NodeArray<NodeArray<edge>>& pred, Fun f) const {
332 if (m_components[id].terminals.size() == 3) {
333 // use a variant that works when only pred[t] has been filled for all terminals t
334 adjEntry start = m_components[id].start;
335 const node c = start->twinNode();
336 f(original(c));
337 for (adjEntry adj : c->adjEntries) {
338 const node u = original(adj->twinNode());
339 node v = original(c);
340 while (v != u) {
341 v = pred[u][v]->opposite(v);
342 f(v);
343 }
344 }
345 return;
346 }
347 f(original(start(id)->theNode()));
348 foreachAdjEntry(id, [&](adjEntry back) {
349 const node u = original(back->twinNode());
350 for (node v = original(back->theNode()); pred[u][v]; v = pred[u][v]->opposite(v)) {
351 f(v);
352 }
353 });
354 }
355};
356
360template<typename T, typename ExtraDataType>
361class FullComponentWithExtraStore : public FullComponentStore<T, ExtraDataType> {
362public:
363 using FullComponentStore<T, ExtraDataType>::FullComponentStore;
364
366 ExtraDataType& extra(int i) {
367 OGDF_ASSERT(i >= 0);
368 OGDF_ASSERT(i < this->m_components.size());
369 return this->m_components[i].extra;
370 }
371
373 const ExtraDataType& extra(int i) const {
374 OGDF_ASSERT(i >= 0);
375 OGDF_ASSERT(i < this->m_components.size());
376 return this->m_components[i].extra;
377 }
378};
379
380template<typename T>
387
391template<typename T>
392class FullComponentWithLossStore : public FullComponentWithExtraStore<T, LossMetadata<T>> {
393protected:
397
405 if (!m_lossTerminal[u] && pred[u]) {
406 m_lossTerminal[u] = findLossTerminal(pred[u]->opposite(u), pred);
407 }
408
409 return m_lossTerminal[u];
410 }
411
412public:
413 using FullComponentWithExtraStore<T, LossMetadata<T>>::FullComponentWithExtraStore;
414
417 m_lossTerminal.init(this->m_graph, nullptr);
418
419 // add zero-cost edges between all terminals (to be removed later),
420 // and set m_lossTerminal mapping for terminals
421 List<edge> zeroEdges;
422 const node s = this->m_terminals.front();
423 const node sC = this->m_nodeCopy[s];
424 m_lossTerminal[sC] = s;
425 for (ListConstIterator<node> it = this->m_terminals.begin().succ(); it.valid(); ++it) {
426 const node v = *it;
427 const node vC = this->m_nodeCopy[v];
428 m_lossTerminal[vC] = v;
429 zeroEdges.pushBack(this->m_graph.newEdge(sC, vC, 0));
430 }
431
432 // compute minimum spanning tree
433 NodeArray<edge> pred(this->m_graph);
434 EdgeArray<bool> isLossEdge(this->m_graph, false);
435 computeMinST(sC, this->m_graph, this->m_graph.edgeWeights(), pred, isLossEdge);
436
437 // remove zero-cost edges again
438 for (edge e : zeroEdges) {
439 this->m_graph.delEdge(e);
440 }
441
442 // find loss bridges and compute loss value
443 for (int id = 0; id < this->size(); ++id) {
444 this->foreachAdjEntry(id, [&](adjEntry adj) {
445 edge e = adj->theEdge();
446 if (!isLossEdge[e]) {
447 this->extra(id).bridges.pushBack(e);
448 findLossTerminal(e->source(), pred);
449 findLossTerminal(e->target(), pred);
450 } else {
451 this->extra(id).loss += this->m_graph.weight(e);
452 }
453 });
454 }
455 }
456
458 T loss(int id) const { return this->extra(id).loss; }
459
461 const List<edge>& lossBridges(int id) const { return this->extra(id).bridges; }
462
471 return m_lossTerminal[v];
472 }
473};
474
475}
476}
Declaration and implementation of Array class and Array algorithms.
Declaration and implementation of ArrayBuffer class.
Declaration of class EdgeWeightedGraph.
Includes declaration of graph class.
Decralation of GraphElement and GraphList classes.
Declaration of doubly linked lists and iterators.
Basic declarations, included by all source files.
Class for adjacency list elements.
Definition Graph_d.h:143
adjEntry twin() const
Returns the corresponding adjacency element associated with the same edge.
Definition Graph_d.h:173
adjEntry cyclicSucc() const
Returns the cyclic successor in the adjacency list.
Definition Graph_d.h:355
edge theEdge() const
Returns the edge associated with this adjacency entry.
Definition Graph_d.h:161
node twinNode() const
Returns the associated node of the corresponding adjacency entry (shorthand for twin()->theNode()).
Definition Graph_d.h:176
node theNode() const
Returns the node whose adjacency list contains this element.
Definition Graph_d.h:167
An array that keeps track of the number of inserted elements; also usable as an efficient stack.
Definition ArrayBuffer.h:64
E popRet()
Removes the newest element from the buffer and returns it.
void push(E e)
Puts a new element in the buffer.
bool empty() const
Returns true if the buffer is empty, false otherwise.
The parameterized class Array implements dynamic arrays of type E.
Definition Array.h:219
void grow(INDEX add, const E &x)
Enlarges the array by add elements and sets new elements to x.
Definition Array.h:830
void quicksort()
Sorts array using Quicksort.
Definition Array.h:639
INDEX size() const
Returns the size (number of elements) of the array.
Definition Array.h:302
Class for the representation of edges.
Definition Graph_d.h:364
adjEntry adjSource() const
Returns the corresponding adjacancy entry at source node.
Definition Graph_d.h:408
adjEntry adjTarget() const
Returns the corresponding adjacancy entry at target node.
Definition Graph_d.h:411
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
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
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
internal::GraphObjectContainer< EdgeElement > edges
The container containing all edge objects.
Definition Graph_d.h:932
Doubly linked lists (maintaining the length of the list).
Definition List.h:1451
iterator pushBack(const E &x)
Adds element x at the end of the list.
Definition List.h:1547
Encapsulates a pointer to a list element.
Definition List.h:113
ListIteratorBase< E, isConst, isReverse > succ() const
Returns successor iterator.
Definition List.h:174
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
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
A data structure to store full components.
void remove(int id)
Removes a component by its id.
void foreachNode(int id, const NodeArray< NodeArray< edge > > &pred, Fun f) const
const NodeArray< bool > & m_isTerminal
Incidence vector for terminal nodes.
EdgeWeightedGraph< T > m_graph
Our graph representation for the full component store.
NodeArray< node > m_nodeCopy
Mapping of original terminals to m_graph nodes.
void copyEdgesWithSimplifiedPaths(Metadata< ExtraDataType > &data, const EdgeWeightedGraphCopy< T > &comp, const ArrayBuffer< node > &nonterminals)
Copy edges from comp into our representation, traversing variant to ignore degree-2 nodes.
void traverseOverDegree2Nonterminals(node &uO, T &weight, EdgeArray< bool > &marked, adjEntry adj, const EdgeWeightedGraphCopy< T > &comp) const
Traverse over degree-2 nonterminals.
bool isEmpty() const
Checks if the store does not contain any full components.
const EdgeWeightedGraph< T > & m_originalGraph
The original Steiner instance.
const List< node > & m_terminals
The terminal list of the original Steiner instance.
ArrayBuffer< Metadata< ExtraDataType > > m_components
List of full components (based on metadata)
bool isTerminal(int id, node t) const
checks if a given node t is a terminal in the full component with given id
void copyEdges(Metadata< ExtraDataType > &data, const EdgeWeightedGraphCopy< T > &comp)
Copy edges from comp into our representation.
void foreachEdge(int id, const NodeArray< NodeArray< edge > > &pred, Fun f) const
NodeArray< node > m_nodeOrig
Mapping of m_graph nodes to original nodes.
FullComponentStore(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal)
T cost(int i) const
Returns the sum of edge costs of this full component.
const EdgeWeightedGraph< T > & graph() const
const Array< node > & terminals(int id) const
Returns the list of terminals in the full component with given id.
void insert(const EdgeWeightedGraphCopy< T > &comp)
Inserts a component. Note that comp is copied and degree-2 nodes are removed.
int size() const
Returns the number of full components in the store.
A data structure to store full components with extra data for each component.
ExtraDataType & extra(int i)
Returns a reference to the extra data of this full component.
const ExtraDataType & extra(int i) const
Returns a const reference to the extra data of this full component.
A data structure to store full components with additional "loss" functionality.
void computeAllLosses()
Compute the loss, both edge set and value, of all full components.
node lossTerminal(node v) const
Returns the terminal (in the original graph) that belongs to a given node v (in the store) according ...
const List< edge > & lossBridges(int id) const
Returns a list of non-loss edges (that are bridges between the Loss components) of full component wit...
NodeArray< node > m_lossTerminal
Indicates which Steiner node is connected to which terminal through the loss edges,...
node findLossTerminal(const node u, const NodeArray< edge > &pred)
Starting from a Steiner node find the nearest terminal along a shortest path.
T loss(int id) const
Returns the loss value of full component with given id.
Declarations for Comparer objects.
T computeMinST(const Graph &G, const EdgeArray< T > &weight, EdgeArray< bool > &isInTree)
Computes a minimum spanning tree using Prim'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
The namespace for all OGDF objects.
Definition GML.h:111
Compare elements based on a single comparable attribute.
Definition comparer.h:402
adjEntry start
Adjacency entry on a terminal where a non-terminal BFS yields the component.
adjEntry start
Adjacency entry on a terminal where a non-terminal BFS yields the component.
Array< node > terminals
Terminals, sorted by node index.
List< edge > bridges
List of non-loss edges.