Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
SteinerTreePreprocessing.h
Go to the documentation of this file.
1
32#pragma once
33
34#include <ogdf/basic/Array.h>
37#include <ogdf/basic/Graph.h>
39#include <ogdf/basic/List.h>
40#include <ogdf/basic/Math.h>
43#include <ogdf/basic/basic.h>
54
55#include <algorithm>
56#include <forward_list>
57#include <functional>
58#include <iostream>
59#include <limits>
60#include <memory>
61#include <set>
62#include <unordered_map>
63#include <utility>
64#include <vector>
65
66namespace ogdf {
67template<typename T>
68class MinSteinerTreeModule;
69
70// Helpers:
71namespace steiner_tree {
77public:
78 int operator()(const NodePair& v) const {
79 return static_cast<int>((static_cast<long long>(min(v.source->index(), v.target->index()) + 11)
80 * (max(v.source->index(), v.target->index()) + 73))
81 % 700001);
82 }
83};
84
90public:
91 bool operator()(const NodePair& pair1, const NodePair& pair2) const {
92 return (pair1.source == pair2.source && pair1.target == pair2.target)
93 || (pair1.source == pair2.target && pair1.target == pair2.source);
94 }
95};
96
97}
98
110template<typename T>
112protected:
117
121
123
132 std::vector<std::vector<int>> m_sonsList;
133
135 std::unique_ptr<MinSteinerTreeModule<T>> m_costUpperBoundAlgorithm;
136
137public:
143 SteinerTreePreprocessing(const EdgeWeightedGraph<T>& wg, const List<node>& terminals,
144 const NodeArray<bool>& isTerminal);
145
155 finalSteinerTree = new EdgeWeightedGraphCopy<T>;
156 // reductions generate new nodes and edges, which may inflate the internal structure
157 if (m_copyGraph.maxNodeIndex() <= m_copyGraph.numberOfNodes() + 5
158 && m_copyGraph.maxEdgeIndex()
159 <= m_copyGraph.numberOfEdges() + 10) { // within inflate tolerance
160 EdgeWeightedGraphCopy<T>* reducedSolution = nullptr;
161 T cost = mst.call(m_copyGraph, m_copyTerminals, m_copyIsTerminal, reducedSolution);
162 cost += costEdgesAlreadyInserted();
163 computeOriginalSolution(*reducedSolution, *finalSteinerTree);
164 delete reducedSolution;
165 return cost;
166 }
167
168 // make a compact copy
169 EdgeWeightedGraph<T> ccGraph;
170 NodeArray<node> nCopy(m_copyGraph, nullptr);
171 EdgeArray<edge> eCopy(m_copyGraph, nullptr);
172 List<node> ccTerminals;
173 NodeArray<bool> ccIsTerminal(ccGraph, false);
174 for (node v : m_copyGraph.nodes) {
175 nCopy[v] = ccGraph.newNode();
176 }
177 for (edge e : m_copyGraph.edges) {
178 eCopy[e] = ccGraph.newEdge(nCopy[e->source()], nCopy[e->target()], m_copyGraph.weight(e));
179 }
180 for (node t : m_copyTerminals) {
181 const node tC = nCopy[t];
182 ccTerminals.pushBack(tC);
183 ccIsTerminal[tC] = true;
184 }
185
186 // solve compact copy
187 EdgeWeightedGraphCopy<T>* ccSolution = nullptr;
188 T cost = mst.call(ccGraph, ccTerminals, ccIsTerminal, ccSolution);
189 cost += costEdgesAlreadyInserted();
190
191 // get reduced and original solution from compact copy solution
192 EdgeWeightedGraphCopy<T> reducedSolution;
193 reducedSolution.setOriginalGraph(m_copyGraph);
194 for (node v : m_copyGraph.nodes) {
195 if (ccSolution->copy(nCopy[v]) != nullptr) { // is in solution
196 reducedSolution.newNode(v);
197 }
198 }
199 for (edge e : m_copyGraph.edges) {
200 if (ccSolution->copy(eCopy[e]) != nullptr) { // is in solution
201 reducedSolution.newEdge(e, m_copyGraph.weight(e));
202 }
203 }
204 delete ccSolution;
205
206 computeOriginalSolution(reducedSolution, *finalSteinerTree);
207 return cost;
208 }
209
216 inline const EdgeWeightedGraph<T>& getReducedGraph() const { return m_copyGraph; }
217
219 inline const List<node>& getReducedTerminals() const { return m_copyTerminals; }
220
222 inline void shuffleReducedTerminals() { m_copyTerminals.permute(); }
223
225 inline const NodeArray<bool>& getReducedIsTerminal() const { return m_copyIsTerminal; }
226
228
236
242 void computeOriginalSolution(const EdgeWeightedGraphCopy<T>& reducedGraphSolution,
243 EdgeWeightedGraphCopy<T>& correspondingOriginalSolution);
244
246
252
255 return repeat([this]() {
256 bool changed = false;
257 changed |= degree2Test();
258 changed |= makeSimple();
259 changed |= deleteLeaves();
260 return changed;
261 });
262 }
263
265 bool reduceFast() {
266 int k = 5; // for PTmTest
267 // comment wrt. Apple Clang 10: making k const, results in compiler error while binding it in lambda
268 // comment wrt. MSVC 19: not binding the const k would result in compiler error
269
270 bool changed = deleteComponentsWithoutTerminals();
271 bool triviallyChanged = false;
272 changed |= repeat([this, &triviallyChanged, k]() {
273 bool innerChanged = false;
274 triviallyChanged = reduceTrivial();
275 // graph guaranteed to be simple and connected
276
277 if (nearestVertexTest()) {
278 makeSimple();
279 innerChanged = true;
280 }
281
282 // precond: connected
283 if (terminalDistanceTest()) {
284 // can occur: disconnected
286 innerChanged = true;
287 }
288
289 // precond: simple, connected
290 innerChanged |= NTDkTest(10, k);
291 // can occur: parallel edges
292
293 // precond: connected
294 innerChanged |= shortLinksTest();
295 // can occur: parallel edges, self-loops
296
297 // precond: connected
298 innerChanged |= lowerBoundBasedTest();
299
300 // is not thaaat good but helps a little:
301 innerChanged |= PTmTest(k);
302 // can occur: parallel edges
303
304 return innerChanged;
305 });
306 return changed | triviallyChanged;
307 }
308
311 return repeat([this] {
312 bool changed = reduceFast();
313 changed |= dualAscentBasedTest();
314 return changed;
315 });
316 }
317
319
325
333 bool deleteLeaves();
334
341 bool makeSimple();
342
350
357 bool leastCostTest();
358
365 bool degree2Test();
366
374 bool PTmTest(const int k = 3);
375
385
392 bool longEdgesTest();
393
404 bool NTDkTest(const int maxTestedDegree = 5, const int k = 3);
405
414 bool nearestVertexTest();
415
424 bool shortLinksTest();
425
436 bool lowerBoundBasedTest(T upperBound);
437
445
455 bool dualAscentBasedTest(int repetitions, T upperBound);
456
461 inline bool dualAscentBasedTest(int repetitions = 1) {
463 }
464
475 bool reachabilityTest(int maxDegreeTest = 0, const int k = 3);
476
485 bool cutReachabilityTest();
486
488
495 inline void setCostUpperBoundAlgorithm(MinSteinerTreeModule<T>* pMinSteinerTreeModule) {
496 m_costUpperBoundAlgorithm.reset(pMinSteinerTreeModule);
497 }
498
500
502 static bool repeat(std::function<bool()> f) {
503 bool changed = false;
504 while (f()) {
505 changed = true;
506 }
507 return changed;
508 }
509
510protected:
519 template<typename TWhat, typename TWhatArray>
520 void addNew(TWhat x, const std::vector<node>& replacedNodes,
521 const std::vector<edge>& replacedEdges, bool deleteReplacedElements,
522 TWhatArray& whatSonsListIndex);
523
525 inline void addNewNode(node v, const std::vector<node>& replacedNodes,
526 const std::vector<edge>& replacedEdges, bool deleteReplacedElements) {
527 addNew(v, replacedNodes, replacedEdges, deleteReplacedElements, m_nodeSonsListIndex);
528 }
529
531 inline void addNewEdge(edge e, const std::vector<node>& replacedNodes,
532 const std::vector<edge>& replacedEdges, bool deleteReplacedElements) {
533 addNew(e, replacedNodes, replacedEdges, deleteReplacedElements, m_edgeSonsListIndex);
534 }
535
542 bool addEdgesToSolution(const List<edge>& edgesToBeAddedInSolution);
543
548
550 void computeShortestPathMatrix(NodeArray<NodeArray<T>>& shortestPath) const;
551
553 void floydWarshall(NodeArray<NodeArray<T>>& shortestPath) const;
554
556 finalSteinerTree = nullptr;
558 finalSteinerTree);
559 }
560
561 // for convenience:
563 EdgeWeightedGraphCopy<T>* finalSteinerTree;
564 T treeCostUpperBound = computeMinSteinerTreeUpperBound(finalSteinerTree);
565 delete finalSteinerTree;
566 return treeCostUpperBound;
567 }
568
570 void addToSolution(const int listIndex, Array<bool, int>& isInSolution) const;
571
573 bool deleteNodesAboveUpperBound(const NodeArray<T>& lowerBoundWithNode, const T upperBound);
575 bool deleteEdgesAboveUpperBound(const EdgeArray<T>& lowerBoundWithEdge, const T upperBound);
576
580 const NodeArray<List<std::pair<node, T>>>& closestTerminals);
581
591 void findClosestNonTerminals(node source, List<node>& reachedNodes, NodeArray<T>& distance,
592 T maxDistance, int expandedEdges) const;
593
600 const NodeArray<List<std::pair<node, T>>>& closestTerminals) const;
601
607 void computeClosestKTerminals(const int k,
608 NodeArray<List<std::pair<node, T>>>& closestTerminals) const;
609
611 void computeRadiusOfTerminals(NodeArray<T>& terminalRadius) const;
612
614 T computeRadiusSum() const;
615
617 template<typename LAMBDA>
618 void computeOptimalTerminals(node v, LAMBDA dist, node& optimalTerminal1,
619 node& optimalTerminal2, NodeArray<T>& distance) const;
620
622 void markSuccessors(node currentNode, const Voronoi<T>& voronoiRegions,
623 NodeArray<bool>& isSuccessorOfMinCostEdge) const;
624
626 void findTwoMinimumCostEdges(node v, edge& first, edge& second) const;
627};
628
629template<typename T>
631 const List<node>& terminals, const NodeArray<bool>& isTerminal)
632 : m_origGraph(wg), m_origTerminals(terminals), m_origIsTerminal(isTerminal), m_eps(1e-6) {
633 OGDF_ASSERT(!m_origGraph.empty());
634
635 // make the initial graph copy
636 m_copyGraph.clear();
639
640 for (node v : m_origGraph.nodes) {
641 nCopy[v] = m_copyGraph.newNode();
642 }
643 for (edge e : m_origGraph.edges) {
644 eCopy[e] = m_copyGraph.newEdge(nCopy[e->source()], nCopy[e->target()], m_origGraph.weight(e));
645 }
646
647 // create the terminals and isTerminal arrays for the copyGraph
648 m_copyTerminals.clear();
650 for (node v : m_origGraph.nodes) {
651 if (m_origIsTerminal(v)) {
652 const node vC = nCopy[v];
653 m_copyTerminals.pushBack(vC);
654 m_copyIsTerminal[vC] = true;
655 }
656 }
658
659 // map every node and edge to a negative number for m_(node/edge)_m_sonsListIndex
662 int nextIndex = 1;
663 for (node v : m_origGraph.nodes) {
664 m_nodeSonsListIndex[nCopy[v]] = -(nextIndex++);
665 }
666 for (edge e : m_origGraph.edges) {
667 m_edgeSonsListIndex[eCopy[e]] = -(nextIndex++);
668 }
669
670 // set the default approximation algorithm for computing the upper bound cost of the solution
672}
673
674template<typename T>
675template<typename TWhat, typename TWhatArray>
676void SteinerTreePreprocessing<T>::addNew(TWhat x, const std::vector<node>& replacedNodes,
677 const std::vector<edge>& replacedEdges, bool deleteReplacedElements,
678 TWhatArray& whatSonsListIndex) {
679 std::vector<int> sonsIndexList;
680 for (node replacedNode : replacedNodes) {
681 sonsIndexList.push_back(m_nodeSonsListIndex[replacedNode]);
682 }
683 for (edge replacedEdge : replacedEdges) {
684 sonsIndexList.push_back(m_edgeSonsListIndex[replacedEdge]);
685 }
686
687 m_sonsList.emplace_back(sonsIndexList);
688 whatSonsListIndex[x] = (int)m_sonsList.size() - 1;
689
690 if (deleteReplacedElements) {
691 for (edge e : replacedEdges) {
692 m_copyGraph.delEdge(e);
693 }
694 for (node v : replacedNodes) {
695 m_copyGraph.delNode(v);
696 }
697 }
698}
699
700template<typename T>
702 Array<bool, int>& isInSolution) const {
703 if (listIndex < 0) { // is starting node or edge
704 isInSolution[listIndex] = true;
705 return;
706 }
707 // is further added node or edge
708 for (int son : m_sonsList[listIndex]) {
709 addToSolution(son, isInSolution);
710 }
711}
712
713template<typename T>
715 const EdgeWeightedGraphCopy<T>& reducedGraphSolution,
716 EdgeWeightedGraphCopy<T>& correspondingOriginalSolution) {
717 correspondingOriginalSolution.setOriginalGraph(m_origGraph); // note that it is not cleared!
718
719 Array<bool, int> isInSolution(-(m_origGraph.numberOfNodes() + m_origGraph.numberOfEdges()), -1,
720 false);
721
722 // make the indices of original nodes/edge true in isInSolution
723 for (node v : reducedGraphSolution.nodes) {
724 addToSolution(m_nodeSonsListIndex[reducedGraphSolution.original(v)], isInSolution);
725 }
726 for (edge e : reducedGraphSolution.edges) {
727 addToSolution(m_edgeSonsListIndex[reducedGraphSolution.original(e)], isInSolution);
728 }
729
730 // insert nodes and edges
731 int nextIndex = 1;
732 for (node v : m_origGraph.nodes) {
733 if (isInSolution[-(nextIndex++)]) {
734 correspondingOriginalSolution.newNode(v);
735 }
736 }
737 for (edge e : m_origGraph.edges) {
738 if (isInSolution[-(nextIndex++)]) {
739 correspondingOriginalSolution.newEdge(e, m_origGraph.weight(e));
740 }
741 }
742}
743
744template<typename T>
746 const EdgeWeightedGraphCopy<T>& tprime,
748 const NodeArray<List<std::pair<node, T>>>& closestTerminals) {
749 struct NewEdgeData {
750 edge e1; // new edge replaces this...
751 edge e2; // ...and this
752 edge already; // but is it already there?
753 T weight;
754 };
755
756 std::forward_list<NewEdgeData> newEdges;
757 for (adjEntry adj1 : v->adjEntries) {
758 const edge e1 = adj1->theEdge();
759 const node adjacentNode1 = adj1->twinNode();
760
761 for (adjEntry adj2 = adj1->succ(); adj2; adj2 = adj2->succ()) {
762 const edge e2 = adj2->theEdge();
763 const node adjacentNode2 = adj2->twinNode();
764
765 T edgeWeight = m_copyGraph.weight(e1) + m_copyGraph.weight(e2);
766
767 const edge f = m_copyGraph.searchEdge(adjacentNode1, adjacentNode2);
768 if (f && m_copyGraph.weight(f) <= edgeWeight) {
769 continue; // check whether there is already a lower cost edge connecting the two adjacent nodes
770 }
771
772 T bottleneckDistance = computeBottleneckDistance(adjacentNode1, adjacentNode2, tprime,
773 tprimeHPD, closestTerminals);
774 if (m_eps.greater(edgeWeight, bottleneckDistance)) {
775 continue; // the PTm test
776 }
777
778 newEdges.emplace_front(NewEdgeData {e1, e2, f, edgeWeight});
779 }
780 }
781
782 for (const auto& newEdge : newEdges) {
783 // delete the old edge (do not generate parallel edges)
784 edge newEdgeInGraph;
785 if (newEdge.already) {
786 OGDF_ASSERT(m_copyGraph.weight(newEdge.already) > newEdge.weight);
787 m_copyGraph.setWeight(newEdge.already, newEdge.weight);
788 newEdgeInGraph = newEdge.already;
789 } else {
790 newEdgeInGraph = m_copyGraph.newEdge(newEdge.e1->opposite(v), newEdge.e2->opposite(v),
791 newEdge.weight);
792 }
793 addNewEdge(newEdgeInGraph, {v}, {newEdge.e1, newEdge.e2}, false);
794 }
795
796 m_copyGraph.delNode(v);
797}
798
799template<typename T>
801 m_copyTerminals.clear();
802 for (node v : m_copyGraph.nodes) {
803 if (m_copyIsTerminal[v]) {
804 m_copyTerminals.pushBack(v);
805 }
806 }
807}
808
809template<typename T>
811 const EdgeWeightedGraphCopy<T>& tprime,
813 const NodeArray<List<std::pair<node, T>>>& closestTerminals) const {
814 T bottleneckDistance = std::numeric_limits<T>::max();
815
816 for (auto& xCloseTerminalDistancePair : closestTerminals[x]) {
817 for (auto& yCloseTerminalDistancePair : closestTerminals[y]) {
818 T possibleBottleneckDistance =
819 xCloseTerminalDistancePair.second + yCloseTerminalDistancePair.second;
820
821 node xTprimeCopy = tprime.copy(xCloseTerminalDistancePair.first);
822 node yTprimeCopy = tprime.copy(yCloseTerminalDistancePair.first);
823 possibleBottleneckDistance +=
824 tprimeHPD.getBottleneckSteinerDistance(xTprimeCopy, yTprimeCopy);
825
826 Math::updateMin(bottleneckDistance, possibleBottleneckDistance);
827 }
828 }
829
830 return bottleneckDistance;
831}
832
833template<typename T>
835 // exceptional case: only one terminal
836 auto deleteAll = [this]() {
837 if (m_copyGraph.numberOfNodes() > 1) {
838 const node w = m_copyTerminals.front();
839 // just remove all other nodes
840 for (node v = m_copyGraph.firstNode(), nextV; v; v = nextV) {
841 nextV = v->succ();
842 if (v != w) {
843 m_copyGraph.delNode(v);
844 }
845 }
846 return true;
847 }
848 return false;
849 };
850 if (m_copyTerminals.size() == 1) {
851 return deleteAll();
852 }
853 // general case: at least 2 terminals
854 BoundedQueue<node> eraseQueue(m_copyGraph.numberOfNodes());
855
856 for (node v : m_copyGraph.nodes) {
857 if (v->degree() == 1) {
858 eraseQueue.append(v);
859 }
860 }
861
862 if (eraseQueue.empty()) {
863 return false;
864 }
865
866 for (; !eraseQueue.empty(); eraseQueue.pop()) {
867 node v = eraseQueue.top();
868 if (v->degree() == 0) {
869 continue;
870 }
871 OGDF_ASSERT(v->degree() == 1);
872 const node w = v->firstAdj()->twinNode();
873 if (m_copyIsTerminal[v]) { // v is a terminal
874 // add edge to solution and contract v into w
875 edge e = v->firstAdj()->theEdge();
876 if (!m_copyIsTerminal[w]) {
877 m_copyIsTerminal[w] = true;
878 m_copyTerminals.pushBack(w);
879 }
880 m_copyTerminals.del(m_copyTerminals.search(v));
881 m_costAlreadyInserted += m_copyGraph.weight(e);
882 int cur = m_nodeSonsListIndex[w];
883 if (cur < 0) { // w is an original (copied) node
884 m_sonsList.emplace_back(
885 std::vector<int> {cur, m_nodeSonsListIndex[v], m_edgeSonsListIndex[e]});
886 m_nodeSonsListIndex[w] = (int)m_sonsList.size() - 1;
887 } else { // w contains already sons
888 m_sonsList[cur].push_back(m_nodeSonsListIndex[v]);
889 m_sonsList[cur].push_back(m_edgeSonsListIndex[e]);
890 }
891 } else { // v is not a terminal
892 if (w->degree() == 2) {
893 eraseQueue.append(w);
894 }
895 }
896 m_copyGraph.delNode(v);
897 if (m_copyTerminals.size() == 1) {
898 deleteAll();
899 return true;
900 }
901 }
902 return true;
903}
904
905template<typename T>
907 bool changed = false;
908 NodeArray<edge> minCostEdge(m_copyGraph, nullptr);
909 for (node v : m_copyGraph.nodes) {
910 for (adjEntry adj = v->firstAdj(), nextAdj; adj; adj = nextAdj) {
911 nextAdj = adj->succ();
912
913 edge e = adj->theEdge();
914 node adjNode = adj->twinNode();
915 if (adjNode == v) { // found a self-loop
916 if (nextAdj == adj->twin()) {
917 nextAdj = nextAdj->succ();
918 }
919 m_copyGraph.delEdge(e);
920 changed = true;
921 } else {
922 if (minCostEdge[adjNode] == nullptr
923 || m_copyGraph.weight(minCostEdge[adjNode]) > m_copyGraph.weight(e)) {
924 if (minCostEdge[adjNode] != nullptr) {
925 m_copyGraph.delEdge(minCostEdge[adjNode]);
926 changed = true;
927 }
928 minCostEdge[adjNode] = e;
929 } else {
930 m_copyGraph.delEdge(e);
931 changed = true;
932 }
933 }
934 }
935
936 for (adjEntry adj : v->adjEntries) {
937 minCostEdge[adj->twinNode()] = nullptr;
938 }
939 }
940 return changed;
941}
942
943template<typename T>
945 for (node v1 : m_copyGraph.nodes) {
946 for (adjEntry adj : v1->adjEntries) {
947 node v2 = adj->twinNode();
948 shortestPath[v1][v2] = shortestPath[v2][v1] =
949 min(shortestPath[v1][v2], m_copyGraph.weight(adj->theEdge()));
950 }
951 }
952
953 for (node pivot : m_copyGraph.nodes) {
954 for (node v1 : m_copyGraph.nodes) {
955 for (node v2 : m_copyGraph.nodes) {
956 if (shortestPath[v1][pivot] == std::numeric_limits<T>::max()
957 || shortestPath[pivot][v2] == std::numeric_limits<T>::max()) {
958 continue;
959 }
960
961 Math::updateMin(shortestPath[v1][v2],
962 shortestPath[v1][pivot] + shortestPath[pivot][v2]);
963 }
964 }
965 }
966}
967
968template<typename T>
970 NodeArray<NodeArray<T>>& shortestPath) const {
971 shortestPath.init(m_copyGraph);
972 for (node v : m_copyGraph.nodes) {
973 shortestPath[v].init(m_copyGraph, std::numeric_limits<T>::max());
974 }
975
976 floydWarshall(shortestPath);
977}
978
979template<typename T>
981 OGDF_ASSERT(!m_copyGraph.empty());
982 bool changed = false;
983 NodeArray<NodeArray<T>> shortestPath;
984 computeShortestPathMatrix(shortestPath);
985
986 for (node v : m_copyGraph.nodes) {
987 for (adjEntry adj = v->firstAdj(), nextAdj; adj; adj = nextAdj) {
988 nextAdj = adj->succ();
989
990 edge e = adj->theEdge();
991 node adjNode = adj->twinNode();
992 if (adjNode != v && shortestPath[v][adjNode] < m_copyGraph.weight(e)) {
993 m_copyGraph.delEdge(e);
994 changed = true;
995 }
996 }
997 }
998 return changed;
999}
1000
1001template<typename T>
1003 OGDF_ASSERT(!m_copyGraph.empty());
1004 bool changed = false;
1005 for (node v = m_copyGraph.firstNode(), nextV; v; v = nextV) {
1006 nextV = v->succ();
1007
1008 if (m_copyIsTerminal[v] || v->degree() != 2) {
1009 continue;
1010 }
1011
1012 // get left and right adjacent nodes
1013 edge eLeft = v->firstAdj()->theEdge();
1014 edge eRight = v->lastAdj()->theEdge();
1015
1016 node vLeft = v->firstAdj()->twinNode();
1017 node vRight = v->lastAdj()->twinNode();
1018 if (vLeft != vRight) {
1019 T weight = m_copyGraph.weight(eLeft) + m_copyGraph.weight(eRight);
1020 edge newEdge = m_copyGraph.newEdge(vLeft, vRight, weight);
1021 addNewEdge(newEdge, {v}, {eLeft, eRight}, true);
1022 changed = true;
1023 } else { // v is leaf with parallel edges or self-loop component
1024 m_copyGraph.delNode(v);
1025 }
1026 }
1027 return changed;
1028}
1029
1030template<typename T>
1032 NodeArray<int> hisConnectedComponent(m_copyGraph, -1);
1033 bool changed = connectedComponents(m_copyGraph, hisConnectedComponent) > 1;
1034 if (changed) {
1035 int componentWithTerminals = -1;
1036 for (node v : m_copyTerminals) {
1037 if (componentWithTerminals != -1 && hisConnectedComponent[v] != componentWithTerminals) {
1038 std::cerr << "terminals in different connected components!\n";
1039 OGDF_ASSERT(false);
1040 }
1041 componentWithTerminals = hisConnectedComponent[v];
1042 }
1043
1044 for (node v = m_copyGraph.firstNode(), nextV; v; v = nextV) {
1045 nextV = v->succ();
1046 if (hisConnectedComponent[v] != componentWithTerminals) {
1047 m_copyGraph.delNode(v);
1048 }
1049 }
1050 }
1051 return changed;
1052}
1053
1054template<typename T>
1056 OGDF_ASSERT(!m_copyGraph.empty());
1057 OGDF_ASSERT(isConnected(m_copyGraph));
1058 bool changed = false;
1059
1062 m_copyTerminals);
1063 T maxBottleneck(0);
1064 for (edge e : tprime.edges) {
1065 Math::updateMax(maxBottleneck, tprime.weight(e));
1066 }
1067
1068 for (edge e = m_copyGraph.firstEdge(), nextE; e; e = nextE) {
1069 nextE = e->succ();
1070
1071 if (m_eps.greater(m_copyGraph.weight(e), maxBottleneck)) {
1072 m_copyGraph.delEdge(e);
1073 changed = true;
1074 }
1075 }
1076
1077 return changed;
1078}
1079
1080template<typename T>
1082 NodeArray<T>& distance, T maxDistance, int expandedEdges) const {
1083 PrioritizedMapQueue<node, T> queue(m_copyGraph);
1084
1085 // initialization
1086 distance[source] = 0;
1087 queue.push(source, distance[source]);
1088
1089 while (!queue.empty()) {
1090 node currentNode = queue.topElement();
1091 queue.pop();
1092
1093 reachedNodes.pushBack(currentNode);
1094
1095 for (adjEntry adj : currentNode->adjEntries) {
1096 edge e = adj->theEdge();
1097 if (expandedEdges <= 0) {
1098 break;
1099 }
1100 --expandedEdges;
1101
1102 node adjacentNode = e->opposite(currentNode);
1103 T possibleDistance = distance[currentNode] + m_copyGraph.weight(e);
1104
1105 if (m_eps.geq(possibleDistance, maxDistance) || m_copyIsTerminal[adjacentNode]) {
1106 continue;
1107 }
1108
1109 if (possibleDistance < distance[adjacentNode]) {
1110 distance[adjacentNode] = possibleDistance;
1111 if (queue.contains(adjacentNode)) { // is already in the queue
1112 queue.decrease(adjacentNode, possibleDistance);
1113 } else {
1114 queue.push(adjacentNode, distance[adjacentNode]);
1115 }
1116 }
1117 }
1118 }
1119}
1120
1121template<typename T>
1123 OGDF_ASSERT(!m_copyGraph.empty());
1124 bool changed = false;
1125 NodeArray<T> xDistance(m_copyGraph, std::numeric_limits<T>::max()),
1126 yDistance(m_copyGraph, std::numeric_limits<T>::max());
1127
1128 for (edge e = m_copyGraph.firstEdge(), nextE; e; e = nextE) {
1129 nextE = e->succ();
1130 List<node> xReachedNodes, yReachedNodes;
1131
1132 findClosestNonTerminals(e->source(), xReachedNodes, xDistance, m_copyGraph.weight(e), 200);
1133 findClosestNonTerminals(e->target(), yReachedNodes, yDistance, m_copyGraph.weight(e), 200);
1134
1135 for (node commonNode : xReachedNodes) {
1136 if (yDistance[commonNode] == std::numeric_limits<T>::max()) { // is not common
1137 continue;
1138 }
1139 if (m_eps.less(xDistance[commonNode] + yDistance[commonNode], m_copyGraph.weight(e))) {
1140 m_copyGraph.delEdge(e);
1141 changed = true;
1142 break;
1143 }
1144 }
1145
1146 for (node reachedNode : xReachedNodes) {
1147 xDistance[reachedNode] = std::numeric_limits<T>::max();
1148 }
1149 for (node reachedNode : yReachedNodes) {
1150 yDistance[reachedNode] = std::numeric_limits<T>::max();
1151 }
1152 }
1153 return changed;
1154}
1155
1156template<typename T>
1158 NodeArray<List<std::pair<node, T>>>& closestTerminals) const {
1159 using NodePairQueue = PrioritizedQueue<NodePair, T>;
1160
1161 closestTerminals.init(m_copyGraph);
1162 NodePairQueue queue;
1163 std::unordered_map<NodePair, typename NodePairQueue::Handle,
1165 qpos;
1166
1167 // initialization
1168 for (node v : m_copyTerminals) {
1169 closestTerminals[v].pushBack(std::make_pair(v, 0));
1170 qpos[NodePair(v, v)] = queue.push(NodePair(v, v), closestTerminals[v].front().second);
1171 }
1172
1173 auto getCurrentDist = [&closestTerminals](const node currentNode, const node sourceTerminal) {
1174 for (std::pair<node, T> currentPair : closestTerminals[currentNode]) {
1175 if (currentPair.first == sourceTerminal) {
1176 return currentPair.second;
1177 }
1178 }
1179 return static_cast<T>(-1);
1180 };
1181
1182 auto setNewDist = [&closestTerminals, k](const node currentNode, const node sourceTerminal,
1183 const T newDist) {
1184 List<std::pair<node, T>>& currentList = closestTerminals[currentNode];
1185
1186 // delete the old distance
1187 for (ListIterator<std::pair<node, T>> it = currentList.begin(); it.valid(); ++it) {
1188 if ((*it).first == sourceTerminal) {
1189 currentList.del(it);
1190 break;
1191 }
1192 }
1193
1194 if (currentList.size() == k) { // the list is full
1195 currentList.popBack(); // delete the largest cost element
1196 }
1197
1198 // add the new distance such that the list remains sorted
1199 if (currentList.empty() || (*currentList.begin()).second >= newDist) { // if the list is empty
1200 currentList.pushFront(std::make_pair(sourceTerminal, newDist));
1201 return;
1202 }
1203
1204 ListIterator<std::pair<node, T>> it = closestTerminals[currentNode].begin();
1205 while (it.succ() != closestTerminals[currentNode].end() && (*it.succ()).second < newDist) {
1206 ++it;
1207 }
1208 closestTerminals[currentNode].insertAfter(std::make_pair(sourceTerminal, newDist), it);
1209 };
1210
1211 while (!queue.empty()) {
1212 NodePair minDistPair = queue.topElement();
1213 queue.pop();
1214 node currentNode = minDistPair.source;
1215 node sourceTerminal = minDistPair.target;
1216
1217 T currentDist = getCurrentDist(currentNode, sourceTerminal);
1218 if (currentDist == -1) { // source terminal not found
1219 continue; // check if the current path needs to be expanded
1220 }
1221
1222 for (adjEntry adj : currentNode->adjEntries) {
1223 edge e = adj->theEdge();
1224 node adjacentNode = e->opposite(currentNode);
1225
1226 if (m_copyIsTerminal[adjacentNode]) {
1227 continue;
1228 }
1229
1230 T possibleNewDistance = currentDist + m_copyGraph.weight(e);
1231
1232 // try to insert the new path from sourceTerminal to adjacentNode
1233 T currentDistToAdjacentNode = getCurrentDist(adjacentNode, sourceTerminal);
1234 if (currentDistToAdjacentNode
1235 != -1) { // there is already one path from sourceTerminal to adjNode
1236 if (possibleNewDistance < currentDistToAdjacentNode) {
1237 queue.decrease(qpos[NodePair(adjacentNode, sourceTerminal)], possibleNewDistance);
1238 setNewDist(adjacentNode, sourceTerminal, possibleNewDistance);
1239 }
1240 } else {
1241 if (closestTerminals[adjacentNode].size() < k
1242 || closestTerminals[adjacentNode].back().second > possibleNewDistance) {
1243 qpos[NodePair(adjacentNode, sourceTerminal)] =
1244 queue.push(NodePair(adjacentNode, sourceTerminal), possibleNewDistance);
1245 setNewDist(adjacentNode, sourceTerminal, possibleNewDistance);
1246 }
1247 }
1248 }
1249 }
1250}
1251
1252template<typename T>
1254 OGDF_ASSERT(!m_copyGraph.empty());
1255 OGDF_ASSERT(isConnected(m_copyGraph));
1256 bool changed = false;
1257
1260 m_copyTerminals);
1261
1262 NodeArray<List<std::pair<node, T>>> closestTerminals;
1263 computeClosestKTerminals(k, closestTerminals);
1264
1266
1267 for (edge e = m_copyGraph.firstEdge(), nextE; e; e = nextE) {
1268 nextE = e->succ();
1269
1270 T bottleneckDistance = computeBottleneckDistance(e->source(), e->target(), tprime,
1271 tprimeHPD, closestTerminals);
1272
1273 if (m_eps.greater(m_copyGraph.weight(e), bottleneckDistance)) {
1274 m_copyGraph.delEdge(e);
1275 changed = true;
1276 }
1277 }
1278
1279 return changed;
1280}
1281
1282template<typename T>
1283bool SteinerTreePreprocessing<T>::NTDkTest(const int maxTestedDegree, const int k) {
1284 OGDF_ASSERT(!m_copyGraph.empty());
1285 if (m_copyTerminals.size() <= 2) {
1286 return false;
1287 }
1288
1289 bool changed = false;
1290 OGDF_ASSERT(isSimpleUndirected(m_copyGraph));
1291 OGDF_ASSERT(isConnected(m_copyGraph));
1292
1295 m_copyTerminals);
1296
1297 NodeArray<List<std::pair<node, T>>> closestTerminals;
1298 computeClosestKTerminals(k, closestTerminals);
1299
1301
1302 for (node v = m_copyGraph.firstNode(), nextV; v; v = nextV) {
1303 nextV = v->succ();
1304
1305 if (m_copyIsTerminal[v]) {
1306 continue;
1307 }
1308 if (v->degree() <= 2 || v->degree() > maxTestedDegree) {
1309 continue;
1310 }
1311
1312 // collect neighbors
1313 List<adjEntry> outgoingAdjs;
1314 for (adjEntry adj : v->adjEntries) {
1315 outgoingAdjs.pushBack(adj);
1316 }
1317 SubsetEnumerator<adjEntry> neighborSubset(outgoingAdjs);
1318
1319 bool deleteNode = true;
1320 for (neighborSubset.begin(3, v->degree()); neighborSubset.valid() && deleteNode;
1321 neighborSubset.next()) {
1322 Graph auxGraph;
1323 std::unordered_map<node, node> initNodeToAuxGraph;
1324 std::unordered_map<node, node> auxGraphToInitNode;
1325
1326 T sumToSelectedAdjacentNodes = 0;
1327
1328 for (int i = 0; i < neighborSubset.size(); ++i) {
1329 const adjEntry adj = neighborSubset[i];
1330 const node adjacentNode = adj->twinNode();
1331 sumToSelectedAdjacentNodes += m_copyGraph.weight(adj->theEdge());
1332
1333 OGDF_ASSERT(initNodeToAuxGraph.find(adjacentNode) == initNodeToAuxGraph.end());
1334
1335 node newAuxGraphNode = auxGraph.newNode();
1336 initNodeToAuxGraph[adjacentNode] = newAuxGraphNode;
1337 auxGraphToInitNode[newAuxGraphNode] = adjacentNode;
1338 }
1339
1340 EdgeArray<T> weight(auxGraph, 0);
1341 for (node auxGraphNode1 : auxGraph.nodes) {
1342 for (node auxGraphNode2 = auxGraphNode1->succ(); auxGraphNode2;
1343 auxGraphNode2 = auxGraphNode2->succ()) {
1344 edge e = auxGraph.newEdge(auxGraphNode1, auxGraphNode2);
1345 weight[e] = computeBottleneckDistance(auxGraphToInitNode[auxGraphNode1],
1346 auxGraphToInitNode[auxGraphNode2], tprime, tprimeHPD, closestTerminals);
1347 }
1348 }
1349
1350 // the auxGraph is now created; run MST on it
1351 EdgeArray<bool> isInTree(auxGraph, false);
1352 T mstCost = computeMinST(auxGraph, weight, isInTree);
1353
1354 if (sumToSelectedAdjacentNodes < mstCost) {
1355 deleteNode = false;
1356 }
1357 }
1358
1359 if (deleteNode) {
1360 deleteSteinerDegreeTwoNode(v, tprime, tprimeHPD, closestTerminals);
1361 changed = true;
1362 }
1363 }
1364
1365 return changed;
1366}
1367
1368template<typename T>
1369void SteinerTreePreprocessing<T>::markSuccessors(node currentNode, const Voronoi<T>& voronoiRegions,
1370 NodeArray<bool>& isSuccessorOfMinCostEdge) const {
1371 isSuccessorOfMinCostEdge[currentNode] = true;
1372
1373 OGDF_ASSERT(voronoiRegions.seed(currentNode) != currentNode);
1374
1375 // recurse for every successor
1376 for (adjEntry adj : currentNode->adjEntries) {
1377 edge e = adj->theEdge();
1378 node adjacentNode = e->opposite(currentNode);
1379
1380 if (voronoiRegions.predecessor(adjacentNode) == currentNode) {
1381 markSuccessors(adjacentNode, voronoiRegions, isSuccessorOfMinCostEdge);
1382 }
1383 }
1384}
1385
1386template<typename T>
1388 /* This is a simple problem, however, there is a weird case that can occur
1389 * in nearestVertexTest(): Imagine a triangle of three terminals where the three edges
1390 * have all equal costs. It can happen that each terminal chooses its first edge to
1391 * be unique, so in the end we add all three edges of the triangle except only two of it.
1392 * We tackle this problem at *this* stage by choosing the edge with smallest index
1393 * in case of a tie in the costs. It is sufficient to do that for the first only
1394 * because this is the edge that will be put into the solution. */
1395 for (adjEntry adj : v->adjEntries) {
1396 edge e = adj->theEdge();
1397 if (first == nullptr // not set or
1398 || m_eps.less(m_copyGraph.weight(e), m_copyGraph.weight(first)) // smaller cost or
1399 || (m_eps.equal(m_copyGraph.weight(e), m_copyGraph.weight(first)) // cost tie but...
1400 && e->index() < first->index())) { // cost tie but smaller index
1401 second = first;
1402 first = e;
1403 } else {
1404 if (second == nullptr || m_eps.less(m_copyGraph.weight(e), m_copyGraph.weight(second))) {
1405 second = e;
1406 }
1407 }
1408 }
1409 OGDF_ASSERT(first != second);
1410}
1411
1412template<typename T>
1414 if (edgesToBeAddedInSolution.empty()) {
1415 return false;
1416 }
1417 for (edge e : edgesToBeAddedInSolution) {
1418 node x = e->source(), y = e->target();
1419 m_sonsList.emplace_back(std::vector<int> {
1420 m_nodeSonsListIndex[x], m_nodeSonsListIndex[y], m_edgeSonsListIndex[e]});
1421 m_costAlreadyInserted += m_copyGraph.weight(e);
1422 node newNode = m_copyGraph.contract(e);
1423 m_nodeSonsListIndex[newNode] = (int)m_sonsList.size() - 1;
1424 m_copyIsTerminal[newNode] = true;
1425 }
1426
1427 recomputeTerminalsList();
1428 return true;
1429}
1430
1431template<typename T>
1433 OGDF_ASSERT(!m_copyGraph.empty());
1434 OGDF_ASSERT(isLoopFree(m_copyGraph));
1435 OGDF_ASSERT(isConnected(m_copyGraph));
1436
1437 Voronoi<T> voronoiRegions(m_copyGraph, m_copyGraph.edgeWeights(), m_copyTerminals);
1438
1439 NodeArray<edge> minCostIncidentEdge1(m_copyGraph, nullptr);
1440 NodeArray<edge> minCostIncidentEdge2(m_copyGraph, nullptr);
1441
1442 // find the first and second minimum-cost edges incident to terminals
1443 for (node terminal : m_copyTerminals) {
1444 if (terminal->degree() >= 2) {
1445 findTwoMinimumCostEdges(terminal, minCostIncidentEdge1[terminal],
1446 minCostIncidentEdge2[terminal]);
1447 }
1448 }
1449
1450 // for each Voronoi region, mark successors (recursively) of the minimum-cost edge
1451 NodeArray<bool> isSuccessorOfMinCostEdge(m_copyGraph, false);
1452 for (node terminal : m_copyTerminals) {
1453 if (terminal->degree() >= 2) {
1454 node closestNode = minCostIncidentEdge1[terminal]->opposite(terminal);
1455
1456 if (voronoiRegions.seed(closestNode) == terminal) {
1457 markSuccessors(closestNode, voronoiRegions, isSuccessorOfMinCostEdge);
1458 }
1459 }
1460 }
1461
1462 // compute for every terminal the distance to the closest terminal
1463 NodeArray<T> distanceToClosestTerminal(m_copyGraph, std::numeric_limits<T>::max());
1464 for (edge e : m_copyGraph.edges) {
1465 node x = e->source(), y = e->target();
1466 node seedX = voronoiRegions.seed(x), seedY = voronoiRegions.seed(y);
1467 if (seedX != seedY) {
1468 // update distanceToClosestTerminal for seed(x)
1469 T distanceThroughE =
1470 voronoiRegions.distance(x) + m_copyGraph.weight(e) + voronoiRegions.distance(y);
1471
1472 if (isSuccessorOfMinCostEdge[x]) {
1473 Math::updateMin(distanceToClosestTerminal[seedX], distanceThroughE);
1474 }
1475 if (isSuccessorOfMinCostEdge[y]) {
1476 Math::updateMin(distanceToClosestTerminal[seedY], distanceThroughE);
1477 }
1478 }
1479 }
1480
1481 // see what edges can be added in solution (Observation 3.2)
1482 List<edge> edgesToBeAddedInSolution;
1483 EdgeArray<bool> willBeAddedInSolution(m_copyGraph, false);
1484 for (node terminal : m_copyTerminals) {
1485 if (terminal->degree() >= 2) {
1486 const edge e = minCostIncidentEdge1[terminal];
1487 const node closestAdjacentNode = e->opposite(terminal);
1488 T distance {voronoiRegions.seed(closestAdjacentNode) == terminal
1489 ? distanceToClosestTerminal[terminal]
1490 : m_copyGraph.weight(e) + voronoiRegions.distance(closestAdjacentNode)};
1491
1492 if (m_eps.geq(m_copyGraph.weight(minCostIncidentEdge2[terminal]), distance)
1493 && !willBeAddedInSolution[e]) {
1494 edgesToBeAddedInSolution.pushBack(e);
1495 willBeAddedInSolution[e] = true;
1496 }
1497 }
1498 }
1499
1500 return addEdgesToSolution(edgesToBeAddedInSolution);
1501}
1502
1503template<typename T>
1505 OGDF_ASSERT(!m_copyGraph.empty());
1506 OGDF_ASSERT(isConnected(m_copyGraph));
1507
1508 Voronoi<T> voronoiRegions(m_copyGraph, m_copyGraph.edgeWeights(), m_copyTerminals);
1509
1510 NodeArray<edge> minCostLeavingRegionEdge1(m_copyGraph, nullptr);
1511 NodeArray<edge> minCostLeavingRegionEdge2(m_copyGraph, nullptr);
1512
1513 // populate minCostLeavingRegions{1,2}
1514 for (edge e : m_copyGraph.edges) {
1515 auto updateMinCostLeavingRegionsFor = [&](node seed) {
1516 if (minCostLeavingRegionEdge1[seed] == nullptr
1517 || m_copyGraph.weight(minCostLeavingRegionEdge1[seed]) > m_copyGraph.weight(e)) {
1518 minCostLeavingRegionEdge2[seed] = minCostLeavingRegionEdge1[seed];
1519 minCostLeavingRegionEdge1[seed] = e;
1520 } else if (minCostLeavingRegionEdge2[seed] == nullptr
1521 || m_copyGraph.weight(minCostLeavingRegionEdge2[seed]) > m_copyGraph.weight(e)) {
1522 minCostLeavingRegionEdge2[seed] = e;
1523 }
1524 };
1525 const node x = e->source(), y = e->target();
1526 const node seedX = voronoiRegions.seed(x), seedY = voronoiRegions.seed(y);
1527 if (seedX != seedY) { // e is a link between Voronoi regions
1528 // update minCostLeavingRegions for both x and y
1529 updateMinCostLeavingRegionsFor(seedX);
1530 updateMinCostLeavingRegionsFor(seedY);
1531 }
1532 }
1533
1534 List<edge> edgesToBeAddedInSolution;
1535 EdgeArray<bool> willBeAddedInSolution(m_copyGraph, false);
1536 for (node terminal : m_copyTerminals) {
1537 if (minCostLeavingRegionEdge2[terminal] == nullptr) {
1538 continue;
1539 }
1540 // XXX: hm, is that smart? If there is no second shortest edge, then the first shortest edge belongs to the solution, doesn't it?
1541
1542 const edge e1 = minCostLeavingRegionEdge1[terminal];
1543 const node x = e1->source(), y = e1->target();
1544 if (m_eps.geq(m_copyGraph.weight(minCostLeavingRegionEdge2[terminal]),
1545 voronoiRegions.distance(x) + m_copyGraph.weight(e1) + voronoiRegions.distance(y))
1546 && !willBeAddedInSolution[e1]) {
1547 edgesToBeAddedInSolution.pushBack(e1);
1548 willBeAddedInSolution[e1] = true;
1549 }
1550 }
1551
1552 return addEdgesToSolution(edgesToBeAddedInSolution);
1553}
1554
1555template<typename T>
1557 OGDF_ASSERT(m_copyTerminals.size() > 1);
1558
1559 // compute the Voronoi regions
1560 Voronoi<T> voronoiRegions(m_copyGraph, m_copyGraph.edgeWeights(), m_copyTerminals);
1561
1562 // compute the radius for each terminal
1563 terminalRadius.init(m_copyGraph, std::numeric_limits<T>::max());
1564 for (node v : m_copyGraph.nodes) {
1565 node seedV = voronoiRegions.seed(v);
1566 T distanceToSeedV = voronoiRegions.distance(v);
1567
1568 for (adjEntry adj : v->adjEntries) {
1569 edge e = adj->theEdge();
1570 node adjacentNode = e->opposite(v);
1571
1572 if (voronoiRegions.seed(adjacentNode) != seedV) {
1573 Math::updateMin(terminalRadius[seedV], distanceToSeedV + m_copyGraph.weight(e));
1574 }
1575 }
1576 }
1577}
1578
1579template<typename T>
1581 const T upperBound) {
1582 bool changed = false;
1583 for (node v = m_copyGraph.firstNode(), nextV; v; v = nextV) {
1584 nextV = v->succ();
1585 if (m_eps.greater(lowerBoundWithNode[v], upperBound)) {
1586 OGDF_ASSERT(!m_copyIsTerminal[v]);
1587 m_copyGraph.delNode(v);
1588 changed = true;
1589 }
1590 }
1591 return changed;
1592}
1593
1594template<typename T>
1596 OGDF_ASSERT(!m_copyGraph.empty());
1597 if (m_copyTerminals.size() <= 1) {
1598 return false;
1599 }
1600
1601 OGDF_ASSERT(isConnected(m_copyGraph));
1602
1603 NodeArray<T> lowerBoundWithNode(m_copyGraph, std::numeric_limits<T>::lowest());
1604
1605 NodeArray<List<std::pair<node, T>>> closestTerminals;
1606 computeClosestKTerminals(3, closestTerminals);
1607
1608 // Update the lowerbound of the cost of a Steiner tree containing one particular node
1609 // as explained in [PV01, page 278, Observation 3.5]
1610 const T radiusSum = computeRadiusSum();
1611 for (node v : m_copyGraph.nodes) {
1612 if (m_copyIsTerminal[v]) {
1613 continue;
1614 }
1615
1616 if (closestTerminals[v].size() < 2) {
1617 lowerBoundWithNode[v] = std::numeric_limits<T>::max();
1618 continue;
1619 }
1620
1621 std::pair<node, T> closestTerminalPair1 = *(closestTerminals[v].get(0)),
1622 closestTerminalPair2 = *(closestTerminals[v].get(1));
1623 T distanceToClosestTerminal1 = closestTerminalPair1.second,
1624 distanceToClosestTerminal2 = closestTerminalPair2.second;
1625
1626 Math::updateMax(lowerBoundWithNode[v],
1627 distanceToClosestTerminal1 + distanceToClosestTerminal2 + radiusSum);
1628 }
1629
1630 // Compute lower bound for edges, see [PV01, page 278, Observation 3.5]
1631 EdgeArray<T> lowerBoundWithEdge(m_copyGraph, 0);
1632 for (edge e : m_copyGraph.edges) {
1633 node x = e->source(), y = e->target();
1634
1635 T distanceToClosestTerminalX = (*closestTerminals[x].begin()).second;
1636 T distanceToClosestTerminalY = (*closestTerminals[y].begin()).second;
1637
1638 Math::updateMax(lowerBoundWithEdge[e],
1639 m_copyGraph.weight(e) + distanceToClosestTerminalX + distanceToClosestTerminalY
1640 + radiusSum);
1641 }
1642
1643 // Update the lowerbound of the cost of a Steiner tree containing one particular node
1644 // as explained in [PV01, pages 279-280, Observation 3.8]
1645 Graph auxiliaryGraph;
1646 NodeArray<node> terminalInAuxiliaryGraph(m_copyGraph, nullptr);
1647 for (node terminal : m_copyTerminals) {
1648 node newAuxiliaryNode = auxiliaryGraph.newNode();
1649 terminalInAuxiliaryGraph[terminal] = newAuxiliaryNode;
1650 }
1651
1652 EdgeArray<T> initialEdgeWeight(m_copyGraph);
1653 for (edge e : m_copyGraph.edges) {
1654 initialEdgeWeight[e] = m_copyGraph.weight(e);
1655 }
1656 Voronoi<T> voronoiRegions(m_copyGraph, initialEdgeWeight, m_copyTerminals);
1657
1660 edgeBetweenNodes;
1661 EdgeArray<T> edgeWeight(auxiliaryGraph, std::numeric_limits<T>::max());
1662 for (edge e : m_copyGraph.edges) {
1663 node x = e->source(), y = e->target();
1664 node seedX = voronoiRegions.seed(x), seedY = voronoiRegions.seed(y);
1665 if (seedX == seedY) {
1666 continue;
1667 }
1668
1669 auto pair = NodePair(terminalInAuxiliaryGraph[seedX], terminalInAuxiliaryGraph[seedY]);
1670 if (edgeBetweenNodes.find(pair) == edgeBetweenNodes.end()) {
1671 edgeBetweenNodes[pair] = auxiliaryGraph.newEdge(terminalInAuxiliaryGraph[seedX],
1672 terminalInAuxiliaryGraph[seedY]);
1673 }
1674 edge auxiliaryEdge = edgeBetweenNodes[pair];
1675 Math::updateMin(edgeWeight[auxiliaryEdge],
1676 min(voronoiRegions.distance(x), voronoiRegions.distance(y)) + m_copyGraph.weight(e));
1677 }
1678
1679 EdgeArray<bool> isInTree(auxiliaryGraph, false);
1680 T minimumSpanningTreeCost = computeMinST(auxiliaryGraph, edgeWeight, isInTree);
1681 T longestEdgeCost = std::numeric_limits<T>::lowest();
1682 for (edge e : auxiliaryGraph.edges) {
1683 if (isInTree[e]) {
1684 Math::updateMax(longestEdgeCost, edgeWeight[e]);
1685 }
1686 }
1687
1688 for (node v : m_copyGraph.nodes) {
1689 if (m_copyIsTerminal[v] || closestTerminals[v].size() < 2) {
1690 continue;
1691 }
1692
1693 std::pair<node, T> closestTerminalPair1 = *(closestTerminals[v].get(0)),
1694 closestTerminalPair2 = *(closestTerminals[v].get(1));
1695 T distanceToClosestTerminal1 = closestTerminalPair1.second,
1696 distanceToClosestTerminal2 = closestTerminalPair2.second;
1697
1698 Math::updateMax(lowerBoundWithNode[v],
1699 minimumSpanningTreeCost - longestEdgeCost + distanceToClosestTerminal1
1700 + distanceToClosestTerminal2);
1701 }
1702
1703 return deleteNodesAboveUpperBound(lowerBoundWithNode, upperBound)
1704 || deleteEdgesAboveUpperBound(lowerBoundWithEdge, upperBound);
1705}
1706
1707template<typename T>
1708bool SteinerTreePreprocessing<T>::dualAscentBasedTest(int repetitions, T upperBound) {
1709 OGDF_ASSERT(!m_copyGraph.empty());
1710 bool changed = false;
1711 if (m_copyTerminals.size() > 1) {
1712 NodeArray<T> lowerBoundNodes;
1713 EdgeArray<T> lowerBoundEdges;
1715 alg.setRepetitions(repetitions);
1716 alg.call(m_copyGraph, m_copyTerminals, lowerBoundNodes, lowerBoundEdges);
1717
1718 changed |= deleteNodesAboveUpperBound(lowerBoundNodes, upperBound);
1719 changed |= deleteEdgesAboveUpperBound(lowerBoundEdges, upperBound);
1720 }
1721 return changed;
1722}
1723
1724template<typename T>
1726 const T upperBound) {
1727 bool changed = false;
1728 for (edge e = m_copyGraph.firstEdge(), nextE; e; e = nextE) {
1729 nextE = e->succ();
1730 if (m_eps.greater(lowerBoundWithEdge[e], upperBound)) {
1731 m_copyGraph.delEdge(e);
1732 changed = true;
1733 }
1734 }
1735 return changed;
1736}
1737
1738template<typename T>
1740 NodeArray<T> terminalRadius;
1741 computeRadiusOfTerminals(terminalRadius);
1742
1743 // instead of sorting, we can simply ignore the two largest radii
1744 T radiusSum = T();
1745 T largestRadius1 = std::numeric_limits<T>::lowest(),
1746 largestRadius2 = std::numeric_limits<T>::lowest();
1747 for (node terminal : m_copyTerminals) {
1748 radiusSum += terminalRadius[terminal];
1749
1750 if (terminalRadius[terminal] > largestRadius1) {
1751 largestRadius2 = largestRadius1;
1752 largestRadius1 = terminalRadius[terminal];
1753 } else {
1754 if (terminalRadius[terminal] > largestRadius2) {
1755 largestRadius2 = terminalRadius[terminal];
1756 }
1757 }
1758 }
1759 radiusSum -= largestRadius1 + largestRadius2;
1760 return radiusSum;
1761}
1762
1763template<typename T>
1764bool SteinerTreePreprocessing<T>::reachabilityTest(int maxDegreeTest, const int k) {
1765 OGDF_ASSERT(!m_copyGraph.empty());
1766 OGDF_ASSERT(isSimpleUndirected(m_copyGraph));
1767 OGDF_ASSERT(isConnected(m_copyGraph));
1768 bool changed = false;
1769 if (maxDegreeTest <= 0) {
1770 maxDegreeTest = m_copyGraph.numberOfNodes();
1771 }
1772
1773 EdgeWeightedGraphCopy<T>* approximatedSteinerTree;
1774 T upperBoundCost = computeMinSteinerTreeUpperBound(approximatedSteinerTree);
1775
1776 NodeArray<bool> isInUpperBoundTree(m_copyGraph, false);
1777 for (node v : approximatedSteinerTree->nodes) {
1778 isInUpperBoundTree[approximatedSteinerTree->original(v)] = true;
1779 }
1780 delete approximatedSteinerTree;
1781
1782 // Initialize tprime and its hpd decomposition used for partially not adding useless edges during nodes' deletion
1785 m_copyTerminals);
1786 OGDF_ASSERT(!tprime.empty());
1787
1788 NodeArray<List<std::pair<node, T>>> closestTerminals;
1789 computeClosestKTerminals(k, closestTerminals);
1790
1792
1793 // check which nodes can be deleted
1794 for (node v = m_copyGraph.firstNode(), nextV; v; v = nextV) {
1795 nextV = v->succ();
1796
1797 if (isInUpperBoundTree[v] || v->degree() > maxDegreeTest) {
1798 continue;
1799 }
1800
1801 // compute v's farthest and closest terminals
1802 Dijkstra<T> dijkstra;
1803
1804 NodeArray<T> distance(m_copyGraph);
1805 NodeArray<edge> predecessor(m_copyGraph, nullptr);
1806 dijkstra.call(m_copyGraph, m_copyGraph.edgeWeights(), v, predecessor, distance);
1807
1808 // compute first, second nearest terminals and farthest terminal
1809 node farthestTerminal = nullptr;
1810 T distanceToFarthestTerminal(0);
1811 T distanceToClosestTerminal1 = std::numeric_limits<T>::max(),
1812 distanceToClosestTerminal2 = std::numeric_limits<T>::max();
1813 for (node terminal : m_copyTerminals) {
1814 if (distanceToFarthestTerminal < distance[terminal]) {
1815 farthestTerminal = terminal;
1816 distanceToFarthestTerminal = distance[terminal];
1817 }
1818
1819 if (distanceToClosestTerminal1 > distance[terminal]) {
1820 distanceToClosestTerminal2 = distanceToClosestTerminal1;
1821 distanceToClosestTerminal1 = distance[terminal];
1822 } else {
1823 if (distanceToClosestTerminal2 > distance[terminal]) {
1824 distanceToClosestTerminal2 = distance[terminal];
1825 }
1826 }
1827 }
1828
1829 if (predecessor[farthestTerminal] == nullptr // is not in the same component with the terminals
1830 || distanceToClosestTerminal2
1831 == std::numeric_limits<T>::max() // cannot reach at least 2 terminals, must be deleted
1832 || m_eps.geq(distanceToFarthestTerminal + distanceToClosestTerminal1
1833 + distanceToClosestTerminal2,
1834 upperBoundCost)) {
1835 changed = true;
1836 // delete the node
1837 if (predecessor[farthestTerminal] != nullptr
1838 && distanceToClosestTerminal2 != std::numeric_limits<T>::max()
1839 && m_eps.less(distanceToFarthestTerminal + distanceToClosestTerminal1,
1840 upperBoundCost)) {
1841 // the deleted node has degree 2 -> replace it with edges
1842 deleteSteinerDegreeTwoNode(v, tprime, tprimeHPD, closestTerminals);
1843 } else {
1844 // just delete the node
1845 m_copyGraph.delNode(v);
1846 }
1847 }
1848 }
1849
1850 return changed;
1851}
1852
1853template<typename T>
1854template<typename LAMBDA>
1856 node& optimalTerminal1, node& optimalTerminal2, NodeArray<T>& distance) const {
1857 // run Dijkstra starting from v
1858 Dijkstra<T> dijkstra;
1859
1860 distance.init(m_copyGraph);
1861 NodeArray<edge> predecessor(m_copyGraph, nullptr);
1862 dijkstra.call(m_copyGraph, m_copyGraph.edgeWeights(), v, predecessor, distance);
1863
1864 for (node terminal : m_copyTerminals) {
1865 if (predecessor[terminal] == nullptr) {
1866 continue;
1867 }
1868
1869 if (optimalTerminal1 == nullptr
1870 || dist(optimalTerminal1, distance) > dist(terminal, distance)) {
1871 optimalTerminal2 = optimalTerminal1;
1872 optimalTerminal1 = terminal;
1873 } else {
1874 if (optimalTerminal2 == nullptr
1875 || dist(optimalTerminal2, distance) > dist(terminal, distance)) {
1876 optimalTerminal2 = terminal;
1877 }
1878 }
1879 }
1880 OGDF_ASSERT(optimalTerminal1 != nullptr);
1881 OGDF_ASSERT(optimalTerminal2 != nullptr);
1882 OGDF_ASSERT(optimalTerminal1 != optimalTerminal2);
1883}
1884
1885template<typename T>
1887 OGDF_ASSERT(!m_copyGraph.empty());
1888 if (m_copyTerminals.size() <= 2) {
1889 return false;
1890 }
1891
1892 // get the upper bound
1893 EdgeWeightedGraphCopy<T>* approximatedSteinerTree;
1894 const T upperBoundCost = computeMinSteinerTreeUpperBound(approximatedSteinerTree);
1895
1896 NodeArray<bool> isInUpperBoundTree(m_copyGraph, false);
1897 for (node v : approximatedSteinerTree->nodes) {
1898 isInUpperBoundTree[approximatedSteinerTree->original(v)] = true;
1899 }
1900 delete approximatedSteinerTree;
1901
1902 T cK = T();
1903 NodeArray<T> minCostOfAdjacentEdge(m_copyGraph, std::numeric_limits<T>::max());
1904 for (node terminal : m_copyTerminals) {
1905 for (adjEntry adj : terminal->adjEntries) {
1906 Math::updateMin(minCostOfAdjacentEdge[terminal], m_copyGraph.weight(adj->theEdge()));
1907 }
1908 cK += minCostOfAdjacentEdge[terminal];
1909 }
1910 auto dist = [&minCostOfAdjacentEdge](node terminal, const NodeArray<T>& distance) {
1911 return distance[terminal] - minCostOfAdjacentEdge[terminal];
1912 };
1913
1914 List<node> delNodes;
1915 std::set<edge> delEdges;
1916 NodeArray<T> vDistance;
1917 NodeArray<T> wDistance;
1918 for (node v = m_copyGraph.firstNode(), nextV; v; v = nextV) {
1919 nextV = v->succ();
1920
1921 if (isInUpperBoundTree[v]) {
1922 continue;
1923 }
1924
1925 // compute its optimal terminals
1926 node vOptimalTerminal1 = nullptr, vOptimalTerminal2 = nullptr;
1927 computeOptimalTerminals(v, dist, vOptimalTerminal1, vOptimalTerminal2, vDistance);
1928
1929 // check whether it can be deleted
1930 if (m_eps.geq(cK + dist(vOptimalTerminal1, vDistance) + dist(vOptimalTerminal2, vDistance),
1931 upperBoundCost)) {
1932 delNodes.pushBack(v);
1933 } else { // it is not deleted, perform the edge test
1934 for (adjEntry adj = v->firstAdj(), adjNext; adj; adj = adjNext) {
1935 adjNext = adj->succ();
1936 node w = adj->twinNode();
1937 if (m_copyIsTerminal[w]) {
1938 continue;
1939 }
1940 node wOptimalTerminal1 = nullptr, wOptimalTerminal2 = nullptr;
1941 computeOptimalTerminals(w, dist, wOptimalTerminal1, wOptimalTerminal2, wDistance);
1942
1943 node vOptimalTerminal = vOptimalTerminal1;
1944 node wOptimalTerminal = wOptimalTerminal1;
1945 if (vOptimalTerminal == wOptimalTerminal) {
1946 // the nearest terminals to v and w are the same, but they have to be
1947 // different. Obtain the minimum choice such that they are different.
1948 if (m_eps.leq(dist(vOptimalTerminal1, vDistance)
1949 + dist(wOptimalTerminal2, wDistance),
1950 dist(vOptimalTerminal2, vDistance)
1951 + dist(wOptimalTerminal1, wDistance))) {
1952 wOptimalTerminal = wOptimalTerminal2;
1953 } else {
1954 vOptimalTerminal = vOptimalTerminal2;
1955 }
1956 }
1957 OGDF_ASSERT(vOptimalTerminal != wOptimalTerminal);
1958 if (m_eps.geq(cK + dist(vOptimalTerminal, vDistance) + dist(wOptimalTerminal, wDistance)
1959 + m_copyGraph.weight(adj->theEdge()),
1960 upperBoundCost)) {
1961 delEdges.insert(adj->theEdge());
1962 }
1963 }
1964 }
1965 }
1966
1967 bool changed = false;
1968 for (edge e : delEdges) {
1969 m_copyGraph.delEdge(e);
1970 changed = true;
1971 }
1972 for (node v : delNodes) {
1973 m_copyGraph.delNode(v);
1974 changed = true;
1975 }
1976 return changed;
1977}
1978
1979}
Declaration and implementation of Array class and Array algorithms.
Declaration and implementation of bounded queue class.
Declaration of class EdgeWeightedGraph.
Extends the GraphCopy concept to weighted graphs.
Compare floating point numbers with epsilons and integral numbers with normal compare operators.
Includes declaration of graph class.
Decralation of GraphElement and GraphList classes.
Definition of the ogdf::steiner_tree:HeavyPathDecomposition class template.
Declaration of doubly linked lists and iterators.
Implementation of Mehlhorn's minimum Steiner tree 2(1-1/l)-approximation algorithm.
Implementation of the 2(1-1/l)-approximation algorithm for the minimum Steiner tree problem by Matsuy...
Priority queue interface wrapping various heaps.
Definition of the ogdf::SteinerTreeLowerBoundDualAscent class template.
A class that allows to enumerate k-subsets.
Definition of ogdf::Voronoi class template.
Mathematical Helpers.
Basic declarations, included by all source files.
Class for adjacency list elements.
Definition Graph_d.h:143
adjEntry succ() const
Returns the successor in the adjacency list.
Definition Graph_d.h:219
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
The parameterized class Array implements dynamic arrays of type E.
Definition Array.h:219
The parameterized class BoundedQueue implements queues with bounded size.
bool empty()
Returns true iff the queue is empty.
void append(const E &x)
Adds x at the end of queue.
const E & top() const
Returns front element.
E pop()
Removes front element and returns it.
Dijkstra's single source shortest path algorithm.
Definition Dijkstra.h:60
void call(const Graph &G, const EdgeArray< T > &weight, const List< node > &sources, NodeArray< edge > &predecessor, NodeArray< T > &distance, bool directed=false, bool arcsReversed=false, node target=nullptr, T maxLength=std::numeric_limits< T >::max())
Calculates, based on the graph G with corresponding edge costs and source nodes, the shortest paths a...
Definition Dijkstra.h:253
Class for the representation of edges.
Definition Graph_d.h:364
int index() const
Returns the index of the edge.
Definition Graph_d.h:396
node opposite(node v) const
Returns the adjacent node different from v.
Definition Graph_d.h:414
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
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.
T weight(const edge e) const
edge newEdge(node v, node w, T weight)
const EdgeArray< T > & edgeWeights() const
void setWeight(const edge e, T weight)
std::enable_if< std::is_integral< T >::value, bool >::type leq(const T &x, const T &y) const
Compare if x is LEQ than y for integral types.
Definition EpsilonTest.h:81
std::enable_if< std::is_integral< T >::value, bool >::type geq(const T &x, const T &y) const
Compare if x is GEQ to y for integral types.
std::enable_if< std::is_integral< T >::value, bool >::type less(const T &x, const T &y) const
Compare if x is LESS than y for integral types.
Definition EpsilonTest.h:55
std::enable_if< std::is_integral< T >::value, bool >::type equal(const T &x, const T &y) const
Compare if x is EQUAL to y for integral types.
std::enable_if< std::is_integral< T >::value, bool >::type greater(const T &x, const T &y) const
Compare if x is GREATER than y for integral types.
node newNode(node vOrig)
Creates a new node in the graph copy with original node vOrig.
Definition GraphCopy.h:191
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
Data type for general directed graphs (adjacency list representation).
Definition Graph_d.h:866
node contract(edge e, bool keepSelfLoops=false)
Contracts edge e while preserving the order of adjacency entries.
int numberOfNodes() const
Returns the number of nodes in the graph.
Definition Graph_d.h:979
edge firstEdge() const
Returns the first edge in the list of all edges.
Definition Graph_d.h:1000
virtual void delNode(node v)
Removes node v and all incident edges from the graph.
int numberOfEdges() const
Returns the number of edges in the graph.
Definition Graph_d.h:982
internal::GraphObjectContainer< NodeElement > nodes
The container containing all node objects.
Definition Graph_d.h:929
virtual void delEdge(edge e)
Removes edge e from the graph.
node newNode(int index=-1)
Creates a new node and returns it.
Definition Graph_d.h:1061
node firstNode() const
Returns the first node in the list of all nodes.
Definition Graph_d.h:994
edge newEdge(node v, node w, int index=-1)
Creates a new edge (v,w) and returns it.
Definition Graph_d.h:1080
bool empty() const
Returns true iff the graph is empty, i.e., contains no nodes.
Definition Graph_d.h:976
edge searchEdge(node v, node w, bool directed=false) const
Searches and returns an edge connecting nodes v and w in time O( min(deg(v ), deg(w ))).
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
int size() const
Returns the number of elements in the list.
Definition List.h:1488
void clear()
Removes all elements from the list.
Definition List.h:1626
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
iterator pushFront(const E &x)
Adds element x at the beginning of the list.
Definition List.h:1534
void popBack()
Removes the last element from the list.
Definition List.h:1598
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
const_reference front() const
Returns a const reference to the first element.
Definition List.h:305
bool empty() const
Returns true iff the list is empty.
Definition List.h:286
ListConstIterator< E > search(const E &e) const
Scans the list for the specified element and returns an iterator to the first occurrence in the list,...
Definition List.h:1280
Serves as an interface for various methods to compute or approximate minimum Steiner trees on undirec...
virtual T call(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, EdgeWeightedGraphCopy< T > *&finalSteinerTree)
Calls the Steiner tree algorithm for nontrivial cases but handles trivial cases directly.
This class implements the minimum Steiner tree 2-approximation algorithm by Takahashi and Matsuyama w...
Class for the representation of nodes.
Definition Graph_d.h:241
int degree() const
Returns the degree of the node (indegree + outdegree).
Definition Graph_d.h:284
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
Definition Graph_d.h:272
node succ() const
Returns the successor in the list of all nodes.
Definition Graph_d.h:293
int index() const
Returns the (unique) node index.
Definition Graph_d.h:275
adjEntry firstAdj() const
Returns the first entry in the adjaceny list.
Definition Graph_d.h:287
adjEntry lastAdj() const
Returns the last entry in the adjacency list.
Definition Graph_d.h:290
Prioritized queue interface wrapper for heaps.
bool empty() const
Checks whether the queue is empty.
void init(const Base *base=nullptr)
Reinitializes the array. Associates the array with the matching registry of base.
Implementation of a dual-ascent-based lower bound heuristic for Steiner tree problems.
T call(const EdgeWeightedGraph< T > &graph, const List< node > &terminals)
Calls the algorithm and returns the lower bound.
void setRepetitions(int num)
Sets the number of repeated calls to the lower bound algorithm (runs with different roots)
This class implements preprocessing strategies for the Steiner tree problem.
T computeMinSteinerTreeUpperBound(EdgeWeightedGraphCopy< T > *&finalSteinerTree) const
bool degree2Test()
deletes degree-2 nodes and replaces them with one edge with the adjacent edges' sum
bool deleteLeaves()
Deletes the leaves of the graph.
bool reduceFastAndDualAscent()
Apply fast reductions and the dual-ascent-based tests iteratively.
void computeOriginalSolution(const EdgeWeightedGraphCopy< T > &reducedGraphSolution, EdgeWeightedGraphCopy< T > &correspondingOriginalSolution)
Computes the solution for the original graph, given a solution on the reduction.
bool deleteEdgesAboveUpperBound(const EdgeArray< T > &lowerBoundWithEdge, const T upperBound)
Deletes the edges whose lowerBoundWithEdge exceeds upperBound.
bool cutReachabilityTest()
Performs a cut reachability test [DV89].
void computeShortestPathMatrix(NodeArray< NodeArray< T > > &shortestPath) const
Computes the shortest path matrix corresponding to the m_copyGraph.
const EdgeWeightedGraph< T > & getReducedGraph() const
Returns the reduced form of the graph.
bool dualAscentBasedTest(int repetitions=1)
Like dualAscentBasedTest(int repetitions, T upperBound) but the upper bound is computed by computeMin...
bool longEdgesTest()
Long-Edges test from [DV89].
T computeRadiusSum() const
Compute the sum of all radii except the two largest.
bool deleteNodesAboveUpperBound(const NodeArray< T > &lowerBoundWithNode, const T upperBound)
Deletes the nodes whose lowerBoundWithNode exceeds upperBound.
void computeOptimalTerminals(node v, LAMBDA dist, node &optimalTerminal1, node &optimalTerminal2, NodeArray< T > &distance) const
Compute first and second best terminals according to function dist.
bool reduceTrivial()
Apply trivial (hence amazingly fast) reductions iteratively, that is degree2Test(),...
T costEdgesAlreadyInserted() const
Returns the cost of the edges already inserted in solution during the reductions.
void addNew(TWhat x, const std::vector< node > &replacedNodes, const std::vector< edge > &replacedEdges, bool deleteReplacedElements, TWhatArray &whatSonsListIndex)
Update internal data structures to let a (new) node or edge represent replaced nodes and/or edges.
void findTwoMinimumCostEdges(node v, edge &first, edge &second) const
Finds the first and second smallest edges incident to v.
EdgeArray< int > m_edgeSonsListIndex
See m_nodeSonsListIndex but for edges.
bool terminalDistanceTest()
Simple terminal distance test [PV01].
static bool repeat(std::function< bool()> f)
Auxiliary function: Repeats a function until it returns false (used for iteratively applying reductio...
std::vector< std::vector< int > > m_sonsList
List with lists (corresponding to nodes and containing the indexes of their sons)
void markSuccessors(node currentNode, const Voronoi< T > &voronoiRegions, NodeArray< bool > &isSuccessorOfMinCostEdge) const
Mark successors of currentNode in its shortest-path tree in voronoiRegions.
bool lowerBoundBasedTest()
Like lowerBoundBasedTest(T upperBound) but the upper bound is computed by computeMinSteinerTreeUpperB...
void setCostUpperBoundAlgorithm(MinSteinerTreeModule< T > *pMinSteinerTreeModule)
Set the module option for the algorithm used for computing the MinSteinerTree cost upper bound.
void floydWarshall(NodeArray< NodeArray< T > > &shortestPath) const
Applies the Floyd-Warshall algorithm on the m_copyGraph. The shortestPath matrix has to be already in...
T m_costAlreadyInserted
The cost of the already inserted in solution edges.
T solve(MinSteinerTreeModule< T > &mst, EdgeWeightedGraphCopy< T > *&finalSteinerTree)
A shortcut to get the solution of a reduced instance.
List< node > m_copyTerminals
The reduced form of terminals.
T computeBottleneckDistance(node x, node y, const EdgeWeightedGraphCopy< T > &tprime, const steiner_tree::HeavyPathDecomposition< T > &tprimeHPD, const NodeArray< List< std::pair< node, T > > > &closestTerminals) const
Heuristic computation [PV01] of the bottleneck Steiner distance between two nodes in a graph.
void computeRadiusOfTerminals(NodeArray< T > &terminalRadius) const
Compute radius of terminals.
SteinerTreePreprocessing(const EdgeWeightedGraph< T > &wg, const List< node > &terminals, const NodeArray< bool > &isTerminal)
void deleteSteinerDegreeTwoNode(node v, const EdgeWeightedGraphCopy< T > &tprime, const steiner_tree::HeavyPathDecomposition< T > &tprimeHPD, const NodeArray< List< std::pair< node, T > > > &closestTerminals)
Deletes a node that is known to have degree 2 in at least one minimum Steiner tree.
const List< node > & getReducedTerminals() const
Returns the list of the terminals corresponding to the reduced graph.
void recomputeTerminalsList()
Used by reductions to recompute the m_copyTerminals list, according to m_copyIsTerminal; useful when ...
const NodeArray< bool > & m_origIsTerminal
Const reference to the original isTerminal.
NodeArray< int > m_nodeSonsListIndex
It contains for each node an index i.
void shuffleReducedTerminals()
Shuffles the list of reduced terminals. This can have an effect on some tests.
void findClosestNonTerminals(node source, List< node > &reachedNodes, NodeArray< T > &distance, T maxDistance, int expandedEdges) const
Heuristic approach to computing the closest non-terminals for one node, such that there is no termina...
void addNewNode(node v, const std::vector< node > &replacedNodes, const std::vector< edge > &replacedEdges, bool deleteReplacedElements)
Calls addNew() for a node.
void computeClosestKTerminals(const int k, NodeArray< List< std::pair< node, T > > > &closestTerminals) const
Computes for every non-terminal the closest k terminals such that there is no other terminal on the p...
std::unique_ptr< MinSteinerTreeModule< T > > m_costUpperBoundAlgorithm
Algorithm used for computing the upper bound for the cost of a minimum Steiner tree.
bool reduceFast()
Apply fast reductions iteratively (includes trivial reductions).
const EdgeWeightedGraph< T > & m_origGraph
Const reference to the original graph.
bool reachabilityTest(int maxDegreeTest=0, const int k=3)
Performs a reachability test [DV89].
EdgeWeightedGraph< T > m_copyGraph
Copy of the original graph; this copy will actually be reduced.
void addNewEdge(edge e, const std::vector< node > &replacedNodes, const std::vector< edge > &replacedEdges, bool deleteReplacedElements)
The function is called after a new edge is added to the copy graph during the reductions.
NodeArray< bool > m_copyIsTerminal
The reduced form of isTerminal.
bool PTmTest(const int k=3)
"Paths with many terminals" test, efficient on paths with many terminals.
bool makeSimple()
Deletes parallel edges keeping only the minimum cost one, and deletes self-loops.
const List< node > & m_origTerminals
Const reference to the original list of terminals.
bool shortLinksTest()
Short links test using Voronoi regions [PV01].
const NodeArray< bool > & getReducedIsTerminal() const
Returns the NodeArray<bool> isTerminal corresponding to the reduced graph.
void addToSolution(const int listIndex, Array< bool, int > &isInSolution) const
Helper method for computeOriginalSolution.
bool leastCostTest()
Performs a least cost test [DV89] computing the whole shortest path matrix.
bool nearestVertexTest()
Nearest vertex test using Voronoi regions [DV89, PV01].
bool addEdgesToSolution(const List< edge > &edgesToBeAddedInSolution)
The function adds a set of edges in the solution.
bool deleteComponentsWithoutTerminals()
Deletes connected components with no terminals.
bool NTDkTest(const int maxTestedDegree=5, const int k=3)
Non-terminals of degree k test [DV89, PV01].
bool dualAscentBasedTest(int repetitions, T upperBound)
Like lowerBoundBasedTest(T upperBound) but uses ogdf::SteinerTreeLowerBoundDualAscent to compute lowe...
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.
int size() const
Returns the cardinality of the subset.
bool valid() const
Checks if the current subset is valid. If not, the subset is either not initialized or all subsets ha...
void next()
Obtains the next subset if possible. The result should be checked using the valid() method.
Computes Voronoi regions in an edge-weighted graph.
Definition Voronoi.h:49
T distance(node v) const
Returns the distance between v and its Voronoi seed.
Definition Voronoi.h:87
node predecessor(node v) const
Returns the nearest node to v on the shortest path to its Voronoi seed.
Definition Voronoi.h:81
node seed(node v) const
Returns the Voronoi seed of node v.
Definition Voronoi.h:90
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
void decrease(const E &element, const P &priority)
Decreases the priority of the given element.
bool contains(const E &element) const
Returns whether this queue contains that key.
void pop()
Removes the topmost element from the queue.
void push(const E &element, const P &priority)
Adds a new element to the queue.
Defines a queue for handling prioritized elements.
const E & topElement() const
Returns the topmost element in the queue.
An implementation of the heavy path decomposition on trees.
T getBottleneckSteinerDistance(node x, node y) const
computes in the bottleneck distance between terminals x and y
A class used by the unordered_maps inside the reductions.
bool operator()(const NodePair &pair1, const NodePair &pair2) const
A class used by the unordered_maps inside the reductions.
Declaration of extended graph algorithms.
Implementation of Dijkstra's single source shortest path algorithm.
int connectedComponents(const Graph &G, NodeArray< int > &component, List< node > *isolated=nullptr, ArrayBuffer< node > *reprs=nullptr)
Computes the connected components of G and optionally generates a list of isolated nodes.
bool isConnected(const Graph &G)
Returns true iff G is connected.
T computeMinST(const Graph &G, const EdgeArray< T > &weight, EdgeArray< bool > &isInTree)
Computes a minimum spanning tree using Prim's algorithm.
bool isLoopFree(const Graph &G)
Returns true iff G contains no self-loop.
bool isSimpleUndirected(const Graph &G)
Returns true iff G contains neither self-loops nor undirected parallel edges.
EdgeElement * edge
The type of edges.
Definition Graph_d.h:75
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition basic.h:52
void updateMin(T &min, const T &newValue)
Stores the minimum of min and newValue in min.
Definition Math.h:102
void updateMax(T &max, const T &newValue)
Stores the maximum of max and newValue in max.
Definition Math.h:94
T constructTerminalSpanningTreeUsingVoronoiRegions(EdgeWeightedGraphCopy< T > &terminalSpanningTree, const EdgeWeightedGraph< T > &graph, const List< node > &terminals)
The namespace for all OGDF objects.
Declaration of simple graph algorithms.