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/Graph.h>
45#include <ogdf/basic/Logger.h>
50
52
53#include <memory>
54// heuristics, approximation algorithms:
56
57// turn on/off logging for STP b&c algorithm
58//#define OGDF_STP_EXACT_LOGGING
59
60// TODO: Add module option for max flow algorithm
61
62namespace ogdf {
63
65
68template<typename T>
70protected:
71 // all the settings
72 const char* m_configFile;
73 double m_eps;
74#ifdef OGDF_STP_EXACT_LOGGING
76#endif
77 std::unique_ptr<MaxFlowModule<double>> m_maxFlowModuleOption;
92
93 // Abacus LP classes
94 class Sub;
95 class EdgeConstraint;
96 class DegreeConstraint;
99 class EdgeVariable;
100
101public:
102 class Master;
103
104 // a lot of setter methods
106 void setEpsilon(double eps) { m_eps = eps; }
107
110#ifdef OGDF_STP_EXACT_LOGGING
113#endif
116
119
122
125
128
131
134
136 void useBackCuts(bool b) { m_backCutComputation = b; }
137
140
143
146
149
152
155 OGDF_ASSERT(b >= 0);
156 OGDF_ASSERT(b <= 2);
158 }
159
162
185
186protected:
187 virtual T computeSteinerTree(const EdgeWeightedGraph<T>& G, const List<node>& terminals,
188 const NodeArray<bool>& isTerminal, EdgeWeightedGraphCopy<T>*& finalSteinerTree) override;
189};
190
192template<typename T>
194public:
204 Master(const EdgeWeightedGraph<T>& wG, const List<node>& terminals,
205 const NodeArray<bool>& isTerminal, double eps, bool relaxed = false);
206
208 virtual ~Master() {
209 delete[] m_bestSolution;
210 delete[] m_terminals;
211 delete[] m_nodes;
212 delete[] m_edges;
213 delete m_pGraph;
214 delete m_pWeightedGraphPH;
215 delete m_pCutPool;
216 }
217
219 void setConfigFile(const char* filename) { m_configfile = filename; }
220#ifdef OGDF_STP_EXACT_LOGGING
223 this->globalLogLevel(Level::Default);
224 this->localLogMode(LogMode::Log);
225 if (outputLevel >= Level::Minor && outputLevel <= Level::Force) {
226 this->localLogLevel((Level)outputLevel);
227 this->globalMinimumLogLevel((Level)outputLevel);
228 }
229 }
230#endif
232 void setMaxFlowModule(MaxFlowModule<double>* module) { m_maxFlowModule = module; }
233
235 MaxFlowModule<double>* getMaxFlowModule() { return m_maxFlowModule; }
236
239
242
245
248
252 this->maxConAdd(m_maxNrAddedCuttingPlanes);
253 this->maxConBuffered(m_maxNrAddedCuttingPlanes);
254 }
255
258
260 void useBackCuts(bool b) { m_backCutComputation = b; }
261
264
267 OGDF_ASSERT(b >= 1);
268 OGDF_ASSERT(b <= 2);
270 }
271
274 OGDF_ASSERT(b >= 1);
275 OGDF_ASSERT(b <= 2);
277 }
278
281
284 OGDF_ASSERT(b >= 0);
285 OGDF_ASSERT(b <= 2);
287 }
288
291
296
298 std::unique_ptr<MinSteinerTreeModule<double>>& getPrimalHeuristic() {
299 return m_primalHeuristic;
300 }
301
304
306 virtual abacus::Sub* firstSub() override { return new Sub(this); }
307
309 bool isSolutionEdge(edge e) const {
310 return m_isSolutionEdge[m_mapToBidirectedGraph1[e]]
311 || m_isSolutionEdge[m_mapToBidirectedGraph2[e]];
312 }
313
315 const Graph& graph() const { return *m_pGraph; }
316
318 int nNodes() const { return m_pGraph->numberOfNodes(); }
319
321 int nEdges() const { return m_pGraph->numberOfEdges(); }
322
324 int nEdgesU() const { return m_nEdgesU; }
325
327 node rootNode() const { return m_root; }
328
330 int nTerminals() const { return m_nTerminals; }
331
333 const node* terminals() const { return m_terminals; }
334
336 node terminal(int i) const { return m_terminals[i]; }
337
339 bool isTerminal(node n) const { return m_isTerminal[n]; }
340
342 const NodeArray<bool> isTerminal() const { return m_isTerminal; }
343
345 int edgeID(edge e) const { return m_edgeIDs[e]; }
346
348 int nodeID(node n) const { return m_nodeIDs[n]; }
349
351 node* nodes() const { return m_nodes; }
352
354 const NodeArray<int>& nodeIDs() const { return m_nodeIDs; }
355
357 const EdgeArray<int>& edgeIDs() const { return m_edgeIDs; }
358
360 edge getEdge(int i) const { return m_edges[i]; }
361
363 node getNode(int i) const { return m_nodes[i]; }
364
366 const EdgeArray<double>& capacities() const { return m_capacities; }
367
369 double capacities(edge e) const { return m_capacities[e]; }
370
372 edge twin(edge e) const { return m_twin[e]; }
373
374 // the twin edge array
375 const EdgeArray<edge>& twins() const { return m_twin; }
376
378 EdgeVariable* getVar(edge e) const { return m_edgeToVar[e]; }
379
381 EdgeVariable* getVarTwin(edge e) const { return m_edgeToVar[m_twin[e]]; }
382
384 bool relaxed() const { return m_relaxed; }
385
387 double solutionValue() const { return this->primalBound(); }
388
390 double* bestSolution() const { return m_bestSolution; }
391
393 void updateBestSolution(double* values);
395 void updateBestSolutionByEdges(const List<edge>& sol);
396
398 void setRelaxedSolValue(double val) { m_relaxedSolValue = val; }
399
401 void setNIterRoot(int val) { m_nIterRoot = val; }
402
405
408
410 bool computeBackCuts() const { return m_backCutComputation; }
411
414
416 bool callPrimalHeuristic() const { return m_callPrimalHeuristic > 0; }
417
420
423
426
428 bool shuffleTerminals() const { return m_shuffleTerminals; }
429
431 int maxPoolSize() const { return m_maxPoolSize; }
432
436 m_poolSizeMax = m_pCutPool->size();
437 }
438 }
439
441 int poolSizeInit() const { return m_poolSizeInit; }
442
444 void incNrCutsTotal(int val) { m_nrCutsTotal += val; }
445
447 void incNrCutsTotal() { m_nrCutsTotal++; }
448
450 int nrCutsTotal() const { return m_nrCutsTotal; }
451
452 /*
453 * Methods for primal heuristics.
454 * Naming convention: suffix "PH"
455 */
456 /*
457 * Edge weighted bidirected graph; used and modified for primal heuristics.
458 * Required for calling MinSteinerTreeModule algorihms
459 */
460 EdgeWeightedGraph<double>& weightedGraphPH() { return *m_pWeightedGraphPH; }
461
463 const List<node>& terminalListPH() const { return m_terminalListPH; }
464
466 const NodeArray<bool>& isTerminalPH() const { return m_isTerminalPH; }
467
469 node rootNodePH() const { return m_rootPH; }
470
472 edge edgeGToWgPH(edge e) const { return m_edgesGToWgPH[e]; }
473
475 edge edgeWgToGPH(edge e) const { return m_edgesWgToGPH[e]; }
476
477 /*
478 * some additional timers
479 */
481 StopwatchWallClock* separationTimer() { return &m_separationTimer; }
482
484 StopwatchWallClock* timerMinSTCut() { return &m_timerMinSTCut; }
485
487 StopwatchWallClock* primalHeuristicTimer() { return &m_primalHeuristicTimer; }
488
489protected:
491 virtual void initializeOptimization() override;
493 virtual void terminateOptimization() override;
495 virtual void initializeParameters() override;
496
497private:
499
501 const char* m_configfile;
502
504 std::unique_ptr<MinSteinerTreeModule<double>> m_primalHeuristic;
505
508
511
514
517
520
529
534
541
552
555
559
560 /*
561 * Stuff for primal heuristics
562 */
577
590
599
609 /*
610 * basic strategy:
611 * Computes mincut between the root and a terminal. Saturates cutedges afterwards.
612 * Continues with same terminal until no violated cut is found. Considers next terminal.
613 * 1: When switching to next terminal saturated edges remain saturated (default)
614 * 2: When switching to next terminal saturated edges are reset to original capacity
615 */
618 /*
619 * for all cutedges e:
620 * 1: capacity[e]=1 (default)
621 * 2: capacity[e]=1/C with C=nr of cutedges
622 */
624
626 /*
627 * adds epsilon to each arc capacity before computing the minimum cut
628 */
630
632 /*
633 * 0: no PH
634 * 1: call PH right before branchting
635 * 2: call PH every iteration
636 */
638
645
646 Master(const Master& rhs);
647 const Master& operator=(const Master& rhs);
648};
649
651template<typename T>
653public:
655 explicit Sub(abacus::Master* master);
657 Sub(abacus::Master* master, abacus::Sub* father, abacus::BranchRule* branchRule);
658
660 virtual ~Sub() { }
661
662#ifdef OGDF_STP_EXACT_LOGGING
664 void printCurrentSolution(bool onlyNonZeros = true);
665#endif
666
667protected:
669 virtual bool feasible() override;
670
672 virtual int separate() override {
673 if (m_alreadySeparated == -1) {
674 m_alreadySeparated = mySeparate();
675 }
676 return m_alreadySeparated;
677 }
678
680 int mySeparate();
681
683 void myImprove();
684
685#ifdef OGDF_STP_EXACT_LOGGING
687 void printMainInfosInFeasible(bool header = false) const;
688#endif
689
690private:
691#ifdef OGDF_STP_EXACT_LOGGING
693 void printConstraint(abacus::Constraint* constraint,
695#endif
696
699
702
717
719 /*
720 * 0: no PH
721 * 1: call PH right before branchting
722 * 2: call PH every iteration
723 */
725
728 return new Sub(master_, this, rule);
729 }
730};
731
733template<typename T>
735public:
737#if 0
738 const abacus::Sub *sub,
739#endif
740 int id, edge e, double coeff, double lb = 0.0, double ub = 1.0,
742 : abacus::Variable(master, nullptr /*, sub*/, false, false, coeff, lb, ub, vartype)
743 , m_edge(e)
744 , m_id(id) {
745 }
746
748 edge theEdge() const { return m_edge; }
749
751 int id() const { return m_id; }
752
754 double coefficient() const { return this->obj(); }
755
757 node source() const { return m_edge->source(); }
758
760 node target() const { return m_edge->target(); }
761
762private:
766 int m_id;
767};
768
770template<typename T>
772public:
773 EdgeConstraint(abacus::Master* master, edge e1, edge e2, int factor = 1.0,
774 abacus::CSense::SENSE sense = abacus::CSense::Less, double rhs = 1.0)
775 : abacus::Constraint(master, nullptr, sense, rhs, false, false, false)
776 , m_e1(e1)
777 , m_e2(e2)
778 , m_factor(factor) { }
779
781 double coeff(const abacus::Variable* v) const override {
783 edge e = edgeVar->theEdge();
784 if (e != m_e1 && e != m_e2) {
785 return 0.0;
786 }
787 return m_factor;
788 }
789
790private:
797};
798
800template<typename T>
802public:
804 abacus::CSense::SENSE sense, double rhs)
805 : abacus::Constraint(master, nullptr, sense, rhs, false, false, false)
806 , m_node(n)
807 , m_coeffIn(coeffIn)
808 , m_coeffOut(coeffOut) { }
809
811 virtual double coeff(const abacus::Variable* v) const override {
813 edge e = edgeVar->theEdge();
814 if (e->target() == m_node) {
815 return m_coeffIn;
816 } else {
817 if (e->source() == m_node) {
818 return m_coeffOut;
819 } else {
820 return 0.0;
821 }
822 }
823 }
824
826 node theNode() const { return m_node; }
827
828private:
832 double m_coeffIn;
835};
836
838template<typename T>
840public:
842 abacus::CSense::SENSE sense, double rhs)
843 : abacus::Constraint(master, nullptr, sense, rhs, false, false, false)
844 , m_edge(e)
845 , m_coeffIn(coeffIn)
846 , m_coeffEdge(coeffEdge) { }
847
849 double coeff(const abacus::Variable* v) const override {
851 edge e = edgeVar->theEdge();
852 // the edge
853 if (e->isParallelDirected(m_edge)) {
854 return m_coeffEdge;
855 }
856 // all edges to vertices x != source
857 if (e->target() != m_edge->source()) {
858 return 0.0;
859 }
860 // reverse edge
861 if (e->source() == m_edge->target()) {
862 return 0.0;
863 }
864 // all ingoing edges of the source (except the reverse edge, of course)
865 return m_coeffIn;
866 }
867
869 edge theEdge() const { return m_edge; }
870
871private:
875 double m_coeffIn;
878};
879
881template<typename T>
883public:
886 : abacus::Constraint(master, nullptr, abacus::CSense::Greater, 1.0, false, false, false)
887 , m_pGraph(&g)
888 , m_name("") {
889#ifdef OGDF_STP_EXACT_LOGGING
890 std::cout << "Creating new DirectedCutConstraint: " << std::endl;
891#endif
892 m_hashKey = 0;
893 m_marked.init(g);
894 for (node n : g.nodes) {
896 m_marked[n] = minSTCut->isInFrontCut(n);
897#ifdef OGDF_STP_EXACT_LOGGING
898 if (m_marked[n]) {
899 // TODO: Use lout instead
900 std::cout << " marked node " << n << std::endl;
901 }
902#endif
903 } else {
905 m_marked[n] = !minSTCut->isInBackCut(n);
906 }
907 if (m_marked[n]) {
908 m_nMarkedNodes++;
909 m_hashKey += n->index();
910 }
911 }
912 m_hashKey += m_nMarkedNodes * g.numberOfNodes() * g.numberOfNodes();
913#ifdef OGDF_STP_EXACT_LOGGING
914 std::cout << " front cut edges:" << std::endl;
915 for (edge e : g.edges) {
916 if (minSTCut->isFrontCutEdge(e)) {
917 std::cout << " " << e << std::endl;
918 }
919 }
920 std::cout << " back cut edges:" << std::endl;
921 for (edge e : g.edges) {
922 if (minSTCut->isBackCutEdge(e)) {
923 std::cout << " " << e << std::endl;
924 }
925 }
926#endif
927 }
928
929 double coeff(const abacus::Variable* v) const override;
930
932 bool active(node n) const { return m_marked[n]; }
933
935 bool cutedge(edge e) const { return m_marked[e->source()] && !m_marked[e->target()]; }
936
938 int nMarkedNodes() const { return m_nMarkedNodes; }
939
941 bool marked(node n) const { return m_marked[n]; }
942
944 unsigned hashKey() const override { return m_hashKey; };
945
947 bool equal(const ConVar* cv) const override;
948
950 const char* name() const override { return m_name; }
951
952private:
955
957 /*
958 * A vertex is marked iff it is separated by the cut
959 * i.e., it is contained in the same set as the target
960 */
962
965
967 unsigned int m_hashKey;
969 const char* m_name;
970};
971
972template<typename T>
974 const List<node>& terminals, const NodeArray<bool>& isTerminal, double eps, bool relaxed)
975 : abacus::Master("MinSteinerTreeDirectedCut::Master", true, false, abacus::OptSense::Min, eps)
976 , m_maxFlowModule(nullptr)
977 , m_configfile(nullptr)
978 , m_relaxed(relaxed)
979 , m_relaxedSolValue(-1)
980 , m_nIterRoot(-1)
981 , m_wG(wG)
982 , m_pCutPool(nullptr)
983 , m_poolSizeInitFactor(5)
984 , m_poolSizeMax(0)
985 , m_maxPoolSize(-1)
986 , m_nrCutsTotal(0)
987 , m_addGSEC2Constraints(true)
988 , m_addDegreeConstraints(true)
989 , m_addIndegreeEdgeConstraints(true)
990 , m_addFlowBalanceConstraints(true)
991 , m_maxNrAddedCuttingPlanes(500)
992 , m_shuffleTerminals(true)
993 , m_backCutComputation(true)
994 , m_nestedCutComputation(true)
995 , m_separationStrategy(1)
996 , m_saturationStrategy(1)
997 , m_minCardinalityCuts(true)
998 , m_callPrimalHeuristic(1) {
999#ifdef OGDF_STP_EXACT_LOGGING
1001 << "Master::Master(): default LP solver: " << this->OSISOLVER_[this->defaultLpSolver()]
1002 << std::endl;
1003#endif
1004 m_pGraph = new Graph();
1005
1006 edge e1, e2;
1007 int i = 0;
1008 int t = 0;
1009
1010 m_nodes = new node[m_wG.numberOfNodes()];
1013 m_nTerminals = terminals.size();
1014 m_root = nullptr;
1015
1016#ifdef OGDF_STP_EXACT_LOGGING
1017 lout(Level::Default) << "Master::Master(): nTerminals=" << m_nTerminals << std::flush;
1018 lout(Level::Minor) << " terminals: " << std::flush;
1019#endif
1022
1023 for (node nOrig : m_wG.nodes) {
1024 node n = m_pGraph->newNode();
1025 nodeMapping[nOrig] = n;
1026 m_nodes[i] = n;
1027 m_nodeIDs[n] = i;
1029 if (m_isTerminal[n]) {
1030#ifdef OGDF_STP_EXACT_LOGGING
1031 lout(Level::Minor) << n << "," << std::flush;
1032#endif
1033 m_terminals[t++] = n;
1034 }
1035 i++;
1036 }
1037#ifdef OGDF_STP_EXACT_LOGGING
1038 lout(Level::Minor) << std::endl << "Master::Master(): edges: ";
1039#endif
1040
1041 m_nEdgesU = m_wG.numberOfEdges();
1043 m_twin.init(*m_pGraph);
1045 m_edges = new edge[2 * m_nEdgesU];
1046
1050
1051 i = 0;
1052 for (edge eOrig : m_wG.edges) {
1053 e1 = m_pGraph->newEdge(nodeMapping[eOrig->source()], nodeMapping[eOrig->target()]);
1054 e2 = m_pGraph->newEdge(nodeMapping[eOrig->target()], nodeMapping[eOrig->source()]);
1055 m_capacities[e1] = m_capacities[e2] = m_wG.weight(eOrig);
1056 m_twin[e1] = e2;
1057 m_twin[e2] = e1;
1058 m_edges[i] = e1;
1059 m_edgeIDs[e1] = i++;
1060 m_edges[i] = e2;
1061 m_edgeIDs[e2] = i++;
1066#ifdef OGDF_STP_EXACT_LOGGING
1067 lout(Level::Minor) << " " << eOrig << "[" << e1 << ", " << e2 << "]" << std::flush;
1068#endif
1069 }
1070#ifdef OGDF_STP_EXACT_LOGGING
1071 lout(Level::Default) << std::endl;
1072#endif
1073 for (node n : m_pGraph->nodes) {
1074 if (m_isTerminal[n]) {
1075 // possibility to set the root node
1076 // default: terminal with highest degree
1077 if (!m_root) {
1078 m_root = n;
1079 } else {
1080 if (m_root->degree() < n->degree()) {
1081 m_root = n;
1082 }
1083 }
1084 }
1085 }
1086
1087#ifdef OGDF_STP_EXACT_LOGGING
1088 lout(Level::Medium) << "Master::Master(): m_root=" << m_root << std::endl;
1089#endif
1090
1092 m_bestSolution = new double[m_pGraph->numberOfEdges()];
1093 for (i = 0; i < m_pGraph->numberOfEdges(); i++) {
1094 m_bestSolution[i] = 1.0;
1095 }
1096
1097 // stuff for "primal heuristic"
1099 m_nodesGToWgPH.init(*m_pGraph);
1100 m_edgesGToWgPH.init(*m_pGraph);
1103
1104 for (node nOrig : m_pGraph->nodes) {
1106 m_nodesGToWgPH[nOrig] = n;
1108 if (m_isTerminalPH[n]) {
1109 m_terminalListPH.pushBack(n);
1110 }
1111 if (m_root == nOrig) {
1112 m_rootPH = n;
1113 }
1114 }
1115
1116 for (edge eOrig : m_pGraph->edges) {
1118 m_nodesGToWgPH[eOrig->target()], 0.0);
1119 m_edgesGToWgPH[eOrig] = e;
1120 m_edgesWgToGPH[e] = eOrig;
1121 }
1122
1123 // set default primal heuristic module to takahashi algorithm
1125}
1126
1127template<typename T>
1129 if (m_configfile) {
1130 bool objectiveInteger = false;
1131 try {
1132 this->readParameters(m_configfile);
1133 } catch (AlgorithmFailureException) {
1134#ifdef OGDF_STP_EXACT_LOGGING
1135 lout(Level::Alarm) << "Master::initializeParameters(): Error reading parameters."
1136 << "Using default values." << std::endl;
1137#endif
1138 }
1139#ifdef OGDF_STP_EXACT_LOGGING
1140 int outputLevel;
1141 getParameter("OutputLevel", outputLevel);
1142 setOutputLevel(static_cast<Level>(outputLevel));
1143#endif
1144 int solver = (OSISOLVER)findParameter("DefaultLpSolver", 12, OSISOLVER_);
1145 this->defaultLpSolver((OSISOLVER)solver);
1146 getParameter("AddGSEC2Constraints", m_addGSEC2Constraints);
1147 getParameter("AddDegreeConstraints", m_addDegreeConstraints);
1148 getParameter("AddIndegreeEdgeConstraints", m_addIndegreeEdgeConstraints);
1149 getParameter("AddFlowBalanceConstraints", m_addFlowBalanceConstraints);
1150 getParameter("MaxNrCuttingPlanes", m_maxNrAddedCuttingPlanes);
1151 getParameter("ShuffleTerminals", m_shuffleTerminals);
1152 getParameter("BackCutComputation", m_backCutComputation);
1153 getParameter("NestedCutComputation", m_nestedCutComputation);
1154 getParameter("SeparationStrategy", m_separationStrategy);
1155 getParameter("SaturationStrategy", m_saturationStrategy);
1156 getParameter("MinCardinalityCuts", m_minCardinalityCuts);
1157 getParameter("PrimalHeuristic", m_callPrimalHeuristic);
1158 getParameter("PoolSizeInitFactor", m_poolSizeInitFactor);
1159 getParameter("ObjInteger", objectiveInteger);
1160 this->objInteger(objectiveInteger);
1161 }
1162
1163#ifdef OGDF_STP_EXACT_LOGGING
1164 lout(Level::High)
1165 << "Master::initializeParameters(): parameters:" << std::endl
1166 << "\tLP-Solver " << OSISOLVER_[this->defaultLpSolver()] << std::endl
1167 << "\tOutputLevel " << this->localLogLevel() << std::endl
1168 << "\tAddDegreeConstraints " << m_addDegreeConstraints << std::endl
1169 << "\tAddIndegreeEdgeConstraints " << m_addIndegreeEdgeConstraints << std::endl
1170 << "\tAddGSEC2Constraints " << m_addGSEC2Constraints << std::endl
1171 << "\tAddFlowBalanceConstraints " << m_addFlowBalanceConstraints << std::endl
1172 << "\tMaxNrCuttingPlanes " << m_maxNrAddedCuttingPlanes << std::endl
1173 << "\tShuffleTerminals " << m_shuffleTerminals << std::endl
1174 << "\tBackCutComputation " << m_backCutComputation << std::endl
1175 << "\tMinCardinalityCuts " << m_minCardinalityCuts << std::endl
1176 << "\tNestedCutComputation " << m_nestedCutComputation << std::endl;
1178 lout(Level::High) << "\t SeparationStrategy " << m_separationStrategy << std::endl
1179 << "\t SaturationStrategy " << m_saturationStrategy << std::endl;
1180 }
1181 lout(Level::High) << "\tPrimalHeuristic " << m_callPrimalHeuristic << std::endl
1182 << "\tPoolSizeInitFactor " << m_poolSizeInitFactor << std::endl
1183 << "\tObjective integer " << this->objInteger() << std::endl
1184 << std::endl;
1185#endif
1187}
1188
1189template<typename T>
1191#ifdef OGDF_STP_EXACT_LOGGING
1192 lout(Level::High) << "Master::initializeOptimization(): Instance properties:" << std::endl
1193 << "\t(nNodes,nEdges) : (" << m_pGraph->numberOfNodes() << ", "
1194 << m_nEdgesU << ")" << std::endl
1195 << "\tNumber of terminals : " << m_nTerminals << std::endl
1196 << "\tRoot node : " << m_root << std::endl
1197 << std::endl;
1198#endif
1199 int i;
1200
1201 // buffer for variables; one for each directed edge
1202 ArrayBuffer<abacus::Variable*> variables(m_pGraph->numberOfEdges());
1203
1204 // add (edge) variables
1206 m_edgeToVar.init(*m_pGraph, nullptr);
1207
1209 if (m_relaxed) {
1211 }
1212
1213 for (i = 0; i < m_pGraph->numberOfEdges(); i++) {
1214 edge e = m_edges[i];
1215 if (e->target() != m_root // not going into root
1216 && !e->isSelfLoop()) {
1217 eVar = new EdgeVariable(this, i, e, m_capacities[e], 0.0, 1.0, vartype);
1218#ifdef OGDF_STP_EXACT_LOGGING
1219 lout(Level::Minor) << "\tadding variable x_" << i << ", edge " << e << std::endl;
1220#endif
1221 } else {
1222 // ub = lb = 0 is not nice, but makes life easier -> ids
1223 OGDF_ASSERT(m_capacities[e] >= 0);
1224 eVar = new EdgeVariable(this, i, e, m_capacities[e], 0.0, 0.0, vartype);
1225#ifdef OGDF_STP_EXACT_LOGGING
1226 lout(Level::Minor) << "\tmuting variable x_" << i << ", edge " << e << std::endl;
1227#endif
1228 }
1229 variables.push(eVar);
1230 m_edgeToVar[e] = eVar;
1231 }
1232
1233 // add constraints
1234 int nCons = 0;
1236 nCons += m_nEdgesU;
1237 }
1239 nCons += m_pGraph->numberOfNodes();
1240 }
1242 nCons += m_pGraph->numberOfEdges();
1243 }
1245 nCons += m_pGraph->numberOfNodes() - 1;
1246 }
1247
1249
1250 i = 0;
1252 EdgeArray<bool> marked(*m_pGraph, false);
1253 for (edge e : m_pGraph->edges) {
1254 if (!marked[e] && !e->isSelfLoop()) { // we have to ignore self-loops here
1256 new EdgeConstraint(this, e, m_twin[e], 1, abacus::CSense::Less, 1.0);
1258 marked[e] = true;
1259 marked[m_twin[e]] = true;
1260
1261#ifdef OGDF_STP_EXACT_LOGGING
1262 lout(Level::Minor)
1263 << "\tadding constraint " << i++ << " GSEC2: edge " << e << std::endl;
1264#endif
1265 }
1266 }
1267 }
1268
1269 // "degree" constraints:
1270 // (1) forall terminals t != m_root: x(delta-(t)) == 1
1271 // (2) forall non-temrinals n : x(delta-(n)) <= 1
1272 // (3) for the root : x(delta+(m_root)) >= 1
1274 for (node n : m_pGraph->nodes) {
1276 if (n == m_root) {
1277 // (3)
1278 newCon = new DegreeConstraint(this, n, 0.0, 1.0, abacus::CSense::Greater, 1.0);
1279 } else {
1280 if (m_isTerminal[n]) {
1281 // (1)
1282 newCon = new DegreeConstraint(this, n, 1.0, 0.0, abacus::CSense::Equal, 1.0);
1283 } else {
1284 // (2)
1285 newCon = new DegreeConstraint(this, n, 1.0, 0.0, abacus::CSense::Less, 1.0);
1286 }
1287 }
1289
1290#ifdef OGDF_STP_EXACT_LOGGING
1291 lout(Level::Minor) << "\tadding constraint " << i++ << " Degree: node " << n << std::endl;
1292#endif
1293 }
1294 }
1295
1297 for (edge e : m_pGraph->edges) {
1298 if (e->source() != m_root) {
1300 new DegreeEdgeConstraint(this, e, 1.0, -1.0, abacus::CSense::Greater, 0.0);
1302
1303#ifdef OGDF_STP_EXACT_LOGGING
1304 lout(Level::Minor)
1305 << "\tadding constraint " << i++ << " Indegree: edge " << e << std::endl;
1306#endif
1307 }
1308 }
1309 }
1310
1312 for (node n : m_pGraph->nodes) {
1313 if (!m_isTerminal[n]) {
1315 new DegreeConstraint(this, n, -1.0, 1.0, abacus::CSense::Greater, 0.0);
1317
1318#ifdef OGDF_STP_EXACT_LOGGING
1319 lout(Level::Minor) << "\tadding constraint " << i++ << " Flow-Balance: node " << n
1320 << std::endl;
1321#endif
1322 }
1323 }
1324 }
1325
1326 m_poolSizeInit = m_poolSizeInitFactor * m_pGraph->numberOfEdges();
1327 m_poolSizeMax = m_poolSizeInit;
1328 // initialize the non-duplicate cut pool
1329 m_pCutPool = new abacus::NonDuplPool<abacus::Constraint, abacus::Variable>(this, m_poolSizeInit,
1330 true);
1331
1332 this->initializePools(basicConstraints, variables, 0, nCons, true);
1333
1334#ifdef OGDF_STP_EXACT_LOGGING
1335 lout(Level::Minor) << "Master::initializeOptimization() done." << std::endl;
1336#endif
1337}
1338
1339template<typename T>
1341#ifdef OGDF_STP_EXACT_LOGGING
1342 int nOnesInSol = 0;
1343#endif
1344 for (int i = 0; i < m_pGraph->numberOfEdges(); i++) {
1345 if (m_bestSolution[i] > 0.5) {
1346 m_isSolutionEdge[m_edges[i]] = true;
1347#ifdef OGDF_STP_EXACT_LOGGING
1348 nOnesInSol++;
1349#endif
1350 }
1351 }
1352
1353#ifdef OGDF_STP_EXACT_LOGGING
1354 lout(Level::High) << std::endl;
1355 if (is_lout(Level::Medium)) {
1356 lout(Level::Medium) << "\toptimum solution edges:" << std::endl;
1357 for (edge e : m_pGraph->edges) {
1358 if (m_isSolutionEdge[e]) {
1359 lout(Level::Medium) << "\t" << e << std::endl;
1360 }
1361 }
1362 }
1363 lout(Level::Medium) << std::endl;
1364
1365 lout(Level::High)
1366 << "Finished optimization. Statistics:" << std::endl
1367 << "Solution " << std::endl
1368 << " value " << this->primalBound() << std::endl
1369 << " rounded sol. value " << ((int)this->primalBound()) << std::endl
1370 << " nr edges " << nOnesInSol << std::endl
1371 << "Status " << this->STATUS_[this->status()] << std::endl
1372 << "Primal/dual bound " << this->primalBound() << "/" << this->dualBound()
1373 << std::endl
1374 << "Relaxed solution value " << this->m_relaxedSolValue << std::endl
1375 << "Nr subproblems " << this->nSub() << std::endl
1376 << "Nr solved LPs " << this->nLp() << std::endl
1377 << "nr solved LPs in root " << m_nIterRoot << std::endl
1378 << std::endl
1379
1380 << "LP Solver " << this->OSISOLVER_[this->defaultLpSolver()] << std::endl
1381 << "Enumeration strategy " << this->ENUMSTRAT_[this->enumerationStrategy()] << std::endl
1382 << "Branching strategy " << this->BRANCHINGSTRAT_[this->branchingStrategy()]
1383 << std::endl
1384 << "Objective integer " << (this->objInteger() ? "true" : "false") << std::endl
1385 << std::endl
1386
1387 << "Total time (centi sec) " << this->totalTime()->centiSeconds() << std::endl
1388 << "Total time " << *this->totalTime() << std::endl
1389 << "Total cow-time " << *this->totalCowTime() << std::endl
1390 << "LP time " << *this->lpTime() << std::endl
1391 << "LP solver time " << *this->lpSolverTime() << std::endl
1392 << "Separation time " << this->m_separationTimer << std::endl
1393 << "Minimum Cut time " << this->m_timerMinSTCut << std::endl
1394 << "Primal heuristic time " << this->m_primalHeuristicTimer << std::endl
1395 << std::endl
1396
1397 << "Initial cutpool size " << this->m_poolSizeInit << std::endl
1398 << "Maximum cutpool size " << this->m_poolSizeMax << std::endl
1399 << "Nr separated cuts " << m_nrCutsTotal << std::endl;
1400
1401 int nDuplicates, nCollisions;
1402 m_pCutPool->statistics(nDuplicates, nCollisions);
1403 lout(Level::High) << "Cutpool duplications " << nDuplicates << std::endl
1404 << "Cutpool collisions " << nCollisions << std::endl
1405 << std::endl;
1406#endif
1407}
1408
1409template<typename T>
1411 double eps = this->eps();
1412 double oneMinusEps = 1.0 - eps;
1413 for (int i = 0; i < m_pGraph->numberOfEdges(); i++) {
1414 if (values[i] > oneMinusEps) {
1415 m_bestSolution[i] = 1.0;
1416 } else if (values[i] < eps) {
1417 m_bestSolution[i] = 0.0;
1418 } else {
1419 m_bestSolution[i] = values[i];
1420 }
1421 }
1422}
1423
1424template<typename T>
1426 for (int i = 0; i < m_pGraph->numberOfEdges(); i++) {
1427 m_bestSolution[i] = 0.0;
1428 }
1429 for (ListConstIterator<edge> it = sol.begin(); it.valid(); it++) {
1430 m_bestSolution[m_edgeIDs[*it]] = 1.0;
1431 }
1432}
1433
1434template<typename T>
1447
1448template<typename T>
1450 abacus::BranchRule* branchRule)
1451 : abacus::Sub(master, father, branchRule), m_alreadySeparated(-1) {
1453#ifdef OGDF_STP_EXACT_LOGGING
1454 m_pMaster->lout(Logger::Level::High) << std::setw(7) << this->id() << std::setw(7) << this->nIter_
1455 << " new subproblem, parent=" << father->id() << std::endl;
1456#endif
1465}
1466
1467#ifdef OGDF_STP_EXACT_LOGGING
1468template<typename T>
1470 if (header) {
1471 // print header
1472 m_pMaster->lout(Logger::Level::High)
1473 << std::endl
1474 << std::setw(7) << "id" << std::setw(7) << "iter" << std::setw(10) << "lp value"
1475 << std::setw(10) << "gl. LB" << std::setw(10) << "gl. UB" << std::setw(10) << "nSub"
1476 << std::setw(10) << "nOpenSub" << std::endl;
1477 } else {
1478 m_pMaster->lout(Logger::Level::High) << std::setw(7) << this->id() << std::setw(7)
1479 << this->nIter_ << std::setw(10) << lp_->value();
1480 if (this->id() == 1) {
1481 m_pMaster->lout(Logger::Level::High) << std::setw(10) << "--";
1482 } else {
1483 m_pMaster->lout(Logger::Level::High) << std::setw(10) << master_->lowerBound();
1484 }
1485 if (master_->feasibleFound()) {
1486 m_pMaster->lout(Logger::Level::High) << std::setw(10) << master_->upperBound();
1487 } else {
1488 m_pMaster->lout(Logger::Level::High) << std::setw(10) << "--";
1489 }
1490 m_pMaster->lout(Logger::Level::High) << std::setw(10) << master_->nSub() << std::setw(10)
1491 << master_->openSub()->number() << std::endl;
1492 m_pMaster->lout(Logger::Level::Minor) << "\tcurrent LP:" << std::endl;
1493 m_pMaster->lout(Logger::Level::Minor) << *lp_;
1494 m_pMaster->lout(Logger::Level::Minor) << std::endl;
1495 }
1496}
1497#endif
1498
1499template<typename T>
1501 double eps = master_->eps();
1502 double oneMinusEps = 1.0 - eps;
1503
1504#ifdef OGDF_STP_EXACT_LOGGING
1505 if (this->nIter_ == 1) {
1506 this->printMainInfosInFeasible(true);
1507 } else {
1508 this->printMainInfosInFeasible(false);
1509 }
1510#endif
1511
1512 // separate directed cuts
1513 m_alreadySeparated = mySeparate();
1514
1515 if (m_alreadySeparated > 0) {
1516 if (m_callPrimalHeuristic == 2 && !m_pMaster->relaxed()) {
1517 myImprove();
1518 }
1519 return false;
1520 }
1521
1522 if (this->id() == 1) {
1523 // set some statistics if we are in the root node
1524 m_pMaster->setRelaxedSolValue(lp_->value());
1525 m_pMaster->setNIterRoot(nIter_);
1526 }
1527
1528 if (!m_pMaster->relaxed()) {
1529 // check integrality
1530 for (int i = 0; i < m_pMaster->nEdges(); i++) {
1531 if (xVal_[i] > eps && xVal_[i] < oneMinusEps) {
1532 // found non-integral solution but no violated directed cuts
1533 // i.e., we have to branch.
1534 // But first, we call the primal heuristic
1535 if (m_callPrimalHeuristic > 0) {
1536 myImprove();
1537 }
1538
1539#ifdef OGDF_STP_EXACT_LOGGING
1540 m_pMaster->lout(Logger::Level::Default)
1541 << "\tsolution is fractional -> Branching." << std::endl;
1542#endif
1543 return false;
1544 }
1545 }
1546 }
1547
1548 if (m_pMaster->betterPrimal(lp_->value())) {
1549#ifdef OGDF_STP_EXACT_LOGGING
1550 m_pMaster->lout(Logger::Level::High)
1551 << std::setw(7) << this->id() << std::setw(7) << this->nIter_
1552 << " found better integer solution with value " << lp_->value();
1553 if (m_pMaster->is_lout(Logger::Level::Medium)) {
1554 m_pMaster->lout(Logger::Level::Medium) << ", variables > 0:" << std::endl;
1556 } else {
1557 m_pMaster->lout(Logger::Level::High) << std::endl;
1558 }
1559#endif
1560 m_pMaster->primalBound(lp_->value());
1561 m_pMaster->updateBestSolution(xVal_);
1562 }
1563
1564 return true;
1565}
1566
1567#ifdef OGDF_STP_EXACT_LOGGING
1568template<typename T>
1570 int nOnesInSol = 0;
1571 double eps = master_->eps();
1572 double oneMinusEps = 1.0 - eps;
1573 for (int i = 0; i < nVar(); i++) {
1574 if (xVal_[i] > -eps && xVal_[i] < eps) {
1575 if (!onlyNonZeros) {
1576 m_pMaster->lout(Logger::Level::Minor) << "\tx" << i << "=0" << std::flush;
1577 m_pMaster->lout(Logger::Level::Minor)
1578 << " [edge " << ((EdgeVariable*)variable(i))->theEdge() << "]" << std::endl;
1579 }
1580 } else if (xVal_[i] > oneMinusEps && xVal_[i] < 1 + eps) {
1581 m_pMaster->lout(Logger::Level::Minor) << "\tx" << i << "=1" << std::flush;
1582 m_pMaster->lout(Logger::Level::Minor)
1583 << " [edge " << ((EdgeVariable*)variable(i))->theEdge() << "]" << std::endl;
1584 nOnesInSol++;
1585 } else {
1586 m_pMaster->lout(Logger::Level::Minor) << "\tx" << i << "=" << xVal_[i] << std::flush;
1587 m_pMaster->lout(Logger::Level::Minor)
1588 << " [edge " << ((EdgeVariable*)variable(i))->theEdge() << "]" << std::endl;
1589 }
1590 }
1591 m_pMaster->lout(Logger::Level::Medium) << "\tnEdges=" << nOnesInSol << std::endl << std::flush;
1592}
1593#endif
1594
1595template<typename T>
1597#ifdef OGDF_STP_EXACT_LOGGING
1598 m_pMaster->lout(Logger::Level::Medium)
1599 << "Sub::mySeparate(): id=" << this->id() << ", iter=" << this->nIter_ << std::endl;
1600#endif
1601 m_pMaster->separationTimer()->start();
1602 double eps = master_->eps();
1603 double cardEps = eps / 100;
1604 double oneMinusEps = 1 - eps;
1605 node r = m_pMaster->rootNode();
1606 const Graph& g = m_pMaster->graph();
1607 int nEdgesU = m_pMaster->nEdgesU();
1608
1609 int nTerminals = m_pMaster->nTerminals();
1610 const node* masterTerminals = m_pMaster->terminals();
1611 Array<node> terminal(nTerminals);
1612
1613 for (int i = 0; i < nTerminals; i++) {
1614 terminal[i] = masterTerminals[i];
1615 }
1616
1617 if (m_shuffleTerminals) {
1618 // shuffle the ordering of the terminals
1619 int j;
1620 node h = nullptr;
1621 for (int i = 0; i < nTerminals - 1; i++) {
1622 j = randomNumber(i, nTerminals - 1);
1623 h = terminal[i];
1624 terminal[i] = terminal[j];
1625 terminal[j] = h;
1626 }
1627 }
1628
1629#ifdef OGDF_STP_EXACT_LOGGING
1630 if (m_pMaster->is_lout(Logger::Level::Medium)) {
1631 m_pMaster->lout(Logger::Level::Medium) << "Sub::mySeparate(): considered terminal ordering: ";
1632 for (int i = 0; i < nTerminals; i++) {
1633 m_pMaster->lout(Logger::Level::Medium) << terminal[i] << " ";
1634 }
1635 m_pMaster->lout(Logger::Level::Medium) << std::endl;
1636 }
1637#endif
1638
1639 EdgeArray<double> capacities;
1640 capacities.init(g, 0);
1641 for (edge e : g.edges) {
1642 // some LP solvers might return a negative epsilon instead of 0 due to numerical reasons
1643 capacities[e] = max(xVal_[m_pMaster->edgeID(e)], 0.);
1645 capacities[e] += cardEps;
1646 }
1647 }
1648
1649#ifdef OGDF_STP_EXACT_LOGGING
1650 m_pMaster->lout(Logger::Level::Minor)
1651 << "Sub::mySeparate(): current capacities (>0) for mincut computation:" << std::endl;
1652 for (edge e : g.edges) {
1653 if (capacities[e] >= 2 * cardEps) {
1654 m_pMaster->lout(Logger::Level::Minor)
1655 << "\tcapacity[" << e << "]=" << capacities[e] << std::endl;
1656 }
1657 }
1658#endif
1659 // Minimum s-t-cut object
1661 // current terminal
1662 node t;
1663 // index of current terminal
1664 int ti = 0;
1665 // value of current cut
1666 double cutValue = 2.0;
1667 // value of current back cut
1668 double cutValueBack = 0.0;
1669 // for backcut computation
1670 int nOtherNodes = 0;
1671 // upper bound for mincut computation
1672 double uBound = 1 + nEdgesU * cardEps;
1673 // nr of violated cuts found
1674 int cutsFound = 0;
1675
1676 // buffer for new constraints
1678
1679 // size of cut and backcut
1682
1683 // Only relevant if nested cuts are computed:
1684 // this list contains the modified edges i.e., the saturated edges.
1685 // The capacity of these edges can is reset in SeparationStrategy 2
1687
1688 // main while loop for the computation of the cutting planes
1689 while (cutsFound < m_maxNrCuttingPlanes && ti < nTerminals) {
1690 t = terminal[ti];
1691 if (t != r) {
1692#ifdef OGDF_STP_EXACT_LOGGING
1693 m_pMaster->lout(Logger::Level::Medium)
1694 << "Sub::mySeparate(): computing minimum cut between root " << r << " and " << t
1695 << std::flush;
1696#endif
1697 // /compute the minimum r-t-cut
1698 /*
1699 * the cut itself is stored in the integer array with values
1700 * in {0,1,2}. If a node has the value 1, it is contained in the
1701 * subset of the root, 2 indicates the set of the separated node, and
1702 * 0 depicts the set in between
1703 */
1704 m_pMaster->timerMinSTCut()->start();
1705
1706 EdgeArray<double> flow;
1707 MaxFlowModule<double>* mf = m_pMaster->getMaxFlowModule();
1708 OGDF_ASSERT(mf != nullptr);
1709 mf->init(g);
1710 mf->useEpsilonTest(cardEps / 100);
1711 cutValue = mf->computeFlow(capacities, r, t, flow);
1712#ifdef OGDF_STP_EXACT_LOGGING
1713 m_pMaster->lout(Logger::Level::Medium) << " Calculated flow:" << std::endl;
1714 for (edge flowEdge : g.edges) {
1715 m_pMaster->lout(Logger::Level::Medium)
1716 << " " << flowEdge << " : " << flow[flowEdge] << " / "
1717 << capacities[flowEdge] << std::endl;
1718 }
1719#endif
1720
1721 // used epsilon should be smaller than the eps used for cardinality cuts heuristic
1722 minSTCut.setEpsilonTest(new EpsilonTest(cardEps / 100));
1723 minSTCut.call(g, capacities, flow, r, t);
1724
1725 m_pMaster->timerMinSTCut()->stop();
1726 cutValueBack = 0;
1727
1728#ifdef OGDF_STP_EXACT_LOGGING
1729 m_pMaster->lout(Logger::Level::Medium) << ", cutvalue=" << cutValue << std::flush;
1730#endif
1731
1732 // min cardinality
1733 if (m_minCardinalityCuts && cutValue < uBound) {
1734 for (edge e : g.edges) {
1735 if (minSTCut.isFrontCutEdge(e)) {
1736 cutValue -= cardEps;
1737 }
1738 if (m_computeBackCuts && minSTCut.isBackCutEdge(e)) {
1739 cutValueBack += capacities[e] - cardEps;
1740 }
1741 }
1742 } else if (m_computeBackCuts) {
1743 for (edge e : g.edges) {
1744 if (minSTCut.isBackCutEdge(e)) {
1745 cutValueBack += capacities[e];
1746 }
1747 }
1748 }
1749
1750 if (m_saturationStrategy == 2) {
1752 for (edge e : g.edges) {
1753 if (minSTCut.isFrontCutEdge(e)) {
1755 }
1756 if (m_computeBackCuts && minSTCut.isBackCutEdge(e)) {
1758 }
1759 }
1760 }
1761
1762#ifdef OGDF_STP_EXACT_LOGGING
1763 m_pMaster->lout(Logger::Level::Medium) << ", actual cutvalue=" << cutValue;
1764 if (m_computeBackCuts) {
1765 m_pMaster->lout(Logger::Level::Medium) << ", actual cutValueBack=" << cutValueBack;
1766 }
1767 m_pMaster->lout(Logger::Level::Medium) << std::endl;
1768#endif
1769 nOtherNodes = 0;
1770
1771 if (cutValue < oneMinusEps) {
1772 // found violated cut
1773 cutsFound++;
1774 // generate new constraint
1777 // push cut to the set of new constraints
1778 newConstraints.push(newCut);
1779
1780#ifdef OGDF_STP_EXACT_LOGGING
1781 m_pMaster->lout(Logger::Level::Medium)
1782 << "Sub::mySeparate(): found violated cut:" << std::endl;
1784#endif
1785 if (m_computeBackCuts && !minSTCut.frontCutIsComplementOfBackCut()
1786 && cutsFound < m_maxNrCuttingPlanes && cutValueBack <= oneMinusEps) {
1787 cutsFound++;
1788 // generate new constraint
1791 // push cut to the set of new constraints
1793
1794#ifdef OGDF_STP_EXACT_LOGGING
1795 m_pMaster->lout(Logger::Level::Medium)
1796 << "Sub::mySeparate(): found violated cut (backcut):" << std::endl;
1798#endif
1799 }
1800
1801 // saturate cut edges in case of nested cut computation
1802 if (m_computeNestedCuts) {
1803 for (edge e : g.edges) {
1804 if (minSTCut.isFrontCutEdge(e)) {
1805 if (m_saturationStrategy == 2) {
1806 capacities[e] = 1.0 / (double)cardinalityCut + eps;
1807 } else {
1808 capacities[e] = 1.0 + eps;
1809 }
1810 // for resetting the saturation after each iteration
1811 if (m_separationStrategy == 2) {
1812 modified.pushBack(e);
1813 }
1814 } else {
1815 if (m_computeBackCuts && nOtherNodes > 0 && cutValueBack <= oneMinusEps
1816 && minSTCut.isBackCutEdge(e)) {
1817 if (m_saturationStrategy == 2) {
1818 capacities[e] = 1.0 / (double)cardinalityBackcut + eps;
1819 } else {
1820 capacities[e] = 1.0 + eps;
1821 }
1822 // for resetting the saturation after each iteration
1823 if (m_separationStrategy == 2) {
1824 modified.pushBack(e);
1825 }
1826 }
1827 }
1828 }
1829 }
1830 }
1831 }
1832
1833 if (!m_computeNestedCuts) {
1834 ti++;
1835 } else {
1836 if (cutValue > oneMinusEps || r == t) {
1837 ti++;
1838 if (m_separationStrategy == 2) {
1839 while (!modified.empty()) {
1840 edge e = modified.popFrontRet();
1841 capacities[e] = xVal_[m_pMaster->edgeID(e)];
1843 capacities[e] += cardEps;
1844 }
1845 }
1846 }
1847 }
1848 }
1849 }
1850
1851 m_alreadySeparated = cutsFound;
1852
1853 if (cutsFound > 0) {
1854 // add separated directed cuts
1855 int nAdded = addCons(newConstraints, m_pMaster->cutPool());
1856 // update statistics
1857 m_pMaster->incNrCutsTotal(nAdded);
1858 m_pMaster->checkSetMaxPoolSize();
1859 if (nAdded != cutsFound) {
1860 // ToDo: is this a problem?!
1861 }
1862 }
1863
1864#ifdef OGDF_STP_EXACT_LOGGING
1865 m_pMaster->lout(Logger::Level::Medium)
1866 << "Sub::mySeparate(): id=" << this->id() << ", iter=" << this->nIter_ << " separated "
1867 << cutsFound << " directed cuts" << std::endl;
1868#endif
1869 m_pMaster->separationTimer()->stop();
1870
1871 return cutsFound;
1872}
1873
1874template<typename T>
1876 m_pMaster->primalHeuristicTimer()->start();
1877
1878#ifdef OGDF_STP_EXACT_LOGGING
1879 m_pMaster->lout(Logger::Level::Minor)
1880 << "Sub::myImprove(): id=" << this->id() << ", iter=" << this->nIter_ << std::endl;
1881#endif
1882 double eps = master_->eps();
1883 const Graph& g = m_pMaster->graph();
1884 EdgeWeightedGraph<double>& ewg = m_pMaster->weightedGraphPH();
1885
1886#ifdef OGDF_STP_EXACT_LOGGING
1887 if (m_pMaster->ilout(Logger::Level::Minor)) {
1888 m_pMaster->lout(Logger::Level::Minor) << "Sub::myImprove(): current solution:" << std::endl;
1889 for (edge e : g.edges) {
1890 m_pMaster->lout(Logger::Level::Minor)
1891 << "\tx" << m_pMaster->edgeID(e) << "=" << xVal_[m_pMaster->edgeID(e)]
1892 << ", edge " << e << std::endl;
1893 }
1894 }
1895#endif
1896
1897 // set edge weights to eps + (1-x_e)*c_e, forall edges e
1898 // thereby, use minimum of e and twin(e), respectively
1899 double currWeight, twinWeight;
1900 for (edge e : g.edges) {
1901 edge e2 = m_pMaster->twin(e);
1902 currWeight = 1.0 - xVal_[m_pMaster->edgeID(e)];
1903 twinWeight = 1.0 - xVal_[m_pMaster->edgeID(e2)];
1904 if (twinWeight < currWeight) {
1906 }
1907 if (currWeight < 0) {
1908 currWeight = 0;
1909 }
1910 ewg.setWeight(m_pMaster->edgeGToWgPH(e),
1911 eps + currWeight * variable(m_pMaster->edgeID(e))->obj());
1912 }
1913
1914#ifdef OGDF_STP_EXACT_LOGGING
1915 if (m_pMaster->ilout(Logger::Level::Minor)) {
1916 m_pMaster->lout(Logger::Level::Minor) << "Sub::myImprove(): edge weights:" << std::endl;
1917 for (edge e : g.edges) {
1918 m_pMaster->lout(Logger::Level::Minor)
1919 << "\tweight[" << e << "]=" << ewg.weight(m_pMaster->edgeGToWgPH(e))
1920 << std::endl;
1921 }
1922 }
1923#endif
1924
1925 // get primal heuristic algorithm
1926 auto& primalHeuristic = m_pMaster->getPrimalHeuristic();
1927
1928 // the computed heuristic solution
1930
1931#ifdef OGDF_STP_EXACT_LOGGING
1932 // heuristic solution value for the modified edge weights
1934#endif
1935 // call primal heuristic
1936 primalHeuristic->call(m_pMaster->weightedGraphPH(), m_pMaster->terminalListPH(),
1937 m_pMaster->isTerminalPH(), heuristicSolutionWg);
1938
1939 // verify that solution is a Steiner tree
1940 bool isSteinerTree = primalHeuristic->isSteinerTree(m_pMaster->weightedGraphPH(),
1941 m_pMaster->terminalListPH(), m_pMaster->isTerminalPH(), *heuristicSolutionWg);
1942
1943#ifdef OGDF_STP_EXACT_LOGGING
1944 m_pMaster->lout(Logger::Level::Default)
1945 << "Sub::myImprove(): primal heuristic algorithm returned solution with "
1946 << "value " << tmpHeuristicSolutionValue << ", isSteinerTree=" << isSteinerTree
1947 << std::endl;
1948#endif
1949
1950 if (isSteinerTree) {
1951 // actual heuristic solution value, i.e., by using real edge weights
1952 double heuristicSolutionValue = 0.0;
1953 // list of edges in the heuristic solution
1955
1956 for (edge e : heuristicSolutionWg->edges) {
1957 edge e2 = m_pMaster->edgeWgToGPH(heuristicSolutionWg->original(e));
1958 solutionEdges.pushBack(e2);
1959 heuristicSolutionValue += m_pMaster->capacities(e2);
1960#ifdef OGDF_STP_EXACT_LOGGING
1961 m_pMaster->lout(Logger::Level::Minor)
1962 << "\t" << e << " -> " << e2 << " c=" << m_pMaster->capacities(e2) << std::endl;
1963#endif
1964 }
1965
1966#ifdef OGDF_STP_EXACT_LOGGING
1967 m_pMaster->lout(Logger::Level::Default)
1968 << "Sub::myImprove(): found integer solution with value " << heuristicSolutionValue
1969 << std::endl;
1970#endif
1971 if (m_pMaster->betterPrimal(heuristicSolutionValue)) {
1972#ifdef OGDF_STP_EXACT_LOGGING
1973 m_pMaster->lout(Logger::Level::High)
1974 << std::setw(7) << this->id() << std::setw(7) << this->nIter_
1975 << " primal heuristic founds better integer solution with value "
1976 << heuristicSolutionValue << std::endl;
1977#endif
1978 m_pMaster->primalBound(heuristicSolutionValue);
1979 m_pMaster->updateBestSolutionByEdges(solutionEdges);
1980 }
1981 }
1982#ifdef OGDF_STP_EXACT_LOGGING
1983 else {
1984 m_pMaster->lout(Logger::Level::High)
1985 << "Sub::myImprove(): id=" << this->id() << ", iter=" << this->nIter_
1986 << ": computed solution is no Steiner tree!" << std::endl;
1987 }
1988#endif
1989
1990 delete heuristicSolutionWg;
1991 m_pMaster->primalHeuristicTimer()->stop();
1992}
1993
1994#ifdef OGDF_STP_EXACT_LOGGING
1995template<typename T>
1997 Logger::Level level) const {
1998 double eps = master_->eps();
1999 double val = 0;
2000 abacus::Variable* var;
2001 bool first = true;
2002 for (int i = 0; i < nVar(); i++) {
2003 var = variable(i);
2004 val = constraint->coeff(var);
2005 if (val > eps || val < -eps) {
2006 if (val > 0) {
2007 if (val > 1 - eps && val < 1 + eps) {
2008 if (!first) {
2009 m_pMaster->lout(level) << " + ";
2010 }
2011 } else {
2012 if (!first) {
2013 m_pMaster->lout(level) << " + " << val;
2014 }
2015 }
2016 } else {
2017 if (val < -1 + eps && val > -1 - eps) {
2018 if (!first) {
2019 m_pMaster->lout(level) << " - ";
2020 } else {
2021 m_pMaster->lout(level) << " -";
2022 }
2023 } else {
2024 if (!first) {
2025 m_pMaster->lout(level) << " - " << (-1) * val;
2026 } else {
2027 m_pMaster->lout(level) << val;
2028 }
2029 }
2030 }
2031 m_pMaster->lout(level) << "x" << i;
2032 first = false;
2033 }
2034 }
2035 m_pMaster->lout(level) << " " << *(constraint->sense()) << " " << constraint->rhs() << std::endl;
2036}
2037#endif
2038
2039template<typename T>
2041 if (m_hashKey != cv->hashKey()) {
2042 return false;
2043 }
2045 if (m_nMarkedNodes != dirCut->nMarkedNodes()) {
2046 return false;
2047 }
2048 for (node n : m_pGraph->nodes) {
2049 if (m_marked[n] != dirCut->marked(n)) {
2050 return false;
2051 }
2052 }
2053 return true;
2054}
2055
2056template<typename T>
2059 if (this->cutedge(edgeVar->theEdge())) {
2060 return 1.0;
2061 }
2062 return 0.0;
2063}
2064
2065template<typename T>
2067 const List<node>& terminals, const NodeArray<bool>& isTerminal,
2069 Master stpMaster(G, terminals, isTerminal, m_eps);
2070 if (m_configFile) {
2071 stpMaster.setConfigFile(m_configFile);
2072 }
2073#ifdef OGDF_STP_EXACT_LOGGING
2074 stpMaster.setOutputLevel(m_outputLevel);
2075#endif
2076 stpMaster.useDegreeConstraints(m_addDegreeConstraints);
2077 stpMaster.useIndegreeEdgeConstraints(m_addIndegreeEdgeConstraints);
2078 stpMaster.useGSEC2Constraints(m_addGSEC2Constraints);
2079 stpMaster.useFlowBalanceConstraints(m_addFlowBalanceConstraints);
2080 stpMaster.setMaxNumberAddedCuttingPlanes(m_maxNrAddedCuttingPlanes);
2081 stpMaster.useTerminalShuffle(m_shuffleTerminals);
2082 stpMaster.useBackCuts(m_backCutComputation);
2083 stpMaster.useNestedCuts(m_nestedCutComputation);
2084 stpMaster.setSeparationStrategy(m_separationStrategy);
2085 stpMaster.setSaturationStrategy(m_saturationStrategy);
2086 stpMaster.useMinCardinalityCuts(m_minCardinalityCuts);
2087 stpMaster.setMaxFlowModule(m_maxFlowModuleOption.get());
2088 if (m_primalHeuristic) {
2089 stpMaster.setPrimalHeuristic(m_primalHeuristic);
2090 }
2091 stpMaster.setPrimalHeuristicCallStrategy(m_callPrimalHeuristic);
2092 stpMaster.setPoolSizeInitFactor(m_poolSizeInitFactor);
2093 // XXX: should we set stpMaster.objInteger true/false according to weights automatically?
2094
2095 // now solve LP
2096 stpMaster.optimize();
2097
2098 // collect solution edges to build Steiner tree
2100 finalSteinerTree->createEmpty(G);
2101 T weight(0);
2102 for (edge e = G.firstEdge(); e; e = e->succ()) {
2103 if (stpMaster.isSolutionEdge(e)) {
2104 const node vO = e->source();
2105 node vC = finalSteinerTree->copy(vO);
2106 if (!vC) {
2107 vC = finalSteinerTree->newNode(vO);
2108 }
2109 const node wO = e->target();
2110 node wC = finalSteinerTree->copy(wO);
2111 if (!wC) {
2112 wC = finalSteinerTree->newNode(wO);
2113 }
2114 T edgeCost = G.weight(e);
2115 finalSteinerTree->newEdge(e, edgeCost);
2116 weight += edgeCost;
2117 }
2118 }
2119
2120 return weight;
2121}
2122
2123}
Declaration of class EdgeWeightedGraph.
Extends the GraphCopy concept to weighted graphs.
Includes declaration of graph class.
Contains logging functionality.
Declaration of min-st-cut algorithms parameterized by max-flow alorithm.
Implementation of the 2(1-1/l)-approximation algorithm for the minimum Steiner tree problem by Matsuy...
Includes Abacus.
Abstract base class for all branching rules.
Definition branchrule.h:59
Forms the virtual base class for all possible constraints given in pool format.
Definition constraint.h:56
CSense * sense()
Returns a pointer to the sense of the constraint.
Definition constraint.h:115
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:130
The master of the optimization.
Definition master.h:69
OSISOLVER
This enumeration defines which solvers can be used to solve the LP-relaxations.
Definition master.h:209
static const char * OSISOLVER_[]
Array for the literal values for possible Osi solvers.
Definition master.h:212
OSISOLVER defaultLpSolver() const
returns the Lp Solver.
Definition master.h:533
Standard pools without constraint duplication.
Definition nonduplpool.h:58
The subproblem.
Definition sub.h:68
int id() const
Returns the identity number of the subproblem.
Definition sub.h:153
int nIter_
The number of iterations in the cutting plane phase.
Definition sub.h:1479
Master * master()
Returns the master of the optimization.
Definition sub.h:320
TYPE
The enumeration with the different variable types.
Definition vartype.h:47
@ Continuous
A continuous variable.
Definition vartype.h:48
@ Binary
A variable having value 0 or 1.
Definition vartype.h:50
Forms the virtual base class for all possible variables given in pool format.
Definition variable.h:59
Exception thrown when an algorithm realizes an internal bug that prevents it from continuing.
Definition exceptions.h:241
An array that keeps track of the number of inserted elements; also usable as an efficient stack.
Definition ArrayBuffer.h:56
The parameterized class Array implements dynamic arrays of type E.
Definition Array.h:214
Dynamic arrays indexed with edges.
Definition EdgeArray.h:125
void init()
Reinitializes the array. Associates the array with no graph.
Definition EdgeArray.h:292
Class for the representation of edges.
Definition Graph_d.h:300
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:362
bool isSelfLoop() const
Returns true iff the edge is a self-loop (source node = target node).
Definition Graph_d.h:356
node target() const
Returns the target node of the edge.
Definition Graph_d.h:338
node source() const
Returns the source node of the edge.
Definition Graph_d.h:335
edge newEdge(node v, node w, T weight)
Data type for general directed graphs (adjacency list representation).
Definition Graph_d.h:521
int numberOfNodes() const
Returns the number of nodes in the graph.
Definition Graph_d.h:622
edge newEdge(node v, node w)
Creates a new edge (v,w) and returns it.
int numberOfEdges() const
Returns the number of edges in the graph.
Definition Graph_d.h:625
internal::GraphObjectContainer< NodeElement > nodes
The container containing all node objects.
Definition Graph_d.h:589
node newNode()
Creates a new node and returns it.
internal::GraphObjectContainer< EdgeElement > edges
The container containing all edge objects.
Definition Graph_d.h:592
Representation of levels in hierarchies.
Definition Level.h:60
Doubly linked lists (maintaining the length of the list).
Definition List.h:1435
Encapsulates a pointer to a list element.
Definition List.h:103
Centralized global and local logging facility working on streams like std::cout.
Definition Logger.h:100
Level
supported log-levels from lowest to highest importance
Definition Logger.h:103
std::ostream & lout(Level level=Level::Default) const
stream for logging-output (local)
Definition Logger.h:142
Computes a max flow via Preflow-Push (global relabeling and gap relabeling heuristic).
Min-st-cut algorithm, that calculates the cut via maxflow.
cutType
The three types of cuts.
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...
Dynamic arrays indexed with nodes.
Definition NodeArray.h:125
void init()
Reinitializes the array. Associates the array with no graph.
Definition NodeArray.h:266
Class for the representation of nodes.
Definition Graph_d.h:177
int degree() const
Returns the degree of the node (indegree + outdegree).
Definition Graph_d.h:220
Implements a stopwatch measuring wall-clock time.
Definition Stopwatch.h:180
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition basic.h:41
int randomNumber(int low, int high)
Returns random integer between low and high (including).
int r[]
static MultilevelBuilder * getDoubleFactoredZeroAdjustedMerger()
The namespace for all OGDF objects.
open subproblems.