dyng
DynamicGraphLayout
graph.h
1 /*
2  Copyright 2020 František Bráblík
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16 #pragma once
17 
18 #include "node.h"
19 #include "edge.h"
20 #include "partitions.h"
21 #include "exceptions.h"
22 
23 #include <unordered_map>
24 #include <vector>
25 #include <utility> // std::move, std::forward
26 
27 namespace dyng {
28 
30 
39 template<typename NodeType, typename EdgeType>
40 class graph;
41 
42 
44 
51 
52 
53 namespace detail {
54 
59 using graph_partitioning = graph<node_partition, edge_partition>;
60 
61 } // namespace detail
62 
63 
64 template<typename NodeType, typename EdgeType>
65 class graph {
66 public:
67  graph() = default;
68 
69  // need to revalidate edge pointers when copying
70  graph(const graph<NodeType, EdgeType>& other) { *this = other; }
71  graph<NodeType, EdgeType>& operator=(const graph<NodeType, EdgeType>& other) {
72  m_nodes = other.m_nodes;
73  m_edges = other.m_edges;
74  m_index = other.m_index;
75  revalidate_edge_ptrs();
76  return *this;
77  }
78 
79  // also need to revalidate edge pointers when moving
80  graph(graph<NodeType, EdgeType>&& other) { *this = std::move(other); }
81  graph<NodeType, EdgeType>& operator=(graph<NodeType, EdgeType>&& other) {
82  m_nodes = std::move(other.m_nodes);
83  m_edges = std::move(other.m_edges);
84  m_index = std::move(other.m_index);
85  revalidate_edge_ptrs();
86  return *this;
87  }
88 
89  // defined to comply with the rule of five
90  ~graph() = default;
91 
93  using node_edges = std::unordered_map<node_id, edge_id>;
94 
96  const std::vector<NodeType>& nodes() const { return m_nodes.vec; }
97 
99 
109  std::vector<NodeType>& nodes() { return m_nodes.vec; }
110 
112  const std::vector<EdgeType>& edges() const { return m_edges.vec; }
113 
115 
125  std::vector<EdgeType>& edges() { return m_edges.vec; }
126 
128 
131  const NodeType& node_at(node_id id) const { return m_nodes.at(id); }
132 
134 
137  NodeType& node_at(node_id id) { return m_nodes.at(id); }
138 
140 
143  const EdgeType& edge_at(edge_id id) const { return m_edges.at(id); }
144 
146 
149  EdgeType& edge_at(edge_id id) { return m_edges.at(id); }
150 
152 
155  unsigned node_index(node_id id) const { return m_nodes.map.at(id); }
156 
158 
161  unsigned edge_index(edge_id id) const { return m_edges.map.at(id); }
162 
170  template<typename Function>
171  void remove_edges_if(Function function) {
172  // remove edges from all entries
173  auto side_effect = [&function, this](const EdgeType& edge){
174  bool result = function(edge);
175  if (result) {
176  for (auto& entry : m_index) {
177  entry.second.erase(edge.one_id());
178  entry.second.erase(edge.two_id());
179  }
180  }
181  return result;
182  };
183  remove_elements_if(m_edges, side_effect);
184  }
185 
193  template<typename Function>
194  void remove_nodes_if(Function function) {
195  // remove necessary entries from index
196  auto side_effect = [&function, this](const NodeType& node){
197  bool result = function(node);
198  if (result) {
199  // remove all edges connected to this node
200  remove_edges_if([id = node.id()](const EdgeType& edge){
201  return edge.one_id() == id || edge.two_id() == id;
202  });
203  // remove index entry for this node
204  m_index.erase(node.id());
205  }
206  return result;
207  };
208  remove_elements_if(m_nodes, side_effect);
209  }
210 
212 
217  void remove_edge(edge_id id) {
218  if (!edge_exists(id)) {
219  throw invalid_graph("edge not available");
220  }
221  remove_edges_if([id](const EdgeType& e){ return e.id() == id; });
222  }
223 
225 
230  void remove_node(node_id id) {
231  if (!node_exists(id)) {
232  throw invalid_graph("edge not available");
233  }
234  remove_nodes_if([id](const NodeType& n){ return n.id() == id; });
235  }
236 
238  NodeType& push_node(NodeType node) {
239  auto found = m_nodes.map.find(node.id());
240  if (found != m_nodes.map.end()) {
241  return m_nodes.vec[found->second];
242  }
243  m_nodes.map.emplace(node.id(), m_nodes.vec.size());
244  m_index.emplace(node.id(), node_edges());
245  m_nodes.vec.push_back(std::move(node));
246  return m_nodes.vec.back();
247  }
248 
250  template<typename ... Args>
251  NodeType& emplace_node(Args&& ... args) {
252  return push_node(NodeType(std::forward<Args>(args)...));
253  }
254 
256  EdgeType& push_edge(EdgeType edge) {
257  auto found = m_edges.map.find(edge.id());
258  if (found != m_edges.map.end()) {
259  return m_edges.vec[found->second];
260  }
261  try {
262  m_index.at(edge.one_id()).emplace(edge.two_id(), edge.id());
263  m_index.at(edge.two_id()).emplace(edge.one_id(), edge.id());
264  } catch (std::out_of_range&) {
265  throw invalid_graph("node not available");
266  }
267  edge.set_ptr(&m_nodes);
268  m_edges.map.emplace(edge.id(), m_edges.vec.size());
269  m_edges.vec.push_back(std::move(edge));
270  return m_edges.vec.back();
271  }
272 
274  template<typename ... Args>
275  EdgeType& emplace_edge(Args&& ... args) {
276  return push_edge(EdgeType(std::forward<Args>(args)...));
277  }
278 
280  void clear_edges() {
281  m_edges.vec.clear();
282  m_edges.map.clear();
283  for (auto& entry : m_index) {
284  entry.second.clear();
285  }
286  }
287 
289  void clear_nodes() {
290  m_nodes.vec.clear();
291  m_nodes.map.clear();
292  m_index.clear();
293  }
294 
296  bool node_exists(node_id id) const { return m_nodes.map.count(id) > 0; }
297 
299 
302  bool edge_exists(edge_id id) const { return m_edges.map.count(id) > 0; }
303 
312  bool edge_exists(node_id one, node_id two) const {
313  return edges_at_node(one).count(node_at(two).id());
314  }
315 
317 
325  return m_index.at(node_at(node).id());
326  }
327 
328 private:
331  std::unordered_map<node_id, std::unordered_map<node_id, edge_id>> m_index;
332 
333  void revalidate_edge_ptrs() {
334  for (auto& e : m_edges.vec) {
335  e.set_ptr(&m_nodes);
336  }
337  }
338 
339  template<typename Type, typename TypeId, typename Function>
340  void remove_elements_if(
341  detail::container<Type, TypeId>& elem
342  , Function function) {
343  // erase the elements
344  elem.vec.erase(std::remove_if(elem.vec.begin(), elem.vec.end(),
345  [&function](const auto& n){ return function(n); }), elem.vec.end());
346  // reestablish the map
347  elem.map.clear();
348  for (unsigned i = 0; i < elem.vec.size(); ++i) {
349  elem.map[elem.vec[i].id()] = i;
350  }
351  }
352 };
353 
354 } // namespace dyng
dyng::graph::node_at
NodeType & node_at(node_id id)
Returns reference to node of given id.
Definition: graph.h:137
dyng::graph::edge_exists
bool edge_exists(node_id one, node_id two) const
Definition: graph.h:312
exceptions.h
dyng::graph::node_at
const NodeType & node_at(node_id id) const
Returns reference to node of given id.
Definition: graph.h:131
dyng::edge_id
Type used as an identifier for edges.
Definition: identifiers.h:87
dyng::basic_edge::one_id
node_id one_id() const
Returns the id of one connected node.
Definition: edge.h:50
dyng::graph::edge_index
unsigned edge_index(edge_id id) const
Returns the index of an edge corresponding to its position in edges().
Definition: graph.h:161
dyng::graph::clear_edges
void clear_edges()
Removes all edges.
Definition: graph.h:280
dyng::graph::push_node
NodeType & push_node(NodeType node)
Adds a new node to the graph.
Definition: graph.h:238
dyng::graph::remove_edge
void remove_edge(edge_id id)
Removes a single edge.
Definition: graph.h:217
dyng::graph::edges
std::vector< EdgeType > & edges()
Returns reference to the vector of all edges in the graph.
Definition: graph.h:125
dyng::graph::remove_edges_if
void remove_edges_if(Function function)
Definition: graph.h:171
dyng::basic_edge::set_ptr
void set_ptr(detail::container< ConnectedNode, node_id > *cont)
Sets the pointer to the object detail::container.
Definition: edge.h:103
dyng::graph::nodes
const std::vector< NodeType > & nodes() const
Returns const reference to the vector of all nodes in the graph.
Definition: graph.h:96
dyng::detail::container
Contains a vector of graph entities and a map to them.
Definition: container.h:34
dyng::graph::emplace_node
NodeType & emplace_node(Args &&... args)
Constructs a new node and adds it to the graph.
Definition: graph.h:251
dyng::graph::push_edge
EdgeType & push_edge(EdgeType edge)
Adds a new edge to the graph.
Definition: graph.h:256
dyng::graph::remove_node
void remove_node(node_id id)
Removes a single node.
Definition: graph.h:230
dyng::graph::node_exists
bool node_exists(node_id id) const
Returns whether a node of a given id exists.
Definition: graph.h:296
dyng::node
Definition: node.h:32
dyng::edge
Definition: edge.h:119
dyng::graph::edge_at
const EdgeType & edge_at(edge_id id) const
Returns reference to edge of given id.
Definition: graph.h:143
dyng::graph::edge_exists
bool edge_exists(edge_id id) const
Returns whether an edge of a given id exists.
Definition: graph.h:302
dyng::graph::nodes
std::vector< NodeType > & nodes()
Returns reference to the vector of all nodes in the graph.
Definition: graph.h:109
dyng::graph::node_index
unsigned node_index(node_id id) const
Returns the index of a node corresponding to its position in nodes().
Definition: graph.h:155
dyng::graph::edges_at_node
const node_edges & edges_at_node(node_id node) const
Returns a container containing all edges adjacent to a given node.
Definition: graph.h:324
dyng::graph::emplace_edge
EdgeType & emplace_edge(Args &&... args)
Constructs a new edge and adds it to the graph.
Definition: graph.h:275
dyng::node::id
node_id id() const
Returns the id.
Definition: node.h:39
dyng::graph< node_partition, edge_partition >::node_edges
std::unordered_map< node_id, edge_id > node_edges
Typedef to simplify return type of the method edges_at_node().
Definition: graph.h:93
dyng::basic_edge::two_id
node_id two_id() const
Returns the id of the other connected node.
Definition: edge.h:53
dyng::graph::edge_at
EdgeType & edge_at(edge_id id)
Returns reference to edge of given id.
Definition: graph.h:149
dyng::invalid_graph
Definition: exceptions.h:40
dyng::node_id
Type used as an identifier for nodes.
Definition: identifiers.h:77
dyng::graph::edges
const std::vector< EdgeType > & edges() const
Returns const reference to the vector of all edges in the graph.
Definition: graph.h:112
dyng::graph::remove_nodes_if
void remove_nodes_if(Function function)
Definition: graph.h:194
dyng::graph
Templated class for representing a static graph and its layout.
Definition: graph.h:40
dyng::graph::clear_nodes
void clear_nodes()
Removes all nodes.
Definition: graph.h:289