Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
MinSteinerTreeShore.h
Go to the documentation of this file.
1
33#pragma once
34
35// enable this to print messages
36// for all branches (edge inclusion or exclusion)
37// additionally a SVG image is generated for each
38// recursion depth
39//#define OGDF_MINSTEINERTREE_SHORE_LOGGING
40
41#include <ogdf/basic/Array2D.h>
42#include <ogdf/basic/Graph.h>
45#include <ogdf/basic/List.h>
46#include <ogdf/basic/basic.h>
49
50#include <iostream>
51#include <limits>
52#include <memory>
53#include <sstream>
54#include <string>
55
56namespace ogdf {
57template<typename T>
58class EdgeWeightedGraph;
59
70template<typename T>
72public:
73 MinSteinerTreeShore() : MAX_WEIGHT(std::numeric_limits<T>::max()) {};
75
76protected:
92 virtual T computeSteinerTree(const EdgeWeightedGraph<T>& G, const List<node>& terminals,
93 const NodeArray<bool>& isTerminal, EdgeWeightedGraphCopy<T>*& finalSteinerTree) override;
94
95 const T MAX_WEIGHT;
96
97private:
100
102 std::unique_ptr<NodeSet> m_terminals;
108
117 bool validateMapping() const;
118
127 T weightOf(edge e) const;
128
145 T bnbInternal(T prevCost, List<edge>& currentEdges);
146
155
166 edge newEdge(node source, node target, const edge originalEdge);
167
176 void moveSource(edge e, node v);
177
186 void moveTarget(edge e, node v);
187
199 void setTerminal(const node v, bool makeTerminal);
200
213 bool isTerminal(const node v) const;
214
227 void setEdgeLookup(const node u, const node v, const edge e);
228
241 edge lookupEdge(const node u, const node v) const;
242
256 edge determineBranchingEdge(T prevCost) const;
257
265 T solve(List<edge>& chosenEdges);
266
271 void printSVG();
272};
273
274template<typename T>
276 const List<node>& terminals, const NodeArray<bool>& isTerminal,
277 EdgeWeightedGraphCopy<T>*& finalSteinerTree) {
278 m_originalGraph = &G;
279 m_originalTerminals = &terminals;
280
281 m_upperBound = MAX_WEIGHT;
282 m_graph.clear();
283 m_mapping.init(m_graph);
284 m_terminals.reset(new NodeSet(m_graph));
285 int nodeCount = m_originalGraph->numberOfNodes();
286 m_edges = Array2D<edge>(0, nodeCount, 0, nodeCount, nullptr);
287
288 NodeArray<node> copiedNodes(*m_originalGraph);
289
290 for (node v : m_originalGraph->nodes) {
291 node copiedNode = m_graph.newNode();
292 copiedNodes[v] = copiedNode;
293 }
294
295 for (edge e : m_originalGraph->edges) {
296 node source = copiedNodes[e->source()], target = copiedNodes[e->target()];
297
298 newEdge(source, target, e);
299 }
300
301 for (node v : *m_originalTerminals) {
302 setTerminal(copiedNodes[v], true);
303 }
304
305 List<edge> chosenEdges;
306 T result = solve(chosenEdges);
307
308 finalSteinerTree = new EdgeWeightedGraphCopy<T>();
309 finalSteinerTree->setOriginalGraph(*m_originalGraph);
310
311 for (edge e : chosenEdges) {
312 node v = e->source();
313 node w = e->target();
314
315 OGDF_ASSERT(v != nullptr);
316 OGDF_ASSERT(w != nullptr);
317 OGDF_ASSERT(e->graphOf() == m_originalGraph);
318 OGDF_ASSERT(v->graphOf() == m_originalGraph);
319 OGDF_ASSERT(w->graphOf() == m_originalGraph);
320
321 if (finalSteinerTree->copy(v) == nullptr) {
322 finalSteinerTree->newNode(v);
323 }
324 if (finalSteinerTree->copy(w) == nullptr) {
325 finalSteinerTree->newNode(w);
326 }
327 finalSteinerTree->newEdge(e, m_originalGraph->weight(e));
328 }
329
330 return result;
331}
332
333template<typename T>
335 T result = MAX_WEIGHT;
336
337 if (e != nullptr) {
338 OGDF_ASSERT(e->graphOf() == &m_graph);
339 OGDF_ASSERT(m_mapping[e] != nullptr);
340 OGDF_ASSERT(m_mapping[e]->graphOf() == m_originalGraph);
341 result = m_originalGraph->weight(m_mapping[e]);
342 }
343
344 return result;
345}
346
347template<typename T>
349 for (edge e : m_graph.edges) {
350 OGDF_ASSERT(m_mapping[e] != nullptr);
351 OGDF_ASSERT(m_mapping[e]->graphOf() == m_originalGraph);
352 }
353 return true;
354}
355
356template<typename T>
358 return m_edges(u->index(), v->index());
359}
360
361template<typename T>
362void MinSteinerTreeShore<T>::setEdgeLookup(const node u, const node v, const edge e) {
363 m_edges(u->index(), v->index()) = m_edges(v->index(), u->index()) = e;
364}
365
366template<typename T>
368 edge result = m_mapping[e];
369 setEdgeLookup(e->source(), e->target(), nullptr);
370 m_graph.delEdge(e);
371 e->~EdgeElement();
372
373 return result;
374}
375
376template<typename T>
378 edge result = m_graph.newEdge(source, target);
379 m_mapping[result] = e;
380 setEdgeLookup(source, target, result);
381
382 return result;
383}
384
385template<typename T>
387 OGDF_ASSERT(e != nullptr);
388 setEdgeLookup(e->source(), e->target(), nullptr);
389 setEdgeLookup(v, e->target(), e);
390 m_graph.moveSource(e, v);
391}
392
393template<typename T>
395 OGDF_ASSERT(e != nullptr);
396 setEdgeLookup(e->source(), e->target(), nullptr);
397 setEdgeLookup(e->source(), v, e);
398 m_graph.moveTarget(e, v);
399}
400
401template<typename T>
403 return m_terminals->isMember(v);
404}
405
406template<typename T>
407void MinSteinerTreeShore<T>::setTerminal(const node v, bool makeTerminal) {
408 if (makeTerminal) {
409 m_terminals->insert(v);
410 } else {
411 m_terminals->remove(v);
412 }
413}
414
415template<typename T>
417 m_chosenEdges.clear();
418
419 m_recursionDepth = 0;
420 List<edge> tmp;
421 T result = bnbInternal(0, tmp);
422
423 for (edge e : m_chosenEdges) {
424 chosenEdges.pushFront(e);
425 }
426
427 return result;
428}
429
430template<typename T>
432 edge result = nullptr;
433 T maxPenalty = -1;
434
435 // calculate penalties for nodes
436 T sumOfMinWeights = 0; // b
437 T sumOfMinTermWeights = 0; // c
438 T absoluteMinTermWeight = MAX_WEIGHT;
439 for (ListConstIterator<node> it = m_terminals->nodes().begin();
440 sumOfMinWeights < MAX_WEIGHT && it.valid(); ++it) {
441 const node t = *it;
442 T minTermWeight = MAX_WEIGHT, minWeight = MAX_WEIGHT, secondMinWeight = MAX_WEIGHT;
443 edge minEdge = nullptr;
444
445 // investigate all edges of each terminal
446 // calculate lower boundary and find branching edge
447 for (adjEntry adj = t->firstAdj(); adj; adj = adj->succ()) {
448 edge e = adj->theEdge();
449 if (weightOf(e) < minWeight) {
450 secondMinWeight = minWeight;
451 minWeight = weightOf(e);
452 minEdge = e;
453 } else {
454 if (weightOf(e) < secondMinWeight) {
455 secondMinWeight = weightOf(e);
456 }
457 }
458
459 if (isTerminal(adj->twinNode()) && weightOf(e) < minTermWeight) {
460 minTermWeight = weightOf(e);
461 if (minTermWeight < absoluteMinTermWeight) {
462 absoluteMinTermWeight = minTermWeight;
463 }
464 }
465 }
466
467 if (sumOfMinTermWeights < MAX_WEIGHT && minTermWeight < MAX_WEIGHT) {
468 sumOfMinTermWeights += minTermWeight;
469 } else {
470 sumOfMinTermWeights = MAX_WEIGHT;
471 }
472 OGDF_ASSERT(absoluteMinTermWeight <= sumOfMinTermWeights);
473
474 // is terminal isolated or has only one edge?
475 // if so we can break here
476 if (minWeight == MAX_WEIGHT || secondMinWeight == MAX_WEIGHT) {
477 result = minEdge;
478 if (minWeight == MAX_WEIGHT) {
479 sumOfMinWeights = MAX_WEIGHT;
480 }
481 } else {
482 sumOfMinWeights += minWeight;
483 // update branching edge if need be
484 T penalty = secondMinWeight - minWeight;
485 if (penalty > maxPenalty) {
486 maxPenalty = penalty;
487 result = minEdge;
488 }
489 }
490 }
491
492 // compare bounds for this graph
493 if (result != nullptr) {
494 T maxCost = m_upperBound - prevCost;
495 if (sumOfMinTermWeights < MAX_WEIGHT) {
496 sumOfMinTermWeights -= absoluteMinTermWeight;
497 }
498 if (maxCost <= sumOfMinWeights && maxCost <= sumOfMinTermWeights) {
499 result = nullptr;
500 }
501 }
502
503 return result;
504}
505
506template<typename T>
508 T result = MAX_WEIGHT;
509 m_recursionDepth++;
510
511#ifdef OGDF_MINSTEINERTREE_SHORE_LOGGING
512 printSVG();
513#endif
514
515 if (prevCost <= m_upperBound) {
516 if (m_terminals->size() < 2) {
517 // update currently chosen edges
518 if (prevCost != m_upperBound || m_chosenEdges.empty()) {
519 m_chosenEdges = List<edge>(currentEdges);
520 }
521 // all terminals are connected
522 m_upperBound = prevCost;
523 result = prevCost;
524 } else {
525 edge branchingEdge = determineBranchingEdge(prevCost);
526 T branchingEdgeWeight = weightOf(branchingEdge);
527
528#ifdef OGDF_MINSTEINERTREE_SHORE_LOGGING
529 for (int i = 0; i < m_recursionDepth; i++) {
530 std::cout << " ";
531 }
532 std::cout << "branching on edge: " << branchingEdge << std::endl;
533#endif
534 // branching edge has been found or there is no feasible solution
535 if (branchingEdgeWeight < MAX_WEIGHT) {
536 // chose node to remove
537 node nodeToRemove = branchingEdge->source();
538 node targetNode = branchingEdge->target();
539
540 // This seems to speed up things.
541 if (nodeToRemove->degree() < targetNode->degree()) {
542 nodeToRemove = branchingEdge->target();
543 targetNode = branchingEdge->source();
544 }
545
546 // remove branching edge
547 edge origBranchingEdge = deleteEdge(branchingEdge);
548
549 List<node> delEdges, movedEdges;
550 List<edge> origDelEdges;
551
552 // first branch: Inclusion of the edge
553 // remove source node of edge and calculate new edge weights
554 OGDF_ASSERT(targetNode != nullptr);
555 OGDF_ASSERT(nodeToRemove != nullptr);
556
557 // remove edges in case of multigraph
558 while (m_graph.searchEdge(targetNode, nodeToRemove) != nullptr) {
559 edge e = m_graph.searchEdge(targetNode, nodeToRemove);
560 delEdges.pushFront(e->target());
561 delEdges.pushFront(e->source());
562 origDelEdges.pushFront(m_mapping[e]);
563 deleteEdge(e);
564 }
565 while (m_graph.searchEdge(nodeToRemove, targetNode) != nullptr) {
566 edge e = m_graph.searchEdge(targetNode, nodeToRemove);
567 delEdges.pushFront(e->target());
568 delEdges.pushFront(e->source());
569 origDelEdges.pushFront(m_mapping[e]);
570 deleteEdge(e);
571 }
572
573 OGDF_ASSERT(m_graph.searchEdge(targetNode, nodeToRemove) == nullptr);
574 OGDF_ASSERT(m_graph.searchEdge(nodeToRemove, targetNode) == nullptr);
575
576 adjEntry adjNext;
577 for (adjEntry adj = nodeToRemove->firstAdj(); adj; adj = adjNext) {
578 adjNext = adj->succ();
579 edge e = adj->theEdge();
580
581 OGDF_ASSERT(e != branchingEdge);
582 OGDF_ASSERT(e->target() == nodeToRemove || e->source() == nodeToRemove);
583 OGDF_ASSERT(adj->twinNode() != targetNode);
584
585 edge f = lookupEdge(targetNode, adj->twinNode());
586 bool deletedEdgeE = false;
587 if (f != nullptr) {
588 if (weightOf(f) < weightOf(e)) {
589 delEdges.pushFront(e->target());
590 delEdges.pushFront(e->source());
591 origDelEdges.pushFront(m_mapping[e]);
592 deleteEdge(e);
593
594 deletedEdgeE = true;
595 } else {
596 delEdges.pushFront(f->target());
597 delEdges.pushFront(f->source());
598 origDelEdges.pushFront(m_mapping[f]);
599 deleteEdge(f);
600 }
601 }
602 if (!deletedEdgeE) {
603 if (e->target() == nodeToRemove) {
604 OGDF_ASSERT(e->source() != targetNode);
605 movedEdges.pushFront(e->source());
606 moveTarget(e, targetNode);
607 } else {
608 OGDF_ASSERT(e->source() == nodeToRemove);
609 OGDF_ASSERT(e->target() != targetNode);
610 movedEdges.pushFront(e->target());
611 moveSource(e, targetNode);
612 }
613 }
614 }
615 // nodeToRemove is isolated at this point
616 // thus no need to actually remove it
617 // (easier to keep track of CopyGraph mapping)
618
619 // remove node from terminals too
620 bool targetNodeIsTerminal = isTerminal(targetNode),
621 nodeToRemoveIsTerminal = isTerminal(nodeToRemove);
622
623 OGDF_ASSERT(targetNodeIsTerminal || nodeToRemoveIsTerminal);
624 setTerminal(nodeToRemove, false);
625 setTerminal(targetNode, true);
626
627#ifdef OGDF_MINSTEINERTREE_SHORE_LOGGING
628 for (int i = 0; i < m_recursionDepth; i++) {
629 std::cout << " ";
630 }
631 std::cout << "inclusion branch" << std::endl;
632#endif
633 // calculate result on modified graph
634 currentEdges.pushFront(origBranchingEdge);
635 result = bnbInternal(branchingEdgeWeight + prevCost, currentEdges);
636 OGDF_ASSERT(currentEdges.front() == origBranchingEdge);
637 currentEdges.popFront();
638
639 // restore previous graph
640
641 // restore terminals
642 setTerminal(nodeToRemove, nodeToRemoveIsTerminal);
643 setTerminal(targetNode, targetNodeIsTerminal);
644
645 // restore moved edges
646 while (!movedEdges.empty()) {
647 node v = movedEdges.popFrontRet();
648
649 edge e = lookupEdge(v, targetNode);
650 OGDF_ASSERT(e != nullptr);
651 OGDF_ASSERT(e->opposite(targetNode) != nodeToRemove);
652
653 if (e->source() == v) {
654 moveTarget(e, nodeToRemove);
655 } else {
656 moveSource(e, nodeToRemove);
657 }
658 }
659
660 // restore deleted edges
661 while (!delEdges.empty()) {
662 OGDF_ASSERT(!origDelEdges.empty());
663
664 node source = delEdges.popFrontRet();
665 node target = delEdges.popFrontRet();
666
667 newEdge(source, target, origDelEdges.popFrontRet());
668 }
669 OGDF_ASSERT(origDelEdges.empty());
670
671#ifdef OGDF_MINSTEINERTREE_SHORE_LOGGING
672 for (int i = 0; i < m_recursionDepth; i++) {
673 std::cout << " ";
674 }
675 std::cout << "exclusion branch" << std::endl;
676#endif
677 // sencond branch: Exclusion of the edge
678 T exEdgeResult = bnbInternal(prevCost, currentEdges);
679
680 // decide which branch returned best result
681 if (exEdgeResult < result) {
682 result = exEdgeResult;
683 }
684
685 // finally: restore the branching edge
686 newEdge(nodeToRemove, targetNode, origBranchingEdge);
687 }
688 }
689 OGDF_ASSERT(validateMapping());
690 }
691 m_recursionDepth--;
692 return result;
693}
694
695template<typename T>
697 EdgeWeightedGraphCopy<T> copiedGraph;
698 copiedGraph.setOriginalGraph(m_graph);
699 List<node> nodes;
700 m_graph.allNodes(nodes);
701 NodeArray<bool> copiedIsTerminal(m_graph);
702 for (node v : nodes) {
703 copiedGraph.newNode(v);
704 copiedIsTerminal[v] = isTerminal(v);
705 }
706 List<edge> edges;
707 m_graph.allEdges(edges);
708 for (edge e : edges) {
709 copiedGraph.newEdge(e, weightOf(e));
710 }
711 std::stringstream filename;
712 filename << "bnb_internal_" << m_recursionDepth << ".svg";
713 this->drawSteinerTreeSVG(copiedGraph, copiedIsTerminal, filename.str().c_str());
714}
715
716}
Declaration and implementation of class Array2D which implements dynamic two dimensional arrays.
Extends the GraphCopy concept to weighted graphs.
Includes declaration of graph class.
Decralation of GraphElement and GraphList classes.
Declaration and implementation of NodeSet, EdgeSet, and AdjEntrySet classes.
Declaration of doubly linked lists and iterators.
Declaration of ogdf::MinSteinerTreeModule.
Basic declarations, included by all source files.
Class for adjacency list elements.
Definition Graph_d.h:143
adjEntry succ() const
Returns the successor in the adjacency list.
Definition Graph_d.h:219
The parameterized class Array2D implements dynamic two-dimensional arrays.
Definition Array2D.h:53
Class for the representation of edges.
Definition Graph_d.h:364
node opposite(node v) const
Returns the adjacent node different from v.
Definition Graph_d.h:414
const Graph * graphOf() const
Returns the graph containing this node (debug only).
Definition Graph_d.h:441
node target() const
Returns the target node of the edge.
Definition Graph_d.h:402
node source() const
Returns the source node of the edge.
Definition Graph_d.h:399
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.
node newNode(node vOrig)
Creates a new node in the graph copy with original node vOrig.
Definition GraphCopy.h:191
edge copy(edge e) const override
Returns the first edge in the list of edges corresponding to edge e.
Definition GraphCopy.h:463
Data type for general directed graphs (adjacency list representation).
Definition Graph_d.h:866
Doubly linked lists (maintaining the length of the list).
Definition List.h:1451
void popFront()
Removes the first element from the list.
Definition List.h:1585
void clear()
Removes all elements from the list.
Definition List.h:1626
iterator pushFront(const E &x)
Adds element x at the beginning of the list.
Definition List.h:1534
E popFrontRet()
Removes the first element from the list and returns it.
Definition List.h:1591
Encapsulates a pointer to a list element.
Definition List.h:113
const_reference front() const
Returns a const reference to the first element.
Definition List.h:305
bool empty() const
Returns true iff the list is empty.
Definition List.h:286
Serves as an interface for various methods to compute or approximate minimum Steiner trees on undirec...
Implementation of Shore, Foulds and Gibbons exact branch and bound algorithm for solving Steiner tree...
void moveSource(edge e, node v)
Moves the source of the edge to the specified node.
bool validateMapping() const
Used to validate the current mapping of edges to orignal edges Used solely for debugging.
T solve(List< edge > &chosenEdges)
Solves the current STP instance.
const EdgeWeightedGraph< T > * m_originalGraph
bool isTerminal(const node v) const
Returns whether this node is a terminal or a Steiner node.
void setEdgeLookup(const node u, const node v, const edge e)
Sets the edge incident to both node u and v.
edge lookupEdge(const node u, const node v) const
Retrieves the edge incident to both node u and v.
void setTerminal(const node v, bool makeTerminal)
Updates the status of the given node to either terminal or Steiner node.
edge deleteEdge(edge e)
Removes the specified edge from the graph.
virtual T computeSteinerTree(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, EdgeWeightedGraphCopy< T > *&finalSteinerTree) override
Builds a minimum Steiner tree given a weighted graph and a list of terminals.
T bnbInternal(T prevCost, List< edge > &currentEdges)
Calculates the optimal Steinter tree recursivly.
const List< node > * m_originalTerminals
void moveTarget(edge e, node v)
Moves the target of the edge to the specified node.
edge newEdge(node source, node target, const edge originalEdge)
Creates a new edge.
edge determineBranchingEdge(T prevCost) const
Decides which edge to branch on.
void printSVG()
Prints the current recursion status as a SVG image of the current reduced STP.
T weightOf(edge e) const
Returns the cost of the specified edge.
std::unique_ptr< NodeSet > m_terminals
Class for the representation of nodes.
Definition Graph_d.h:241
int degree() const
Returns the degree of the node (indegree + outdegree).
Definition Graph_d.h:284
int index() const
Returns the (unique) node index.
Definition Graph_d.h:275
adjEntry firstAdj() const
Returns the first entry in the adjaceny list.
Definition Graph_d.h:287
const Graph * graphOf() const
Returns the graph containing this node (debug only).
Definition Graph_d.h:345
Node sets.
Definition GraphSets.h:54
RegisteredArray for edges of a graph, specialized for EdgeArray<edge>.
Definition Graph_d.h:717
RegisteredArray for nodes, edges and adjEntries of a graph.
Definition Graph_d.h:659
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition basic.h:52
The namespace for all OGDF objects.
Definition GML.h:111