56#include <forward_list>
62#include <unordered_map>
68class MinSteinerTreeModule;
71namespace steiner_tree {
164 delete reducedSolution;
178 eCopy[e] = ccGraph.
newEdge(nCopy[e->source()], nCopy[e->target()],
m_copyGraph.weight(e));
181 const node tC = nCopy[t];
183 ccIsTerminal[tC] =
true;
188 T cost = mst.
call(ccGraph, ccTerminals, ccIsTerminal, ccSolution);
195 if (ccSolution->
copy(nCopy[v]) !=
nullptr) {
200 if (ccSolution->
copy(eCopy[e]) !=
nullptr) {
256 bool changed =
false;
271 bool triviallyChanged =
false;
272 changed |=
repeat([
this, &triviallyChanged, k]() {
273 bool innerChanged =
false;
306 return changed | triviallyChanged;
404 bool NTDkTest(
const int maxTestedDegree = 5,
const int k = 3);
502 static bool repeat(std::function<
bool()> f) {
503 bool changed =
false;
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);
526 const std::vector<edge>& replacedEdges,
bool deleteReplacedElements) {
532 const std::vector<edge>& replacedEdges,
bool deleteReplacedElements) {
556 finalSteinerTree =
nullptr;
565 delete finalSteinerTree;
566 return treeCostUpperBound;
580 const NodeArray<
List<std::pair<node, T>>>& closestTerminals);
592 T maxDistance,
int expandedEdges)
const;
600 const NodeArray<
List<std::pair<node, T>>>& closestTerminals)
const;
608 NodeArray<
List<std::pair<node, T>>>& closestTerminals)
const;
617 template<
typename LAMBDA>
632 : m_origGraph(wg), m_origTerminals(terminals), m_origIsTerminal(isTerminal), m_eps(1e-6) {
652 const node vC = nCopy[v];
675template<
typename TWhat,
typename TWhatArray>
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]);
683 for (
edge replacedEdge : replacedEdges) {
684 sonsIndexList.push_back(m_edgeSonsListIndex[replacedEdge]);
687 m_sonsList.emplace_back(sonsIndexList);
688 whatSonsListIndex[x] = (int)m_sonsList.size() - 1;
690 if (deleteReplacedElements) {
691 for (
edge e : replacedEdges) {
694 for (
node v : replacedNodes) {
704 isInSolution[listIndex] =
true;
708 for (
int son : m_sonsList[listIndex]) {
709 addToSolution(son, isInSolution);
723 for (
node v : reducedGraphSolution.
nodes) {
724 addToSolution(m_nodeSonsListIndex[reducedGraphSolution.
original(v)], isInSolution);
726 for (
edge e : reducedGraphSolution.
edges) {
727 addToSolution(m_edgeSonsListIndex[reducedGraphSolution.
original(e)], isInSolution);
733 if (isInSolution[-(nextIndex++)]) {
734 correspondingOriginalSolution.
newNode(v);
738 if (isInSolution[-(nextIndex++)]) {
739 correspondingOriginalSolution.
newEdge(e, m_origGraph.
weight(e));
748 const NodeArray<
List<std::pair<node, T>>>& closestTerminals) {
756 std::forward_list<NewEdgeData> newEdges;
758 const edge e1 = adj1->theEdge();
759 const node adjacentNode1 = adj1->twinNode();
762 const edge e2 = adj2->theEdge();
763 const node adjacentNode2 = adj2->twinNode();
765 T edgeWeight = m_copyGraph.
weight(e1) + m_copyGraph.
weight(e2);
767 const edge f = m_copyGraph.
searchEdge(adjacentNode1, adjacentNode2);
768 if (f && m_copyGraph.
weight(f) <= edgeWeight) {
772 T bottleneckDistance = computeBottleneckDistance(adjacentNode1, adjacentNode2, tprime,
773 tprimeHPD, closestTerminals);
774 if (m_eps.
greater(edgeWeight, bottleneckDistance)) {
778 newEdges.emplace_front(NewEdgeData {e1, e2, f, edgeWeight});
782 for (
const auto& newEdge : newEdges) {
785 if (newEdge.already) {
787 m_copyGraph.
setWeight(newEdge.already, newEdge.weight);
788 newEdgeInGraph = newEdge.already;
790 newEdgeInGraph = m_copyGraph.
newEdge(newEdge.e1->opposite(v), newEdge.e2->opposite(v),
793 addNewEdge(newEdgeInGraph, {v}, {newEdge.e1, newEdge.e2},
false);
801 m_copyTerminals.
clear();
803 if (m_copyIsTerminal[v]) {
813 const NodeArray<
List<std::pair<node, T>>>& closestTerminals)
const {
814 T bottleneckDistance = std::numeric_limits<T>::max();
816 for (
auto& xCloseTerminalDistancePair : closestTerminals[x]) {
817 for (
auto& yCloseTerminalDistancePair : closestTerminals[y]) {
818 T possibleBottleneckDistance =
819 xCloseTerminalDistancePair.second + yCloseTerminalDistancePair.second;
821 node xTprimeCopy = tprime.
copy(xCloseTerminalDistancePair.first);
822 node yTprimeCopy = tprime.
copy(yCloseTerminalDistancePair.first);
823 possibleBottleneckDistance +=
830 return bottleneckDistance;
836 auto deleteAll = [
this]() {
840 for (
node v = m_copyGraph.
firstNode(), nextV; v; v = nextV) {
850 if (m_copyTerminals.
size() == 1) {
857 if (v->degree() == 1) {
862 if (eraseQueue.
empty()) {
866 for (; !eraseQueue.
empty(); eraseQueue.
pop()) {
873 if (m_copyIsTerminal[v]) {
876 if (!m_copyIsTerminal[w]) {
877 m_copyIsTerminal[w] =
true;
880 m_copyTerminals.
del(m_copyTerminals.
search(v));
881 m_costAlreadyInserted += m_copyGraph.
weight(e);
882 int cur = m_nodeSonsListIndex[w];
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;
888 m_sonsList[cur].push_back(m_nodeSonsListIndex[v]);
889 m_sonsList[cur].push_back(m_edgeSonsListIndex[e]);
897 if (m_copyTerminals.
size() == 1) {
907 bool changed =
false;
910 for (
adjEntry adj = v->firstAdj(), nextAdj; adj; adj = nextAdj) {
911 nextAdj = adj->succ();
913 edge e = adj->theEdge();
914 node adjNode = adj->twinNode();
916 if (nextAdj == adj->twin()) {
917 nextAdj = nextAdj->
succ();
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]);
928 minCostEdge[adjNode] = e;
936 for (
adjEntry adj : v->adjEntries) {
937 minCostEdge[adj->twinNode()] =
nullptr;
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()));
956 if (shortestPath[v1][pivot] == std::numeric_limits<T>::max()
957 || shortestPath[pivot][v2] == std::numeric_limits<T>::max()) {
962 shortestPath[v1][pivot] + shortestPath[pivot][v2]);
971 shortestPath.init(m_copyGraph);
973 shortestPath[v].init(m_copyGraph, std::numeric_limits<T>::max());
976 floydWarshall(shortestPath);
982 bool changed =
false;
984 computeShortestPathMatrix(shortestPath);
987 for (
adjEntry adj = v->firstAdj(), nextAdj; adj; adj = nextAdj) {
988 nextAdj = adj->succ();
990 edge e = adj->theEdge();
991 node adjNode = adj->twinNode();
992 if (adjNode != v && shortestPath[v][adjNode] < m_copyGraph.
weight(e)) {
1004 bool changed =
false;
1005 for (
node v = m_copyGraph.
firstNode(), nextV; v; v = nextV) {
1008 if (m_copyIsTerminal[v] || v->degree() != 2) {
1013 edge eLeft = v->firstAdj()->theEdge();
1014 edge eRight = v->lastAdj()->theEdge();
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);
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";
1041 componentWithTerminals = hisConnectedComponent[v];
1044 for (
node v = m_copyGraph.
firstNode(), nextV; v; v = nextV) {
1046 if (hisConnectedComponent[v] != componentWithTerminals) {
1058 bool changed =
false;
1068 for (
edge e = m_copyGraph.
firstEdge(), nextE; e; e = nextE) {
1082 NodeArray<T>& distance, T maxDistance,
int expandedEdges)
const {
1086 distance[source] = 0;
1087 queue.
push(source, distance[source]);
1089 while (!queue.
empty()) {
1093 reachedNodes.
pushBack(currentNode);
1096 edge e = adj->theEdge();
1097 if (expandedEdges <= 0) {
1103 T possibleDistance = distance[currentNode] + m_copyGraph.
weight(e);
1105 if (m_eps.
geq(possibleDistance, maxDistance) || m_copyIsTerminal[adjacentNode]) {
1109 if (possibleDistance < distance[adjacentNode]) {
1110 distance[adjacentNode] = possibleDistance;
1111 if (queue.
contains(adjacentNode)) {
1112 queue.
decrease(adjacentNode, possibleDistance);
1114 queue.
push(adjacentNode, distance[adjacentNode]);
1124 bool changed =
false;
1125 NodeArray<T> xDistance(m_copyGraph, std::numeric_limits<T>::max()),
1126 yDistance(m_copyGraph, std::numeric_limits<T>::max());
1128 for (
edge e = m_copyGraph.
firstEdge(), nextE; e; e = nextE) {
1132 findClosestNonTerminals(e->source(), xReachedNodes, xDistance, m_copyGraph.
weight(e), 200);
1133 findClosestNonTerminals(e->target(), yReachedNodes, yDistance, m_copyGraph.
weight(e), 200);
1135 for (
node commonNode : xReachedNodes) {
1136 if (yDistance[commonNode] == std::numeric_limits<T>::max()) {
1139 if (m_eps.
less(xDistance[commonNode] + yDistance[commonNode], m_copyGraph.
weight(e))) {
1146 for (
node reachedNode : xReachedNodes) {
1147 xDistance[reachedNode] = std::numeric_limits<T>::max();
1149 for (
node reachedNode : yReachedNodes) {
1150 yDistance[reachedNode] = std::numeric_limits<T>::max();
1158 NodeArray<
List<std::pair<node, T>>>& closestTerminals)
const {
1161 closestTerminals.init(m_copyGraph);
1162 NodePairQueue queue;
1163 std::unordered_map<
NodePair,
typename NodePairQueue::Handle,
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);
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;
1179 return static_cast<T
>(-1);
1182 auto setNewDist = [&closestTerminals, k](
const node currentNode,
const node sourceTerminal,
1188 if ((*it).first == sourceTerminal) {
1189 currentList.
del(it);
1194 if (currentList.
size() == k) {
1199 if (currentList.
empty() || (*currentList.
begin()).second >= newDist) {
1200 currentList.
pushFront(std::make_pair(sourceTerminal, newDist));
1205 while (it.
succ() != closestTerminals[currentNode].end() && (*it.
succ()).second < newDist) {
1208 closestTerminals[currentNode].insertAfter(std::make_pair(sourceTerminal, newDist), it);
1211 while (!queue.empty()) {
1212 NodePair minDistPair = queue.topElement();
1217 T currentDist = getCurrentDist(currentNode, sourceTerminal);
1218 if (currentDist == -1) {
1223 edge e = adj->theEdge();
1226 if (m_copyIsTerminal[adjacentNode]) {
1230 T possibleNewDistance = currentDist + m_copyGraph.
weight(e);
1233 T currentDistToAdjacentNode = getCurrentDist(adjacentNode, sourceTerminal);
1234 if (currentDistToAdjacentNode
1236 if (possibleNewDistance < currentDistToAdjacentNode) {
1237 queue.decrease(qpos[
NodePair(adjacentNode, sourceTerminal)], possibleNewDistance);
1238 setNewDist(adjacentNode, sourceTerminal, possibleNewDistance);
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);
1256 bool changed =
false;
1263 computeClosestKTerminals(k, closestTerminals);
1267 for (
edge e = m_copyGraph.
firstEdge(), nextE; e; e = nextE) {
1270 T bottleneckDistance = computeBottleneckDistance(e->source(), e->target(), tprime,
1271 tprimeHPD, closestTerminals);
1273 if (m_eps.
greater(m_copyGraph.
weight(e), bottleneckDistance)) {
1285 if (m_copyTerminals.
size() <= 2) {
1289 bool changed =
false;
1298 computeClosestKTerminals(k, closestTerminals);
1302 for (
node v = m_copyGraph.
firstNode(), nextV; v; v = nextV) {
1305 if (m_copyIsTerminal[v]) {
1308 if (v->degree() <= 2 || v->degree() > maxTestedDegree) {
1314 for (
adjEntry adj : v->adjEntries) {
1319 bool deleteNode =
true;
1320 for (neighborSubset.
begin(3, v->degree()); neighborSubset.
valid() && deleteNode;
1321 neighborSubset.
next()) {
1323 std::unordered_map<node, node> initNodeToAuxGraph;
1324 std::unordered_map<node, node> auxGraphToInitNode;
1326 T sumToSelectedAdjacentNodes = 0;
1328 for (
int i = 0; i < neighborSubset.
size(); ++i) {
1329 const adjEntry adj = neighborSubset[i];
1331 sumToSelectedAdjacentNodes += m_copyGraph.
weight(adj->
theEdge());
1333 OGDF_ASSERT(initNodeToAuxGraph.find(adjacentNode) == initNodeToAuxGraph.end());
1336 initNodeToAuxGraph[adjacentNode] = newAuxGraphNode;
1337 auxGraphToInitNode[newAuxGraphNode] = adjacentNode;
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);
1354 if (sumToSelectedAdjacentNodes < mstCost) {
1360 deleteSteinerDegreeTwoNode(v, tprime, tprimeHPD, closestTerminals);
1371 isSuccessorOfMinCostEdge[currentNode] =
true;
1377 edge e = adj->theEdge();
1380 if (voronoiRegions.
predecessor(adjacentNode) == currentNode) {
1381 markSuccessors(adjacentNode, voronoiRegions, isSuccessorOfMinCostEdge);
1396 edge e = adj->theEdge();
1397 if (first ==
nullptr
1404 if (second ==
nullptr || m_eps.
less(m_copyGraph.
weight(e), m_copyGraph.
weight(second))) {
1414 if (edgesToBeAddedInSolution.
empty()) {
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);
1423 m_nodeSonsListIndex[newNode] = (int)m_sonsList.size() - 1;
1424 m_copyIsTerminal[newNode] =
true;
1427 recomputeTerminalsList();
1443 for (
node terminal : m_copyTerminals) {
1444 if (terminal->degree() >= 2) {
1445 findTwoMinimumCostEdges(terminal, minCostIncidentEdge1[terminal],
1446 minCostIncidentEdge2[terminal]);
1452 for (
node terminal : m_copyTerminals) {
1453 if (terminal->degree() >= 2) {
1454 node closestNode = minCostIncidentEdge1[terminal]->opposite(terminal);
1456 if (voronoiRegions.
seed(closestNode) == terminal) {
1457 markSuccessors(closestNode, voronoiRegions, isSuccessorOfMinCostEdge);
1463 NodeArray<T> distanceToClosestTerminal(m_copyGraph, std::numeric_limits<T>::max());
1465 node x = e->source(), y = e->target();
1466 node seedX = voronoiRegions.
seed(x), seedY = voronoiRegions.
seed(y);
1467 if (seedX != seedY) {
1469 T distanceThroughE =
1472 if (isSuccessorOfMinCostEdge[x]) {
1475 if (isSuccessorOfMinCostEdge[y]) {
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)};
1492 if (m_eps.
geq(m_copyGraph.
weight(minCostIncidentEdge2[terminal]), distance)
1493 && !willBeAddedInSolution[e]) {
1494 edgesToBeAddedInSolution.
pushBack(e);
1495 willBeAddedInSolution[e] =
true;
1500 return addEdgesToSolution(edgesToBeAddedInSolution);
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;
1525 const node x = e->source(), y = e->target();
1526 const node seedX = voronoiRegions.
seed(x), seedY = voronoiRegions.
seed(y);
1527 if (seedX != seedY) {
1529 updateMinCostLeavingRegionsFor(seedX);
1530 updateMinCostLeavingRegionsFor(seedY);
1536 for (
node terminal : m_copyTerminals) {
1537 if (minCostLeavingRegionEdge2[terminal] ==
nullptr) {
1542 const edge e1 = minCostLeavingRegionEdge1[terminal];
1544 if (m_eps.
geq(m_copyGraph.
weight(minCostLeavingRegionEdge2[terminal]),
1546 && !willBeAddedInSolution[e1]) {
1547 edgesToBeAddedInSolution.
pushBack(e1);
1548 willBeAddedInSolution[e1] =
true;
1552 return addEdgesToSolution(edgesToBeAddedInSolution);
1563 terminalRadius.
init(m_copyGraph, std::numeric_limits<T>::max());
1565 node seedV = voronoiRegions.
seed(v);
1566 T distanceToSeedV = voronoiRegions.
distance(v);
1568 for (
adjEntry adj : v->adjEntries) {
1569 edge e = adj->theEdge();
1572 if (voronoiRegions.
seed(adjacentNode) != seedV) {
1581 const T upperBound) {
1582 bool changed =
false;
1583 for (
node v = m_copyGraph.
firstNode(), nextV; v; v = nextV) {
1585 if (m_eps.
greater(lowerBoundWithNode[v], upperBound)) {
1597 if (m_copyTerminals.
size() <= 1) {
1603 NodeArray<T> lowerBoundWithNode(m_copyGraph, std::numeric_limits<T>::lowest());
1606 computeClosestKTerminals(3, closestTerminals);
1610 const T radiusSum = computeRadiusSum();
1612 if (m_copyIsTerminal[v]) {
1616 if (closestTerminals[v].size() < 2) {
1617 lowerBoundWithNode[v] = std::numeric_limits<T>::max();
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;
1627 distanceToClosestTerminal1 + distanceToClosestTerminal2 + radiusSum);
1633 node x = e->source(), y = e->target();
1635 T distanceToClosestTerminalX = (*closestTerminals[x].begin()).second;
1636 T distanceToClosestTerminalY = (*closestTerminals[y].begin()).second;
1639 m_copyGraph.
weight(e) + distanceToClosestTerminalX + distanceToClosestTerminalY
1645 Graph auxiliaryGraph;
1647 for (
node terminal : m_copyTerminals) {
1649 terminalInAuxiliaryGraph[terminal] = newAuxiliaryNode;
1654 initialEdgeWeight[e] = m_copyGraph.
weight(e);
1656 Voronoi<T> voronoiRegions(m_copyGraph, initialEdgeWeight, m_copyTerminals);
1661 EdgeArray<T> edgeWeight(auxiliaryGraph, std::numeric_limits<T>::max());
1663 node x = e->source(), y = e->target();
1664 node seedX = voronoiRegions.
seed(x), seedY = voronoiRegions.
seed(y);
1665 if (seedX == seedY) {
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]);
1674 edge auxiliaryEdge = edgeBetweenNodes[pair];
1680 T minimumSpanningTreeCost =
computeMinST(auxiliaryGraph, edgeWeight, isInTree);
1681 T longestEdgeCost = std::numeric_limits<T>::lowest();
1689 if (m_copyIsTerminal[v] || closestTerminals[v].size() < 2) {
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;
1699 minimumSpanningTreeCost - longestEdgeCost + distanceToClosestTerminal1
1700 + distanceToClosestTerminal2);
1703 return deleteNodesAboveUpperBound(lowerBoundWithNode, upperBound)
1704 || deleteEdgesAboveUpperBound(lowerBoundWithEdge, upperBound);
1710 bool changed =
false;
1711 if (m_copyTerminals.
size() > 1) {
1716 alg.
call(m_copyGraph, m_copyTerminals, lowerBoundNodes, lowerBoundEdges);
1718 changed |= deleteNodesAboveUpperBound(lowerBoundNodes, upperBound);
1719 changed |= deleteEdgesAboveUpperBound(lowerBoundEdges, upperBound);
1726 const T upperBound) {
1727 bool changed =
false;
1728 for (
edge e = m_copyGraph.
firstEdge(), nextE; e; e = nextE) {
1730 if (m_eps.
greater(lowerBoundWithEdge[e], upperBound)) {
1741 computeRadiusOfTerminals(terminalRadius);
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];
1750 if (terminalRadius[terminal] > largestRadius1) {
1751 largestRadius2 = largestRadius1;
1752 largestRadius1 = terminalRadius[terminal];
1754 if (terminalRadius[terminal] > largestRadius2) {
1755 largestRadius2 = terminalRadius[terminal];
1759 radiusSum -= largestRadius1 + largestRadius2;
1768 bool changed =
false;
1769 if (maxDegreeTest <= 0) {
1774 T upperBoundCost = computeMinSteinerTreeUpperBound(approximatedSteinerTree);
1777 for (
node v : approximatedSteinerTree->
nodes) {
1778 isInUpperBoundTree[approximatedSteinerTree->
original(v)] =
true;
1780 delete approximatedSteinerTree;
1789 computeClosestKTerminals(k, closestTerminals);
1794 for (
node v = m_copyGraph.
firstNode(), nextV; v; v = nextV) {
1797 if (isInUpperBoundTree[v] || v->degree() > maxDegreeTest) {
1806 dijkstra.
call(m_copyGraph, m_copyGraph.
edgeWeights(), v, predecessor, distance);
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];
1819 if (distanceToClosestTerminal1 > distance[terminal]) {
1820 distanceToClosestTerminal2 = distanceToClosestTerminal1;
1821 distanceToClosestTerminal1 = distance[terminal];
1823 if (distanceToClosestTerminal2 > distance[terminal]) {
1824 distanceToClosestTerminal2 = distance[terminal];
1829 if (predecessor[farthestTerminal] ==
nullptr
1830 || distanceToClosestTerminal2
1831 == std::numeric_limits<T>::max()
1832 || m_eps.
geq(distanceToFarthestTerminal + distanceToClosestTerminal1
1833 + distanceToClosestTerminal2,
1837 if (predecessor[farthestTerminal] !=
nullptr
1838 && distanceToClosestTerminal2 != std::numeric_limits<T>::max()
1839 && m_eps.
less(distanceToFarthestTerminal + distanceToClosestTerminal1,
1842 deleteSteinerDegreeTwoNode(v, tprime, tprimeHPD, closestTerminals);
1854template<
typename LAMBDA>
1860 distance.
init(m_copyGraph);
1862 dijkstra.
call(m_copyGraph, m_copyGraph.
edgeWeights(), v, predecessor, distance);
1864 for (
node terminal : m_copyTerminals) {
1865 if (predecessor[terminal] ==
nullptr) {
1869 if (optimalTerminal1 ==
nullptr
1870 || dist(optimalTerminal1, distance) > dist(terminal, distance)) {
1871 optimalTerminal2 = optimalTerminal1;
1872 optimalTerminal1 = terminal;
1874 if (optimalTerminal2 ==
nullptr
1875 || dist(optimalTerminal2, distance) > dist(terminal, distance)) {
1876 optimalTerminal2 = terminal;
1882 OGDF_ASSERT(optimalTerminal1 != optimalTerminal2);
1888 if (m_copyTerminals.
size() <= 2) {
1894 const T upperBoundCost = computeMinSteinerTreeUpperBound(approximatedSteinerTree);
1897 for (
node v : approximatedSteinerTree->
nodes) {
1898 isInUpperBoundTree[approximatedSteinerTree->
original(v)] =
true;
1900 delete approximatedSteinerTree;
1903 NodeArray<T> minCostOfAdjacentEdge(m_copyGraph, std::numeric_limits<T>::max());
1904 for (
node terminal : m_copyTerminals) {
1905 for (
adjEntry adj : terminal->adjEntries) {
1908 cK += minCostOfAdjacentEdge[terminal];
1910 auto dist = [&minCostOfAdjacentEdge](
node terminal,
const NodeArray<T>& distance) {
1911 return distance[terminal] - minCostOfAdjacentEdge[terminal];
1915 std::set<edge> delEdges;
1918 for (
node v = m_copyGraph.
firstNode(), nextV; v; v = nextV) {
1921 if (isInUpperBoundTree[v]) {
1926 node vOptimalTerminal1 =
nullptr, vOptimalTerminal2 =
nullptr;
1927 computeOptimalTerminals(v, dist, vOptimalTerminal1, vOptimalTerminal2, vDistance);
1930 if (m_eps.
geq(cK + dist(vOptimalTerminal1, vDistance) + dist(vOptimalTerminal2, vDistance),
1934 for (
adjEntry adj = v->firstAdj(), adjNext; adj; adj = adjNext) {
1935 adjNext = adj->
succ();
1936 node w = adj->twinNode();
1937 if (m_copyIsTerminal[w]) {
1940 node wOptimalTerminal1 =
nullptr, wOptimalTerminal2 =
nullptr;
1941 computeOptimalTerminals(w, dist, wOptimalTerminal1, wOptimalTerminal2, wDistance);
1943 node vOptimalTerminal = vOptimalTerminal1;
1944 node wOptimalTerminal = wOptimalTerminal1;
1945 if (vOptimalTerminal == wOptimalTerminal) {
1948 if (m_eps.
leq(dist(vOptimalTerminal1, vDistance)
1949 + dist(wOptimalTerminal2, wDistance),
1950 dist(vOptimalTerminal2, vDistance)
1951 + dist(wOptimalTerminal1, wDistance))) {
1952 wOptimalTerminal = wOptimalTerminal2;
1954 vOptimalTerminal = vOptimalTerminal2;
1957 OGDF_ASSERT(vOptimalTerminal != wOptimalTerminal);
1958 if (m_eps.
geq(cK + dist(vOptimalTerminal, vDistance) + dist(wOptimalTerminal, wDistance)
1959 + m_copyGraph.
weight(adj->theEdge()),
1961 delEdges.insert(adj->theEdge());
1967 bool changed =
false;
1968 for (
edge e : delEdges) {
1972 for (
node v : delNodes) {
Declaration and implementation of Array class and Array algorithms.
Declaration and implementation of bounded queue class.
Declaration of class EdgeWeightedGraph.
Extends the GraphCopy concept to weighted graphs.
Compare floating point numbers with epsilons and integral numbers with normal compare operators.
Includes declaration of graph class.
Decralation of GraphElement and GraphList classes.
Definition of the ogdf::steiner_tree:HeavyPathDecomposition class template.
Declaration of doubly linked lists and iterators.
Implementation of Mehlhorn's minimum Steiner tree 2(1-1/l)-approximation algorithm.
Implementation of the 2(1-1/l)-approximation algorithm for the minimum Steiner tree problem by Matsuy...
Priority queue interface wrapping various heaps.
Definition of the ogdf::SteinerTreeLowerBoundDualAscent class template.
A class that allows to enumerate k-subsets.
Definition of ogdf::Voronoi class template.
Basic declarations, included by all source files.
Class for adjacency list elements.
adjEntry succ() const
Returns the successor in the adjacency list.
edge theEdge() const
Returns the edge associated with this adjacency entry.
node twinNode() const
Returns the associated node of the corresponding adjacency entry (shorthand for twin()->theNode()).
The parameterized class Array implements dynamic arrays of type E.
The parameterized class BoundedQueue implements queues with bounded size.
bool empty()
Returns true iff the queue is empty.
void append(const E &x)
Adds x at the end of queue.
const E & top() const
Returns front element.
E pop()
Removes front element and returns it.
Dijkstra's single source shortest path algorithm.
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...
Class for the representation of edges.
int index() const
Returns the index of the edge.
node opposite(node v) const
Returns the adjacent node different from v.
node target() const
Returns the target node of the edge.
node source() const
Returns the source node of the edge.
edge newEdge(node u, node v, T weight)
void setOriginalGraph(const Graph *wG) override
Re-initializes the copy using G (which might be null), but does not create any nodes or edges.
T weight(const edge e) const
T weight(const edge e) const
edge newEdge(node v, node w, T weight)
const EdgeArray< T > & edgeWeights() const
void setWeight(const edge e, T weight)
std::enable_if< std::is_integral< T >::value, bool >::type leq(const T &x, const T &y) const
Compare if x is LEQ than y for integral types.
std::enable_if< std::is_integral< T >::value, bool >::type geq(const T &x, const T &y) const
Compare if x is GEQ to y for integral types.
std::enable_if< std::is_integral< T >::value, bool >::type less(const T &x, const T &y) const
Compare if x is LESS than y for integral types.
std::enable_if< std::is_integral< T >::value, bool >::type equal(const T &x, const T &y) const
Compare if x is EQUAL to y for integral types.
std::enable_if< std::is_integral< T >::value, bool >::type greater(const T &x, const T &y) const
Compare if x is GREATER than y for integral types.
node newNode(node vOrig)
Creates a new node in the graph copy with original node vOrig.
const Graph & original() const
Returns a reference to the original graph.
edge copy(edge e) const override
Returns the first edge in the list of edges corresponding to edge e.
Data type for general directed graphs (adjacency list representation).
node contract(edge e, bool keepSelfLoops=false)
Contracts edge e while preserving the order of adjacency entries.
int numberOfNodes() const
Returns the number of nodes in the graph.
edge firstEdge() const
Returns the first edge in the list of all edges.
virtual void delNode(node v)
Removes node v and all incident edges from the graph.
int numberOfEdges() const
Returns the number of edges in the graph.
internal::GraphObjectContainer< NodeElement > nodes
The container containing all node objects.
virtual void delEdge(edge e)
Removes edge e from the graph.
node newNode(int index=-1)
Creates a new node and returns it.
node firstNode() const
Returns the first node in the list of all nodes.
edge newEdge(node v, node w, int index=-1)
Creates a new edge (v,w) and returns it.
bool empty() const
Returns true iff the graph is empty, i.e., contains no nodes.
edge searchEdge(node v, node w, bool directed=false) const
Searches and returns an edge connecting nodes v and w in time O( min(deg(v ), deg(w ))).
internal::GraphObjectContainer< EdgeElement > edges
The container containing all edge objects.
Doubly linked lists (maintaining the length of the list).
int size() const
Returns the number of elements in the list.
void clear()
Removes all elements from the list.
iterator pushBack(const E &x)
Adds element x at the end of the list.
void del(iterator it)
Removes it from the list.
iterator pushFront(const E &x)
Adds element x at the beginning of the list.
void popBack()
Removes the last element from the list.
Encapsulates a pointer to a list element.
bool valid() const
Returns true iff the iterator points to an element.
ListIteratorBase< E, isConst, isReverse > succ() const
Returns successor iterator.
iterator begin()
Returns an iterator to the first element of the list.
const_reference front() const
Returns a const reference to the first element.
bool empty() const
Returns true iff the list is empty.
ListConstIterator< E > search(const E &e) const
Scans the list for the specified element and returns an iterator to the first occurrence in the list,...
Serves as an interface for various methods to compute or approximate minimum Steiner trees on undirec...
virtual T call(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, EdgeWeightedGraphCopy< T > *&finalSteinerTree)
Calls the Steiner tree algorithm for nontrivial cases but handles trivial cases directly.
This class implements the minimum Steiner tree 2-approximation algorithm by Takahashi and Matsuyama w...
Class for the representation of nodes.
int degree() const
Returns the degree of the node (indegree + outdegree).
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
node succ() const
Returns the successor in the list of all nodes.
int index() const
Returns the (unique) node index.
adjEntry firstAdj() const
Returns the first entry in the adjaceny list.
adjEntry lastAdj() const
Returns the last entry in the adjacency list.
Prioritized queue interface wrapper for heaps.
bool empty() const
Checks whether the queue is empty.
void init(const Base *base=nullptr)
Reinitializes the array. Associates the array with the matching registry of base.
Implementation of a dual-ascent-based lower bound heuristic for Steiner tree problems.
T call(const EdgeWeightedGraph< T > &graph, const List< node > &terminals)
Calls the algorithm and returns the lower bound.
void setRepetitions(int num)
Sets the number of repeated calls to the lower bound algorithm (runs with different roots)
This class implements preprocessing strategies for the Steiner tree problem.
T computeMinSteinerTreeUpperBound(EdgeWeightedGraphCopy< T > *&finalSteinerTree) const
bool degree2Test()
deletes degree-2 nodes and replaces them with one edge with the adjacent edges' sum
bool deleteLeaves()
Deletes the leaves of the graph.
bool reduceFastAndDualAscent()
Apply fast reductions and the dual-ascent-based tests iteratively.
void computeOriginalSolution(const EdgeWeightedGraphCopy< T > &reducedGraphSolution, EdgeWeightedGraphCopy< T > &correspondingOriginalSolution)
Computes the solution for the original graph, given a solution on the reduction.
bool deleteEdgesAboveUpperBound(const EdgeArray< T > &lowerBoundWithEdge, const T upperBound)
Deletes the edges whose lowerBoundWithEdge exceeds upperBound.
bool cutReachabilityTest()
Performs a cut reachability test [DV89].
void computeShortestPathMatrix(NodeArray< NodeArray< T > > &shortestPath) const
Computes the shortest path matrix corresponding to the m_copyGraph.
const EdgeWeightedGraph< T > & getReducedGraph() const
Returns the reduced form of the graph.
bool dualAscentBasedTest(int repetitions=1)
Like dualAscentBasedTest(int repetitions, T upperBound) but the upper bound is computed by computeMin...
bool longEdgesTest()
Long-Edges test from [DV89].
T computeRadiusSum() const
Compute the sum of all radii except the two largest.
bool deleteNodesAboveUpperBound(const NodeArray< T > &lowerBoundWithNode, const T upperBound)
Deletes the nodes whose lowerBoundWithNode exceeds upperBound.
void computeOptimalTerminals(node v, LAMBDA dist, node &optimalTerminal1, node &optimalTerminal2, NodeArray< T > &distance) const
Compute first and second best terminals according to function dist.
bool reduceTrivial()
Apply trivial (hence amazingly fast) reductions iteratively, that is degree2Test(),...
T costEdgesAlreadyInserted() const
Returns the cost of the edges already inserted in solution during the reductions.
void addNew(TWhat x, const std::vector< node > &replacedNodes, const std::vector< edge > &replacedEdges, bool deleteReplacedElements, TWhatArray &whatSonsListIndex)
Update internal data structures to let a (new) node or edge represent replaced nodes and/or edges.
void findTwoMinimumCostEdges(node v, edge &first, edge &second) const
Finds the first and second smallest edges incident to v.
EdgeArray< int > m_edgeSonsListIndex
See m_nodeSonsListIndex but for edges.
bool terminalDistanceTest()
Simple terminal distance test [PV01].
static bool repeat(std::function< bool()> f)
Auxiliary function: Repeats a function until it returns false (used for iteratively applying reductio...
std::vector< std::vector< int > > m_sonsList
List with lists (corresponding to nodes and containing the indexes of their sons)
void markSuccessors(node currentNode, const Voronoi< T > &voronoiRegions, NodeArray< bool > &isSuccessorOfMinCostEdge) const
Mark successors of currentNode in its shortest-path tree in voronoiRegions.
bool lowerBoundBasedTest()
Like lowerBoundBasedTest(T upperBound) but the upper bound is computed by computeMinSteinerTreeUpperB...
T computeMinSteinerTreeUpperBound() const
void setCostUpperBoundAlgorithm(MinSteinerTreeModule< T > *pMinSteinerTreeModule)
Set the module option for the algorithm used for computing the MinSteinerTree cost upper bound.
void floydWarshall(NodeArray< NodeArray< T > > &shortestPath) const
Applies the Floyd-Warshall algorithm on the m_copyGraph. The shortestPath matrix has to be already in...
T m_costAlreadyInserted
The cost of the already inserted in solution edges.
T solve(MinSteinerTreeModule< T > &mst, EdgeWeightedGraphCopy< T > *&finalSteinerTree)
A shortcut to get the solution of a reduced instance.
List< node > m_copyTerminals
The reduced form of terminals.
T computeBottleneckDistance(node x, node y, const EdgeWeightedGraphCopy< T > &tprime, const steiner_tree::HeavyPathDecomposition< T > &tprimeHPD, const NodeArray< List< std::pair< node, T > > > &closestTerminals) const
Heuristic computation [PV01] of the bottleneck Steiner distance between two nodes in a graph.
void computeRadiusOfTerminals(NodeArray< T > &terminalRadius) const
Compute radius of terminals.
SteinerTreePreprocessing(const EdgeWeightedGraph< T > &wg, const List< node > &terminals, const NodeArray< bool > &isTerminal)
void deleteSteinerDegreeTwoNode(node v, const EdgeWeightedGraphCopy< T > &tprime, const steiner_tree::HeavyPathDecomposition< T > &tprimeHPD, const NodeArray< List< std::pair< node, T > > > &closestTerminals)
Deletes a node that is known to have degree 2 in at least one minimum Steiner tree.
const List< node > & getReducedTerminals() const
Returns the list of the terminals corresponding to the reduced graph.
void recomputeTerminalsList()
Used by reductions to recompute the m_copyTerminals list, according to m_copyIsTerminal; useful when ...
const NodeArray< bool > & m_origIsTerminal
Const reference to the original isTerminal.
NodeArray< int > m_nodeSonsListIndex
It contains for each node an index i.
void shuffleReducedTerminals()
Shuffles the list of reduced terminals. This can have an effect on some tests.
void findClosestNonTerminals(node source, List< node > &reachedNodes, NodeArray< T > &distance, T maxDistance, int expandedEdges) const
Heuristic approach to computing the closest non-terminals for one node, such that there is no termina...
void addNewNode(node v, const std::vector< node > &replacedNodes, const std::vector< edge > &replacedEdges, bool deleteReplacedElements)
Calls addNew() for a node.
void computeClosestKTerminals(const int k, NodeArray< List< std::pair< node, T > > > &closestTerminals) const
Computes for every non-terminal the closest k terminals such that there is no other terminal on the p...
std::unique_ptr< MinSteinerTreeModule< T > > m_costUpperBoundAlgorithm
Algorithm used for computing the upper bound for the cost of a minimum Steiner tree.
bool reduceFast()
Apply fast reductions iteratively (includes trivial reductions).
const EdgeWeightedGraph< T > & m_origGraph
Const reference to the original graph.
bool reachabilityTest(int maxDegreeTest=0, const int k=3)
Performs a reachability test [DV89].
EdgeWeightedGraph< T > m_copyGraph
Copy of the original graph; this copy will actually be reduced.
void addNewEdge(edge e, const std::vector< node > &replacedNodes, const std::vector< edge > &replacedEdges, bool deleteReplacedElements)
The function is called after a new edge is added to the copy graph during the reductions.
NodeArray< bool > m_copyIsTerminal
The reduced form of isTerminal.
bool PTmTest(const int k=3)
"Paths with many terminals" test, efficient on paths with many terminals.
bool makeSimple()
Deletes parallel edges keeping only the minimum cost one, and deletes self-loops.
const List< node > & m_origTerminals
Const reference to the original list of terminals.
bool shortLinksTest()
Short links test using Voronoi regions [PV01].
const NodeArray< bool > & getReducedIsTerminal() const
Returns the NodeArray<bool> isTerminal corresponding to the reduced graph.
void addToSolution(const int listIndex, Array< bool, int > &isInSolution) const
Helper method for computeOriginalSolution.
bool leastCostTest()
Performs a least cost test [DV89] computing the whole shortest path matrix.
bool nearestVertexTest()
Nearest vertex test using Voronoi regions [DV89, PV01].
bool addEdgesToSolution(const List< edge > &edgesToBeAddedInSolution)
The function adds a set of edges in the solution.
bool deleteComponentsWithoutTerminals()
Deletes connected components with no terminals.
bool NTDkTest(const int maxTestedDegree=5, const int k=3)
Non-terminals of degree k test [DV89, PV01].
bool dualAscentBasedTest(int repetitions, T upperBound)
Like lowerBoundBasedTest(T upperBound) but uses ogdf::SteinerTreeLowerBoundDualAscent to compute lowe...
Enumerator for k-subsets of a given type.
void begin(int low, int high)
Initializes the SubsetEnumerator to enumerate subsets of cardinalities from low to high.
int size() const
Returns the cardinality of the subset.
bool valid() const
Checks if the current subset is valid. If not, the subset is either not initialized or all subsets ha...
void next()
Obtains the next subset if possible. The result should be checked using the valid() method.
Computes Voronoi regions in an edge-weighted graph.
T distance(node v) const
Returns the distance between v and its Voronoi seed.
node predecessor(node v) const
Returns the nearest node to v on the shortest path to its Voronoi seed.
node seed(node v) const
Returns the Voronoi seed of node v.
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
RegisteredArray for nodes, edges and adjEntries of a graph.
void decrease(const E &element, const P &priority)
Decreases the priority of the given element.
bool contains(const E &element) const
Returns whether this queue contains that key.
void pop()
Removes the topmost element from the queue.
void push(const E &element, const P &priority)
Adds a new element to the queue.
Defines a queue for handling prioritized elements.
const E & topElement() const
Returns the topmost element in the queue.
An implementation of the heavy path decomposition on trees.
T getBottleneckSteinerDistance(node x, node y) const
computes in the bottleneck distance between terminals x and y
A class used by the unordered_maps inside the reductions.
bool operator()(const NodePair &pair1, const NodePair &pair2) const
A class used by the unordered_maps inside the reductions.
int operator()(const NodePair &v) const
Declaration of extended graph algorithms.
Implementation of Dijkstra's single source shortest path algorithm.
int connectedComponents(const Graph &G, NodeArray< int > &component, List< node > *isolated=nullptr, ArrayBuffer< node > *reprs=nullptr)
Computes the connected components of G and optionally generates a list of isolated nodes.
bool isConnected(const Graph &G)
Returns true iff G is connected.
T computeMinST(const Graph &G, const EdgeArray< T > &weight, EdgeArray< bool > &isInTree)
Computes a minimum spanning tree using Prim's algorithm.
bool isLoopFree(const Graph &G)
Returns true iff G contains no self-loop.
bool isSimpleUndirected(const Graph &G)
Returns true iff G contains neither self-loops nor undirected parallel edges.
EdgeElement * edge
The type of edges.
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
void updateMin(T &min, const T &newValue)
Stores the minimum of min and newValue in min.
void updateMax(T &max, const T &newValue)
Stores the maximum of max and newValue in max.
T constructTerminalSpanningTreeUsingVoronoiRegions(EdgeWeightedGraphCopy< T > &terminalSpanningTree, const EdgeWeightedGraph< T > &graph, const List< node > &terminals)
The namespace for all OGDF objects.
Declaration of simple graph algorithms.