Loading [MathJax]/extensions/tex2jax.js

Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
SpannerBerman.h
Go to the documentation of this file.
1
32#pragma once
33
36#include <ogdf/external/coin.h>
38
39#include <iomanip>
40
41namespace ogdf {
42
75template<typename TWeight>
76class SpannerBerman : public SpannerModule<TWeight> {
77public:
78 static Logger logger;
79
81 m_OPT = 0;
82 m_osi = nullptr;
83 }
84
85 virtual ~SpannerBerman() { resetLP(); }
86
88 virtual bool preconditionsOk(const GraphAttributes& GA, double stretch,
89 std::string& error) override {
90 if (!isSimple(GA.constGraph())) {
91 error = "The graph is not simple";
92 return false;
93 }
94 if (!isConnected(GA.constGraph())) {
95 error = "The graph is not connected";
96 return false;
97 }
98 if (stretch < 1.0) {
99 error = "The stretch must be >= 1.0";
100 return false;
101 }
102 return true;
103 }
104
105private:
110
111 const Graph* m_G;
117
118 // Some precalculated values that are needed all over the place.
120 double m_beta;
121 double m_sqrtlog;
123
126
129
130 // Solving the LP
132 std::vector<CoinPackedVector*> m_constraints;
133 int m_OPT;
134
136
137 inline bool isThinEdge(edge e) { return !m_isThickEdge(e); }
138
139 inline bool isThickEdge(edge e) { return m_isThickEdge(e); }
140
146 void resetLP() {
147 for (auto c : m_constraints) {
148 delete c;
149 }
150 m_constraints.clear();
151
152 delete m_osi;
153 m_osi = nullptr;
154 }
155
157 virtual void init(const GraphAttributes& GA, double stretch, GraphCopySimple& spanner,
158 EdgeArray<bool>& inSpanner) override {
159 resetLP();
161
162 m_G = &GA.constGraph();
163 m_weight.init(*m_G);
164 for (edge e : m_G->edges) {
165 m_weight[e] = getWeight(*m_GA, e);
166 }
168 m_isThickEdge.init(*m_G, false);
169
174
175 m_E1.init(*m_G, false);
176 m_E2.init(*m_G, false);
177
178 m_inDistance.init(*m_G);
179 m_outDistance.init(*m_G);
180 }
181
183 virtual typename SpannerModule<TWeight>::ReturnType execute() override {
184 if (m_G->numberOfNodes() == 0 || m_G->numberOfEdges() == 0) {
186 }
187 firstPart();
188 printStats();
189
190 logger.lout() << "\n2. Part\n" << std::endl;
191 bool ok = secondPart();
192
193 if (!ok) {
195 }
196
197 printStats(true);
198
199 // Fill m_inSpanner since only E1, E2 and m_spanner are correctly filled.
200 for (edge e : m_G->edges) {
201 (*m_inSpanner)[e] = m_E1[e] || m_E2[e];
202 }
204 }
205
209 void firstPart() {
212 for (node n : m_G->nodes) {
216 }
217
218 int max = ceil(m_beta * log(m_G->numberOfNodes())); // log is ln
219 logger.lout() << "max=" << max << std::endl;
220 for (int i = 0; i < max; i++) {
221 node v = m_G->chooseNode();
222 for (node n : m_G->nodes) {
223 edge e1 = inPredecessor[v][n];
224 if (e1) {
226 }
227
228 edge e2 = outPredecessor[v][n];
229 if (e2) {
231 }
232 }
233 }
235 logger.lout() << "thickEdgeNodeAmountLimit=" << m_thickEdgeNodeAmountLimit << std::endl;
236 printStats();
237
238 logger.lout() << "add unsettled thick edges" << std::endl;
241 }
242
246 void inArborescence(const GraphAttributes& GA, node root, NodeArray<edge>& predecessor,
249 dijkstra.callUnbound(*m_G, m_weight, root, predecessor, distance, true, true);
250 }
251
255 void outArborescence(const GraphAttributes& GA, node root, NodeArray<edge>& predecessor,
258 dijkstra.callUnbound(*m_G, m_weight, root, predecessor, distance, true, false);
259 }
260
265 if (!m_E1[e]) {
266 m_E1[e] = true;
267 m_spanner->newEdge(e);
269 }
270 }
271
272 void printStats(bool assert = false) {
273 logger.lout() << "\nStats:" << std::endl;
274 logger.lout() << m_spanner->numberOfEdges() << " covered of " << m_G->numberOfEdges()
275 << std::endl;
276
277 int E1amount = 0;
278 int E1amountThick = 0;
279 int E1amountThinNotInE2 = 0;
280 int E2amount = 0;
281 int E2amountThin = 0;
282 int E2amountThickNotInE1 = 0;
283 int edgesCovered = 0;
284 int duplicateCovered = 0;
285 for (edge e : m_G->edges) {
286 if (m_E1[e]) {
287 E1amount++;
288 if (isThickEdge(e)) {
290 } else if (!m_E2[e]) {
292 }
293 }
294 if (m_E2[e]) {
295 E2amount++;
296 if (isThinEdge(e)) {
297 E2amountThin++;
298 } else if (!m_E1[e]) {
300 }
301 }
302 if (m_E1[e] || m_E2[e]) {
303 edgesCovered++;
304 }
305 if (m_E1[e] && m_E2[e]) {
307 }
308 }
309
310 logger.lout() << "covered: " << edgesCovered << ", duplicate edges: " << duplicateCovered
311 << std::endl;
312 logger.lout() << "E1: " << E1amount << " edges, " << E1amountThick << " thick, "
313 << (E1amount - E1amountThick) << " thin, " << E1amountThinNotInE2
314 << " thin edges not in E2" << std::endl;
315 logger.lout() << "E2: " << E2amount << " edges, " << E2amountThin << " thin, "
316 << (E2amount - E2amountThin) << " thick, " << E2amountThickNotInE1
317 << " thick edges not in E1" << std::endl;
318
319#if defined(OGDF_DEBUG)
320 if (assert) {
323 }
324#endif
325
327 }
328
330 for (edge e : m_G->edges) {
332 }
333 }
334
340
342 for (node n : m_G->nodes) {
343 TWeight sd = m_outDistance[e->source()][n];
344 TWeight td = m_inDistance[e->target()][n];
345 if (sd == std::numeric_limits<TWeight>::max()
346 || td == std::numeric_limits<TWeight>::max()) {
347 continue;
348 }
349
350 OGDF_ASSERT(m_eps.geq(sd + td, static_cast<TWeight>(0)));
351 OGDF_ASSERT(m_eps.less(sd + td, std::numeric_limits<TWeight>::max()));
352
353 if (m_eps.leq((sd + td), maxDistance)) {
355 }
356
358 return true;
359 }
360 }
361 return false;
362 }
363
368
380
382 const node t, int maxLookupDist) {
383 NodeArray<TWeight> distances;
384 NodeArray<edge> predecessor;
386 dijkstra.callBound(G, weights, s, predecessor, distances, m_GA->directed(),
387 false, // arcs are not reversed
389 return distances[t];
390 }
391
393 for (edge e : m_G->edges) {
394 if (m_E1[e]) {
395 continue;
396 }
397
398 if (isThickEdge(e)) {
399 if (!isSettledEdge(e)) {
401 }
403 }
404 }
405 }
406
410 bool secondPart() {
411 logger.lout() << "Using LP-Solver: " << Configuration::whichLPSolver() << std::endl;
413 m_osi->messageHandler()->setLogLevel(0); // 0=nothing .. 4=verbose
414 m_osi->setObjSense(1); // minimize
415
416 // One column per edge (x_e)
418 EdgeArray<int> indices(*m_G);
419 int i = 0;
420 for (edge e : m_G->edges) {
421 m_osi->addCol(zero, 0, 1, 1); // vec, lower, upper, objective
422 indices[e] = i++;
423 }
424
425
427 for (edge e : m_G->edges) {
428 optConstraint->insert(indices[e], 1);
429 }
430 // sense: 'E' == 'G' >= 'L' <=
431 m_osi->addRow(*optConstraint, 'L', 0, 0); // constraint, sense, rhs (will be set below), range
432 m_constraints.push_back(optConstraint);
433
434 // Set the initial value of the OPT constraint rhs to n-1
435 setOpt(m_G->numberOfNodes() - 1);
436
437 int amountSolverCalls = 0;
438 while (true) {
439 if (amountSolverCalls == 0) {
440 m_osi->initialSolve();
441 } else {
442 m_osi->resolve();
443 }
445
447
448 logger.lout() << amountSolverCalls << ". solve... ";
449 if (m_osi->isProvenOptimal()) {
450 logger.lout() << "-> optimal (" << m_osi->getObjValue() << ")" << std::endl;
451 logger.lout() << " separation... ";
452 SeparationResult result = separation(m_osi->getColSolution(), indices);
453
454 if (result == SeparationResult::Solution) {
455 for (edge e : m_G->edges) {
456 if (m_E2[e] && !m_E1[e]) {
457 m_spanner->newEdge(e);
459 }
460 }
461 logger.lout() << "-> Found solution\n" << std::endl;
462 logger.lout() << "solver calls: " << amountSolverCalls << std::endl;
463 logger.lout() << "cols: " << m_osi->getNumCols() << std::endl;
464 logger.lout() << "rows: " << m_osi->getNumRows() << std::endl;
465 return true;
466 } else if (result == SeparationResult::NewConstraint) {
467 logger.lout() << "-> New constraint" << std::endl;
468 } else { // Fail
469 logger.lout() << "-> Failed" << std::endl;
470 if (!setOpt(m_OPT + 1)) {
471 return false;
472 }
473 }
474 } else if (m_osi->isProvenPrimalInfeasible()) {
475 logger.lout() << "-> Infeasible" << std::endl;
476 if (!setOpt(m_OPT + 1)) {
477 return false;
478 }
479 } else if (m_osi->isProvenDualInfeasible()) {
480 logger.lout() << "-> Unbounded" << std::endl;
481 return false;
482 } else {
483 logger.lout() << "-> No solution found" << std::endl;
484 return false;
485 }
486 };
487 }
488
494 bool setOpt(int opt) {
495 m_OPT = opt;
496 if (m_OPT > m_nSquared) {
497 logger.lout() << " OPT is too large. Abort." << std::endl;
498 return false;
499 }
500 float percentage = static_cast<float>(m_OPT) / m_nSquared * 100.0;
501 logger.lout() << " Set OPT to " << m_OPT << " (" << std::fixed << std::setprecision(2)
502 << percentage << "% of " << m_nSquared << ")" << std::endl;
503
504 m_osi->setRowBounds(0, 0.0,
505 m_OPT); // this is the opt constraint; it is 0 <= sum(x_e) <= m_OPT
506 return true;
507 }
508
509 SeparationResult separation(const double* solution, const EdgeArray<int>& indices) {
510 EdgeArray<bool> out(*m_G, false);
512
513 GraphCopySimple copy;
514 copy.createEmpty(*m_G);
515 for (node n : m_G->nodes) {
516 copy.newNode(n);
517 }
519 int amountCoveredEdges = 0;
520 for (edge e : m_G->edges) {
521 if (out[e]) {
523 copy.newEdge(e);
524 copyWeight[copy.copy(e)] = m_weight[e];
525 }
526 }
527
528 bool settlesAllThinEdges = true;
529 edge unsettledThinEdge = nullptr;
530 for (edge e : m_G->edges) {
531 if (isThinEdge(e) && !isSettledEdge(e, copy, copyWeight)) {
532 settlesAllThinEdges = false;
534 break;
535 }
536 }
537
539 if (amountCoveredEdges <= ceil(2.0 * m_OPT * m_sqrtlog)) {
540 m_E2 = out;
542 } else {
544 }
545 } else {
548
549 double rowValue = 0.0;
550 int i = 0;
551 for (edge e : m_G->edges) {
552 if (antispanner[e]) {
553 rowValue += solution[i];
554 }
555 i++;
556 }
557 if (m_eps.less(rowValue, 1.0)) {
558 // create new constraint
560 for (edge e : m_G->edges) {
561 if (antispanner[e]) {
562 c->insert(indices[e], 1);
563 }
564 }
565 m_osi->addRow(*c, 'G', 1, 0);
566 m_constraints.push_back(c);
568 } else {
570 }
571 }
572 }
573
575 int i = 0;
576 for (edge e : m_G->edges) {
577 double p = m_sqrtlog * fractions[i++]; // P may not be limited to 1: if it is >1 the
578 // result will always be true, which is ok.
579 out[e] = randomDouble(0.0, 1.0) <= p;
580 }
581 }
582
585 GraphCopySimple copy;
586 copy.createEmpty(*m_G);
587 for (node n : m_G->nodes) {
588 copy.newNode(n);
589 }
591
593 for (edge e : m_G->edges) {
594 // s->e->t
595 TWeight s_e = m_outDistance[unsettledThinEdge->source()][e->source()];
596 TWeight e_t = m_inDistance[unsettledThinEdge->target()][e->target()];
597 bool isInLocalSubgraph = (s_e != std::numeric_limits<TWeight>::max()
598 && e_t != std::numeric_limits<TWeight>::max()
599 && m_eps.leq(s_e + m_weight[e] + e_t, maxDistance));
600
601 // Graph to maximize is E_st\‍(E_st\E2) = E_st intersection E2
602 // intersection[e] = isInLocalSubgraph && out[e];
603 if (isInLocalSubgraph && out[e]) {
604 copy.newEdge(e);
605 copyWeight[copy.copy(e)] = m_weight[e];
606 }
607
608 // e in antispanner <=> e in E_st and not in E2 (=out)
609 antispanner[e] = isInLocalSubgraph && !out[e];
610 }
612
613 // minimize antispanner
614 bool changed;
615 do {
616 changed = false;
617 for (edge e : m_G->edges) {
618 if (!antispanner[e]) {
619 continue;
620 }
621
622 // try to remove e and check, if still no path <=stretch*max exists
623 // -> removing e from antispanner is equivalent to adding e to the graph
624 copy.newEdge(e);
625 copyWeight[copy.copy(e)] = m_weight[e];
626 antispanner[e] = false;
627
629 // we've found an unnecessary edge.
630 changed = true;
631 } else {
632 antispanner[e] = true;
633 copy.delEdge(copy.copy(e));
634 }
635 }
636
638 } while (changed);
639 }
640
647};
648
649template<typename TWeight>
651 Logger::Level::High); // do not log by default
652
653}
Basic module for spanner algorithms.
static OsiSolverInterface * createCorrectOsiSolverInterface()
Get a new solver and set its initial log level according to the level of CoinLog.
Definition coin.h:52
static constexpr LPSolver whichLPSolver()
Returns the LP-solver used by OGDF.
Definition config.h:255
Dijkstra's single source shortest path algorithm.
Definition Dijkstra.h:53
void callUnbound(const Graph &G, const EdgeArray< T > &weight, const List< node > &sources, NodeArray< edge > &predecessor, NodeArray< T > &distance, bool directed=false, bool arcsReversed=false)
Calculates, based on the graph G with corresponding edge costs and source nodes, the shortest paths a...
Definition Dijkstra.h:70
void callBound(const Graph &G, const EdgeArray< T > &weight, const List< node > &sources, NodeArray< edge > &predecessor, NodeArray< T > &distance, bool directed, bool arcsReversed, node target, T maxLength=std::numeric_limits< T >::max())
Calculates, based on the graph G with corresponding edge costs and source nodes, the shortest paths a...
Definition Dijkstra.h:138
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
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
std::enable_if< std::is_integral< T >::value, bool >::type leq(const T &x, const T &y) const
Compare if x is LEQ than y for integral types.
Definition EpsilonTest.h:83
std::enable_if< std::is_integral< T >::value, bool >::type geq(const T &x, const T &y) const
Compare if x is GEQ to y for integral types.
std::enable_if< std::is_integral< T >::value, bool >::type less(const T &x, const T &y) const
Compare if x is LESS than y for integral types.
Definition EpsilonTest.h:57
Stores additional attributes of a graph (like layout information).
bool directed() const
Returns if the graph is directed.
Copies of graphs with mapping between nodes and edges.
Definition GraphCopy.h:59
edge newEdge(edge eOrig)
Creates a new edge in the graph copy with original edge eOrig.
Definition GraphCopy.h:188
virtual void delEdge(edge e) override
Removes edge e.
node copy(node v) const
Returns the node in the graph copy corresponding to v.
Definition GraphCopy.h:124
node newNode(node vOrig)
Creates a new node in the graph copy with original node vOrig.
Definition GraphCopy.h:173
void createEmpty(const Graph &G)
Re-initializes the copy using G, but does not create any nodes or edges.
Data type for general directed graphs (adjacency list representation).
Definition Graph_d.h:521
node chooseNode(std::function< bool(node)> includeNode=[](node) { return true;}, bool isFastTest=true) const
Returns a random node.
int numberOfNodes() const
Returns the number of nodes in the graph.
Definition Graph_d.h:622
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
internal::GraphObjectContainer< EdgeElement > edges
The container containing all edge objects.
Definition Graph_d.h:592
Centralized global and local logging facility working on streams like std::cout.
Definition Logger.h:100
@ Log
the object is in logging mode, using its own localLogLevel
std::ostream & lout(Level level=Level::Default) const
stream for logging-output (local)
Definition Logger.h:142
ReturnType
The return type of a module.
Definition Module.h:50
Dynamic arrays indexed with nodes.
Definition NodeArray.h:125
Class for the representation of nodes.
Definition Graph_d.h:177
Approximation algorithm for calculating spanners.
void randomizedSelection(const double *fractions, EdgeArray< bool > &out)
void firstPart()
First part of the algorithm: Settle all thick edges.
static Logger logger
void printStats(bool assert=false)
bool isThickEdge(edge e)
EdgeArray< bool > m_isThickEdge
bool secondPart()
The second part: Settling all thin edges.
bool isSettledEdge(const edge e, const GraphCopySimple &_spanner, const EdgeArray< TWeight > &_spannerWeight)
bool isSettledEdge(const edge e)
Shortcut for calling isSettledEdge with m_spanner and the current spanner weights.
const Graph * m_G
const reference to the original graph
int m_OPT
The current guess for our optimal LP value.
SeparationResult
Used to indicate the result of the separation method.
EdgeArray< bool > m_E1
Holds the set E1 from the first part of the algorithm.
EdgeArray< bool > m_E2
Holds the set E2 from the second part of the algorithm.
NodeArray< NodeArray< TWeight > > m_outDistance
m_outDistance[v][w]: distance from v in a v-rooted out-arborescense to w
int m_thickEdgeNodeAmountLimit
n/beta
EdgeArray< TWeight > m_spannerWeight
weights for m_spanner Caches, if an edge is thick (or thin).
SeparationResult separation(const double *solution, const EdgeArray< int > &indices)
OsiSolverInterface * m_osi
the solver.
virtual void init(const GraphAttributes &GA, double stretch, GraphCopySimple &spanner, EdgeArray< bool > &inSpanner) override
Initializes members and create an empty spanner.
void outArborescence(const GraphAttributes &GA, node root, NodeArray< edge > &predecessor, NodeArray< TWeight > &distance)
Calculates an out-arborescense rooted at root.
virtual SpannerModule< TWeight >::ReturnType execute() override
Executes the core algorithm.
double m_beta
sqrt(n)
NodeArray< NodeArray< TWeight > > m_inDistance
m_inDistance[v][w]: distance from v in a v-rooted in-arborescense to w
void inArborescence(const GraphAttributes &GA, node root, NodeArray< edge > &predecessor, NodeArray< TWeight > &distance)
Calculates an in-arborescense rooted at root.
void addEdgeToSpanner(edge e)
Add an edge to the spanner.
virtual bool preconditionsOk(const GraphAttributes &GA, double stretch, std::string &error) override
bool setOpt(int opt)
Set a new value for m_OPT.
bool _isThickEdge(edge e)
Actually calculates whether e is thick or not.
std::vector< CoinPackedVector * > m_constraints
Holds all constraints so they can be freed at destruction.
EdgeArray< TWeight > m_weight
weights of each edge from m_G
void createAntispanner(const edge unsettledThinEdge, const EdgeArray< bool > &out, EdgeArray< bool > &antispanner)
void resetLP()
Resets the LP defining variables.
bool isThinEdge(edge e)
double m_sqrtlog
sqrt(n) * ln(n)
TWeight distance(const GraphCopySimple &G, const EdgeArray< TWeight > &weights, const node s, const node t, int maxLookupDist)
Interface for spanner algorithms.
void assertTimeLeft()
Assert, that time is left.
virtual void init(const GraphAttributes &GA, double stretch, GraphCopySimple &spanner, EdgeArray< bool > &inSpanner)
Initializes members and create an empty spanner.
const GraphAttributes * m_GA
EdgeArray< bool > * m_inSpanner
static TWeight getWeight(const GraphAttributes &GA, edge e)
GraphCopySimple * m_spanner
Definition of ogdf::CoinManager.
Declaration of extended graph algorithms.
bool isConnected(const Graph &G)
Returns true iff G is connected.
bool isSimple(const Graph &G)
Returns true iff G contains neither self-loops nor parallel edges.
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition basic.h:41
double randomDouble(double low, double high)
Returns a random double value from the interval [low, high).
static MultilevelBuilder * getDoubleFactoredZeroAdjustedMerger()
The namespace for all OGDF objects.
Declaration of simple graph algorithms.