Loading [MathJax]/extensions/tex2jax.js

Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
SteinerTreePreprocessing.h
Go to the documentation of this file.
1
32#pragma once
33
40
41#include <forward_list>
42#include <memory>
43#include <set>
44#include <unordered_map>
45
46namespace ogdf {
47
48// Helpers:
49namespace steiner_tree {
55public:
56 int operator()(const NodePair& v) const {
57 return static_cast<int>((static_cast<long long>(min(v.source->index(), v.target->index()) + 11)
58 * (max(v.source->index(), v.target->index()) + 73))
59 % 700001);
60 }
61};
62
68public:
69 bool operator()(const NodePair& pair1, const NodePair& pair2) const {
70 return (pair1.source == pair2.source && pair1.target == pair2.target)
71 || (pair1.source == pair2.target && pair1.target == pair2.source);
72 }
73};
74
75}
76
88template<typename T>
90protected:
95
99
101
110 std::vector<std::vector<int>> m_sonsList;
111
113 std::unique_ptr<MinSteinerTreeModule<T>> m_costUpperBoundAlgorithm;
114
115public:
122 const NodeArray<bool>& isTerminal);
123
134 // reductions generate new nodes and edges, which may inflate the internal structure
135 if (m_copyGraph.maxNodeIndex() <= m_copyGraph.numberOfNodes() + 5
136 && m_copyGraph.maxEdgeIndex()
137 <= m_copyGraph.numberOfEdges() + 10) { // within inflate tolerance
140 cost += costEdgesAlreadyInserted();
142 delete reducedSolution;
143 return cost;
144 }
145
146 // make a compact copy
152 for (node v : m_copyGraph.nodes) {
153 nCopy[v] = ccGraph.newNode();
154 }
155 for (edge e : m_copyGraph.edges) {
156 eCopy[e] = ccGraph.newEdge(nCopy[e->source()], nCopy[e->target()], m_copyGraph.weight(e));
157 }
158 for (node t : m_copyTerminals) {
159 const node tC = nCopy[t];
160 ccTerminals.pushBack(tC);
161 ccIsTerminal[tC] = true;
162 }
163
164 // solve compact copy
167 cost += costEdgesAlreadyInserted();
168
169 // get reduced and original solution from compact copy solution
172 for (node v : m_copyGraph.nodes) {
173 if (ccSolution->copy(nCopy[v]) != nullptr) { // is in solution
174 reducedSolution.newNode(v);
175 }
176 }
177 for (edge e : m_copyGraph.edges) {
178 if (ccSolution->copy(eCopy[e]) != nullptr) { // is in solution
179 reducedSolution.newEdge(e, m_copyGraph.weight(e));
180 }
181 }
182 delete ccSolution;
183
185 return cost;
186 }
187
194 inline const EdgeWeightedGraph<T>& getReducedGraph() const { return m_copyGraph; }
195
197 inline const List<node>& getReducedTerminals() const { return m_copyTerminals; }
198
200 inline void shuffleReducedTerminals() { m_copyTerminals.permute(); }
201
203 inline const NodeArray<bool>& getReducedIsTerminal() const { return m_copyIsTerminal; }
204
206
214
222
224
230
233 return repeat([this]() {
234 bool changed = false;
235 changed |= degree2Test();
236 changed |= makeSimple();
238 return changed;
239 });
240 }
241
243 bool reduceFast() {
244 int k = 5; // for PTmTest
245 // comment wrt. Apple Clang 10: making k const, results in compiler error while binding it in lambda
246 // comment wrt. MSVC 19: not binding the const k would result in compiler error
247
249 bool triviallyChanged = false;
250 changed |= repeat([this, &triviallyChanged, k]() {
251 bool innerChanged = false;
253 // graph guaranteed to be simple and connected
254
255 if (nearestVertexTest()) {
256 makeSimple();
257 innerChanged = true;
258 }
259
260 // precond: connected
261 if (terminalDistanceTest()) {
262 // can occur: disconnected
264 innerChanged = true;
265 }
266
267 // precond: simple, connected
268 innerChanged |= NTDkTest(10, k);
269 // can occur: parallel edges
270
271 // precond: connected
273 // can occur: parallel edges, self-loops
274
275 // precond: connected
277
278 // is not thaaat good but helps a little:
279 innerChanged |= PTmTest(k);
280 // can occur: parallel edges
281
282 return innerChanged;
283 });
284 return changed | triviallyChanged;
285 }
286
289 return repeat([this] {
290 bool changed = reduceFast();
292 return changed;
293 });
294 }
295
297
303
311 bool deleteLeaves();
312
319 bool makeSimple();
320
328
335 bool leastCostTest();
336
343 bool degree2Test();
344
352 bool PTmTest(const int k = 3);
353
363
370 bool longEdgesTest();
371
382 bool NTDkTest(const int maxTestedDegree = 5, const int k = 3);
383
392 bool nearestVertexTest();
393
402 bool shortLinksTest();
403
414 bool lowerBoundBasedTest(T upperBound);
415
423
433 bool dualAscentBasedTest(int repetitions, T upperBound);
434
442
453 bool reachabilityTest(int maxDegreeTest = 0, const int k = 3);
454
463 bool cutReachabilityTest();
464
466
476
478
480 static bool repeat(std::function<bool()> f) {
481 bool changed = false;
482 while (f()) {
483 changed = true;
484 }
485 return changed;
486 }
487
488protected:
497 template<typename TWhat, typename TWhatArray>
498 void addNew(TWhat x, const std::vector<node>& replacedNodes,
499 const std::vector<edge>& replacedEdges, bool deleteReplacedElements,
501
503 inline void addNewNode(node v, const std::vector<node>& replacedNodes,
504 const std::vector<edge>& replacedEdges, bool deleteReplacedElements) {
506 }
507
509 inline void addNewEdge(edge e, const std::vector<node>& replacedNodes,
510 const std::vector<edge>& replacedEdges, bool deleteReplacedElements) {
512 }
513
521
526
529
532
538
539 // for convenience:
546
549
551 bool deleteNodesAboveUpperBound(const NodeArray<T>& lowerBoundWithNode, const T upperBound);
553 bool deleteEdgesAboveUpperBound(const EdgeArray<T>& lowerBoundWithEdge, const T upperBound);
554
558 const NodeArray<List<std::pair<node, T>>>& closestTerminals);
559
570 T maxDistance, int expandedEdges) const;
571
578 const NodeArray<List<std::pair<node, T>>>& closestTerminals) const;
579
585 void computeClosestKTerminals(const int k,
586 NodeArray<List<std::pair<node, T>>>& closestTerminals) const;
587
590
592 T computeRadiusSum() const;
593
595 template<typename LAMBDA>
597 node& optimalTerminal2, NodeArray<T>& distance) const;
598
602
604 void findTwoMinimumCostEdges(node v, edge& first, edge& second) const;
605};
606
607template<typename T>
609 const List<node>& terminals, const NodeArray<bool>& isTerminal)
610 : m_origGraph(wg), m_origTerminals(terminals), m_origIsTerminal(isTerminal), m_eps(1e-6) {
611 OGDF_ASSERT(!m_origGraph.empty());
612
613 // make the initial graph copy
614 m_copyGraph.clear();
617
618 for (node v : m_origGraph.nodes) {
619 nCopy[v] = m_copyGraph.newNode();
620 }
621 for (edge e : m_origGraph.edges) {
622 eCopy[e] = m_copyGraph.newEdge(nCopy[e->source()], nCopy[e->target()], m_origGraph.weight(e));
623 }
624
625 // create the terminals and isTerminal arrays for the copyGraph
626 m_copyTerminals.clear();
628 for (node v : m_origGraph.nodes) {
629 if (m_origIsTerminal(v)) {
630 const node vC = nCopy[v];
631 m_copyTerminals.pushBack(vC);
632 m_copyIsTerminal[vC] = true;
633 }
634 }
636
637 // map every node and edge to a negative number for m_(node/edge)_m_sonsListIndex
640 int nextIndex = 1;
641 for (node v : m_origGraph.nodes) {
643 }
644 for (edge e : m_origGraph.edges) {
646 }
647
648 // set the default approximation algorithm for computing the upper bound cost of the solution
650}
651
652template<typename T>
653template<typename TWhat, typename TWhatArray>
655 const std::vector<edge>& replacedEdges, bool deleteReplacedElements,
657 std::vector<int> sonsIndexList;
659 sonsIndexList.push_back(m_nodeSonsListIndex[replacedNode]);
660 }
662 sonsIndexList.push_back(m_edgeSonsListIndex[replacedEdge]);
663 }
664
665 m_sonsList.emplace_back(sonsIndexList);
666 whatSonsListIndex[x] = (int)m_sonsList.size() - 1;
667
669 for (edge e : replacedEdges) {
670 m_copyGraph.delEdge(e);
671 }
672 for (node v : replacedNodes) {
673 m_copyGraph.delNode(v);
674 }
675 }
676}
677
678template<typename T>
681 if (listIndex < 0) { // is starting node or edge
682 isInSolution[listIndex] = true;
683 return;
684 }
685 // is further added node or edge
686 for (int son : m_sonsList[listIndex]) {
687 addToSolution(son, isInSolution);
688 }
689}
690
691template<typename T>
695 correspondingOriginalSolution.createEmpty(m_origGraph); // note that it is not cleared!
696
697 Array<bool, int> isInSolution(-(m_origGraph.numberOfNodes() + m_origGraph.numberOfEdges()), -1,
698 false);
699
700 // make the indices of original nodes/edge true in isInSolution
701 for (node v : reducedGraphSolution.nodes) {
702 addToSolution(m_nodeSonsListIndex[reducedGraphSolution.original(v)], isInSolution);
703 }
704 for (edge e : reducedGraphSolution.edges) {
705 addToSolution(m_edgeSonsListIndex[reducedGraphSolution.original(e)], isInSolution);
706 }
707
708 // insert nodes and edges
709 int nextIndex = 1;
710 for (node v : m_origGraph.nodes) {
711 if (isInSolution[-(nextIndex++)]) {
713 }
714 }
715 for (edge e : m_origGraph.edges) {
716 if (isInSolution[-(nextIndex++)]) {
717 correspondingOriginalSolution.newEdge(e, m_origGraph.weight(e));
718 }
719 }
720}
721
722template<typename T>
726 const NodeArray<List<std::pair<node, T>>>& closestTerminals) {
727 struct NewEdgeData {
728 edge e1; // new edge replaces this...
729 edge e2; // ...and this
730 edge already; // but is it already there?
731 T weight;
732 };
733
734 std::forward_list<NewEdgeData> newEdges;
735 for (adjEntry adj1 : v->adjEntries) {
736 const edge e1 = adj1->theEdge();
737 const node adjacentNode1 = adj1->twinNode();
738
739 for (adjEntry adj2 = adj1->succ(); adj2; adj2 = adj2->succ()) {
740 const edge e2 = adj2->theEdge();
741 const node adjacentNode2 = adj2->twinNode();
742
743 T edgeWeight = m_copyGraph.weight(e1) + m_copyGraph.weight(e2);
744
745 const edge f = m_copyGraph.searchEdge(adjacentNode1, adjacentNode2);
746 if (f && m_copyGraph.weight(f) <= edgeWeight) {
747 continue; // check whether there is already a lower cost edge connecting the two adjacent nodes
748 }
749
750 T bottleneckDistance = computeBottleneckDistance(adjacentNode1, adjacentNode2, tprime,
752 if (m_eps.greater(edgeWeight, bottleneckDistance)) {
753 continue; // the PTm test
754 }
755
756 newEdges.emplace_front(NewEdgeData {e1, e2, f, edgeWeight});
757 }
758 }
759
760 for (const auto& newEdge : newEdges) {
761 // delete the old edge (do not generate parallel edges)
763 if (newEdge.already) {
764 OGDF_ASSERT(m_copyGraph.weight(newEdge.already) > newEdge.weight);
765 m_copyGraph.setWeight(newEdge.already, newEdge.weight);
766 newEdgeInGraph = newEdge.already;
767 } else {
768 newEdgeInGraph = m_copyGraph.newEdge(newEdge.e1->opposite(v), newEdge.e2->opposite(v),
769 newEdge.weight);
770 }
771 addNewEdge(newEdgeInGraph, {v}, {newEdge.e1, newEdge.e2}, false);
772 }
773
774 m_copyGraph.delNode(v);
775}
776
777template<typename T>
779 m_copyTerminals.clear();
780 for (node v : m_copyGraph.nodes) {
781 if (m_copyIsTerminal[v]) {
782 m_copyTerminals.pushBack(v);
783 }
784 }
785}
786
787template<typename T>
810
811template<typename T>
813 // exceptional case: only one terminal
814 auto deleteAll = [this]() {
815 if (m_copyGraph.numberOfNodes() > 1) {
816 const node w = m_copyTerminals.front();
817 // just remove all other nodes
818 for (node v = m_copyGraph.firstNode(), nextV; v; v = nextV) {
819 nextV = v->succ();
820 if (v != w) {
821 m_copyGraph.delNode(v);
822 }
823 }
824 return true;
825 }
826 return false;
827 };
828 if (m_copyTerminals.size() == 1) {
829 return deleteAll();
830 }
831 // general case: at least 2 terminals
833
834 for (node v : m_copyGraph.nodes) {
835 if (v->degree() == 1) {
836 eraseQueue.append(v);
837 }
838 }
839
840 if (eraseQueue.empty()) {
841 return false;
842 }
843
844 for (; !eraseQueue.empty(); eraseQueue.pop()) {
845 node v = eraseQueue.top();
846 if (v->degree() == 0) {
847 continue;
848 }
849 OGDF_ASSERT(v->degree() == 1);
850 const node w = v->firstAdj()->twinNode();
851 if (m_copyIsTerminal[v]) { // v is a terminal
852 // add edge to solution and contract v into w
853 edge e = v->firstAdj()->theEdge();
854 if (!m_copyIsTerminal[w]) {
855 m_copyIsTerminal[w] = true;
856 m_copyTerminals.pushBack(w);
857 }
858 m_copyTerminals.del(m_copyTerminals.search(v));
859 m_costAlreadyInserted += m_copyGraph.weight(e);
860 int cur = m_nodeSonsListIndex[w];
861 if (cur < 0) { // w is an original (copied) node
862 m_sonsList.emplace_back(
863 std::vector<int> {cur, m_nodeSonsListIndex[v], m_edgeSonsListIndex[e]});
864 m_nodeSonsListIndex[w] = (int)m_sonsList.size() - 1;
865 } else { // w contains already sons
866 m_sonsList[cur].push_back(m_nodeSonsListIndex[v]);
867 m_sonsList[cur].push_back(m_edgeSonsListIndex[e]);
868 }
869 } else { // v is not a terminal
870 if (w->degree() == 2) {
871 eraseQueue.append(w);
872 }
873 }
874 m_copyGraph.delNode(v);
875 if (m_copyTerminals.size() == 1) {
876 deleteAll();
877 return true;
878 }
879 }
880 return true;
881}
882
883template<typename T>
885 bool changed = false;
886 NodeArray<edge> minCostEdge(m_copyGraph, nullptr);
887 for (node v : m_copyGraph.nodes) {
888 for (adjEntry adj = v->firstAdj(), nextAdj; adj; adj = nextAdj) {
889 nextAdj = adj->succ();
890
891 edge e = adj->theEdge();
892 node adjNode = adj->twinNode();
893 if (adjNode == v) { // found a self-loop
894 if (nextAdj == adj->twin()) {
895 nextAdj = nextAdj->succ();
896 }
897 m_copyGraph.delEdge(e);
898 changed = true;
899 } else {
900 if (minCostEdge[adjNode] == nullptr
901 || m_copyGraph.weight(minCostEdge[adjNode]) > m_copyGraph.weight(e)) {
902 if (minCostEdge[adjNode] != nullptr) {
903 m_copyGraph.delEdge(minCostEdge[adjNode]);
904 changed = true;
905 }
906 minCostEdge[adjNode] = e;
907 } else {
908 m_copyGraph.delEdge(e);
909 changed = true;
910 }
911 }
912 }
913
914 for (adjEntry adj : v->adjEntries) {
915 minCostEdge[adj->twinNode()] = nullptr;
916 }
917 }
918 return changed;
919}
920
921template<typename T>
923 for (node v1 : m_copyGraph.nodes) {
924 for (adjEntry adj : v1->adjEntries) {
925 node v2 = adj->twinNode();
927 min(shortestPath[v1][v2], m_copyGraph.weight(adj->theEdge()));
928 }
929 }
930
931 for (node pivot : m_copyGraph.nodes) {
932 for (node v1 : m_copyGraph.nodes) {
933 for (node v2 : m_copyGraph.nodes) {
934 if (shortestPath[v1][pivot] == std::numeric_limits<T>::max()
935 || shortestPath[pivot][v2] == std::numeric_limits<T>::max()) {
936 continue;
937 }
938
941 }
942 }
943 }
944}
945
946template<typename T>
949 shortestPath.init(m_copyGraph);
950 for (node v : m_copyGraph.nodes) {
951 shortestPath[v].init(m_copyGraph, std::numeric_limits<T>::max());
952 }
953
954 floydWarshall(shortestPath);
955}
956
957template<typename T>
959 OGDF_ASSERT(!m_copyGraph.empty());
960 bool changed = false;
962 computeShortestPathMatrix(shortestPath);
963
964 for (node v : m_copyGraph.nodes) {
965 for (adjEntry adj = v->firstAdj(), nextAdj; adj; adj = nextAdj) {
966 nextAdj = adj->succ();
967
968 edge e = adj->theEdge();
969 node adjNode = adj->twinNode();
970 if (adjNode != v && shortestPath[v][adjNode] < m_copyGraph.weight(e)) {
971 m_copyGraph.delEdge(e);
972 changed = true;
973 }
974 }
975 }
976 return changed;
977}
978
979template<typename T>
981 OGDF_ASSERT(!m_copyGraph.empty());
982 bool changed = false;
983 for (node v = m_copyGraph.firstNode(), nextV; v; v = nextV) {
984 nextV = v->succ();
985
986 if (m_copyIsTerminal[v] || v->degree() != 2) {
987 continue;
988 }
989
990 // get left and right adjacent nodes
991 edge eLeft = v->firstAdj()->theEdge();
992 edge eRight = v->lastAdj()->theEdge();
993
994 node vLeft = v->firstAdj()->twinNode();
995 node vRight = v->lastAdj()->twinNode();
996 if (vLeft != vRight) {
997 T weight = m_copyGraph.weight(eLeft) + m_copyGraph.weight(eRight);
998 edge newEdge = m_copyGraph.newEdge(vLeft, vRight, weight);
999 addNewEdge(newEdge, {v}, {eLeft, eRight}, true);
1000 changed = true;
1001 } else { // v is leaf with parallel edges or self-loop component
1002 m_copyGraph.delNode(v);
1003 }
1004 }
1005 return changed;
1006}
1007
1008template<typename T>
1010 NodeArray<int> hisConnectedComponent(m_copyGraph, -1);
1011 bool changed = connectedComponents(m_copyGraph, hisConnectedComponent) > 1;
1012 if (changed) {
1013 int componentWithTerminals = -1;
1014 for (node v : m_copyTerminals) {
1016 std::cerr << "terminals in different connected components!\n";
1017 OGDF_ASSERT(false);
1018 }
1020 }
1021
1022 for (node v = m_copyGraph.firstNode(), nextV; v; v = nextV) {
1023 nextV = v->succ();
1025 m_copyGraph.delNode(v);
1026 }
1027 }
1028 }
1029 return changed;
1030}
1031
1032template<typename T>
1034 OGDF_ASSERT(!m_copyGraph.empty());
1035 OGDF_ASSERT(isConnected(m_copyGraph));
1036 bool changed = false;
1037
1040 m_copyTerminals);
1041 T maxBottleneck(0);
1042 for (edge e : tprime.edges) {
1044 }
1045
1046 for (edge e = m_copyGraph.firstEdge(), nextE; e; e = nextE) {
1047 nextE = e->succ();
1048
1049 if (m_eps.greater(m_copyGraph.weight(e), maxBottleneck)) {
1050 m_copyGraph.delEdge(e);
1051 changed = true;
1052 }
1053 }
1054
1055 return changed;
1056}
1057
1058template<typename T>
1060 NodeArray<T>& distance, T maxDistance, int expandedEdges) const {
1062
1063 // initialization
1064 distance[source] = 0;
1065 queue.push(source, distance[source]);
1066
1067 while (!queue.empty()) {
1068 node currentNode = queue.topElement();
1069 queue.pop();
1070
1071 reachedNodes.pushBack(currentNode);
1072
1073 for (adjEntry adj : currentNode->adjEntries) {
1074 edge e = adj->theEdge();
1075 if (expandedEdges <= 0) {
1076 break;
1077 }
1078 --expandedEdges;
1079
1081 T possibleDistance = distance[currentNode] + m_copyGraph.weight(e);
1082
1083 if (m_eps.geq(possibleDistance, maxDistance) || m_copyIsTerminal[adjacentNode]) {
1084 continue;
1085 }
1086
1087 if (possibleDistance < distance[adjacentNode]) {
1088 distance[adjacentNode] = possibleDistance;
1089 if (queue.contains(adjacentNode)) { // is already in the queue
1091 } else {
1092 queue.push(adjacentNode, distance[adjacentNode]);
1093 }
1094 }
1095 }
1096 }
1097}
1098
1099template<typename T>
1101 OGDF_ASSERT(!m_copyGraph.empty());
1102 bool changed = false;
1103 NodeArray<T> xDistance(m_copyGraph, std::numeric_limits<T>::max()),
1104 yDistance(m_copyGraph, std::numeric_limits<T>::max());
1105
1106 for (edge e = m_copyGraph.firstEdge(), nextE; e; e = nextE) {
1107 nextE = e->succ();
1109
1110 findClosestNonTerminals(e->source(), xReachedNodes, xDistance, m_copyGraph.weight(e), 200);
1111 findClosestNonTerminals(e->target(), yReachedNodes, yDistance, m_copyGraph.weight(e), 200);
1112
1113 for (node commonNode : xReachedNodes) {
1114 if (yDistance[commonNode] == std::numeric_limits<T>::max()) { // is not common
1115 continue;
1116 }
1117 if (m_eps.less(xDistance[commonNode] + yDistance[commonNode], m_copyGraph.weight(e))) {
1118 m_copyGraph.delEdge(e);
1119 changed = true;
1120 break;
1121 }
1122 }
1123
1125 xDistance[reachedNode] = std::numeric_limits<T>::max();
1126 }
1128 yDistance[reachedNode] = std::numeric_limits<T>::max();
1129 }
1130 }
1131 return changed;
1132}
1133
1134template<typename T>
1136 NodeArray<List<std::pair<node, T>>>& closestTerminals) const {
1138
1139 closestTerminals.init(m_copyGraph);
1141 std::unordered_map<NodePair, typename NodePairQueue::Handle,
1143 qpos;
1144
1145 // initialization
1146 for (node v : m_copyTerminals) {
1147 closestTerminals[v].pushBack(std::make_pair(v, 0));
1148 qpos[NodePair(v, v)] = queue.push(NodePair(v, v), closestTerminals[v].front().second);
1149 }
1150
1152 for (std::pair<node, T> currentPair : closestTerminals[currentNode]) {
1153 if (currentPair.first == sourceTerminal) {
1154 return currentPair.second;
1155 }
1156 }
1157 return static_cast<T>(-1);
1158 };
1159
1161 const T newDist) {
1163
1164 // delete the old distance
1165 for (ListIterator<std::pair<node, T>> it = currentList.begin(); it.valid(); ++it) {
1166 if ((*it).first == sourceTerminal) {
1167 currentList.del(it);
1168 break;
1169 }
1170 }
1171
1172 if (currentList.size() == k) { // the list is full
1173 currentList.popBack(); // delete the largest cost element
1174 }
1175
1176 // add the new distance such that the list remains sorted
1177 if (currentList.empty() || (*currentList.begin()).second >= newDist) { // if the list is empty
1178 currentList.pushFront(std::make_pair(sourceTerminal, newDist));
1179 return;
1180 }
1181
1183 while (it.succ() != closestTerminals[currentNode].end() && (*it.succ()).second < newDist) {
1184 ++it;
1185 }
1186 closestTerminals[currentNode].insertAfter(std::make_pair(sourceTerminal, newDist), it);
1187 };
1188
1189 while (!queue.empty()) {
1190 NodePair minDistPair = queue.topElement();
1191 queue.pop();
1192 node currentNode = minDistPair.source;
1194
1196 if (currentDist == -1) { // source terminal not found
1197 continue; // check if the current path needs to be expanded
1198 }
1199
1200 for (adjEntry adj : currentNode->adjEntries) {
1201 edge e = adj->theEdge();
1203
1204 if (m_copyIsTerminal[adjacentNode]) {
1205 continue;
1206 }
1207
1208 T possibleNewDistance = currentDist + m_copyGraph.weight(e);
1209
1210 // try to insert the new path from sourceTerminal to adjacentNode
1213 != -1) { // there is already one path from sourceTerminal to adjNode
1217 }
1218 } else {
1219 if (closestTerminals[adjacentNode].size() < k
1224 }
1225 }
1226 }
1227 }
1228}
1229
1230template<typename T>
1232 OGDF_ASSERT(!m_copyGraph.empty());
1233 OGDF_ASSERT(isConnected(m_copyGraph));
1234 bool changed = false;
1235
1238 m_copyTerminals);
1239
1241 computeClosestKTerminals(k, closestTerminals);
1242
1244
1245 for (edge e = m_copyGraph.firstEdge(), nextE; e; e = nextE) {
1246 nextE = e->succ();
1247
1248 T bottleneckDistance = computeBottleneckDistance(e->source(), e->target(), tprime,
1250
1251 if (m_eps.greater(m_copyGraph.weight(e), bottleneckDistance)) {
1252 m_copyGraph.delEdge(e);
1253 changed = true;
1254 }
1255 }
1256
1257 return changed;
1258}
1259
1260template<typename T>
1262 OGDF_ASSERT(!m_copyGraph.empty());
1263 if (m_copyTerminals.size() <= 2) {
1264 return false;
1265 }
1266
1267 bool changed = false;
1268 OGDF_ASSERT(isSimpleUndirected(m_copyGraph));
1269 OGDF_ASSERT(isConnected(m_copyGraph));
1270
1273 m_copyTerminals);
1274
1276 computeClosestKTerminals(k, closestTerminals);
1277
1279
1280 for (node v = m_copyGraph.firstNode(), nextV; v; v = nextV) {
1281 nextV = v->succ();
1282
1283 if (m_copyIsTerminal[v]) {
1284 continue;
1285 }
1286 if (v->degree() <= 2 || v->degree() > maxTestedDegree) {
1287 continue;
1288 }
1289
1290 // collect neighbors
1292 for (adjEntry adj : v->adjEntries) {
1293 outgoingAdjs.pushBack(adj);
1294 }
1296
1297 bool deleteNode = true;
1298 for (neighborSubset.begin(3, v->degree()); neighborSubset.valid() && deleteNode;
1299 neighborSubset.next()) {
1301 std::unordered_map<node, node> initNodeToAuxGraph;
1302 std::unordered_map<node, node> auxGraphToInitNode;
1303
1305
1306 for (int i = 0; i < neighborSubset.size(); ++i) {
1307 const adjEntry adj = neighborSubset[i];
1308 const node adjacentNode = adj->twinNode();
1309 sumToSelectedAdjacentNodes += m_copyGraph.weight(adj->theEdge());
1310
1312
1313 node newAuxGraphNode = auxGraph.newNode();
1316 }
1317
1318 EdgeArray<T> weight(auxGraph, 0);
1319 for (node auxGraphNode1 : auxGraph.nodes) {
1321 auxGraphNode2 = auxGraphNode2->succ()) {
1323 weight[e] = computeBottleneckDistance(auxGraphToInitNode[auxGraphNode1],
1325 }
1326 }
1327
1328 // the auxGraph is now created; run MST on it
1329 EdgeArray<bool> isInTree(auxGraph, false);
1330 T mstCost = computeMinST(auxGraph, weight, isInTree);
1331
1333 deleteNode = false;
1334 }
1335 }
1336
1337 if (deleteNode) {
1338 deleteSteinerDegreeTwoNode(v, tprime, tprimeHPD, closestTerminals);
1339 changed = true;
1340 }
1341 }
1342
1343 return changed;
1344}
1345
1346template<typename T>
1350
1352
1353 // recurse for every successor
1354 for (adjEntry adj : currentNode->adjEntries) {
1355 edge e = adj->theEdge();
1357
1358 if (voronoiRegions.predecessor(adjacentNode) == currentNode) {
1360 }
1361 }
1362}
1363
1364template<typename T>
1366 /* This is a simple problem, however, there is a weird case that can occur
1367 * in nearestVertexTest(): Imagine a triangle of three terminals where the three edges
1368 * have all equal costs. It can happen that each terminal chooses its first edge to
1369 * be unique, so in the end we add all three edges of the triangle except only two of it.
1370 * We tackle this problem at *this* stage by choosing the edge with smallest index
1371 * in case of a tie in the costs. It is sufficient to do that for the first only
1372 * because this is the edge that will be put into the solution. */
1373 for (adjEntry adj : v->adjEntries) {
1374 edge e = adj->theEdge();
1375 if (first == nullptr // not set or
1376 || m_eps.less(m_copyGraph.weight(e), m_copyGraph.weight(first)) // smaller cost or
1377 || (m_eps.equal(m_copyGraph.weight(e), m_copyGraph.weight(first)) // cost tie but...
1378 && e->index() < first->index())) { // cost tie but smaller index
1379 second = first;
1380 first = e;
1381 } else {
1382 if (second == nullptr || m_eps.less(m_copyGraph.weight(e), m_copyGraph.weight(second))) {
1383 second = e;
1384 }
1385 }
1386 }
1387 OGDF_ASSERT(first != second);
1388}
1389
1390template<typename T>
1392 if (edgesToBeAddedInSolution.empty()) {
1393 return false;
1394 }
1395 for (edge e : edgesToBeAddedInSolution) {
1396 node x = e->source(), y = e->target();
1397 m_sonsList.emplace_back(std::vector<int> {
1398 m_nodeSonsListIndex[x], m_nodeSonsListIndex[y], m_edgeSonsListIndex[e]});
1399 m_costAlreadyInserted += m_copyGraph.weight(e);
1400 node newNode = m_copyGraph.contract(e);
1401 m_nodeSonsListIndex[newNode] = (int)m_sonsList.size() - 1;
1402 m_copyIsTerminal[newNode] = true;
1403 }
1404
1405 recomputeTerminalsList();
1406 return true;
1407}
1408
1409template<typename T>
1411 OGDF_ASSERT(!m_copyGraph.empty());
1412 OGDF_ASSERT(isLoopFree(m_copyGraph));
1413 OGDF_ASSERT(isConnected(m_copyGraph));
1414
1415 Voronoi<T> voronoiRegions(m_copyGraph, m_copyGraph.edgeWeights(), m_copyTerminals);
1416
1417 NodeArray<edge> minCostIncidentEdge1(m_copyGraph, nullptr);
1418 NodeArray<edge> minCostIncidentEdge2(m_copyGraph, nullptr);
1419
1420 // find the first and second minimum-cost edges incident to terminals
1421 for (node terminal : m_copyTerminals) {
1422 if (terminal->degree() >= 2) {
1423 findTwoMinimumCostEdges(terminal, minCostIncidentEdge1[terminal],
1424 minCostIncidentEdge2[terminal]);
1425 }
1426 }
1427
1428 // for each Voronoi region, mark successors (recursively) of the minimum-cost edge
1429 NodeArray<bool> isSuccessorOfMinCostEdge(m_copyGraph, false);
1430 for (node terminal : m_copyTerminals) {
1431 if (terminal->degree() >= 2) {
1432 node closestNode = minCostIncidentEdge1[terminal]->opposite(terminal);
1433
1434 if (voronoiRegions.seed(closestNode) == terminal) {
1436 }
1437 }
1438 }
1439
1440 // compute for every terminal the distance to the closest terminal
1441 NodeArray<T> distanceToClosestTerminal(m_copyGraph, std::numeric_limits<T>::max());
1442 for (edge e : m_copyGraph.edges) {
1443 node x = e->source(), y = e->target();
1444 node seedX = voronoiRegions.seed(x), seedY = voronoiRegions.seed(y);
1445 if (seedX != seedY) {
1446 // update distanceToClosestTerminal for seed(x)
1448 voronoiRegions.distance(x) + m_copyGraph.weight(e) + voronoiRegions.distance(y);
1449
1450 if (isSuccessorOfMinCostEdge[x]) {
1452 }
1453 if (isSuccessorOfMinCostEdge[y]) {
1455 }
1456 }
1457 }
1458
1459 // see what edges can be added in solution (Observation 3.2)
1461 EdgeArray<bool> willBeAddedInSolution(m_copyGraph, false);
1462 for (node terminal : m_copyTerminals) {
1463 if (terminal->degree() >= 2) {
1464 const edge e = minCostIncidentEdge1[terminal];
1465 const node closestAdjacentNode = e->opposite(terminal);
1466 T distance {voronoiRegions.seed(closestAdjacentNode) == terminal
1467 ? distanceToClosestTerminal[terminal]
1468 : m_copyGraph.weight(e) + voronoiRegions.distance(closestAdjacentNode)};
1469
1470 if (m_eps.geq(m_copyGraph.weight(minCostIncidentEdge2[terminal]), distance)
1471 && !willBeAddedInSolution[e]) {
1472 edgesToBeAddedInSolution.pushBack(e);
1473 willBeAddedInSolution[e] = true;
1474 }
1475 }
1476 }
1477
1478 return addEdgesToSolution(edgesToBeAddedInSolution);
1479}
1480
1481template<typename T>
1483 OGDF_ASSERT(!m_copyGraph.empty());
1484 OGDF_ASSERT(isConnected(m_copyGraph));
1485
1486 Voronoi<T> voronoiRegions(m_copyGraph, m_copyGraph.edgeWeights(), m_copyTerminals);
1487
1488 NodeArray<edge> minCostLeavingRegionEdge1(m_copyGraph, nullptr);
1489 NodeArray<edge> minCostLeavingRegionEdge2(m_copyGraph, nullptr);
1490
1491 // populate minCostLeavingRegions{1,2}
1492 for (edge e : m_copyGraph.edges) {
1493 auto updateMinCostLeavingRegionsFor = [&](node seed) {
1494 if (minCostLeavingRegionEdge1[seed] == nullptr
1495 || m_copyGraph.weight(minCostLeavingRegionEdge1[seed]) > m_copyGraph.weight(e)) {
1497 minCostLeavingRegionEdge1[seed] = e;
1498 } else if (minCostLeavingRegionEdge2[seed] == nullptr
1499 || m_copyGraph.weight(minCostLeavingRegionEdge2[seed]) > m_copyGraph.weight(e)) {
1500 minCostLeavingRegionEdge2[seed] = e;
1501 }
1502 };
1503 const node x = e->source(), y = e->target();
1504 const node seedX = voronoiRegions.seed(x), seedY = voronoiRegions.seed(y);
1505 if (seedX != seedY) { // e is a link between Voronoi regions
1506 // update minCostLeavingRegions for both x and y
1509 }
1510 }
1511
1513 EdgeArray<bool> willBeAddedInSolution(m_copyGraph, false);
1514 for (node terminal : m_copyTerminals) {
1515 if (minCostLeavingRegionEdge2[terminal] == nullptr) {
1516 continue;
1517 }
1518 // XXX: hm, is that smart? If there is no second shortest edge, then the first shortest edge belongs to the solution, doesn't it?
1519
1520 const edge e1 = minCostLeavingRegionEdge1[terminal];
1521 const node x = e1->source(), y = e1->target();
1522 if (m_eps.geq(m_copyGraph.weight(minCostLeavingRegionEdge2[terminal]),
1523 voronoiRegions.distance(x) + m_copyGraph.weight(e1) + voronoiRegions.distance(y))
1525 edgesToBeAddedInSolution.pushBack(e1);
1526 willBeAddedInSolution[e1] = true;
1527 }
1528 }
1529
1530 return addEdgesToSolution(edgesToBeAddedInSolution);
1531}
1532
1533template<typename T>
1535 OGDF_ASSERT(m_copyTerminals.size() > 1);
1536
1537 // compute the Voronoi regions
1538 Voronoi<T> voronoiRegions(m_copyGraph, m_copyGraph.edgeWeights(), m_copyTerminals);
1539
1540 // compute the radius for each terminal
1541 terminalRadius.init(m_copyGraph, std::numeric_limits<T>::max());
1542 for (node v : m_copyGraph.nodes) {
1543 node seedV = voronoiRegions.seed(v);
1544 T distanceToSeedV = voronoiRegions.distance(v);
1545
1546 for (adjEntry adj : v->adjEntries) {
1547 edge e = adj->theEdge();
1548 node adjacentNode = e->opposite(v);
1549
1550 if (voronoiRegions.seed(adjacentNode) != seedV) {
1552 }
1553 }
1554 }
1555}
1556
1557template<typename T>
1559 const T upperBound) {
1560 bool changed = false;
1561 for (node v = m_copyGraph.firstNode(), nextV; v; v = nextV) {
1562 nextV = v->succ();
1563 if (m_eps.greater(lowerBoundWithNode[v], upperBound)) {
1564 OGDF_ASSERT(!m_copyIsTerminal[v]);
1565 m_copyGraph.delNode(v);
1566 changed = true;
1567 }
1568 }
1569 return changed;
1570}
1571
1572template<typename T>
1574 OGDF_ASSERT(!m_copyGraph.empty());
1575 if (m_copyTerminals.size() <= 1) {
1576 return false;
1577 }
1578
1579 OGDF_ASSERT(isConnected(m_copyGraph));
1580
1581 NodeArray<T> lowerBoundWithNode(m_copyGraph, std::numeric_limits<T>::lowest());
1582
1584 computeClosestKTerminals(3, closestTerminals);
1585
1586 // Update the lowerbound of the cost of a Steiner tree containing one particular node
1587 // as explained in [PV01, page 278, Observation 3.5]
1588 const T radiusSum = computeRadiusSum();
1589 for (node v : m_copyGraph.nodes) {
1590 if (m_copyIsTerminal[v]) {
1591 continue;
1592 }
1593
1594 if (closestTerminals[v].size() < 2) {
1595 lowerBoundWithNode[v] = std::numeric_limits<T>::max();
1596 continue;
1597 }
1598
1599 std::pair<node, T> closestTerminalPair1 = *(closestTerminals[v].get(0)),
1603
1606 }
1607
1608 // Compute lower bound for edges, see [PV01, page 278, Observation 3.5]
1609 EdgeArray<T> lowerBoundWithEdge(m_copyGraph, 0);
1610 for (edge e : m_copyGraph.edges) {
1611 node x = e->source(), y = e->target();
1612
1615
1618 + radiusSum);
1619 }
1620
1621 // Update the lowerbound of the cost of a Steiner tree containing one particular node
1622 // as explained in [PV01, pages 279-280, Observation 3.8]
1623 Graph auxiliaryGraph;
1624 NodeArray<node> terminalInAuxiliaryGraph(m_copyGraph, nullptr);
1625 for (node terminal : m_copyTerminals) {
1626 node newAuxiliaryNode = auxiliaryGraph.newNode();
1628 }
1629
1630 EdgeArray<T> initialEdgeWeight(m_copyGraph);
1631 for (edge e : m_copyGraph.edges) {
1632 initialEdgeWeight[e] = m_copyGraph.weight(e);
1633 }
1634 Voronoi<T> voronoiRegions(m_copyGraph, initialEdgeWeight, m_copyTerminals);
1635
1639 EdgeArray<T> edgeWeight(auxiliaryGraph, std::numeric_limits<T>::max());
1640 for (edge e : m_copyGraph.edges) {
1641 node x = e->source(), y = e->target();
1642 node seedX = voronoiRegions.seed(x), seedY = voronoiRegions.seed(y);
1643 if (seedX == seedY) {
1644 continue;
1645 }
1646
1648 if (edgeBetweenNodes.find(pair) == edgeBetweenNodes.end()) {
1651 }
1653 Math::updateMin(edgeWeight[auxiliaryEdge],
1654 min(voronoiRegions.distance(x), voronoiRegions.distance(y)) + m_copyGraph.weight(e));
1655 }
1656
1657 EdgeArray<bool> isInTree(auxiliaryGraph, false);
1658 T minimumSpanningTreeCost = computeMinST(auxiliaryGraph, edgeWeight, isInTree);
1659 T longestEdgeCost = std::numeric_limits<T>::lowest();
1660 for (edge e : auxiliaryGraph.edges) {
1661 if (isInTree[e]) {
1662 Math::updateMax(longestEdgeCost, edgeWeight[e]);
1663 }
1664 }
1665
1666 for (node v : m_copyGraph.nodes) {
1667 if (m_copyIsTerminal[v] || closestTerminals[v].size() < 2) {
1668 continue;
1669 }
1670
1671 std::pair<node, T> closestTerminalPair1 = *(closestTerminals[v].get(0)),
1675
1679 }
1680
1681 return deleteNodesAboveUpperBound(lowerBoundWithNode, upperBound)
1682 || deleteEdgesAboveUpperBound(lowerBoundWithEdge, upperBound);
1683}
1684
1685template<typename T>
1687 OGDF_ASSERT(!m_copyGraph.empty());
1688 bool changed = false;
1689 if (m_copyTerminals.size() > 1) {
1693 alg.setRepetitions(repetitions);
1694 alg.call(m_copyGraph, m_copyTerminals, lowerBoundNodes, lowerBoundEdges);
1695
1696 changed |= deleteNodesAboveUpperBound(lowerBoundNodes, upperBound);
1697 changed |= deleteEdgesAboveUpperBound(lowerBoundEdges, upperBound);
1698 }
1699 return changed;
1700}
1701
1702template<typename T>
1704 const T upperBound) {
1705 bool changed = false;
1706 for (edge e = m_copyGraph.firstEdge(), nextE; e; e = nextE) {
1707 nextE = e->succ();
1708 if (m_eps.greater(lowerBoundWithEdge[e], upperBound)) {
1709 m_copyGraph.delEdge(e);
1710 changed = true;
1711 }
1712 }
1713 return changed;
1714}
1715
1716template<typename T>
1719 computeRadiusOfTerminals(terminalRadius);
1720
1721 // instead of sorting, we can simply ignore the two largest radii
1722 T radiusSum = T();
1723 T largestRadius1 = std::numeric_limits<T>::lowest(),
1724 largestRadius2 = std::numeric_limits<T>::lowest();
1725 for (node terminal : m_copyTerminals) {
1726 radiusSum += terminalRadius[terminal];
1727
1728 if (terminalRadius[terminal] > largestRadius1) {
1730 largestRadius1 = terminalRadius[terminal];
1731 } else {
1732 if (terminalRadius[terminal] > largestRadius2) {
1733 largestRadius2 = terminalRadius[terminal];
1734 }
1735 }
1736 }
1738 return radiusSum;
1739}
1740
1741template<typename T>
1743 OGDF_ASSERT(!m_copyGraph.empty());
1744 OGDF_ASSERT(isSimpleUndirected(m_copyGraph));
1745 OGDF_ASSERT(isConnected(m_copyGraph));
1746 bool changed = false;
1747 if (maxDegreeTest <= 0) {
1748 maxDegreeTest = m_copyGraph.numberOfNodes();
1749 }
1750
1752 T upperBoundCost = computeMinSteinerTreeUpperBound(approximatedSteinerTree);
1753
1754 NodeArray<bool> isInUpperBoundTree(m_copyGraph, false);
1755 for (node v : approximatedSteinerTree->nodes) {
1756 isInUpperBoundTree[approximatedSteinerTree->original(v)] = true;
1757 }
1759
1760 // Initialize tprime and its hpd decomposition used for partially not adding useless edges during nodes' deletion
1763 m_copyTerminals);
1764 OGDF_ASSERT(!tprime.empty());
1765
1767 computeClosestKTerminals(k, closestTerminals);
1768
1770
1771 // check which nodes can be deleted
1772 for (node v = m_copyGraph.firstNode(), nextV; v; v = nextV) {
1773 nextV = v->succ();
1774
1775 if (isInUpperBoundTree[v] || v->degree() > maxDegreeTest) {
1776 continue;
1777 }
1778
1779 // compute v's farthest and closest terminals
1781
1782 NodeArray<T> distance(m_copyGraph);
1783 NodeArray<edge> predecessor(m_copyGraph, nullptr);
1784 dijkstra.call(m_copyGraph, m_copyGraph.edgeWeights(), v, predecessor, distance);
1785
1786 // compute first, second nearest terminals and farthest terminal
1787 node farthestTerminal = nullptr;
1789 T distanceToClosestTerminal1 = std::numeric_limits<T>::max(),
1790 distanceToClosestTerminal2 = std::numeric_limits<T>::max();
1791 for (node terminal : m_copyTerminals) {
1792 if (distanceToFarthestTerminal < distance[terminal]) {
1793 farthestTerminal = terminal;
1794 distanceToFarthestTerminal = distance[terminal];
1795 }
1796
1797 if (distanceToClosestTerminal1 > distance[terminal]) {
1799 distanceToClosestTerminal1 = distance[terminal];
1800 } else {
1801 if (distanceToClosestTerminal2 > distance[terminal]) {
1802 distanceToClosestTerminal2 = distance[terminal];
1803 }
1804 }
1805 }
1806
1807 if (predecessor[farthestTerminal] == nullptr // is not in the same component with the terminals
1809 == std::numeric_limits<T>::max() // cannot reach at least 2 terminals, must be deleted
1812 upperBoundCost)) {
1813 changed = true;
1814 // delete the node
1815 if (predecessor[farthestTerminal] != nullptr
1816 && distanceToClosestTerminal2 != std::numeric_limits<T>::max()
1818 upperBoundCost)) {
1819 // the deleted node has degree 2 -> replace it with edges
1820 deleteSteinerDegreeTwoNode(v, tprime, tprimeHPD, closestTerminals);
1821 } else {
1822 // just delete the node
1823 m_copyGraph.delNode(v);
1824 }
1825 }
1826 }
1827
1828 return changed;
1829}
1830
1831template<typename T>
1832template<typename LAMBDA>
1835 // run Dijkstra starting from v
1837
1838 distance.init(m_copyGraph);
1839 NodeArray<edge> predecessor(m_copyGraph, nullptr);
1840 dijkstra.call(m_copyGraph, m_copyGraph.edgeWeights(), v, predecessor, distance);
1841
1842 for (node terminal : m_copyTerminals) {
1843 if (predecessor[terminal] == nullptr) {
1844 continue;
1845 }
1846
1847 if (optimalTerminal1 == nullptr
1848 || dist(optimalTerminal1, distance) > dist(terminal, distance)) {
1850 optimalTerminal1 = terminal;
1851 } else {
1852 if (optimalTerminal2 == nullptr
1853 || dist(optimalTerminal2, distance) > dist(terminal, distance)) {
1854 optimalTerminal2 = terminal;
1855 }
1856 }
1857 }
1858 OGDF_ASSERT(optimalTerminal1 != nullptr);
1859 OGDF_ASSERT(optimalTerminal2 != nullptr);
1861}
1862
1863template<typename T>
1865 OGDF_ASSERT(!m_copyGraph.empty());
1866 if (m_copyTerminals.size() <= 2) {
1867 return false;
1868 }
1869
1870 // get the upper bound
1872 const T upperBoundCost = computeMinSteinerTreeUpperBound(approximatedSteinerTree);
1873
1874 NodeArray<bool> isInUpperBoundTree(m_copyGraph, false);
1875 for (node v : approximatedSteinerTree->nodes) {
1876 isInUpperBoundTree[approximatedSteinerTree->original(v)] = true;
1877 }
1879
1880 T cK = T();
1881 NodeArray<T> minCostOfAdjacentEdge(m_copyGraph, std::numeric_limits<T>::max());
1882 for (node terminal : m_copyTerminals) {
1883 for (adjEntry adj : terminal->adjEntries) {
1884 Math::updateMin(minCostOfAdjacentEdge[terminal], m_copyGraph.weight(adj->theEdge()));
1885 }
1886 cK += minCostOfAdjacentEdge[terminal];
1887 }
1888 auto dist = [&minCostOfAdjacentEdge](node terminal, const NodeArray<T>& distance) {
1889 return distance[terminal] - minCostOfAdjacentEdge[terminal];
1890 };
1891
1893 std::set<edge> delEdges;
1896 for (node v = m_copyGraph.firstNode(), nextV; v; v = nextV) {
1897 nextV = v->succ();
1898
1899 if (isInUpperBoundTree[v]) {
1900 continue;
1901 }
1902
1903 // compute its optimal terminals
1904 node vOptimalTerminal1 = nullptr, vOptimalTerminal2 = nullptr;
1905 computeOptimalTerminals(v, dist, vOptimalTerminal1, vOptimalTerminal2, vDistance);
1906
1907 // check whether it can be deleted
1909 upperBoundCost)) {
1910 delNodes.pushBack(v);
1911 } else { // it is not deleted, perform the edge test
1912 for (adjEntry adj = v->firstAdj(), adjNext; adj; adj = adjNext) {
1913 adjNext = adj->succ();
1914 node w = adj->twinNode();
1915 if (m_copyIsTerminal[w]) {
1916 continue;
1917 }
1918 node wOptimalTerminal1 = nullptr, wOptimalTerminal2 = nullptr;
1919 computeOptimalTerminals(w, dist, wOptimalTerminal1, wOptimalTerminal2, wDistance);
1920
1924 // the nearest terminals to v and w are the same, but they have to be
1925 // different. Obtain the minimum choice such that they are different.
1931 } else {
1933 }
1934 }
1937 + m_copyGraph.weight(adj->theEdge()),
1938 upperBoundCost)) {
1939 delEdges.insert(adj->theEdge());
1940 }
1941 }
1942 }
1943 }
1944
1945 bool changed = false;
1946 for (edge e : delEdges) {
1947 m_copyGraph.delEdge(e);
1948 changed = true;
1949 }
1950 for (node v : delNodes) {
1951 m_copyGraph.delNode(v);
1952 changed = true;
1953 }
1954 return changed;
1955}
1956
1957}
Declaration and implementation of bounded queue class.
Definition of the ogdf::steiner_tree:HeavyPathDecomposition class template.
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...
Definition of the ogdf::SteinerTreeLowerBoundDualAscent class template.
A class that allows to enumerate k-subsets.
Class for adjacency list elements.
Definition Graph_d.h:79
edge theEdge() const
Returns the edge associated with this adjacency entry.
Definition Graph_d.h:97
node twinNode() const
Returns the associated node of the corresponding adjacency entry (shorthand for twin()->theNode()).
Definition Graph_d.h:112
The parameterized class Array implements dynamic arrays of type E.
Definition Array.h:214
INDEX size() const
Returns the size (number of elements) of the array.
Definition Array.h:297
The parameterized class BoundedQueue implements queues with bounded size.
Dijkstra's single source shortest path algorithm.
Definition Dijkstra.h:53
Dynamic arrays indexed with edges.
Definition EdgeArray.h:125
void init()
Reinitializes the array. Associates the array with no graph.
Definition EdgeArray.h:292
Class for the representation of edges.
Definition Graph_d.h:300
int index() const
Returns the index of the edge.
Definition Graph_d.h:332
node opposite(node v) const
Returns the adjacent node different from v.
Definition Graph_d.h:350
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:83
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:57
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.
Data type for general directed graphs (adjacency list representation).
Definition Graph_d.h:521
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:622
edge firstEdge() const
Returns the first edge in the list of all edges.
Definition Graph_d.h:652
edge newEdge(node v, node w)
Creates a new edge (v,w) and returns it.
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:625
internal::GraphObjectContainer< NodeElement > nodes
The container containing all node objects.
Definition Graph_d.h:589
virtual void delEdge(edge e)
Removes edge e from the graph.
node firstNode() const
Returns the first node in the list of all nodes.
Definition Graph_d.h:646
bool empty() const
Returns true iff the graph is empty, i.e., contains no nodes.
Definition Graph_d.h:619
node newNode()
Creates a new node and returns it.
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:592
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
void clear()
Removes all elements from the list.
Definition List.h:1610
iterator pushBack(const E &x)
Adds element x at the end of the list.
Definition List.h:1531
void del(iterator it)
Removes it from the list.
Definition List.h:1595
Encapsulates a pointer to a list element.
Definition List.h:103
ListIteratorBase< E, isConst, isReverse > succ() const
Returns successor iterator.
Definition List.h:158
const_reference front() const
Returns a const reference to the first element.
Definition List.h:289
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:1264
Serves as an interface for various methods to compute or approximate minimum Steiner trees on undirec...
This class implements the minimum Steiner tree 2-approximation algorithm by Takahashi and Matsuyama w...
Dynamic arrays indexed with nodes.
Definition NodeArray.h:125
void init()
Reinitializes the array. Associates the array with no graph.
Definition NodeArray.h:266
Class for the representation of nodes.
Definition Graph_d.h:177
int degree() const
Returns the degree of the node (indegree + outdegree).
Definition Graph_d.h:220
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
Definition Graph_d.h:208
int index() const
Returns the (unique) node index.
Definition Graph_d.h:211
adjEntry firstAdj() const
Returns the first entry in the adjaceny list.
Definition Graph_d.h:223
Prioritized queue interface wrapper for heaps.
Implementation of a dual-ascent-based lower bound heuristic for Steiner tree problems.
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.
Computes Voronoi regions in an edge-weighted graph.
Definition Voronoi.h:46
Defines a queue for handling prioritized elements.
An implementation of the heavy path decomposition on trees.
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.
bool isConnected(const Graph &G)
Returns true iff G is connected.
int connectedComponents(const Graph &G, NodeArray< int > &component, List< node > *isolated=nullptr)
Computes the connected components of G and optionally generates a list of isolated nodes.
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:68
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition basic.h:41
static MultilevelBuilder * getDoubleFactoredZeroAdjustedMerger()
void updateMin(T &min, const T &newValue)
Stores the minimum of min and newValue in min.
Definition Math.h:98
void updateMax(T &max, const T &newValue)
Stores the maximum of max and newValue in max.
Definition Math.h:90
T constructTerminalSpanningTreeUsingVoronoiRegions(EdgeWeightedGraphCopy< T > &terminalSpanningTree, const EdgeWeightedGraph< T > &graph, const List< node > &terminals)
The namespace for all OGDF objects.