Skip to content

Commit ff16472

Browse files
committed
YT-CPPGL-56: Replace the usage of pointers for vertex storage to a simple composite type [Part 2]
- Modified the `edge_descriptor` class to store only the IDs of its incident vertices - Aligned graph implementation to use vertex IDs instead of descriptor objects for internal operations - Aligned algorithms to use vertex-ID-based callbacks
1 parent 8a54dc0 commit ff16472

37 files changed

Lines changed: 481 additions & 752 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ While the `gl::graph` class is the key element of the library, it's not the only
114114
- [The vertex and edge classes - representation of the graph's elements](/docs/graph_elements.md)
115115
- [I/O operations](/docs/io.md)
116116
- [Graph topology generators](/docs/topologies.md)
117-
- [Algorithms](/docs/algoithms.md)
117+
- [Algorithms](/docs/algorithms.md)
118118
- [Core utility types](/docs/core_util_types.md)
119119
- [Additional functionality](/docs/additional_functionality.md)
120120

Lines changed: 46 additions & 52 deletions
Large diffs are not rendered by default.

docs/graph.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,5 +551,5 @@ This section provides links to additional resources and documentation that can h
551551
- [The vertex and edge classes - representation of the graph's elements](/docs/graph_elements.md)
552552
- [I/O operations](/docs/io.md)
553553
- [Graph topology generators](/docs/topologies.md)
554-
- [Algorithms](/docs/algoithms.md)
554+
- [Algorithms](/docs/algorithms.md)
555555
- [Core utility types](/docs/core_util_types.md)

docs/graph_elements.md

Lines changed: 19 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,6 @@ By default, the `edge_descriptor` class does not carry any properties and assume
8787

8888
### Template parameters
8989

90-
- **`VertexType`**: The type of the vertex descriptors connected by this edge.
91-
- *Constraints*: must satisfy the **`type_traits::c_instantiation_of<vertex_descriptor>`** concept
9290
- **`DirectionalTag`**: A tag type indicating whether the edge is directed or undirected.
9391
- *Default value*: `directed_t`
9492
- *Constraints*: must satisfy the **`type_traits::c_edge_directional_tag`** concept (either `directed_t` or `undirected_t`)
@@ -99,15 +97,14 @@ By default, the `edge_descriptor` class does not carry any properties and assume
9997
### Member types
10098

10199
- **`type`**: Alias for the `edge_descriptor` itself.
102-
- **`vertex_type`**: Type of the vertex descriptors connected by this edge.
103100
- **`directional_tag`**: The tag indicating whether the edge is directed or undirected.
104101
- **`properties_type`**: Type of the edge properties as defined by the `Properties` template parameter.
105102

106103
### Constructors
107104

108-
- **`edge_descriptor(const vertex_type& first, const vertex_type& second)`**:
105+
- **`edge_descriptor(const types::id_type first, const types::id_type second)`**:
109106
- Constructs an edge between two vertices.
110-
- **`edge_descriptor(const vertex_type& first, const vertex_type& second, const properties_type& properties)`**:
107+
- **`edge_descriptor(const types::id_type first, const types::id_type second, const properties_type& properties)`**:
111108
- Constructs an edge between two vertices with specified properties.
112109
- *Constraints*: the `properties_type` must be non-default.
113110
- **Move constructor and assignment operator**: *default*
@@ -133,88 +130,48 @@ The destructor is *defaulted*, allowing proper cleanup of the `edge_descriptor`
133130
- **`incident_vertices() const`**:
134131
- *Description*: Returns a pair of references to the two vertices connected by the edge.
135132
- *Returned value*: $(u, v)$
136-
- *Return type*: `types::homogeneous_pair<const vertex_type&>`
137-
138-
- **`incident_vertex_ids() const`**:
139-
- *Description*: Returns the unique IDs of the vertices connected by the edge.
140-
- *Returned value*: $(u_{id}, v_{id})$
141-
- *Return type*: `types::homogeneous_pair<types::id_type>`
133+
- *Return type*: `types::homogeneous_pair<const types::id_type>`
142134

143135
- **`first() const`**:
144136
- *Description*: Returns a reference to the first vertex of the edge.
145137
- *Returned value*: $u$
146-
- *Return type*: `const vertex_type&`
138+
- *Return type*: `types::id_type`
147139

148140
- **`second() const`**:
149141
- *Description*: Returns a reference to the second vertex of the edge.
150142
- *Returned value*: $v$
151-
- *Return type*: `const vertex_type&`
143+
- *Return type*: `const types::id_type`
152144

153-
- **`incident_vertex(vertex) const`**:
145+
- **`incident_vertex(vertex_id) const`**:
154146
- *Description*: Returns the vertex on the other end of the edge relative to the provided vertex. Throws an error if the provided vertex is not incident with the edge.
155147
- *Returned value*:
156-
- $v$ if $\text{vertex} = u$
157-
- $u$ if $\text{vertex} = v$
148+
- $v$ if $\text{vertex-id} = u$
149+
- $u$ if $\text{vertex-id} = v$
158150
- error otherwise
159151
- *Parameters*:
160-
- `vertex: const vertex_type&` – the vertex for which the opposite vertex is requested.
161-
- *Return type*: `const vertex_type&`
162-
163-
- **`incident_vertex(vertex_id) const`**:
164-
- *Description*: Returns the vertex on the other end of the edge relative to the provided vertex ID. Throws an error if the provided vertex ID is invalid.
165-
- *Returned value*:
166-
- $v$ if $\text{vertex-id} = u_{id}$
167-
- $u$ if $\text{vertex-id} = v_{id}$
168-
- error otherwise
169-
- *Parameters*:
170-
- `vertex_id: const types::id_type` – the vertex ID for which the opposite vertex ID is requested.
171-
- *Return type*: `types::id_type`
172-
173-
- **`is_incident_with(vertex) const`**:
174-
- *Description*: Returns `true` if the provided vertex is connected to the edge.
175-
- *Returned value*: $\text{vertex} \in {u, v}$
176-
- *Parameters*:
177-
- `vertex: const vertex_type&` – the vertex to check for incidence with the edge.
178-
- *Return type*: `bool`
152+
- `vertex_id: const types::id_type` – the vertex for which the opposite vertex is requested.
153+
- *Return type*: `const types::id_type`
179154

180155
- **`is_incident_with(vertex_id) const`**:
181156
- *Description*: Returns `true` if a vertex with the given ID is connected to the edge.
182-
- *Returned value*: $\text{vertex-id} \in {u_{id}, v_{id}}$
157+
- *Returned value*: $\text{vertex-id} \in {u, v}$
183158
- *Parameters*:
184159
- `vertex_id: const types::id_type` – the vertex ID to check for incidence with the edge.
185160
- *Return type*: `bool`
186161

187-
- **`is_incident_from(vertex) const`**:
188-
- *Description*: Returns `true` if the provided vertex is the source of the edge (for directed edges).
189-
- *Returned value*:
190-
- For directed edges: $\text{vertex} = u$
191-
- For undirected edges: `is_incident_with(vertex)`
192-
- *Parameters*:
193-
- `vertex: const vertex_type&` – the vertex to check if it is the source.
194-
- *Return type*: `bool`
195-
196162
- **`is_incident_from(vertex_id) const`**:
197163
- *Description*: Returns `true` if a vertex with the given ID is the source of the edge.
198164
- *Returned value*:
199-
- For directed edges: $\text{vertex-id} = u_{id}$
200-
- For undirected edges: `is_incident_with(vertex)`
165+
- For directed edges: $\text{vertex-id} = u$
166+
- For undirected edges: `is_incident_with(vertex_id)`
201167
- *Parameters*:
202168
- `vertex_id: const types::id_type` – the vertex ID to check if it is the source.
203169
- *Return type*: `bool`
204170

205-
- **`is_incident_to(vertex) const`**:
206-
- *Description*: Returns `true` if the provided vertex is the target vertex of the edge (for directed edges).
207-
- *Returned value*:
208-
- For directed edges: $\text{vertex} = v$
209-
- For undirected edges: `is_incident_with(vertex)`
210-
- *Parameters*:
211-
- `vertex: const vertex_type&` – the vertex to check if it is the target.
212-
- *Return type*: `bool`
213-
214171
- **`is_incident_to(vertex_id) const`**:
215172
- *Description*: Returns `true` if a vertex with the given ID is the target of the edge.
216173
- *Returned value*:
217-
- For directed edges: $\text{vertex-id} = v_{id}$
174+
- For directed edges: $\text{vertex-id} = v$
218175
- For undirected edges: `is_incident_with(vertex)`
219176
- *Parameters*:
220177
- `vertex_id: const types::id_type` – the vertex ID to check if it is the target.
@@ -231,28 +188,23 @@ The destructor is *defaulted*, allowing proper cleanup of the `edge_descriptor`
231188

232189
```cpp
233190
template <
234-
type_traits::c_instantiation_of<vertex_descriptor> VertexType,
235191
type_traits::c_edge_directional_tag DirectionalTag = directed_t,
236192
type_traits::c_properties Properties = types::empty_properties>
237-
using edge = edge_descriptor<VertexType, DirectionalTag, Properties>;
193+
using edge = edge_descriptor<DirectionalTag, Properties>;
238194
```
239195

240196
- `directed_edge`: An alias for `edge_descriptor` specifically for directed edges.
241197

242198
```cpp
243-
template <
244-
type_traits::c_instantiation_of<vertex_descriptor> VertexType,
245-
type_traits::c_properties Properties = types::empty_properties>
246-
using directed_edge = edge_descriptor<VertexType, directed_t, Properties>;
199+
template <type_traits::c_properties Properties = types::empty_properties>
200+
using directed_edge = edge_descriptor<directed_t, Properties>;
247201
```
248202

249203
- `undirected_edge`: An alias for `edge_descriptor` specifically for undirected edges.
250204

251205
```cpp
252-
template <
253-
type_traits::c_instantiation_of<vertex_descriptor> VertexType,
254-
type_traits::c_properties Properties = types::empty_properties>
255-
using undirected_edge = edge_descriptor<VertexType, undirected_t, Properties>;
206+
template <type_traits::c_properties Properties = types::empty_properties>
207+
using undirected_edge = edge_descriptor<undirected_t, Properties>;
256208
```
257209

258210
<br />

include/gl/algorithm/breadth_first_search.hpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@ namespace gl::algorithm {
1313
template <
1414
result_discriminator ResultDiscriminator = algorithm::ret,
1515
type_traits::c_graph GraphType,
16-
type_traits::c_optional_vertex_callback<GraphType, void> PreVisitCallback =
17-
algorithm::empty_callback,
18-
type_traits::c_optional_vertex_callback<GraphType, void> PostVisitCallback =
19-
algorithm::empty_callback>
16+
type_traits::c_optional_id_callback<void> PreVisitCallback = algorithm::empty_callback,
17+
type_traits::c_optional_id_callback<void> PostVisitCallback = algorithm::empty_callback>
2018
impl::alg_return_type<ResultDiscriminator, predecessors_descriptor> breadth_first_search(
2119
const GraphType& graph,
2220
const std::optional<types::id_type>& root_vertex_id_opt = no_root_vertex,
@@ -35,8 +33,8 @@ impl::alg_return_type<ResultDiscriminator, predecessors_descriptor> breadth_firs
3533
impl::bfs(
3634
graph,
3735
impl::init_range(root_vertex_id_opt.value()),
38-
impl::default_visit_vertex_predicate<GraphType>(visited),
39-
impl::default_visit_callback<GraphType, ResultDiscriminator>(visited, pd),
36+
impl::default_visit_vertex_predicate(visited),
37+
impl::default_visit_callback<ResultDiscriminator>(visited, pd),
4038
impl::default_enqueue_vertex_predicate<GraphType, true>(visited),
4139
pre_visit,
4240
post_visit
@@ -47,8 +45,8 @@ impl::alg_return_type<ResultDiscriminator, predecessors_descriptor> breadth_firs
4745
impl::bfs(
4846
graph,
4947
impl::init_range(root_id),
50-
impl::default_visit_vertex_predicate<GraphType>(visited),
51-
impl::default_visit_callback<GraphType, ResultDiscriminator>(visited, pd),
48+
impl::default_visit_vertex_predicate(visited),
49+
impl::default_visit_callback<ResultDiscriminator>(visited, pd),
5250
impl::default_enqueue_vertex_predicate<GraphType, true>(visited),
5351
pre_visit,
5452
post_visit

include/gl/algorithm/coloring.hpp

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@ using bicoloring_type = std::vector<types::binary_color>;
1212

1313
template <
1414
type_traits::c_graph GraphType,
15-
type_traits::c_optional_vertex_callback<GraphType, void> PreVisitCallback =
16-
algorithm::empty_callback,
17-
type_traits::c_optional_vertex_callback<GraphType, void> PostVisitCallback =
18-
algorithm::empty_callback>
15+
type_traits::c_optional_id_callback<void> PreVisitCallback = algorithm::empty_callback,
16+
type_traits::c_optional_id_callback<void> PostVisitCallback = algorithm::empty_callback>
1917
[[nodiscard]] std::optional<bicoloring_type> bipartite_coloring(
2018
const GraphType& graph,
2119
const PreVisitCallback& pre_visit = {},
@@ -28,8 +26,7 @@ template <
2826
coloring_opt.emplace(graph.n_vertices(), bin_color_value::unset);
2927
auto& coloring = coloring_opt.value();
3028

31-
for (const auto root_vertex : graph.vertices()) {
32-
const auto root_id = root_vertex.id();
29+
for (const auto root_id : graph.vertex_ids()) {
3330
if (coloring[root_id].is_set())
3431
continue;
3532

@@ -41,19 +38,18 @@ template <
4138
impl::init_range(root_id),
4239
algorithm::empty_callback{}, // visit predicate
4340
algorithm::empty_callback{}, // visit callback
44-
[&coloring](const vertex_type& vertex, const edge_type& in_edge)
41+
[&coloring](const types::id_type vertex_id, const edge_type& in_edge)
4542
-> predicate_result { // enqueue predicate
4643
if (in_edge.is_loop())
4744
return false;
4845

49-
const auto vertex_id = vertex.id();
50-
const auto source_id = in_edge.incident_vertex(vertex).id();
46+
const auto pred_id = in_edge.incident_vertex(vertex_id);
5147

52-
if (coloring[vertex_id] == coloring[source_id])
48+
if (coloring[vertex_id] == coloring[pred_id])
5349
return predicate_result::unknown; // graph is not bipartite
5450

55-
if (not coloring[vertex.id()].is_set()) {
56-
coloring[vertex.id()] = coloring[source_id].next();
51+
if (not coloring[vertex_id].is_set()) {
52+
coloring[vertex_id] = coloring[pred_id].next();
5753
return true;
5854
}
5955

include/gl/algorithm/depth_first_search.hpp

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@ namespace gl::algorithm {
1212
template <
1313
result_discriminator ResultDiscriminator = algorithm::ret,
1414
type_traits::c_graph GraphType,
15-
type_traits::c_optional_vertex_callback<GraphType, void> PreVisitCallback =
16-
algorithm::empty_callback,
17-
type_traits::c_optional_vertex_callback<GraphType, void> PostVisitCallback =
18-
algorithm::empty_callback>
15+
type_traits::c_optional_id_callback<void> PreVisitCallback = algorithm::empty_callback,
16+
type_traits::c_optional_id_callback<void> PostVisitCallback = algorithm::empty_callback>
1917
impl::alg_return_type<ResultDiscriminator, predecessors_descriptor> depth_first_search(
2018
const GraphType& graph,
2119
const std::optional<types::id_type>& root_vertex_id_opt = no_root_vertex,
@@ -33,21 +31,21 @@ impl::alg_return_type<ResultDiscriminator, predecessors_descriptor> depth_first_
3331
if (root_vertex_id_opt) {
3432
impl::dfs(
3533
graph,
36-
graph.get_vertex(root_vertex_id_opt.value()),
37-
impl::default_visit_vertex_predicate<GraphType>(visited),
38-
impl::default_visit_callback<GraphType, ResultDiscriminator>(visited, pd),
34+
root_vertex_id_opt.value(),
35+
impl::default_visit_vertex_predicate(visited),
36+
impl::default_visit_callback<ResultDiscriminator>(visited, pd),
3937
impl::default_enqueue_vertex_predicate<GraphType>(visited),
4038
pre_visit,
4139
post_visit
4240
);
4341
}
4442
else {
45-
for (const auto& root_vertex : graph.vertices())
43+
for (const auto root_vertex_id : graph.vertex_ids())
4644
impl::dfs(
4745
graph,
48-
root_vertex,
49-
impl::default_visit_vertex_predicate<GraphType>(visited),
50-
impl::default_visit_callback<GraphType, ResultDiscriminator>(visited, pd),
46+
root_vertex_id,
47+
impl::default_visit_vertex_predicate(visited),
48+
impl::default_visit_callback<ResultDiscriminator>(visited, pd),
5149
impl::default_enqueue_vertex_predicate<GraphType>(visited),
5250
pre_visit,
5351
post_visit
@@ -61,10 +59,8 @@ impl::alg_return_type<ResultDiscriminator, predecessors_descriptor> depth_first_
6159
template <
6260
result_discriminator ResultDiscriminator = algorithm::ret,
6361
type_traits::c_graph GraphType,
64-
type_traits::c_optional_vertex_callback<GraphType, void> PreVisitCallback =
65-
algorithm::empty_callback,
66-
type_traits::c_optional_vertex_callback<GraphType, void> PostVisitCallback =
67-
algorithm::empty_callback>
62+
type_traits::c_optional_id_callback<void> PreVisitCallback = algorithm::empty_callback,
63+
type_traits::c_optional_id_callback<void> PostVisitCallback = algorithm::empty_callback>
6864
impl::alg_return_type<ResultDiscriminator, predecessors_descriptor> recursive_depth_first_search(
6965
const GraphType& graph,
7066
const std::optional<types::id_type>& root_vertex_id_opt = no_root_vertex,
@@ -83,23 +79,23 @@ impl::alg_return_type<ResultDiscriminator, predecessors_descriptor> recursive_de
8379
const auto root_id = root_vertex_id_opt.value();
8480
impl::r_dfs(
8581
graph,
86-
graph.get_vertex(root_id),
8782
root_id,
88-
impl::default_visit_vertex_predicate<GraphType>(visited),
89-
impl::default_visit_callback<GraphType, ResultDiscriminator>(visited, pd),
83+
root_id, // pred_id
84+
impl::default_visit_vertex_predicate(visited),
85+
impl::default_visit_callback<ResultDiscriminator>(visited, pd),
9086
impl::default_enqueue_vertex_predicate<GraphType>(visited),
9187
pre_visit,
9288
post_visit
9389
);
9490
}
9591
else {
96-
for (const auto& root_vertex : graph.vertices())
92+
for (const auto& root_id : graph.vertex_ids())
9793
impl::r_dfs(
9894
graph,
99-
root_vertex,
100-
root_vertex.id(),
101-
impl::default_visit_vertex_predicate<GraphType>(visited),
102-
impl::default_visit_callback<GraphType, ResultDiscriminator>(visited, pd),
95+
root_id,
96+
root_id, // pred_id
97+
impl::default_visit_vertex_predicate(visited),
98+
impl::default_visit_callback<ResultDiscriminator>(visited, pd),
10399
impl::default_enqueue_vertex_predicate<GraphType>(visited),
104100
pre_visit,
105101
post_visit

include/gl/algorithm/dijkstra.hpp

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,8 @@ template <type_traits::c_graph GraphType>
6565

6666
template <
6767
type_traits::c_graph GraphType,
68-
type_traits::c_optional_vertex_callback<GraphType, void> PreVisitCallback =
69-
algorithm::empty_callback,
70-
type_traits::c_optional_vertex_callback<GraphType, void> PostVisitCallback =
71-
algorithm::empty_callback>
68+
type_traits::c_optional_id_callback<void> PreVisitCallback = algorithm::empty_callback,
69+
type_traits::c_optional_id_callback<void> PostVisitCallback = algorithm::empty_callback>
7270
[[nodiscard]] paths_descriptor_type<GraphType> dijkstra_shortest_paths(
7371
const GraphType& graph,
7472
const types::id_type source_id,
@@ -94,22 +92,21 @@ template <
9492
impl::init_range(source_id),
9593
algorithm::empty_callback{}, // visit predicate
9694
algorithm::empty_callback{}, // visit callback
97-
[&paths, &negative_edge](const vertex_type& vertex, const edge_type& in_edge)
95+
[&paths, &negative_edge](const types::id_type vertex_id, const edge_type& in_edge)
9896
-> predicate_result { // enqueue predicate
99-
const auto vertex_id = vertex.id();
100-
const auto source_id = in_edge.incident_vertex(vertex).id();
97+
const auto pred_id = in_edge.incident_vertex(vertex_id);
10198

10299
const auto edge_weight = get_weight<GraphType>(in_edge);
103100
if (edge_weight < constants::zero) {
104101
negative_edge = std::cref(in_edge);
105102
return predicate_result::unknown;
106103
}
107104

108-
const auto new_distance = paths.distances[source_id] + edge_weight;
105+
const auto new_distance = paths.distances[pred_id] + edge_weight;
109106
if (not paths.predecessors[vertex_id].has_value()
110107
or new_distance < paths.distances[vertex_id]) {
111108
paths.distances[vertex_id] = new_distance;
112-
paths.predecessors[vertex_id].emplace(source_id);
109+
paths.predecessors[vertex_id].emplace(pred_id);
113110
return true;
114111
}
115112

@@ -121,8 +118,8 @@ template <
121118
const auto& edge = negative_edge.value().get();
122119
throw std::invalid_argument(std::format(
123120
"[alg::dijkstra_shortest_paths] Found an edge with a negative weight: [{}, {} | w={}]",
124-
edge.first().id(),
125-
edge.second().id(),
121+
edge.first(),
122+
edge.second(),
126123
get_weight<GraphType>(edge)
127124
));
128125
}

0 commit comments

Comments
 (0)