Skip to content

Commit 8a54dc0

Browse files
committed
YT-CPPGL-56: Replace the usage of pointers for vertex storage to a simple composite type [Part 1]
- The `graph` class does not store a list of vertices directly, only the number of vertices and optionally a vertex properties map - Added a vertex property map getter to the `graph` class - Vertices are compared only using IDs within the graph representation models - Removed vertex validation within implementation types - only the `graph` class validates vertices - The properties members of the vertex and edge classes are now private with added getter methods
1 parent e220490 commit 8a54dc0

34 files changed

Lines changed: 480 additions & 617 deletions

docs/graph.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,14 +138,14 @@ Based on the specified traits, the `graph` class defines the following types:
138138
### Vertex Operations
139139

140140
- **`graph.vertices() const`**:
141-
- *Description*: Returns an iterator range over all vertices in the graph.
141+
- *Description*: Returns an view over all vertices in the graph.
142142
- *Returned value*: $V$
143-
- *Return type*: `types::iterator_range<vertex_iterator_type>`
143+
- *Return type*: A random access *view* with values of type `vertex_type`.
144144

145145
- **`graph.vertex_ids() const`**:
146146
- *Description*: Returns a range of vertex IDs, starting from the initial vertex ID to the number of vertices in the grap.
147147
- *Returned value*: $(v_{id} : v \in V)$
148-
- *Return type*: `std::ranges::iota_view<types::id_type, types::id_type>`
148+
- *Return type*: A random access *view* with values of type `types::id_type` : `std::ranges::iota_view`.
149149

150150
- **`graph.get_vertex(vertex_id) const`**:
151151
- *Description*: Retrieves the vertex object associated with the given vertex ID.
@@ -296,6 +296,10 @@ Based on the specified traits, the `graph` class defines the following types:
296296
- *Description*: Returns a vector containing the degrees of the corresponding vertices (degree at index `i` corresponds to the vertex with an ID equal `i`).
297297
- *Return type*: `std::vector<types::size_type>`
298298

299+
- **`graph.vertex_properties_map() const`**:
300+
- *Description*: Returns a *map-like view* the properties of the corresponding vertices (property at index `i` corresponds to the vertex with an ID equal `i`).
301+
- *Return type*: A random access view with values of type `vertex_properties_type&`
302+
299303
<br />
300304

301305
### Edge Operations

docs/graph_elements.md

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ This section provides an overview of the `vertex_descriptor` and `edge_descripto
1414

1515
## The vertex class
1616

17-
The `vertex_descriptor` class represents the identity of a vertex in the graph, optionally carrying additional properties as specified by the user. This class is designed for use within the `Graph` class and serves as a lightweight identifier for vertices, providing an interface for accessing vertex properties, comparing vertex descriptors, and outputting vertex information.
17+
The `vertex_descriptor` class represents the vertex in the graph, optionally carrying additional properties as specified by the user. This class is designed for use within the `graph` class and serves as a lightweight identifier for vertices, providing an interface for accessing vertex properties, comparing vertex descriptors, and outputting vertex information.
1818

1919
By default, the `vertex_descriptor` class does not carry any properties. However, properties can be associated with each vertex by passing a custom type as the template parameter `Properties`, making it highly flexible and customizable for various graph use cases.
2020

21-
### Template parameters
21+
### Template Parameters
2222

2323
- **`Properties`**: A type that defines the properties associated with each vertex.
2424
- *Default value*: `types::empty_properties`
2525
- *Constraints*: must satisfy the **`type_traits::c_properties`** concept
2626

27-
### Member types
27+
### Member Types
2828

2929
- **`type`**: Alias for the `vertex_descriptor` itself.
3030
- **`properties_type`**: Type of the vertex properties as defined by the `Properties` template parameter.
@@ -37,10 +37,10 @@ By default, the `vertex_descriptor` class does not carry any properties. However
3737
- Constructs a `vertex_descriptor` with a unique ID and specified properties.
3838
- *Constraints*: the `properties_type` must be non-default.
3939
- **Move constructor and assignment operator**: *default*
40+
- **Copy constructor and assignment operator**: *default*
4041

4142
- **Deleted**:
4243
- **Default constructor**: prevent creating multiple vertices with the default ID within a graph.
43-
- **Copy constructor and assignment operator**: prevent copying of `vertex_descriptor` instances, as they are meant to be identificators.
4444

4545
### Desctructor
4646

@@ -52,6 +52,10 @@ The destructor is *defaulted*, allowing proper cleanup of the `vertex_descriptor
5252
- *Description*: Returns the unique identifier of the vertex.
5353
- *Return type*: `types::id_type`
5454

