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
43
44namespace ogdf {
45
63template<typename T>
65private:
66 class Main;
67
68protected:
73 int m_seed;
74
75public:
82
84
89 void setMaxComponentSize(int k) { m_restricted = k; }
90
95 void setSeed(int seed) { m_seed = seed; }
96
103
109 void disablePreprocessing(bool preprocess = true) { m_preprocess = !preprocess; }
110
116
117protected:
126 virtual T computeSteinerTree(const EdgeWeightedGraph<T>& G, const List<node>& terminals,
127 const NodeArray<bool>& isTerminal, EdgeWeightedGraphCopy<T>*& finalSteinerTree) override;
128};
129
130template<typename T>
132 const List<node>& terminals, const NodeArray<bool>& isTerminal,
134 std::minstd_rand rng(m_seed);
135 List<node> sortedTerminals(terminals);
137 Main main(G, sortedTerminals, isTerminal, m_restricted, m_use2approx, m_separateCycles);
138 return main.getApproximation(finalSteinerTree, rng, m_preprocess);
139}
140
143template<typename T>
150
152 enum class Approx2State {
153 Off,
154 On,
155 JustUseIt,
156 };
158
159 const double m_eps;
160
163
166
168 void findFull2Components(const NodeArray<NodeArray<T>>& distance,
169 const NodeArray<NodeArray<edge>>& pred);
171 void findFull3Components(const NodeArray<NodeArray<T>>& distance,
172 const NodeArray<NodeArray<edge>>& pred);
174 void find3RestrictedComponents();
176 void findFullComponentsDW();
178 void findFullComponentsEMV();
180 template<typename FCG>
181 void retrieveComponents(const FCG& fcg);
183 void findFullComponents();
184
188
191 for (int k = m_fullCompStore.size() - 1; k >= 0; --k) {
192 OGDF_ASSERT(k < m_fullCompStore.size());
193 if (m_fullCompStore.extra(k) <= m_eps) {
194 m_fullCompStore.remove(k);
195 }
196 }
197 }
198
201 ids.quicksort();
202 for (int i = ids.size() - 1; i >= 0; --i) {
203 m_fullCompStore.remove(ids[i]);
204 }
205 }
206
209 m_fullCompStore.foreachNode(id, [&](node v) { isNewTerminal[v] = true; });
210 }
211
215 Graph H; // a graph where each component is a star
216 NodeArray<int> id(H); // ids each center of the star to the component id
217 NodeArray<node> copy(m_G, nullptr); // ids orig in m_G -> copy in H
218
219 List<node> centers; // all centers
220 for (int i = 0; i < m_fullCompStore.size(); ++i) {
221 const node center = H.newNode();
222 centers.pushBack(center);
223 id[center] = i;
224
225 for (node vG : m_fullCompStore.terminals(i)) {
226 node vH = copy[vG];
227 if (!vH) {
228 vH = H.newNode();
229 copy[vG] = vH;
230 }
231 H.newEdge(vH, center); // target is always center
232 }
233 }
234
235 // find components to be inserted into the steinerTree and insert them
236 ArrayBuffer<int> inactive; // ids of components we insert; we have to remove them from the set of active components afterwards
237 bool changed;
238 do {
239 changed = false;
241 for (ListIterator<node> it = centers.begin(); it.valid(); it = it2) {
242 it2 = it.succ();
243 node c = *it;
244 int innerNodes = 0; // count inner nodes
245 for (adjEntry adj : c->adjEntries) {
246 innerNodes += (adj->twinNode()->degree() != 1);
247 }
248 if (innerNodes <= 1) { // this center represents a component to add to steinerTree
249 // insert component into steinerTree
250 addComponent(isNewTerminal, id[c]);
251
252 // remove center from H (adjacent leaves can remain being isolated nodes)
253 inactive.push(id[c]);
254 H.delNode(c);
255 centers.del(it);
256
257 changed = true;
258 }
259 }
260 } while (changed);
261
262 removeComponents(inactive);
263 }
264
266
267public:
269 Main(const EdgeWeightedGraph<T>& G, const List<node>& terminals,
270 const NodeArray<bool>& isTerminal, int restricted, bool use2approx, bool separateCycles,
271 double eps = 1e-8)
272 : m_G(G)
273 , m_isTerminal(isTerminal)
274 , m_terminals(terminals)
275 , m_fullCompStore(G, m_terminals, isTerminal)
278 , m_eps(eps)
279 , m_approx2SteinerTree(nullptr)
280 , m_approx2Weight(0) {
281 if (m_use2approx == Approx2State::On) { // add upper bound by 2-approximation
283 m_approx2Weight = mstT.call(m_G, m_terminals, m_isTerminal, m_approx2SteinerTree);
284 }
285
286 if (m_restricted > m_terminals.size()) {
287 m_restricted = m_terminals.size();
288 }
289
290 findFullComponents();
291
292 steiner_tree::LPRelaxationSER<T> lp(m_G, m_terminals, m_isTerminal, m_fullCompStore,
293 m_approx2Weight, separateCycles ? m_restricted + 1 : 0, m_eps);
294 if (!lp.solve()) {
295 OGDF_ASSERT(m_use2approx == Approx2State::On);
296 m_use2approx = Approx2State::JustUseIt;
297 }
298 }
299
300 ~Main() { }
301
303 T getApproximation(EdgeWeightedGraphCopy<T>*& finalSteinerTree, const std::minstd_rand& rng,
304 const bool doPreprocessing = true);
305};
306
307template<typename T>
309 const NodeArray<NodeArray<edge>>& pred) {
311 fcg.call(m_G, m_terminals, distance, pred, [&](node s, node t, T cost) {
314 minComp.newEdge(minComp.newNode(s), minComp.newNode(t), distance[s][t]);
316 });
317}
318
319template<typename T>
321 const NodeArray<NodeArray<edge>>& pred) {
323 fcg.call(m_G, m_terminals, m_isTerminal, distance, pred,
324 [&](node t0, node t1, node t2, node minCenter, T minCost) {
325 // create a full 3-component
327 minComp.createEmpty(m_G);
328 node minCenterC = minComp.newNode(minCenter);
329 minComp.newEdge(minComp.newNode(t0), minCenterC, distance[t0][minCenter]);
330 minComp.newEdge(minComp.newNode(t1), minCenterC, distance[t1][minCenter]);
331 minComp.newEdge(minComp.newNode(t2), minCenterC, distance[t2][minCenter]);
332 m_fullCompStore.insert(minComp);
333 });
334}
335
336template<typename T>
338 NodeArray<NodeArray<T>> distance;
340
342 m_terminals, m_isTerminal, m_restricted);
343
344 findFull2Components(distance, pred);
345 if (m_restricted == 3) {
346 findFull3Components(distance, pred);
347 }
348}
349
350template<typename T>
351template<typename FCG>
354 for (terminalSubset.begin(2, m_restricted); terminalSubset.valid(); terminalSubset.next()) {
355 EdgeWeightedGraphCopy<T> component;
356 List<node> terminals;
357 terminalSubset.list(terminals);
358 fcg.getSteinerTreeFor(terminals, component);
359 if (fcg.isValidComponent(component)) {
360 m_fullCompStore.insert(component);
361 }
362 }
363}
364
365template<typename T>
367 NodeArray<NodeArray<T>> distance;
370 m_terminals, m_isTerminal, m_restricted);
371
372 steiner_tree::FullComponentGeneratorDreyfusWagner<T> fcg(m_G, m_terminals, m_isTerminal,
373 distance, pred);
374 fcg.call(m_restricted);
375 retrieveComponents(fcg);
376}
377
378template<typename T>
385
386template<typename T>
388 if (m_restricted >= 4) { // use Dreyfus-Wagner based full component generation
390 m_G.numberOfEdges())) {
391 findFullComponentsEMV();
392 } else {
393 findFullComponentsDW();
394 }
395 } else {
396 find3RestrictedComponents();
397 }
398}
399
400template<typename T>
402 const std::minstd_rand& rng, const bool doPreprocessing) {
403 if (m_use2approx == Approx2State::JustUseIt) {
404 // no remaining components
405 finalSteinerTree = m_approx2SteinerTree;
406 return m_approx2Weight;
407 }
408
409 removeInactiveComponents();
410
411 NodeArray<bool> isNewTerminal(m_G, false);
412 for (node v : m_terminals) {
413 isNewTerminal[v] = true;
414 }
415
416 if (doPreprocessing) {
417 preprocess(isNewTerminal);
418 }
419
420 if (!m_fullCompStore.isEmpty()) {
421 steiner_tree::goemans::Approximation<T> approx(m_G, m_terminals, m_isTerminal,
422 m_fullCompStore, rng, m_eps);
423 approx.solve(isNewTerminal);
424 }
425
427
428 if (m_use2approx != Approx2State::Off) {
429 if (m_approx2Weight < cost) {
430 delete finalSteinerTree;
431 finalSteinerTree = m_approx2SteinerTree;
432 cost = m_approx2Weight;
433 } else {
434 delete m_approx2SteinerTree;
435 }
436 }
437
438 return cost;
439}
440
441}
Definition of ogdf::steiner_tree::goemans::Approximation class template.
Definition of ogdf::steiner_tree::Full2ComponentGenerator class template.
Definition of ogdf::steiner_tree::Full3ComponentGeneratorVoronoi class template.
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 ogdf::steiner_tree::LPRelaxationSER class template.
Class for adjacency list elements.
Definition Graph_d.h:79
An array that keeps track of the number of inserted elements; also usable as an efficient stack.
Definition ArrayBuffer.h:56
void push(E e)
Puts a new element in the buffer.
Data type for general directed graphs (adjacency list representation).
Definition Graph_d.h:521
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 insert(const E &x, iterator it, Direction dir=Direction::after)
Inserts element x before or after it.
Definition List.h:1544
Encapsulates a pointer to a list element.
Definition List.h:103
bool valid() const
Returns true iff the iterator points to an element.
Definition List.h:137
ListIteratorBase< E, isConst, isReverse > succ() const
Returns successor iterator.
Definition List.h:158
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.
Dynamic arrays indexed with nodes.
Definition NodeArray.h:125
Class for the representation of nodes.
Definition Graph_d.h:177
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
Definition Graph_d.h:208
Enumerator for k-subsets of a given type.
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.
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:41
static MultilevelBuilder * getDoubleFactoredZeroAdjustedMerger()
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...