Skip to content

Commit 2af8943

Browse files
committed
hyperedge list alignment
1 parent afa3fc9 commit 2af8943

5 files changed

Lines changed: 110 additions & 55 deletions

File tree

include/hgl/hypergraph_traits.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ template <
3737
type_traits::c_hypergraph_directional_tag DirectionalTag = undirected_t,
3838
type_traits::c_properties VertexProperties = types::empty_properties,
3939
type_traits::c_properties HyperedgeProperties = types::empty_properties>
40-
using adjacency_list_hg_traits =
41-
hypergraph_traits<DirectionalTag, VertexProperties, HyperedgeProperties, impl::adjacency_list_t>;
40+
using vertex_list_hg_traits =
41+
hypergraph_traits<DirectionalTag, VertexProperties, HyperedgeProperties, impl::vertex_list_t>;
4242

4343
template <
4444
type_traits::c_hypergraph_directional_tag DirectionalTag = undirected_t,
@@ -69,9 +69,9 @@ concept c_hyperedge_list_hg_traits =
6969
and std::same_as<typename TraitsType::implementation_tag, impl::hyperedge_list_t>;
7070

7171
template <typename TraitsType>
72-
concept c_adjacency_list_hg_traits =
72+
concept c_vertex_list_hg_traits =
7373
c_instantiation_of<TraitsType, hypergraph_traits>
74-
and std::same_as<typename TraitsType::implementation_tag, impl::adjacency_list_t>;
74+
and std::same_as<typename TraitsType::implementation_tag, impl::vertex_list_t>;
7575

7676
template <typename TraitsType>
7777
concept c_incidence_matrix_hg_traits =

include/hgl/impl/hyperedge_list.hpp

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "hgl/types/types.hpp"
88

99
#include <algorithm>
10+
#include <ranges>
1011
#include <vector>
1112

