Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

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 
46 namespace ogdf {
47 
48 // Helpers:
49 namespace steiner_tree {
55 public:
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 
68 public:
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 
88 template<typename T>
90 protected:
95 
99 
101 
110  std::vector<std::vector<int>> m_sonsList;
111 
113  std::unique_ptr<MinSteinerTreeModule<T>> m_costUpperBoundAlgorithm;
114 
115 public:
121  SteinerTreePreprocessing(const EdgeWeightedGraph<T>& wg, const List<node>& terminals,
122  const NodeArray<bool>& isTerminal);
123 
133  finalSteinerTree = new EdgeWeightedGraphCopy<T>;
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
138  EdgeWeightedGraphCopy<T>* reducedSolution = nullptr;
139  T cost = mst.call(m_copyGraph, m_copyTerminals, m_copyIsTerminal, reducedSolution);
140  cost += costEdgesAlreadyInserted();
141  computeOriginalSolution(*reducedSolution, *finalSteinerTree);
142  delete reducedSolution;
143  return cost;
144  }
145 
146  // make a compact copy
147  EdgeWeightedGraph<T> ccGraph;
148  NodeArray<node> nCopy(m_copyGraph, nullptr);
149  EdgeArray<edge> eCopy(m_copyGraph, nullptr);
150  List<node> ccTerminals;
151  NodeArray<bool> ccIsTerminal(ccGraph, false);
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
165  EdgeWeightedGraphCopy<T>* ccSolution = nullptr;
166  T cost = mst.call(ccGraph, ccTerminals, ccIsTerminal, ccSolution);
167  cost += costEdgesAlreadyInserted();
168 
169  // get reduced and original solution from compact copy solution
170  EdgeWeightedGraphCopy<T> reducedSolution;
171  reducedSolution.setOriginalGraph(m_copyGraph);
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 
184  computeOriginalSolution(reducedSolution, *finalSteinerTree);
185  return cost;
186  }
187 
192  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 
211  inline T costEdgesAlreadyInserted() const { return m_costAlreadyInserted; }
214 
220  void computeOriginalSolution(const EdgeWeightedGraphCopy<T>& reducedGraphSolution,
221  EdgeWeightedGraphCopy<T>& correspondingOriginalSolution);
222 
224 
229 
232  bool reduceTrivial() {
233  return repeat([this]() {
234  bool changed = false;
235  changed |= degree2Test();
236  changed |= makeSimple();
237  changed |= deleteLeaves();
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 
248  bool changed = deleteComponentsWithoutTerminals();
249  bool triviallyChanged = false;
250  changed |= repeat([this, &triviallyChanged, k]() {
251  bool innerChanged = false;
252  triviallyChanged = reduceTrivial();
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
272  innerChanged |= shortLinksTest();
273  // can occur: parallel edges, self-loops
274 
275  // precond: connected
276  innerChanged |= lowerBoundBasedTest();
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();
291  changed |= dualAscentBasedTest();
292  return changed;
293  });
294  }
295 
297 
302 
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 
362  bool terminalDistanceTest();
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 
420  inline bool lowerBoundBasedTest() {
422  }
423 
433  bool dualAscentBasedTest(int repetitions, T upperBound);
434 
439  inline bool dualAscentBasedTest(int repetitions = 1) {
441  }
442 
453  bool reachabilityTest(int maxDegreeTest = 0, const int k = 3);
454 
463  bool cutReachabilityTest();
464 
466 
471  inline void setCostUpperBoundAlgorithm(MinSteinerTreeModule<T>* pMinSteinerTreeModule) {
474  m_costUpperBoundAlgorithm.reset(pMinSteinerTreeModule);
475  }
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 
488 protected:
497  template<typename TWhat, typename TWhatArray>
498  void addNew(TWhat x, const std::vector<node>& replacedNodes,
499  const std::vector<edge>& replacedEdges, bool deleteReplacedElements,
500  TWhatArray& whatSonsListIndex);
501 
503  inline void addNewNode(node v, const std::vector<node>& replacedNodes,
504  const std::vector<edge>& replacedEdges, bool deleteReplacedElements) {
505  addNew(v, replacedNodes, replacedEdges, deleteReplacedElements, m_nodeSonsListIndex);
506  }
507 
509  inline void addNewEdge(edge e, const std::vector<node>& replacedNodes,
510  const std::vector<edge>& replacedEdges, bool deleteReplacedElements) {
511  addNew(e, replacedNodes, replacedEdges, deleteReplacedElements, m_edgeSonsListIndex);
512  }
513 
520  bool addEdgesToSolution(const List<edge>& edgesToBeAddedInSolution);
521 
525  void recomputeTerminalsList();
526 
528  void computeShortestPathMatrix(NodeArray<NodeArray<T>>& shortestPath) const;
529 
531  void floydWarshall(NodeArray<NodeArray<T>>& shortestPath) const;
532 
533  inline T computeMinSteinerTreeUpperBound(EdgeWeightedGraphCopy<T>*& finalSteinerTree) const {
534  finalSteinerTree = nullptr;
536  finalSteinerTree);
537  }
538 
539  // for convenience:
541  EdgeWeightedGraphCopy<T>* finalSteinerTree;
542  T treeCostUpperBound = computeMinSteinerTreeUpperBound(finalSteinerTree);
543  delete finalSteinerTree;
544  return treeCostUpperBound;
545  }
546 
548  void addToSolution(const int listIndex, Array<bool, int>& isInSolution) const;
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 
569  void findClosestNonTerminals(node source, List<node>& reachedNodes, NodeArray<T>& distance,
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 
589  void computeRadiusOfTerminals(NodeArray<T>& terminalRadius) const;
590 
592  T computeRadiusSum() const;
593 
595  template<typename LAMBDA>
596  void computeOptimalTerminals(node v, LAMBDA dist, node& optimalTerminal1,
597  node& optimalTerminal2, NodeArray<T>& distance) const;
598 
600  void markSuccessors(node currentNode, const Voronoi<T>& voronoiRegions,
601  NodeArray<bool>& isSuccessorOfMinCostEdge) const;
602 
604  void findTwoMinimumCostEdges(node v, edge& first, edge& second) const;
605 };
606 
607 template<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();
627  m_copyIsTerminal.init(m_copyGraph, false);
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) {
642  m_nodeSonsListIndex[nCopy[v]] = -(nextIndex++);
643  }
644  for (edge e : m_origGraph.edges) {
645  m_edgeSonsListIndex[eCopy[e]] = -(nextIndex++);
646  }
647 
648  // set the default approximation algorithm for computing the upper bound cost of the solution
650 }
651 
652 template<typename T>
653 template<typename TWhat, typename TWhatArray>
654 void SteinerTreePreprocessing<T>::addNew(TWhat x, const std::vector<node>& replacedNodes,
655  const std::vector<edge>& replacedEdges, bool deleteReplacedElements,
656  TWhatArray& whatSonsListIndex) {
657  std::vector<int> sonsIndexList;
658  for (node replacedNode : replacedNodes) {
659  sonsIndexList.push_back(m_nodeSonsListIndex[replacedNode]);
660  }
661  for (edge replacedEdge : replacedEdges) {
662  sonsIndexList.push_back(m_edgeSonsListIndex[replacedEdge]);
663  }
664 
665  m_sonsList.emplace_back(sonsIndexList);
666  whatSonsListIndex[x] = (int)m_sonsList.size() - 1;
667 
668  if (deleteReplacedElements) {
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 
678 template<typename T>
680  Array<bool, int>& isInSolution) const {
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 
691 template<typename T>
693  const EdgeWeightedGraphCopy<T>& reducedGraphSolution,
694  EdgeWeightedGraphCopy<T>& correspondingOriginalSolution) {
695  correspondingOriginalSolution.setOriginalGraph(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++)]) {
712  correspondingOriginalSolution.newNode(v);
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 
722 template<typename T>
724  const EdgeWeightedGraphCopy<T>& tprime,
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,
751  tprimeHPD, closestTerminals);
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)
762  edge newEdgeInGraph;
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 
777 template<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 
787 template<typename T>
789  const EdgeWeightedGraphCopy<T>& tprime,
791  const NodeArray<List<std::pair<node, T>>>& closestTerminals) const {
792  T bottleneckDistance = std::numeric_limits<T>::max();
793 
794  for (auto& xCloseTerminalDistancePair : closestTerminals[x]) {
795  for (auto& yCloseTerminalDistancePair : closestTerminals[y]) {
796  T possibleBottleneckDistance =
797  xCloseTerminalDistancePair.second + yCloseTerminalDistancePair.second;
798 
799  node xTprimeCopy = tprime.copy(xCloseTerminalDistancePair.first);
800  node yTprimeCopy = tprime.copy(yCloseTerminalDistancePair.first);
801  possibleBottleneckDistance +=
802  tprimeHPD.getBottleneckSteinerDistance(xTprimeCopy, yTprimeCopy);
803 
804  Math::updateMin(bottleneckDistance, possibleBottleneckDistance);
805  }
806  }
807 
808  return bottleneckDistance;
809 }
810 
811 template<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
832  BoundedQueue<node> eraseQueue(m_copyGraph.numberOfNodes());
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 
883 template<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 
921 template<typename T>
923  for (node v1 : m_copyGraph.nodes) {
924  for (adjEntry adj : v1->adjEntries) {
925  node v2 = adj->twinNode();
926  shortestPath[v1][v2] = shortestPath[v2][v1] =
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 
939  Math::updateMin(shortestPath[v1][v2],
940  shortestPath[v1][pivot] + shortestPath[pivot][v2]);
941  }
942  }
943  }
944 }
945 
946 template<typename T>
948  NodeArray<NodeArray<T>>& shortestPath) const {
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 
957 template<typename T>
959  OGDF_ASSERT(!m_copyGraph.empty());
960  bool changed = false;
961  NodeArray<NodeArray<T>> shortestPath;
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 
979 template<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 
1008 template<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) {
1015  if (componentWithTerminals != -1 && hisConnectedComponent[v] != componentWithTerminals) {
1016  std::cerr << "terminals in different connected components!\n";
1017  OGDF_ASSERT(false);
1018  }
1019  componentWithTerminals = hisConnectedComponent[v];
1020  }
1021 
1022  for (node v = m_copyGraph.firstNode(), nextV; v; v = nextV) {
1023  nextV = v->succ();
1024  if (hisConnectedComponent[v] != componentWithTerminals) {
1025  m_copyGraph.delNode(v);
1026  }
1027  }
1028  }
1029  return changed;
1030 }
1031 
1032 template<typename T>
1034  OGDF_ASSERT(!m_copyGraph.empty());
1035  OGDF_ASSERT(isConnected(m_copyGraph));
1036  bool changed = false;
1037 
1038  EdgeWeightedGraphCopy<T> tprime;
1040  m_copyTerminals);
1041  T maxBottleneck(0);
1042  for (edge e : tprime.edges) {
1043  Math::updateMax(maxBottleneck, tprime.weight(e));
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 
1058 template<typename T>
1060  NodeArray<T>& distance, T maxDistance, int expandedEdges) const {
1061  PrioritizedMapQueue<node, T> queue(m_copyGraph);
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 
1080  node adjacentNode = e->opposite(currentNode);
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
1090  queue.decrease(adjacentNode, possibleDistance);
1091  } else {
1092  queue.push(adjacentNode, distance[adjacentNode]);
1093  }
1094  }
1095  }
1096  }
1097 }
1098 
1099 template<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();
1108  List<node> xReachedNodes, yReachedNodes;
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 
1124  for (node reachedNode : xReachedNodes) {
1125  xDistance[reachedNode] = std::numeric_limits<T>::max();
1126  }
1127  for (node reachedNode : yReachedNodes) {
1128  yDistance[reachedNode] = std::numeric_limits<T>::max();
1129  }
1130  }
1131  return changed;
1132 }
1133 
1134 template<typename T>
1136  NodeArray<List<std::pair<node, T>>>& closestTerminals) const {
1137  using NodePairQueue = PrioritizedQueue<NodePair, T>;
1138 
1139  closestTerminals.init(m_copyGraph);
1140  NodePairQueue queue;
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 
1151  auto getCurrentDist = [&closestTerminals](const node currentNode, const node sourceTerminal) {
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 
1160  auto setNewDist = [&closestTerminals, k](const node currentNode, const node sourceTerminal,
1161  const T newDist) {
1162  List<std::pair<node, T>>& currentList = closestTerminals[currentNode];
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 
1182  ListIterator<std::pair<node, T>> it = closestTerminals[currentNode].begin();
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;
1193  node sourceTerminal = minDistPair.target;
1194 
1195  T currentDist = getCurrentDist(currentNode, sourceTerminal);
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();
1202  node adjacentNode = e->opposite(currentNode);
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
1211  T currentDistToAdjacentNode = getCurrentDist(adjacentNode, sourceTerminal);
1212  if (currentDistToAdjacentNode
1213  != -1) { // there is already one path from sourceTerminal to adjNode
1214  if (possibleNewDistance < currentDistToAdjacentNode) {
1215  queue.decrease(qpos[NodePair(adjacentNode, sourceTerminal)], possibleNewDistance);
1216  setNewDist(adjacentNode, sourceTerminal, possibleNewDistance);
1217  }
1218  } else {
1219  if (closestTerminals[adjacentNode].size() < k
1220  || closestTerminals[adjacentNode].back().second > possibleNewDistance) {
1221  qpos[NodePair(adjacentNode, sourceTerminal)] =
1222  queue.push(NodePair(adjacentNode, sourceTerminal), possibleNewDistance);
1223  setNewDist(adjacentNode, sourceTerminal, possibleNewDistance);
1224  }
1225  }
1226  }
1227  }
1228 }
1229 
1230 template<typename T>
1232  OGDF_ASSERT(!m_copyGraph.empty());
1233  OGDF_ASSERT(isConnected(m_copyGraph));
1234  bool changed = false;
1235 
1236  EdgeWeightedGraphCopy<T> tprime;
1238  m_copyTerminals);
1239 
1240  NodeArray<List<std::pair<node, T>>> closestTerminals;
1241  computeClosestKTerminals(k, closestTerminals);
1242 
1243  steiner_tree::HeavyPathDecomposition<T> tprimeHPD(tprime);
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,
1249  tprimeHPD, closestTerminals);
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 
1260 template<typename T>
1261 bool SteinerTreePreprocessing<T>::NTDkTest(const int maxTestedDegree, const int k) {
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 
1271  EdgeWeightedGraphCopy<T> tprime;
1273  m_copyTerminals);
1274 
1275  NodeArray<List<std::pair<node, T>>> closestTerminals;
1276  computeClosestKTerminals(k, closestTerminals);
1277 
1278  steiner_tree::HeavyPathDecomposition<T> tprimeHPD(tprime);
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
1291  List<adjEntry> outgoingAdjs;
1292  for (adjEntry adj : v->adjEntries) {
1293  outgoingAdjs.pushBack(adj);
1294  }
1295  SubsetEnumerator<adjEntry> neighborSubset(outgoingAdjs);
1296 
1297  bool deleteNode = true;
1298  for (neighborSubset.begin(3, v->degree()); neighborSubset.valid() && deleteNode;
1299  neighborSubset.next()) {
1300  Graph auxGraph;
1301  std::unordered_map<node, node> initNodeToAuxGraph;
1302  std::unordered_map<node, node> auxGraphToInitNode;
1303 
1304  T sumToSelectedAdjacentNodes = 0;
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 
1311  OGDF_ASSERT(initNodeToAuxGraph.find(adjacentNode) == initNodeToAuxGraph.end());
1312 
1313  node newAuxGraphNode = auxGraph.newNode();
1314  initNodeToAuxGraph[adjacentNode] = newAuxGraphNode;
1315  auxGraphToInitNode[newAuxGraphNode] = adjacentNode;
1316  }
1317 
1318  EdgeArray<T> weight(auxGraph, 0);
1319  for (node auxGraphNode1 : auxGraph.nodes) {
1320  for (node auxGraphNode2 = auxGraphNode1->succ(); auxGraphNode2;
1321  auxGraphNode2 = auxGraphNode2->succ()) {
1322  edge e = auxGraph.newEdge(auxGraphNode1, auxGraphNode2);
1323  weight[e] = computeBottleneckDistance(auxGraphToInitNode[auxGraphNode1],
1324  auxGraphToInitNode[auxGraphNode2], tprime, tprimeHPD, closestTerminals);
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 
1332  if (sumToSelectedAdjacentNodes < mstCost) {
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 
1346 template<typename T>
1347 void SteinerTreePreprocessing<T>::markSuccessors(node currentNode, const Voronoi<T>& voronoiRegions,
1348  NodeArray<bool>& isSuccessorOfMinCostEdge) const {
1349  isSuccessorOfMinCostEdge[currentNode] = true;
1350 
1351  OGDF_ASSERT(voronoiRegions.seed(currentNode) != currentNode);
1352 
1353  // recurse for every successor
1354  for (adjEntry adj : currentNode->adjEntries) {
1355  edge e = adj->theEdge();
1356  node adjacentNode = e->opposite(currentNode);
1357 
1358  if (voronoiRegions.predecessor(adjacentNode) == currentNode) {
1359  markSuccessors(adjacentNode, voronoiRegions, isSuccessorOfMinCostEdge);
1360  }
1361  }
1362 }
1363 
1364 template<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 
1390 template<typename T>
1391 bool SteinerTreePreprocessing<T>::addEdgesToSolution(const List<edge>& edgesToBeAddedInSolution) {
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 
1409 template<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) {
1435  markSuccessors(closestNode, voronoiRegions, isSuccessorOfMinCostEdge);
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)
1447  T distanceThroughE =
1448  voronoiRegions.distance(x) + m_copyGraph.weight(e) + voronoiRegions.distance(y);
1449 
1450  if (isSuccessorOfMinCostEdge[x]) {
1451  Math::updateMin(distanceToClosestTerminal[seedX], distanceThroughE);
1452  }
1453  if (isSuccessorOfMinCostEdge[y]) {
1454  Math::updateMin(distanceToClosestTerminal[seedY], distanceThroughE);
1455  }
1456  }
1457  }
1458 
1459  // see what edges can be added in solution (Observation 3.2)
1460  List<edge> edgesToBeAddedInSolution;
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 
1481 template<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)) {
1496  minCostLeavingRegionEdge2[seed] = minCostLeavingRegionEdge1[seed];
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
1507  updateMinCostLeavingRegionsFor(seedX);
1508  updateMinCostLeavingRegionsFor(seedY);
1509  }
1510  }
1511 
1512  List<edge> edgesToBeAddedInSolution;
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))
1524  && !willBeAddedInSolution[e1]) {
1525  edgesToBeAddedInSolution.pushBack(e1);
1526  willBeAddedInSolution[e1] = true;
1527  }
1528  }
1529 
1530  return addEdgesToSolution(edgesToBeAddedInSolution);
1531 }
1532 
1533 template<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) {
1551  Math::updateMin(terminalRadius[seedV], distanceToSeedV + m_copyGraph.weight(e));
1552  }
1553  }
1554  }
1555 }
1556 
1557 template<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 
1572 template<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 
1583  NodeArray<List<std::pair<node, T>>> closestTerminals;
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)),
1600  closestTerminalPair2 = *(closestTerminals[v].get(1));
1601  T distanceToClosestTerminal1 = closestTerminalPair1.second,
1602  distanceToClosestTerminal2 = closestTerminalPair2.second;
1603 
1604  Math::updateMax(lowerBoundWithNode[v],
1605  distanceToClosestTerminal1 + distanceToClosestTerminal2 + radiusSum);
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 
1613  T distanceToClosestTerminalX = (*closestTerminals[x].begin()).second;
1614  T distanceToClosestTerminalY = (*closestTerminals[y].begin()).second;
1615 
1616  Math::updateMax(lowerBoundWithEdge[e],
1617  m_copyGraph.weight(e) + distanceToClosestTerminalX + distanceToClosestTerminalY
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();
1627  terminalInAuxiliaryGraph[terminal] = newAuxiliaryNode;
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 
1638  edgeBetweenNodes;
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 
1647  auto pair = NodePair(terminalInAuxiliaryGraph[seedX], terminalInAuxiliaryGraph[seedY]);
1648  if (edgeBetweenNodes.find(pair) == edgeBetweenNodes.end()) {
1649  edgeBetweenNodes[pair] = auxiliaryGraph.newEdge(terminalInAuxiliaryGraph[seedX],
1650  terminalInAuxiliaryGraph[seedY]);
1651  }
1652  edge auxiliaryEdge = edgeBetweenNodes[pair];
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)),
1672  closestTerminalPair2 = *(closestTerminals[v].get(1));
1673  T distanceToClosestTerminal1 = closestTerminalPair1.second,
1674  distanceToClosestTerminal2 = closestTerminalPair2.second;
1675 
1676  Math::updateMax(lowerBoundWithNode[v],
1677  minimumSpanningTreeCost - longestEdgeCost + distanceToClosestTerminal1
1678  + distanceToClosestTerminal2);
1679  }
1680 
1681  return deleteNodesAboveUpperBound(lowerBoundWithNode, upperBound)
1682  || deleteEdgesAboveUpperBound(lowerBoundWithEdge, upperBound);
1683 }
1684 
1685 template<typename T>
1686 bool SteinerTreePreprocessing<T>::dualAscentBasedTest(int repetitions, T upperBound) {
1687  OGDF_ASSERT(!m_copyGraph.empty());
1688  bool changed = false;
1689  if (m_copyTerminals.size() > 1) {
1690  NodeArray<T> lowerBoundNodes;
1691  EdgeArray<T> lowerBoundEdges;
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 
1702 template<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 
1716 template<typename T>
1718  NodeArray<T> terminalRadius;
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) {
1729  largestRadius2 = largestRadius1;
1730  largestRadius1 = terminalRadius[terminal];
1731  } else {
1732  if (terminalRadius[terminal] > largestRadius2) {
1733  largestRadius2 = terminalRadius[terminal];
1734  }
1735  }
1736  }
1737  radiusSum -= largestRadius1 + largestRadius2;
1738  return radiusSum;
1739 }
1740 
1741 template<typename T>
1742 bool SteinerTreePreprocessing<T>::reachabilityTest(int maxDegreeTest, const int k) {
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 
1751  EdgeWeightedGraphCopy<T>* approximatedSteinerTree;
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  }
1758  delete approximatedSteinerTree;
1759 
1760  // Initialize tprime and its hpd decomposition used for partially not adding useless edges during nodes' deletion
1761  EdgeWeightedGraphCopy<T> tprime;
1763  m_copyTerminals);
1764  OGDF_ASSERT(!tprime.empty());
1765 
1766  NodeArray<List<std::pair<node, T>>> closestTerminals;
1767  computeClosestKTerminals(k, closestTerminals);
1768 
1769  steiner_tree::HeavyPathDecomposition<T> tprimeHPD(tprime);
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
1780  Dijkstra<T> dijkstra;
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;
1788  T distanceToFarthestTerminal(0);
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]) {
1798  distanceToClosestTerminal2 = distanceToClosestTerminal1;
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
1808  || distanceToClosestTerminal2
1809  == std::numeric_limits<T>::max() // cannot reach at least 2 terminals, must be deleted
1810  || m_eps.geq(distanceToFarthestTerminal + distanceToClosestTerminal1
1811  + distanceToClosestTerminal2,
1812  upperBoundCost)) {
1813  changed = true;
1814  // delete the node
1815  if (predecessor[farthestTerminal] != nullptr
1816  && distanceToClosestTerminal2 != std::numeric_limits<T>::max()
1817  && m_eps.less(distanceToFarthestTerminal + distanceToClosestTerminal1,
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 
1831 template<typename T>
1832 template<typename LAMBDA>
1834  node& optimalTerminal1, node& optimalTerminal2, NodeArray<T>& distance) const {
1835  // run Dijkstra starting from v
1836  Dijkstra<T> dijkstra;
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)) {
1849  optimalTerminal2 = optimalTerminal1;
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);
1860  OGDF_ASSERT(optimalTerminal1 != optimalTerminal2);
1861 }
1862 
1863 template<typename T>
1865  OGDF_ASSERT(!m_copyGraph.empty());
1866  if (m_copyTerminals.size() <= 2) {
1867  return false;
1868  }
1869 
1870  // get the upper bound
1871  EdgeWeightedGraphCopy<T>* approximatedSteinerTree;
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  }
1878  delete approximatedSteinerTree;
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 
1892  List<node> delNodes;
1893  std::set<edge> delEdges;
1894  NodeArray<T> vDistance;
1895  NodeArray<T> wDistance;
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
1908  if (m_eps.geq(cK + dist(vOptimalTerminal1, vDistance) + dist(vOptimalTerminal2, vDistance),
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 
1921  node vOptimalTerminal = vOptimalTerminal1;
1922  node wOptimalTerminal = wOptimalTerminal1;
1923  if (vOptimalTerminal == wOptimalTerminal) {
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.
1926  if (m_eps.leq(dist(vOptimalTerminal1, vDistance)
1927  + dist(wOptimalTerminal2, wDistance),
1928  dist(vOptimalTerminal2, vDistance)
1929  + dist(wOptimalTerminal1, wDistance))) {
1930  wOptimalTerminal = wOptimalTerminal2;
1931  } else {
1932  vOptimalTerminal = vOptimalTerminal2;
1933  }
1934  }
1935  OGDF_ASSERT(vOptimalTerminal != wOptimalTerminal);
1936  if (m_eps.geq(cK + dist(vOptimalTerminal, vDistance) + dist(wOptimalTerminal, wDistance)
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 }
ogdf
The namespace for all OGDF objects.
Definition: AugmentationModule.h:36
ogdf::SubsetEnumerator::size
int size() const
Returns the cardinality of the subset.
Definition: SubsetEnumerator.h:137
ogdf::Graph::empty
bool empty() const
Returns true iff the graph is empty, i.e., contains no nodes.
Definition: Graph_d.h:971
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:1059
ogdf::EdgeWeightedGraph::newEdge
edge newEdge(node v, node w, T weight)
Definition: EdgeWeightedGraph.h:47
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:1365
ogdf::EpsilonTest
Definition: EpsilonTest.h:41
ogdf::SubsetEnumerator
Enumerator for k-subsets of a given type.
Definition: SubsetEnumerator.h:88
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:213
OGDF_ASSERT
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition: basic.h:54
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:339
ogdf::EdgeWeightedGraph::weight
T weight(const edge e) const
Definition: EdgeWeightedGraph.h:58
ogdf::Graph::delNode
virtual void delNode(node v)
Removes node v and all incident edges from the graph.
ogdf::SteinerTreePreprocessing::dualAscentBasedTest
bool dualAscentBasedTest(int repetitions, T upperBound)
Like lowerBoundBasedTest(T upperBound) but uses ogdf::SteinerTreeLowerBoundDualAscent to compute lowe...
Definition: SteinerTreePreprocessing.h:1686
ogdf::SteinerTreePreprocessing::reduceFastAndDualAscent
bool reduceFastAndDualAscent()
Apply fast reductions and the dual-ascent-based tests iteratively.
Definition: SteinerTreePreprocessing.h:288
ogdf::SteinerTreePreprocessing::reduceTrivial
bool reduceTrivial()
Apply trivial (hence amazingly fast) reductions iteratively, that is degree2Test(),...
Definition: SteinerTreePreprocessing.h:232
ogdf::SteinerTreePreprocessing::shuffleReducedTerminals
void shuffleReducedTerminals()
Shuffles the list of reduced terminals. This can have an effect on some tests.
Definition: SteinerTreePreprocessing.h:200
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:331
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:473
ogdf::NodeElement::lastAdj
adjEntry lastAdj() const
Returns the last entry in the adjacency list.
Definition: Graph_d.h:282
ogdf::NodeElement::index
int index() const
Returns the (unique) node index.
Definition: Graph_d.h:267
ogdf::SteinerTreePreprocessing::m_origTerminals
const List< node > & m_origTerminals
Const reference to the original list of terminals.
Definition: SteinerTreePreprocessing.h:92
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:347
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:158
ogdf::NodePair
Definition: Graph_d.h:2101
ogdf::SteinerTreePreprocessing::m_origGraph
const EdgeWeightedGraph< T > & m_origGraph
Const reference to the original graph.
Definition: SteinerTreePreprocessing.h:91
ogdf::SubsetEnumerator::begin
void begin(int low, int high)
Initializes the SubsetEnumerator to enumerate subsets of cardinalities from low to high.
Definition: SubsetEnumerator.h:120
ogdf::SteinerTreePreprocessing::NTDkTest
bool NTDkTest(const int maxTestedDegree=5, const int k=3)
Non-terminals of degree k test [DV89, PV01].
Definition: SteinerTreePreprocessing.h:1261
ogdf::Voronoi
Computes Voronoi regions in an edge-weighted graph.
Definition: Voronoi.h:46
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:145
ogdf::SteinerTreePreprocessing::computeShortestPathMatrix
void computeShortestPathMatrix(NodeArray< NodeArray< T >> &shortestPath) const
Computes the shortest path matrix corresponding to the m_copyGraph.
Definition: SteinerTreePreprocessing.h:947
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:788
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:107
ogdf::steiner_tree::UnorderedNodePairHasher::operator()
int operator()(const NodePair &v) const
Definition: SteinerTreePreprocessing.h:56
ogdf::EdgeElement::index
int index() const
Returns the index of the edge.
Definition: Graph_d.h:388
ogdf::NodePair::source
node source
Definition: Graph_d.h:2102
ogdf::SteinerTreePreprocessing::computeRadiusSum
T computeRadiusSum() const
Compute the sum of all radii except the two largest.
Definition: SteinerTreePreprocessing.h:1717
ogdf::steiner_tree::UnorderedNodePairHasher
A class used by the unordered_maps inside the reductions.
Definition: SteinerTreePreprocessing.h:54
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:654
ogdf::EdgeWeightedGraphCopy::newEdge
edge newEdge(node u, node v, T weight)
Definition: EdgeWeightedGraphCopy.h:165
ogdf::SteinerTreePreprocessing::longEdgesTest
bool longEdgesTest()
Long-Edges test from [DV89].
Definition: SteinerTreePreprocessing.h:1100
ogdf::SteinerTreePreprocessing::computeMinSteinerTreeUpperBound
T computeMinSteinerTreeUpperBound(EdgeWeightedGraphCopy< T > *&finalSteinerTree) const
Definition: SteinerTreePreprocessing.h:533
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:1135
ogdf::SteinerTreePreprocessing::getReducedTerminals
const List< node > & getReducedTerminals() const
Returns the list of the terminals corresponding to the reduced graph.
Definition: SteinerTreePreprocessing.h:197
ogdf::SteinerTreePreprocessing::nearestVertexTest
bool nearestVertexTest()
Nearest vertex test using Voronoi regions [DV89, PV01].
Definition: SteinerTreePreprocessing.h:1410
ogdf::SteinerTreePreprocessing::cutReachabilityTest
bool cutReachabilityTest()
Performs a cut reachability test [DV89].
Definition: SteinerTreePreprocessing.h:1864
ogdf::BoundedQueue::empty
bool empty()
Returns true iff the queue is empty.
Definition: BoundedQueue.h:148
ogdf::pq_internal::PrioritizedQueue
Defines a queue for handling prioritized elements.
Definition: PriorityQueue.h:269
ogdf::Graph::delEdge
virtual void delEdge(edge e)
Removes edge e from the graph.
ogdf::SteinerTreeLowerBoundDualAscent
Implementation of a dual-ascent-based lower bound heuristic for Steiner tree problems.
Definition: SteinerTreeLowerBoundDualAscent.h:252
ogdf::steiner_tree::constructTerminalSpanningTreeUsingVoronoiRegions
T constructTerminalSpanningTreeUsingVoronoiRegions(EdgeWeightedGraphCopy< T > &terminalSpanningTree, const EdgeWeightedGraph< T > &graph, const List< node > &terminals)
Definition: MinSteinerTreeMehlhorn.h:295
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:316
ogdf::AdjElement
Class for adjacency list elements.
Definition: Graph_d.h:135
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:57
ogdf::SteinerTreePreprocessing::deleteLeaves
bool deleteLeaves()
Deletes the leaves of the graph.
Definition: SteinerTreePreprocessing.h:812
ogdf::MinSteinerTreeModule
Serves as an interface for various methods to compute or approximate minimum Steiner trees on undirec...
Definition: MinSteinerTreeModule.h:59
ogdf::SteinerTreePreprocessing::m_eps
const EpsilonTest m_eps
Definition: SteinerTreePreprocessing.h:94
ogdf::SteinerTreePreprocessing::computeRadiusOfTerminals
void computeRadiusOfTerminals(NodeArray< T > &terminalRadius) const
Compute radius of terminals.
Definition: SteinerTreePreprocessing.h:1534
ogdf::ListIteratorBase::succ
ListIteratorBase< E, isConst, isReverse > succ() const
Returns successor iterator.
Definition: List.h:164
ogdf::Dijkstra
Dijkstra's single source shortest path algorithm.
Definition: Dijkstra.h:53
ogdf::edge
EdgeElement * edge
The type of edges.
Definition: Graph_d.h:67
ogdf::BoundedQueue::pop
E pop()
Removes front element and returns it.
Definition: BoundedQueue.h:190
ogdf::AdjElement::theEdge
edge theEdge() const
Returns the edge associated with this adjacency entry.
Definition: Graph_d.h:153
ogdf::SteinerTreePreprocessing::deleteEdgesAboveUpperBound
bool deleteEdgesAboveUpperBound(const EdgeArray< T > &lowerBoundWithEdge, const T upperBound)
Deletes the edges whose lowerBoundWithEdge exceeds upperBound.
Definition: SteinerTreePreprocessing.h:1703
ogdf::PriorityQueue::empty
bool empty() const
Checks whether the queue is empty.
Definition: PriorityQueue.h:209
ogdf::Graph::nodes
internal::GraphObjectContainer< NodeElement > nodes
The container containing all node objects.
Definition: Graph_d.h:924
ogdf::Graph::numberOfEdges
int numberOfEdges() const
Returns the number of edges in the graph.
Definition: Graph_d.h:977
ogdf::SteinerTreePreprocessing::deleteComponentsWithoutTerminals
bool deleteComponentsWithoutTerminals()
Deletes connected components with no terminals.
Definition: SteinerTreePreprocessing.h:1009
ogdf::NodeElement::degree
int degree() const
Returns the degree of the node (indegree + outdegree).
Definition: Graph_d.h:276
ogdf::List::pushFront
iterator pushFront(const E &x)
Adds element x at the beginning of the list.
Definition: List.h:1524
ogdf::SteinerTreePreprocessing::makeSimple
bool makeSimple()
Deletes parallel edges keeping only the minimum cost one, and deletes self-loops.
Definition: SteinerTreePreprocessing.h:884
ogdf::Graph::numberOfNodes
int numberOfNodes() const
Returns the number of nodes in the graph.
Definition: Graph_d.h:974
ogdf::EdgeWeightedGraph
Definition: EdgeWeightedGraph.h:39
ogdf::Array
The parameterized class Array implements dynamic arrays of type E.
Definition: Array.h:214
ogdf::AdjElement::succ
adjEntry succ() const
Returns the successor in the adjacency list.
Definition: Graph_d.h:211
ogdf::PrioritizedMapQueue
Prioritized queue interface wrapper for heaps.
Definition: PriorityQueue.h:409
ogdf::NodeElement::adjEntries
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
Definition: Graph_d.h:264
ogdf::BoundedQueue
The parameterized class BoundedQueue implements queues with bounded size.
Definition: BoundedQueue.h:47
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:509
ogdf::EdgeWeightedGraph::edgeWeights
const EdgeArray< T > & edgeWeights() const
Definition: EdgeWeightedGraph.h:60
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:246
ogdf::NodePair::target
node target
Definition: Graph_d.h:2103
ogdf::Graph::firstNode
node firstNode() const
Returns the first node in the list of all nodes.
Definition: Graph_d.h:989
ogdf::SteinerTreePreprocessing::lowerBoundBasedTest
bool lowerBoundBasedTest()
Like lowerBoundBasedTest(T upperBound) but the upper bound is computed by computeMinSteinerTreeUpperB...
Definition: SteinerTreePreprocessing.h:420
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:692
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:439
ogdf::SteinerTreePreprocessing::shortLinksTest
bool shortLinksTest()
Short links test using Voronoi regions [PV01].
Definition: SteinerTreePreprocessing.h:1482
ogdf::EdgeElement::source
node source() const
Returns the source node of the edge.
Definition: Graph_d.h:391
ogdf::GraphCopyBase::newNode
node newNode(node vOrig)
Creates a new node in the graph copy with original node vOrig.
Definition: GraphCopy.h:185
ogdf::Math::updateMin
void updateMin(T &min, const T &newValue)
Stores the minimum of min and newValue in min.
Definition: Math.h:98
ogdf::SteinerTreeLowerBoundDualAscent::call
T call(const EdgeWeightedGraph< T > &graph, const List< node > &terminals)
Calls the algorithm and returns the lower bound.
Definition: SteinerTreeLowerBoundDualAscent.h:336
ogdf::List
Doubly linked lists (maintaining the length of the list).
Definition: List.h:42
ogdf::internal::GraphRegisteredArray
RegisteredArray for nodes, edges and adjEntries of a graph.
Definition: Graph_d.h:651
ogdf::Graph
Data type for general directed graphs (adjacency list representation).
Definition: Graph_d.h:862
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:161
ogdf::isSimpleUndirected
bool isSimpleUndirected(const Graph &G)
Returns true iff G contains neither self-loops nor undirected parallel edges.
Definition: simple_graph_alg.h:415
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:113
ogdf::SteinerTreePreprocessing::m_copyIsTerminal
NodeArray< bool > m_copyIsTerminal
The reduced form of isTerminal.
Definition: SteinerTreePreprocessing.h:98
ogdf::SteinerTreePreprocessing::m_copyGraph
EdgeWeightedGraph< T > m_copyGraph
Copy of the original graph; this copy will actually be reduced.
Definition: SteinerTreePreprocessing.h:96
ogdf::isLoopFree
bool isLoopFree(const Graph &G)
Returns true iff G contains no self-loop.
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:284
ogdf::AdjElement::twinNode
node twinNode() const
Returns the associated node of the corresponding adjacency entry (shorthand for twin()->theNode()).
Definition: Graph_d.h:168
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:457
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:83
ogdf::SteinerTreePreprocessing::computeMinSteinerTreeUpperBound
T computeMinSteinerTreeUpperBound() const
Definition: SteinerTreePreprocessing.h:540
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:386
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:679
ogdf::SteinerTreePreprocessing::m_copyTerminals
List< node > m_copyTerminals
The reduced form of terminals.
Definition: SteinerTreePreprocessing.h:97
ogdf::SteinerTreePreprocessing::deleteNodesAboveUpperBound
bool deleteNodesAboveUpperBound(const NodeArray< T > &lowerBoundWithNode, const T upperBound)
Deletes the nodes whose lowerBoundWithNode exceeds upperBound.
Definition: SteinerTreePreprocessing.h:1558
ogdf::SteinerTreePreprocessing::getReducedGraph
const EdgeWeightedGraph< T > & getReducedGraph() const
Returns the reduced form of the graph.
Definition: SteinerTreePreprocessing.h:194
ogdf::SteinerTreePreprocessing::leastCostTest
bool leastCostTest()
Performs a least cost test [DV89] computing the whole shortest path matrix.
Definition: SteinerTreePreprocessing.h:958
ogdf::Voronoi::distance
T distance(node v) const
Returns the distance between v and its Voronoi seed.
Definition: Voronoi.h:84
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:109
ogdf::SteinerTreePreprocessing::PTmTest
bool PTmTest(const int k=3)
"Paths with many terminals" test, efficient on paths with many terminals.
Definition: SteinerTreePreprocessing.h:1231
ogdf::Graph::firstEdge
edge firstEdge() const
Returns the first edge in the list of all edges.
Definition: Graph_d.h:995
ogdf::Graph::edges
internal::GraphObjectContainer< EdgeElement > edges
The container containing all edge objects.
Definition: Graph_d.h:927
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:349
ogdf::Math::updateMax
void updateMax(T &max, const T &newValue)
Stores the maximum of max and newValue in max.
Definition: Math.h:90
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:1833
ogdf::MinSteinerTreeTakahashi
This class implements the minimum Steiner tree 2-approximation algorithm by Takahashi and Matsuyama w...
Definition: MinSteinerTreeTakahashi.h:57
ogdf::List::del
void del(iterator it)
Removes it from the list.
Definition: List.h:1601
ogdf::SteinerTreePreprocessing::solve
T solve(MinSteinerTreeModule< T > &mst, EdgeWeightedGraphCopy< T > *&finalSteinerTree)
A shortcut to get the solution of a reduced instance.
Definition: SteinerTreePreprocessing.h:132
ogdf::List::popBack
void popBack()
Removes the last element from the list.
Definition: List.h:1588
ogdf::SteinerTreePreprocessing::m_costAlreadyInserted
T m_costAlreadyInserted
The cost of the already inserted in solution edges.
Definition: SteinerTreePreprocessing.h:100
ogdf::SteinerTreePreprocessing::reduceFast
bool reduceFast()
Apply fast reductions iteratively (includes trivial reductions).
Definition: SteinerTreePreprocessing.h:243
ogdf::steiner_tree::UnorderedNodePairEquality::operator()
bool operator()(const NodePair &pair1, const NodePair &pair2) const
Definition: SteinerTreePreprocessing.h:69
ogdf::Graph::newNode
node newNode(int index=-1)
Creates a new node and returns it.
Definition: Graph_d.h:1056
ogdf::SteinerTreePreprocessing::addEdgesToSolution
bool addEdgesToSolution(const List< edge > &edgesToBeAddedInSolution)
The function adds a set of edges in the solution.
Definition: SteinerTreePreprocessing.h:1391
ogdf::EdgeWeightedGraphCopy::weight
T weight(const edge e) const
Definition: EdgeWeightedGraphCopy.h:57
ogdf::NodeElement::firstAdj
adjEntry firstAdj() const
Returns the first entry in the adjaceny list.
Definition: Graph_d.h:279
ogdf::SteinerTreePreprocessing::m_edgeSonsListIndex
EdgeArray< int > m_edgeSonsListIndex
See m_nodeSonsListIndex but for edges.
Definition: SteinerTreePreprocessing.h:109
ogdf::NodeElement::succ
node succ() const
Returns the successor in the list of all nodes.
Definition: Graph_d.h:285
ogdf::EdgeWeightedGraphCopy
Definition: EdgeWeightedGraphCopy.h:40
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:110
ogdf::EdgeElement::opposite
node opposite(node v) const
Returns the adjacent node different from v.
Definition: Graph_d.h:406
ogdf::SteinerTreePreprocessing::m_nodeSonsListIndex
NodeArray< int > m_nodeSonsListIndex
It contains for each node an index i.
Definition: SteinerTreePreprocessing.h:102
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:503
ogdf::EdgeElement
Class for the representation of edges.
Definition: Graph_d.h:356
ogdf::Graph::searchEdge
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 ))).
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:723
ogdf::EdgeWeightedGraph::newNode
node newNode()
Definition: EdgeWeightedGraph.h:53
ogdf::EdgeElement::succ
edge succ() const
Returns the successor in the list of all edges.
Definition: Graph_d.h:426
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:165
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:849
ogdf::List::size
int size() const
Returns the number of elements in the list.
Definition: List.h:1478
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:135
BoundedQueue.h
Declaration and implementation of bounded queue class.
ogdf::Graph::contract
node contract(edge e, bool keepSelfLoops=false)
Contracts edge e while preserving the order of adjacency entries.
ogdf::ListIteratorBase
Encapsulates a pointer to a list element.
Definition: List.h:46
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:333
ogdf::EdgeWeightedGraph::setWeight
void setWeight(const edge e, T weight)
Definition: EdgeWeightedGraph.h:62
ogdf::BoundedQueue::top
const E & top() const
Returns front element.
Definition: BoundedQueue.h:107
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:78
ogdf::EdgeElement::target
node target() const
Returns the target node of the edge.
Definition: Graph_d.h:394
ogdf::SteinerTreePreprocessing::reachabilityTest
bool reachabilityTest(int maxDegreeTest=0, const int k=3)
Performs a reachability test [DV89].
Definition: SteinerTreePreprocessing.h:1742
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:1075
ogdf::steiner_tree::HeavyPathDecomposition
An implementation of the heavy path decomposition on trees.
Definition: HeavyPathDecomposition.h:47
ogdf::SteinerTreePreprocessing
This class implements preprocessing strategies for the Steiner tree problem.
Definition: SteinerTreePreprocessing.h:89
ogdf::SteinerTreePreprocessing::terminalDistanceTest
bool terminalDistanceTest()
Simple terminal distance test [PV01].
Definition: SteinerTreePreprocessing.h:1033
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:922
ogdf::NodeElement
Class for the representation of nodes.
Definition: Graph_d.h:233
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:1347
ogdf::SteinerTreePreprocessing::SteinerTreePreprocessing
SteinerTreePreprocessing(const EdgeWeightedGraph< T > &wg, const List< node > &terminals, const NodeArray< bool > &isTerminal)
Definition: SteinerTreePreprocessing.h:608
ogdf::Voronoi::seed
node seed(node v) const
Returns the Voronoi seed of node v.
Definition: Voronoi.h:87
ogdf::SteinerTreePreprocessing::getReducedIsTerminal
const NodeArray< bool > & getReducedIsTerminal() const
Returns the NodeArray<bool> isTerminal corresponding to the reduced graph.
Definition: SteinerTreePreprocessing.h:203
ogdf::SteinerTreePreprocessing::degree2Test
bool degree2Test()
deletes degree-2 nodes and replaces them with one edge with the adjacent edges' sum
Definition: SteinerTreePreprocessing.h:980
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:480
ogdf::SteinerTreePreprocessing::m_origIsTerminal
const NodeArray< bool > & m_origIsTerminal
Const reference to the original isTerminal.
Definition: SteinerTreePreprocessing.h:93
ogdf::steiner_tree::UnorderedNodePairEquality
A class used by the unordered_maps inside the reductions.
Definition: SteinerTreePreprocessing.h:67
ogdf::List::pushBack
iterator pushBack(const E &x)
Adds element x at the end of the list.
Definition: List.h:1537
ogdf::GraphCopyBase::original
const Graph & original() const
Returns a reference to the original graph.
Definition: GraphCopy.h:98
ogdf::internal::EdgeArrayBase2
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
Definition: Graph_d.h:709
ogdf::List::clear
void clear()
Removes all elements from the list.
Definition: List.h:1616
ogdf::SteinerTreePreprocessing::recomputeTerminalsList
void recomputeTerminalsList()
Used by reductions to recompute the m_copyTerminals list, according to m_copyIsTerminal; useful when ...
Definition: SteinerTreePreprocessing.h:778
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:181