Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
MinSteinerTreeDirectedCut.h
Go to the documentation of this file.
1
42#pragma once
43
44#include <ogdf/basic/Array.h>
47#include <ogdf/basic/Graph.h>
49#include <ogdf/basic/List.h>
50#include <ogdf/basic/Logger.h>
52#include <ogdf/basic/basic.h>
61
63#include <ogdf/lib/abacus/opensub.h> // IWYU pragma: keep
64
65#include <algorithm>
66#include <iomanip>
67#include <iostream>
68#include <memory>
69
70// turn on/off logging for STP b&c algorithm
71//#define OGDF_STP_EXACT_LOGGING
72
73// TODO: Add module option for max flow algorithm
74
75namespace ogdf {
76
78
81template<typename T>
83protected:
84 // all the settings
85 const char* m_configFile;
86 double m_eps;
87#ifdef OGDF_STP_EXACT_LOGGING
88 Logger::Level m_outputLevel;
89#endif
90 std::unique_ptr<MaxFlowModule<double>> m_maxFlowModuleOption;
105
106 class DegreeConstraint;
109 class EdgeConstraint;
110 class EdgeVariable;
111 // Abacus LP classes
112 class Sub;
113
114public:
115 class Master;
116
117 // a lot of setter methods
119 void setEpsilon(double eps) { m_eps = eps; }
120
122 void setConfigFile(const char* configfile) { m_configFile = configfile; }
123#ifdef OGDF_STP_EXACT_LOGGING
125 void setOutputLevel(Logger::Level outputLevel) { m_outputLevel = outputLevel; }
126#endif
129
132
135
138
141
144
147
149 void useBackCuts(bool b) { m_backCutComputation = b; }
150
153
156
159
162
165
168 OGDF_ASSERT(b >= 0);
169 OGDF_ASSERT(b <= 2);
171 }
172
175
177 : m_configFile(nullptr)
178 , m_eps(1e-6)
179#ifdef OGDF_STP_EXACT_LOGGING
180 , m_outputLevel(Logger::Level::Default)
181#endif
188 , m_shuffleTerminals(true)
195 , m_primalHeuristic(nullptr)
197 }
198
199protected:
200 virtual T computeSteinerTree(const EdgeWeightedGraph<T>& G, const List<node>& terminals,
201 const NodeArray<bool>& isTerminal, EdgeWeightedGraphCopy<T>*& finalSteinerTree) override;
202};
203
205template<typename T>
207public:
217 Master(const EdgeWeightedGraph<T>& wG, const List<node>& terminals,
218 const NodeArray<bool>& isTerminal, double eps, bool relaxed = false);
219
221 virtual ~Master() {
222 delete[] m_bestSolution;
223 delete[] m_terminals;
224 delete[] m_nodes;
225 delete[] m_edges;
226 delete m_pGraph;
227 delete m_pWeightedGraphPH;
228 delete m_pCutPool;
229 }
230
232 void setConfigFile(const char* filename) { m_configfile = filename; }
233#ifdef OGDF_STP_EXACT_LOGGING
235 void setOutputLevel(Level outputLevel) {
236 this->globalLogLevel(Level::Default);
237 this->localLogMode(LogMode::Log);
238 if (outputLevel >= Level::Minor && outputLevel <= Level::Force) {
239 this->localLogLevel((Level)outputLevel);
240 this->globalMinimumLogLevel((Level)outputLevel);
241 }
242 }
243#endif
245 void setMaxFlowModule(MaxFlowModule<double>* module) { m_maxFlowModule = module; }
246
248 MaxFlowModule<double>* getMaxFlowModule() { return m_maxFlowModule; }
249
252
255
258
261
265 this->maxConAdd(m_maxNrAddedCuttingPlanes);
266 this->maxConBuffered(m_maxNrAddedCuttingPlanes);
267 }
268
271
273 void useBackCuts(bool b) { m_backCutComputation = b; }
274
277
280 OGDF_ASSERT(b >= 1);
281 OGDF_ASSERT(b <= 2);
283 }
284
287 OGDF_ASSERT(b >= 1);
288 OGDF_ASSERT(b <= 2);
290 }
291
294
297 OGDF_ASSERT(b >= 0);
298 OGDF_ASSERT(b <= 2);
300 }
301
304
307 m_primalHeuristic.reset(pMinSteinerTreeModule);
308 }
309
311 std::unique_ptr<MinSteinerTreeModule<double>>& getPrimalHeuristic() {
312 return m_primalHeuristic;
313 }
314
317
319 virtual abacus::Sub* firstSub() override { return new Sub(this); }
320
322 bool isSolutionEdge(edge e) const {
323 return m_isSolutionEdge[m_mapToBidirectedGraph1[e]]
324 || m_isSolutionEdge[m_mapToBidirectedGraph2[e]];
325 }
326
328 const Graph& graph() const { return *m_pGraph; }
329
331 int nNodes() const { return m_pGraph->numberOfNodes(); }
332
334 int nEdges() const { return m_pGraph->numberOfEdges(); }
335
337 int nEdgesU() const { return m_nEdgesU; }
338
340 node rootNode() const { return m_root; }
341
343 int nTerminals() const { return m_nTerminals; }
344
346 const node* terminals() const { return m_terminals; }
347
349 node terminal(int i) const { return m_terminals[i]; }
350
352 bool isTerminal(node n) const { return m_isTerminal[n]; }
353
355 const NodeArray<bool> isTerminal() const { return m_isTerminal; }
356
358 int edgeID(edge e) const { return m_edgeIDs[e]; }
359
361 int nodeID(node n) const { return m_nodeIDs[n]; }
362
364 node* nodes() const { return m_nodes; }
365
367 const NodeArray<int>& nodeIDs() const { return m_nodeIDs; }
368
370 const EdgeArray<int>& edgeIDs() const { return m_edgeIDs; }
371
373 edge getEdge(int i) const { return m_edges[i]; }
374
376 node getNode(int i) const { return m_nodes[i]; }
377
379 const EdgeArray<double>& capacities() const { return m_capacities; }
380
382 double capacities(edge e) const { return m_capacities[e]; }
383
385 edge twin(edge e) const { return m_twin[e]; }
386
387 // the twin edge array
388 const EdgeArray<edge>& twins() const { return m_twin; }
389
391 EdgeVariable* getVar(edge e) const { return m_edgeToVar[e]; }
392
394 EdgeVariable* getVarTwin(edge e) const { return m_edgeToVar[m_twin[e]]; }
395
397 bool relaxed() const { return m_relaxed; }
398
400 double solutionValue() const { return this->primalBound(); }
401
403 double* bestSolution() const { return m_bestSolution; }
404
406 void updateBestSolution(double* values);
408 void updateBestSolutionByEdges(const List<edge>& sol);
409
411 void setRelaxedSolValue(double val) { m_relaxedSolValue = val; }
412
414 void setNIterRoot(int val) { m_nIterRoot = val; }
415
418
421
423 bool computeBackCuts() const { return m_backCutComputation; }
424
427
429 bool callPrimalHeuristic() const { return m_callPrimalHeuristic > 0; }
430
433
436
439
441 bool shuffleTerminals() const { return m_shuffleTerminals; }
442
444 int maxPoolSize() const { return m_maxPoolSize; }
445
448 if (m_poolSizeMax < m_pCutPool->size()) {
449 m_poolSizeMax = m_pCutPool->size();
450 }
451 }
452
454 int poolSizeInit() const { return m_poolSizeInit; }
455
457 void incNrCutsTotal(int val) { m_nrCutsTotal += val; }
458
460 void incNrCutsTotal() { m_nrCutsTotal++; }
461
463 int nrCutsTotal() const { return m_nrCutsTotal; }
464
465 /*
466 * Methods for primal heuristics.
467 * Naming convention: suffix "PH"
468 */
469 /*
470 * Edge weighted bidirected graph; used and modified for primal heuristics.
471 * Required for calling MinSteinerTreeModule algorihms
472 */
473 EdgeWeightedGraph<double>& weightedGraphPH() { return *m_pWeightedGraphPH; }
474
476 const List<node>& terminalListPH() const { return m_terminalListPH; }
477
479 const NodeArray<bool>& isTerminalPH() const { return m_isTerminalPH; }
480
482 node rootNodePH() const { return m_rootPH; }
483
485 edge edgeGToWgPH(edge e) const { return m_edgesGToWgPH[e]; }
486
488 edge edgeWgToGPH(edge e) const { return m_edgesWgToGPH[e]; }
489
490 /*
491 * some additional timers
492 */
494 StopwatchWallClock* separationTimer() { return &m_separationTimer; }
495
497 StopwatchWallClock* timerMinSTCut() { return &m_timerMinSTCut; }
498
500 StopwatchWallClock* primalHeuristicTimer() { return &m_primalHeuristicTimer; }
501
502protected:
504 virtual void initializeOptimization() override;
506 virtual void terminateOptimization() override;
508 virtual void initializeParameters() override;
509
510private:
512
514 const char* m_configfile;
515
517 std::unique_ptr<MinSteinerTreeModule<double>> m_primalHeuristic;
518
521
524
527
530
533
542
547
554
565
568
572
573 /*
574 * Stuff for primal heuristics
575 */
590
603
612
622 /*
623 * basic strategy:
624 * Computes mincut between the root and a terminal. Saturates cutedges afterwards.
625 * Continues with same terminal until no violated cut is found. Considers next terminal.
626 * 1: When switching to next terminal saturated edges remain saturated (default)
627 * 2: When switching to next terminal saturated edges are reset to original capacity
628 */
631 /*
632 * for all cutedges e:
633 * 1: capacity[e]=1 (default)
634 * 2: capacity[e]=1/C with C=nr of cutedges
635 */
637
639 /*
640 * adds epsilon to each arc capacity before computing the minimum cut
641 */
643
645 /*
646 * 0: no PH
647 * 1: call PH right before branchting
648 * 2: call PH every iteration
649 */
651
658
659 Master(const Master& rhs);
660 const Master& operator=(const Master& rhs);
661};
662
664template<typename T>
666public:
668 explicit Sub(abacus::Master* master);
670 Sub(abacus::Master* master, abacus::Sub* father, abacus::BranchRule* branchRule);
671
673 virtual ~Sub() { }
674
675#ifdef OGDF_STP_EXACT_LOGGING
677 void printCurrentSolution(bool onlyNonZeros = true);
678#endif
679
680protected:
682 virtual bool feasible() override;
683
685 virtual int separate() override {
686 if (m_alreadySeparated == -1) {
687 m_alreadySeparated = mySeparate();
688 }
689 return m_alreadySeparated;
690 }
691
693 int mySeparate();
694
696 void myImprove();
697
698#ifdef OGDF_STP_EXACT_LOGGING
700 void printMainInfosInFeasible(bool header = false) const;
701#endif
702
703private:
704#ifdef OGDF_STP_EXACT_LOGGING
706 void printConstraint(abacus::Constraint* constraint,
708#endif
709
712
715
730
732 /*
733 * 0: no PH
734 * 1: call PH right before branchting
735 * 2: call PH every iteration
736 */
738
740 virtual Sub* generateSon(abacus::BranchRule* rule) override {
741 return new Sub(master_, this, rule);
742 }
743};
744
746template<typename T>
748public:
750#if 0
751 const abacus::Sub *sub,
752#endif
753 int id, edge e, double coeff, double lb = 0.0, double ub = 1.0,
755 : abacus::Variable(master, nullptr /*, sub*/, false, false, coeff, lb, ub, vartype)
756 , m_edge(e)
757 , m_id(id) {
758 }
759
761 edge theEdge() const { return m_edge; }
762
764 int id() const { return m_id; }
765
767 double coefficient() const { return this->obj(); }
768
770 node source() const { return m_edge->source(); }
771
773 node target() const { return m_edge->target(); }
774
775private:
779 int m_id;
780};
781
783template<typename T>
785public:
786 EdgeConstraint(abacus::Master* master, edge e1, edge e2, int factor = 1.0,
787 abacus::CSense::SENSE sense = abacus::CSense::Less, double rhs = 1.0)
788 : abacus::Constraint(master, nullptr, sense, rhs, false, false, false)
789 , m_e1(e1)
790 , m_e2(e2)
791 , m_factor(factor) { }
792
794 double coeff(const abacus::Variable* v) const override {
795 EdgeVariable* edgeVar = (EdgeVariable*)v;
796 edge e = edgeVar->theEdge();
797 if (e != m_e1 && e != m_e2) {
798 return 0.0;
799 }
800 return m_factor;
801 }
802
803private:
810};
811
813template<typename T>
815public:
816 DegreeConstraint(abacus::Master* master, node n, double coeffIn, double coeffOut,
817 abacus::CSense::SENSE sense, double rhs)
818 : abacus::Constraint(master, nullptr, sense, rhs, false, false, false)
819 , m_node(n)
820 , m_coeffIn(coeffIn)
821 , m_coeffOut(coeffOut) { }
822
824 virtual double coeff(const abacus::Variable* v) const override {
825 EdgeVariable* edgeVar = (EdgeVariable*)v;
826 edge e = edgeVar->theEdge();
827 if (e->target() == m_node) {
828 return m_coeffIn;
829 } else {
830 if (e->source() == m_node) {
831 return m_coeffOut;
832 } else {
833 return 0.0;
834 }
835 }
836 }
837
839 node theNode() const { return m_node; }
840
841private:
845 double m_coeffIn;
848};
849
851template<typename T>
853public:
854 DegreeEdgeConstraint(abacus::Master* master, edge e, double coeffIn, double coeffEdge,
855 abacus::CSense::SENSE sense, double rhs)
856 : abacus::Constraint(master, nullptr, sense, rhs, false, false, false)
857 , m_edge(e)
858 , m_coeffIn(coeffIn)
859 , m_coeffEdge(coeffEdge) { }
860
862 double coeff(const abacus::Variable* v) const override {
863 EdgeVariable* edgeVar = (EdgeVariable*)v;
864 edge e = edgeVar->theEdge();
865 // the edge
866 if (e->isParallelDirected(m_edge)) {
867 return m_coeffEdge;
868 }
869 // all edges to vertices x != source
870 if (e->target() != m_edge->source()) {
871 return 0.0;
872 }
873 // reverse edge
874 if (e->source() == m_edge->target()) {
875 return 0.0;
876 }
877 // all ingoing edges of the source (except the reverse edge, of course)
878 return m_coeffIn;
879 }
880
882 edge theEdge() const { return m_edge; }
883
884private:
888 double m_coeffIn;
891};
892
894template<typename T>
896public:
899 : abacus::Constraint(master, nullptr, abacus::CSense::Greater, 1.0, false, false, false)
900 , m_pGraph(&g)
901 , m_name("") {
902#ifdef OGDF_STP_EXACT_LOGGING
903 std::cout << "Creating new DirectedCutConstraint: " << std::endl;
904#endif
905 m_hashKey = 0;
906 m_marked.init(g);
907 for (node n : g.nodes) {
909 m_marked[n] = minSTCut->isInFrontCut(n);
910#ifdef OGDF_STP_EXACT_LOGGING
911 if (m_marked[n]) {
912 // TODO: Use lout instead
913 std::cout << " marked node " << n << std::endl;
914 }
915#endif
916 } else {
918 m_marked[n] = !minSTCut->isInBackCut(n);
919 }
920 if (m_marked[n]) {
921 m_nMarkedNodes++;
922 m_hashKey += n->index();
923 }
924 }
925 m_hashKey += m_nMarkedNodes * g.numberOfNodes() * g.numberOfNodes();
926#ifdef OGDF_STP_EXACT_LOGGING
927 std::cout << " front cut edges:" << std::endl;
928 for (edge e : g.edges) {
929 if (minSTCut->isFrontCutEdge(e)) {
930 std::cout << " " << e << std::endl;
931 }
932 }
933 std::cout << " back cut edges:" << std::endl;
934 for (edge e : g.edges) {
935 if (minSTCut->isBackCutEdge(e)) {
936 std::cout << " " << e << std::endl;
937 }
938 }
939#endif
940 }
941
942 double coeff(const abacus::Variable* v) const override;
943
945 bool active(node n) const { return m_marked[n]; }
946
948 bool cutedge(edge e) const { return m_marked[e->source()] && !m_marked[e->target()]; }
949
951 int nMarkedNodes() const { return m_nMarkedNodes; }
952
954 bool marked(node n) const { return m_marked[n]; }
955
957 unsigned hashKey() const override { return m_hashKey; };
958
960 bool equal(const ConVar* cv) const override;
961
963 const char* name() const override { return m_name; }
964
965private:
968
970 /*
971 * A vertex is marked iff it is separated by the cut
972 * i.e., it is contained in the same set as the target
973 */
975
978
980 unsigned int m_hashKey;
982 const char* m_name;
983};
984
985template<typename T>
987 const List<node>& terminals, const NodeArray<bool>& isTerminal, double eps, bool relaxed)
988 : abacus::Master("MinSteinerTreeDirectedCut::Master", true, false, abacus::OptSense::Min, eps)
989 , m_maxFlowModule(nullptr)
990 , m_configfile(nullptr)
991 , m_relaxed(relaxed)
992 , m_relaxedSolValue(-1)
993 , m_nIterRoot(-1)
994 , m_wG(wG)
995 , m_pCutPool(nullptr)
996 , m_poolSizeInitFactor(5)
997 , m_poolSizeMax(0)
998 , m_maxPoolSize(-1)
999 , m_nrCutsTotal(0)
1000 , m_addGSEC2Constraints(true)
1001 , m_addDegreeConstraints(true)
1002 , m_addIndegreeEdgeConstraints(true)
1003 , m_addFlowBalanceConstraints(true)
1004 , m_maxNrAddedCuttingPlanes(500)
1005 , m_shuffleTerminals(true)
1006 , m_backCutComputation(true)
1007 , m_nestedCutComputation(true)
1008 , m_separationStrategy(1)
1009 , m_saturationStrategy(1)
1010 , m_minCardinalityCuts(true)
1011 , m_callPrimalHeuristic(1) {
1012#ifdef OGDF_STP_EXACT_LOGGING
1014 << "Master::Master(): default LP solver: " << this->OSISOLVER_[this->defaultLpSolver()]
1015 << std::endl;
1016#endif
1017 m_pGraph = new Graph();
1018
1019 edge e1, e2;
1020 int i = 0;
1021 int t = 0;
1022
1023 m_nodes = new node[m_wG.numberOfNodes()];
1024 m_nodeIDs.init(*m_pGraph);
1025 m_isTerminal.init(*m_pGraph);
1026 m_nTerminals = terminals.size();
1027 m_root = nullptr;
1028
1029#ifdef OGDF_STP_EXACT_LOGGING
1030 lout(Level::Default) << "Master::Master(): nTerminals=" << m_nTerminals << std::flush;
1031 lout(Level::Minor) << " terminals: " << std::flush;
1032#endif
1033 NodeArray<node> nodeMapping(m_wG);
1035
1036 for (node nOrig : m_wG.nodes) {
1037 node n = m_pGraph->newNode();
1038 nodeMapping[nOrig] = n;
1039 m_nodes[i] = n;
1040 m_nodeIDs[n] = i;
1041 m_isTerminal[n] = isTerminal[nOrig];
1042 if (m_isTerminal[n]) {
1043#ifdef OGDF_STP_EXACT_LOGGING
1044 lout(Level::Minor) << n << "," << std::flush;
1045#endif
1046 m_terminals[t++] = n;
1047 }
1048 i++;
1049 }
1050#ifdef OGDF_STP_EXACT_LOGGING
1051 lout(Level::Minor) << std::endl << "Master::Master(): edges: ";
1052#endif
1053
1054 m_nEdgesU = m_wG.numberOfEdges();
1055 m_capacities.init(*m_pGraph);
1056 m_twin.init(*m_pGraph);
1057 m_edgeIDs.init(*m_pGraph);
1058 m_edges = new edge[2 * m_nEdgesU];
1059
1063
1064 i = 0;
1065 for (edge eOrig : m_wG.edges) {
1066 e1 = m_pGraph->newEdge(nodeMapping[eOrig->source()], nodeMapping[eOrig->target()]);
1067 e2 = m_pGraph->newEdge(nodeMapping[eOrig->target()], nodeMapping[eOrig->source()]);
1068 m_capacities[e1] = m_capacities[e2] = m_wG.weight(eOrig);
1069 m_twin[e1] = e2;
1070 m_twin[e2] = e1;
1071 m_edges[i] = e1;
1072 m_edgeIDs[e1] = i++;
1073 m_edges[i] = e2;
1074 m_edgeIDs[e2] = i++;
1075 m_mapToOrigGraph[e1] = eOrig;
1076 m_mapToOrigGraph[e2] = eOrig;
1077 m_mapToBidirectedGraph1[eOrig] = e1;
1078 m_mapToBidirectedGraph2[eOrig] = e2;
1079#ifdef OGDF_STP_EXACT_LOGGING
1080 lout(Level::Minor) << " " << eOrig << "[" << e1 << ", " << e2 << "]" << std::flush;
1081#endif
1082 }
1083#ifdef OGDF_STP_EXACT_LOGGING
1084 lout(Level::Default) << std::endl;
1085#endif
1086 for (node n : m_pGraph->nodes) {
1087 if (m_isTerminal[n]) {
1088 // possibility to set the root node
1089 // default: terminal with highest degree
1090 if (!m_root) {
1091 m_root = n;
1092 } else {
1093 if (m_root->degree() < n->degree()) {
1094 m_root = n;
1095 }
1096 }
1097 }
1098 }
1099
1100#ifdef OGDF_STP_EXACT_LOGGING
1101 lout(Level::Medium) << "Master::Master(): m_root=" << m_root << std::endl;
1102#endif
1103
1104 m_isSolutionEdge.init(*m_pGraph, false);
1105 m_bestSolution = new double[m_pGraph->numberOfEdges()];
1106 for (i = 0; i < m_pGraph->numberOfEdges(); i++) {
1107 m_bestSolution[i] = 1.0;
1108 }
1109
1110 // stuff for "primal heuristic"
1112 m_nodesGToWgPH.init(*m_pGraph);
1113 m_edgesGToWgPH.init(*m_pGraph);
1116
1117 for (node nOrig : m_pGraph->nodes) {
1119 m_nodesGToWgPH[nOrig] = n;
1120 m_isTerminalPH[n] = m_isTerminal[nOrig];
1121 if (m_isTerminalPH[n]) {
1122 m_terminalListPH.pushBack(n);
1123 }
1124 if (m_root == nOrig) {
1125 m_rootPH = n;
1126 }
1127 }
1128
1129 for (edge eOrig : m_pGraph->edges) {
1130 edge e = m_pWeightedGraphPH->newEdge(m_nodesGToWgPH[eOrig->source()],
1131 m_nodesGToWgPH[eOrig->target()], 0.0);
1132 m_edgesGToWgPH[eOrig] = e;
1133 m_edgesWgToGPH[e] = eOrig;
1134 }
1135
1136 // set default primal heuristic module to takahashi algorithm
1138}
1139
1140template<typename T>
1142 if (m_configfile) {
1143 bool objectiveInteger = false;
1144 try {
1145 this->readParameters(m_configfile);
1146 } catch (const AlgorithmFailureException&) {
1147#ifdef OGDF_STP_EXACT_LOGGING
1148 lout(Level::Alarm) << "Master::initializeParameters(): Error reading parameters."
1149 << "Using default values." << std::endl;
1150#endif
1151 }
1152#ifdef OGDF_STP_EXACT_LOGGING
1153 int outputLevel;
1154 getParameter("OutputLevel", outputLevel);
1155 setOutputLevel(static_cast<Level>(outputLevel));
1156#endif
1157 int solver = (OSISOLVER)findParameter("DefaultLpSolver", 12, OSISOLVER_);
1158 this->defaultLpSolver((OSISOLVER)solver);
1159 getParameter("AddGSEC2Constraints", m_addGSEC2Constraints);
1160 getParameter("AddDegreeConstraints", m_addDegreeConstraints);
1161 getParameter("AddIndegreeEdgeConstraints", m_addIndegreeEdgeConstraints);
1162 getParameter("AddFlowBalanceConstraints", m_addFlowBalanceConstraints);
1163 getParameter("MaxNrCuttingPlanes", m_maxNrAddedCuttingPlanes);
1164 getParameter("ShuffleTerminals", m_shuffleTerminals);
1165 getParameter("BackCutComputation", m_backCutComputation);
1166 getParameter("NestedCutComputation", m_nestedCutComputation);
1167 getParameter("SeparationStrategy", m_separationStrategy);
1168 getParameter("SaturationStrategy", m_saturationStrategy);
1169 getParameter("MinCardinalityCuts", m_minCardinalityCuts);
1170 getParameter("PrimalHeuristic", m_callPrimalHeuristic);
1171 getParameter("PoolSizeInitFactor", m_poolSizeInitFactor);
1172 getParameter("ObjInteger", objectiveInteger);
1173 this->objInteger(objectiveInteger);
1174 }
1175
1176#ifdef OGDF_STP_EXACT_LOGGING
1177 lout(Level::High)
1178 << "Master::initializeParameters(): parameters:" << std::endl
1179 << "\tLP-Solver " << OSISOLVER_[this->defaultLpSolver()] << std::endl
1180 << "\tOutputLevel " << this->localLogLevel() << std::endl
1181 << "\tAddDegreeConstraints " << m_addDegreeConstraints << std::endl
1182 << "\tAddIndegreeEdgeConstraints " << m_addIndegreeEdgeConstraints << std::endl
1183 << "\tAddGSEC2Constraints " << m_addGSEC2Constraints << std::endl
1184 << "\tAddFlowBalanceConstraints " << m_addFlowBalanceConstraints << std::endl
1185 << "\tMaxNrCuttingPlanes " << m_maxNrAddedCuttingPlanes << std::endl
1186 << "\tShuffleTerminals " << m_shuffleTerminals << std::endl
1187 << "\tBackCutComputation " << m_backCutComputation << std::endl
1188 << "\tMinCardinalityCuts " << m_minCardinalityCuts << std::endl
1189 << "\tNestedCutComputation " << m_nestedCutComputation << std::endl;
1191 lout(Level::High) << "\t SeparationStrategy " << m_separationStrategy << std::endl
1192 << "\t SaturationStrategy " << m_saturationStrategy << std::endl;
1193 }
1194 lout(Level::High) << "\tPrimalHeuristic " << m_callPrimalHeuristic << std::endl
1195 << "\tPoolSizeInitFactor " << m_poolSizeInitFactor << std::endl
1196 << "\tObjective integer " << this->objInteger() << std::endl
1197 << std::endl;
1198#endif
1200}
1201
1202template<typename T>
1204#ifdef OGDF_STP_EXACT_LOGGING
1205 lout(Level::High) << "Master::initializeOptimization(): Instance properties:" << std::endl
1206 << "\t(nNodes,nEdges) : (" << m_pGraph->numberOfNodes() << ", "
1207 << m_nEdgesU << ")" << std::endl
1208 << "\tNumber of terminals : " << m_nTerminals << std::endl
1209 << "\tRoot node : " << m_root << std::endl
1210 << std::endl;
1211#endif
1212 int i;
1213
1214 // buffer for variables; one for each directed edge
1215 ArrayBuffer<abacus::Variable*> variables(m_pGraph->numberOfEdges());
1216
1217 // add (edge) variables
1218 EdgeVariable* eVar;
1219 m_edgeToVar.init(*m_pGraph, nullptr);
1220
1222 if (m_relaxed) {
1224 }
1225
1226 for (i = 0; i < m_pGraph->numberOfEdges(); i++) {
1227 edge e = m_edges[i];
1228 if (e->target() != m_root // not going into root
1229 && !e->isSelfLoop()) {
1230 eVar = new EdgeVariable(this, i, e, m_capacities[e], 0.0, 1.0, vartype);
1231#ifdef OGDF_STP_EXACT_LOGGING
1232 lout(Level::Minor) << "\tadding variable x_" << i << ", edge " << e << std::endl;
1233#endif
1234 } else {
1235 // ub = lb = 0 is not nice, but makes life easier -> ids
1236 OGDF_ASSERT(m_capacities[e] >= 0);
1237 eVar = new EdgeVariable(this, i, e, m_capacities[e], 0.0, 0.0, vartype);
1238#ifdef OGDF_STP_EXACT_LOGGING
1239 lout(Level::Minor) << "\tmuting variable x_" << i << ", edge " << e << std::endl;
1240#endif
1241 }
1242 variables.push(eVar);
1243 m_edgeToVar[e] = eVar;
1244 }
1245
1246 // add constraints
1247 int nCons = 0;
1249 nCons += m_nEdgesU;
1250 }
1252 nCons += m_pGraph->numberOfNodes();
1253 }
1255 nCons += m_pGraph->numberOfEdges();
1256 }
1258 nCons += m_pGraph->numberOfNodes() - 1;
1259 }
1260
1261 ArrayBuffer<abacus::Constraint*> basicConstraints(nCons);
1262
1263 i = 0;
1265 EdgeArray<bool> marked(*m_pGraph, false);
1266 for (edge e : m_pGraph->edges) {
1267 if (!marked[e] && !e->isSelfLoop()) { // we have to ignore self-loops here
1268 EdgeConstraint* newCon =
1269 new EdgeConstraint(this, e, m_twin[e], 1, abacus::CSense::Less, 1.0);
1270 basicConstraints.push(newCon);
1271 marked[e] = true;
1272 marked[m_twin[e]] = true;
1273
1274#ifdef OGDF_STP_EXACT_LOGGING
1275 lout(Level::Minor)
1276 << "\tadding constraint " << i++ << " GSEC2: edge " << e << std::endl;
1277#endif
1278 }
1279 }
1280 }
1281
1282 // "degree" constraints:
1283 // (1) forall terminals t != m_root: x(delta-(t)) == 1
1284 // (2) forall non-temrinals n : x(delta-(n)) <= 1
1285 // (3) for the root : x(delta+(m_root)) >= 1
1287 for (node n : m_pGraph->nodes) {
1288 DegreeConstraint* newCon;
1289 if (n == m_root) {
1290 // (3)
1291 newCon = new DegreeConstraint(this, n, 0.0, 1.0, abacus::CSense::Greater, 1.0);
1292 } else {
1293 if (m_isTerminal[n]) {
1294 // (1)
1295 newCon = new DegreeConstraint(this, n, 1.0, 0.0, abacus::CSense::Equal, 1.0);
1296 } else {
1297 // (2)
1298 newCon = new DegreeConstraint(this, n, 1.0, 0.0, abacus::CSense::Less, 1.0);
1299 }
1300 }
1301 basicConstraints.push(newCon);
1302
1303#ifdef OGDF_STP_EXACT_LOGGING
1304 lout(Level::Minor) << "\tadding constraint " << i++ << " Degree: node " << n << std::endl;
1305#endif
1306 }
1307 }
1308
1310 for (edge e : m_pGraph->edges) {
1311 if (e->source() != m_root) {
1312 DegreeEdgeConstraint* newCon =
1313 new DegreeEdgeConstraint(this, e, 1.0, -1.0, abacus::CSense::Greater, 0.0);
1314 basicConstraints.push(newCon);
1315
1316#ifdef OGDF_STP_EXACT_LOGGING
1317 lout(Level::Minor)
1318 << "\tadding constraint " << i++ << " Indegree: edge " << e << std::endl;
1319#endif
1320 }
1321 }
1322 }
1323
1325 for (node n : m_pGraph->nodes) {
1326 if (!m_isTerminal[n]) {
1327 DegreeConstraint* newCon =
1328 new DegreeConstraint(this, n, -1.0, 1.0, abacus::CSense::Greater, 0.0);
1329 basicConstraints.push(newCon);
1330
1331#ifdef OGDF_STP_EXACT_LOGGING
1332 lout(Level::Minor) << "\tadding constraint " << i++ << " Flow-Balance: node " << n
1333 << std::endl;
1334#endif
1335 }
1336 }
1337 }
1338
1339 m_poolSizeInit = m_poolSizeInitFactor * m_pGraph->numberOfEdges();
1340 m_poolSizeMax = m_poolSizeInit;
1341 // initialize the non-duplicate cut pool
1342 m_pCutPool = new abacus::NonDuplPool<abacus::Constraint, abacus::Variable>(this, m_poolSizeInit,
1343 true);
1344
1345 this->initializePools(basicConstraints, variables, 0, nCons, true);
1346
1347#ifdef OGDF_STP_EXACT_LOGGING
1348 lout(Level::Minor) << "Master::initializeOptimization() done." << std::endl;
1349#endif
1350}
1351
1352template<typename T>
1354#ifdef OGDF_STP_EXACT_LOGGING
1355 int nOnesInSol = 0;
1356#endif
1357 for (int i = 0; i < m_pGraph->numberOfEdges(); i++) {
1358 if (m_bestSolution[i] > 0.5) {
1359 m_isSolutionEdge[m_edges[i]] = true;
1360#ifdef OGDF_STP_EXACT_LOGGING
1361 nOnesInSol++;
1362#endif
1363 }
1364 }
1365
1366#ifdef OGDF_STP_EXACT_LOGGING
1367 lout(Level::High) << std::endl;
1368 if (is_lout(Level::Medium)) {
1369 lout(Level::Medium) << "\toptimum solution edges:" << std::endl;
1370 for (edge e : m_pGraph->edges) {
1371 if (m_isSolutionEdge[e]) {
1372 lout(Level::Medium) << "\t" << e << std::endl;
1373 }
1374 }
1375 }
1376 lout(Level::Medium) << std::endl;
1377
1378 lout(Level::High)
1379 << "Finished optimization. Statistics:" << std::endl
1380 << "Solution " << std::endl
1381 << " value " << this->primalBound() << std::endl
1382 << " rounded sol. value " << ((int)this->primalBound()) << std::endl
1383 << " nr edges " << nOnesInSol << std::endl
1384 << "Status " << this->STATUS_[this->status()] << std::endl
1385 << "Primal/dual bound " << this->primalBound() << "/" << this->dualBound()
1386 << std::endl
1387 << "Relaxed solution value " << this->m_relaxedSolValue << std::endl
1388 << "Nr subproblems " << this->nSub() << std::endl
1389 << "Nr solved LPs " << this->nLp() << std::endl
1390 << "nr solved LPs in root " << m_nIterRoot << std::endl
1391 << std::endl
1392
1393 << "LP Solver " << this->OSISOLVER_[this->defaultLpSolver()] << std::endl
1394 << "Enumeration strategy " << this->ENUMSTRAT_[this->enumerationStrategy()] << std::endl
1395 << "Branching strategy " << this->BRANCHINGSTRAT_[this->branchingStrategy()]
1396 << std::endl
1397 << "Objective integer " << (this->objInteger() ? "true" : "false") << std::endl
1398 << std::endl
1399
1400 << "Total time (centi sec) " << this->totalTime()->centiSeconds() << std::endl
1401 << "Total time " << *this->totalTime() << std::endl
1402 << "Total cow-time " << *this->totalCowTime() << std::endl
1403 << "LP time " << *this->lpTime() << std::endl
1404 << "LP solver time " << *this->lpSolverTime() << std::endl
1405 << "Separation time " << this->m_separationTimer << std::endl
1406 << "Minimum Cut time " << this->m_timerMinSTCut << std::endl
1407 << "Primal heuristic time " << this->m_primalHeuristicTimer << std::endl
1408 << std::endl
1409
1410 << "Initial cutpool size " << this->m_poolSizeInit << std::endl
1411 << "Maximum cutpool size " << this->m_poolSizeMax << std::endl
1412 << "Nr separated cuts " << m_nrCutsTotal << std::endl;
1413
1414 int nDuplicates, nCollisions;
1415 m_pCutPool->statistics(nDuplicates, nCollisions);
1416 lout(Level::High) << "Cutpool duplications " << nDuplicates << std::endl
1417 << "Cutpool collisions " << nCollisions << std::endl
1418 << std::endl;
1419#endif
1420}
1421
1422template<typename T>
1424 double eps = this->eps();
1425 double oneMinusEps = 1.0 - eps;
1426 for (int i = 0; i < m_pGraph->numberOfEdges(); i++) {
1427 if (values[i] > oneMinusEps) {
1428 m_bestSolution[i] = 1.0;
1429 } else if (values[i] < eps) {
1430 m_bestSolution[i] = 0.0;
1431 } else {
1432 m_bestSolution[i] = values[i];
1433 }
1434 }
1435}
1436
1437template<typename T>
1439 for (int i = 0; i < m_pGraph->numberOfEdges(); i++) {
1440 m_bestSolution[i] = 0.0;
1441 }
1442 for (ListConstIterator<edge> it = sol.begin(); it.valid(); it++) {
1443 m_bestSolution[m_edgeIDs[*it]] = 1.0;
1444 }
1445}
1446
1447template<typename T>
1460
1461template<typename T>
1463 abacus::BranchRule* branchRule)
1464 : abacus::Sub(master, father, branchRule), m_alreadySeparated(-1) {
1466#ifdef OGDF_STP_EXACT_LOGGING
1467 m_pMaster->lout(Logger::Level::High) << std::setw(7) << this->id() << std::setw(7) << this->nIter_
1468 << " new subproblem, parent=" << father->id() << std::endl;
1469#endif
1478}
1479
1480#ifdef OGDF_STP_EXACT_LOGGING
1481template<typename T>
1483 if (header) {
1484 // print header
1485 m_pMaster->lout(Logger::Level::High)
1486 << std::endl
1487 << std::setw(7) << "id" << std::setw(7) << "iter" << std::setw(10) << "lp value"
1488 << std::setw(10) << "gl. LB" << std::setw(10) << "gl. UB" << std::setw(10) << "nSub"
1489 << std::setw(10) << "nOpenSub" << std::endl;
1490 } else {
1491 m_pMaster->lout(Logger::Level::High) << std::setw(7) << this->id() << std::setw(7)
1492 << this->nIter_ << std::setw(10) << lp_->value();
1493 if (this->id() == 1) {
1494 m_pMaster->lout(Logger::Level::High) << std::setw(10) << "--";
1495 } else {
1496 m_pMaster->lout(Logger::Level::High) << std::setw(10) << master_->lowerBound();
1497 }
1498 if (master_->feasibleFound()) {
1499 m_pMaster->lout(Logger::Level::High) << std::setw(10) << master_->upperBound();
1500 } else {
1501 m_pMaster->lout(Logger::Level::High) << std::setw(10) << "--";
1502 }
1503 m_pMaster->lout(Logger::Level::High) << std::setw(10) << master_->nSub() << std::setw(10)
1504 << master_->openSub()->number() << std::endl;
1505 m_pMaster->lout(Logger::Level::Minor) << "\tcurrent LP:" << std::endl;
1506 m_pMaster->lout(Logger::Level::Minor) << *lp_;
1507 m_pMaster->lout(Logger::Level::Minor) << std::endl;
1508 }
1509}
1510#endif
1511
1512template<typename T>
1514 double eps = master_->eps();
1515 double oneMinusEps = 1.0 - eps;
1516
1517#ifdef OGDF_STP_EXACT_LOGGING
1518 if (this->nIter_ == 1) {
1519 this->printMainInfosInFeasible(true);
1520 } else {
1521 this->printMainInfosInFeasible(false);
1522 }
1523#endif
1524
1525 // separate directed cuts
1526 m_alreadySeparated = mySeparate();
1527
1528 if (m_alreadySeparated > 0) {
1529 if (m_callPrimalHeuristic == 2 && !m_pMaster->relaxed()) {
1530 myImprove();
1531 }
1532 return false;
1533 }
1534
1535 if (this->id() == 1) {
1536 // set some statistics if we are in the root node
1537 m_pMaster->setRelaxedSolValue(lp_->value());
1538 m_pMaster->setNIterRoot(nIter_);
1539 }
1540
1541 if (!m_pMaster->relaxed()) {
1542 // check integrality
1543 for (int i = 0; i < m_pMaster->nEdges(); i++) {
1544 if (xVal_[i] > eps && xVal_[i] < oneMinusEps) {
1545 // found non-integral solution but no violated directed cuts
1546 // i.e., we have to branch.
1547 // But first, we call the primal heuristic
1548 if (m_callPrimalHeuristic > 0) {
1549 myImprove();
1550 }
1551
1552#ifdef OGDF_STP_EXACT_LOGGING
1553 m_pMaster->lout(Logger::Level::Default)
1554 << "\tsolution is fractional -> Branching." << std::endl;
1555#endif
1556 return false;
1557 }
1558 }
1559 }
1560
1561 if (m_pMaster->betterPrimal(lp_->value())) {
1562#ifdef OGDF_STP_EXACT_LOGGING
1563 m_pMaster->lout(Logger::Level::High)
1564 << std::setw(7) << this->id() << std::setw(7) << this->nIter_
1565 << " found better integer solution with value " << lp_->value();
1566 if (m_pMaster->is_lout(Logger::Level::Medium)) {
1567 m_pMaster->lout(Logger::Level::Medium) << ", variables > 0:" << std::endl;
1568 printCurrentSolution();
1569 } else {
1570 m_pMaster->lout(Logger::Level::High) << std::endl;
1571 }
1572#endif
1573 m_pMaster->primalBound(lp_->value());
1574 m_pMaster->updateBestSolution(xVal_);
1575 }
1576
1577 return true;
1578}
1579
1580#ifdef OGDF_STP_EXACT_LOGGING
1581template<typename T>
1583 int nOnesInSol = 0;
1584 double eps = master_->eps();
1585 double oneMinusEps = 1.0 - eps;
1586 for (int i = 0; i < nVar(); i++) {
1587 if (xVal_[i] > -eps && xVal_[i] < eps) {
1588 if (!onlyNonZeros) {
1589 m_pMaster->lout(Logger::Level::Minor) << "\tx" << i << "=0" << std::flush;
1590 m_pMaster->lout(Logger::Level::Minor)
1591 << " [edge " << ((EdgeVariable*)variable(i))->theEdge() << "]" << std::endl;
1592 }
1593 } else if (xVal_[i] > oneMinusEps && xVal_[i] < 1 + eps) {
1594 m_pMaster->lout(Logger::Level::Minor) << "\tx" << i << "=1" << std::flush;
1595 m_pMaster->lout(Logger::Level::Minor)
1596 << " [edge " << ((EdgeVariable*)variable(i))->theEdge() << "]" << std::endl;
1597 nOnesInSol++;
1598 } else {
1599 m_pMaster->lout(Logger::Level::Minor) << "\tx" << i << "=" << xVal_[i] << std::flush;
1600 m_pMaster->lout(Logger::Level::Minor)
1601 << " [edge " << ((EdgeVariable*)variable(i))->theEdge() << "]" << std::endl;
1602 }
1603 }
1604 m_pMaster->lout(Logger::Level::Medium) << "\tnEdges=" << nOnesInSol << std::endl << std::flush;
1605}
1606#endif
1607
1608template<typename T>
1610#ifdef OGDF_STP_EXACT_LOGGING
1611 m_pMaster->lout(Logger::Level::Medium)
1612 << "Sub::mySeparate(): id=" << this->id() << ", iter=" << this->nIter_ << std::endl;
1613#endif
1614 m_pMaster->separationTimer()->start();
1615 double eps = master_->eps();
1616 double cardEps = eps / 100;
1617 double oneMinusEps = 1 - eps;
1618 node r = m_pMaster->rootNode();
1619 const Graph& g = m_pMaster->graph();
1620 int nEdgesU = m_pMaster->nEdgesU();
1621
1622 int nTerminals = m_pMaster->nTerminals();
1623 const node* masterTerminals = m_pMaster->terminals();
1624 Array<node> terminal(nTerminals);
1625
1626 for (int i = 0; i < nTerminals; i++) {
1627 terminal[i] = masterTerminals[i];
1628 }
1629
1630 if (m_shuffleTerminals) {
1631 // shuffle the ordering of the terminals
1632 int j;
1633 node h = nullptr;
1634 for (int i = 0; i < nTerminals - 1; i++) {
1635 j = randomNumber(i, nTerminals - 1);
1636 h = terminal[i];
1637 terminal[i] = terminal[j];
1638 terminal[j] = h;
1639 }
1640 }
1641
1642#ifdef OGDF_STP_EXACT_LOGGING
1643 if (m_pMaster->is_lout(Logger::Level::Medium)) {
1644 m_pMaster->lout(Logger::Level::Medium) << "Sub::mySeparate(): considered terminal ordering: ";
1645 for (int i = 0; i < nTerminals; i++) {
1646 m_pMaster->lout(Logger::Level::Medium) << terminal[i] << " ";
1647 }
1648 m_pMaster->lout(Logger::Level::Medium) << std::endl;
1649 }
1650#endif
1651
1652 EdgeArray<double> capacities;
1653 capacities.init(g, 0);
1654 for (edge e : g.edges) {
1655 // some LP solvers might return a negative epsilon instead of 0 due to numerical reasons
1656 capacities[e] = max(xVal_[m_pMaster->edgeID(e)], 0.);
1658 capacities[e] += cardEps;
1659 }
1660 }
1661
1662#ifdef OGDF_STP_EXACT_LOGGING
1663 m_pMaster->lout(Logger::Level::Minor)
1664 << "Sub::mySeparate(): current capacities (>0) for mincut computation:" << std::endl;
1665 for (edge e : g.edges) {
1666 if (capacities[e] >= 2 * cardEps) {
1667 m_pMaster->lout(Logger::Level::Minor)
1668 << "\tcapacity[" << e << "]=" << capacities[e] << std::endl;
1669 }
1670 }
1671#endif
1672 // Minimum s-t-cut object
1673 MinSTCutMaxFlow<double> minSTCut;
1674 // current terminal
1675 node t;
1676 // index of current terminal
1677 int ti = 0;
1678 // value of current cut
1679 double cutValue = 2.0;
1680 // value of current back cut
1681 double cutValueBack = 0.0;
1682 // for backcut computation
1683 int nOtherNodes = 0;
1684 // upper bound for mincut computation
1685 double uBound = 1 + nEdgesU * cardEps;
1686 // nr of violated cuts found
1687 int cutsFound = 0;
1688
1689 // buffer for new constraints
1690 ArrayBuffer<abacus::Constraint*> newConstraints(m_maxNrCuttingPlanes);
1691
1692 // size of cut and backcut
1693 int cardinalityCut, cardinalityBackcut;
1694 cardinalityCut = cardinalityBackcut = 0;
1695
1696 // Only relevant if nested cuts are computed:
1697 // this list contains the modified edges i.e., the saturated edges.
1698 // The capacity of these edges can is reset in SeparationStrategy 2
1699 List<edge> modified;
1700
1701 // main while loop for the computation of the cutting planes
1702 while (cutsFound < m_maxNrCuttingPlanes && ti < nTerminals) {
1703 t = terminal[ti];
1704 if (t != r) {
1705#ifdef OGDF_STP_EXACT_LOGGING
1706 m_pMaster->lout(Logger::Level::Medium)
1707 << "Sub::mySeparate(): computing minimum cut between root " << r << " and " << t
1708 << std::flush;
1709#endif
1710 // /compute the minimum r-t-cut
1711 /*
1712 * the cut itself is stored in the integer array with values
1713 * in {0,1,2}. If a node has the value 1, it is contained in the
1714 * subset of the root, 2 indicates the set of the separated node, and
1715 * 0 depicts the set in between
1716 */
1717 m_pMaster->timerMinSTCut()->start();
1718
1719 EdgeArray<double> flow;
1720 MaxFlowModule<double>* mf = m_pMaster->getMaxFlowModule();
1721 OGDF_ASSERT(mf != nullptr);
1722 mf->init(g);
1723 mf->useEpsilonTest(cardEps / 100);
1724 cutValue = mf->computeFlow(capacities, r, t, flow);
1725#ifdef OGDF_STP_EXACT_LOGGING
1726 m_pMaster->lout(Logger::Level::Medium) << " Calculated flow:" << std::endl;
1727 for (edge flowEdge : g.edges) {
1728 m_pMaster->lout(Logger::Level::Medium)
1729 << " " << flowEdge << " : " << flow[flowEdge] << " / "
1730 << capacities[flowEdge] << std::endl;
1731 }
1732#endif
1733
1734 // used epsilon should be smaller than the eps used for cardinality cuts heuristic
1735 minSTCut.setEpsilonTest(new EpsilonTest(cardEps / 100));
1736 minSTCut.call(g, capacities, flow, r, t);
1737
1738 m_pMaster->timerMinSTCut()->stop();
1739 cutValueBack = 0;
1740
1741#ifdef OGDF_STP_EXACT_LOGGING
1742 m_pMaster->lout(Logger::Level::Medium) << ", cutvalue=" << cutValue << std::flush;
1743#endif
1744
1745 // min cardinality
1746 if (m_minCardinalityCuts && cutValue < uBound) {
1747 for (edge e : g.edges) {
1748 if (minSTCut.isFrontCutEdge(e)) {
1749 cutValue -= cardEps;
1750 }
1751 if (m_computeBackCuts && minSTCut.isBackCutEdge(e)) {
1752 cutValueBack += capacities[e] - cardEps;
1753 }
1754 }
1755 } else if (m_computeBackCuts) {
1756 for (edge e : g.edges) {
1757 if (minSTCut.isBackCutEdge(e)) {
1758 cutValueBack += capacities[e];
1759 }
1760 }
1761 }
1762
1763 if (m_saturationStrategy == 2) {
1764 cardinalityCut = cardinalityBackcut = 0;
1765 for (edge e : g.edges) {
1766 if (minSTCut.isFrontCutEdge(e)) {
1767 cardinalityCut++;
1768 }
1769 if (m_computeBackCuts && minSTCut.isBackCutEdge(e)) {
1770 cardinalityBackcut++;
1771 }
1772 }
1773 }
1774
1775#ifdef OGDF_STP_EXACT_LOGGING
1776 m_pMaster->lout(Logger::Level::Medium) << ", actual cutvalue=" << cutValue;
1777 if (m_computeBackCuts) {
1778 m_pMaster->lout(Logger::Level::Medium) << ", actual cutValueBack=" << cutValueBack;
1779 }
1780 m_pMaster->lout(Logger::Level::Medium) << std::endl;
1781#endif
1782 nOtherNodes = 0;
1783
1784 if (cutValue < oneMinusEps) {
1785 // found violated cut
1786 cutsFound++;
1787 // generate new constraint
1788 DirectedCutConstraint* newCut = new DirectedCutConstraint(master_, g, &minSTCut,
1790 // push cut to the set of new constraints
1791 newConstraints.push(newCut);
1792
1793#ifdef OGDF_STP_EXACT_LOGGING
1794 m_pMaster->lout(Logger::Level::Medium)
1795 << "Sub::mySeparate(): found violated cut:" << std::endl;
1796 printConstraint(newCut, Logger::Level::Medium);
1797#endif
1798 if (m_computeBackCuts && !minSTCut.frontCutIsComplementOfBackCut()
1799 && cutsFound < m_maxNrCuttingPlanes && cutValueBack <= oneMinusEps) {
1800 cutsFound++;
1801 // generate new constraint
1802 DirectedCutConstraint* newBackCut = new DirectedCutConstraint(master_, g,
1804 // push cut to the set of new constraints
1805 newConstraints.push(newBackCut);
1806
1807#ifdef OGDF_STP_EXACT_LOGGING
1808 m_pMaster->lout(Logger::Level::Medium)
1809 << "Sub::mySeparate(): found violated cut (backcut):" << std::endl;
1810 printConstraint(newBackCut, Logger::Level::Medium);
1811#endif
1812 }
1813
1814 // saturate cut edges in case of nested cut computation
1815 if (m_computeNestedCuts) {
1816 for (edge e : g.edges) {
1817 if (minSTCut.isFrontCutEdge(e)) {
1818 if (m_saturationStrategy == 2) {
1819 capacities[e] = 1.0 / (double)cardinalityCut + eps;
1820 } else {
1821 capacities[e] = 1.0 + eps;
1822 }
1823 // for resetting the saturation after each iteration
1824 if (m_separationStrategy == 2) {
1825 modified.pushBack(e);
1826 }
1827 } else {
1828 if (m_computeBackCuts && nOtherNodes > 0 && cutValueBack <= oneMinusEps
1829 && minSTCut.isBackCutEdge(e)) {
1830 if (m_saturationStrategy == 2) {
1831 capacities[e] = 1.0 / (double)cardinalityBackcut + eps;
1832 } else {
1833 capacities[e] = 1.0 + eps;
1834 }
1835 // for resetting the saturation after each iteration
1836 if (m_separationStrategy == 2) {
1837 modified.pushBack(e);
1838 }
1839 }
1840 }
1841 }
1842 }
1843 }
1844 }
1845
1846 if (!m_computeNestedCuts) {
1847 ti++;
1848 } else {
1849 if (cutValue > oneMinusEps || r == t) {
1850 ti++;
1851 if (m_separationStrategy == 2) {
1852 while (!modified.empty()) {
1853 edge e = modified.popFrontRet();
1854 capacities[e] = xVal_[m_pMaster->edgeID(e)];
1856 capacities[e] += cardEps;
1857 }
1858 }
1859 }
1860 }
1861 }
1862 }
1863
1864 m_alreadySeparated = cutsFound;
1865
1866 if (cutsFound > 0) {
1867 // add separated directed cuts
1868 int nAdded = addCons(newConstraints, m_pMaster->cutPool());
1869 // update statistics
1870 m_pMaster->incNrCutsTotal(nAdded);
1871 m_pMaster->checkSetMaxPoolSize();
1872 if (nAdded != cutsFound) {
1873 // ToDo: is this a problem?!
1874 }
1875 }
1876
1877#ifdef OGDF_STP_EXACT_LOGGING
1878 m_pMaster->lout(Logger::Level::Medium)
1879 << "Sub::mySeparate(): id=" << this->id() << ", iter=" << this->nIter_ << " separated "
1880 << cutsFound << " directed cuts" << std::endl;
1881#endif
1882 m_pMaster->separationTimer()->stop();
1883
1884 return cutsFound;
1885}
1886
1887template<typename T>
1889 m_pMaster->primalHeuristicTimer()->start();
1890
1891#ifdef OGDF_STP_EXACT_LOGGING
1892 m_pMaster->lout(Logger::Level::Minor)
1893 << "Sub::myImprove(): id=" << this->id() << ", iter=" << this->nIter_ << std::endl;
1894#endif
1895 double eps = master_->eps();
1896 const Graph& g = m_pMaster->graph();
1897 EdgeWeightedGraph<double>& ewg = m_pMaster->weightedGraphPH();
1898
1899#ifdef OGDF_STP_EXACT_LOGGING
1900 if (m_pMaster->ilout(Logger::Level::Minor)) {
1901 m_pMaster->lout(Logger::Level::Minor) << "Sub::myImprove(): current solution:" << std::endl;
1902 for (edge e : g.edges) {
1903 m_pMaster->lout(Logger::Level::Minor)
1904 << "\tx" << m_pMaster->edgeID(e) << "=" << xVal_[m_pMaster->edgeID(e)]
1905 << ", edge " << e << std::endl;
1906 }
1907 }
1908#endif
1909
1910 // set edge weights to eps + (1-x_e)*c_e, forall edges e
1911 // thereby, use minimum of e and twin(e), respectively
1912 double currWeight, twinWeight;
1913 for (edge e : g.edges) {
1914 edge e2 = m_pMaster->twin(e);
1915 currWeight = 1.0 - xVal_[m_pMaster->edgeID(e)];
1916 twinWeight = 1.0 - xVal_[m_pMaster->edgeID(e2)];
1917 if (twinWeight < currWeight) {
1918 currWeight = twinWeight;
1919 }
1920 if (currWeight < 0) {
1921 currWeight = 0;
1922 }
1923 ewg.setWeight(m_pMaster->edgeGToWgPH(e),
1924 eps + currWeight * variable(m_pMaster->edgeID(e))->obj());
1925 }
1926
1927#ifdef OGDF_STP_EXACT_LOGGING
1928 if (m_pMaster->ilout(Logger::Level::Minor)) {
1929 m_pMaster->lout(Logger::Level::Minor) << "Sub::myImprove(): edge weights:" << std::endl;
1930 for (edge e : g.edges) {
1931 m_pMaster->lout(Logger::Level::Minor)
1932 << "\tweight[" << e << "]=" << ewg.weight(m_pMaster->edgeGToWgPH(e))
1933 << std::endl;
1934 }
1935 }
1936#endif
1937
1938 // get primal heuristic algorithm
1939 auto& primalHeuristic = m_pMaster->getPrimalHeuristic();
1940
1941 // the computed heuristic solution
1942 EdgeWeightedGraphCopy<double>* heuristicSolutionWg = nullptr;
1943
1944#ifdef OGDF_STP_EXACT_LOGGING
1945 // heuristic solution value for the modified edge weights
1946 double tmpHeuristicSolutionValue =
1947#endif
1948 // call primal heuristic
1949 primalHeuristic->call(m_pMaster->weightedGraphPH(), m_pMaster->terminalListPH(),
1950 m_pMaster->isTerminalPH(), heuristicSolutionWg);
1951
1952 // verify that solution is a Steiner tree
1953 bool isSteinerTree = primalHeuristic->isSteinerTree(m_pMaster->weightedGraphPH(),
1954 m_pMaster->terminalListPH(), m_pMaster->isTerminalPH(), *heuristicSolutionWg);
1955
1956#ifdef OGDF_STP_EXACT_LOGGING
1957 m_pMaster->lout(Logger::Level::Default)
1958 << "Sub::myImprove(): primal heuristic algorithm returned solution with "
1959 << "value " << tmpHeuristicSolutionValue << ", isSteinerTree=" << isSteinerTree
1960 << std::endl;
1961#endif
1962
1963 if (isSteinerTree) {
1964 // actual heuristic solution value, i.e., by using real edge weights
1965 double heuristicSolutionValue = 0.0;
1966 // list of edges in the heuristic solution
1967 List<edge> solutionEdges;
1968
1969 for (edge e : heuristicSolutionWg->edges) {
1970 edge e2 = m_pMaster->edgeWgToGPH(heuristicSolutionWg->original(e));
1971 solutionEdges.pushBack(e2);
1972 heuristicSolutionValue += m_pMaster->capacities(e2);
1973#ifdef OGDF_STP_EXACT_LOGGING
1974 m_pMaster->lout(Logger::Level::Minor)
1975 << "\t" << e << " -> " << e2 << " c=" << m_pMaster->capacities(e2) << std::endl;
1976#endif
1977 }
1978
1979#ifdef OGDF_STP_EXACT_LOGGING
1980 m_pMaster->lout(Logger::Level::Default)
1981 << "Sub::myImprove(): found integer solution with value " << heuristicSolutionValue
1982 << std::endl;
1983#endif
1984 if (m_pMaster->betterPrimal(heuristicSolutionValue)) {
1985#ifdef OGDF_STP_EXACT_LOGGING
1986 m_pMaster->lout(Logger::Level::High)
1987 << std::setw(7) << this->id() << std::setw(7) << this->nIter_
1988 << " primal heuristic founds better integer solution with value "
1989 << heuristicSolutionValue << std::endl;
1990#endif
1991 m_pMaster->primalBound(heuristicSolutionValue);
1992 m_pMaster->updateBestSolutionByEdges(solutionEdges);
1993 }
1994 }
1995#ifdef OGDF_STP_EXACT_LOGGING
1996 else {
1997 m_pMaster->lout(Logger::Level::High)
1998 << "Sub::myImprove(): id=" << this->id() << ", iter=" << this->nIter_
1999 << ": computed solution is no Steiner tree!" << std::endl;
2000 }
2001#endif
2002
2003 delete heuristicSolutionWg;
2004 m_pMaster->primalHeuristicTimer()->stop();
2005}
2006
2007#ifdef OGDF_STP_EXACT_LOGGING
2008template<typename T>
2010 Logger::Level level) const {
2011 double eps = master_->eps();
2012 double val = 0;
2013 abacus::Variable* var;
2014 bool first = true;
2015 for (int i = 0; i < nVar(); i++) {
2016 var = variable(i);
2017 val = constraint->coeff(var);
2018 if (val > eps || val < -eps) {
2019 if (val > 0) {
2020 if (val > 1 - eps && val < 1 + eps) {
2021 if (!first) {
2022 m_pMaster->lout(level) << " + ";
2023 }
2024 } else {
2025 if (!first) {
2026 m_pMaster->lout(level) << " + " << val;
2027 }
2028 }
2029 } else {
2030 if (val < -1 + eps && val > -1 - eps) {
2031 if (!first) {
2032 m_pMaster->lout(level) << " - ";
2033 } else {
2034 m_pMaster->lout(level) << " -";
2035 }
2036 } else {
2037 if (!first) {
2038 m_pMaster->lout(level) << " - " << (-1) * val;
2039 } else {
2040 m_pMaster->lout(level) << val;
2041 }
2042 }
2043 }
2044 m_pMaster->lout(level) << "x" << i;
2045 first = false;
2046 }
2047 }
2048 m_pMaster->lout(level) << " " << *(constraint->sense()) << " " << constraint->rhs() << std::endl;
2049}
2050#endif
2051
2052template<typename T>
2054 if (m_hashKey != cv->hashKey()) {
2055 return false;
2056 }
2058 if (m_nMarkedNodes != dirCut->nMarkedNodes()) {
2059 return false;
2060 }
2061 for (node n : m_pGraph->nodes) {
2062 if (m_marked[n] != dirCut->marked(n)) {
2063 return false;
2064 }
2065 }
2066 return true;
2067}
2068
2069template<typename T>
2071 EdgeVariable* edgeVar = (EdgeVariable*)v;
2072 if (this->cutedge(edgeVar->theEdge())) {
2073 return 1.0;
2074 }
2075 return 0.0;
2076}
2077
2078template<typename T>
2080 const List<node>& terminals, const NodeArray<bool>& isTerminal,
2081 EdgeWeightedGraphCopy<T>*& finalSteinerTree) {
2082 Master stpMaster(G, terminals, isTerminal, m_eps);
2083 if (m_configFile) {
2084 stpMaster.setConfigFile(m_configFile);
2085 }
2086#ifdef OGDF_STP_EXACT_LOGGING
2087 stpMaster.setOutputLevel(m_outputLevel);
2088#endif
2100 stpMaster.setMaxFlowModule(m_maxFlowModuleOption.get());
2101 if (m_primalHeuristic) {
2103 }
2106 // XXX: should we set stpMaster.objInteger true/false according to weights automatically?
2107
2108 // now solve LP
2109 stpMaster.optimize();
2110
2111 // collect solution edges to build Steiner tree
2112 finalSteinerTree = new EdgeWeightedGraphCopy<T>();
2113 finalSteinerTree->setOriginalGraph(G);
2114 T weight(0);
2115 for (edge e = G.firstEdge(); e; e = e->succ()) {
2116 if (stpMaster.isSolutionEdge(e)) {
2117 const node vO = e->source();
2118 node vC = finalSteinerTree->copy(vO);
2119 if (!vC) {
2120 vC = finalSteinerTree->newNode(vO);
2121 }
2122 const node wO = e->target();
2123 node wC = finalSteinerTree->copy(wO);
2124 if (!wC) {
2125 wC = finalSteinerTree->newNode(wO);
2126 }
2127 T edgeCost = G.weight(e);
2128 finalSteinerTree->newEdge(e, edgeCost);
2129 weight += edgeCost;
2130 }
2131 }
2132
2133 return weight;
2134}
2135
2136}
Declaration and implementation of Array class and Array algorithms.
Declaration and implementation of ArrayBuffer 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.
Declaration of doubly linked lists and iterators.
Contains logging functionality.
Declaration and implementation of Goldberg-Tarjan max-flow algorithm with global relabeling and gap r...
Interface for Max Flow Algorithms.
Declaration of min-st-cut algorithms parameterized by max-flow alorithm.
Declaration of ogdf::MinSteinerTreeModule.
Implementation of the 2(1-1/l)-approximation algorithm for the minimum Steiner tree problem by Matsuy...
Declaration of stopwatch classes.
Includes Abacus.
Basic declarations, included by all source files.
Abstract base class for all branching rules.
Definition branchrule.h:60
Forms the virtual base class for all possible constraints given in pool format.
Definition constraint.h:57
CSense * sense()
Returns a pointer to the sense of the constraint.
Definition constraint.h:116
virtual double coeff(const Variable *v) const =0
Returns the coefficient of the variable v in the constraint.
virtual double rhs() const
Returns the right hand side of the constraint.
Definition constraint.h:131
The master of the optimization.
Definition master.h:70
OSISOLVER
This enumeration defines which solvers can be used to solve the LP-relaxations.
Definition master.h:210
STATUS optimize()
Performs the optimization by branch-and-bound.
static const char * OSISOLVER_[]
Array for the literal values for possible Osi solvers.
Definition master.h:213
OSISOLVER defaultLpSolver() const
returns the Lp Solver.
Definition master.h:534
Standard pools without constraint duplication.
Definition nonduplpool.h:59
The subproblem.
Definition sub.h:69
int id() const
Returns the identity number of the subproblem.
Definition sub.h:154
int nIter_
The number of iterations in the cutting plane phase.
Definition sub.h:1480
Master * master()
Returns the master of the optimization.
Definition sub.h:321
TYPE
The enumeration with the different variable types.
Definition vartype.h:48
@ Continuous
A continuous variable.
Definition vartype.h:49
@ Binary
A variable having value 0 or 1.
Definition vartype.h:51
Forms the virtual base class for all possible variables given in pool format.
Definition variable.h:60
Exception thrown when an algorithm realizes an internal bug that prevents it from continuing.
Definition exceptions.h:247
An array that keeps track of the number of inserted elements; also usable as an efficient stack.
Definition ArrayBuffer.h:64
void push(E e)
Puts a new element in the buffer.
The parameterized class Array implements dynamic arrays of type E.
Definition Array.h:219
Class for the representation of edges.
Definition Graph_d.h:364
bool isParallelDirected(edge e) const
Returns true iff edge e is parallel to this (directed) edge (or if it is the same edge)
Definition Graph_d.h:426
bool isSelfLoop() const
Returns true iff the edge is a self-loop (source node = target node).
Definition Graph_d.h:420
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.
T weight(const edge e) const
edge newEdge(node v, node w, T weight)
void setWeight(const edge e, T weight)
node newNode(node vOrig)
Creates a new node in the graph copy with original node vOrig.
Definition GraphCopy.h:191
const Graph & original() const
Returns a reference to the original graph.
Definition GraphCopy.h:104
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
int numberOfNodes() const
Returns the number of nodes in the graph.
Definition Graph_d.h:979
int numberOfEdges() const
Returns the number of edges in the graph.
Definition Graph_d.h:982
internal::GraphObjectContainer< NodeElement > nodes
The container containing all node objects.
Definition Graph_d.h:929
node newNode(int index=-1)
Creates a new node and returns it.
Definition Graph_d.h:1061
edge newEdge(node v, node w, int index=-1)
Creates a new edge (v,w) and returns it.
Definition Graph_d.h:1080
internal::GraphObjectContainer< EdgeElement > edges
The container containing all edge objects.
Definition Graph_d.h:932
Representation of levels in hierarchies.
Definition Level.h:66
Doubly linked lists (maintaining the length of the list).
Definition List.h:1451
iterator pushBack(const E &x)
Adds element x at the end of the list.
Definition List.h:1547
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
iterator begin()
Returns an iterator to the first element of the list.
Definition List.h:391
bool empty() const
Returns true iff the list is empty.
Definition List.h:286
Centralized global and local logging facility working on streams like std::cout.
Definition Logger.h:102
std::ostream & lout(Level level=Level::Default, bool indent=true) const
stream for logging-output (local)
Definition Logger.h:160
Level
supported log-levels from lowest to highest importance
Definition Logger.h:105
Computes a max flow via Preflow-Push (global relabeling and gap relabeling heuristic).
virtual void init(const Graph &graph, EdgeArray< T > *flow=nullptr)
Initialize the problem with a graph and optional flow array. If no flow array is given,...
void useEpsilonTest(const double &eps)
Change the used EpsilonTest from StandardEpsilonTest to a user given EpsilonTest.
T computeFlow(EdgeArray< T > &cap, node &s, node &t, EdgeArray< T > &flow)
Only a shortcut for computeValue and computeFlowAfterValue.
Min-st-cut algorithm, that calculates the cut via maxflow.
bool isBackCutEdge(const edge e) const
Returns whether this edge is entering the back cut.
bool isInBackCut(const node v) const
Returns whether this node is part of the back cut.
cutType
The three types of cuts.
bool isFrontCutEdge(const edge e) const
Returns whether this edge is leaving the front cut.
void setEpsilonTest(EpsilonTest *et)
Assigns a new epsilon test.
virtual bool call(const Graph &graph, const EdgeArray< TCost > &weight, node s, node t, List< edge > &edgeList, edge e_st=nullptr) override
The actual algorithm call.
bool frontCutIsComplementOfBackCut() const
Returns whether the front cut is the complement of the backcut.
bool isInFrontCut(const node v) const
Returns whether this node is part of the front cut.
Constraint for nodes, e.g., in/outdegree stuff.
virtual double coeff(const abacus::Variable *v) const override
coefficient of variable in constraint
DegreeConstraint(abacus::Master *master, node n, double coeffIn, double coeffOut, abacus::CSense::SENSE sense, double rhs)
Constraint for relating the indegree and one outgoing edge of a node.
DegreeEdgeConstraint(abacus::Master *master, edge e, double coeffIn, double coeffEdge, abacus::CSense::SENSE sense, double rhs)
double coeff(const abacus::Variable *v) const override
coefficient of variable in constraint
Class for directed cuts (i.e., separated Steiner cuts)
NodeArray< bool > m_marked
defines if a vertex is on the left side of the cut (false) or not
bool active(node n) const
returns true iff the node n is separated by this cut
unsigned hashKey() const override
retuns an hashkey for the cut; required method for nonduplpool
bool equal(const ConVar *cv) const override
tests if cuts are equal; required method for nonduplpool
unsigned int m_hashKey
hashkey of the cut; required method for nonduplpool
bool cutedge(edge e) const
returns true iff the edge is contained in the cut
const char * m_name
name of the cut; required method for nonduplpool
DirectedCutConstraint(abacus::Master *master, const Graph &g, const MinSTCutMaxFlow< double > *minSTCut, MinSTCutMaxFlow< double >::cutType _cutType)
double coeff(const abacus::Variable *v) const override
Returns the coefficient of the variable v in the constraint.
const char * name() const override
return the name of the cut; required method for nonduplpool
Constraint for edges, e.g., subtour elimination constraints of size 2 ((G)SEC2)
edge m_e2
twin of m_e1, i.e., if m_e1 = (u,v) it holds m_e2 = (v,u)
int m_factor
factor for edge coefficients in the constraint
EdgeConstraint(abacus::Master *master, edge e1, edge e2, int factor=1.0, abacus::CSense::SENSE sense=abacus::CSense::Less, double rhs=1.0)
double coeff(const abacus::Variable *v) const override
coefficient of variable in constraint
edge m_e1
base edge and twin of m_e2, i.e., if m_e1 = (u,v) it holds m_e2 = (v,u)
double coefficient() const
objective function coefficient
EdgeVariable(abacus::Master *master, int id, edge e, double coeff, double lb=0.0, double ub=1.0, abacus::VarType::TYPE vartype=abacus::VarType::Binary)
Master problem of Steiner tree branch&cut algorithm
StopwatchWallClock * separationTimer()
timer for separation
double m_relaxedSolValue
statistics: solution value of the relaxed master problem
bool isTerminal(node n) const
true if n is a terminal
const node * terminals() const
terminals in an array
StopwatchWallClock * timerMinSTCut()
timer for minimum st-cut computations. Measures updates + algorithm
StopwatchWallClock * primalHeuristicTimer()
timer for primal heuristics
edge edgeGToWgPH(edge e) const
edge mapping m_pGraph -> m_pWeightedGraphPH
bool m_addIndegreeEdgeConstraints
add constraints concerning the indegree of a node w.r.t. one outgoing edge: indeg(v) >= outgoing edge...
EdgeWeightedGraph< double > * m_pWeightedGraphPH
edge weighted bidirected graph; used and modified for primal heuristics
const NodeArray< bool > & isTerminalPH() const
terminal yes/no (in m_pWeightedGraphPH)
double capacities(edge e) const
costs for edge e
void updateBestSolutionByEdges(const List< edge > &sol)
updates best found solution by list of edges
EdgeArray< edge > m_mapToOrigGraph
the undirected edge in the original graph for each arc in m_pGraph
virtual void terminateOptimization() override
store solution in EdgeArray
node m_root
the virtual root of our graph. This node is a terminal.
void checkSetMaxPoolSize()
checks if current pool size is maximum and sets it if necessary
MaxFlowModule< double > * getMaxFlowModule()
Get the maximum flow module used by separation algorithm.
int m_maxPoolSize
statistic number of cuts in pool
void useFlowBalanceConstraints(bool b)
Switch usage of flow balance constraints on or off.
int nNodes() const
number of nodes of the graph
edge edgeWgToGPH(edge e) const
edge mapping m_pWeightedGraphPH -> m_pGraph
node * m_terminals
terminal index -> terminal node
int maxNrAddedCuttingPlanes() const
maximum nr of cutting planes
const NodeArray< int > & nodeIDs() const
lp variable ids of nodes
EdgeWeightedGraph< double > & weightedGraphPH()
NodeArray< node > m_nodesGToWgPH
node mapping m_pGraph -> m_pWeightedGraphPH
bool relaxed() const
solve relaxed LP or ILP
int m_callPrimalHeuristic
parameter: primal heuristic (PH) call strategy
bool computeNestedCuts() const
parameter: nested cut computation
const EdgeArray< int > & edgeIDs() const
lp variable ids of edges
void useNestedCuts(bool b)
Switch computation of nested cuts on or off.
node m_rootPH
root node in m_pWeightedGraphPH
int m_separationStrategy
parameter: separation strategy, only important if nested cuts are computed
bool m_backCutComputation
parameter: compute back cuts yes/no i.e., outgoing edges of the root-set
abacus::NonDuplPool< abacus::Constraint, abacus::Variable > * m_pCutPool
the non-duplicate cut pool for the directed Steiner cuts
const char * m_configfile
problem dependent config file
int m_poolSizeInitFactor
parameter: factor for the initial size of the cutting pool
void setSaturationStrategy(int b)
Set saturation strategy for nested cuts.
void useMinCardinalityCuts(bool b)
Switch usage of the cardinality heuristic (minimum-cardinality cuts) on or off.
int nEdges() const
returns the number of edges
bool m_nestedCutComputation
parameter: compute nested cuts yes/no i.e., saturate all cut edges and recompute the mincut
void useIndegreeEdgeConstraints(bool b)
Switch usage of indegree edge constraints (indeg(v) >= outgoing edge(v,x) for all x) on or off.
EdgeArray< edge > m_mapToBidirectedGraph1
the first directed arc in m_pGraph for an original edge
void useTerminalShuffle(bool b)
Switch terminal shuffling before separation on or off.
bool m_minCardinalityCuts
parameter: compute minimum cardinality cuts
void incNrCutsTotal(int val)
increases the number of separated directed cuts
void updateBestSolution(double *values)
updates best found solution
double solutionValue() const
solution value after solving the problem, i.e., returns final primal bound
std::unique_ptr< MinSteinerTreeModule< double > > m_primalHeuristic
Algorithm used for the primal heuristic.
EdgeVariable * getVar(edge e) const
returns the variable assigned to edge e
int maxPoolSize() const
the maximum pool size during the algorithm
StopwatchWallClock m_timerMinSTCut
timer for minimum st-cut computations. Measures updates + algorithm
EdgeArray< edge > m_mapToBidirectedGraph2
the second directed arc in m_pGraph for an original edge
void incNrCutsTotal()
increases the number of separated directed cuts by 1
EdgeVariable * getVarTwin(edge e) const
returns the variable assigned to the twin of edge e
void setNIterRoot(int val)
nr of iterations in the root node
int m_saturationStrategy
parameter: saturation strategy, only important if nested cuts are computed
const EdgeWeightedGraph< T > & m_wG
the original weighted graph
bool m_addDegreeConstraints
parameter: add constraints concerning the indegree of a node, like: indeg <= 1 for all vertices
int nrCutsTotal() const
total number of separated directed cuts
virtual void initializeOptimization() override
insert variables and base constraints
void setMaxFlowModule(MaxFlowModule< double > *module)
Set the maximum flow module to be used for separation.
const Graph & graph() const
the directed graph, i.e., the bidirection of the input graph
int edgeID(edge e) const
edge -> id of lp variable
StopwatchWallClock m_primalHeuristicTimer
timer for primal heuristics
abacus::NonDuplPool< abacus::Constraint, abacus::Variable > * cutPool()
the non-duplicate cutpool for the separated Steiner cuts
std::unique_ptr< MinSteinerTreeModule< double > > & getPrimalHeuristic()
the primal heuristic module
bool m_relaxed
parameter: indicates whether we solve the relaxed problem (LP) or the ILP
NodeArray< bool > m_isTerminalPH
is terminal yes/no (in m_pWeightedGraphPH)
void setMaxNumberAddedCuttingPlanes(int b)
Set maximum number of added cutting planes per iteration.
int nodeID(node n) const
npde -> id of lp variable
virtual abacus::Sub * firstSub() override
generates the first subproblem
const NodeArray< bool > isTerminal() const
boolean array of terminal status
virtual void initializeParameters() override
read/set parameters from file
int m_maxNrAddedCuttingPlanes
parameter: maximum nr of cutting planes per iteration
void setSeparationStrategy(int b)
Set separation strategy for nested cuts.
node rootNodePH() const
root node (in m_pWeightedGraphPH)
bool minCardinalityCuts() const
parameter: compute minimum cardinality cuts
const Master & operator=(const Master &rhs)
void useDegreeConstraints(bool b)
Switch usage of degree constraints (like indeg <= 1) on or off.
void setRelaxedSolValue(double val)
solution value of the root
bool m_shuffleTerminals
parameter: shuffle the list of terminals right before separation
void setPoolSizeInitFactor(int b)
Set factor for the initial size of the cutting pool.
NodeArray< bool > m_isTerminal
node is terminal yes/no
edge twin(edge e) const
the twin edge, i.e. twin[(u,v)] = (v,u)
EdgeArray< edge > m_edgesWgToGPH
edge mapping m_pWeightedGraphPH -> m_pGraph
double * bestSolution() const
the best found solution
List< node > m_terminalListPH
list of terminal nodes (in m_pWeightedGraphPH)
EdgeArray< EdgeVariable * > m_edgeToVar
edge -> lp variable
bool computeBackCuts() const
parameter: back cut computation
bool shuffleTerminals() const
shuffle ordering of terminals before each separation routine
void useBackCuts(bool b)
Switch computation of back-cuts on or off.
EdgeArray< edge > m_edgesGToWgPH
edge mapping m_pGraph -> m_pWeightedGraphPH
bool isSolutionEdge(edge e) const
returns true iff original edge is contained in optimum solution
void setPrimalHeuristic(MinSteinerTreeModule< double > *pMinSteinerTreeModule)
Set the module option for the primal heuristic.
bool m_addGSEC2Constraints
parameter: add GSEC2 constraints yes/no, i.e. x_uv + x_vu <= 1
Master(const EdgeWeightedGraph< T > &wG, const List< node > &terminals, const NodeArray< bool > &isTerminal, double eps, bool relaxed=false)
Constructor of the master problem.
int callPrimalHeuristicStrategy() const
strategy for calling primal heuristic (PH)
void setConfigFile(const char *filename)
Set the config file to use that overrides all other settings.
int m_nrCutsTotal
total number of separated directed cuts
int saturationStrategy() const
strategy for saturating edges during separation; Only relevant for nested cuts
void setPrimalHeuristicCallStrategy(int b)
Set primal heuristic call strategy.
node terminal(int i) const
get terminal with index i
StopwatchWallClock m_separationTimer
timer for separation
const List< node > & terminalListPH() const
list of terminals (in m_pWeightedGraphPH)
bool m_addFlowBalanceConstraints
parameter: add flow balance constraints for Steiner nodes n: outdeg(n) >= indeg(n)
bool callPrimalHeuristic() const
parameter: call primal heuristic yes/no
node rootNode() const
the designated root node (special terminal)
void useGSEC2Constraints(bool b)
Switch usage of constraints x_uv + x_vu <= 1 on or off.
int nEdgesU() const
returns number of undirected edges, i.e., nEdges()/2
EdgeArray< edge > m_twin
the twin edges (u,v) <-> (v,u)
int separationStrategy() const
strategy for separating directed Steiner cuts; Only relevant for nested cuts
const EdgeArray< double > & capacities() const
edge costs
int m_nIterRoot
statistics: nr of iterations in the root node of the b&b tree
Subproblem of Steiner tree algorithm.
void myImprove()
primal heuristic procedure
virtual int separate() override
calls mySeparate() if mySeparate wasn't called in another procedure
virtual bool feasible() override
checks if the current solution is feasible, i.e., calls mySeparate()
int m_callPrimalHeuristic
Strategy for primal heuristic (PH) calls.
virtual ~Sub()
The destructor only deletes the sons of the node.
int m_alreadySeparated
nr of already separated cuts, default is -1
Sub(abacus::Master *master)
Constructor for the root problem of the b&b tree.
virtual Sub * generateSon(abacus::BranchRule *rule) override
generates a b&b node
int m_maxNrCuttingPlanes
maximum nr of cutting planes to be added
Master * m_pMaster
the master problem of the b&c algorithm
This class implements the Directed Cut Integer Linear Program for the Steiner tree problem.
void useIndegreeEdgeConstraints(bool b)
Switch usage of indegree edge constraints (indeg(v) >= outgoing edge(v,x) for all x) on or off.
void useNestedCuts(bool b)
Switch computation of nested cuts on or off.
void setPrimalHeuristicCallStrategy(int b)
Set primal heuristic call strategy.
void setPoolSizeInitFactor(int b)
Set factor for the initial size of the cutting pool.
void setMaxFlowModule(MaxFlowModule< double > *module)
Set the maximum flow module to be used for separation.
void setPrimalHeuristic(MinSteinerTreeModule< double > *b)
Set the module option for the primal heuristic (use MinSteinerTreeModule<double> types)....
void setConfigFile(const char *configfile)
Set a configuration file to use. The contents of the configuration file can override all other used o...
std::unique_ptr< MaxFlowModule< double > > m_maxFlowModuleOption
void setSaturationStrategy(int b)
Set saturation strategy for nested cuts.
void useTerminalShuffle(bool b)
Switch terminal shuffling before separation on or off.
MinSteinerTreeModule< double > * m_primalHeuristic
void useMinCardinalityCuts(bool b)
Switch usage of the cardinality heuristic (minimum-cardinality cuts) on or off.
void setEpsilon(double eps)
Set the epsilon for the LP.
virtual T computeSteinerTree(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, EdgeWeightedGraphCopy< T > *&finalSteinerTree) override
Computes the actual Steiner tree.
void useBackCuts(bool b)
Switch computation of back-cuts on or off.
void useFlowBalanceConstraints(bool b)
Switch usage of flow balance constraints on or off.
void useGSEC2Constraints(bool b)
Switch usage of constraints x_uv + x_vu <= 1 on or off.
void setMaxNumberAddedCuttingPlanes(int b)
Set maximum number of added cutting planes per iteration.
void setSeparationStrategy(int b)
Set separation strategy for nested cuts.
void useDegreeConstraints(bool b)
Switch usage of degree constraints (like indeg <= 1) on or off.
Serves as an interface for various methods to compute or approximate minimum Steiner trees on undirec...
static bool isSteinerTree(const EdgeWeightedGraph< T > &G, const List< node > &terminals, const NodeArray< bool > &isTerminal, const EdgeWeightedGraphCopy< T > &steinerTree)
Checks in O(n) time if a given tree is acually a Steiner Tree.
This class implements the minimum Steiner tree 2-approximation algorithm by Takahashi and Matsuyama w...
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
void init(const Base *base=nullptr)
Reinitializes the array. Associates the array with the matching registry of base.
Implements a stopwatch measuring wall-clock time.
Definition Stopwatch.h:183
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
Definition of exception classes.
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition basic.h:52
int randomNumber(int low, int high)
Returns random integer between low and high (including).
The namespace for all OGDF objects.
open subproblems.