1213
#ifdef HGL_TESTING
@@ -45,11 +46,30 @@ class undirected_hyperedge_list final {
4546

4647
// --- vertex methods ---
4748

48-
gl_attr_force_inline void add_vertices(const types::size_type) const noexcept {}
49+
// clang-format off
50+
51+
gl_attr_force_inline void add_vertices([[maybe_unused]] const types::size_type) const noexcept {}
52+
53+
// clang-format on
4954

5055
void remove_vertex(const types::id_type vertex_id) noexcept {
51-
for (auto& hyperedge_vertices : this->_storage)
52-
this->_unbind_impl(hyperedge_vertices, vertex_id);
56+
for (auto& hyperedge : this->_storage)
57+
this->_unbind_impl(hyperedge, vertex_id);
58+
}
59+
60+
[[nodiscard]] auto incident_hyperedges(const types::id_type vertex_id) const noexcept {
61+
return std::views::iota(0uz, this->_storage.size())
62+
| std::views::filter([this, vertex_id](types::id_type hid) {
63+
return this->_are_bound_impl(this->_storage[hid], vertex_id);
64+
});
65+
}
66+
67+
[[nodiscard]] types::size_type degree(const types::id_type vertex_id) const noexcept {
68+
types::size_type deg = 0uz;
69+
for (const auto& hyperedge : this->_storage)
70+
if (this->_are_bound_impl(hyperedge, vertex_id))
71+
++deg;
72+
return deg;
5373
}
5474

5575
// --- hyperedge methods ---
@@ -62,26 +82,26 @@ class undirected_hyperedge_list final {
6282
this->_storage.erase(this->_storage.begin() + hyperedge_id);
6383
}
6484

65-
[[nodiscard]] gl_attr_force_inline types::size_type hyperedge_size(
66-
const types::id_type hyperedge_id
85+
[[nodiscard]] gl_attr_force_inline auto incident_vertices(const types::id_type hyperedge_id
6786
) const noexcept {
68-
return this->_storage[hyperedge_id].size();
87+
return std::views::all(this->_storage[hyperedge_id]);
6988
}
7089

71-
[[nodiscard]] gl_attr_force_inline auto hyperedge_vertices(const types::id_type hyperedge_id
90+
[[nodiscard]] gl_attr_force_inline types::size_type hyperedge_size(
91+
const types::id_type hyperedge_id
7292
) const noexcept {
73-
return std::views::all(this->_storage[hyperedge_id]);
93+
return this->_storage[hyperedge_id].size();
7494
}
7595

7696
// --- binding methods ---
7797

7898
void bind(const types::id_type hyperedge_id, const types::id_type vertex_id) noexcept {
79-
auto& hyperedge_vertices = this->_storage[hyperedge_id];
99+
auto& hyperedge = this->_storage[hyperedge_id];
80100

81101
// insert the id at the correct position to keep the vertex-id collection sorted
82-
const auto it = std::ranges::lower_bound(hyperedge_vertices, vertex_id);
83-
if (it == hyperedge_vertices.end() or *it != vertex_id)
84-
hyperedge_vertices.insert(it, vertex_id);
102+
const auto it = std::ranges::lower_bound(hyperedge, vertex_id);
103+
if (it == hyperedge.end() or *it != vertex_id)
104+
hyperedge.insert(it, vertex_id);
85105
}
86106

87107
gl_attr_force_inline void unbind(
@@ -90,24 +110,28 @@ class undirected_hyperedge_list final {
90110
this->_unbind_impl(this->_storage[hyperedge_id], vertex_id);
91111
}
92112

93-
[[nodiscard]] bool are_bound(const types::id_type hyperedge_id, const types::id_type vertex_id)
94-
const noexcept {
95-
auto& hyperedge_vertices = this->_storage[hyperedge_id];
96-
const auto vertex_it = std::ranges::lower_bound(hyperedge_vertices, vertex_id);
97-
return vertex_it != hyperedge_vertices.end() and *vertex_it == vertex_id;
113+
[[nodiscard]] gl_attr_force_inline bool are_bound(
114+
const types::id_type hyperedge_id, const types::id_type vertex_id
115+
) const noexcept {
116+
return this->_are_bound_impl(this->_storage[hyperedge_id], vertex_id);
98117
}
99118

100119
#ifdef HGL_TESTING
101120
friend struct hgl_testing::test_hyperedge_list;
102121
#endif
103122

104123
private:
105-
void _unbind_impl(
106-
hyperedge_storage_type& hyperedge_vertices, const types::id_type vertex_id
107-
) noexcept {
108-
const auto vertex_it = std::ranges::lower_bound(hyperedge_vertices, vertex_id);
109-
if (vertex_it != hyperedge_vertices.end() and *vertex_it == vertex_id)
110-
hyperedge_vertices.erase(vertex_it);
124+
void _unbind_impl(hyperedge_storage_type& hyperedge, const types::id_type vertex_id) noexcept {
125+
const auto vertex_it = std::ranges::lower_bound(hyperedge, vertex_id);
126+
if (vertex_it != hyperedge.end() and *vertex_it == vertex_id)
127+
hyperedge.erase(vertex_it);
128+
}
129+
130+
[[nodiscard]] bool _are_bound_impl(
131+
const hyperedge_storage_type& hyperedge, const types::id_type vertex_id
132+
) const noexcept {
133+
const auto vertex_it = std::ranges::lower_bound(hyperedge, vertex_id);
134+
return vertex_it != hyperedge.end() and *vertex_it == vertex_id;
111135
}
112136

113137
hypergraph_storage_type _storage;

include/hgl/impl/impl_tags.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace impl {
1212

1313
struct hyperedge_list_t {};
1414

15-
struct adjacency_list_t {};
15+
struct vertex_list_t {};
1616

1717
struct incidence_matrix_t {};
1818

@@ -22,7 +22,7 @@ namespace type_traits {
2222

2323
template <typename T>
2424
concept c_hypergraph_impl_tag =
25-
c_one_of<T, impl::hyperedge_list_t, impl::adjacency_list_t, impl::incidence_matrix_t>;
25+
c_one_of<T, impl::hyperedge_list_t, impl::vertex_list_t, impl::incidence_matrix_t>;
2626

2727
} // namespace type_traits
2828

tests/source/hgl/test_hyperedge_list.cpp

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -58,75 +58,72 @@ TEST_CASE_FIXTURE(
5858
CHECK_EQ(storage(sut).size(), constants::n_hyperedges - 1uz);
5959
}
6060

61-
TEST_CASE_FIXTURE(test_undirected_hyperedge_list, "hyperedge_size should return 0 by default") {
61+
TEST_CASE_FIXTURE(
62+
test_undirected_hyperedge_list, "incident_vertices should remove an empty view by default"
63+
) {
6264
sut_type sut{constants::n_vertices, constants::n_hyperedges};
6365
CHECK(std::ranges::all_of(constants::hyperedge_ids_view, [&sut](const auto hyperedge_id) {
64-
return sut.hyperedge_size(hyperedge_id) == 0uz;
66+
return std::ranges::empty(sut.incident_vertices(hyperedge_id));
6567
}));
6668
}
6769

68-
TEST_CASE_FIXTURE(
69-
test_undirected_hyperedge_list, "hyperedge_vertices should remove an empty view by default"
70-
) {
70+
TEST_CASE_FIXTURE(test_undirected_hyperedge_list, "hyperedge_size should return 0 by default") {
7171
sut_type sut{constants::n_vertices, constants::n_hyperedges};
7272
CHECK(std::ranges::all_of(constants::hyperedge_ids_view, [&sut](const auto hyperedge_id) {
73-
return std::ranges::empty(sut.hyperedge_vertices(hyperedge_id));
73+
return sut.hyperedge_size(hyperedge_id) == 0uz;
7474
}));
7575
}
7676

7777
TEST_CASE_FIXTURE(
7878
test_undirected_hyperedge_list,
79-
"bind(hyperedge, vertex) should add the vertex to the given hyperedge's storage only if the "
80-
"they are not bound"
79+
"bind should add the vertex to the given hyperedge's storage only if the they are not bound"
8180
) {
8281
sut_type sut{constants::n_vertices, constants::n_hyperedges};
83-
REQUIRE(std::ranges::empty(sut.hyperedge_vertices(constants::id1)));
82+
REQUIRE(std::ranges::empty(sut.incident_vertices(constants::id1)));
8483

8584
sut.bind(constants::id1, constants::id1);
86-
const auto vertices1 = sut.hyperedge_vertices(constants::id1) | std::ranges::to<std::vector>();
85+
const auto vertices1 = sut.incident_vertices(constants::id1) | std::ranges::to<std::vector>();
8786
CHECK_EQ(sut.hyperedge_size(constants::id1), 1uz);
8887
CHECK_EQ(std::ranges::size(vertices1), 1uz);
8988
CHECK(std::ranges::contains(vertices1, constants::id1));
9089
CHECK(std::ranges::equal(vertices1, storage(sut)[constants::id1]));
9190

9291
sut.bind(constants::id1, constants::id1);
93-
const auto vertices2 = sut.hyperedge_vertices(constants::id1) | std::ranges::to<std::vector>();
92+
const auto vertices2 = sut.incident_vertices(constants::id1) | std::ranges::to<std::vector>();
9493
CHECK_EQ(std::ranges::size(vertices2), 1uz);
9594
CHECK(std::ranges::equal(vertices2, vertices1));
9695
}
9796

9897
TEST_CASE_FIXTURE(
9998
test_undirected_hyperedge_list,
100-
"unbind(hyperedge, vertex) should remove the vertex from the given hyperedge's storage only if "
101-
"the they are bound"
99+
"unbind should remove the vertex from the given hyperedge's storage only if the they are bound"
102100
) {
103101
sut_type sut{constants::n_vertices, constants::n_hyperedges};
104-
REQUIRE(std::ranges::empty(sut.hyperedge_vertices(constants::id1)));
102+
REQUIRE(std::ranges::empty(sut.incident_vertices(constants::id1)));
105103

106104
sut.bind(constants::id1, constants::id1);
107-
const auto vertices1 = sut.hyperedge_vertices(constants::id1) | std::ranges::to<std::vector>();
105+
const auto vertices1 = sut.incident_vertices(constants::id1) | std::ranges::to<std::vector>();
108106
REQUIRE_EQ(sut.hyperedge_size(constants::id1), 1uz);
109107
REQUIRE(std::ranges::contains(vertices1, constants::id1));
110108

111109
sut.unbind(constants::id1, constants::id2);
112-
const auto vertices2 = sut.hyperedge_vertices(constants::id1) | std::ranges::to<std::vector>();
110+
const auto vertices2 = sut.incident_vertices(constants::id1) | std::ranges::to<std::vector>();
113111
REQUIRE_EQ(std::ranges::size(vertices2), 1uz);
114112
REQUIRE(std::ranges::equal(vertices2, vertices1));
115113

116114
sut.unbind(constants::id1, constants::id1);
117-
CHECK(std::ranges::empty(sut.hyperedge_vertices(constants::id1)));
115+
CHECK(std::ranges::empty(sut.incident_vertices(constants::id1)));
118116
}
119117

120118
TEST_CASE_FIXTURE(
121119
test_undirected_hyperedge_list,
122-
"are_bound(hyperedge, vertex) should return true only when the given vertex is present in the "
123-
"hyperedge's storage"
120+
"are_bound should return true only when the given vertex is present in the hyperedge's storage"
124121
) {
125122
sut_type sut{constants::n_vertices, constants::n_hyperedges};
126-
REQUIRE(std::ranges::empty(sut.hyperedge_vertices(constants::id1)));
123+
REQUIRE(std::ranges::empty(sut.incident_vertices(constants::id1)));
127124

128125
sut.bind(constants::id1, constants::id1);
129-
const auto vertices1 = sut.hyperedge_vertices(constants::id1) | std::ranges::to<std::vector>();
126+
const auto vertices1 = sut.incident_vertices(constants::id1) | std::ranges::to<std::vector>();
130127
REQUIRE_EQ(sut.hyperedge_size(constants::id1), 1uz);
131128
REQUIRE(std::ranges::contains(vertices1, constants::id1));
132129

@@ -135,6 +132,12 @@ TEST_CASE_FIXTURE(
135132
CHECK_FALSE(sut.are_bound(constants::id2, constants::id1));
136133
}
137134

135+
TEST_CASE_FIXTURE(test_undirected_hyperedge_list, "add_vertices should do nothing") {
136+
sut_type sut{};
137+
sut.add_vertices(constants::n_vertices);
138+
CHECK(storage(sut).empty());
139+
}
140+
138141
TEST_CASE_FIXTURE(
139142
test_undirected_hyperedge_list,
140143
"remove_vertex should unbind the given vertex from all hyperedges"
@@ -148,10 +151,38 @@ TEST_CASE_FIXTURE(
148151

149152
sut.remove_vertex(constants::id1);
150153
CHECK(std::ranges::all_of(constants::hyperedge_ids_view, [&sut](const auto hyperedge_id) {
151-
return std::ranges::empty(sut.hyperedge_vertices(hyperedge_id));
154+
return std::ranges::empty(sut.incident_vertices(hyperedge_id));
152155
}));
153156
}
154157

158+
TEST_CASE_FIXTURE(
159+
test_undirected_hyperedge_list,
160+
"incident_hyperedges should return a view of the vertex's incident hyperedge ids"
161+
) {
162+
sut_type sut{constants::n_vertices, constants::n_hyperedges};
163+
164+
constexpr auto vertex_id = constants::id1;
165+
REQUIRE(std::ranges::empty(sut.incident_hyperedges(vertex_id)));
166+
167+
for (const auto hyperedge_id : constants::hyperedge_ids_view)
168+
sut.bind(hyperedge_id, vertex_id);
169+
CHECK(std::ranges::equal(sut.incident_hyperedges(vertex_id), constants::hyperedge_ids_view));
170+
}
171+
172+
TEST_CASE_FIXTURE(
173+
test_undirected_hyperedge_list,
174+
"degree should return the number of the vertex's incident hyperedges"
175+
) {
176+
sut_type sut{constants::n_vertices, constants::n_hyperedges};
177+
178+
constexpr auto vertex_id = constants::id1;
179+
REQUIRE_EQ(sut.degree(vertex_id), 0uz);
180+
181+
for (const auto hyperedge_id : constants::hyperedge_ids_view)
182+
sut.bind(hyperedge_id, vertex_id);
183+
CHECK_EQ(sut.degree(vertex_id), constants::n_hyperedges);
184+
}
185+
155186
TEST_SUITE_END(); // test_hyperedge_list
156187

157188
} // namespace hgl_testing

tests/source/hgl/test_hypergraph.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,8 @@ TEST_CASE_TEMPLATE_INSTANTIATE(
371371
hypergraph_traits_template,
372372
hgl::hyperedge_list_hg_traits<hgl::undirected_t>, // undirected edge list
373373
hgl::hyperedge_list_hg_traits<hgl::bf_directed_t>, // bf-directed edge list
374-
hgl::adjacency_list_hg_traits<hgl::undirected_t>, // undirected adjacency list
375-
hgl::adjacency_list_hg_traits<hgl::bf_directed_t>, // bf-directed adjacency list
374+
hgl::vertex_list_hg_traits<hgl::undirected_t>, // undirected adjacency list
375+
hgl::vertex_list_hg_traits<hgl::bf_directed_t>, // bf-directed adjacency list
376376
hgl::incidence_matrix_hg_traits<hgl::undirected_t>, // undirected incidence matrix
377377
hgl::incidence_matrix_hg_traits<hgl::bf_directed_t> // bf-directed incidence matrix
378378
);
@@ -422,11 +422,11 @@ TEST_CASE_TEMPLATE_INSTANTIATE(
422422
hgl::bf_directed_t,
423423
hgl::types::name_property,
424424
hgl::types::name_property>, // bf-directed hyperedge list
425-
hgl::adjacency_list_hg_traits<
425+
hgl::vertex_list_hg_traits<
426426
hgl::undirected_t,
427427
hgl::types::name_property,
428428
hgl::types::name_property>, // undirected adjacency list
429-
hgl::adjacency_list_hg_traits<
429+
hgl::vertex_list_hg_traits<
430430
hgl::bf_directed_t,
431431
hgl::types::name_property,
432432
hgl::types::name_property>, // bf-directed adjacency list

0 commit comments

Comments
 (0)