55+
- **`properties()`**:
56+
- *Description*: Returns a mutable reference to the properties associated with the vertex.
57+
- *Return type*: `properties_type&`
58+
5559
- **`operator==(const vertex_descriptor& other) const`**:
5660
- *Description*: Compares two `vertex_descriptor` objects for equality based on their vertex IDs.
5761
- *Parameters*:
@@ -64,12 +68,6 @@ The destructor is *defaulted*, allowing proper cleanup of the `vertex_descriptor
6468
- `other: const vertex_descriptor&` – the vertex descriptor to compare with.
6569
- *Return type*: `std::strong_ordering`
6670

67-
### Member variables
68-
69-
- **`properties`**:
70-
- *Description*: A mutable member that stores the properties associated with the vertex.
71-
- *Type*: Defined by the `Properties` template parameter.
72-
7371
### Additional utility
7472

7573
- **`vertex`**: A convenient alias for `vertex_descriptor` with customizable properties.
@@ -137,6 +135,11 @@ The destructor is *defaulted*, allowing proper cleanup of the `edge_descriptor`
137135
- *Returned value*: $(u, v)$
138136
- *Return type*: `types::homogeneous_pair<const vertex_type&>`
139137

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>`
142+
140143
- **`first() const`**:
141144
- *Description*: Returns a reference to the first vertex of the edge.
142145
- *Returned value*: $u$
@@ -147,22 +150,7 @@ The destructor is *defaulted*, allowing proper cleanup of the `edge_descriptor`
147150
- *Returned value*: $v$
148151
- *Return type*: `const vertex_type&`
149152

150-
- **`incident_vertex_ids() const`**:
151-
- *Description*: Returns the unique IDs of the vertices connected by the edge.
152-
- *Returned value*: $(u_{id}, v_{id})$
153-
- *Return type*: `types::homogeneous_pair<types::id_type>`
154-
155-
- **`first_id() const`**:
156-
- *Description*: Returns the ID of the first vertex.
157-
- *Returned value*: $u_{id}$
158-
- *Return type*: `types::id_type`
159-
160-
- **`second_id() const`**:
161-
- *Description*: Returns the ID of the second vertex.
162-
- *Returned value*: $v_{id}$
163-
- *Return type*: `types::id_type`
164-
165-
- **`incident_vertex(const vertex_type& vertex) const`**:
153+
- **`incident_vertex(vertex) const`**:
166154
- *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.
167155
- *Returned value*:
168156
- $v$ if $\text{vertex} = u$
@@ -172,24 +160,31 @@ The destructor is *defaulted*, allowing proper cleanup of the `edge_descriptor`
172160
- `vertex: const vertex_type&` – the vertex for which the opposite vertex is requested.
173161
- *Return type*: `const vertex_type&`
174162

175-
- **`incident_vertex_id(const types::id_type vertex_id) const`**:
176-
- *Description*: Returns the ID of 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.
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.
177165
- *Returned value*:
178-
- $v_{id}$ if $\text{vertex-id} = u_{id}$
179-
- $u_{id}$ if $\text{vertex-id} = v_{id}$
166+
- $v$ if $\text{vertex-id} = u_{id}$
167+
- $u$ if $\text{vertex-id} = v_{id}$
180168
- error otherwise
181169
- *Parameters*:
182170
- `vertex_id: const types::id_type` – the vertex ID for which the opposite vertex ID is requested.
183171
- *Return type*: `types::id_type`
184172

185-
- **`is_incident_with(const vertex_type& vertex) const`**:
173+
- **`is_incident_with(vertex) const`**:
186174
- *Description*: Returns `true` if the provided vertex is connected to the edge.
187175
- *Returned value*: $\text{vertex} \in {u, v}$
188176
- *Parameters*:
189177
- `vertex: const vertex_type&` – the vertex to check for incidence with the edge.
190178
- *Return type*: `bool`
191179

192-
- **`is_incident_from(const vertex_type& vertex) const`**:
180+
- **`is_incident_with(vertex_id) const`**:
181+
- *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}}$
183+
- *Parameters*:
184+
- `vertex_id: const types::id_type` – the vertex ID to check for incidence with the edge.
185+
- *Return type*: `bool`
186+
187+
- **`is_incident_from(vertex) const`**:
193188
- *Description*: Returns `true` if the provided vertex is the source of the edge (for directed edges).
194189
- *Returned value*:
195190
- For directed edges: $\text{vertex} = u$
@@ -198,7 +193,16 @@ The destructor is *defaulted*, allowing proper cleanup of the `edge_descriptor`
198193
- `vertex: const vertex_type&` – the vertex to check if it is the source.
199194
- *Return type*: `bool`
200195

201-
- **`is_incident_to(const vertex_type& vertex) const`**:
196+
- **`is_incident_from(vertex_id) const`**:
197+
- *Description*: Returns `true` if a vertex with the given ID is the source of the edge.
198+
- *Returned value*:
199+
- For directed edges: $\text{vertex-id} = u_{id}$
200+
- For undirected edges: `is_incident_with(vertex)`
201+
- *Parameters*:
202+
- `vertex_id: const types::id_type` – the vertex ID to check if it is the source.
203+
- *Return type*: `bool`
204+
205+
- **`is_incident_to(vertex) const`**:
202206
- *Description*: Returns `true` if the provided vertex is the target vertex of the edge (for directed edges).
203207
- *Returned value*:
204208
- For directed edges: $\text{vertex} = v$
@@ -207,17 +211,20 @@ The destructor is *defaulted*, allowing proper cleanup of the `edge_descriptor`
207211
- `vertex: const vertex_type&` – the vertex to check if it is the target.
208212
- *Return type*: `bool`
209213

