Skip to content

Commit 0027f14

Browse files
authored
YT-CPPGL-74: Refactor element properties storage
Replaced the std::vector<std::unique_ptr<T>> containers with std::vector<T> for hypergraph element property storage
1 parent 56c8060 commit 0027f14

8 files changed

Lines changed: 109 additions & 133 deletions

File tree

include/gl/graph.hpp

Lines changed: 76 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -85,26 +85,23 @@ class graph final {
8585
using vertex_properties_map_type = std::conditional_t<
8686
traits::c_empty_properties<vertex_properties_type>,
8787
empty_properties_map,
88-
std::vector<std::unique_ptr<vertex_properties_type>>>;
88+
std::vector<vertex_properties_type>>;
8989

9090
using edge_type = typename traits_type::edge_type;
9191
using edge_properties_type = typename traits_type::edge_properties_type;
9292

9393
using edge_properties_map_type = std::conditional_t<
9494
traits::c_empty_properties<edge_properties_type>,
9595
empty_properties_map,
96-
std::vector<std::unique_ptr<edge_properties_type>>>;
96+
std::vector<edge_properties_type>>;
9797

9898
graph& operator=(const graph&) = delete;
9999

100100
graph() = default;
101101

102102
explicit graph(const size_type n_vertices) : _n_vertices(n_vertices), _impl(n_vertices) {
103-
if constexpr (traits::c_non_empty_properties<vertex_properties_type>) {
104-
this->_vertex_properties.reserve(n_vertices);
105-
for (auto _ : this->vertex_ids())
106-
this->_vertex_properties.push_back(std::make_unique<vertex_properties_type>());
107-
}
103+
if constexpr (traits::c_non_empty_properties<vertex_properties_type>)
104+
this->_vertex_properties.resize(n_vertices);
108105
}
109106

110107
graph(graph&&) noexcept = default;
@@ -129,10 +126,7 @@ class graph final {
129126
const auto new_vertex_id = static_cast<id_type>(this->_n_vertices++);
130127

131128
if constexpr (traits::c_non_empty_properties<vertex_properties_type>)
132-
return vertex_descriptor{
133-
new_vertex_id,
134-
*this->_vertex_properties.emplace_back(std::make_unique<vertex_properties_type>())
135-
};
129+
return vertex_descriptor{new_vertex_id, this->_vertex_properties.emplace_back()};
136130
else
137131
return vertex_descriptor{new_vertex_id};
138132
}
@@ -143,22 +137,16 @@ class graph final {
143137
this->_impl.add_vertex();
144138
return vertex_descriptor{
145139
static_cast<id_type>(this->_n_vertices++),
146-
*this->_vertex_properties.emplace_back(
147-
std::make_unique<vertex_properties_type>(std::move(properties))
148-
)
140+
this->_vertex_properties.emplace_back(std::move(properties))
149141
};
150142
}
151143

152144
void add_vertices(const size_type n) {
153145
this->_impl.add_vertices(n);
154146
this->_n_vertices += n;
155147

156-
if constexpr (traits::c_non_empty_properties<vertex_properties_type>) {
157-
const auto old_size = this->_vertex_properties.size();
158-
this->_vertex_properties.reserve(this->_n_vertices);
159-
for (auto _ = old_size; _ < this->_n_vertices; ++_)
160-
this->_vertex_properties.push_back(std::make_unique<vertex_properties_type>());
161-
}
148+
if constexpr (traits::c_non_empty_properties<vertex_properties_type>)
149+
this->_vertex_properties.resize(this->_n_vertices);
162150
}
163151

164152
void add_vertices_with(
@@ -171,12 +159,12 @@ class graph final {
171159
this->_impl.add_vertices(n);
172160
this->_n_vertices += n;
173161

174-
if constexpr (traits::c_non_empty_properties<vertex_properties_type>) {
175-
for (auto& properties : properties_rng)
176-
this->_vertex_properties.emplace_back(
177-
std::make_unique<vertex_properties_type>(properties)
178-
);
179-
}
162+
if constexpr (traits::c_non_empty_properties<vertex_properties_type>)
163+
this->_vertex_properties.insert(
164+
this->_vertex_properties.end(),
165+
std::ranges::begin(properties_rng),
166+
std::ranges::end(properties_rng)
167+
);
180168
}
181169

182170
gl_attr_force_inline void remove_vertex(const id_type vertex_id) {
@@ -231,7 +219,7 @@ class graph final {
231219
[[nodiscard]] gl_attr_force_inline vertex_type vertex_unchecked(const id_type vertex_id
232220
) const noexcept {
233221
if constexpr (traits::c_non_empty_properties<vertex_properties_type>)
234-
return vertex_descriptor{vertex_id, *this->_vertex_properties[vertex_id]};
222+
return vertex_descriptor{vertex_id, this->_vertex_properties[vertex_id]};
235223
else
236224
return vertex_descriptor{vertex_id};
237225
}
@@ -241,21 +229,8 @@ class graph final {
241229
return this->vertex_unchecked(vertex_id);
242230
}
243231

244-
[[nodiscard]] gl_attr_force_inline auto vertices() const noexcept
245-
requires(traits::c_empty_properties<vertex_properties_type>)
246-
{
247-
return this->vertex_ids()
248-
| std::views::transform([](const id_type id) { return vertex_descriptor{id}; });
249-
}
250-
251-
[[nodiscard]] gl_attr_force_inline auto vertices() const noexcept
252-
requires(traits::c_non_empty_properties<vertex_properties_type>)
253-
{
254-
return this->_vertex_properties | std::views::enumerate
255-
| std::views::transform([](const auto& x) {
256-
const auto& [id, ptr] = x;
257-
return vertex_descriptor{static_cast<id_type>(id), *ptr};
258-
});
232+
[[nodiscard]] gl_attr_force_inline auto vertices() const noexcept {
233+
return this->vertex_ids() | std::views::transform(this->_create_vertex_descriptor());
259234
}
260235

261236
[[nodiscard]] gl_attr_force_inline auto vertex_ids() const noexcept {
@@ -264,7 +239,7 @@ class graph final {
264239

265240
[[nodiscard]] gl_attr_force_inline auto neighbors(const id_type vertex_id) const {
266241
return this->neighbor_ids(vertex_id)
267-
| std::views::transform([this](const id_type id) { return this->vertex(id); });
242+
| std::views::transform(this->_create_vertex_descriptor());
268243
}
269244

270245
[[nodiscard]] gl_attr_force_inline auto neighbors(vertex_type vertex) const {
@@ -282,7 +257,7 @@ class graph final {
282257

283258
[[nodiscard]] gl_attr_force_inline auto predecessors(const id_type vertex_id) const {
284259
return this->predecessor_ids(vertex_id)
285-
| std::views::transform([this](const id_type id) { return this->vertex(id); });
260+
| std::views::transform(this->_create_vertex_descriptor());
286261
}
287262

288263
[[nodiscard]] gl_attr_force_inline auto predecessors(vertex_type vertex) const {
@@ -300,7 +275,7 @@ class graph final {
300275

301276
[[nodiscard]] gl_attr_force_inline auto successors(const id_type vertex_id) const {
302277
return this->successor_ids(vertex_id)
303-
| std::views::transform([this](const id_type id) { return this->vertex(id); });
278+
| std::views::transform(this->_create_vertex_descriptor());
304279
}
305280

306281
[[nodiscard]] gl_attr_force_inline auto successors(vertex_type vertex) const {
@@ -321,13 +296,13 @@ class graph final {
321296
requires(traits::c_non_empty_properties<vertex_properties_type>)
322297
{
323298
this->_verify_vertex_id(id);
324-
return *this->_vertex_properties[id];
299+
return this->_vertex_properties[id];
325300
}
326301

327302
[[nodiscard]] gl_attr_force_inline auto vertex_properties_map() const noexcept
328303
requires(traits::c_non_empty_properties<vertex_properties_type>)
329304
{
330-
return util::deref_view(this->_vertex_properties);
305+
return std::views::all(this->_vertex_properties);
331306
}
332307

333308
// --- degree getters ---
@@ -380,18 +355,16 @@ class graph final {
380355
const auto new_edge_id = static_cast<id_type>(this->_n_edges++);
381356
this->_impl.add_edge(new_edge_id, source_id, target_id);
382357

383-
if constexpr (traits::c_non_empty_properties<edge_properties_type>) {
384-
const auto& p =
385-
this->_edge_properties.emplace_back(std::make_unique<edge_properties_type>());
386-
return edge_type{new_edge_id, source_id, target_id, *p};
387-
}
388-
else {
358+
if constexpr (traits::c_non_empty_properties<edge_properties_type>)
359+
return edge_type{
360+
new_edge_id, source_id, target_id, this->_edge_properties.emplace_back()
361+
};
362+
else
389363
return edge_type{new_edge_id, source_id, target_id};
390-
}
391364
}
392365

393366
edge_type add_edge_with(
394-
const id_type source_id, const id_type target_id, const edge_properties_type& properties
367+
const id_type source_id, const id_type target_id, edge_properties_type properties
395368
)
396369
requires(traits::c_non_empty_properties<edge_properties_type>)
397370
{
@@ -401,9 +374,12 @@ class graph final {
401374
const auto new_edge_id = static_cast<id_type>(this->_n_edges++);
402375
this->_impl.add_edge(new_edge_id, source_id, target_id);
403376

404-
auto& p =
405-
this->_edge_properties.emplace_back(std::make_unique<edge_properties_type>(properties));
406-
return edge_type{new_edge_id, source_id, target_id, *p};
377+
return edge_type{
378+
new_edge_id,
379+
source_id,
380+
target_id,
381+
this->_edge_properties.emplace_back(std::move(properties))
382+
};
407383
}
408384

409385
// clang-format off
@@ -427,12 +403,8 @@ class graph final {
427403
const id_type source_id, const traits::c_sized_range_of<id_type> auto& target_id_rng
428404
) {
429405
this->_verify_vertex_id(source_id);
430-
431-
for (auto target_id : target_id_rng) {
406+
for (auto target_id : target_id_rng)
432407
this->_verify_vertex_id(target_id);
433-
if constexpr (traits::c_non_empty_properties<edge_properties_type>)
434-
this->_edge_properties.emplace_back(std::make_unique<edge_properties_type>());
435-
}
436408

437409
const auto prev_n_edges = this->_n_edges;
438410
this->_n_edges += std::ranges::size(target_id_rng);
@@ -441,18 +413,17 @@ class graph final {
441413
source_id,
442414
target_id_rng
443415
);
416+
417+
if constexpr (traits::c_non_empty_properties<edge_properties_type>)
418+
this->_edge_properties.resize(this->_n_edges);
444419
}
445420

446421
void add_edges_from(
447422
vertex_type source, const traits::c_sized_range_of<vertex_type> auto& target_rng
448423
) {
449424
this->_verify_vertex_id(source.id());
450-
451-
for (auto target : target_rng) {
425+
for (auto target : target_rng)
452426
this->_verify_vertex_id(target.id());
453-
if constexpr (traits::c_non_empty_properties<edge_properties_type>)
454-
this->_edge_properties.emplace_back(std::make_unique<edge_properties_type>());
455-
}
456427

457428
const auto prev_n_edges = this->_n_edges;
458429
this->_n_edges += std::ranges::size(target_rng);
@@ -461,6 +432,10 @@ class graph final {
461432
source.id(),
462433
target_rng | std::views::transform(&vertex_type::id)
463434
);
435+
436+
437+
if constexpr (traits::c_non_empty_properties<edge_properties_type>)
438+
this->_edge_properties.resize(this->_n_edges);
464439
}
465440

466441
void remove_edge(const edge_type& edge) {
@@ -579,13 +554,13 @@ class graph final {
579554
if (id >= this->_n_edges)
580555
throw std::out_of_range(std::format("Got invalid edge id [{}]", id));
581556

582-
return *this->_edge_properties[id];
557+
return this->_edge_properties[id];
583558
}
584559

585560
[[nodiscard]] gl_attr_force_inline auto edge_properties_map() const noexcept
586561
requires(traits::c_non_empty_properties<edge_properties_type>)
587562
{
588-
return util::deref_view(this->_edge_properties);
563+
return std::views::all(this->_edge_properties);
589564
}
590565

591566
// --- adjacency and incidence methods ---
@@ -627,19 +602,15 @@ class graph final {
627602
// --- comparison ---
628603

629604
[[nodiscard]] friend bool operator==(const graph& lhs, const graph& rhs) noexcept {
630-
constexpr auto val_eq = [](const auto& ptr_a, const auto& ptr_b) {
631-
return *ptr_a == *ptr_b;
632-
};
633-
634605
if (lhs._n_vertices != rhs._n_vertices or lhs._n_edges != rhs._n_edges)
635606
return false;
636607

637608
if constexpr (traits::c_non_empty_properties<vertex_properties_type>)
638-
if (not std::ranges::equal(lhs._vertex_properties, rhs._vertex_properties, val_eq))
609+
if (lhs._vertex_properties != rhs._vertex_properties)
639610
return false;
640611

641612
if constexpr (traits::c_non_empty_properties<edge_properties_type>)
642-
if (not std::ranges::equal(lhs._edge_properties, rhs._edge_properties, val_eq))
613+
if (lhs._edge_properties != rhs._edge_properties)
643614
return false;
644615

645616
return lhs._impl == rhs._impl;
@@ -678,23 +649,11 @@ class graph final {
678649
using fmt_traits = io::detail::graph_fmt_traits<directional_tag>;
679650

680651
graph(const graph& other)
681-
: _n_vertices{other._n_vertices}, _n_edges{other._n_edges}, _impl{other._impl} {
682-
// Deep copy vertex properties
683-
if constexpr (traits::c_non_empty_properties<vertex_properties_type>) {
684-
this->_vertex_properties.reserve(other._vertex_properties.size());
685-
for (const auto& property : other._vertex_properties)
686-
this->_vertex_properties.push_back(
687-
std::make_unique<vertex_properties_type>(*property)
688-
);
689-
}
690-
691-
// Deep copy edge properties
692-
if constexpr (traits::c_non_empty_properties<edge_properties_type>) {
693-
this->_edge_properties.reserve(other._edge_properties.size());
694-
for (const auto& property : other._edge_properties)
695-
this->_edge_properties.push_back(std::make_unique<edge_properties_type>(*property));
696-
}
697-
}
652+
: _n_vertices{other._n_vertices},
653+
_n_edges{other._n_edges},
654+
_impl{other._impl},
655+
_vertex_properties{other._vertex_properties},
656+
_edge_properties{other._edge_properties} {}
698657

699658
// --- element validation ---
700659

@@ -731,6 +690,22 @@ class graph final {
731690
}
732691
}
733692

693+
// --- transformations ---
694+
695+
gl_attr_force_inline auto _create_vertex_descriptor() const noexcept
696+
requires(traits::c_empty_properties<vertex_properties_type>)
697+
{
698+
return [](const id_type id) { return vertex_type{id}; };
699+
}
700+
701+
gl_attr_force_inline auto _create_vertex_descriptor() const noexcept
702+
requires(traits::c_non_empty_properties<vertex_properties_type>)
703+
{
704+
return [&pmap = this->_vertex_properties](const id_type id) {
705+
return vertex_type{id, pmap[to_idx(id)]};
706+
};
707+
}
708+
734709
// --- I/O utility ---
735710

736711
struct concise_target_formatter {
@@ -898,13 +873,18 @@ class graph final {
898873
return is;
899874
}
900875

876+
// --- data members ---
877+
901878
size_type _n_vertices = 0uz;
902879
size_type _n_edges = 0uz;
903880

904-
[[no_unique_address]] vertex_properties_map_type _vertex_properties{};
905-
[[no_unique_address]] edge_properties_map_type _edge_properties{};
906-
907881
implementation_type _impl{};
882+
883+
/// @todo Replace mutability with proper const-correct getter overloads to ensure thread safety guarantees associated with the const qualifier
884+
[[no_unique_address]] mutable vertex_properties_map_type _vertex_properties{};
885+
886+
/// @todo Replace mutability with proper const-correct getter overloads to ensure thread safety guarantees associated with the const qualifier
887+
[[no_unique_address]] mutable edge_properties_map_type _edge_properties{};
908888
};
909889

910890
// --- general graph utility ---

0 commit comments

Comments
 (0)