cgrapht 1.0.0
A modern C++20 header-only graph library
Loading...
Searching...
No Matches
graph.hpp
Go to the documentation of this file.
1
17#pragma once
18
19#include <unordered_set>
20#include <functional>
21#include <ranges>
22#include <unordered_map>
23
24#include "cgrapht/commons.hpp"
25#include "models.hpp"
26
27namespace cgrapht {
33 template <Hashable E>
34 struct Edge {
35 std::size_t from_id;
36 std::size_t to_id;
38
39 bool operator==(const Edge& other) const {
40 return from_id == other.from_id && edge == other.edge && to_id == other.to_id;
41 }
42 };
43
45
46 struct EdgeSet {
47 std::unordered_set<std::size_t> incoming_edges;
48 std::unordered_set<std::size_t> outgoing_edges;
49 };
50
52
53
62 template <Hashable V, Hashable E>
64 private:
65 std::unordered_map<std::size_t, V> vertex_index{};
66 std::unordered_map<std::size_t, Edge<E>> edge_index{};
67 std::unordered_map<std::size_t, EdgeSet> adjacency_list{};
68
69 public:
81 Result<std::size_t, ErrorType> delete_vertex(std::size_t vertex_id);
89 Result<std::size_t, ErrorType> add_edge(std::size_t from_id, std::size_t to_id, const E& e);
101 Result<V, ErrorType> get_vertex(std::size_t id) const;
107 Result<Edge<E>, ErrorType> get_edge(std::size_t id) const;
119 Result<std::unordered_set<std::size_t>, ErrorType> get_parents(std::size_t vertex_id) const;
138
143 std::ranges::forward_range auto get_vertices() const & {
144 return vertex_index | std::views::values;
145 }
146
151 std::ranges::forward_range auto get_edges() const & {
152 return edge_index | std::views::values;
153 }
154 };
155
156 template <Hashable V, Hashable E> Result<std::size_t, ErrorType> DirectedGraph<V, E>::add_vertex(const V& v) {
157 std::size_t vertex_id {std::hash<V>{}(v)};
158 if (!vertex_index.contains(vertex_id)) {
159 vertex_index.emplace(vertex_id, v);
160 adjacency_list.emplace(vertex_id, EdgeSet{});
161 }
163 }
164
165 template <Hashable V, Hashable E> Result<std::size_t, ErrorType> DirectedGraph<V, E>::delete_vertex(std::size_t vertex_id) {
166 if (!vertex_index.contains(vertex_id)) {
168 }
169 if (adjacency_list.contains(vertex_id) && adjacency_list[vertex_id].incoming_edges.empty() && adjacency_list[vertex_id].outgoing_edges.empty()) {
170 adjacency_list.erase(vertex_id);
171 vertex_index.erase(vertex_id);
173 }
175 }
176
177 template <Hashable V, Hashable E> Result<std::size_t, ErrorType> DirectedGraph<V, E>::add_edge(std::size_t from_id, std::size_t to_id, const E& e) {
178 if (!vertex_index.contains(from_id) || !vertex_index.contains(to_id)) {
180 }
181
182 if (std::size_t edge_id {std::hash<E>{}(e)}; edge_index.contains(edge_id)) {
183 if (Edge edge_info = edge_index.at(edge_id); edge_info.from_id != from_id || edge_info.to_id != to_id) {
185 }
187 } else {
188 edge_index.emplace(edge_id, Edge<E>{from_id, to_id, e});
189 adjacency_list[from_id].outgoing_edges.insert(edge_id);
190 adjacency_list[to_id].incoming_edges.insert(edge_id);
192 }
193 }
194
195 template <Hashable V, Hashable E> Result<std::size_t, ErrorType> DirectedGraph<V, E>::delete_edge(std::size_t edge_id) {
196 if (auto it = edge_index.find(edge_id); it != edge_index.end()) {
197 const auto& [from_id, to_id, _] = it->second;
198 edge_index.erase(it);
199 adjacency_list[from_id].outgoing_edges.erase(edge_id);
200 adjacency_list[to_id].incoming_edges.erase(edge_id);
202 }
204 }
205
206 template <Hashable V, Hashable E> Result<V, ErrorType> DirectedGraph<V, E>::get_vertex(std::size_t id) const {
207 if (vertex_index.contains(id)) {
208 return Result<V, ErrorType>::success(vertex_index.at(id));
209 }
211 }
212
213 template <Hashable V, Hashable E> Result<Edge<E>, ErrorType> DirectedGraph<V, E>::get_edge(std::size_t id) const {
214 if (edge_index.contains(id)) {
215 return Result<Edge<E>, ErrorType>::success(edge_index.at(id));
216 }
218 }
219
220 template <Hashable V, Hashable E> Result<std::unordered_set<std::size_t>, ErrorType> DirectedGraph<V, E>::get_children(std::size_t vertex_id) const {
221 if (!adjacency_list.contains(vertex_id)) {
223 }
224 auto children = adjacency_list.at(vertex_id).outgoing_edges
225 | std::views::transform([this](const auto& edge_id) {
226 return edge_index.at(edge_id).to_id;
227 });
228 std::unordered_set<std::size_t> children_set(children.begin(), children.end());
229 return Result<std::unordered_set<std::size_t>, ErrorType>::success(std::move(children_set));
230 }
231
232 template <Hashable V, Hashable E> Result<std::unordered_set<std::size_t>, ErrorType> DirectedGraph<V, E>::get_parents(std::size_t vertex_id) const {
233 if (!adjacency_list.contains(vertex_id)) {
235 }
236 auto parents = adjacency_list.at(vertex_id).incoming_edges
237 | std::views::transform([this](const auto& edge_id) {
238 return edge_index.at(edge_id).from_id;
239 });
240 std::unordered_set<std::size_t> parent_set(parents.begin(), parents.end());
241 return Result<std::unordered_set<std::size_t>, ErrorType>::success(std::move(parent_set));
242 }
243
244 template <Hashable V, Hashable E> Result<std::unordered_set<std::size_t>, ErrorType> DirectedGraph<V, E>::get_neighbours(std::size_t vertex_id) const {
245 if (!adjacency_list.contains(vertex_id)) {
247 }
248
249 auto children = adjacency_list.at(vertex_id).outgoing_edges
250 | std::views::transform([this](const auto& edge_id) {
251 return edge_index.at(edge_id).to_id;
252 });
253
254 auto parents = adjacency_list.at(vertex_id).incoming_edges
255 | std::views::transform([this](const auto& edge_id) {
256 return edge_index.at(edge_id).from_id;
257 });
258
259 std::unordered_set<std::size_t> neighbours {children.begin(), children.end()};
260 neighbours.insert(parents.begin(), parents.end());
261
262 return Result<std::unordered_set<std::size_t>, ErrorType>::success(std::move(neighbours));
263 }
264
265 template <Hashable V, Hashable E> Result<std::unordered_set<std::size_t>, ErrorType> DirectedGraph<V, E>::get_outgoing_edges(std::size_t vertex_id) const {
266 if (!adjacency_list.contains(vertex_id)) {
268 }
269 auto children {adjacency_list.at(vertex_id).outgoing_edges};
270 return Result<std::unordered_set<std::size_t>, ErrorType>::success(std::move(children));
271 }
272
273 template <Hashable V, Hashable E> Result<std::unordered_set<std::size_t>, ErrorType> DirectedGraph<V, E>::get_incoming_edges(std::size_t vertex_id) const {
274 if (!adjacency_list.contains(vertex_id)) {
276 }
277 auto children {adjacency_list.at(vertex_id).incoming_edges};
278 return Result<std::unordered_set<std::size_t>, ErrorType>::success(std::move(children));
279 }
280}
Directed graph with hashed vertex and edge ids.
Definition graph.hpp:63
Result< std::unordered_set< std::size_t >, ErrorType > get_parents(std::size_t vertex_id) const
Get adjacent parents (incoming neighbors).
Definition graph.hpp:232
std::ranges::forward_range auto get_vertices() const &
View of all vertex payloads.
Definition graph.hpp:143
Result< std::unordered_set< std::size_t >, ErrorType > get_outgoing_edges(std::size_t vertex_id) const
Get outgoing edge ids for a vertex.
Definition graph.hpp:265
Result< V, ErrorType > get_vertex(std::size_t id) const
Fetch a vertex payload by id.
Definition graph.hpp:206
Result< std::size_t, ErrorType > delete_vertex(std::size_t vertex_id)
Delete a vertex if it has no incident edges.
Definition graph.hpp:165
Result< Edge< E >, ErrorType > get_edge(std::size_t id) const
Fetch an edge record by id.
Definition graph.hpp:213
Result< std::size_t, ErrorType > add_vertex(const V &v)
Add a vertex to the graph.
Definition graph.hpp:156
Result< std::size_t, ErrorType > delete_edge(std::size_t edge_id)
Delete an edge by id.
Definition graph.hpp:195
Result< std::size_t, ErrorType > add_edge(std::size_t from_id, std::size_t to_id, const E &e)
Add a directed edge between two vertices.
Definition graph.hpp:177
Result< std::unordered_set< std::size_t >, ErrorType > get_incoming_edges(std::size_t vertex_id) const
Get incoming edge ids for a vertex.
Definition graph.hpp:273
std::ranges::forward_range auto get_edges() const &
View of all edge records.
Definition graph.hpp:151
Result< std::unordered_set< std::size_t >, ErrorType > get_neighbours(std::size_t vertex_id) const
Get all adjacent neighbors (incoming or outgoing).
Definition graph.hpp:244
Result< std::unordered_set< std::size_t >, ErrorType > get_children(std::size_t vertex_id) const
Get adjacent children (outgoing neighbors).
Definition graph.hpp:220
Result wrapper for success or error values.
Definition models.hpp:18
static Result error(const ERROR &e)
Construct an error result from a const reference.
Definition models.hpp:52
static Result success(const SUCCESS &s)
Construct a success result from a const reference.
Definition models.hpp:30
Common concepts and utilities.
ErrorType
Error codes returned by graph operations.
Definition models.hpp:147
@ VERTEX_NOT_FREE
The vertex cannot be removed because it still has incident edges.
@ ABSENT_VERTEX
The referenced vertex ID does not exist in the graph.
@ EDGE_ALREADY_EXISTS
An edge between the two vertices already exists.
@ ABSENT_EDGE
The referenced edge ID does not exist in the graph.
Core result and error types.
Edge record for a directed graph.
Definition graph.hpp:34
std::size_t from_id
Definition graph.hpp:35
std::size_t to_id
Definition graph.hpp:36
bool operator==(const Edge &other) const
Definition graph.hpp:39