Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
SpannerElkinNeiman.h
Go to the documentation of this file.
1
33#pragma once
34
36#include <ogdf/basic/Graph.h>
39#include <ogdf/basic/Math.h>
40#include <ogdf/basic/Queue.h>
41#include <ogdf/basic/basic.h>
45
46#include <cmath>
47#include <limits>
48#include <string>
49
50namespace ogdf {
51class GraphCopySimple;
52
105template<typename TWeight>
106class SpannerElkinNeiman : public SpannerModule<TWeight> {
108 const Graph* m_G;
109
111
112 double m_c;
113 double m_beta;
114 int m_k;
116
125 const double DEFAULT_EPSILON = 0.8;
126
127public:
129
130 SpannerElkinNeiman(double epsilon) : SpannerModule<TWeight>() { setEpsilon(epsilon); }
131
137 void setEpsilon(double epsilon) {
138 OGDF_ASSERT(epsilon > 0);
139 m_c = 3.0 / epsilon; // see corollary 8.
140 }
141
143 virtual bool preconditionsOk(const GraphAttributes& GA, double stretch,
144 std::string& error) override {
145 if (GA.directed()) {
146 error = "The graph must be undirected";
147 return false;
148 }
149 double integralPart;
150 if (std::modf(stretch, &integralPart) != 0.0) {
151 error = "The stretch is required to be an integer, not " + to_string(stretch);
152 return false;
153 }
154 int intStretch = static_cast<int>(stretch);
155 if (intStretch < 1) {
156 error = "The stretch must be >= 1.0";
157 return false;
158 }
159 if (intStretch % 2 == 0) {
160 error = "The stretch must be odd";
161 return false;
162 }
164 error = "The graph must be unweighted";
165 return false;
166 }
167 return true;
168 }
169
170private:
172 virtual void init(const GraphAttributes& GA, double stretch, GraphCopySimple& spanner,
173 EdgeArray<bool>& inSpanner) override {
174 SpannerModule<TWeight>::init(GA, stretch, spanner, inSpanner);
175 m_intStretch = static_cast<int>(stretch);
176 m_k = (m_intStretch + 1) / 2;
177 m_G = &GA.constGraph();
178 m_beta = log(m_c * m_G->numberOfNodes()) / m_k; // log is logarithm naturale
179 }
180
181 struct BfsNode {
183 int depth;
185
186 BfsNode(node _n, double _depth, edge _p) : n(_n), depth(_depth), p(_p) { }
187 };
188
190 virtual typename SpannerModule<TWeight>::ReturnType execute() override {
192 // First, assign each node the random variable. This is done here, so the first
193 // feasibility check can be done fast (This is the one that mostly fails).
194 for (node u : m_G->nodes) {
196 // Feasibility check 1: See proof of Theorem 1 and "Standard Centralized Model" in 2.1.1
197 if (m_eps.geq(r[u], static_cast<double>(m_k))) {
199 }
200 }
201
202 // Paper: u broadcasts a message x within distance k
203 // Here: Fix a receiving node x and find all nodes u within distance k, that send messages to x
204 for (node x : m_G->nodes) {
205 // values[u]: x recieves the value from u (m_u(x))
206 NodeArray<double> values(*m_G, std::numeric_limits<double>::lowest());
207
208 // firstEdge[u]: The first edge from the path x->u (p_u(x))
209 // This is the last edge from the path u->x as described in the paper
210 NodeArray<edge> firstEdge(*m_G, nullptr);
211
212 QueuePure<BfsNode> queue;
213 NodeArray<bool> marked(*m_G, false); // Just visit a node once.
214
215 queue.emplace(x, 0, nullptr); // p==nullptr: we do not have a first edge
216 marked[x] = true; // Do not revisit x
217 while (!queue.empty()) {
218 BfsNode bfsNode = queue.pop();
219 int distance = bfsNode.depth + 1;
220
221 for (adjEntry adj : bfsNode.n->adjEntries) {
222 node u = adj->twinNode();
223 if (marked[u]) {
224 continue;
225 }
226
227 edge p = bfsNode.p;
228 if (p == nullptr) {
229 p = adj->theEdge(); // Set the first edge: Only happens in the first expansion step of the bfs
230 }
231
232 values[u] = r[u] - distance;
233 firstEdge[u] = p;
234
235 if (distance < m_k) { // distance is <= m_k when a dfs node is popped from the stack.
236 queue.emplace(u, distance, p);
237 marked[u] = true;
238 }
239 }
240 }
241
243
244 // find edges that must be added to the spanner:
245 // m(x) = max{m_u(x) | u in V}
246 double maxValue = std::numeric_limits<double>::lowest();
247 for (node u : m_G->nodes) {
248 Math::updateMax(maxValue, values[u]);
249 }
250 maxValue -= 1.0; // m(x)-1
251
253
254 // Add the edges (sets C(x) in the paper) to the spanner.
255 for (node u : m_G->nodes) {
256 if (values[u] != std::numeric_limits<double>::lowest()
257 && m_eps.geq(values[u], maxValue)) {
258 edge e = firstEdge[u];
259 if (!(*m_inSpanner)[e]) {
260 m_spanner->newEdge(e);
261 (*m_inSpanner)[e] = true;
262 }
263 }
264 }
265
267 }
268
269 // Feasibility check 2: See proof of Theorem 1 and "Standard Centralized Model" in 2.1.1
270 // Note: The paper states, that the spanner must have >= n-1 edges. This does not work, for
271 // all graphs, e.g. a forest as an input. The paper never states, that the graph must be connected
272 // nor that it can be disconnected. Using n-<amount components> does work and solves the issue above.
273 int components = connectedComponents(*m_G);
274 if (m_spanner->numberOfEdges() < m_G->numberOfNodes() - components) {
276 }
277
279 }
280
281 using SpannerModule<TWeight>::assertTimeLeft;
282 using SpannerModule<TWeight>::m_GA;
283 using SpannerModule<TWeight>::m_stretch;
284 using SpannerModule<TWeight>::m_spanner;
285 using SpannerModule<TWeight>::m_inSpanner;
286};
287
294template<typename TWeight>
296public:
299};
300
301}
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.
Decralation of GraphElement and GraphList classes.
A wrapper class for iterating calls to spanner algorithms.
Basic module for spanner algorithms.
Mathematical Helpers.
Declaration and implementation of list-based queues (classes QueuePure<E> and Queue<E>).
Basic declarations, included by all source files.
Class for adjacency list elements.
Definition Graph_d.h:143
Class for the representation of edges.
Definition Graph_d.h:364
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.
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.
static const long edgeDoubleWeight
Corresponds to edge attribute doubleWeight(edge).
bool has(long attr) const
Returns true iff all attributes in attr are available.
static const long edgeIntWeight
Corresponds to edge attribute intWeight(edge).
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
Data type for general directed graphs (adjacency list representation).
Definition Graph_d.h:866
int numberOfNodes() const
Returns the number of nodes in the graph.
Definition Graph_d.h:979
int numberOfEdges() const
Returns the number of edges in the graph.
Definition Graph_d.h:982
internal::GraphObjectContainer< NodeElement > nodes
The container containing all node objects.
Definition Graph_d.h:929
ReturnType
The return type of a module.
Definition Module.h:52
Class for the representation of nodes.
Definition Graph_d.h:241
internal::GraphObjectContainer< AdjElement > adjEntries
The container containing all entries in the adjacency list of this node.
Definition Graph_d.h:272
Implementation of list-based queues.
Definition Queue.h:55
iterator emplace(Args &&... args)
Adds a new element at the end of the queue.
Definition Queue.h:178
bool empty() const
Returns true iff the queue is empty.
Definition Queue.h:93
E pop()
Removes front element and returns it.
Definition Queue.h:183
Randomized multiplicative spanner calculation by propagating random messages through the graph.
int m_intStretch
m_stretch, but as an integer
double m_c
the parameter for the exponential distribution.
double m_beta
The parameter for the exponential distribution.
virtual SpannerModule< TWeight >::ReturnType execute() override
Executes the core algorithm.
virtual void init(const GraphAttributes &GA, double stretch, GraphCopySimple &spanner, EdgeArray< bool > &inSpanner) override
Initializes members and create an empty spanner.
int m_k
the parameter k derived from the stretch
virtual bool preconditionsOk(const GraphAttributes &GA, double stretch, std::string &error) override
void setEpsilon(double epsilon)
Sets the epsilon for this algorithm.
const Graph * m_G
The original graph.
const double DEFAULT_EPSILON
The default value for epsilon.
Use the ogdf::SpannerIteratedWrapper to execute the ogdf::SpannerElkinNeiman algorithm up to 200 time...
A implementation-independed wrapper class to execute a spanner algorithm multiple times.
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
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
int connectedComponents(const Graph &G, NodeArray< int > &component, List< node > *isolated=nullptr, ArrayBuffer< node > *reprs=nullptr)
Computes the connected components of G and optionally generates a list of isolated nodes.
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition basic.h:52
double randomDoubleExponential(double beta)
Returns a random double value from the exponential distribution.
void updateMax(T &max, const T &newValue)
Stores the maximum of max and newValue in max.
Definition Math.h:94
The namespace for all OGDF objects.
Declaration of simple graph algorithms.
edge p
The first edge of the path.
BfsNode(node _n, double _depth, edge _p)
node n
The current node to visit all neighbors from.
int depth
The depth of the node n.