Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

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