Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
SpannerBerman.h
Go to the documentation of this file.
1
32#pragma once
33
35#include <ogdf/basic/Graph.h>
39#include <ogdf/basic/Logger.h>
40#include <ogdf/basic/basic.h>
44
45#include <ogdf/external/coin.h>
46
47#include <cmath>
48#include <iomanip>
49#include <limits>
50#include <ostream>
51#include <string>
52#include <vector>
53
54namespace ogdf {
55
88template<typename TWeight>
89class SpannerBerman : public SpannerModule<TWeight> {
90public:
91 static Logger logger;
92
94 m_OPT = 0;
95 m_osi = nullptr;
96 }
97
98 virtual ~SpannerBerman() { resetLP(); }
99
101 virtual bool preconditionsOk(const GraphAttributes& GA, double stretch,
102 std::string& error) override {
103 if (!isSimple(GA.constGraph())) {
104 error = "The graph is not simple";
105 return false;
106 }
107 if (!isConnected(GA.constGraph())) {
108 error = "The graph is not connected";
109 return false;
110 }
111 if (stretch < 1.0) {
112 error = "The stretch must be >= 1.0";
113 return false;
114 }
115 return true;
116 }
117
118private:
123
124 const Graph* m_G;
130
131 // Some precalculated values that are needed all over the place.
133 double m_beta;
134 double m_sqrtlog;
136
139
142
143 // Solving the LP
144 OsiSolverInterface* m_osi;
145 std::vector<CoinPackedVector*> m_constraints;
146 int m_OPT;
147
149
150 inline bool isThinEdge(edge e) { return !m_isThickEdge(e); }
151
152 inline bool isThickEdge(edge e) { return m_isThickEdge(e); }
153
159 void resetLP() {
160 for (auto c : m_constraints) {
161 delete c;
162 }
163 m_constraints.clear();
164
165 delete m_osi;
166 m_osi = nullptr;
167 }
168
170 virtual void init(const GraphAttributes& GA, double stretch, GraphCopySimple& spanner,
171 EdgeArray<bool>& inSpanner) override {
172 resetLP();
173 SpannerModule<TWeight>::init(GA, stretch, spanner, inSpanner);
174
175 m_G = &GA.constGraph();
176 m_weight.init(*m_G);
177 for (edge e : m_G->edges) {
178 m_weight[e] = getWeight(*m_GA, e);
179 }
181 m_isThickEdge.init(*m_G, false);
182
184 m_beta = sqrt(m_G->numberOfNodes()),
187
188 m_E1.init(*m_G, false);
189 m_E2.init(*m_G, false);
190
191 m_inDistance.init(*m_G);
192 m_outDistance.init(*m_G);
193 }
194
196 virtual typename SpannerModule<TWeight>::ReturnType execute() override {
197 if (m_G->numberOfNodes() == 0 || m_G->numberOfEdges() == 0) {
199 }
200 firstPart();
201 printStats();
202
203 logger.lout() << "\n2. Part\n" << std::endl;
204 bool ok = secondPart();
205
206 if (!ok) {
208 }
209
210 printStats(true);
211
212 // Fill m_inSpanner since only E1, E2 and m_spanner are correctly filled.
213 for (edge e : m_G->edges) {
214 (*m_inSpanner)[e] = m_E1[e] || m_E2[e];
215 }
217 }
218
222 void firstPart() {
223 NodeArray<NodeArray<edge>> inPredecessor(*m_G);
224 NodeArray<NodeArray<edge>> outPredecessor(*m_G);
225 for (node n : m_G->nodes) {
226 inArborescence(*m_GA, n, inPredecessor[n], m_inDistance[n]);
227 outArborescence(*m_GA, n, outPredecessor[n], m_outDistance[n]);
229 }
230
231 int max = ceil(m_beta * log(m_G->numberOfNodes())); // log is ln
232 logger.lout() << "max=" << max << std::endl;
233 for (int i = 0; i < max; i++) {
234 node v = m_G->chooseNode();
235 for (node n : m_G->nodes) {
236 edge e1 = inPredecessor[v][n];
237 if (e1) {
239 }
240
241 edge e2 = outPredecessor[v][n];
242 if (e2) {
244 }
245 }
246 }
248 logger.lout() << "thickEdgeNodeAmountLimit=" << m_thickEdgeNodeAmountLimit << std::endl;
249 printStats();
250
251 logger.lout() << "add unsettled thick edges" << std::endl;
254 }
255
259 void inArborescence(const GraphAttributes& GA, node root, NodeArray<edge>& predecessor,
261 Dijkstra<TWeight> dijkstra;
262 dijkstra.callUnbound(*m_G, m_weight, root, predecessor, distance, true, true);
263 }
264
268 void outArborescence(const GraphAttributes& GA, node root, NodeArray<edge>& predecessor,
270 Dijkstra<TWeight> dijkstra;
271 dijkstra.callUnbound(*m_G, m_weight, root, predecessor, distance, true, false);
272 }
273
278 if (!m_E1[e]) {
279 m_E1[e] = true;
280 m_spanner->newEdge(e);
282 }
283 }
284
285 void printStats(bool assert = false) {
286 logger.lout() << "\nStats:" << std::endl;
287 logger.lout() << m_spanner->numberOfEdges() << " covered of " << m_G->numberOfEdges()
288 << std::endl;
289
290 int E1amount = 0;
291 int E1amountThick = 0;
292 int E1amountThinNotInE2 = 0;
293 int E2amount = 0;
294 int E2amountThin = 0;
295 int E2amountThickNotInE1 = 0;
296 int edgesCovered = 0;
297 int duplicateCovered = 0;
298 for (edge e : m_G->edges) {
299 if (m_E1[e]) {
300 E1amount++;
301 if (isThickEdge(e)) {
302 E1amountThick++;
303 } else if (!m_E2[e]) {
304 E1amountThinNotInE2++;
305 }
306 }
307 if (m_E2[e]) {
308 E2amount++;
309 if (isThinEdge(e)) {
310 E2amountThin++;
311 } else if (!m_E1[e]) {
312 E2amountThickNotInE1++;
313 }
314 }
315 if (m_E1[e] || m_E2[e]) {
316 edgesCovered++;
317 }
318 if (m_E1[e] && m_E2[e]) {
319 duplicateCovered++;
320 }
321 }
322
323 logger.lout() << "covered: " << edgesCovered << ", duplicate edges: " << duplicateCovered
324 << std::endl;
325 logger.lout() << "E1: " << E1amount << " edges, " << E1amountThick << " thick, "
326 << (E1amount - E1amountThick) << " thin, " << E1amountThinNotInE2
327 << " thin edges not in E2" << std::endl;
328 logger.lout() << "E2: " << E2amount << " edges, " << E2amountThin << " thin, "
329 << (E2amount - E2amountThin) << " thick, " << E2amountThickNotInE1
330 << " thick edges not in E1" << std::endl;
331
332#if defined(OGDF_DEBUG)
333 if (assert) {
334 OGDF_ASSERT((E1amountThick + E2amountThin + E1amountThinNotInE2 + E2amountThickNotInE1)
336 }
337#endif
338
339 OGDF_ASSERT(edgesCovered == m_spanner->numberOfEdges());
340 }
341
343 for (edge e : m_G->edges) {
345 }
346 }
347
352 const TWeight maxDistance = m_stretch * m_weight[e];
353
354 int amountNodesInShortestPaths = 0;
355 for (node n : m_G->nodes) {
356 TWeight sd = m_outDistance[e->source()][n];
357 TWeight td = m_inDistance[e->target()][n];
358 if (sd == std::numeric_limits<TWeight>::max()
359 || td == std::numeric_limits<TWeight>::max()) {
360 continue;
361 }
362
363 OGDF_ASSERT(m_eps.geq(sd + td, static_cast<TWeight>(0)));
364 OGDF_ASSERT(m_eps.less(sd + td, std::numeric_limits<TWeight>::max()));
365
366 if (m_eps.leq((sd + td), maxDistance)) {
367 amountNodesInShortestPaths++;
368 }
369
370 if (amountNodesInShortestPaths >= m_thickEdgeNodeAmountLimit) {
371 return true;
372 }
373 }
374 return false;
375 }
376
381
385 bool isSettledEdge(const edge e, const GraphCopySimple& _spanner,
386 const EdgeArray<TWeight>& _spannerWeight) {
387 const TWeight maxDistance = m_stretch * m_weight[e];
388 node u = _spanner.copy(e->source());
389 node v = _spanner.copy(e->target());
390 TWeight currentSpannerDistance = distance(_spanner, _spannerWeight, u, v, ceil(maxDistance));
391 return m_eps.leq(currentSpannerDistance, maxDistance);
392 }
393
394 TWeight distance(const GraphCopySimple& G, const EdgeArray<TWeight>& weights, const node s,
395 const node t, int maxLookupDist) {
396 NodeArray<TWeight> distances;
397 NodeArray<edge> predecessor;
398 Dijkstra<TWeight> dijkstra;
399 dijkstra.callBound(G, weights, s, predecessor, distances, m_GA->directed(),
400 false, // arcs are not reversed
401 t, maxLookupDist);
402 return distances[t];
403 }
404
406 for (edge e : m_G->edges) {
407 if (m_E1[e]) {
408 continue;
409 }
410
411 if (isThickEdge(e)) {
412 if (!isSettledEdge(e)) {
414 }
416 }
417 }
418 }
419
423 bool secondPart() {
424 logger.lout() << "Using LP-Solver: " << Configuration::whichLPSolver() << std::endl;
426 m_osi->messageHandler()->setLogLevel(0); // 0=nothing .. 4=verbose
427 m_osi->setObjSense(1); // minimize
428
429 // One column per edge (x_e)
430 CoinPackedVector zero;
431 EdgeArray<int> indices(*m_G);
432 int i = 0;
433 for (edge e : m_G->edges) {
434 m_osi->addCol(zero, 0, 1, 1); // vec, lower, upper, objective
435 indices[e] = i++;
436 }
437
438
439 CoinPackedVector* optConstraint = new CoinPackedVector;
440 for (edge e : m_G->edges) {
441 optConstraint->insert(indices[e], 1);
442 }
443 // sense: 'E' == 'G' >= 'L' <=
444 m_osi->addRow(*optConstraint, 'L', 0, 0); // constraint, sense, rhs (will be set below), range
445 m_constraints.push_back(optConstraint);
446
447 // Set the initial value of the OPT constraint rhs to n-1
448 setOpt(m_G->numberOfNodes() - 1);
449
450 int amountSolverCalls = 0;
451 while (true) {
452 if (amountSolverCalls == 0) {
453 m_osi->initialSolve();
454 } else {
455 m_osi->resolve();
456 }
457 amountSolverCalls++;
458
460
461 logger.lout() << amountSolverCalls << ". solve... ";
462 if (m_osi->isProvenOptimal()) {
463 logger.lout() << "-> optimal (" << m_osi->getObjValue() << ")" << std::endl;
464 logger.lout() << " separation... ";
465 SeparationResult result = separation(m_osi->getColSolution(), indices);
466
467 if (result == SeparationResult::Solution) {
468 for (edge e : m_G->edges) {
469 if (m_E2[e] && !m_E1[e]) {
470 m_spanner->newEdge(e);
472 }
473 }
474 logger.lout() << "-> Found solution\n" << std::endl;
475 logger.lout() << "solver calls: " << amountSolverCalls << std::endl;
476 logger.lout() << "cols: " << m_osi->getNumCols() << std::endl;
477 logger.lout() << "rows: " << m_osi->getNumRows() << std::endl;
478 return true;
479 } else if (result == SeparationResult::NewConstraint) {
480 logger.lout() << "-> New constraint" << std::endl;
481 } else { // Fail
482 logger.lout() << "-> Failed" << std::endl;
483 if (!setOpt(m_OPT + 1)) {
484 return false;
485 }
486 }
487 } else if (m_osi->isProvenPrimalInfeasible()) {
488 logger.lout() << "-> Infeasible" << std::endl;
489 if (!setOpt(m_OPT + 1)) {
490 return false;
491 }
492 } else if (m_osi->isProvenDualInfeasible()) {
493 logger.lout() << "-> Unbounded" << std::endl;
494 return false;
495 } else {
496 logger.lout() << "-> No solution found" << std::endl;
497 return false;
498 }
499 };
500 }
501
507 bool setOpt(int opt) {
508 m_OPT = opt;
509 if (m_OPT > m_nSquared) {
510 logger.lout() << " OPT is too large. Abort." << std::endl;
511 return false;
512 }
513 float percentage = static_cast<float>(m_OPT) / m_nSquared * 100.0;
514 logger.lout() << " Set OPT to " << m_OPT << " (" << std::fixed << std::setprecision(2)
515 << percentage << "% of " << m_nSquared << ")" << std::endl;
516
517 m_osi->setRowBounds(0, 0.0,
518 m_OPT); // this is the opt constraint; it is 0 <= sum(x_e) <= m_OPT
519 return true;
520 }
521
522 SeparationResult separation(const double* solution, const EdgeArray<int>& indices) {
523 EdgeArray<bool> out(*m_G, false);
524 randomizedSelection(solution, out);
525
526 GraphCopySimple copy;
527 copy.setOriginalGraph(*m_G);
528 for (node n : m_G->nodes) {
529 copy.newNode(n);
530 }
531 EdgeArray<TWeight> copyWeight(copy);
532 int amountCoveredEdges = 0;
533 for (edge e : m_G->edges) {
534 if (out[e]) {
535 amountCoveredEdges++;
536 copy.newEdge(e);
537 copyWeight[copy.copy(e)] = m_weight[e];
538 }
539 }
540
541 bool settlesAllThinEdges = true;
542 edge unsettledThinEdge = nullptr;
543 for (edge e : m_G->edges) {
544 if (isThinEdge(e) && !isSettledEdge(e, copy, copyWeight)) {
545 settlesAllThinEdges = false;
546 unsettledThinEdge = e;
547 break;
548 }
549 }
550
551 if (settlesAllThinEdges) {
552 if (amountCoveredEdges <= ceil(2.0 * m_OPT * m_sqrtlog)) {
553 m_E2 = out;
555 } else {
557 }
558 } else {
559 EdgeArray<bool> antispanner(*m_G, false);
560 createAntispanner(unsettledThinEdge, out, antispanner);
561
562 double rowValue = 0.0;
563 int i = 0;
564 for (edge e : m_G->edges) {
565 if (antispanner[e]) {
566 rowValue += solution[i];
567 }
568 i++;
569 }
570 if (m_eps.less(rowValue, 1.0)) {
571 // create new constraint
572 CoinPackedVector* c = new CoinPackedVector;
573 for (edge e : m_G->edges) {
574 if (antispanner[e]) {
575 c->insert(indices[e], 1);
576 }
577 }
578 m_osi->addRow(*c, 'G', 1, 0);
579 m_constraints.push_back(c);
581 } else {
583 }
584 }
585 }
586
587 void randomizedSelection(const double* fractions, EdgeArray<bool>& out) {
588 int i = 0;
589 for (edge e : m_G->edges) {
590 double p = m_sqrtlog * fractions[i++]; // P may not be limited to 1: if it is >1 the
591 // result will always be true, which is ok.
592 out[e] = randomDouble(0.0, 1.0) <= p;
593 }
594 }
595
596 void createAntispanner(const edge unsettledThinEdge, const EdgeArray<bool>& out,
597 EdgeArray<bool>& antispanner) {
598 GraphCopySimple copy;
599 copy.setOriginalGraph(*m_G);
600 for (node n : m_G->nodes) {
601 copy.newNode(n);
602 }
603 EdgeArray<TWeight> copyWeight(copy);
604
605 const TWeight maxDistance = m_stretch * m_weight[unsettledThinEdge];
606 for (edge e : m_G->edges) {
607 // s->e->t
608 TWeight s_e = m_outDistance[unsettledThinEdge->source()][e->source()];
609 TWeight e_t = m_inDistance[unsettledThinEdge->target()][e->target()];
610 bool isInLocalSubgraph = (s_e != std::numeric_limits<TWeight>::max()
611 && e_t != std::numeric_limits<TWeight>::max()
612 && m_eps.leq(s_e + m_weight[e] + e_t, maxDistance));
613
614 // Graph to maximize is E_st\‍(E_st\E2) = E_st intersection E2
615 // intersection[e] = isInLocalSubgraph && out[e];
616 if (isInLocalSubgraph && out[e]) {
617 copy.newEdge(e);
618 copyWeight[copy.copy(e)] = m_weight[e];
619 }
620
621 // e in antispanner <=> e in E_st and not in E2 (=out)
622 antispanner[e] = isInLocalSubgraph && !out[e];
623 }
625
626 // minimize antispanner
627 bool changed;
628 do {
629 changed = false;
630 for (edge e : m_G->edges) {
631 if (!antispanner[e]) {
632 continue;
633 }
634
635 // try to remove e and check, if still no path <=stretch*max exists
636 // -> removing e from antispanner is equivalent to adding e to the graph
637 copy.newEdge(e);
638 copyWeight[copy.copy(e)] = m_weight[e];
639 antispanner[e] = false;
640
641 if (!isSettledEdge(unsettledThinEdge, copy, copyWeight)) {
642 // we've found an unnecessary edge.
643 changed = true;
644 } else {
645 antispanner[e] = true;
646 copy.delEdge(copy.copy(e));
647 }
648 }
649
651 } while (changed);
652 }
653
654 using SpannerModule<TWeight>::getWeight;
655 using SpannerModule<TWeight>::assertTimeLeft;
656 using SpannerModule<TWeight>::m_GA;
657 using SpannerModule<TWeight>::m_stretch;
658 using SpannerModule<TWeight>::m_spanner;
659 using SpannerModule<TWeight>::m_inSpanner;
660};
661
662template<typename TWeight>
664 Logger::Level::High); // do not log by default
665
666}
Compare floating point numbers with epsilons and integral numbers with normal compare operators.
Includes declaration of graph class.
Declaration of class GraphAttributes which extends a Graph by additional attributes.
Declaration of graph copy classes.
Decralation of GraphElement and GraphList classes.
Contains logging functionality.
Basic module for spanner algorithms.
Basic declarations, included by all source files.
static OsiSolverInterface * createCorrectOsiSolverInterface()
Get a new solver and set its initial log level according to the level of CoinLog.
Definition coin.h:55
static constexpr LPSolver whichLPSolver()
Returns the LP-solver used by OGDF.
Definition config.h:421
Dijkstra's single source shortest path algorithm.
Definition Dijkstra.h:60
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:77
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:145
Class for the representation of edges.
Definition Graph_d.h:364
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
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:81
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:55
Stores additional attributes of a graph (like layout information).
bool directed() const
Returns if the graph is directed.
const Graph & constGraph() const
Returns a reference to the associated graph.
node newNode(node vOrig)
Creates a new node in the graph copy with original node vOrig.
Definition GraphCopy.h:191
Copies of graphs with mapping between nodes and edges.
Definition GraphCopy.h:260
edge newEdge(edge eOrig)
Creates a new edge in the graph copy with original edge eOrig.
Definition GraphCopy.h:320
void setOriginalGraph(const Graph *G) override
Re-initializes the copy using G (which might be null), but does not create any nodes or edges.
void delEdge(edge e) override
Removes edge e.
edge copy(edge e) const override
Returns the edge in the graph copy corresponding to e.
Definition GraphCopy.h:294
Data type for general directed graphs (adjacency list representation).
Definition Graph_d.h:866
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: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
internal::GraphObjectContainer< EdgeElement > edges
The container containing all edge objects.
Definition Graph_d.h:932
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
@ Log
the object is in logging mode, using its own localLogLevel
ReturnType
The return type of a module.
Definition Module.h:52
Class for the representation of nodes.
Definition Graph_d.h:241
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
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 ogdf::CoinManager.
Implementation of Dijkstra's single source shortest path algorithm.
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:52
double randomDouble(double low, double high)
Returns a random double value from the interval [low, high).
The namespace for all OGDF objects.
Declaration of simple graph algorithms.