214+
- **`is_incident_to(vertex_id) const`**:
215+
- *Description*: Returns `true` if a vertex with the given ID is the target of the edge.
216+
- *Returned value*:
217+
- For directed edges: $\text{vertex-id} = v_{id}$
218+
- For undirected edges: `is_incident_with(vertex)`
219+
- *Parameters*:
220+
- `vertex_id: const types::id_type` – the vertex ID to check if it is the target.
221+
- *Return type*: `bool`
222+
210223
- **`is_loop() const`**:
211224
- *Description*: Returns `true` if the edge is a loop (i.e., both vertices are the same).
212225
- *Returned value*: $u = v$
213226
- *Return type*: `bool`
214227

215-
### Member variables
216-
217-
- **`properties`**:
218-
- *Description*: A mutable member that stores the properties associated with the edge.
219-
- *Type*: Defined by the `Properties` template parameter.
220-
221228
### Additional utility
222229

223230
- **`edge`**: A convenient alias for `edge_descriptor`, with customizable properties and directionality.

include/gl/algorithm/coloring.hpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ template <
2828
coloring_opt.emplace(graph.n_vertices(), bin_color_value::unset);
2929
auto& coloring = coloring_opt.value();
3030

31-
for (const auto& root_vertex : graph.vertices()) {
31+
for (const auto root_vertex : graph.vertices()) {
3232
const auto root_id = root_vertex.id();
3333
if (coloring[root_id].is_set())
3434
continue;
@@ -80,14 +80,14 @@ template <
8080
type_traits::c_sized_range_of<types::binary_color> ColorRange>
8181
requires(type_traits::c_binary_color_properties_type<typename GraphType::vertex_properties_type>)
8282
bool apply_coloring(GraphType& graph, const ColorRange& color_range) {
83+
using color_type = typename GraphType::vertex_properties_type::color_type;
84+
8385
if (color_range.size() != graph.n_vertices())
8486
return false;
8587

86-
using color_type = typename GraphType::vertex_properties_type::color_type;
87-
auto color_it = std::ranges::begin(color_range);
88-
89-
for (const auto& vertex : graph.vertices())
90-
vertex.properties.color = color_type{*color_it++};
88+
auto vertices = graph.vertices(); // store the view to extend its lifetime
89+
for (const auto& [vertex, color] : std::views::zip(vertices, color_range))
90+
vertex.properties().color = color;
9191

9292
return true;
9393
}

include/gl/algorithm/dijkstra.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ template <
121121
const auto& edge = negative_edge.value().get();
122122
throw std::invalid_argument(std::format(
123123
"[alg::dijkstra_shortest_paths] Found an edge with a negative weight: [{}, {} | w={}]",
124-
edge.first_id(),
125-
edge.second_id(),
124+
edge.first().id(),
125+
edge.second().id(),
126126
get_weight<GraphType>(edge)
127127
));
128128
}

include/gl/algorithm/mst.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ template <
8080
const auto& min_edge = min_edge_info.edge.get();
8181
const auto min_weight = get_weight<GraphType>(min_edge);
8282

83-
const auto& target_id = min_edge.incident_vertex_id(min_edge_info.source_id);
83+
const auto& target_id = min_edge.incident_vertex(min_edge_info.source_id).id();
8484
if (visited[target_id])
8585
continue;
8686

@@ -93,7 +93,7 @@ template <
9393

9494
// enqueue all edges adjacent to the `target` vertex if they lead to unvisited verties
9595
for (const auto& edge : graph.adjacent_edges(target_id))
96-
if (not visited[edge.incident_vertex_id(target_id)])
96+
if (not visited[edge.incident_vertex(target_id).id()])
9797
edge_queue.emplace(edge, target_id);
9898
}
9999

@@ -157,7 +157,7 @@ requires type_traits::c_has_numeric_limits_max<types::vertex_distance_type<Graph
157157
// Update adjacent vertices
158158
for (const auto& edge : graph.adjacent_edges(vertex_id)) {
159159
const auto edge_weight = get_weight<GraphType>(edge);
160-
const auto incident_vertex_id = edge.incident_vertex_id(vertex_id);
160+
const auto incident_vertex_id = edge.incident_vertex(vertex_id).id();
161161

162162
if (not in_mst[incident_vertex_id] && edge_weight < min_cost[incident_vertex_id]) {
163163
min_cost[incident_vertex_id] = edge_weight;

0 commit comments

Comments
 (0)