Skip to content

Commit fbc251b

Browse files
authored
YT-CPPHGL-31: Implement flat matrix hypergraph representation models
- Defined a new implementation tag: `flat_matrix_t` - Aligned the implementation-tag-related constraints - Created a new internal implementation class `flat_incidence_matrix` with specializations for each layout tag - Aligned the implementation of `flat_matrix` and `flat_jagged_vector` to use subranges instead of spans as row/segment types to work properly with the boolean value type
1 parent 112a859 commit fbc251b

22 files changed

Lines changed: 2597 additions & 265 deletions

include/gl/impl/adjacency_matrix.hpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -107,17 +107,17 @@ class adjacency_matrix final {
107107
}
108108

109109
[[nodiscard]] gl_attr_force_inline bool has_edge(id_type source_id, id_type target_id) const {
110-
return this->_matrix[to_idx(source_id)][to_idx(target_id)] != invalid_id;
110+
return specialized_impl::get_entry(*this, source_id, target_id) != invalid_id;
111111
}
112112

113113
[[nodiscard]] bool has_edge(const edge_type& edge) const {
114-
return this->_matrix[to_idx(edge.source())][to_idx(edge.target())] == edge.id();
114+
return specialized_impl::get_entry(*this, edge.source(), edge.target()) == edge.id();
115115
}
116116

