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
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.