Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
MinSteinerTreeGoemans139.h
Go to the documentation of this file.
1
33#pragma once
34
36#include <ogdf/basic/Graph.h>
38#include <ogdf/basic/List.h>
40#include <ogdf/basic/basic.h>
54
55#include <random>
56
57namespace ogdf {
58template<typename T>
59class EdgeWeightedGraph;
60
78template<typename T>
80private:
81 class Main;
82
83protected:
88 int m_seed;
89
90public:
92 : m_restricted(3)
93 , m_preprocess(true)
94 , m_use2approx(false)
95 , m_separateCycles(false)
96 , m_seed(1337) { }
97
99
105
110 void setSeed(int seed) { m_seed = seed; }
111
117 void use2Approximation(bool use2approx = true) { m_use2approx = use2approx; }
118
124 void disablePreprocessing(bool preprocess = true) { m_preprocess = !preprocess; }
125
131
132protected:
141 virtual T computeSteinerTree(const EdgeWeightedGraph<T>& G, const List<node>& terminals,
142 const NodeArray<bool>& isTerminal, EdgeWeightedGraphCopy<T>*& finalSteinerTree) override;
143};
144
145template<typename T>
147 const List<node>& terminals, const NodeArray<bool>& isTerminal,
148 EdgeWeightedGraphCopy<T>*& finalSteinerTree) {
149 std::minstd_rand rng(m_seed);
150 List<node> sortedTerminals(terminals);
152 Main main(G, sortedTerminals, isTerminal, m_restricted, m_use2approx, m_separateCycles);
153 return main.getApproximation(finalSteinerTree, rng, m_preprocess);
154}
155
158template<typename T>
165
167 enum class Approx2State {
168 Off,
169 On,
170 JustUseIt,
171 };
173
174 const double m_eps;
175
178
181
183 void findFull2Components(const NodeArray<NodeArray<T>>& distance,
184 const NodeArray<NodeArray<edge>>& pred);
186 void findFull3Components(const NodeArray<NodeArray<T>>& distance,
187 const NodeArray<NodeArray<edge>>& pred);
189 void find3RestrictedComponents();
191 void findFullComponentsDW();
193 void findFullComponentsEMV();
195 template<typename FCG>
196 void retrieveComponents(const FCG& fcg);
198 void findFullComponents();
199
203
206 for (int k = m_fullCompStore.size() - 1; k >= 0; --k) {
207 OGDF_ASSERT(k < m_fullCompStore.size());
208 if (m_fullCompStore.extra(k) <= m_eps) {
209 m_fullCompStore.remove(k);
210 }
211 }
212 }
213
216 ids.quicksort();
217 for (int i = ids.size() - 1; i >= 0; --i) {
218 m_fullCompStore.remove(ids[i]);
219 }
220 }
221
223 void addComponent(NodeArray<bool>& isNewTerminal, int id) {
224 m_fullCompStore.foreachNode(id, [&](node v) { isNewTerminal[v] = true; });
225 }
226
229 void preprocess(NodeArray<bool>& isNewTerminal) {
230 Graph H; // a graph where each component is a star
231 NodeArray<int> id(H); // ids each center of the star to the component id
232 NodeArray<node> copy(m_G, nullptr); // ids orig in m_G -> copy in H
233
234 List<node> centers; // all centers
235 for (int i = 0; i < m_fullCompStore.size(); ++i) {
236 const node center = H.newNode();
237 centers.pushBack(center);
238 id[center] = i;
239
240 for (node vG : m_fullCompStore.terminals(i)) {
241 node vH = copy[vG];
242 if (!vH) {
243 vH = H.newNode();
244 copy[vG] = vH;
245 }
246 H.newEdge(vH, center); // target is always center
247 }
248 }
249
250 // find components to be inserted into the steinerTree and insert them
251 ArrayBuffer<int> inactive; // ids of components we insert; we have to remove them from the set of active components afterwards
252 bool changed;
253 do {
254 changed = false;
256 for (ListIterator<node> it = centers.begin(); it.valid(); it = it2) {
257 it2 = it.succ();
258 node c = *it;
259 int innerNodes = 0; // count inner nodes
260 for (adjEntry adj : c->adjEntries) {
261 innerNodes += (adj->twinNode()->degree() != 1);
262 }
263 if (innerNodes <= 1) { // this center represents a component to add to steinerTree
264 // insert component into steinerTree
265 addComponent(isNewTerminal, id[c]);
266
267 // remove center from H (adjacent leaves can remain being isolated nodes)
268 inactive.push(id[c]);
269 H.delNode(c);
270 centers.del(it);
271
272 changed = true;
273 }
274 }
275 } while (changed);
276
277 removeComponents(inactive);
278 }
279
281
282public:
284 Main(const EdgeWeightedGraph<T>& G, const List<node>& terminals,
285 const NodeArray<bool>& isTerminal, int restricted, bool use2approx, bool separateCycles,
286 double eps = 1e-8)
287 : m_G(G)
288 , m_isTerminal(isTerminal)
289 , m_terminals(terminals)
290 , m_fullCompStore(G, m_terminals, isTerminal)
291 , m_restricted(restricted)
292 , m_use2approx(use2approx ? Approx2State::On : Approx2State::Off)
293 , m_eps(eps)
294 , m_approx2SteinerTree(nullptr)
295 , m_approx2Weight(0) {
296 if (m_use2approx == Approx2State::On) { // add upper bound by 2-approximation
298 m_approx2Weight = mstT.call(m_G, m_terminals, m_isTerminal, m_approx2SteinerTree);
299 }
300
301 if (m_restricted > m_terminals.size()) {
302 m_restricted = m_terminals.size();
303 }
304
305 findFullComponents();
306
307 steiner_tree::LPRelaxationSER<T> lp(m_G, m_terminals, m_isTerminal, m_fullCompStore,
308 m_approx2Weight, separateCycles ? m_restricted + 1 : 0, m_eps);
309 if (!lp.solve()) {
310 OGDF_ASSERT(m_use2approx == Approx2State::On);
311 m_use2approx = Approx2State::JustUseIt;
312 }
313 }
314
315 ~Main() { }
316
318 T getApproximation(EdgeWeightedGraphCopy<T>*& finalSteinerTree, const std::minstd_rand& rng,
319 const bool doPreprocessing = true);
320};
321
322template<typename T>
324 const NodeArray<NodeArray<edge>>& pred) {
326 fcg.call(m_G, m_terminals, distance, pred, [&](node s, node t, T cost) {
328 minComp.setOriginalGraph(m_G);
329 minComp.newEdge(minComp.newNode(s), minComp.newNode(t), distance[s][t]);
330 m_fullCompStore.insert(minComp);
331 });
332}
333
334template<typename T>
336 const NodeArray<NodeArray<edge>>& pred) {
338 fcg.call(m_G, m_terminals, m_isTerminal, distance, pred,
339 [&](node t0, node t1, node t2, node minCenter, T minCost) {
340 // create a full 3-component
342 minComp.setOriginalGraph(m_G);
343 node minCenterC = minComp.newNode(minCenter);
344 minComp.newEdge(minComp.newNode(t0), minCenterC, distance[t0][minCenter]);
345 minComp.newEdge(minComp.newNode(t1), minCenterC, distance[t1][minCenter]);
346 minComp.newEdge(minComp.newNode(t2), minCenterC, distance[t2][minCenter]);
347 m_fullCompStore.insert(minComp);
348 });
349}
350
351template<typename T>
353 NodeArray<NodeArray<T>> distance;
355
357 m_terminals, m_isTerminal, m_restricted);
358
359 findFull2Components(distance, pred);
360 if (m_restricted == 3) {
361 findFull3Components(distance, pred);
362 }
363}
364
365template<typename T>
366template<typename FCG>
368 SubsetEnumerator<node> terminalSubset(m_terminals);
369 for (terminalSubset.begin(2, m_restricted); terminalSubset.valid(); terminalSubset.next()) {
370 EdgeWeightedGraphCopy<T> component;
371 List<node> terminals;
372 terminalSubset.list(terminals);
373 fcg.getSteinerTreeFor(terminals, component);
374 if (fcg.isValidComponent(component)) {
375 m_fullCompStore.insert(component);
376 }
377 }
378}
379
380template<typename T>
382 NodeArray<NodeArray<T>> distance;
385 m_terminals, m_isTerminal, m_restricted);
386
387 steiner_tree::FullComponentGeneratorDreyfusWagner<T> fcg(m_G, m_terminals, m_isTerminal,
388 distance, pred);
389 fcg.call(m_restricted);
390 retrieveComponents(fcg);
391}
392
393template<typename T>
396 m_isTerminal);
397 fcg.call(m_restricted);
398 retrieveComponents(fcg);
399}
400
401template<typename T>
403 if (m_restricted >= 4) { // use Dreyfus-Wagner based full component generation
405 m_G.numberOfEdges())) {
406 findFullComponentsEMV();
407 } else {
408 findFullComponentsDW();
409 }
410 } else {
411 find3RestrictedComponents();
412 }
413}
414
415template<typename T>
417 const std::minstd_rand& rng, const bool doPreprocessing) {
418 if (m_use2approx == Approx2State::JustUseIt) {
419 // no remaining components
420 finalSteinerTree = m_approx2SteinerTree;
421 return m_approx2Weight;
422 }
423
424 removeInactiveComponents();
425
426 NodeArray<bool> isNewTerminal(m_G, false);
427 for (node v : m_terminals) {
428 isNewTerminal[v] = true;
429 }
430
431 if (doPreprocessing) {
432 preprocess(isNewTerminal);
433 }
434
435 if (!m_fullCompStore.isEmpty()) {
436 steiner_tree::goemans::Approximation<T> approx(m_G, m_terminals, m_isTerminal,
437 m_fullCompStore, rng, m_eps);
438 approx.solve(isNewTerminal);
439 }
440
441 T cost = steiner_tree::obtainFinalSteinerTree(m_G, isNewTerminal, m_isTerminal, finalSteinerTree);
442
443 if (m_use2approx != Approx2State::Off) {
444 if (m_approx2Weight < cost) {
445 delete finalSteinerTree;
446 finalSteinerTree = m_approx2SteinerTree;
447 cost = m_approx2Weight;
448 } else {
449 delete m_approx2SteinerTree;
450 }
451 }
452
453 return cost;
454}
455
456}
Definition of ogdf::steiner_tree::goemans::Approximation class template.
Declaration and implementation of ArrayBuffer class.
Extends the GraphCopy concept to weighted graphs.
Definition of ogdf::steiner_tree::Full2ComponentGenerator class template.
Definition of ogdf::steiner_tree::Full3ComponentGeneratorVoronoi class template.
Definition of the FullComponentDecisions class.
Definition of the FullComponentGeneratorCaller class template.
Definition of the ogdf::steiner_tree::FullComponentGeneratorDreyfusWagner class template.
Definition of the ogdf::steiner_tree::FullComponentGeneratorDreyfusWagnerWithoutMatrix class template...
Definition of the FullComponentStore class template.
Includes declaration of graph class.
Decralation of GraphElement and GraphList classes.
Definition of ogdf::steiner_tree::LPRelaxationSER class template.
Declaration of doubly linked lists and iterators.
Declaration of ogdf::MinSteinerTreeModule.
Implementation of the 2(1-1/l)-approximation algorithm for the minimum Steiner tree problem by Matsuy...
A class that allows to enumerate k-subsets.
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 push(E e)
Puts a new element in the buffer.
void quicksort()
Sorts buffer using Quicksort.
INDEX size() const
Returns number of elements in the buffer.
edge newEdge(node u, node v, T weight)
void setOriginalGraph(const Graph *wG) override
Re-initializes the copy using G (which might be null), but does not create any nodes or edges.
node newNode(node vOrig)
Creates a new node in the graph copy with original node vOrig.
Definition GraphCopy.h:191
Data type for general directed graphs (adjacency list representation).
Definition Graph_d.h:866
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
void del(iterator it)
Removes it from the list.
Definition List.h:1611
Encapsulates a pointer to a list element.
Definition List.h:113
bool valid() const
Returns true iff the iterator points to an element.
Definition List.h:153
ListIteratorBase< E, isConst, isReverse > succ() const
Returns successor iterator.
Definition List.h:174
iterator begin()
Returns an iterator to the first element of the list.
Definition List.h:391
Class managing LP-based approximation.
const List< node > & m_terminals
List of terminals.
T getApproximation(EdgeWeightedGraphCopy< T > *&finalSteinerTree, const std::minstd_rand &rng, const bool doPreprocessing=true)
Obtain an (1.39+epsilon)-approximation based on the LP solution.
void removeComponents(ArrayBuffer< int > &ids)
Remove the full components with the given ids.
void addComponent(NodeArray< bool > &isNewTerminal, int id)
Add a full component to the final solution (by changing nonterminals to terminals)
const double m_eps
epsilon for double operations
void removeInactiveComponents()
Remove inactive components from m_fullCompStore (since we do not need them any longer)
void find3RestrictedComponents()
Find 3-restricted components.
void retrieveComponents(const FCG &fcg)
Auxiliary function to retrieve components by Dreyfus-Wagner/Erickson et al implementation.
steiner_tree::FullComponentWithExtraStore< T, double > m_fullCompStore
all enumerated full components, with solution
void findFull2Components(const NodeArray< NodeArray< T > > &distance, const NodeArray< NodeArray< edge > > &pred)
Find full components of size 2.
EdgeWeightedGraphCopy< T > * m_approx2SteinerTree
void findFullComponentsEMV()
Find full components using algorithm by Erickson et al.
Main(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, int restricted, bool use2approx, bool separateCycles, double eps=1e-8)
Initialize all attributes, sort the terminal list.
void preprocess(NodeArray< bool > &isNewTerminal)
Preprocess LP solution.
void findFullComponentsDW()
Find full components using algorithm by Dreyfus-Wagner.
void findFull3Components(const NodeArray< NodeArray< T > > &distance, const NodeArray< NodeArray< edge > > &pred)
Find full components of size 3.
This class implements the (1.39+epsilon)-approximation algorithm for the Steiner tree problem by Goem...
void setSeed(int seed)
Set seed for the random number generation.
void setMaxComponentSize(int k)
Sets the maximal number of terminals in a full component.
void separateCycles(bool separateCycles=true)
Use stronger LP relaxation (not recommended in general)
void disablePreprocessing(bool preprocess=true)
Disable preprocessing of LP solutions.
virtual T computeSteinerTree(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, EdgeWeightedGraphCopy< T > *&finalSteinerTree) override
Builds a minimum Steiner tree for a given weighted graph with terminals.
void use2Approximation(bool use2approx=true)
Use Takahashi-Matsuyama 2-approximation as upper bounds.
Serves as an interface for various methods to compute or approximate minimum Steiner trees on undirec...
static void sortTerminals(List< node > &terminals)
Sort terminals by index.
This class implements the minimum Steiner tree 2-approximation algorithm by Takahashi and Matsuyama w...
virtual T call(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, EdgeWeightedGraphCopy< T > *&finalSteinerTree, const node startNode)
An extended call method with specific start node.
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
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.
bool valid() const
Checks if the current subset is valid. If not, the subset is either not initialized or all subsets ha...
void list(List< T > &subset) const
Obtains (appends) a list of the subset members.
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
Trivial full 2-component generation by lookups of shortest paths between terminal pairs.
void call(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< NodeArray< T > > &distance, const NodeArray< NodeArray< edge > > &pred, std::function< void(node, node, T)> generateFunction) const
Generate full 2-components and call generateFunction for each full 2-component.
Full 3-component generation using Voronoi regions.
void call(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, const NodeArray< NodeArray< T > > &distance, const NodeArray< NodeArray< edge > > &pred, std::function< void(node, node, node, node, T)> generateFunction) const
Generate full components and call generateFunction for each full component.
static void computeDistanceMatrix(NodeArray< NodeArray< T > > &distance, NodeArray< NodeArray< edge > > &pred, const EdgeWeightedGraph< T > &graph, const List< node > &terminals, const NodeArray< bool > &isTerminal, int restricted)
Computes distance and predecessor matrix.
A generator for restricted full components (for Steiner tree approximations) based on the Dreyfus-Wag...
A generator for restricted full components (for Steiner tree approximations) based on the Dreyfus-Wag...
void insert(const EdgeWeightedGraphCopy< T > &comp)
Inserts a component. Note that comp is copied and degree-2 nodes are removed.
A data structure to store full components with extra data for each component.
Class managing the component-based subtour elimination LP relaxation for the Steiner tree problem and...
bool solve()
Solve the LP. The solution will be written to the extra data of the full component store.
The actual 1.39-approximation algorithm by Goemans et al. with a set of terminalized nodes as result.
void solve(NodeArray< bool > &isNewTerminal)
Perform the actual approximation algorithm on the LP solution.
Algorithms used by at least two functions of Steiner tree code or its internal helpers.
int main()
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition basic.h:52
T obtainFinalSteinerTree(const EdgeWeightedGraph< T > &G, const NodeArray< bool > &isTerminal, const NodeArray< bool > &isOriginalTerminal, EdgeWeightedGraphCopy< T > *&finalSteinerTree)
The namespace for all OGDF objects.
static bool shouldUseErickson(int n, int m)
Returns true iff the rule of thumb predicts to use the algorithm by Erickson et al instead of the Dre...