117117
[[nodiscard]] std::optional<edge_type> get_edge(id_type source_id, id_type target_id) const
118118
requires(traits::c_has_empty_properties<edge_type>)
119119
{
120-
const auto edge_id = this->_matrix[to_idx(source_id)][to_idx(target_id)];
120+
const auto edge_id = specialized_impl::get_entry(*this, source_id, target_id);
121121
if (edge_id == invalid_id)
122122
return std::nullopt;
123123
return std::make_optional<edge_type>(edge_id, source_id, target_id);
@@ -128,7 +128,7 @@ class adjacency_matrix final {
128128
) const
129129
requires(traits::c_has_non_empty_properties<edge_type>)
130130
{
131-
const auto edge_id = this->_matrix[to_idx(source_id)][to_idx(target_id)];
131+
const auto edge_id = specialized_impl::get_entry(*this, source_id, target_id);
132132
if (edge_id == invalid_id)
133133
return std::nullopt;
134134
return std::make_optional<edge_type>(
@@ -139,7 +139,7 @@ class adjacency_matrix final {
139139
[[nodiscard]] std::vector<edge_type> get_edges(id_type source_id, id_type target_id) const
140140
requires(traits::c_has_empty_properties<edge_type>)
141141
{
142-
const auto edge_id = this->_matrix[to_idx(source_id)][to_idx(target_id)];
142+
const auto edge_id = specialized_impl::get_entry(*this, source_id, target_id);
143143
if (edge_id == invalid_id)
144144
return std::vector<edge_type>();
145145
return std::vector<edge_type>{
@@ -152,7 +152,7 @@ class adjacency_matrix final {
152152
) const
153153
requires(traits::c_has_non_empty_properties<edge_type>)
154154
{
155-
const auto edge_id = this->_matrix[to_idx(source_id)][to_idx(target_id)];
155+
const auto edge_id = specialized_impl::get_entry(*this, source_id, target_id);
156156
if (edge_id == invalid_id)
157157
return std::vector<edge_type>();
158158
return std::vector<edge_type>{
@@ -197,11 +197,13 @@ class adjacency_matrix final {
197197
{
198198
return std::views::iota(initial_id_v<id_type>, this->_matrix.size())
199199
| std::views::filter([this, vertex_id](const auto source_id) {
200-
return this->_matrix[to_idx(source_id)][to_idx(vertex_id)] != invalid_id;
200+
return specialized_impl::get_entry(*this, source_id, vertex_id) != invalid_id;
201201
})
202202
| std::views::transform([this, vertex_id](const auto source_id) {
203203
return edge_type{
204-
this->_matrix[to_idx(source_id)][to_idx(vertex_id)], source_id, vertex_id
204+
specialized_impl::get_entry(*this, source_id, vertex_id),
205+
source_id,
206+
vertex_id
205207
};
206208
});
207209
}
@@ -213,10 +215,10 @@ class adjacency_matrix final {
213215
{
214216
return std::views::iota(initial_id_v<id_type>, this->_matrix.size())
215217
| std::views::filter([this, vertex_id](const auto source_id) {
216-
return this->_matrix[to_idx(source_id)][to_idx(vertex_id)] != invalid_id;
218+
return specialized_impl::get_entry(*this, source_id, vertex_id) != invalid_id;
217219
})
218220
| std::views::transform([this, vertex_id, &edge_properties_map](const auto source_id) {
219-
const auto edge_id = this->_matrix[to_idx(source_id)][to_idx(vertex_id)];
221+
const auto edge_id = specialized_impl::get_entry(*this, source_id, vertex_id);
220222
return edge_type{
221223
edge_id, source_id, vertex_id, *edge_properties_map[to_idx(edge_id)]
222224
};

include/gl/impl/specialized/adjacency_list.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ struct directed_adjacency_list {
154154
}
155155

156156
// remove the list of edges incident from the vertex entirely
157-
self._list.erase(self._list.begin() + static_cast<std::ptrdiff_t>(vertex_id));
157+
self._list.erase(self._list.begin() + to_diff(vertex_id));
158158
return removed_edges;
159159
}
160160

@@ -255,7 +255,7 @@ struct undirected_adjacency_list {
255255
const auto removed_edges =
256256
self._list[vertex_idx] | std::views::transform(&item_type::edge_id)
257257
| std::ranges::to<std::vector>();
258-
self._list.erase(self._list.begin() + static_cast<std::ptrdiff_t>(vertex_id));
258+
self._list.erase(self._list.begin() + to_diff(vertex_id));
259259
return removed_edges;
260260
}
261261

include/gl/impl/specialized/adjacency_matrix.hpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,17 +154,21 @@ struct directed_adjacency_matrix {
154154
| std::views::filter([](auto edge_id) { return edge_id != invalid_id; })
155155
| std::ranges::to<std::vector>();
156156

157-
self._matrix.erase(self._matrix.begin() + static_cast<std::ptrdiff_t>(vertex_id));
158-
157+
const auto vertex_pos = to_diff(vertex_id);
158+
self._matrix.erase(self._matrix.begin() + vertex_pos);
159159
for (auto& row : self._matrix) {
160160
if (const auto edge_id = row[vertex_idx]; edge_id != invalid_id)
161161
removed_edges.push_back(edge_id);
162-
row.erase(row.begin() + static_cast<std::ptrdiff_t>(vertex_id));
162+
row.erase(row.begin() + vertex_pos);
163163
}
164164

165165
return removed_edges;
166166
}
167167

168+
static id_type get_entry(const impl_type& self, id_type source_id, id_type target_id) {
169+
return self._matrix[to_idx(source_id)][to_idx(target_id)];
170+
}
171+
168172
static inline void add_edge(
169173
impl_type& self, id_type edge_id, id_type source_id, id_type target_id
170174
) {
@@ -277,14 +281,18 @@ struct undirected_adjacency_matrix {
277281
| std::views::filter([](auto edge_id) { return edge_id != invalid_id; })
278282
| std::ranges::to<std::vector>();
279283

280-
const auto vertex_pos = static_cast<std::ptrdiff_t>(vertex_id);
284+
const auto vertex_pos = to_diff(vertex_id);
281285
self._matrix.erase(self._matrix.begin() + vertex_pos);
282286
for (auto& row : self._matrix)
283287
row.erase(row.begin() + vertex_pos);
284288

285289
return removed_edges;
286290
}
287291

292+
static id_type get_entry(const impl_type& self, id_type source_id, id_type target_id) {
293+
return self._matrix[to_idx(source_id)][to_idx(target_id)];
294+
}
295+
288296
static void add_edge(impl_type& self, id_type edge_id, id_type source_id, id_type target_id) {
289297
detail::check_edge_override(self._matrix, source_id, target_id);
290298

include/gl/impl/specialized/flat_adjacency_matrix.hpp

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#pragma once
66

7+
#include "gl/attributes/diagnostics.hpp"
78
#include "gl/constants.hpp"
89
#include "gl/decl/impl_tags.hpp"
910
#include "gl/graph_traits.hpp"
@@ -19,6 +20,36 @@
1920

2021
namespace gl::impl::specialized {
2122

23+
namespace detail {
24+
25+
template <traits::c_id_type IdType>
26+
[[nodiscard]] auto& strict_get(flat_matrix<IdType>& id_matrix, const auto& edge) {
27+
// get the edge and validate the address
28+
const auto [source_id, target_id] = edge.incident_vertices();
29+
auto& edge_id = id_matrix[to_idx(source_id), to_idx(target_id)];
30+
if (edge.id() != edge_id)
31+
throw std::invalid_argument(std::format(
32+
"Got invalid edge [id = {} | vertices = ({}, {})]", edge.id(), source_id, target_id
33+
));
34+
35+
return edge_id;
36+
}
37+
38+
template <traits::c_id_type IdType>
39+
inline void check_edge_override(
40+
const flat_matrix<IdType>& id_matrix, const IdType source_id, const IdType target_id
41+
) {
42+
if (const auto edge_id = id_matrix[to_idx(source_id), to_idx(target_id)]; edge_id != invalid_id)
43+
throw std::logic_error(std::format(
44+
"Cannot override an existing edge: [id = {}, vertices = ({}, {})]",
45+
edge_id,
46+
source_id,
47+
target_id
48+
));
49+
}
50+
51+
} // namespace detail
52+
2253
template <traits::c_instantiation_of<adjacency_matrix> AdjacencyMatrix>
2354
requires(traits::c_directed_edge<typename AdjacencyMatrix::edge_type>)
2455
struct directed_flat_adjacency_matrix {
@@ -66,9 +97,10 @@ struct directed_flat_adjacency_matrix {
6697
const auto row = self._matrix[vertex_idx];
6798
const auto col = self._matrix.col(vertex_idx);
6899

69-
for (auto v_idx = 0uz; v_idx < self._matrix.n_rows(); ++v_idx)
70-
deg += static_cast<size_type>(row[v_idx] != invalid_id)
71-
+ static_cast<size_type>(col[static_cast<std::ptrdiff_t>(v_idx)] != invalid_id);
100+
const auto n_rows_bound = to_diff(self._matrix.n_rows());
101+
for (auto v_pos = 0z; v_pos < n_rows_bound; ++v_pos)
102+
deg += static_cast<size_type>(row[v_pos] != invalid_id)
103+
+ static_cast<size_type>(col[v_pos] != invalid_id);
72104

73105
return deg;
74106
}
@@ -121,7 +153,7 @@ struct directed_flat_adjacency_matrix {
121153
if (r_idx == vertex_idx)
122154
continue;
123155

124-
const auto edge_id = col[static_cast<std::ptrdiff_t>(r_idx)];
156+
const auto edge_id = col[to_diff(r_idx)];
125157
if (edge_id != invalid_id)
126158
removed_edges.push_back(edge_id);
127159
}
@@ -133,6 +165,10 @@ struct directed_flat_adjacency_matrix {
133165
return removed_edges;
134166
}
135167

168+
static id_type get_entry(const impl_type& self, id_type source_id, id_type target_id) {
169+
return self._matrix[to_idx(source_id), to_idx(target_id)];
170+
}
171+
136172
static inline void add_edge(
137173
impl_type& self, id_type edge_id, id_type source_id, id_type target_id
138174
) {
@@ -151,7 +187,7 @@ struct directed_flat_adjacency_matrix {
151187

152188
auto matrix_source_row = self._matrix[to_idx(source_id)];
153189
for (auto [edge_id, target_id] : std::views::zip(edge_ids, target_ids))
154-
matrix_source_row[to_idx(target_id)] = edge_id;
190+
matrix_source_row[to_diff(target_id)] = edge_id;
155191
}
156192

157193
static inline void remove_edge(impl_type& self, const edge_type& edge) {
@@ -246,6 +282,10 @@ struct undirected_flat_adjacency_matrix {
246282
return removed_edges;
247283
}
248284

285+
static id_type get_entry(const impl_type& self, id_type source_id, id_type target_id) {
286+
return self._matrix[to_idx(source_id), to_idx(target_id)];
287+
}
288+
249289
static void add_edge(impl_type& self, id_type edge_id, id_type source_id, id_type target_id) {
250290
detail::check_edge_override(self._matrix, source_id, target_id);
251291

@@ -271,7 +311,7 @@ struct undirected_flat_adjacency_matrix {
271311

272312
for (auto [edge_id, target_id] : std::views::zip(edge_ids, target_ids)) {
273313
const auto target_idx = to_idx(target_id);
274-
matrix_source_row[target_idx] = edge_id;
314+
matrix_source_row[to_diff(target_id)] = edge_id;
275315
if (target_idx != source_idx)
276316
self._matrix[target_idx, source_idx] = edge_id;
277317
}

include/gl/types/core.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "gl/attributes/force_inline.hpp"
88

99
#include <concepts>
10+
#include <cstddef>
1011
#include <cstdint>
1112
#include <optional>
1213
#include <utility>
@@ -27,6 +28,10 @@ concept c_id_type = std::unsigned_integral<T>;
2728
return static_cast<size_type>(id);
2829
}
2930

31+
[[nodiscard]] gl_attr_force_inline constexpr std::ptrdiff_t to_diff(std::integral auto i) noexcept {
32+
return static_cast<std::ptrdiff_t>(i);
33+
}
34+
3035
template <typename T>
3136
using homogeneous_pair = std::pair<T, T>;
3237

0 commit comments

Comments
 (0)