From f5f5e0c7ad1055ac4b581f8192fb3c7d2a8b4cc5 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Tue, 28 Apr 2026 10:31:00 +0200 Subject: [PATCH 01/28] invalid_argument instead of out_of_range for invalid vertices and hyperedges --- include/hgl/hypergraph.hpp | 4 +- tests/source/hgl/test_hypergraph.cpp | 101 ++++++++++++++++----------- 2 files changed, 63 insertions(+), 42 deletions(-) diff --git a/include/hgl/hypergraph.hpp b/include/hgl/hypergraph.hpp index c88f1834..72aa4dc4 100644 --- a/include/hgl/hypergraph.hpp +++ b/include/hgl/hypergraph.hpp @@ -1239,7 +1239,7 @@ class hypergraph final { gl_attr_force_inline void _verify_vertex_id(const id_type vertex_id) const { if (not this->has_vertex(vertex_id)) - throw std::out_of_range(std::format("Got invalid vertex id [{}]", vertex_id)); + throw std::invalid_argument(std::format("Got invalid vertex id [{}]", vertex_id)); } void _remove_vertex_impl(const id_type vertex_id) { @@ -1270,7 +1270,7 @@ class hypergraph final { gl_attr_force_inline void _verify_hyperedge_id(const id_type hyperedge_id) const { if (not this->has_hyperedge(hyperedge_id)) - throw std::out_of_range(std::format("Got invalid hyperedge id [{}]", hyperedge_id)); + throw std::invalid_argument(std::format("Got invalid hyperedge id [{}]", hyperedge_id)); } void _remove_hyperedge_impl(const id_type hyperedge_id) { diff --git a/tests/source/hgl/test_hypergraph.cpp b/tests/source/hgl/test_hypergraph.cpp index bb299e68..40dc071e 100644 --- a/tests/source/hgl/test_hypergraph.cpp +++ b/tests/source/hgl/test_hypergraph.cpp @@ -77,7 +77,7 @@ TEST_CASE_TEMPLATE_DEFINE( REQUIRE(rng::equal(sut.vertices() | vw::transform(get_id), constants::vertex_ids_view)); REQUIRE(rng::equal(sut.vertex_ids(), constants::vertex_ids_view)); - CHECK_THROWS_AS(discard(sut.vertex(constants::out_of_rng_vid)), std::out_of_range); + CHECK_THROWS_AS(discard(sut.vertex(constants::out_of_rng_vid)), std::invalid_argument); } SUBCASE("a hypergraph constructed with n_vertices and n_hyperedges parameters should contain " @@ -89,12 +89,12 @@ TEST_CASE_TEMPLATE_DEFINE( REQUIRE(rng::equal(sut.vertices() | vw::transform(get_id), constants::vertex_ids_view)); REQUIRE(rng::equal(sut.vertex_ids(), constants::vertex_ids_view)); - CHECK_THROWS_AS(discard(sut.vertex(constants::out_of_rng_vid)), std::out_of_range); + CHECK_THROWS_AS(discard(sut.vertex(constants::out_of_rng_vid)), std::invalid_argument); REQUIRE(rng::equal(sut.hyperedges() | vw::transform(get_id), constants::hyperedge_ids_view) ); REQUIRE(rng::equal(sut.hyperedge_ids(), constants::hyperedge_ids_view)); - CHECK_THROWS_AS(discard(sut.hyperedge(constants::out_of_rng_eid)), std::out_of_range); + CHECK_THROWS_AS(discard(sut.hyperedge(constants::out_of_rng_eid)), std::invalid_argument); } // --- vertex modifiers --- @@ -166,7 +166,7 @@ TEST_CASE_TEMPLATE_DEFINE( sut.remove_vertex(constants::id1); REQUIRE_EQ(sut.n_vertices(), constants::n_vertices - 1uz); - CHECK_THROWS_AS(discard(sut.vertex(constants::n_vertices - 1uz)), std::out_of_range); + CHECK_THROWS_AS(discard(sut.vertex(constants::n_vertices - 1uz)), std::invalid_argument); } SUBCASE("remove_vertex(id) should do nothing if the given id is invalid") { @@ -183,7 +183,7 @@ TEST_CASE_TEMPLATE_DEFINE( sut.remove_vertex(constants::id1); REQUIRE_EQ(sut.n_vertices(), constants::n_vertices - 1uz); - CHECK_THROWS_AS(discard(sut.vertex(constants::n_vertices - 1uz)), std::out_of_range); + CHECK_THROWS_AS(discard(sut.vertex(constants::n_vertices - 1uz)), std::invalid_argument); } SUBCASE("remove_vertices(ids) should properly remove elements at given indices (ignoring " @@ -226,8 +226,10 @@ TEST_CASE_TEMPLATE_DEFINE( SUBCASE("vertex/at should throw if the given id is invalid") { sut_type sut{constants::n_vertices}; - CHECK_THROWS_AS(discard(sut.vertex(constants::out_of_rng_vid)), std::out_of_range); - CHECK_THROWS_AS(discard(sut.at(hgl::vertex, constants::out_of_rng_vid)), std::out_of_range); + CHECK_THROWS_AS(discard(sut.vertex(constants::out_of_rng_vid)), std::invalid_argument); + CHECK_THROWS_AS( + discard(sut.at(hgl::vertex, constants::out_of_rng_vid)), std::invalid_argument + ); } SUBCASE("vertex/at should return a vertex with the given id") { @@ -483,7 +485,9 @@ TEST_CASE_TEMPLATE_DEFINE( sut.remove_hyperedge(constants::id1); REQUIRE_EQ(sut.n_hyperedges(), constants::n_hyperedges - 1uz); - CHECK_THROWS_AS(discard(sut.hyperedge(constants::n_hyperedges - 1uz)), std::out_of_range); + CHECK_THROWS_AS( + discard(sut.hyperedge(constants::n_hyperedges - 1uz)), std::invalid_argument + ); } SUBCASE("remove_hyperedge(id) should do nothing if the given id is invalid") { @@ -500,7 +504,9 @@ TEST_CASE_TEMPLATE_DEFINE( sut.remove_hyperedge(constants::id1); REQUIRE_EQ(sut.n_hyperedges(), constants::n_hyperedges - 1uz); - CHECK_THROWS_AS(discard(sut.hyperedge(constants::n_hyperedges - 1uz)), std::out_of_range); + CHECK_THROWS_AS( + discard(sut.hyperedge(constants::n_hyperedges - 1uz)), std::invalid_argument + ); } SUBCASE("remove_hyperedges_from(ids) should properly remove elements at given indices " @@ -543,9 +549,9 @@ TEST_CASE_TEMPLATE_DEFINE( SUBCASE("hyperedge/at should throw if the given id is invalid") { sut_type sut{constants::n_vertices, constants::n_hyperedges}; - CHECK_THROWS_AS(discard(sut.hyperedge(constants::out_of_rng_eid)), std::out_of_range); + CHECK_THROWS_AS(discard(sut.hyperedge(constants::out_of_rng_eid)), std::invalid_argument); CHECK_THROWS_AS( - discard(sut.at(hgl::hyperedge, constants::out_of_rng_eid)), std::out_of_range + discard(sut.at(hgl::hyperedge, constants::out_of_rng_eid)), std::invalid_argument ); } @@ -579,74 +585,89 @@ TEST_CASE_TEMPLATE_DEFINE( if constexpr (std::same_as) { CHECK_THROWS_AS( - sut.bind(constants::out_of_rng_vid, constants::out_of_rng_eid), std::out_of_range + sut.bind(constants::out_of_rng_vid, constants::out_of_rng_eid), + std::invalid_argument + ); + CHECK_THROWS_AS( + sut.bind(constants::id1, constants::out_of_rng_eid), std::invalid_argument + ); + CHECK_THROWS_AS( + sut.bind(constants::out_of_rng_vid, constants::id1), std::invalid_argument ); - CHECK_THROWS_AS(sut.bind(constants::id1, constants::out_of_rng_eid), std::out_of_range); - CHECK_THROWS_AS(sut.bind(constants::out_of_rng_vid, constants::id1), std::out_of_range); } if constexpr (std::same_as) { CHECK_THROWS_AS( sut.bind_tail(constants::out_of_rng_vid, constants::out_of_rng_eid), - std::out_of_range + std::invalid_argument ); CHECK_THROWS_AS( - sut.bind_tail(constants::id1, constants::out_of_rng_eid), std::out_of_range + sut.bind_tail(constants::id1, constants::out_of_rng_eid), std::invalid_argument ); CHECK_THROWS_AS( - sut.bind_tail(constants::out_of_rng_vid, constants::id1), std::out_of_range + sut.bind_tail(constants::out_of_rng_vid, constants::id1), std::invalid_argument ); CHECK_THROWS_AS( sut.bind_head(constants::out_of_rng_vid, constants::out_of_rng_eid), - std::out_of_range + std::invalid_argument ); CHECK_THROWS_AS( - sut.bind_head(constants::id1, constants::out_of_rng_eid), std::out_of_range + sut.bind_head(constants::id1, constants::out_of_rng_eid), std::invalid_argument ); CHECK_THROWS_AS( - sut.bind_head(constants::out_of_rng_vid, constants::id1), std::out_of_range + sut.bind_head(constants::out_of_rng_vid, constants::id1), std::invalid_argument ); } CHECK_THROWS_AS( - sut.unbind(constants::out_of_rng_vid, constants::out_of_rng_eid), std::out_of_range + sut.unbind(constants::out_of_rng_vid, constants::out_of_rng_eid), std::invalid_argument + ); + CHECK_THROWS_AS( + sut.unbind(constants::id1, constants::out_of_rng_eid), std::invalid_argument + ); + CHECK_THROWS_AS( + sut.unbind(constants::out_of_rng_vid, constants::id1), std::invalid_argument ); - CHECK_THROWS_AS(sut.unbind(constants::id1, constants::out_of_rng_eid), std::out_of_range); - CHECK_THROWS_AS(sut.unbind(constants::out_of_rng_vid, constants::id1), std::out_of_range); CHECK_THROWS_AS( discard(sut.are_incident(constants::out_of_rng_vid, constants::out_of_rng_eid)), - std::out_of_range + std::invalid_argument ); CHECK_THROWS_AS( - discard(sut.are_incident(constants::id1, constants::out_of_rng_eid)), std::out_of_range + discard(sut.are_incident(constants::id1, constants::out_of_rng_eid)), + std::invalid_argument ); CHECK_THROWS_AS( - discard(sut.are_incident(constants::out_of_rng_vid, constants::id1)), std::out_of_range + discard(sut.are_incident(constants::out_of_rng_vid, constants::id1)), + std::invalid_argument ); if constexpr (std::same_as) { CHECK_THROWS_AS( discard(sut.is_tail(constants::out_of_rng_vid, constants::out_of_rng_eid)), - std::out_of_range + std::invalid_argument ); CHECK_THROWS_AS( - discard(sut.is_tail(constants::id1, constants::out_of_rng_eid)), std::out_of_range + discard(sut.is_tail(constants::id1, constants::out_of_rng_eid)), + std::invalid_argument ); CHECK_THROWS_AS( - discard(sut.is_tail(constants::out_of_rng_vid, constants::id1)), std::out_of_range + discard(sut.is_tail(constants::out_of_rng_vid, constants::id1)), + std::invalid_argument ); CHECK_THROWS_AS( discard(sut.is_head(constants::out_of_rng_vid, constants::out_of_rng_eid)), - std::out_of_range + std::invalid_argument ); CHECK_THROWS_AS( - discard(sut.is_head(constants::id1, constants::out_of_rng_eid)), std::out_of_range + discard(sut.is_head(constants::id1, constants::out_of_rng_eid)), + std::invalid_argument ); CHECK_THROWS_AS( - discard(sut.is_head(constants::out_of_rng_vid, constants::id1)), std::out_of_range + discard(sut.is_head(constants::out_of_rng_vid, constants::id1)), + std::invalid_argument ); } @@ -842,10 +863,10 @@ TEST_CASE_TEMPLATE_DEFINE( sut_type sut{constants::n_vertices, constants::n_hyperedges}; CHECK_THROWS_AS( discard(sut.incident_hyperedges(vertex_type{constants::out_of_rng_vid})), - std::out_of_range + std::invalid_argument ); CHECK_THROWS_AS( - discard(sut.degree(vertex_type{constants::out_of_rng_vid})), std::out_of_range + discard(sut.degree(vertex_type{constants::out_of_rng_vid})), std::invalid_argument ); GL_SUPPRESS_WARNING_END; @@ -974,11 +995,11 @@ TEST_CASE_TEMPLATE_DEFINE( sut_type sut{constants::n_vertices, constants::n_hyperedges}; CHECK_THROWS_AS( discard(sut.incident_vertices(hyperedge_type{constants::out_of_rng_eid})), - std::out_of_range + std::invalid_argument ); CHECK_THROWS_AS( discard(sut.hyperedge_size(hyperedge_type{constants::out_of_rng_eid})), - std::out_of_range + std::invalid_argument ); GL_SUPPRESS_WARNING_END; @@ -1312,7 +1333,7 @@ TEST_CASE_TEMPLATE_DEFINE( CHECK_EQ(sut.vertex_properties(id), std::format("vertex_{}", id)); } CHECK_THROWS_AS( - discard(sut.vertex_properties(constants::out_of_rng_vid)), std::out_of_range + discard(sut.vertex_properties(constants::out_of_rng_vid)), std::invalid_argument ); } @@ -1325,7 +1346,7 @@ TEST_CASE_TEMPLATE_DEFINE( CHECK_EQ(sut.hyperedge_properties(id), std::format("hyperedge_{}", id)); } CHECK_THROWS_AS( - discard(sut.hyperedge_properties(constants::out_of_rng_eid)), std::out_of_range + discard(sut.hyperedge_properties(constants::out_of_rng_eid)), std::invalid_argument ); } @@ -1481,7 +1502,7 @@ TEST_CASE_TEMPLATE_DEFINE( CHECK_EQ(sut.vertex_properties(id), std::format("vertex_{}", id)); } CHECK_THROWS_AS( - discard(sut.vertex_properties(constants::out_of_rng_vid)), std::out_of_range + discard(sut.vertex_properties(constants::out_of_rng_vid)), std::invalid_argument ); } @@ -1494,7 +1515,7 @@ TEST_CASE_TEMPLATE_DEFINE( CHECK_EQ(sut.hyperedge_properties(id), std::format("hyperedge_{}", id)); } CHECK_THROWS_AS( - discard(sut.hyperedge_properties(constants::out_of_rng_eid)), std::out_of_range + discard(sut.hyperedge_properties(constants::out_of_rng_eid)), std::invalid_argument ); } From 940eaa36c8d236dec8826c616e67b356cb12f07e Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Tue, 28 Apr 2026 10:40:29 +0200 Subject: [PATCH 02/28] aligned the naming of callback parameters --- include/hgl/algorithm/templates/bfs.hpp | 20 +++++++++---------- include/hgl/algorithm/templates/dfs.hpp | 20 +++++++++---------- .../algorithm/traversal/backward_search.hpp | 8 ++++---- .../traversal/breadth_first_search.hpp | 8 ++++---- .../traversal/depth_first_search.hpp | 8 ++++---- .../algorithm/traversal/forward_search.hpp | 8 ++++---- include/hgl/algorithm/util.hpp | 7 ++----- 7 files changed, 38 insertions(+), 41 deletions(-) diff --git a/include/hgl/algorithm/templates/bfs.hpp b/include/hgl/algorithm/templates/bfs.hpp index 01b62c13..90c65e27 100644 --- a/include/hgl/algorithm/templates/bfs.hpp +++ b/include/hgl/algorithm/templates/bfs.hpp @@ -15,20 +15,20 @@ template < traversal_direction Dir = traversal_direction::forward, traits::c_hypergraph H, traits::c_forward_range_of> InitQueueRangeType = std::vector>, - traits::c_optional_predicate&> VisitVertexPredicate = empty_callback, + traits::c_optional_predicate&> VisitPredicate = empty_callback, traits::c_optional_predicate&> VisitCallback = empty_callback, traits::c_optional_decision_predicate - TraverseHyperedgePred = empty_callback, - traits::c_decision_predicate&> EnqueueVertexPred = empty_callback, + TraverseHyperedgePredicate = empty_callback, + traits::c_decision_predicate&> EnqueuePredicate = empty_callback, traits::c_optional_callback&> PreVisitCallback = empty_callback, traits::c_optional_callback&> PostVisitCallback = empty_callback> bool bfs( const H& hypergraph, const InitQueueRangeType& initial_queue_content, - const VisitVertexPredicate& visit_vertex_pred = {}, + const VisitPredicate& visit_pred = {}, const VisitCallback& visit = {}, - const TraverseHyperedgePred& traverse_he_pred = {}, - const EnqueueVertexPred& enqueue_vertex_pred = {}, + const TraverseHyperedgePredicate& traverse_he_pred = {}, + const EnqueuePredicate& enqueue_pred = {}, const PreVisitCallback& pre_visit = {}, const PostVisitCallback& post_visit = {} ) { @@ -45,8 +45,8 @@ bool bfs( const search_node curr_node = q.front(); q.pop(); - if constexpr (not traits::c_empty_callback) - if (not visit_vertex_pred(curr_node)) + if constexpr (not traits::c_empty_callback) + if (not visit_pred(curr_node)) continue; if constexpr (not traits::c_empty_callback) @@ -57,7 +57,7 @@ bool bfs( return false; for (const auto he_id : policy::target_hyperedges(hypergraph, curr_node.vertex_id)) { - if constexpr (not traits::c_empty_callback) { + if constexpr (not traits::c_empty_callback) { const auto traverse = traverse_he_pred(he_id, curr_node.vertex_id); if (traverse == decision::abort) return false; @@ -70,7 +70,7 @@ bool bfs( continue; search_node tgt_node{target_id, curr_node.vertex_id, he_id}; - const auto enqueue = enqueue_vertex_pred(tgt_node); + const auto enqueue = enqueue_pred(tgt_node); if (enqueue == decision::abort) return false; if (enqueue) diff --git a/include/hgl/algorithm/templates/dfs.hpp b/include/hgl/algorithm/templates/dfs.hpp index 02dbb288..1988bd22 100644 --- a/include/hgl/algorithm/templates/dfs.hpp +++ b/include/hgl/algorithm/templates/dfs.hpp @@ -14,20 +14,20 @@ template < traversal_direction Dir = traversal_direction::forward, traits::c_hypergraph H, traits::c_forward_range_of> InitQueueRangeType = std::vector>, - traits::c_optional_predicate&> VisitVertexPredicate = empty_callback, + traits::c_optional_predicate&> VisitPredicate = empty_callback, traits::c_optional_predicate&> VisitCallback = empty_callback, traits::c_optional_decision_predicate - TraverseHyperedgePred = empty_callback, - traits::c_decision_predicate&> EnqueueVertexPred = empty_callback, + TraverseHyperedgePredicate = empty_callback, + traits::c_decision_predicate&> EnqueuePredicate = empty_callback, traits::c_optional_callback&> PreVisitCallback = empty_callback, traits::c_optional_callback&> PostVisitCallback = empty_callback> bool dfs( const H& hypergraph, const InitQueueRangeType& initial_queue_content, - const VisitVertexPredicate& visit_vertex_pred = {}, + const VisitPredicate& visit_pred = {}, const VisitCallback& visit = {}, - const TraverseHyperedgePred& traverse_he_pred = {}, - const EnqueueVertexPred& enqueue_vertex_pred = {}, + const TraverseHyperedgePredicate& traverse_he_pred = {}, + const EnqueuePredicate& enqueue_pred = {}, const PreVisitCallback& pre_visit = {}, const PostVisitCallback& post_visit = {} ) { @@ -44,8 +44,8 @@ bool dfs( const search_node curr_node = s.top(); s.pop(); - if constexpr (not traits::c_empty_callback) - if (not visit_vertex_pred(curr_node)) + if constexpr (not traits::c_empty_callback) + if (not visit_pred(curr_node)) continue; if constexpr (not traits::c_empty_callback) @@ -56,7 +56,7 @@ bool dfs( return false; for (const auto he_id : policy::target_hyperedges(hypergraph, curr_node.vertex_id)) { - if constexpr (not traits::c_empty_callback) { + if constexpr (not traits::c_empty_callback) { const auto traverse = traverse_he_pred(he_id, curr_node.vertex_id); if (traverse == decision::abort) return false; @@ -69,7 +69,7 @@ bool dfs( continue; search_node tgt_node{target_id, curr_node.vertex_id, he_id}; - const auto enqueue = enqueue_vertex_pred(tgt_node); + const auto enqueue = enqueue_pred(tgt_node); if (enqueue == decision::abort) return false; if (enqueue) diff --git a/include/hgl/algorithm/traversal/backward_search.hpp b/include/hgl/algorithm/traversal/backward_search.hpp index 84d97217..449739c1 100644 --- a/include/hgl/algorithm/traversal/backward_search.hpp +++ b/include/hgl/algorithm/traversal/backward_search.hpp @@ -39,10 +39,10 @@ result_type> backward_bfs( bfs( hypergraph, root_queue, - default_visit_vertex_predicate(visited_vertices), + default_visit_predicate(visited_vertices), default_visit_callback(visited_vertices, stree), blocking_traverse_hyperedge_predicate(tail_unvisited), - default_enqueue_vertex_predicate(visited_vertices), + default_enqueue_predicate(visited_vertices), pre_visit, post_visit ); @@ -80,10 +80,10 @@ result_type> backward_dfs( dfs( hypergraph, root_queue, - default_visit_vertex_predicate(visited_vertices), + default_visit_predicate(visited_vertices), default_visit_callback(visited_vertices, stree), blocking_traverse_hyperedge_predicate(tail_unvisited), - default_enqueue_vertex_predicate(visited_vertices), + default_enqueue_predicate(visited_vertices), pre_visit, post_visit ); diff --git a/include/hgl/algorithm/traversal/breadth_first_search.hpp b/include/hgl/algorithm/traversal/breadth_first_search.hpp index 06e0cbc2..987f50d7 100644 --- a/include/hgl/algorithm/traversal/breadth_first_search.hpp +++ b/include/hgl/algorithm/traversal/breadth_first_search.hpp @@ -32,10 +32,10 @@ result_type> breadth_first_search( bfs( hypergraph, init_range(root_vertex_id), - default_visit_vertex_predicate(visited_vertices), + default_visit_predicate(visited_vertices), default_visit_callback(visited_vertices, stree), default_traverse_hyperedge_predicate(visited_hyperedges), - default_enqueue_vertex_predicate(visited_vertices), + default_enqueue_predicate(visited_vertices), pre_visit, post_visit ); @@ -45,10 +45,10 @@ result_type> breadth_first_search( bfs( hypergraph, init_range(root_id), - default_visit_vertex_predicate(visited_vertices), + default_visit_predicate(visited_vertices), default_visit_callback(visited_vertices, stree), default_traverse_hyperedge_predicate(visited_hyperedges), - default_enqueue_vertex_predicate(visited_vertices), + default_enqueue_predicate(visited_vertices), pre_visit, post_visit ); diff --git a/include/hgl/algorithm/traversal/depth_first_search.hpp b/include/hgl/algorithm/traversal/depth_first_search.hpp index 77913dac..23312ec0 100644 --- a/include/hgl/algorithm/traversal/depth_first_search.hpp +++ b/include/hgl/algorithm/traversal/depth_first_search.hpp @@ -32,10 +32,10 @@ result_type> depth_first_search( dfs( hypergraph, init_range(root_vertex_id), - default_visit_vertex_predicate(visited_vertices), + default_visit_predicate(visited_vertices), default_visit_callback(visited_vertices, stree), default_traverse_hyperedge_predicate(visited_hyperedges), - default_enqueue_vertex_predicate(visited_vertices), + default_enqueue_predicate(visited_vertices), pre_visit, post_visit ); @@ -45,10 +45,10 @@ result_type> depth_first_search( dfs( hypergraph, init_range(root_id), - default_visit_vertex_predicate(visited_vertices), + default_visit_predicate(visited_vertices), default_visit_callback(visited_vertices, stree), default_traverse_hyperedge_predicate(visited_hyperedges), - default_enqueue_vertex_predicate(visited_vertices), + default_enqueue_predicate(visited_vertices), pre_visit, post_visit ); diff --git a/include/hgl/algorithm/traversal/forward_search.hpp b/include/hgl/algorithm/traversal/forward_search.hpp index e70d9b51..8b35a6b0 100644 --- a/include/hgl/algorithm/traversal/forward_search.hpp +++ b/include/hgl/algorithm/traversal/forward_search.hpp @@ -39,10 +39,10 @@ result_type> forward_bfs( bfs( hypergraph, root_queue, - default_visit_vertex_predicate(visited_vertices), + default_visit_predicate(visited_vertices), default_visit_callback(visited_vertices, stree), blocking_traverse_hyperedge_predicate(head_unvisited), - default_enqueue_vertex_predicate(visited_vertices), + default_enqueue_predicate(visited_vertices), pre_visit, post_visit ); @@ -80,10 +80,10 @@ result_type> forward_dfs( dfs( hypergraph, root_queue, - default_visit_vertex_predicate(visited_vertices), + default_visit_predicate(visited_vertices), default_visit_callback(visited_vertices, stree), blocking_traverse_hyperedge_predicate(head_unvisited), - default_enqueue_vertex_predicate(visited_vertices), + default_enqueue_predicate(visited_vertices), pre_visit, post_visit ); diff --git a/include/hgl/algorithm/util.hpp b/include/hgl/algorithm/util.hpp index 68d85c2c..de9415c6 100644 --- a/include/hgl/algorithm/util.hpp +++ b/include/hgl/algorithm/util.hpp @@ -34,8 +34,7 @@ template } template -[[nodiscard]] gl_attr_force_inline auto default_visit_vertex_predicate(std::vector& visited_v -) { +[[nodiscard]] gl_attr_force_inline auto default_visit_predicate(std::vector& visited_v) { return [&](const search_node& node) -> bool { return not visited_v[to_idx(node.vertex_id)]; }; @@ -76,9 +75,7 @@ template } template -[[nodiscard]] gl_attr_force_inline auto default_enqueue_vertex_predicate( - std::vector& visited_v -) { +[[nodiscard]] gl_attr_force_inline auto default_enqueue_predicate(std::vector& visited_v) { using return_t = std::conditional_t; return [&](const search_node& node) -> return_t { return return_t(not visited_v[to_idx(node.vertex_id)]); From 444ca03cd294f3aeec1260939562be1f11210af5 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Tue, 28 Apr 2026 11:03:51 +0200 Subject: [PATCH 03/28] init hgl doc pages --- docs/hgl/algorithms/overview.md | 1 + docs/hgl/algorithms/templates.md | 1 + docs/hgl/algorithms/traversal.md | 1 + docs/hgl/architecture.md | 1 + docs/hgl/conversions.md | 1 + docs/hgl/io.md | 1 + docs/hgl/properties.md | 1 + docs/hgl/quick_start.md | 1 + mkdocs.yml | 11 ++++++++++- uv.lock | 2 +- 10 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 docs/hgl/algorithms/overview.md create mode 100644 docs/hgl/algorithms/templates.md create mode 100644 docs/hgl/algorithms/traversal.md create mode 100644 docs/hgl/architecture.md create mode 100644 docs/hgl/conversions.md create mode 100644 docs/hgl/io.md create mode 100644 docs/hgl/properties.md create mode 100644 docs/hgl/quick_start.md diff --git a/docs/hgl/algorithms/overview.md b/docs/hgl/algorithms/overview.md new file mode 100644 index 00000000..2e35af60 --- /dev/null +++ b/docs/hgl/algorithms/overview.md @@ -0,0 +1 @@ +# Algorithms Overview diff --git a/docs/hgl/algorithms/templates.md b/docs/hgl/algorithms/templates.md new file mode 100644 index 00000000..c4dd9fec --- /dev/null +++ b/docs/hgl/algorithms/templates.md @@ -0,0 +1 @@ +# The Generic Templates diff --git a/docs/hgl/algorithms/traversal.md b/docs/hgl/algorithms/traversal.md new file mode 100644 index 00000000..c64653fb --- /dev/null +++ b/docs/hgl/algorithms/traversal.md @@ -0,0 +1 @@ +# Concrete Traversals diff --git a/docs/hgl/architecture.md b/docs/hgl/architecture.md new file mode 100644 index 00000000..81e67ccf --- /dev/null +++ b/docs/hgl/architecture.md @@ -0,0 +1 @@ +# Architecture & Basics diff --git a/docs/hgl/conversions.md b/docs/hgl/conversions.md new file mode 100644 index 00000000..d707152e --- /dev/null +++ b/docs/hgl/conversions.md @@ -0,0 +1 @@ +# Graph Conversions diff --git a/docs/hgl/io.md b/docs/hgl/io.md new file mode 100644 index 00000000..ce86cfe6 --- /dev/null +++ b/docs/hgl/io.md @@ -0,0 +1 @@ +# I/O Utility diff --git a/docs/hgl/properties.md b/docs/hgl/properties.md new file mode 100644 index 00000000..f727c2e5 --- /dev/null +++ b/docs/hgl/properties.md @@ -0,0 +1 @@ +# Hypergraph Properties diff --git a/docs/hgl/quick_start.md b/docs/hgl/quick_start.md new file mode 100644 index 00000000..05cf8c1f --- /dev/null +++ b/docs/hgl/quick_start.md @@ -0,0 +1 @@ +# Quick Start diff --git a/mkdocs.yml b/mkdocs.yml index d268a786..d17276f6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -26,7 +26,16 @@ nav: - Traits & Concepts: cpp-gl/group__GL-Traits.md - Generic Types: cpp-gl/group__GL-Types.md - General Utilities: cpp-gl/group__GL-Util.md - # - HGL: + - HGL: + - Quick Start: hgl/quick_start.md + - Architecture & Basics: hgl/architecture.md + - Hypergraph Properties: hgl/properties.md + - Graph Conversions: hgl/conversions.md + - Algorithms: + - Overview: hgl/algorithms/overview.md + - Generic Templates: hgl/algorithms/templates.md + - Concrete Traversals: hgl/algorithms/traversal.md + - I/O Utility: hgl/io.md - API Index: - Modules: cpp-gl/modules.md - Classes & Structs: cpp-gl/annotated.md diff --git a/uv.lock b/uv.lock index 8e918b1a..bb21e0cb 100644 --- a/uv.lock +++ b/uv.lock @@ -171,7 +171,7 @@ wheels = [ [[package]] name = "cpp-gl" -version = "0.1.0" +version = "2.0.0" source = { virtual = "." } dependencies = [ { name = "mike" }, From 592880e2bd90770f44587dea284a2939ff7c552b Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Tue, 28 Apr 2026 12:03:35 +0200 Subject: [PATCH 04/28] init hgl groups + first hgl doc comments --- docs/config/groups.dox | 83 +++++++++++++++++++++++++++++++-- include/gl/constants.hpp | 4 +- include/gl/types/properties.hpp | 6 +-- include/gl/util/ranges.hpp | 2 +- include/hgl/constants.hpp | 33 +++++++++++-- include/hgl/conversion.hpp | 4 +- include/hgl/traits.hpp | 18 ++++++- include/hgl/types.hpp | 69 ++++++++++++++++++++++++--- include/hgl/util.hpp | 33 +++++++++++-- mkdocs.yml | 8 ++++ 10 files changed, 233 insertions(+), 27 deletions(-) diff --git a/docs/config/groups.dox b/docs/config/groups.dox index c0b84628..e205da9f 100644 --- a/docs/config/groups.dox +++ b/docs/config/groups.dox @@ -44,12 +44,12 @@ /// @defgroup GL-IO I/O Utility /// @ingroup GL -/// @brief Comprehensive I/O stream support, parsing algorithms, and range-based formatting. +/// @brief I/O stream operations, formatting, and serialization of graph data. /// /// This group provides the necessary infrastructure to seamlessly serialize, deserialize, and -/// visualize complex graph structures. By offering a robust set of stream manipulators, formatting -/// traits, and configuration options, it allows users to easily translate in-memory graphs to -/// and from standard streams, files, or custom string representations. +/// visualize complex graph structures. By offering a robust set of stream manipulators, range +/// formatting utilities, and configuration options, it allows users to easily translate +/// in-memory graphs to and from standard streams, files, or custom string representations. /// @defgroup GL-Traits Traits & Concepts /// @ingroup GL @@ -83,3 +83,78 @@ /// operations, these utilities are completely independent of graph theory semantics. They provide /// a curated set of highly reusable, general-purpose tools that can smoothly integrate into any /// modern C++ codebase. + +/// @defgroup HGL Hypergraph Library (HGL) +/// @brief The top-level module defining the general-purpose C++ template hypergraph library. +/// +/// This module provides a comprehensive suite of memory-efficient hypergraph representations, +/// highly customizable algorithms, and robust I/O facilities. Designed strictly around modern +/// C++ paradigms, the library leverages templates and concepts to deliver an API that is fast, +/// generic, and type-safe. It supports both undirected hypergraphs and backward-forward (bf) +/// directed hypergraphs. + +/// @defgroup HGL-Core Core Hypergraph Components +/// @ingroup HGL +/// @brief Fundamental hypergraph data structures, element descriptors, and configuration tags. +/// +/// This group establishes the primary user interface for the HGL module, centered around the highly +/// configurable @ref hgl::hypergraph "hypergraph" class. It provides the core data types, such as vertex +/// and hyperedge descriptors, required to safely navigate and manipulate graph elements. +/// +/// Furthermore, this module defines the declarative tags (e.g., directedness and backend +/// implementation types) that dictate both the behavioral semantics and the underlying memory +/// layout of the instantiated hypergraphs. + +/// @defgroup HGL-Algorithm Hypergraph Algorithms +/// @ingroup HGL +/// @brief Generic hypergraph algorithms, search templates, and related utilities. +/// +/// This module provides hypergraph-specific algorithmic templates and implementations +/// (such as forward and backward searches) designed to natively navigate the complex, +/// high-order structure of hyperedges. + +/// @defgroup HGL-IO I/O Utility +/// @ingroup HGL +/// @brief I/O stream operations, formatting, and serialization of hypergraph data. +/// +/// Provides the necessary infrastructure to seamlessly serialize, deserialize, and visualize +/// complex hypergraph structures. It utilizes shared stream manipulation options and range +/// formatting utilities to easily translate in-memory hypergraphs to and from standard +/// streams and files. + +/// @defgroup HGL-Traits Traits & Concepts +/// @ingroup HGL +/// @brief Type traits, template constraints, and compile-time metaprogramming utilities. +/// +/// These components form the strict conceptual backbone of the HGL module, used extensively +/// to ensure internal type safety and correctness, while also constraining user-defined types +/// to required structural contracts. +/// +/// @section concepts_link Detailed Concept Specifications +/// For a detailed list of all HGL module's concepts and their formal requirements, please refer to +/// the [HGL Concepts Documentation](hgl_concepts.md#hgl-concepts-documentation) page. +/// +/// > [!NOTE] +/// > +/// > Because hypergraphs share the same underlying implementation design and mechanisms as standard +/// > graphs, they seamlessly reuse the same fundamental C++20 concepts. Therefore the HGL module +/// > pulls in all standard graph traits, concept checkers, and metaprogramming utilities from the +/// > GL module. To get an overview of these traits and concepts, please refer to the GL module's +/// > @ref GL-Traits documentation page. + +/// @defgroup HGL-Types Generic Types +/// @ingroup HGL +/// @brief Independent, high-performance data structures and utility types. +/// +/// This module houses a variety of general-purpose data structures and types. Though they are +/// utilized natively by hypergraph implementations, these components are carefully decoupled +/// from specific hypergraph logic, they can be seamlessly extracted and utilized in broader +/// contexts. + +/// @defgroup HGL-Util General Utilities +/// @ingroup HGL +/// @brief Practical, domain-agnostic C++ helpers, mathematical functions, and polyfills. +/// +/// These utilities provide a curated set of general-purpose tools that support the +/// library's internal algorithms and hypergraph modeling operations, completely +/// independent of strict hypergraph theory semantics. diff --git a/include/gl/constants.hpp b/include/gl/constants.hpp index c1171196..e0412216 100644 --- a/include/gl/constants.hpp +++ b/include/gl/constants.hpp @@ -38,7 +38,7 @@ struct initial_id_t { }; /// @ingroup GL GL-Core -/// @brief A constant instance of `initial_id_t` that can be used to represent the initial ID value of 0 for graph elements in a type-safe manner. +/// @brief An `initial_id_t` tag constant that can be used to represent the initial ID value of 0 for graph elements in a type-safe manner. /// /// ### Example Usage /// ```cpp @@ -96,7 +96,7 @@ struct invalid_id_t { }; /// @ingroup GL GL-Core -/// @brief A constant instance of `invalid_id_t` that can be used to represent the invalid ID value for graph elements in a type-safe manner. +/// @brief An `invalid_id_t` tag constant that can be used to represent the invalid ID value for graph elements in a type-safe manner. /// /// ### Example Usage /// ```cpp diff --git a/include/gl/types/properties.hpp b/include/gl/types/properties.hpp index c58c0210..01915fa9 100644 --- a/include/gl/types/properties.hpp +++ b/include/gl/types/properties.hpp @@ -17,16 +17,16 @@ namespace gl { /// @ingroup GL GL-Core -/// @brief A tag struct representing no user-defined properties. +/// @brief A stateless, empty structural tag representing an absence of properties. /// /// > [!IMPORTANT] /// > /// > This type is used as a default `properties_type` for graph components that do not require any user-defined data. -/// > It serves as a marker to indicate that the component is "property-less" and can be optimized accordingly. +/// > It serves as a marker to indicate that the component is *property-less* and can be optimized accordingly. struct empty_properties {}; /// @ingroup GL GL-Core -/// @brief A tag struct representing an empty property map. +/// @brief A stateless, empty structural tag indicating that the absence of a property map container. /// /// > [!NOTE] /// > diff --git a/include/gl/util/ranges.hpp b/include/gl/util/ranges.hpp index d0d4729f..6070dab8 100644 --- a/include/gl/util/ranges.hpp +++ b/include/gl/util/ranges.hpp @@ -13,7 +13,7 @@ namespace gl::util { /// @ingroup GL GL-Util -/// @brief Returns the size of a range. +/// @brief Safely determines the size of a range. /// /// This function returns the size of a range if it is a sized range, otherwise it computes the /// distance between the beginning and end of the range. Note that computing the distance for diff --git a/include/hgl/constants.hpp b/include/hgl/constants.hpp index 275c70d9..e58826ac 100644 --- a/include/hgl/constants.hpp +++ b/include/hgl/constants.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/constants.hpp +/// @brief Defines common constant values and types used across the HGL module. + #pragma once #include "gl/constants.hpp" @@ -9,12 +12,34 @@ namespace hgl { -using gl::initial_id; -using gl::initial_id_t; +/// @ingroup HGL-Core +/// @brief A constant representing the initial ID value of 0 for hypergraph elements. +/// @see gl::initial_id_v using gl::initial_id_v; -using gl::invalid_id; -using gl::invalid_id_t; +/// @ingroup HGL-Core +/// @brief A helper type that can be implicitly converted to the initial ID value of 0 for any valid ID type. +/// @see gl::initial_id_t +using gl::initial_id_t; + +/// @ingroup HGL-Core +/// @brief An `initial_id_t` tag constant that can be used to represent the initial ID value of 0 for hypergraph elements in a type-safe manner. +/// @see gl::initial_id +using gl::initial_id; + +/// @ingroup HGL-Core +/// @brief A constant representing the invalid ID value for hypergraph elements, defined as the maximum value of the specified ID type. +/// @see gl::invalid_id_v using gl::invalid_id_v; +/// @ingroup HGL-Core +/// @brief A helper type that can be implicitly converted to the invalid ID value for any valid ID type. +/// @see gl::invalid_id_t +using gl::invalid_id_t; + +/// @ingroup HGL-Core +/// @brief An `invalid_id_t` tag constant that can be used to represent the invalid ID value for hypergraph elements in a type-safe manner. +/// @see gl::invalid_id +using gl::invalid_id; + } // namespace hgl diff --git a/include/hgl/conversion.hpp b/include/hgl/conversion.hpp index 598a9519..7ce31509 100644 --- a/include/hgl/conversion.hpp +++ b/include/hgl/conversion.hpp @@ -231,7 +231,7 @@ template [[nodiscard]] G projection(const traits::c_undirected_hypergraph auto& h) { - using edge_vertices = hgl::homogeneous_pair; + using edge_vertices = homogeneous_pair; std::vector edges; for (const auto eid : h.hyperedge_ids()) { @@ -263,7 +263,7 @@ requires std::same_as [[nodiscard]] G projection(const traits::c_bf_directed_hypergraph auto& h) { - using edge_vertices = hgl::homogeneous_pair; + using edge_vertices = homogeneous_pair; std::vector edges; for (const auto eid : h.hyperedge_ids()) { diff --git a/include/hgl/traits.hpp b/include/hgl/traits.hpp index 1ea438f1..43eb770c 100644 --- a/include/hgl/traits.hpp +++ b/include/hgl/traits.hpp @@ -2,13 +2,27 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/traits.hpp +/// @brief Contains C++20 concepts and type traits used to constrain hypergraph library templates. + #pragma once #include "gl/traits.hpp" #include "hgl/types.hpp" -namespace hgl::traits { +namespace hgl { + +/// @ingroup HGL-Traits +/// @brief Traits and concepts for the HGL module. +/// +/// This namespace contains the definitions of all hypergraph-specifc concepts and type traits +/// used to constrain library types and pulls in all standard graph traits, concept checkers, +/// and metaprogramming utilities from `gl::traits`. Because hypergraphs share the same underlying +/// implementation design and mechanisms as standard graphs, they seamlessly reuse the same +/// fundamental C++20 concepts. +namespace traits { using namespace gl::traits; -} // namespace hgl::traits +} // namespace traits +} // namespace hgl diff --git a/include/hgl/types.hpp b/include/hgl/types.hpp index 9b34faf7..a4e2ef01 100644 --- a/include/hgl/types.hpp +++ b/include/hgl/types.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/types.hpp +/// @brief Core type definitions, generic data structures, and properties for the HGL module. + #pragma once #include "gl/types/core.hpp" @@ -13,27 +16,81 @@ namespace hgl { // --- core types --- -using gl::default_id_type; +/// @ingroup HGL-Types +/// @brief @copybrief gl::size_type +/// +/// Used primarily for indices, counts, and sizes of hypergraph components. +/// @see gl::size_type using gl::size_type; -using gl::to_diff; +/// @ingroup HGL-Types +/// @brief The default unsigned integer type used for vertex and hyperedge identifiers. +/// @see gl::default_id_type +using gl::default_id_type; + +/// @ingroup HGL-Types +/// @brief @copybrief gl::to_idx +/// @see gl::to_idx using gl::to_idx; +/// @ingroup HGL-Types +/// @brief @copybrief gl::to_diff +/// @see gl::to_diff +using gl::to_diff; + // --- generic data structures --- +/// @ingroup HGL-Types +/// @brief @copybrief gl::flat_jagged_vector +/// @see gl::flat_jagged_vector using gl::flat_jagged_vector; + +/// @ingroup HGL-Types +/// @brief @copybrief gl::flat_matrix +/// @see gl::flat_matrix using gl::flat_matrix; + +/// @ingroup HGL-Types +/// @brief @copybrief gl::homogeneous_pair +/// @see gl::homogeneous_pair using gl::homogeneous_pair; // --- property types --- -using gl::bin_color_value; -using gl::binary_color; -using gl::binary_color_property; -using gl::dynamic_properties; +/// @ingroup HGL-Core +/// @brief @copybrief gl::empty_properties +/// +/// > [!IMPORTANT] +/// > +/// > This type is used as a default `properties_type` for hypergraph components that do not require any user-defined data. +/// > It serves as a marker to indicate that the component is *property-less* and can be optimized accordingly. +/// +/// @see gl::empty_properties using gl::empty_properties; + +/// @ingroup HGL-Core +/// @brief @copybrief gl::empty_properties_map +/// +/// > [!NOTE] +/// > +/// > This type is used internally by the library to optimize storage for hypergraph components that have no properties. +/// +/// @see gl::empty_properties_map using gl::empty_properties_map; + +/// @ingroup HGL-Core +/// @brief @copybrief gl::name_property +/// @see gl::name_property using gl::name_property; + +/// @ingroup HGL-Core +/// @brief @copybrief gl::dynamic_properties +/// @see gl::dynamic_properties +using gl::dynamic_properties; + +/// @ingroup HGL-Core +/// @brief A property struct providing arithmetic weight for hyperedges or vertices. +/// @see gl::weight_property using gl::weight_property; } // namespace hgl diff --git a/include/hgl/util.hpp b/include/hgl/util.hpp index cad5c1f2..17bddef3 100644 --- a/include/hgl/util.hpp +++ b/include/hgl/util.hpp @@ -2,16 +2,43 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/util.hpp +/// @brief Defines utility functions and views for working with C++20 ranges in the HGL module. + #pragma once #include "gl/util/ranges.hpp" namespace hgl::util { +/// @ingroup HGL-Util +/// @brief @copybrief gl::util::range_size +/// @see gl::util::range_size +using gl::util::range_size; + +/// @ingroup HGL-Util +/// @brief @copybrief gl::util::is_constant +/// @see gl::util::is_constant +using gl::util::is_constant; + +/// @ingroup HGL-Util +/// @brief @copybrief gl::util::all_equal +/// @see gl::util::all_equal using gl::util::all_equal; -using gl::util::concat; + +/// @ingroup HGL-Util +/// @brief @copybrief gl::util::concat_view +/// @see gl::util::concat_view using gl::util::concat_view; -using gl::util::is_constant; -using gl::util::range_size; + +/// @ingroup HGL-Util +/// @brief @copybrief gl::util::concat_fn +/// @see gl::util::concat_fn +using gl::util::concat_fn; + +/// @ingroup HGL-Util +/// @brief @copybrief gl::util::concat +/// @see gl::util::concat +using gl::util::concat; } // namespace hgl::util diff --git a/mkdocs.yml b/mkdocs.yml index d17276f6..c4b096a9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -36,6 +36,14 @@ nav: - Generic Templates: hgl/algorithms/templates.md - Concrete Traversals: hgl/algorithms/traversal.md - I/O Utility: hgl/io.md + - API Reference: + - Overview: cpp-gl/group__HGL.md + - Core Components: cpp-gl/group__HGL-Core.md + - Algorithms: cpp-gl/group__HGL-Algorithm.md + - I/O Utility: cpp-gl/group__HGL-IO.md + - Traits & Concepts: cpp-gl/group__HGL-Traits.md + - Generic Types: cpp-gl/group__HGL-Types.md + - General Utilities: cpp-gl/group__HGL-Util.md - API Index: - Modules: cpp-gl/modules.md - Classes & Structs: cpp-gl/annotated.md From 46be8f98f70ec23bf4bd914ec987b831b3b1fcc6 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Tue, 28 Apr 2026 13:25:13 +0200 Subject: [PATCH 05/28] hypergraph elements and dir tags docs --- include/gl/edge_descriptor.hpp | 2 - include/gl/vertex_descriptor.hpp | 6 +- include/hgl/directional_tags.hpp | 23 +++++ include/hgl/hypergraph_elements.hpp | 147 +++++++++++++++++++++++++++- include/hgl/traits.hpp | 1 - include/hgl/types.hpp | 41 ++++---- 6 files changed, 193 insertions(+), 27 deletions(-) diff --git a/include/gl/edge_descriptor.hpp b/include/gl/edge_descriptor.hpp index 1db055f5..70edafec 100644 --- a/include/gl/edge_descriptor.hpp +++ b/include/gl/edge_descriptor.hpp @@ -22,8 +22,6 @@ namespace gl { /// @ingroup GL GL-Core /// @brief A lightweight wrapper representing a graph edge with its endpoints and optional properties. /// -/// **Module:** Part of the @ref GL-Core "Core Graph Components" group. -/// /// The `edge_descriptor` class provides a type-safe and efficient way to represent /// edges in both directed and undirected graph structures. It encapsulates the unique /// identifier of the edge, its source and target vertices, and optional property data. diff --git a/include/gl/vertex_descriptor.hpp b/include/gl/vertex_descriptor.hpp index ddda49eb..b293c954 100644 --- a/include/gl/vertex_descriptor.hpp +++ b/include/gl/vertex_descriptor.hpp @@ -23,8 +23,6 @@ namespace gl { /// @ingroup GL GL-Core /// @brief A lightweight wrapper around a vertex identifier with optional properties. /// -/// **Module:** Part of the @ref GL-Core "Core Graph Components" group. -/// /// The `vertex_descriptor` class provides a type-safe and efficient way to represent /// vertices in graph structures. It acts as a lightweight wrapper that combines /// a unique identifier with optional property data, ensuring safe access and @@ -32,7 +30,7 @@ namespace gl { /// /// > [!WARNING] This class is not intended to be instantiated directly. /// > -/// > Instead, `vertex_descriptor` objects should be retrieved from the @ref gl::graph class instance that owns the given vertex. +/// > Instead, `vertex_descriptor` objects should be retrieved from the @ref gl::graph "graph" class instance that owns the given vertex. /// /// ### Example Usage /// ```cpp @@ -49,7 +47,7 @@ namespace gl { /// /// 1\. Apply the stream manipulator to ensure custom property data is included in the output. /// -/// 2\. Use the arrow operator `->` to read custom properties attached to the vertex. +/// 2\. Use the arrow operator `->` to read custom properties attached to the vertex (assuming the vertex properties type contains `parent` and `level` members). /// /// 3\. Access another vertex via the `graph` using its ID, and modify the current vertex's properties. /// diff --git a/include/hgl/directional_tags.hpp b/include/hgl/directional_tags.hpp index 517ed337..1235ceeb 100644 --- a/include/hgl/directional_tags.hpp +++ b/include/hgl/directional_tags.hpp @@ -2,22 +2,45 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/directional_tags.hpp +/// @brief Defines tag types used to specify the directionality of a hypergraph. + #pragma once #include "hgl/traits.hpp" namespace hgl { +/// @ingroup HGL-Core +/// @brief Tag type specifying that a hypergraph is undirected. +/// +/// In an undirected hypergraph, a hyperedge is strictly defined as a set of incident vertices, +/// with no distinction between origin and destination. struct undirected_t { + /// @brief Self type identity for internal metaprogramming use. using type = std::type_identity_t; }; +/// @ingroup HGL-Core +/// @brief Tag type specifying that a hypergraph is backward-forward (BF) directed. +/// +/// In a BF-directed hypergraph, each hyperedge maps a distinct set of *tail* vertices (origins) +/// to a distinct set of *head* vertices (destinations). struct bf_directed_t { + /// @brief Self type identity for internal metaprogramming use. using type = std::type_identity_t; }; namespace traits { +/// @ingroup HGL-Traits +/// @brief Validates if a type is a valid hypergraph directional tag. +/// +/// The valid hypergraph directional tags are @ref hgl::undirected_t "undirected_t" and +/// @ref hgl::bf_directed_t "bf_directed_t". This concept is used to constrain template +/// parameters intended to specify the directionality of a hypergraph. +/// +/// @tparam T The type to evaluate against the concept. template concept c_hypergraph_directional_tag = c_one_of; diff --git a/include/hgl/hypergraph_elements.hpp b/include/hgl/hypergraph_elements.hpp index 231f0071..bef1f593 100644 --- a/include/hgl/hypergraph_elements.hpp +++ b/include/hgl/hypergraph_elements.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/hypergraph_elements.hpp +/// @brief Defines the hyperedge_descriptor class, element tags, and aliases for hypergraph elements. + #pragma once #include "gl/traits.hpp" @@ -16,35 +19,128 @@ namespace hgl { // --- hypergraph elements --- -using gl::vertex_descriptor; - +/// @ingroup HGL-Core +/// @brief Type alias adapting the standard graph vertex descriptor for hypergraphs. +/// +/// Because hypergraphs and standard graphs share the same fundamental vertex representation, +/// this alias imports the @ref gl::vertex_descriptor type into the HGL module. It provides a +/// type-safe wrapper combining a unique identifier with optional property data. +/// +/// > [!WARNING] This class is not intended to be instantiated directly. +/// > +/// > Instead, `vertex_descriptor` objects should be retrieved from the @ref hgl::hypergraph class instance that owns the given vertex. +/// +/// ### Example Usage +/// ```cpp +/// std::cout << gl::io::verbose << gl::io::with_vertex_properties; // (1)! +/// +/// for (const auto& vertex : hypergraph.vertices()) { +/// const auto deg = hypergraph.degree(vertex); // (2)! +/// +/// if (deg == 0uz) +/// vertex->description = "Isolated node"; // (3)! +/// else +/// vertex->description = std::format("Connected to {} hyperedges", deg); +/// +/// std::cout << "Vertex details: " << vertex << '\n'; +/// } +/// ``` +/// +/// 1. Stream manipulators inject persistent formatting state globally before iterating. +/// 2. Use the owning hypergraph to query topological information about the vertex. +/// 3. Safely access and modify the underlying properties via the `->` operator (assuming the vertex properties type contains a `description` member). +/// +/// ### Template Parameters +/// | Parameter | Description | Default | Constraint | +/// | :-------- | :--- | :--- | :--- | +/// | Properties | The type of property data attached to the vertex. | @ref hgl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | IdType | The underlying integer type used for the IDs. | @ref hgl::default_id_type "default_id_type" | [**c_id_type**](gl_concepts.md#gl-traits-c-id-type) | +/// +/// ### See Also +/// - @ref gl::vertex_descriptor for the full definition of the type. +/// - @ref hgl::hyperedge_descriptor "hyperedge_descriptor" for the corresponding hyperedge wrapper class. +/// - @ref hgl::hypergraph "hypergraph" for the owning hypergraph class that manages vertex descriptors. +template < + traits::c_properties Properties = empty_properties, + traits::c_id_type IdType = default_id_type> +using vertex_descriptor = gl::vertex_descriptor; + +/// @ingroup HGL-Core +/// @brief A lightweight wrapper representing a hypergraph edge with optional properties. +/// +/// The `hyperedge_descriptor` class provides a type-safe and efficient way to represent +/// hyperedges in hypergraph structures. It acts as a lightweight wrapper that encapsulates +/// the unique identifier of the hyperedge and its optional property data, ensuring safe +/// access and comparison operations. +/// +/// > [!WARNING] This class is not intended to be instantiated directly. +/// > +/// > Instead, `hyperedge_descriptor` objects should be retrieved from the @ref hgl::hypergraph class instance that owns the given hyperedge. +/// +/// ### Example Usage +/// ```cpp +/// std::cout << gl::io::verbose << gl::io::with_hyperedge_properties; // (1)! +/// +/// for (const auto& hyperedge : hypergraph.hyperedges()) { +/// const auto size = hypergraph.hyperedge_size(hyperedge); // (2)! +/// hyperedge->weight = static_cast(size) * 1.5; // (3)! +/// std::cout << "Hyperedge details: " << hyperedge << '\n'; +/// } +/// ``` +/// +/// 1. Stream manipulators like `with_hyperedge_properties` apply persistently to `std::cout`. +/// 2. Use the owning hypergraph to query topological information about the hyperedge. +/// 3. Update the payload properties through the overloaded `->` operator (assuming the hyperedge properties type contains a `weight` member). +/// +/// ### Template Parameters +/// | Parameter | Description | Default | Constraint | +/// | :-------- | :--- | :--- | :--- | +/// | Properties | The type of property data attached to the hyperedge. | @ref hgl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | IdType | The underlying integer type used for the IDs. | @ref hgl::default_id_type "default_id_type" | [**c_id_type**](gl_concepts.md#gl-traits-c-id-type) | +/// +/// ### See Also +/// - @ref hgl::vertex_descriptor "vertex_descriptor" for the corresponding vertex wrapper class. +/// - @ref hgl::hypergraph "hypergraph" for the owning hypergraph class that manages hyperedge descriptors. template < traits::c_properties Properties = empty_properties, traits::c_id_type IdType = default_id_type> class hyperedge_descriptor final { public: + /// @brief Self type alias. using type = hyperedge_descriptor; + /// @brief The underlying integer type used for the hyperedge identifier. using id_type = IdType; + /// @brief The type of property data attached to the hyperedge. using properties_type = Properties; + /// @brief Default constructor creating an invalid hyperedge descriptor. hyperedge_descriptor() { *this = hyperedge_descriptor::invalid(); } + /// @brief Constructs a property-less hyperedge descriptor from a raw ID. + /// @param id The raw identifier of the hyperedge. explicit hyperedge_descriptor(const id_type id) requires(traits::c_empty_properties) : _id(id) {} + /// @brief Constructs a hyperedge descriptor binding an ID to its properties. + /// @param id The raw identifier of the hyperedge. + /// @param properties A reference to the underlying properties payload. explicit hyperedge_descriptor(const id_type id, properties_type& properties) requires(traits::c_non_empty_properties) : _id(id), _properties(properties) {} + /// @brief Returns a special descriptor representing an invalid or uninitialized property-less hyperedge. + /// @return An invalid `hyperedge_descriptor`. [[nodiscard]] gl_attr_force_inline static hyperedge_descriptor invalid() noexcept requires(traits::c_empty_properties) { return hyperedge_descriptor(invalid_id); } + /// @brief Returns a special descriptor representing an invalid or uninitialized hyperedge with properties. + /// @return An invalid `hyperedge_descriptor`. [[nodiscard]] gl_attr_force_inline static hyperedge_descriptor invalid() noexcept requires(traits::c_non_empty_properties) { @@ -52,36 +148,56 @@ class hyperedge_descriptor final { return hyperedge_descriptor(invalid_id, invalid_properties); } + /// @brief Default copy constructor. hyperedge_descriptor(const hyperedge_descriptor&) = default; + /// @brief Default copy assignment operator. hyperedge_descriptor& operator=(const hyperedge_descriptor&) = default; + /// @brief Default move constructor. hyperedge_descriptor(hyperedge_descriptor&&) noexcept = default; + /// @brief Default move assignment operator. hyperedge_descriptor& operator=(hyperedge_descriptor&&) noexcept = default; + /// @brief Default destructor. ~hyperedge_descriptor() = default; + /// @brief Compares two hyperedge descriptors for equality. + /// @param other The descriptor to compare against. + /// @return `true` if both descriptors hold the same ID, `false` otherwise. [[nodiscard]] bool operator==(const hyperedge_descriptor& other) const noexcept { return this->_id == other._id; } + /// @brief Contextually converts the descriptor to a boolean. + /// @return `true` if the descriptor is valid, `false` otherwise. [[nodiscard]] gl_attr_force_inline operator bool() const noexcept { return this->is_valid(); } + /// @brief Compares two hyperedge descriptors to establish a strict ordering based on their IDs. + /// @param other The descriptor to compare against. + /// @return The result of the three-way comparison between the underlying IDs. [[nodiscard]] gl_attr_force_inline std::strong_ordering operator<=>( const hyperedge_descriptor& other ) const noexcept { return this->_id <=> other._id; } + /// @brief Checks if the descriptor represents a valid hyperedge. + /// @return `true` if the underlying ID is not the invalid ID constant, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool is_valid() const noexcept { return this->_id != invalid_id; } + /// @brief Retrieves the raw underlying ID of the hyperedge. + /// @return The hyperedge's integer ID. [[nodiscard]] gl_attr_force_inline id_type id() const noexcept { return this->_id; } + /// @brief Retrieves a reference to the property payload attached to the hyperedge. + /// @return A mutable reference to the underlying properties. + /// @throws std::logic_error If the descriptor is invalid. [[nodiscard]] gl_attr_force_inline properties_type& properties() const requires(traits::c_non_empty_properties) { @@ -89,6 +205,9 @@ class hyperedge_descriptor final { return this->_properties.get(); } + /// @brief Arrow operator providing direct access to the hyperedge's properties. + /// @return A pointer to the underlying properties. + /// @throws std::logic_error If the descriptor is invalid. [[nodiscard]] gl_attr_force_inline properties_type* operator->() const requires(traits::c_non_empty_properties) { @@ -96,6 +215,9 @@ class hyperedge_descriptor final { return &this->_properties.get(); } + /// @brief Dereference operator providing direct access to the hyperedge's properties. + /// @return A reference to the underlying properties. + /// @throws std::logic_error If the descriptor is invalid. [[nodiscard]] gl_attr_force_inline properties_type& operator*() const requires(traits::c_non_empty_properties) { @@ -103,6 +225,13 @@ class hyperedge_descriptor final { return this->_properties.get(); } + /// @brief Serializes the hyperedge descriptor to an output stream. + /// + /// Depending on active stream flags, this outputs the descriptor in verbose or concise formats. + /// + /// @param os The target output stream. + /// @param hyperedge The hyperedge descriptor to format. + /// @return The stream reference for chaining. friend std::ostream& operator<<(std::ostream& os, const hyperedge_descriptor& hyperedge) { using enum io::detail::option_bit; @@ -154,15 +283,29 @@ class hyperedge_descriptor final { // --- hypergraph element tags --- +/// @ingroup HGL-Core +/// @brief Tag type representing a vertex element in a hypergraph. struct vertex_t {}; +/// @ingroup HGL-Core +/// @brief Tag type representing a hyperedge element in a hypergraph. struct hyperedge_t {}; +/// @ingroup HGL-Core +/// @brief A constant instance of `vertex_t` used for tagging and generic dispatching. inline constexpr vertex_t vertex{}; +/// @ingroup HGL-Core +/// @brief A constant instance of `hyperedge_t` used for tagging and generic dispatching. inline constexpr hyperedge_t hyperedge{}; namespace traits { +/// @ingroup HGL-Traits +/// @brief Validates if a type is a valid hypergraph element tag. +/// +/// The valid hypergraph element tags are @ref hgl::vertex_t "vertex_t" and @ref hgl::hyperedge_t "hyperedge_t". +/// +/// @tparam T The type to evaluate against the concept. template concept c_hypergraph_element_tag = c_one_of; diff --git a/include/hgl/traits.hpp b/include/hgl/traits.hpp index 43eb770c..2698ce44 100644 --- a/include/hgl/traits.hpp +++ b/include/hgl/traits.hpp @@ -8,7 +8,6 @@ #pragma once #include "gl/traits.hpp" -#include "hgl/types.hpp" namespace hgl { diff --git a/include/hgl/types.hpp b/include/hgl/types.hpp index a4e2ef01..dc6ea549 100644 --- a/include/hgl/types.hpp +++ b/include/hgl/types.hpp @@ -11,6 +11,7 @@ #include "gl/types/flat_jagged_vector.hpp" #include "gl/types/flat_matrix.hpp" #include "gl/types/properties.hpp" +#include "hgl/traits.hpp" namespace hgl { @@ -21,12 +22,12 @@ namespace hgl { /// /// Used primarily for indices, counts, and sizes of hypergraph components. /// @see gl::size_type -using gl::size_type; +using size_type = gl::size_type; /// @ingroup HGL-Types /// @brief The default unsigned integer type used for vertex and hyperedge identifiers. /// @see gl::default_id_type -using gl::default_id_type; +using default_id_type = gl::default_id_type; /// @ingroup HGL-Types /// @brief @copybrief gl::to_idx @@ -42,18 +43,21 @@ using gl::to_diff; /// @ingroup HGL-Types /// @brief @copybrief gl::flat_jagged_vector -/// @see gl::flat_jagged_vector -using gl::flat_jagged_vector; +/// @see gl::flat_jagged_vector for the full type definition +template +using flat_jagged_vector = gl::flat_jagged_vector; /// @ingroup HGL-Types /// @brief @copybrief gl::flat_matrix -/// @see gl::flat_matrix -using gl::flat_matrix; +/// @see gl::flat_matrix for the full type definition +template +using flat_matrix = gl::flat_matrix; /// @ingroup HGL-Types /// @brief @copybrief gl::homogeneous_pair -/// @see gl::homogeneous_pair -using gl::homogeneous_pair; +/// @see gl::homogeneous_pair for the full type definition +template +using homogeneous_pair = gl::homogeneous_pair; // --- property types --- @@ -65,8 +69,8 @@ using gl::homogeneous_pair; /// > This type is used as a default `properties_type` for hypergraph components that do not require any user-defined data. /// > It serves as a marker to indicate that the component is *property-less* and can be optimized accordingly. /// -/// @see gl::empty_properties -using gl::empty_properties; +/// @see gl::empty_properties for the full type definition +using empty_properties = gl::empty_properties; /// @ingroup HGL-Core /// @brief @copybrief gl::empty_properties_map @@ -75,22 +79,23 @@ using gl::empty_properties; /// > /// > This type is used internally by the library to optimize storage for hypergraph components that have no properties. /// -/// @see gl::empty_properties_map -using gl::empty_properties_map; +/// @see gl::empty_properties_map for the full type definition +using empty_properties_map = gl::empty_properties_map; /// @ingroup HGL-Core /// @brief @copybrief gl::name_property -/// @see gl::name_property -using gl::name_property; +/// @see gl::name_property for the full type definition +using name_property = gl::name_property; /// @ingroup HGL-Core /// @brief @copybrief gl::dynamic_properties -/// @see gl::dynamic_properties -using gl::dynamic_properties; +/// @see gl::dynamic_properties for the full type definition +using dynamic_properties = gl::dynamic_properties; /// @ingroup HGL-Core /// @brief A property struct providing arithmetic weight for hyperedges or vertices. -/// @see gl::weight_property -using gl::weight_property; +/// @see gl::weight_property for the full type definition +template +using weight_property = gl::weight_property; } // namespace hgl From b6b2ec5a9a1699ca7fe4c8dfa7ba43b4e8ca46b1 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Tue, 28 Apr 2026 14:28:40 +0200 Subject: [PATCH 06/28] structural tags + hypergraph traits docs --- include/hgl/decl/impl_tags.hpp | 34 +++++++++ include/hgl/hypergraph_traits.hpp | 111 ++++++++++++++++++++++++++++++ include/hgl/impl/impl_tags.hpp | 45 ++++++++++++ include/hgl/impl/layout_tags.hpp | 41 +++++++++++ 4 files changed, 231 insertions(+) diff --git a/include/hgl/decl/impl_tags.hpp b/include/hgl/decl/impl_tags.hpp index 88efd474..4f452a31 100644 --- a/include/hgl/decl/impl_tags.hpp +++ b/include/hgl/decl/impl_tags.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/decl/impl_tags.hpp +/// @brief Forward declares implementation tags for different hypergraph representations. + #pragma once #include "hgl/impl/layout_tags.hpp" @@ -35,26 +38,57 @@ struct flat_matrix_t; namespace traits { +/// @ingroup HGL-Traits +/// @brief Validates if a type is an instantiation of the @ref hgl::impl::list_t "impl::list_t" tag. +/// @tparam T The type to evaluate against the concept. template concept c_hypergraph_list_impl = c_instantiation_of; +/// @ingroup HGL-Traits +/// @brief Validates if a type is an instantiation of the @ref hgl::impl::flat_list_t "impl::flat_list_t" tag. +/// @tparam T The type to evaluate against the concept. template concept c_hypergraph_flat_list_impl = c_instantiation_of; +/// @ingroup HGL-Traits +/// @brief Validates if a type is a valid incidence list implementation tag (either standard or flattened). +/// @tparam T The type to evaluate against the concept. template concept c_hypergraph_incidence_list_impl = c_hypergraph_list_impl or c_hypergraph_flat_list_impl; +/// @ingroup HGL-Traits +/// @brief Validates if a type is an instantiation of the @ref hgl::impl::matrix_t "impl::matrix_t" tag. +/// @tparam T The type to evaluate against the concept. template concept c_hypergraph_matrix_impl = c_instantiation_of; +/// @ingroup HGL-Traits +/// @brief Validates if a type is an instantiation of the @ref hgl::impl::flat_matrix_t "impl::flat_matrix_t" tag. +/// @tparam T The type to evaluate against the concept. template concept c_hypergraph_flat_matrix_impl = c_instantiation_of; +/// @ingroup HGL-Traits +/// @brief Validates if a type is a valid incidence matrix implementation tag (either standard or flattened). +/// @tparam T The type to evaluate against the concept. template concept c_hypergraph_incidence_matrix_impl = c_hypergraph_matrix_impl or c_hypergraph_flat_matrix_impl; +/// @ingroup HGL-Traits +/// @brief Validates if a type is one of the defined hypergraph implementation tags. +/// +/// This concept is used to constrain template parameters that are expected to be +/// specific hypergraph implementation tags, ensuring type safety and clear intent in template usage. +/// +/// The valid tags include: +/// - @ref hgl::impl::list_t "impl::list_t": Represents a standard incidence list implementation. +/// - @ref hgl::impl::flat_list_t "impl::flat_list_t": Represents a flattened incidence list implementation. +/// - @ref hgl::impl::matrix_t "impl::matrix_t": Represents a standard incidence matrix implementation. +/// - @ref hgl::impl::flat_matrix_t "impl::flat_matrix_t": Represents a flattened incidence matrix implementation. +/// +/// @tparam T The type to evaluate against the concept. template concept c_hypergraph_impl_tag = c_hypergraph_incidence_list_impl or c_hypergraph_incidence_matrix_impl; diff --git a/include/hgl/hypergraph_traits.hpp b/include/hgl/hypergraph_traits.hpp index 173d2cbd..4beaf84e 100644 --- a/include/hgl/hypergraph_traits.hpp +++ b/include/hgl/hypergraph_traits.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/hypergraph_traits.hpp +/// @brief Defines the primary hypergraph traits structure and related type aliases for different configurations. + #pragma once #include "gl/types/core.hpp" @@ -12,24 +15,57 @@ namespace hgl { +/// @ingroup HGL-Core +/// @brief Primary hypergraph traits structure that encapsulates all necessary type information for hypergraph implementations. +/// +/// This structure serves as the central point for defining the properties and types associated with a hypergraph, +/// including directionality, element properties, as well as implementation and identifier types. It provides a clean, +/// extensible way to customize hypergraph behavior. +/// +/// ### Template Parameters +/// | Parameter | Description | Default | Constraint | +/// | :-------- | :---------- | :------ | :--------- | +/// | DirectionalTag | Specifies whether the hypergraph is undirected or bf_directed. | @ref hgl::undirected_t "undirected_t" | [**c_hypergraph_directional_tag**](hgl_concepts.md#hgl-traits-c-hypergraph-directional-tag) | +/// | VertexProperties | The type of properties attached to each vertex. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | HyperedgeProperties | The type of properties attached to each hyperedge. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | ImplTag | Specifies the internal container representation for incidence. | @ref hgl::impl::list_t "impl::list_t<>" | [**c_hypergraph_impl_tag**](hgl_concepts.md#hgl-traits-c-hypergraph-impl-tag) | template < traits::c_hypergraph_directional_tag DirectionalTag = undirected_t, traits::c_properties VertexProperties = empty_properties, traits::c_properties HyperedgeProperties = empty_properties, traits::c_hypergraph_impl_tag ImplTag = impl::list_t<>> struct hypergraph_traits { + /// @brief The directional tag indicating the hypergraph's orientation. using directional_tag = DirectionalTag; + /// @brief The implementation tag defining the internal storage mechanism. using implementation_tag = ImplTag; + /// @brief The layout tag governing major/minor element ordering. using layout_tag = typename implementation_tag::layout_tag; + /// @brief The integer type used for element identifiers. using id_type = typename implementation_tag::id_type; + /// @brief The fully resolved type representing a vertex descriptor. using vertex_type = vertex_descriptor; + /// @brief The property payload type associated with vertices. using vertex_properties_type = typename vertex_type::properties_type; + /// @brief The fully resolved type representing a hyperedge descriptor. using hyperedge_type = hyperedge_descriptor; + /// @brief The property payload type associated with hyperedges. using hyperedge_properties_type = typename hyperedge_type::properties_type; }; +/// @ingroup HGL-Core +/// @brief Convenience alias for `hypergraph_traits` using a standard incidence list implementation. +/// +/// ### Template Parameters +/// | Parameter | Description | Default | Constraint | +/// | :-------- | :---------- | :------ | :--------- | +/// | LayoutTag | Memory orientation for the list structures. | @ref hgl::impl::bidirectional_t "bidirectional_t" | [**c_hypergraph_layout_tag**](hgl_concepts.md#hgl-traits-c-hypergraph-layout-tag) | +/// | DirectionalTag | Specifies whether the hypergraph is undirected or bf_directed. | @ref hgl::undirected_t "undirected_t" | [**c_hypergraph_directional_tag**](hgl_concepts.md#hgl-traits-c-hypergraph-directional-tag) | +/// | VertexProperties | The type of properties attached to each vertex. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | HyperedgeProperties | The type of properties attached to each hyperedge. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | IdType | The integer type used for element identifiers. | @ref gl::default_id_type "default_id_type" | [**c_id_type**](gl_concepts.md#gl-traits-c-id-type) | template < traits::c_hypergraph_layout_tag LayoutTag = impl::bidirectional_t, traits::c_hypergraph_directional_tag DirectionalTag = undirected_t, @@ -42,6 +78,17 @@ using list_hypergraph_traits = hypergraph_traits< HyperedgeProperties, impl::list_t>; +/// @ingroup HGL-Core +/// @brief Convenience alias for `hypergraph_traits` using a flattened incidence list implementation. +/// +/// ### Template Parameters +/// | Parameter | Description | Default | Constraint | +/// | :-------- | :---------- | :------ | :--------- | +/// | LayoutTag | Memory orientation for the flat list structures. | @ref hgl::impl::bidirectional_t "bidirectional_t" | [**c_hypergraph_layout_tag**](hgl_concepts.md#hgl-traits-c-hypergraph-layout-tag) | +/// | DirectionalTag | Specifies whether the hypergraph is undirected or bf_directed. | @ref hgl::undirected_t "undirected_t" | [**c_hypergraph_directional_tag**](hgl_concepts.md#hgl-traits-c-hypergraph-directional-tag) | +/// | VertexProperties | The type of properties attached to each vertex. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | HyperedgeProperties | The type of properties attached to each hyperedge. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | IdType | The integer type used for element identifiers. | @ref gl::default_id_type "default_id_type" | [**c_id_type**](gl_concepts.md#gl-traits-c-id-type) | template < traits::c_hypergraph_layout_tag LayoutTag = impl::bidirectional_t, traits::c_hypergraph_directional_tag DirectionalTag = undirected_t, @@ -54,6 +101,17 @@ using flat_list_hypergraph_traits = hypergraph_traits< HyperedgeProperties, impl::flat_list_t>; +/// @ingroup HGL-Core +/// @brief Convenience alias for `hypergraph_traits` using a standard incidence matrix implementation. +/// +/// ### Template Parameters +/// | Parameter | Description | Default | Constraint | +/// | :-------- | :---------- | :------ | :--------- | +/// | LayoutTag | Memory orientation (must be asymmetric). | @ref hgl::impl::hyperedge_major_t "hyperedge_major_t" | [**c_hypergraph_asymmetric_layout_tag**](hgl_concepts.md#hgl-traits-c-hypergraph-asymmetric-layout-tag) | +/// | DirectionalTag | Specifies whether the hypergraph is undirected or bf_directed. | @ref hgl::undirected_t "undirected_t" | [**c_hypergraph_directional_tag**](hgl_concepts.md#hgl-traits-c-hypergraph-directional-tag) | +/// | VertexProperties | The type of properties attached to each vertex. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | HyperedgeProperties | The type of properties attached to each hyperedge. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | IdType | The integer type used for element identifiers. | @ref gl::default_id_type "default_id_type" | [**c_id_type**](gl_concepts.md#gl-traits-c-id-type) | template < traits::c_hypergraph_asymmetric_layout_tag LayoutTag = impl::hyperedge_major_t, traits::c_hypergraph_directional_tag DirectionalTag = undirected_t, @@ -66,6 +124,17 @@ using matrix_hypergraph_traits = hypergraph_traits< HyperedgeProperties, impl::matrix_t>; +/// @ingroup HGL-Core +/// @brief Convenience alias for `hypergraph_traits` using a flattened incidence matrix implementation. +/// +/// ### Template Parameters +/// | Parameter | Description | Default | Constraint | +/// | :-------- | :---------- | :------ | :--------- | +/// | LayoutTag | Memory orientation (must be asymmetric). | @ref hgl::impl::hyperedge_major_t "hyperedge_major_t" | [**c_hypergraph_asymmetric_layout_tag**](hgl_concepts.md#hgl-traits-c-hypergraph-asymmetric-layout-tag) | +/// | DirectionalTag | Specifies whether the hypergraph is undirected or bf_directed. | @ref hgl::undirected_t "undirected_t" | [**c_hypergraph_directional_tag**](hgl_concepts.md#hgl-traits-c-hypergraph-directional-tag) | +/// | VertexProperties | The type of properties attached to each vertex. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | HyperedgeProperties | The type of properties attached to each hyperedge. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | IdType | The integer type used for element identifiers. | @ref gl::default_id_type "default_id_type" | [**c_id_type**](gl_concepts.md#gl-traits-c-id-type) | template < traits::c_hypergraph_asymmetric_layout_tag LayoutTag = impl::hyperedge_major_t, traits::c_hypergraph_directional_tag DirectionalTag = undirected_t, @@ -78,6 +147,15 @@ using flat_matrix_hypergraph_traits = hypergraph_traits< HyperedgeProperties, impl::flat_matrix_t>; +/// @ingroup HGL-Core +/// @brief Type alias for undirected hypergraph traits with configurable properties and implementation. +/// +/// ### Template Parameters +/// | Parameter | Description | Default | Constraint | +/// | :-------- | :---------- | :------ | :--------- | +/// | VertexProperties | The type of properties attached to each vertex. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | HyperedgeProperties | The type of properties attached to each hyperedge. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | ImplTag | Specifies the internal container representation for incidence. | @ref hgl::impl::list_t "impl::list_t<>" | [**c_hypergraph_impl_tag**](hgl_concepts.md#hgl-traits-c-hypergraph-impl-tag) | template < traits::c_properties VertexProperties = empty_properties, traits::c_properties HyperedgeProperties = empty_properties, @@ -85,6 +163,15 @@ template < using undirected_hypergraph_traits = hypergraph_traits; +/// @ingroup HGL-Core +/// @brief Type alias for bf-directed hypergraph traits with configurable properties and implementation. +/// +/// ### Template Parameters +/// | Parameter | Description | Default | Constraint | +/// | :-------- | :---------- | :------ | :--------- | +/// | VertexProperties | The type of properties attached to each vertex. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | HyperedgeProperties | The type of properties attached to each hyperedge. | @ref gl::empty_properties "empty_properties" | [**c_properties**](gl_concepts.md#gl-traits-c-properties) | +/// | ImplTag | Specifies the internal container representation for incidence. | @ref hgl::impl::list_t "impl::list_t<>" | [**c_hypergraph_impl_tag**](hgl_concepts.md#hgl-traits-c-hypergraph-impl-tag) | template < traits::c_properties VertexProperties = empty_properties, traits::c_properties HyperedgeProperties = empty_properties, @@ -94,39 +181,63 @@ using bf_directed_hypergraph_traits = namespace traits { +/// @ingroup HGL-Traits +/// @brief Validates if a type is an instantiation of @ref hgl::hypergraph_traits "hypergraph_traits" with a standard incidence list implementation. +/// @tparam TraitsType The type to evaluate against the concept. template concept c_list_hypergraph_traits = c_instantiation_of and c_hypergraph_list_impl; +/// @ingroup HGL-Traits +/// @brief Validates if a type is an instantiation of @ref hgl::hypergraph_traits "hypergraph_traits" with a flattened incidence list implementation. +/// @tparam TraitsType The type to evaluate against the concept. template concept c_flat_list_hypergraph_traits = c_instantiation_of and c_hypergraph_flat_list_impl; +/// @ingroup HGL-Traits +/// @brief Validates if a type is an instantiation of @ref hgl::hypergraph_traits "hypergraph_traits" with an incidence list implementation (either standard or flattened). +/// @tparam TraitsType The type to evaluate against the concept. template concept c_incidence_list_hypergraph_traits = c_list_hypergraph_traits or c_flat_list_hypergraph_traits; +/// @ingroup HGL-Traits +/// @brief Validates if a type is an instantiation of @ref hgl::hypergraph_traits "hypergraph_traits" with a standard incidence matrix implementation. +/// @tparam TraitsType The type to evaluate against the concept. template concept c_matrix_hypergraph_traits = c_instantiation_of and c_hypergraph_matrix_impl; +/// @ingroup HGL-Traits +/// @brief Validates if a type is an instantiation of @ref hgl::hypergraph_traits "hypergraph_traits" with a flattened incidence matrix implementation. +/// @tparam TraitsType The type to evaluate against the concept. template concept c_flat_matrix_hypergraph_traits = c_instantiation_of and c_hypergraph_flat_matrix_impl; +/// @ingroup HGL-Traits +/// @brief Validates if a type is an instantiation of @ref hgl::hypergraph_traits "hypergraph_traits" with an incidence matrix implementation (either standard or flattened). +/// @tparam TraitsType The type to evaluate against the concept. template concept c_incidence_matrix_hypergraph_traits = c_matrix_hypergraph_traits or c_flat_matrix_hypergraph_traits; +/// @ingroup HGL-Traits +/// @brief Validates if a type is an instantiation of @ref hgl::hypergraph_traits "hypergraph_traits" with an undirected configuration. +/// @tparam TraitsType The type to evaluate against the concept. template concept c_undirected_hypergraph_traits = c_instantiation_of and std::same_as; +/// @ingroup HGL-Traits +/// @brief Validates if a type is an instantiation of @ref hgl::hypergraph_traits "hypergraph_traits" with a bf_directed configuration. +/// @tparam TraitsType The type to evaluate against the concept. template concept c_bf_directed_hypergraph_traits = c_instantiation_of diff --git a/include/hgl/impl/impl_tags.hpp b/include/hgl/impl/impl_tags.hpp index e9674112..e8e74b10 100644 --- a/include/hgl/impl/impl_tags.hpp +++ b/include/hgl/impl/impl_tags.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/impl/impl_tags.hpp +/// @brief Defines implementation tags for different hypergraph representations. + #pragma once #include "hgl/decl/impl_tags.hpp" @@ -13,46 +16,88 @@ namespace hgl::impl { +/// @ingroup HGL-Core +/// @headerfile hgl/impl/impl_tags.hpp +/// @brief Tag struct for the standard incidence list hypergraph implementation. +/// @tparam LayoutTag Specifies the memory layout orientation for the underlying data structure. +/// @tparam IdType The underlying integer type used for identifiers. template struct list_t { + /// @brief Self type alias. using type = list_t; + /// @brief The configured layout tag. using layout_tag = LayoutTag; + /// @brief The configured identifier type. using id_type = IdType; + /// @brief Type alias for the underlying incidence list hypergraph implementation based on the provided directional tag. + /// @tparam DirectionalTag The directional tag (e.g., undirected or bf_directed). template using implementation_type = incidence_list; }; +/// @ingroup HGL-Core +/// @headerfile hgl/impl/impl_tags.hpp +/// @brief Tag struct for the flattened incidence list hypergraph implementation. +/// @tparam LayoutTag Specifies the memory layout orientation for the underlying data structure. +/// @tparam IdType The underlying integer type used for identifiers. +/// @see @ref gl::flat_jagged_vector "flat_jagged_vector" for the data structure used for the underlying model implementation. template struct flat_list_t { + /// @brief Self type alias. using type = flat_list_t; + /// @brief The configured layout tag. using layout_tag = LayoutTag; + /// @brief The configured identifier type. using id_type = IdType; + /// @brief Type alias for the underlying flattened incidence list hypergraph implementation based on the provided directional tag. + /// @tparam DirectionalTag The directional tag (e.g., undirected or bf_directed). template using implementation_type = flat_incidence_list; }; +/// @ingroup HGL-Core +/// @headerfile hgl/impl/impl_tags.hpp +/// @brief Tag struct for the standard incidence matrix hypergraph implementation. +/// @tparam LayoutTag Specifies the memory layout orientation for the underlying data structure (must be asymmetric). +/// @tparam IdType The underlying integer type used for identifiers. template struct matrix_t { + /// @brief Self type alias. using type = matrix_t; + /// @brief The configured asymmetric layout tag. using layout_tag = LayoutTag; + /// @brief The configured identifier type. using id_type = IdType; + /// @brief Type alias for the underlying incidence matrix hypergraph implementation based on the provided directional tag. + /// @tparam DirectionalTag The directional tag (e.g., undirected or bf_directed). template using implementation_type = incidence_matrix; }; +/// @ingroup HGL-Core +/// @headerfile hgl/impl/impl_tags.hpp +/// @brief Tag struct for the flattened incidence matrix hypergraph implementation. +/// @tparam LayoutTag Specifies the memory layout orientation for the underlying data structure (must be asymmetric). +/// @tparam IdType The underlying integer type used for identifiers. +/// @see @ref gl::flat_matrix "flat_matrix" for the data structure used for the underlying model implementation. template struct flat_matrix_t { + /// @brief Self type alias. using type = flat_matrix_t; + /// @brief The configured asymmetric layout tag. using layout_tag = LayoutTag; + /// @brief The configured identifier type. using id_type = IdType; + /// @brief Type alias for the underlying flattened incidence matrix hypergraph implementation based on the provided directional tag. + /// @tparam DirectionalTag The directional tag (e.g., undirected or bf_directed). template using implementation_type = flat_incidence_matrix; }; diff --git a/include/hgl/impl/layout_tags.hpp b/include/hgl/impl/layout_tags.hpp index 25e551f2..6ed125af 100644 --- a/include/hgl/impl/layout_tags.hpp +++ b/include/hgl/impl/layout_tags.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/impl/layout_tags.hpp +/// @brief Defines layout tags that dictate the memory orientation and primary indexing of incidence structures. + #pragma once #include "hgl/hypergraph_elements.hpp" @@ -14,10 +17,15 @@ namespace hgl { namespace impl { +/// @ingroup HGL-Core +/// @brief Layout tag designating vertices as the primary structural dimension of the incidence representation. struct vertex_major_t { + /// @brief The major element type for this layout. using major_element = vertex_t; + /// @brief The minor element type for this layout. using minor_element = hyperedge_t; + /// @brief Retrieves the major element from the provided arguments based on the layout rules. template [[nodiscard]] static constexpr T major( const T& vertex_el, [[maybe_unused]] const T& hyperedge_el @@ -25,6 +33,7 @@ struct vertex_major_t { return vertex_el; } + /// @brief Retrieves the minor element from the provided arguments based on the layout rules. template [[nodiscard]] static constexpr T minor( [[maybe_unused]] const T& vertex_el, const T& hyperedge_el @@ -32,6 +41,8 @@ struct vertex_major_t { return hyperedge_el; } + /// @brief Packages data associated with the vertex and hyperedge elements into a strictly ordered major-minor pair. + /// @tparam T The type associated with hypergraph's elements to be ordered. template [[nodiscard]] static constexpr homogeneous_pair majmin( const T& vertex_el, const T& hyperedge_el @@ -40,10 +51,15 @@ struct vertex_major_t { } }; +/// @ingroup HGL-Core +/// @brief Layout tag designating hyperedges as the primary structural dimension of the incidence representation. struct hyperedge_major_t { + /// @brief The major element type for this layout. using major_element = hyperedge_t; + /// @brief The minor element type for this layout. using minor_element = vertex_t; + /// @brief Retrieves the major element from the provided arguments based on the layout rules. template [[nodiscard]] static constexpr T major( [[maybe_unused]] const T& vertex_el, const T& hyperedge_el @@ -51,6 +67,7 @@ struct hyperedge_major_t { return hyperedge_el; } + /// @brief Retrieves the minor element from the provided arguments based on the layout rules. template [[nodiscard]] static constexpr T minor( const T& vertex_el, [[maybe_unused]] const T& hyperedge_el @@ -58,6 +75,8 @@ struct hyperedge_major_t { return vertex_el; } + /// @brief Packages data associated with the vertex and hyperedge elements into a strictly ordered major-minor pair. + /// @tparam T The type associated with hypergraph's elements to be ordered. template [[nodiscard]] static constexpr homogeneous_pair majmin( const T& vertex_el, const T& hyperedge_el @@ -66,25 +85,47 @@ struct hyperedge_major_t { } }; +/// @ingroup HGL-Core +/// @brief Layout tag representing a symmetric or fully bidirectional incidence structural layout. struct bidirectional_t {}; } // namespace impl namespace traits { +/// @ingroup HGL-Traits +/// @brief Validates if a type is a valid hypergraph layout tag. +/// +/// The valid hypergraph layout tags are @ref hgl::impl::vertex_major_t "vertex_major_t", +/// @ref hgl::impl::hyperedge_major_t "hyperedge_major_t", and @ref hgl::impl::bidirectional_t "bidirectional_t". +/// +/// @tparam T The type to evaluate against the concept. template concept c_hypergraph_layout_tag = c_one_of; +/// @ingroup HGL-Traits +/// @brief Validates if a type is a valid asymmetric hypergraph layout tag. +/// +/// Symmetric or bidirectional layouts are excluded. Valid tags are @ref hgl::impl::vertex_major_t "vertex_major_t" +/// and @ref hgl::impl::hyperedge_major_t "hyperedge_major_t". +/// +/// @tparam T The type to evaluate against the concept. template concept c_hypergraph_asymmetric_layout_tag = c_one_of; } // namespace traits +/// @ingroup HGL-Traits +/// @brief Extracts the major element tag type associated with a specific asymmetric layout. +/// @tparam LT The asymmetric layout tag type. template using major_element_t = typename LT::major_element; +/// @ingroup HGL-Traits +/// @brief Extracts the minor element tag type associated with a specific asymmetric layout. +/// @tparam LT The asymmetric layout tag type. template using minor_element_t = typename LT::minor_element; From e28158d4550b299342792d28207343ae20d25a85 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Tue, 28 Apr 2026 15:56:30 +0200 Subject: [PATCH 07/28] hypergraph doc --- include/hgl/hypergraph.hpp | 572 ++++++++++++++++++++++++++++++++++++- 1 file changed, 565 insertions(+), 7 deletions(-) diff --git a/include/hgl/hypergraph.hpp b/include/hgl/hypergraph.hpp index 72aa4dc4..61d7c9f7 100644 --- a/include/hgl/hypergraph.hpp +++ b/include/hgl/hypergraph.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/hypergraph.hpp +/// @brief Defines the core generic `hypergraph` container and its associated traits and utilities. + #pragma once #include "gl/attributes/force_inline.hpp" @@ -30,82 +33,193 @@ class hypergraph; namespace traits { +/// @ingroup HGL-Traits +/// @brief Concept checking if a type is an instantiation of the generic @ref hgl::hypergraph "hypergraph" class. +/// @tparam H The type to evaluate against the concept. template concept c_hypergraph = c_instantiation_of; +/// @ingroup HGL-Traits +/// @brief Concept checking if a hypergraph is undirected. +/// @see @ref hgl::undirected_t "undirected_t" for the directional tag used to constrain this concept. +/// @tparam H The type to evaluate against the concept. template concept c_undirected_hypergraph = c_hypergraph and std::same_as; +/// @ingroup HGL-Traits +/// @brief Concept checking if a hypergraph is backward-forward (bf) directed. +/// @see @ref hgl::bf_directed_t "bf_directed_t" for the directional tag used to constrain this concept. +/// @tparam H The type to evaluate against the concept. template concept c_bf_directed_hypergraph = c_hypergraph and std::same_as; + +/// @ingroup HGL-Traits +/// @brief Concept checking if a hypergraph uses a standard incidence list implementation. +/// @tparam H The type to evaluate against the concept. template concept c_list_hypergraph = c_hypergraph and c_hypergraph_list_impl; +/// @ingroup HGL-Traits +/// @brief Concept checking if a hypergraph uses a flattened incidence list implementation. +/// @tparam H The type to evaluate against the concept. template concept c_flat_list_hypergraph = c_hypergraph and c_hypergraph_flat_list_impl; +/// @ingroup HGL-Traits +/// @brief Concept checking if a hypergraph uses any incidence list implementation (standard or flattened). +/// @tparam H The type to evaluate against the concept. template concept c_incidence_list_hypergraph = c_hypergraph and c_hypergraph_incidence_list_impl; +/// @ingroup HGL-Traits +/// @brief Concept checking if a hypergraph uses a standard incidence matrix implementation. +/// @tparam H The type to evaluate against the concept. template concept c_matrix_hypergraph = c_hypergraph and c_hypergraph_matrix_impl; +/// @ingroup HGL-Traits +/// @brief Concept checking if a hypergraph uses a flattened incidence matrix implementation. +/// @tparam H The type to evaluate against the concept. template concept c_flat_matrix_hypergraph = c_hypergraph and c_hypergraph_flat_matrix_impl; +/// @ingroup HGL-Traits +/// @brief Concept checking if a hypergraph uses any incidence matrix implementation (standard or flattened). +/// @tparam H The type to evaluate against the concept. template concept c_incidence_matrix_hypergraph = c_hypergraph and c_hypergraph_incidence_matrix_impl; } // namespace traits +/// @ingroup HGL-Core +/// @brief Creates a deep copy of a given hypergraph. +/// @tparam Hypergraph The concrete hypergraph type. +/// @param source The hypergraph to clone. +/// @return A duplicated instance of the source hypergraph. template [[nodiscard]] Hypergraph clone(const Hypergraph& source); +/// @ingroup HGL-Core +/// @brief Converts a hypergraph to a different implementation type (e.g., from incidence list to incidence matrix). +/// @tparam TargetImplTag The implementation tag defining the target storage mechanism. +/// @tparam Hypergraph The concrete hypergraph type of the source. +/// @param source The hypergraph to convert. +/// @return A new hypergraph matching the target implementation type with identical topology and properties. template [[nodiscard]] auto to(Hypergraph&& source); namespace detail { +/// @brief Internal structure for dispatching the hypergraph implementation conversion logic. template struct to_impl; } // namespace detail -/// @ingroup hgl +/// @ingroup HGL-Core +/// @brief The primary, highly configurable generic container for representing n-ary hypergraphs. +/// +/// **Module:** Part of the @ref HGL-Core "Core Hypergraph Components" group. +/// +/// Unlike standard graphs where edges connect exactly two vertices, hypergraphs generalize this +/// concept by allowing a single hyperedge to connect any number of vertices simultaneously. +/// The `hgl::hypergraph` template provides a strictly type-safe, highly optimized, and conceptually +/// robust API for modeling both undirected and backward-forward (bf) directed hypergraphs. +/// +/// ### Key Features +/// - **Zero-cost Abstractions**: Core query logic is resolved at compile time through layout tags and static dispatch, removing unnecessary overhead. +/// - **Multiple Internal Representations**: Supports both incidence matrices (optimal for dense, rigid structures) and incidence lists (optimal for sparse, dynamic structures), further separated into standard and flat-memory contiguous variants. +/// - **Arbitrary Properties Injection**: Allows seamless integration of completely custom, user-defined data structures (properties) directly into vertices and hyperedges without inheriting from intrusive base classes. +/// - **Standard Range Support**: Exposes lightweight views compliant with C++20 `std::ranges`, enabling functional-style iteration and algorithms. +/// +/// ### Basic Definitions +/// - **Hyperedge**: A generalized edge that can connect any subset of vertices. +/// - **Incidence**: The fundamental relationship in a hypergraph. A vertex is "incident to" a hyperedge if it is contained within that hyperedge's set. +/// - **Undirected**: A hyperedge is simply a set of incident vertices. +/// - **BF-Directed**: A hyperedge maps a distinct subset of vertices (the **Tail**) to another distinct subset of vertices (the **Head**). +/// +/// ### Example Usage +/// ```cpp +/// // 1. Define a directed hypergraph with string names on vertices and weights on hyperedges. +/// using traits = hgl::hypergraph_traits< +/// hgl::bf_directed_t, gl::name_property, gl::weight_property +/// >; +/// hgl::hypergraph h; +/// +/// // 2. Add properties via the descriptor dereference operator. +/// auto v0 = h.add_vertex(); v0->name = "Source"; // (1)! +/// auto v1 = h.add_vertex(); v1->name = "Target A"; +/// auto v2 = h.add_vertex(); v2->name = "Target B"; +/// +/// // 3. For directed hypergraphs, specify Tail vertices and Head vertices. +/// auto e = h.add_hyperedge({v0}, {v1, v2}); // (2)! +/// e->weight = 5.5; +/// +/// std::cout << gl::io::verbose << gl::io::with_properties; // (3)! +/// std::cout << "Hypergraph:\n" << h << '\n'; +/// ``` +/// +/// 1. Vertices are created and returned as safe @ref hgl::vertex_descriptor "vertex_descriptor" wrappers. +/// 2. Hyperedges are created and returned as safe @ref hgl::hyperedge_descriptor "hyperedge_descriptor" wrappers. +/// 3. Standard GL stream manipulators format both graph and hypergraph output identically. +/// +/// ### Template Parameters +/// | Parameter | Description | Default | Constraint | +/// | :-------- | :---------- | :------ | :--------- | +/// | HypergraphTraits | The core configuration object dictating the directionality, properties, identifier types, and internal memory layout of the hypergraph. | @ref hgl::hypergraph_traits "hypergraph_traits<>" | [**c_instantiation_of**](hgl_concepts.md#hgl-traits-c-instantiation-of) | +/// +/// ### See Also +/// - @ref hgl::hypergraph_traits "hypergraph_traits" for configuring the underlying properties and tags. +/// - @ref gl::io::options_manip "options_manip" for custom stream formatting options. template HypergraphTraits> class hypergraph final { public: + /// @brief The configured traits type governing this hypergraph's parameters. using traits_type = HypergraphTraits; + /// @brief The directional tag indicating if the hypergraph is undirected or bf_directed. using directional_tag = typename traits_type::directional_tag; + /// @brief The implementation tag defining the internal storage mechanism. using implementation_tag = typename traits_type::implementation_tag; + /// @brief The instantiated underlying implementation class matching the directional tag. using implementation_type = typename implementation_tag::template implementation_type; + /// @brief The underlying integer type used for identifiers. using id_type = typename traits_type::id_type; + /// @brief The fully resolved type representing a vertex descriptor. using vertex_type = typename traits_type::vertex_type; + /// @brief The user-defined property payload type associated with vertices. using vertex_properties_type = typename traits_type::vertex_properties_type; + /// @brief The container type used for storing vertex properties internally. using vertex_properties_map_type = std::conditional_t< traits::c_empty_properties, empty_properties_map, std::vector>; + /// @brief The fully resolved type representing a hyperedge descriptor. using hyperedge_type = typename traits_type::hyperedge_type; + /// @brief The user-defined property payload type associated with hyperedges. using hyperedge_properties_type = typename traits_type::hyperedge_properties_type; + /// @brief The container type used for storing hyperedge properties internally. using hyperedge_properties_map_type = std::conditional_t< traits::c_empty_properties, empty_properties_map, std::vector>; + /// @brief Deleted copy assignment operator to prevent trivial overwrites (use `hgl::clone` instead). hypergraph& operator=(const hypergraph&) = delete; + /// @brief Constructs an empty hypergraph, optionally pre-allocating capacity. + /// @param n_vertices The initial capacity or fixed count of vertices (especially relevant for matrix implementations). + /// @param n_hyperedges The initial capacity or fixed count of hyperedges. explicit hypergraph(const size_type n_vertices = 0uz, const size_type n_hyperedges = 0uz) : _n_vertices(n_vertices), _n_hyperedges(n_hyperedges), _impl(n_vertices, n_hyperedges) { if constexpr (traits::c_non_empty_properties) @@ -115,23 +229,32 @@ class hypergraph final { this->_hyperedge_properties.resize(n_hyperedges); } + /// @brief Default move constructor. hypergraph(hypergraph&&) noexcept = default; + /// @brief Default move assignment operator. hypergraph& operator=(hypergraph&&) noexcept = default; + /// @brief Default destructor. ~hypergraph() = default; // --- size methods --- + /// @brief Retrieves the current number of valid vertices in the hypergraph. + /// @return The total vertex count. [[nodiscard]] gl_attr_force_inline size_type n_vertices() const noexcept { return this->_n_vertices; } + /// @brief Retrieves the current number of valid hyperedges in the hypergraph. + /// @return The total hyperedge count. [[nodiscard]] gl_attr_force_inline size_type n_hyperedges() const noexcept { return this->_n_hyperedges; } // --- vertex modifiers --- + /// @brief Adds a single new property-less vertex to the hypergraph. + /// @return A safe `vertex_descriptor` pointing to the newly created vertex. vertex_type add_vertex() { this->_impl.add_vertices(1uz); const auto new_vertex_id = static_cast(this->_n_vertices++); @@ -142,6 +265,9 @@ class hypergraph final { return vertex_type{new_vertex_id}; } + /// @brief Adds a single new vertex to the hypergraph and initializes its properties. + /// @param properties The property payload to assign to the new vertex. + /// @return A safe `vertex_descriptor` pointing to the newly created vertex. vertex_type add_vertex_with(vertex_properties_type properties) requires(traits::c_non_empty_properties) { @@ -152,6 +278,8 @@ class hypergraph final { }; } + /// @brief Efficiently adds multiple property-less vertices to the hypergraph. + /// @param n The number of vertices to add. void add_vertices(const size_type n) { this->_impl.add_vertices(n); this->_n_vertices += n; @@ -160,6 +288,8 @@ class hypergraph final { this->_vertex_properties.resize(this->_n_vertices); } + /// @brief Efficiently adds multiple vertices to the hypergraph, initializing them with a range of properties. + /// @param properties_rng A range containing the property payloads to assign to the newly created vertices. void add_vertices_with( const traits::c_sized_range_of auto& properties_rng ) @@ -178,14 +308,20 @@ class hypergraph final { ); } + /// @brief Safely removes a vertex and completely purges it from all incident hyperedges. + /// @param vertex_id The raw identifier of the vertex to remove. gl_attr_force_inline void remove_vertex(const id_type vertex_id) { this->_remove_vertex_impl(vertex_id); } + /// @brief Safely removes a vertex and completely purges it from all incident hyperedges. + /// @param vertex The `vertex_descriptor` wrapping the vertex to remove. gl_attr_force_inline void remove_vertex(const vertex_type& vertex) { this->remove_vertex(vertex.id()); } + /// @brief Removes multiple vertices specified by a range of raw identifiers. + /// @param vertex_id_rng A range of raw vertex identifiers to be removed. void remove_vertices(const traits::c_forward_range_of auto& vertex_id_rng) { // sorts ids in a descending n_vertices and removes duplicate ids std::set> vertex_id_set( @@ -197,6 +333,8 @@ class hypergraph final { this->_remove_vertex_impl(vertex_id); } + /// @brief Removes multiple vertices specified by a range of vertex descriptors. + /// @param vertex_rng A range of `vertex_descriptor`s representing the vertices to be removed. void remove_vertices(const traits::c_sized_range_of auto& vertex_rng) { // sort vertices in a descending n_vertices (by id) and removes duplicate ids std::set> vertex_set( @@ -210,23 +348,40 @@ class hypergraph final { // --- vertex getters --- + /// @brief Checks if a vertex with the given raw ID exists in the hypergraph. + /// @param vertex_id The raw identifier to check. + /// @return `true` if the vertex exists, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool has_vertex(const id_type vertex_id) const { return vertex_id < this->_n_vertices; } + /// @brief Checks if the vertex referenced by the provided descriptor exists in the hypergraph. + /// @param vertex The vertex descriptor to check. + /// @return `true` if the vertex exists, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool has_vertex(vertex_type vertex) const { return this->has_vertex(vertex.id()); } + /// @brief Safely wraps a raw vertex ID into a `vertex_descriptor`, bounds-checking the ID. + /// @param vertex_id The raw numeric identifier of the vertex. + /// @return The constructed `vertex_descriptor`. + /// @throws std::invalid_argument If the `vertex_id` is out of bounds. [[nodiscard]] vertex_type vertex(const id_type vertex_id) const { this->_verify_vertex_id(vertex_id); return this->vertex_unchecked(vertex_id); } + /// @brief Accesses a `vertex_descriptor` using a tag-based accessor. + /// @param vertex_id The raw numeric identifier of the vertex. + /// @return The constructed `vertex_descriptor`. + /// @throws std::invalid_argument If the `vertex_id` is out of bounds. [[nodiscard]] gl_attr_force_inline vertex_type at(vertex_t, const id_type vertex_id) const { return this->vertex(vertex_id); } + /// @brief Wraps a raw vertex ID into a `vertex_descriptor` without performing bounds checking. + /// @param vertex_id The raw numeric identifier of the vertex. + /// @return The constructed `vertex_descriptor`. [[nodiscard]] gl_attr_force_inline vertex_type vertex_unchecked(const id_type vertex_id) const { if constexpr (traits::c_non_empty_properties) return vertex_type{vertex_id, this->_vertex_properties[vertex_id]}; @@ -234,19 +389,30 @@ class hypergraph final { return vertex_type{vertex_id}; } + /// @brief Subscript operator to access a `vertex_descriptor` without bounds checking. + /// @param vertex_id The raw numeric identifier of the vertex. + /// @return The constructed `vertex_descriptor`. [[nodiscard]] gl_attr_force_inline vertex_type operator[](vertex_t, const id_type vertex_id) const { return this->vertex_unchecked(vertex_id); } + /// @brief Returns a transformed range wrapping all valid vertices into `vertex_descriptor`s. + /// @return A lazily evaluated view of all `vertex_descriptor`s. [[nodiscard]] gl_attr_force_inline auto vertices() const noexcept { return this->vertex_ids() | std::views::transform(this->_create_vertex_descriptor()); } + /// @brief Returns a lightweight, lazily evaluated range over all valid raw vertex IDs. + /// @return A view of all vertex identifiers. [[nodiscard]] gl_attr_force_inline auto vertex_ids() const noexcept { return std::views::iota(initial_id_v, this->_n_vertices); } + /// @brief Retrieves a reference to the properties of a specified vertex. + /// @param vertex_id The identifier of the vertex. + /// @return A mutable reference to the vertex's properties. + /// @throws std::invalid_argument If the `vertex_id` is out of bounds. [[nodiscard]] gl_attr_force_inline vertex_properties_type& vertex_properties( const id_type vertex_id ) const @@ -256,6 +422,8 @@ class hypergraph final { return this->_vertex_properties[vertex_id]; } + /// @brief Returns a view over the internal vertex properties container. + /// @return A standard view over the continuous block of property payloads. [[nodiscard]] gl_attr_force_inline auto vertex_properties_map() const noexcept requires(traits::c_non_empty_properties) { @@ -264,6 +432,8 @@ class hypergraph final { // --- hyperedge modifiers --- + /// @brief Adds a single new, empty, property-less hyperedge to the hypergraph. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. hyperedge_type add_hyperedge() { this->_impl.add_hyperedges(1uz); const auto new_hyperedge_id = static_cast(this->_n_hyperedges++); @@ -274,6 +444,9 @@ class hypergraph final { return hyperedge_type{new_hyperedge_id}; } + /// @brief Adds a single new empty hyperedge and initializes its properties. + /// @param properties The property payload to assign to the new hyperedge. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. hyperedge_type add_hyperedge_with(hyperedge_properties_type properties) requires(traits::c_non_empty_properties) { @@ -284,6 +457,9 @@ class hypergraph final { }; } + /// @brief Adds a new hyperedge and immediately binds a range of vertices to it (undirected). + /// @param vertex_id_rng A range of raw vertex identifiers to incident with the new hyperedge. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. hyperedge_type add_hyperedge(const traits::c_forward_range_of auto& vertex_id_rng) requires std::same_as { @@ -292,12 +468,18 @@ class hypergraph final { return he; } + /// @brief Adds a new hyperedge and immediately binds an initializer list of vertices to it (undirected). + /// @param vertex_ids An initializer list of raw vertex identifiers. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. gl_attr_force_inline hyperedge_type add_hyperedge(std::initializer_list vertex_ids) requires std::same_as { return this->add_hyperedge(std::views::all(vertex_ids)); } + /// @brief Adds a new hyperedge and immediately binds a range of vertex descriptors to it (undirected). + /// @param vertex_rng A range of `vertex_descriptor` objects. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. gl_attr_force_inline hyperedge_type add_hyperedge(const traits::c_forward_range_of auto& vertex_rng) requires std::same_as @@ -305,12 +487,19 @@ class hypergraph final { return this->add_hyperedge(vertex_rng | std::views::transform(&vertex_type::id)); } + /// @brief Adds a new hyperedge and immediately binds an initializer list of vertex descriptors to it (undirected). + /// @param vertices An initializer list of `vertex_descriptor` objects. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. gl_attr_force_inline hyperedge_type add_hyperedge(std::initializer_list vertices) requires std::same_as { return this->add_hyperedge(std::views::all(vertices)); } + /// @brief Adds a new hyperedge, initializes properties, and binds a range of vertices to it (undirected). + /// @param vertex_id_rng A range of raw vertex identifiers. + /// @param properties The property payload to assign. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. hyperedge_type add_hyperedge_with( const traits::c_forward_range_of auto& vertex_id_rng, hyperedge_properties_type properties @@ -322,6 +511,10 @@ class hypergraph final { return he; } + /// @brief Adds a new hyperedge, initializes properties, and binds an initializer list of vertices to it (undirected). + /// @param vertex_ids An initializer list of raw vertex identifiers. + /// @param properties The property payload to assign. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. hyperedge_type add_hyperedge_with( std::initializer_list vertex_ids, hyperedge_properties_type properties ) @@ -330,6 +523,10 @@ class hypergraph final { return this->add_hyperedge_with(std::views::all(vertex_ids), std::move(properties)); } + /// @brief Adds a new hyperedge, initializes properties, and binds a range of vertex descriptors to it (undirected). + /// @param vertex_rng A range of `vertex_descriptor` objects. + /// @param properties The property payload to assign. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. gl_attr_force_inline hyperedge_type add_hyperedge_with( const traits::c_forward_range_of auto& vertex_rng, hyperedge_properties_type properties @@ -341,6 +538,10 @@ class hypergraph final { ); } + /// @brief Adds a new hyperedge, initializes properties, and binds an initializer list of vertex descriptors to it (undirected). + /// @param vertices An initializer list of `vertex_descriptor` objects. + /// @param properties The property payload to assign. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. hyperedge_type add_hyperedge_with( std::initializer_list vertices, hyperedge_properties_type properties ) @@ -349,6 +550,10 @@ class hypergraph final { return this->add_hyperedge_with(std::views::all(vertices), std::move(properties)); } + /// @brief Adds a new hyperedge and immediately binds tail and head vertices to it (bf-directed). + /// @param tail_id_rng A range of raw vertex identifiers forming the Tail. + /// @param head_id_rng A range of raw vertex identifiers forming the Head. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. hyperedge_type add_hyperedge( const traits::c_forward_range_of auto& tail_id_rng, const traits::c_forward_range_of auto& head_id_rng @@ -361,6 +566,10 @@ class hypergraph final { return he; } + /// @brief Adds a new hyperedge and immediately binds tail and head vertices to it (bf-directed). + /// @param tail_ids An initializer list of raw vertex identifiers forming the Tail. + /// @param head_ids An initializer list of raw vertex identifiers forming the Head. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. gl_attr_force_inline hyperedge_type add_hyperedge(std::initializer_list tail_ids, std::initializer_list head_ids) requires std::same_as @@ -368,6 +577,10 @@ class hypergraph final { return this->add_hyperedge(std::views::all(tail_ids), std::views::all(head_ids)); } + /// @brief Adds a new hyperedge and immediately binds tail and head vertices to it (bf-directed). + /// @param tail_rng A range of `vertex_descriptor`s forming the Tail. + /// @param head_rng A range of `vertex_descriptor`s forming the Head. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. gl_attr_force_inline hyperedge_type add_hyperedge( const traits::c_forward_range_of auto& tail_rng, const traits::c_forward_range_of auto& head_rng @@ -380,6 +593,10 @@ class hypergraph final { ); } + /// @brief Adds a new hyperedge and immediately binds tail and head vertices to it (bf-directed). + /// @param tail An initializer list of `vertex_descriptor`s forming the Tail. + /// @param head An initializer list of `vertex_descriptor`s forming the Head. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. gl_attr_force_inline hyperedge_type add_hyperedge(std::initializer_list tail, std::initializer_list head) requires std::same_as @@ -387,6 +604,11 @@ class hypergraph final { return this->add_hyperedge(std::views::all(tail), std::views::all(head)); } + /// @brief Adds a new hyperedge, initializes properties, and binds tail and head vertices to it (bf-directed). + /// @param tail_id_rng A range of raw vertex identifiers forming the Tail. + /// @param head_id_rng A range of raw vertex identifiers forming the Head. + /// @param properties The property payload to assign. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. hyperedge_type add_hyperedge_with( const traits::c_forward_range_of auto& tail_id_rng, const traits::c_forward_range_of auto& head_id_rng, @@ -400,6 +622,11 @@ class hypergraph final { return he; } + /// @brief Adds a new hyperedge, initializes properties, and binds tail and head vertices to it (bf-directed). + /// @param tail_ids An initializer list of raw vertex identifiers forming the Tail. + /// @param head_ids An initializer list of raw vertex identifiers forming the Head. + /// @param properties The property payload to assign. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. gl_attr_force_inline hyperedge_type add_hyperedge_with( std::initializer_list tail_ids, std::initializer_list head_ids, @@ -412,6 +639,11 @@ class hypergraph final { ); } + /// @brief Adds a new hyperedge, initializes properties, and binds tail and head vertices to it (bf-directed). + /// @param tail_rng A range of `vertex_descriptor`s forming the Tail. + /// @param head_rng A range of `vertex_descriptor`s forming the Head. + /// @param properties The property payload to assign. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. gl_attr_force_inline hyperedge_type add_hyperedge_with( const traits::c_forward_range_of auto& tail_rng, const traits::c_forward_range_of auto& head_rng, @@ -426,6 +658,11 @@ class hypergraph final { ); } + /// @brief Adds a new hyperedge, initializes properties, and binds tail and head vertices to it (bf-directed). + /// @param tail An initializer list of `vertex_descriptor`s forming the Tail. + /// @param head An initializer list of `vertex_descriptor`s forming the Head. + /// @param properties The property payload to assign. + /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. gl_attr_force_inline hyperedge_type add_hyperedge_with( std::initializer_list tail, std::initializer_list head, @@ -438,6 +675,8 @@ class hypergraph final { ); } + /// @brief Efficiently adds multiple property-less hyperedges to the hypergraph. + /// @param n The number of empty hyperedges to add. void add_hyperedges(const size_type n) { this->_impl.add_hyperedges(n); this->_n_hyperedges += n; @@ -446,6 +685,8 @@ class hypergraph final { this->_hyperedge_properties.resize(this->_n_hyperedges); } + /// @brief Efficiently adds multiple hyperedges to the hypergraph, initializing them with a range of properties. + /// @param properties_rng A range containing the property payloads to assign to the newly created hyperedges. void add_hyperedges_with( const traits::c_sized_range_of auto& properties_rng ) @@ -464,14 +705,20 @@ class hypergraph final { ); } + /// @brief Safely removes a hyperedge and completely unbinds it from all incident vertices. + /// @param hyperedge_id The raw identifier of the hyperedge to remove. gl_attr_force_inline void remove_hyperedge(const id_type hyperedge_id) { this->_remove_hyperedge_impl(hyperedge_id); } + /// @brief Safely removes a hyperedge and completely unbinds it from all incident vertices. + /// @param hyperedge The `hyperedge_descriptor` wrapping the hyperedge to remove. gl_attr_force_inline void remove_hyperedge(const hyperedge_type& hyperedge) { this->remove_hyperedge(hyperedge.id()); } + /// @brief Removes multiple hyperedges specified by a range of raw identifiers. + /// @param hyperedge_id_rng A range of raw hyperedge identifiers to be removed. void remove_hyperedges_from(const traits::c_forward_range_of auto& hyperedge_id_rng) { // sorts ids in a descending n_vertices and removes duplicate ids std::set> hyperedge_id_set( @@ -483,6 +730,8 @@ class hypergraph final { this->_remove_hyperedge_impl(hyperedge_id); } + /// @brief Removes multiple hyperedges specified by a range of hyperedge descriptors. + /// @param hyperedge_rng A range of `hyperedge_descriptor`s representing the hyperedges to be removed. void remove_hyperedges_from(const traits::c_sized_range_of auto& hyperedge_rng ) { // sort hyperedges in a descending n_vertices (by id) and removes duplicate ids @@ -497,24 +746,41 @@ class hypergraph final { // --- hyperedge getters --- + /// @brief Checks if a hyperedge with the given raw ID exists in the hypergraph. + /// @param hyperedge_id The raw identifier to check. + /// @return `true` if the hyperedge exists, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool has_hyperedge(const id_type hyperedge_id) const { return hyperedge_id < this->_n_hyperedges; } + /// @brief Checks if the hyperedge referenced by the provided descriptor exists in the hypergraph. + /// @param hyperedge The hyperedge descriptor to check. + /// @return `true` if the hyperedge exists, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool has_hyperedge(hyperedge_type hyperedge) const { return this->has_hyperedge(hyperedge.id()); } + /// @brief Safely wraps a raw hyperedge ID into a `hyperedge_descriptor`, bounds-checking the ID. + /// @param hyperedge_id The raw numeric identifier of the hyperedge. + /// @return The constructed `hyperedge_descriptor`. + /// @throws std::invalid_argument If the `hyperedge_id` is out of bounds. [[nodiscard]] hyperedge_type hyperedge(const id_type hyperedge_id) const { this->_verify_hyperedge_id(hyperedge_id); return this->hyperedge_unchecked(hyperedge_id); } + /// @brief Accesses a `hyperedge_descriptor` using a tag-based accessor. + /// @param hyperedge_id The raw numeric identifier of the hyperedge. + /// @return The constructed `hyperedge_descriptor`. + /// @throws std::invalid_argument If the `hyperedge_id` is out of bounds. [[nodiscard]] gl_attr_force_inline hyperedge_type at(hyperedge_t, const id_type hyperedge_id) const { return this->hyperedge(hyperedge_id); } + /// @brief Wraps a raw hyperedge ID into a `hyperedge_descriptor` without performing bounds checking. + /// @param hyperedge_id The raw numeric identifier of the hyperedge. + /// @return The constructed `hyperedge_descriptor`. [[nodiscard]] gl_attr_force_inline hyperedge_type hyperedge_unchecked(const id_type hyperedge_id ) const { if constexpr (traits::c_non_empty_properties) @@ -523,19 +789,30 @@ class hypergraph final { return hyperedge_type{hyperedge_id}; } + /// @brief Subscript operator to access a `hyperedge_descriptor` without bounds checking. + /// @param hyperedge_id The raw numeric identifier of the hyperedge. + /// @return The constructed `hyperedge_descriptor`. [[nodiscard]] gl_attr_force_inline hyperedge_type operator[](hyperedge_t, const id_type hyperedge_id) const { return this->hyperedge_unchecked(hyperedge_id); } + /// @brief Returns a transformed range wrapping all valid hyperedges into `hyperedge_descriptor`s. + /// @return A lazily evaluated view of all `hyperedge_descriptor`s. [[nodiscard]] gl_attr_force_inline auto hyperedges() const noexcept { return this->hyperedge_ids() | std::views::transform(this->_create_hyperedge_descriptor()); } + /// @brief Returns a lightweight, lazily evaluated range over all valid raw hyperedge IDs. + /// @return A view of all hyperedge identifiers. [[nodiscard]] gl_attr_force_inline auto hyperedge_ids() const noexcept { return std::views::iota(initial_id_v, this->_n_hyperedges); } + /// @brief Retrieves a reference to the properties of a specified hyperedge. + /// @param id The identifier of the hyperedge. + /// @return A mutable reference to the hyperedge's properties. + /// @throws std::invalid_argument If the `id` is out of bounds. [[nodiscard]] gl_attr_force_inline hyperedge_properties_type& hyperedge_properties( const id_type id ) const @@ -545,6 +822,8 @@ class hypergraph final { return this->_hyperedge_properties[id]; } + /// @brief Returns a view over the internal hyperedge properties container. + /// @return A standard view over the continuous block of property payloads. [[nodiscard]] gl_attr_force_inline auto hyperedge_properties_map() const noexcept requires(traits::c_non_empty_properties) { @@ -553,6 +832,9 @@ class hypergraph final { // --- incidence modifiers --- + /// @brief Binds a vertex to a hyperedge in an undirected hypergraph. + /// @param vertex_id The raw identifier of the vertex. + /// @param hyperedge_id The raw identifier of the hyperedge. void bind(const id_type vertex_id, const id_type hyperedge_id) requires std::same_as { @@ -561,12 +843,18 @@ class hypergraph final { this->_impl.bind(vertex_id, hyperedge_id); } + /// @brief Binds a vertex to a hyperedge in an undirected hypergraph using descriptors. + /// @param vertex The `vertex_descriptor` mapping to the vertex. + /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. gl_attr_force_inline void bind(const vertex_type& vertex, const hyperedge_type& hyperedge) requires std::same_as { this->bind(vertex.id(), hyperedge.id()); } + /// @brief Binds a range of vertices to a single hyperedge in an undirected hypergraph. + /// @param vertex_id_rng A range of raw vertex identifiers. + /// @param hyperedge_id The raw identifier of the hyperedge. void bind( const traits::c_forward_range_of auto& vertex_id_rng, const id_type hyperedge_id ) @@ -579,6 +867,9 @@ class hypergraph final { } } + /// @brief Binds an initializer list of vertices to a single hyperedge in an undirected hypergraph. + /// @param vertex_ids An initializer list of raw vertex identifiers. + /// @param hyperedge_id The raw identifier of the hyperedge. gl_attr_force_inline void bind( std::initializer_list vertex_ids, const id_type hyperedge_id ) @@ -587,6 +878,9 @@ class hypergraph final { this->bind(std::views::all(vertex_ids), hyperedge_id); } + /// @brief Binds a range of vertex descriptors to a single hyperedge in an undirected hypergraph. + /// @param vertex_rng A range of `vertex_descriptor`s. + /// @param hyperedge The `hyperedge_descriptor` wrapping the target hyperedge. gl_attr_force_inline void bind( const traits::c_forward_range_of auto& vertex_rng, const hyperedge_type& hyperedge @@ -596,6 +890,9 @@ class hypergraph final { this->bind(vertex_rng | std::views::transform(&vertex_type::id), hyperedge.id()); } + /// @brief Binds an initializer list of vertex descriptors to a single hyperedge in an undirected hypergraph. + /// @param vertices An initializer list of `vertex_descriptor`s. + /// @param hyperedge The `hyperedge_descriptor` wrapping the target hyperedge. gl_attr_force_inline void bind( std::initializer_list vertices, const hyperedge_type& hyperedge ) @@ -604,6 +901,9 @@ class hypergraph final { this->bind(std::views::all(vertices), hyperedge); } + /// @brief Binds a single vertex to a range of hyperedges in an undirected hypergraph. + /// @param vertex_id The raw identifier of the vertex. + /// @param hyperedge_id_rng A range of raw hyperedge identifiers. void bind( const id_type vertex_id, const traits::c_forward_range_of auto& hyperedge_id_rng ) @@ -616,6 +916,9 @@ class hypergraph final { } } + /// @brief Binds a single vertex to an initializer list of hyperedges in an undirected hypergraph. + /// @param vertex_id The raw identifier of the vertex. + /// @param hyperedge_ids An initializer list of raw hyperedge identifiers. gl_attr_force_inline void bind( const id_type vertex_id, std::initializer_list hyperedge_ids ) @@ -624,6 +927,9 @@ class hypergraph final { this->bind(vertex_id, std::views::all(hyperedge_ids)); } + /// @brief Binds a single vertex descriptor to a range of hyperedge descriptors in an undirected hypergraph. + /// @param vertex The target `vertex_descriptor`. + /// @param hyperedge_rng A range of `hyperedge_descriptor`s. gl_attr_force_inline void bind( const vertex_type& vertex, const traits::c_forward_range_of auto& hyperedge_rng @@ -633,6 +939,9 @@ class hypergraph final { this->bind(vertex.id(), hyperedge_rng | std::views::transform(&hyperedge_type::id)); } + /// @brief Binds a single vertex descriptor to an initializer list of hyperedge descriptors in an undirected hypergraph. + /// @param vertex The target `vertex_descriptor`. + /// @param hyperedges An initializer list of `hyperedge_descriptor`s. gl_attr_force_inline void bind( const vertex_type& vertex, std::initializer_list hyperedges ) @@ -641,6 +950,9 @@ class hypergraph final { this->bind(vertex, std::views::all(hyperedges)); } + /// @brief Binds a vertex to the tail (source) of a bf-directed hyperedge. + /// @param vertex_id The raw identifier of the vertex. + /// @param hyperedge_id The raw identifier of the hyperedge. void bind_tail(const id_type vertex_id, const id_type hyperedge_id) requires std::same_as { @@ -649,12 +961,18 @@ class hypergraph final { this->_impl.bind_tail(vertex_id, hyperedge_id); } + /// @brief Binds a vertex descriptor to the tail (source) of a bf-directed hyperedge descriptor. + /// @param vertex The `vertex_descriptor` mapping to the vertex. + /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. gl_attr_force_inline void bind_tail(const vertex_type& vertex, const hyperedge_type& hyperedge) requires std::same_as { this->bind_tail(vertex.id(), hyperedge.id()); } + /// @brief Binds a range of vertices to the tail of a single bf-directed hyperedge. + /// @param vertex_id_rng A range of raw vertex identifiers. + /// @param hyperedge_id The raw identifier of the hyperedge. void bind_tail( const traits::c_forward_range_of auto& vertex_id_rng, const id_type hyperedge_id ) @@ -667,6 +985,9 @@ class hypergraph final { } } + /// @brief Binds an initializer list of vertices to the tail of a single bf-directed hyperedge. + /// @param vertex_ids An initializer list of raw vertex identifiers. + /// @param hyperedge_id The raw identifier of the hyperedge. gl_attr_force_inline void bind_tail( std::initializer_list vertex_ids, const id_type hyperedge_id ) @@ -675,6 +996,9 @@ class hypergraph final { this->bind_tail(std::views::all(vertex_ids), hyperedge_id); } + /// @brief Binds a range of vertex descriptors to the tail of a single bf-directed hyperedge descriptor. + /// @param vertex_rng A range of `vertex_descriptor`s. + /// @param hyperedge The `hyperedge_descriptor` wrapping the target hyperedge. gl_attr_force_inline void bind_tail( const traits::c_forward_range_of auto& vertex_rng, const hyperedge_type& hyperedge @@ -684,6 +1008,9 @@ class hypergraph final { this->bind_tail(vertex_rng | std::views::transform(&vertex_type::id), hyperedge.id()); } + /// @brief Binds an initializer list of vertex descriptors to the tail of a single bf-directed hyperedge descriptor. + /// @param vertices An initializer list of `vertex_descriptor`s. + /// @param hyperedge The `hyperedge_descriptor` wrapping the target hyperedge. gl_attr_force_inline void bind_tail( std::initializer_list vertices, const hyperedge_type& hyperedge ) @@ -692,6 +1019,9 @@ class hypergraph final { this->bind_tail(std::views::all(vertices), hyperedge); } + /// @brief Binds a single vertex to the tails of a range of bf-directed hyperedges. + /// @param vertex_id The raw identifier of the vertex. + /// @param hyperedge_id_rng A range of raw hyperedge identifiers. void bind_tail( const id_type vertex_id, const traits::c_forward_range_of auto& hyperedge_id_rng ) @@ -704,6 +1034,9 @@ class hypergraph final { } } + /// @brief Binds a single vertex to the tails of an initializer list of bf-directed hyperedges. + /// @param vertex_id The raw identifier of the vertex. + /// @param hyperedge_ids An initializer list of raw hyperedge identifiers. gl_attr_force_inline void bind_tail( const id_type vertex_id, std::initializer_list hyperedge_ids ) @@ -712,6 +1045,9 @@ class hypergraph final { this->bind_tail(vertex_id, std::views::all(hyperedge_ids)); } + /// @brief Binds a single vertex descriptor to the tails of a range of bf-directed hyperedge descriptors. + /// @param vertex The target `vertex_descriptor`. + /// @param hyperedge_rng A range of `hyperedge_descriptor`s. gl_attr_force_inline void bind_tail( const vertex_type& vertex, const traits::c_forward_range_of auto& hyperedge_rng @@ -721,6 +1057,9 @@ class hypergraph final { this->bind_tail(vertex.id(), hyperedge_rng | std::views::transform(&hyperedge_type::id)); } + /// @brief Binds a single vertex descriptor to the tails of an initializer list of bf-directed hyperedge descriptors. + /// @param vertex The target `vertex_descriptor`. + /// @param hyperedges An initializer list of `hyperedge_descriptor`s. gl_attr_force_inline void bind_tail( const vertex_type& vertex, std::initializer_list hyperedges ) @@ -729,6 +1068,9 @@ class hypergraph final { this->bind_tail(vertex, std::views::all(hyperedges)); } + /// @brief Binds a vertex to the head (destination) of a bf-directed hyperedge. + /// @param vertex_id The raw identifier of the vertex. + /// @param hyperedge_id The raw identifier of the hyperedge. void bind_head(const id_type vertex_id, const id_type hyperedge_id) requires std::same_as { @@ -737,12 +1079,18 @@ class hypergraph final { this->_impl.bind_head(vertex_id, hyperedge_id); } + /// @brief Binds a vertex descriptor to the head (destination) of a bf-directed hyperedge descriptor. + /// @param vertex The `vertex_descriptor` mapping to the vertex. + /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. gl_attr_force_inline void bind_head(const vertex_type& vertex, const hyperedge_type& hyperedge) requires std::same_as { this->bind_head(vertex.id(), hyperedge.id()); } + /// @brief Binds a range of vertices to the head of a single bf-directed hyperedge. + /// @param vertex_id_rng A range of raw vertex identifiers. + /// @param hyperedge_id The raw identifier of the hyperedge. void bind_head( const traits::c_forward_range_of auto& vertex_id_rng, const id_type hyperedge_id ) @@ -755,6 +1103,9 @@ class hypergraph final { } } + /// @brief Binds an initializer list of vertices to the head of a single bf-directed hyperedge. + /// @param vertex_ids An initializer list of raw vertex identifiers. + /// @param hyperedge_id The raw identifier of the hyperedge. gl_attr_force_inline void bind_head( std::initializer_list vertex_ids, const id_type hyperedge_id ) @@ -763,6 +1114,9 @@ class hypergraph final { this->bind_head(std::views::all(vertex_ids), hyperedge_id); } + /// @brief Binds a range of vertex descriptors to the head of a single bf-directed hyperedge descriptor. + /// @param vertex_rng A range of `vertex_descriptor`s. + /// @param hyperedge The `hyperedge_descriptor` wrapping the target hyperedge. gl_attr_force_inline void bind_head( const traits::c_forward_range_of auto& vertex_rng, const hyperedge_type& hyperedge @@ -772,6 +1126,9 @@ class hypergraph final { this->bind_head(vertex_rng | std::views::transform(&vertex_type::id), hyperedge.id()); } + /// @brief Binds an initializer list of vertex descriptors to the head of a single bf-directed hyperedge descriptor. + /// @param vertices An initializer list of `vertex_descriptor`s. + /// @param hyperedge The `hyperedge_descriptor` wrapping the target hyperedge. gl_attr_force_inline void bind_head( std::initializer_list vertices, const hyperedge_type& hyperedge ) @@ -780,6 +1137,9 @@ class hypergraph final { this->bind_head(std::views::all(vertices), hyperedge); } + /// @brief Binds a single vertex to the heads of a range of bf-directed hyperedges. + /// @param vertex_id The raw identifier of the vertex. + /// @param hyperedge_id_rng A range of raw hyperedge identifiers. void bind_head( const id_type vertex_id, const traits::c_forward_range_of auto& hyperedge_id_rng ) @@ -792,6 +1152,9 @@ class hypergraph final { } } + /// @brief Binds a single vertex to the heads of an initializer list of bf-directed hyperedges. + /// @param vertex_id The raw identifier of the vertex. + /// @param hyperedge_ids An initializer list of raw hyperedge identifiers. gl_attr_force_inline void bind_head( const id_type vertex_id, std::initializer_list hyperedge_ids ) @@ -800,6 +1163,9 @@ class hypergraph final { this->bind_head(vertex_id, std::views::all(hyperedge_ids)); } + /// @brief Binds a single vertex descriptor to the heads of a range of bf-directed hyperedge descriptors. + /// @param vertex The target `vertex_descriptor`. + /// @param hyperedge_rng A range of `hyperedge_descriptor`s. gl_attr_force_inline void bind_head( const vertex_type& vertex, const traits::c_forward_range_of auto& hyperedge_rng @@ -809,6 +1175,9 @@ class hypergraph final { this->bind_head(vertex.id(), hyperedge_rng | std::views::transform(&hyperedge_type::id)); } + /// @brief Binds a single vertex descriptor to the heads of an initializer list of bf-directed hyperedge descriptors. + /// @param vertex The target `vertex_descriptor`. + /// @param hyperedges An initializer list of `hyperedge_descriptor`s. gl_attr_force_inline void bind_head( const vertex_type& vertex, std::initializer_list hyperedges ) @@ -817,30 +1186,48 @@ class hypergraph final { this->bind_head(vertex, std::views::all(hyperedges)); } + /// @brief Unbinds a vertex from a hyperedge entirely. + /// @param vertex_id The raw identifier of the vertex. + /// @param hyperedge_id The raw identifier of the hyperedge. void unbind(const id_type vertex_id, const id_type hyperedge_id) { this->_verify_vertex_id(vertex_id); this->_verify_hyperedge_id(hyperedge_id); this->_impl.unbind(vertex_id, hyperedge_id); } + /// @brief Unbinds a vertex from a hyperedge entirely using descriptors. + /// @param vertex The `vertex_descriptor` mapping to the vertex. + /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. gl_attr_force_inline void unbind(const vertex_type& vertex, const hyperedge_type& hyperedge) { this->unbind(vertex.id(), hyperedge.id()); } // --- incidence validators --- + /// @brief Evaluates whether a vertex and a hyperedge are currently incident. + /// @param vertex_id The raw identifier of the vertex. + /// @param hyperedge_id The raw identifier of the hyperedge. + /// @return `true` if the vertex belongs to the hyperedge (in any direction), `false` otherwise. [[nodiscard]] bool are_incident(const id_type vertex_id, const id_type hyperedge_id) const { this->_verify_vertex_id(vertex_id); this->_verify_hyperedge_id(hyperedge_id); return this->_impl.are_bound(vertex_id, hyperedge_id); } + /// @brief Evaluates whether a vertex and a hyperedge are currently incident using descriptors. + /// @param vertex The `vertex_descriptor` mapping to the vertex. + /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. + /// @return `true` if the vertex belongs to the hyperedge, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool are_incident( const vertex_type& vertex, const hyperedge_type& hyperedge ) const { return this->are_incident(vertex.id(), hyperedge.id()); } + /// @brief Evaluates whether a vertex is in the tail (source) of a bf-directed hyperedge. + /// @param vertex_id The raw identifier of the vertex. + /// @param hyperedge_id The raw identifier of the hyperedge. + /// @return `true` if the vertex is in the tail set, `false` otherwise. [[nodiscard]] bool is_tail(const id_type vertex_id, const id_type hyperedge_id) const requires std::same_as { @@ -849,6 +1236,10 @@ class hypergraph final { return this->_impl.is_tail(vertex_id, hyperedge_id); } + /// @brief Evaluates whether a vertex descriptor is in the tail of a bf-directed hyperedge descriptor. + /// @param vertex The `vertex_descriptor` mapping to the vertex. + /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. + /// @return `true` if the vertex is in the tail set, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool is_tail( const vertex_type& vertex, const hyperedge_type& hyperedge ) const @@ -857,6 +1248,10 @@ class hypergraph final { return this->is_tail(vertex.id(), hyperedge.id()); } + /// @brief Evaluates whether a vertex is in the head (destination) of a bf-directed hyperedge. + /// @param vertex_id The raw identifier of the vertex. + /// @param hyperedge_id The raw identifier of the hyperedge. + /// @return `true` if the vertex is in the head set, `false` otherwise. [[nodiscard]] bool is_head(const id_type vertex_id, const id_type hyperedge_id) const requires std::same_as { @@ -865,6 +1260,10 @@ class hypergraph final { return this->_impl.is_head(vertex_id, hyperedge_id); } + /// @brief Evaluates whether a vertex descriptor is in the head of a bf-directed hyperedge descriptor. + /// @param vertex The `vertex_descriptor` mapping to the vertex. + /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. + /// @return `true` if the vertex is in the head set, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool is_head( const vertex_type& vertex, const hyperedge_type& hyperedge ) const @@ -875,38 +1274,53 @@ class hypergraph final { // --- incidence getters --- + /// @brief Returns a transformed range of fully bound `hyperedge_descriptor`s incident to the specified vertex. + /// @param vertex_id The raw identifier of the vertex. [[nodiscard]] gl_attr_force_inline auto incident_hyperedges(const id_type vertex_id) { return this->incident_hyperedge_ids(vertex_id) | std::views::transform(this->_create_hyperedge_descriptor()); } + /// @brief Returns a transformed range of fully bound `hyperedge_descriptor`s incident to the specified vertex descriptor. + /// @param vertex The target `vertex_descriptor`. [[nodiscard]] gl_attr_force_inline auto incident_hyperedges(const vertex_type& vertex) { return this->incident_hyperedges(vertex.id()); } + /// @brief Returns a lightweight range of raw hyperedge IDs incident to the specified vertex. + /// @param vertex_id The raw identifier of the vertex. [[nodiscard]] auto incident_hyperedge_ids(const id_type vertex_id) const { this->_verify_vertex_id(vertex_id); return this->_impl.incident_hyperedges(vertex_id); } + /// @brief Returns a lightweight range of raw hyperedge IDs incident to the specified vertex descriptor. + /// @param vertex The target `vertex_descriptor`. [[nodiscard]] gl_attr_force_inline auto incident_hyperedge_ids(const vertex_type& vertex ) const { return this->incident_hyperedge_ids(vertex.id()); } + /// @brief Retrieves the total degree (number of incident hyperedges) of a vertex. + /// @param vertex_id The raw identifier of the vertex. [[nodiscard]] size_type degree(const id_type vertex_id) const { this->_verify_vertex_id(vertex_id); return this->_impl.degree(vertex_id); } + /// @brief Retrieves the total degree (number of incident hyperedges) of a vertex descriptor. + /// @param vertex The target `vertex_descriptor`. [[nodiscard]] gl_attr_force_inline size_type degree(const vertex_type& vertex) const { return this->degree(vertex.id()); } + /// @brief Generates a contiguous degree map for all valid vertices in the hypergraph. [[nodiscard]] std::vector degree_map() const { return this->_impl.degree_map(this->_n_vertices); } + /// @brief Returns a transformed range of fully bound `hyperedge_descriptor`s originating from the specified vertex (bf-directed). + /// @param vertex_id The raw identifier of the vertex. [[nodiscard]] gl_attr_force_inline auto out_hyperedges(const id_type vertex_id) const requires std::same_as { @@ -914,12 +1328,16 @@ class hypergraph final { | std::views::transform(this->_create_hyperedge_descriptor()); } + /// @brief Returns a transformed range of fully bound `hyperedge_descriptor`s originating from the specified vertex descriptor (bf-directed). + /// @param vertex The target `vertex_descriptor`. [[nodiscard]] gl_attr_force_inline auto out_hyperedges(const vertex_type& vertex) const requires std::same_as { return this->out_hyperedges(vertex.id()); } + /// @brief Returns a lightweight range of raw hyperedge IDs originating from the specified vertex (bf-directed). + /// @param vertex_id The raw identifier of the vertex. [[nodiscard]] auto out_hyperedge_ids(const id_type vertex_id) const requires std::same_as { @@ -927,12 +1345,16 @@ class hypergraph final { return this->_impl.out_hyperedges(vertex_id); } + /// @brief Returns a lightweight range of raw hyperedge IDs originating from the specified vertex descriptor (bf-directed). + /// @param vertex The target `vertex_descriptor`. [[nodiscard]] gl_attr_force_inline auto out_hyperedge_ids(const vertex_type& vertex) const requires std::same_as { return this->out_hyperedge_ids(vertex.id()); } + /// @brief Retrieves the out-degree (number of hyperedges where the vertex is in the Tail) of a vertex. + /// @param vertex_id The raw identifier of the vertex. [[nodiscard]] size_type out_degree(const id_type vertex_id) const requires std::same_as { @@ -940,18 +1362,23 @@ class hypergraph final { return this->_impl.out_degree(vertex_id); } + /// @brief Retrieves the out-degree (number of hyperedges where the vertex is in the Tail) of a vertex descriptor. + /// @param vertex The target `vertex_descriptor`. [[nodiscard]] gl_attr_force_inline size_type out_degree(const vertex_type& vertex) const requires std::same_as { return this->out_degree(vertex.id()); } + /// @brief Generates a contiguous out-degree map for all valid vertices in the bf-directed hypergraph. [[nodiscard]] std::vector out_degree_map() const requires std::same_as { return this->_impl.out_degree_map(this->_n_vertices); } + /// @brief Returns a transformed range of fully bound `hyperedge_descriptor`s entering the specified vertex (bf-directed). + /// @param vertex_id The raw identifier of the vertex. [[nodiscard]] gl_attr_force_inline auto in_hyperedges(const id_type vertex_id) const requires std::same_as { @@ -959,12 +1386,16 @@ class hypergraph final { | std::views::transform(this->_create_hyperedge_descriptor()); } + /// @brief Returns a transformed range of fully bound `hyperedge_descriptor`s entering the specified vertex descriptor (bf-directed). + /// @param vertex The target `vertex_descriptor`. [[nodiscard]] gl_attr_force_inline auto in_hyperedges(const vertex_type& vertex) const requires std::same_as { return this->in_hyperedges(vertex.id()); } + /// @brief Returns a lightweight range of raw hyperedge IDs entering the specified vertex (bf-directed). + /// @param vertex_id The raw identifier of the vertex. [[nodiscard]] auto in_hyperedge_ids(const id_type vertex_id) const requires std::same_as { @@ -972,12 +1403,16 @@ class hypergraph final { return this->_impl.in_hyperedges(vertex_id); } + /// @brief Returns a lightweight range of raw hyperedge IDs entering the specified vertex descriptor (bf-directed). + /// @param vertex The target `vertex_descriptor`. [[nodiscard]] gl_attr_force_inline auto in_hyperedge_ids(const vertex_type& vertex) const requires std::same_as { return this->in_hyperedge_ids(vertex.id()); } + /// @brief Retrieves the in-degree (number of hyperedges where the vertex is in the Head) of a vertex. + /// @param vertex_id The raw identifier of the vertex. [[nodiscard]] size_type in_degree(const id_type vertex_id) const requires std::same_as { @@ -985,52 +1420,70 @@ class hypergraph final { return this->_impl.in_degree(vertex_id); } + /// @brief Retrieves the in-degree (number of hyperedges where the vertex is in the Head) of a vertex descriptor. + /// @param vertex The target `vertex_descriptor`. [[nodiscard]] gl_attr_force_inline size_type in_degree(const vertex_type& vertex) const requires std::same_as { return this->in_degree(vertex.id()); } + /// @brief Generates a contiguous in-degree map for all valid vertices in the bf-directed hypergraph. [[nodiscard]] std::vector in_degree_map() const requires std::same_as { return this->_impl.in_degree_map(this->_n_vertices); } + /// @brief Returns a transformed range of fully bound `vertex_descriptor`s incident to the specified hyperedge. + /// @param hyperedge_id The raw identifier of the hyperedge. [[nodiscard]] auto incident_vertices(const id_type hyperedge_id) const { return this->incident_vertex_ids(hyperedge_id) | std::views::transform(this->_create_vertex_descriptor()); } + /// @brief Returns a transformed range of fully bound `vertex_descriptor`s incident to the specified hyperedge descriptor. + /// @param hyperedge The target `hyperedge_descriptor`. [[nodiscard]] gl_attr_force_inline auto incident_vertices(const hyperedge_type& hyperedge ) const { return this->incident_vertices(hyperedge.id()); } + /// @brief Returns a lightweight range of raw vertex IDs incident to the specified hyperedge. + /// @param hyperedge_id The raw identifier of the hyperedge. [[nodiscard]] auto incident_vertex_ids(const id_type hyperedge_id) const { this->_verify_hyperedge_id(hyperedge_id); return this->_impl.incident_vertices(hyperedge_id); } + /// @brief Returns a lightweight range of raw vertex IDs incident to the specified hyperedge descriptor. + /// @param hyperedge The target `hyperedge_descriptor`. [[nodiscard]] gl_attr_force_inline auto incident_vertex_ids(const hyperedge_type& hyperedge ) const { return this->incident_vertex_ids(hyperedge.id()); } + /// @brief Retrieves the size (total number of incident vertices) of a hyperedge. + /// @param hyperedge_id The raw identifier of the hyperedge. [[nodiscard]] size_type hyperedge_size(const id_type hyperedge_id) const { this->_verify_hyperedge_id(hyperedge_id); return this->_impl.hyperedge_size(hyperedge_id); } + /// @brief Retrieves the size (total number of incident vertices) of a hyperedge descriptor. + /// @param hyperedge The target `hyperedge_descriptor`. [[nodiscard]] gl_attr_force_inline size_type hyperedge_size(const hyperedge_type& hyperedge ) const { return this->hyperedge_size(hyperedge.id()); } + /// @brief Generates a contiguous map of sizes for all valid hyperedges in the hypergraph. [[nodiscard]] std::vector hyperedge_size_map() const { return this->_impl.hyperedge_size_map(this->_n_hyperedges); } + /// @brief Returns a transformed range of fully bound `vertex_descriptor`s in the tail of the specified bf-directed hyperedge. + /// @param hyperedge_id The raw identifier of the hyperedge. [[nodiscard]] gl_attr_force_inline auto tail(const id_type hyperedge_id) const requires std::same_as { @@ -1038,12 +1491,16 @@ class hypergraph final { | std::views::transform(this->_create_vertex_descriptor()); } + /// @brief Returns a transformed range of fully bound `vertex_descriptor`s in the tail of the specified bf-directed hyperedge descriptor. + /// @param hyperedge The target `hyperedge_descriptor`. [[nodiscard]] gl_attr_force_inline auto tail(const hyperedge_type& hyperedge) const requires std::same_as { return this->tail(hyperedge.id()); } + /// @brief Returns a lightweight range of raw vertex IDs in the tail of the specified bf-directed hyperedge. + /// @param hyperedge_id The raw identifier of the hyperedge. [[nodiscard]] auto tail_ids(const id_type hyperedge_id) const requires std::same_as { @@ -1051,12 +1508,16 @@ class hypergraph final { return this->_impl.tail(hyperedge_id); } + /// @brief Returns a lightweight range of raw vertex IDs in the tail of the specified bf-directed hyperedge descriptor. + /// @param hyperedge The target `hyperedge_descriptor`. [[nodiscard]] gl_attr_force_inline auto tail_ids(const hyperedge_type& hyperedge) const requires std::same_as { return this->tail_ids(hyperedge.id()); } + /// @brief Retrieves the tail size (number of tail vertices) of a bf-directed hyperedge. + /// @param hyperedge_id The raw identifier of the hyperedge. [[nodiscard]] size_type tail_size(const id_type hyperedge_id) const requires std::same_as { @@ -1064,18 +1525,23 @@ class hypergraph final { return this->_impl.tail_size(hyperedge_id); } + /// @brief Retrieves the tail size (number of tail vertices) of a bf-directed hyperedge descriptor. + /// @param hyperedge The target `hyperedge_descriptor`. [[nodiscard]] gl_attr_force_inline size_type tail_size(const hyperedge_type& hyperedge) const requires std::same_as { return this->tail_size(hyperedge.id()); } + /// @brief Generates a contiguous map of tail sizes for all valid bf-directed hyperedges. [[nodiscard]] std::vector tail_size_map() const requires std::same_as { return this->_impl.tail_size_map(this->_n_hyperedges); } + /// @brief Returns a transformed range of fully bound `vertex_descriptor`s in the head of the specified bf-directed hyperedge. + /// @param hyperedge_id The raw identifier of the hyperedge. [[nodiscard]] gl_attr_force_inline auto head(const id_type hyperedge_id) const requires std::same_as { @@ -1083,12 +1549,16 @@ class hypergraph final { | std::views::transform(this->_create_vertex_descriptor()); } + /// @brief Returns a transformed range of fully bound `vertex_descriptor`s in the head of the specified bf-directed hyperedge descriptor. + /// @param hyperedge The target `hyperedge_descriptor`. [[nodiscard]] gl_attr_force_inline auto head(const hyperedge_type& hyperedge) const requires std::same_as { return this->head(hyperedge.id()); } + /// @brief Returns a lightweight range of raw vertex IDs in the head of the specified bf-directed hyperedge. + /// @param hyperedge_id The raw identifier of the hyperedge. [[nodiscard]] auto head_ids(const id_type hyperedge_id) const requires std::same_as { @@ -1096,12 +1566,16 @@ class hypergraph final { return this->_impl.head(hyperedge_id); } + /// @brief Returns a lightweight range of raw vertex IDs in the head of the specified bf-directed hyperedge descriptor. + /// @param hyperedge The target `hyperedge_descriptor`. [[nodiscard]] gl_attr_force_inline auto head_ids(const hyperedge_type& hyperedge) const requires std::same_as { return this->head_ids(hyperedge.id()); } + /// @brief Retrieves the head size (number of head vertices) of a bf-directed hyperedge. + /// @param hyperedge_id The raw identifier of the hyperedge. [[nodiscard]] size_type head_size(const id_type hyperedge_id) const requires std::same_as { @@ -1109,12 +1583,15 @@ class hypergraph final { return this->_impl.head_size(hyperedge_id); } + /// @brief Retrieves the head size (number of head vertices) of a bf-directed hyperedge descriptor. + /// @param hyperedge The target `hyperedge_descriptor`. [[nodiscard]] gl_attr_force_inline size_type head_size(const hyperedge_type& hyperedge) const requires std::same_as { return this->head_size(hyperedge.id()); } + /// @brief Generates a contiguous map of head sizes for all valid bf-directed hyperedges. [[nodiscard]] std::vector head_size_map() const requires std::same_as { @@ -1123,6 +1600,7 @@ class hypergraph final { // --- comparison --- + /// @brief Compares two hypergraphs for strict equality, checking topologies and properties. [[nodiscard]] friend bool operator==(const hypergraph& lhs, const hypergraph& rhs) noexcept { if (lhs._n_vertices != rhs._n_vertices or lhs._n_hyperedges != rhs._n_hyperedges) return false; @@ -1140,11 +1618,15 @@ class hypergraph final { // --- I/O utility --- + /// @brief Helper structure used to correctly format an individual hyperedge into an output stream. struct hyperedge_formatter { public: + /// @brief The hypergraph owning the hyperedge. const hypergraph& hg; + /// @brief The hyperedge descriptor to be formatted. const hyperedge_type hyperedge; + /// @brief Stream insertion operator for undirected hyperedge formatters. friend std::ostream& operator<<(std::ostream& os, const hyperedge_formatter& proxy) requires std::same_as { @@ -1168,6 +1650,7 @@ class hypergraph final { return os; } + /// @brief Stream insertion operator for bf-directed hyperedge formatters. friend std::ostream& operator<<(std::ostream& os, const hyperedge_formatter& proxy) requires std::same_as { @@ -1194,10 +1677,20 @@ class hypergraph final { } }; + /// @brief Retrieves a formatter object that safely encapsulates a hyperedge for stream output. + /// @param hyperedge The target `hyperedge_descriptor`. + /// @return A `hyperedge_formatter` structure prepared for standard stream insertion. [[nodiscard]] hyperedge_formatter display(const hyperedge_type& hyperedge) const { return hyperedge_formatter{*this, hyperedge}; } + /// @brief Formats and outputs the entire hypergraph structure to a standard output stream. + /// + /// Behaves dynamically based on whether the `gl::io::verbose` or `gl::io::spec_fmt` stream manipulators are set. + /// + /// @param os The target output stream. + /// @param hg The hypergraph instance to serialize or print. + /// @return The stream reference for chaining. friend std::ostream& operator<<(std::ostream& os, const hypergraph& hg) { using enum io::detail::option_bit; @@ -1210,30 +1703,32 @@ class hypergraph final { return hg._concise_write(os); } + /// @brief Reads and deserializes an entire hypergraph from a given stream using HGSF format. + /// @param is The input stream containing HGSF formatted data. + /// @param hg The hypergraph instance to populate. + /// @return The stream reference for chaining. friend gl_attr_force_inline std::istream& operator>>(std::istream& is, hypergraph& hg) { return hg._hgsf_read(is); } // --- friend declarations --- + /// @brief Creates a deep copy of a given hypergraph. template friend Hypergraph clone(const Hypergraph& source); + /// @brief Converts a hypergraph to a different implementation type. template friend auto to(Hypergraph&& source); + /// @brief Internal structure for dispatching implementation conversion. template < traits::c_hypergraph_impl_tag TargetImplTag, traits::c_hypergraph_impl_tag SourceImplTag> friend struct detail::to_impl; private: - hypergraph(const hypergraph& other) - : _n_vertices{other._n_vertices}, - _n_hyperedges{other._n_hyperedges}, - _impl{other._impl}, - _vertex_properties{other._vertex_properties}, - _hyperedge_properties{other._hyperedge_properties} {} + hypergraph(const hypergraph& other) = default; // --- vertex methods --- @@ -1520,9 +2015,12 @@ class hypergraph final { // --- data members --- + /// @brief The current count of initialized vertices. size_type _n_vertices = 0uz; + /// @brief The current count of initialized hyperedges. size_type _n_hyperedges = 0uz; + /// @brief The underlying container implementation (matrix or list). implementation_type _impl{}; /// @todo Replace mutability with proper const-correct getter overloads to ensure thread safety guarantees associated with the const qualifier @@ -1539,6 +2037,8 @@ template return Hypergraph(source); } +/// @ingroup HGL-Core +/// @brief Convenience alias for an undirected hypergraph. template < traits::c_properties VertexProperties = empty_properties, traits::c_properties HyperedgeProperties = empty_properties, @@ -1546,6 +2046,8 @@ template < using undirected_hypergraph = hypergraph>; +/// @ingroup HGL-Core +/// @brief Convenience alias for a bf-directed hypergraph. template < traits::c_properties VertexProperties = empty_properties, traits::c_properties HyperedgeProperties = empty_properties, @@ -1553,6 +2055,8 @@ template < using bf_directed_hypergraph = hypergraph>; +/// @ingroup HGL-Core +/// @brief Convenience alias for a hypergraph backed by a standard incidence list. template < traits::c_hypergraph_layout_tag LayoutTag = impl::bidirectional_t, traits::c_hypergraph_directional_tag DirectionalTag = undirected_t, @@ -1562,6 +2066,8 @@ template < using list_hypergraph = hypergraph< list_hypergraph_traits>; +/// @ingroup HGL-Core +/// @brief Convenience alias for a hypergraph backed by a flat incidence list. template < traits::c_hypergraph_layout_tag LayoutTag = impl::bidirectional_t, traits::c_hypergraph_directional_tag DirectionalTag = undirected_t, @@ -1575,6 +2081,8 @@ using flat_list_hypergraph = hypergraph>; +/// @ingroup HGL-Core +/// @brief Convenience alias for a hypergraph backed by a standard incidence matrix. template < traits::c_hypergraph_layout_tag LayoutTag = impl::bidirectional_t, traits::c_hypergraph_directional_tag DirectionalTag = undirected_t, @@ -1584,6 +2092,8 @@ template < using matrix_hypergraph = hypergraph< matrix_hypergraph_traits>; +/// @ingroup HGL-Core +/// @brief Convenience alias for a hypergraph backed by a flat incidence matrix. template < traits::c_hypergraph_layout_tag LayoutTag = impl::bidirectional_t, traits::c_hypergraph_directional_tag DirectionalTag = undirected_t, @@ -1599,34 +2109,46 @@ using flat_matrix_hypergraph = hypergraph Date: Tue, 28 Apr 2026 20:41:47 +0200 Subject: [PATCH 08/28] hypergraph docs refinement --- include/gl/graph.hpp | 314 ++++----- include/hgl/hypergraph.hpp | 1304 +++++++++++++++++++++++------------- 2 files changed, 984 insertions(+), 634 deletions(-) diff --git a/include/gl/graph.hpp b/include/gl/graph.hpp index f72bccd0..f602bbd7 100644 --- a/include/gl/graph.hpp +++ b/include/gl/graph.hpp @@ -106,15 +106,16 @@ struct to_impl; } // namespace detail /// @ingroup GL GL-Core -/// @brief The primary graph container using a policy-based architecture. +/// @brief The generic graph container using a policy-based architecture. /// -/// `graph` relies on the provided `GraphTraits` to determine its behavior, properties -/// (such as directionality), and underlying memory representation. It exposes a unified +/// This class relies on the provided `GraphTraits` to determine its behavior, element +/// property types, and the underlying memory representation. It exposes a unified /// API for adding, removing, and iterating over vertices and edges regardless of the backend. /// /// ### Key Features /// - **Policy-based design**: Behavior and representation are determined by `GraphTraits`. -/// - **Flexible directionality**: Support for both directed and undirected graphs. +/// - **Zero-cost Abstractions**: Core query logic is resolved at compile time through implementation tags and static dispatch, removing unnecessary overhead. +/// - **Configurable directionality**: Support for both directed and undirected graphs. /// - **Multiple representations**: Choose the underlying memory model that best suits your algorithmic and cache-locality needs: /// - @ref gl::impl::list_t "list_t": Standard adjacency list. /// - @ref gl::impl::flat_list_t "flat_list_t": Flattened adjacency list. @@ -122,6 +123,7 @@ struct to_impl; /// - @ref gl::impl::flat_matrix_t "flat_matrix_t": Flattened adjacency matrix. /// - **Property support**: Vertices and edges can carry arbitrary properties. /// - **Unified API**: Consistent interface regardless of the underlying implementation. +/// - **Standard Range Support**: Exposes lightweight views compliant with C++20 `std::ranges`, enabling functional-style iteration and algorithms. /// /// ### Basic Definitions /// A graph \f$G = (V, E)\f$ consists of a set of vertices \f$V\f$ and a set of edges \f$E\f$. @@ -152,6 +154,8 @@ struct to_impl; /// for (auto neighbor : g.neighbors(v0)) // (5)! /// process(neighbor); /// +/// std::cout << "Topology:\n" << g << '\n'; // (6)! +/// /// return 0; /// } /// ``` @@ -166,29 +170,31 @@ struct to_impl; /// /// 5\. Iterate over neighbors of `v0`. /// +/// 6\. Utilize the builtin I/O stream support of the `graph` class to print its topology to the console. +/// /// ### API Design: IDs vs. Descriptors -/// The graph exposes a dual API to accommodate different performance and ergonomic needs: +/// The `graph` class exposes a dual API to accommodate different performance and ergonomic needs: /// /// - **Inputs**: Most query methods are overloaded to accept either a raw `id_type` or a `vertex_type`/`edge_type` descriptor. They are functionally identical. /// - **Outputs**: Methods ending in `_ids` (e.g., `neighbor_ids`) return views of raw integral IDs. Methods without this suffix (e.g., `neighbors`) automatically map those IDs to the proper descriptor objects. -/// - **Performance**: Descriptor-returning methods incur a slight overhead if the graph utilizes rich properties, as the descriptor must fetch the property payload. If you only need topology, prefer the `_ids` variants. +/// - **Performance**: Descriptor-returning methods incur a slight overhead if the graph utilizes properties, as the property reference must be fetched and bound to each descriptor. If you only need topology, prefer the `_ids` variants. /// /// ### Descriptor Invalidation Behavior /// The graph maintains the following invalidation semantics: /// -/// - **Vertex addition**: Does not invalidate vertex IDs. However, property references stored in vertex descriptors may be invalidated. -/// - **Vertex removal**: Invalidates vertex descriptors, IDs, and property references. Subsequent vertex IDs may shift depending on the implementation. +/// - **Vertex addition**: Does not invalidate vertex IDs. However, property references stored in existing vertex descriptors may be invalidated. Has no effect on edge descriptors. +/// - **Vertex removal**: May invalidate both vertex and edge descriptors, IDs, and property references. /// - **Edge addition**: Does not invalidate vertex or edge IDs. However, property references stored in edge descriptors may be invalidated. -/// - **Edge removal**: Invalidates edge descriptors, IDs, and property references. Vertex descriptors remain valid. +/// - **Edge removal**: Invalidates edge descriptors, IDs, and property references. Has no effect on vertex descriptors. /// - **Property access**: References to vertex or edge properties obtained from the map may be invalidated by modifications to the graph structure. /// /// ### Template Parameters /// | Parameter | Description | Constraint | /// | :-------- | :--- | :--- | -/// | GraphTraits | Traits struct specifying behavior and representation | An instantiation of @ref gl::graph_traits "graph_traits" | +/// | GraphTraits | Traits struct specifying the behavior and representation of the graph. | An instantiation of @ref gl::graph_traits "graph_traits" | /// /// ### See Also -/// - @ref gl::directed_graph "directed_graph" : Convenience alias for directed graphs with standard list-based representation. +/// - @ref gl::directed_graph "directed_graph" : Convenience alias for directed graphs. /// - @ref gl::undirected_graph "undirected_graph" : Convenience alias for undirected graphs. /// - @ref gl::clone "clone" : Create a deep copy of a graph. /// - @ref gl::to "to" : Convert a graph to a different implementation. @@ -215,7 +221,7 @@ class graph final { /// @brief Type tag indicating the underlying implementation model. using implementation_tag = typename traits_type::implementation_tag; - /// @brief The internal implementation structure managing the adjacency logic. + /// @brief The underlying implementation type matching the directional tag. using implementation_type = typename implementation_tag::template type; friend implementation_type; @@ -253,12 +259,12 @@ class graph final { this->_vertex_properties.resize(n_vertices); } - /// @brief Move constructor transfers ownership from another graph. + /// @brief Default move constructor. graph(graph&&) noexcept = default; - /// @brief Move assignment transfers ownership from another graph. + /// @brief Default move assignment operator. graph& operator=(graph&&) noexcept = default; - /// @brief Destructor cleans up graph memory. + /// @brief Default destructor. ~graph() = default; /// @brief Graph copy assignment is disabled to avoid accidental copies. Use @ref gl::clone "clone" instead. @@ -281,7 +287,7 @@ class graph final { // --- vertex modifiers --- /// @brief Adds a new, default-initialized vertex to the graph. - /// @return A descriptor for the newly created vertex. + /// @return A descriptor of the newly created vertex. /// @copydetails detail::graph_doc_anchors::add_vertex_note() vertex_type add_vertex() { this->_impl.add_vertex(); @@ -293,9 +299,9 @@ class graph final { return vertex_descriptor{new_vertex_id}; } - /// @brief Adds a new vertex with specific properties. + /// @brief Adds a new vertex with the given properties to the graph. /// @param properties The property payload for the new vertex. - /// @return A descriptor for the newly created vertex. + /// @return A descriptor of the newly created vertex. /// @copydetails detail::graph_doc_anchors::add_vertex_note() vertex_type add_vertex_with(vertex_properties_type properties) requires(traits::c_non_empty_properties) @@ -307,7 +313,7 @@ class graph final { }; } - /// @brief Adds a specified number of default-initialized vertices to the graph en masse. + /// @brief Adds a specified number of default-initialized vertices to the graph. /// @param n The number of vertices to add. /// @copydetails detail::graph_doc_anchors::add_vertex_note() void add_vertices(const size_type n) { @@ -378,7 +384,7 @@ class graph final { /// @throws std::invalid_argument If any vertex descriptor is invalid. /// @copydetails detail::graph_doc_anchors::remove_vertex_wrn() gl_attr_force_inline void remove_vertices( - const traits::c_sized_range_of auto& vertex_rng + const traits::c_forward_range_of auto& vertex_rng ) { this->remove_vertices( vertex_rng | std::views::transform([](const auto& v) { return v.id(); }) @@ -436,6 +442,10 @@ class graph final { /// @brief Returns a vertex descriptor without bounds checking (array access style). /// @param vertex_id The ID of the vertex. /// @return The corresponding vertex descriptor. + /// + /// > [!WARNING] Undefined Behavior + /// > + /// > No bounds checking is performed. Passing an invalid ID results in Undefined Behavior. [[nodiscard]] gl_attr_force_inline vertex_type operator[](const id_type vertex_id ) const noexcept { return this->vertex_unchecked(vertex_id); @@ -579,7 +589,7 @@ class graph final { return this->_vertex_properties[id]; } - /// @brief Retrieves a view over all vertex properties in the graph. + /// @brief Retrieves a random-access view over all vertex properties in the graph. /// @return A view mapping each active vertex index to its property. [[nodiscard]] gl_attr_force_inline auto vertex_properties_map() const noexcept requires(traits::c_non_empty_properties) @@ -910,9 +920,9 @@ class graph final { return this->edges(source.id(), target.id()); } - /// @brief Retrieves all incident edges attached to a vertex. + /// @brief Retrieves all edges incident with a vertex. /// @param vertex_id The vertex ID. - /// @return A view or container representing the incident edges. + /// @return A view representing the set of incident edges. /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] inline auto incident_edges(const id_type vertex_id) const { this->_verify_vertex_id(vertex_id); @@ -922,17 +932,17 @@ class graph final { return this->_impl.incident_edges(vertex_id); } - /// @brief Retrieves all incident edges attached to a vertex. + /// @brief Retrieves all incident with a vertex. /// @param vertex The vertex descriptor. - /// @return A view or container representing the incident edges. + /// @return A view representing the set of incident edges. /// @throws std::invalid_argument If the vertex descriptor is invalid. [[nodiscard]] gl_attr_force_inline auto incident_edges(vertex_type vertex) const { return this->incident_edges(vertex.id()); } - /// @brief Retrieves all incoming edges entering a vertex. + /// @brief Retrieves all incoming edges of a vertex (going into the vertex). /// @param vertex_id The vertex ID. - /// @return A view or container representing the incoming edges. + /// @return A view representing the set of incoming edges. /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] inline auto in_edges(const id_type vertex_id) const { this->_verify_vertex_id(vertex_id); @@ -942,17 +952,17 @@ class graph final { return this->_impl.in_edges(vertex_id); } - /// @brief Retrieves all incoming edges entering a vertex. + /// @brief Retrieves all incoming edges of a vertex (going into the vertex). /// @param vertex The vertex descriptor. - /// @return A view or container representing the incoming edges. + /// @return A view representing the set of incoming edges. /// @throws std::invalid_argument If the vertex descriptor is invalid. [[nodiscard]] gl_attr_force_inline auto in_edges(vertex_type vertex) const { return this->in_edges(vertex.id()); } - /// @brief Retrieves all outgoing edges leaving a vertex. + /// @brief Retrieves all outgoing edges of a vertex (going out of the vertex). /// @param vertex_id The vertex ID. - /// @return A view or container representing the outgoing edges. + /// @return A view representing the set of outgoing edges. /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] inline auto out_edges(const id_type vertex_id) const { this->_verify_vertex_id(vertex_id); @@ -962,9 +972,9 @@ class graph final { return this->_impl.out_edges(vertex_id); } - /// @brief Retrieves all outgoing edges leaving a vertex. + /// @brief Retrieves all outgoing edges of a vertex (going out of the vertex). /// @param vertex The vertex descriptor. - /// @return A view or container representing the outgoing edges. + /// @return A view representing the set of outgoing edges. /// @throws std::invalid_argument If the vertex descriptor is invalid. [[nodiscard]] gl_attr_force_inline auto out_edges(vertex_type vertex) const { return this->out_edges(vertex.id()); @@ -1110,9 +1120,9 @@ class graph final { // --- stream operators --- - /// @brief Serializes the graph's string representation to an output stream. + /// @brief Formats and outputs the entire graph structure to a standard output stream. /// - /// Depending on active stream flags, this outputs in verbose, concise, or GSF formats. + /// The generated string representation of the graph depends on the currently active formatting options of the stream. /// /// @param os The target output stream. /// @param g The graph instance to write. @@ -1129,7 +1139,7 @@ class graph final { return g._concise_write(os); } - /// @brief Deserializes graph structure data from an input stream (GSF format). + /// @brief Deserializes graph structure data from an input stream (using the GSF format). /// @param is The source input stream. /// @param g The graph instance to populate. /// @return The stream reference for chaining. @@ -1410,7 +1420,7 @@ template } /// @ingroup GL GL-Core -/// @brief Convenience alias for defining a standard directed graph. +/// @brief Convenience alias for defining a directed graph. template < traits::c_properties VertexProperties = empty_properties, traits::c_properties EdgeProperties = empty_properties, @@ -1420,7 +1430,7 @@ using directed_graph = graph>; /// @ingroup GL GL-Core -/// @brief Convenience alias for defining a standard undirected graph. +/// @brief Convenience alias for defining a nundirected graph. template < traits::c_properties VertexProperties = empty_properties, traits::c_properties EdgeProperties = empty_properties, @@ -1430,7 +1440,7 @@ using undirected_graph = graph>; /// @ingroup GL GL-Core -/// @brief Convenience alias for defining a graph utilizing an adjacency list. +/// @brief Convenience alias for defining a graph utilizing an adjacency list implementation model. template < traits::c_graph_directional_tag DirectionalTag = directed_t, traits::c_properties VertexProperties = empty_properties, @@ -1440,7 +1450,7 @@ using list_graph = graph>; /// @ingroup GL GL-Core -/// @brief Convenience alias for defining a graph utilizing an adjacency matrix. +/// @brief Convenience alias for defining a graph utilizing an adjacency matrix implementation model. template < traits::c_graph_directional_tag DirectionalTag = directed_t, traits::c_properties VertexProperties = empty_properties, @@ -1450,7 +1460,7 @@ using matrix_graph = graph>; /// @ingroup GL GL-Core -/// @brief Convenience alias for defining a graph utilizing a flattened adjacency list. +/// @brief Convenience alias for defining a graph utilizing a flattened adjacency list implementation model. template < traits::c_graph_directional_tag DirectionalTag = directed_t, traits::c_properties VertexProperties = empty_properties, @@ -1460,7 +1470,7 @@ using flat_list_graph = graph>; /// @ingroup GL GL-Core -/// @brief Convenience alias for defining a graph utilizing a flattened adjacency matrix. +/// @brief Convenience alias for defining a graph utilizing a flattened adjacency matrix implementation model. template < traits::c_graph_directional_tag DirectionalTag = directed_t, traits::c_properties VertexProperties = empty_properties, @@ -1514,122 +1524,120 @@ template return static_cast(1ll); } -namespace detail { +namespace detail::graph_doc_anchors { -struct graph_doc_anchors { - // --- callouts --- +// --- callouts --- - /// > [!IMPORTANT] ID Stability - /// > - /// > Adding vertices does **not** invalidate existing vertex IDs. **However**, property references stored in vertex descriptors may be invalidated. - void add_vertex_note(); - - /// > [!WARNING] Descriptor and ID Invalidation - /// > - /// > Removing a vertex invalidates: - /// > - All vertex descriptors and IDs for vertices with higher IDs (they shift down). - /// > - All edge descriptors and IDs for edges incident to this vertex. - /// > - All references to vertex and edge properties obtained from the property maps. - /// > - References to vertex properties obtained via `vertex_properties()`. - /// > - /// > Proceed with caution when maintaining external vertex IDs or edge descriptors. - void remove_vertex_wrn(); - - /// > [!IMPORTANT] ID Stability - /// > - /// > Adding edges does **not** invalidate vertex or edge IDs. **However**, property references stored in edge descriptors may be invalidated. - void add_edge_note(); - - /// > [!WARNING] Edge Descriptor Invalidation - /// > - /// > Removing an edge invalidates: - /// > - All edge descriptors and IDs for edges with higher IDs (they shift down). - /// > - References to edge properties obtained via `edge_properties()`. - /// > - References to edge properties obtained from `edge_properties_map()`. - /// > - /// > Vertex descriptors and IDs remain valid. - void remove_edge_wrn(); +/// > [!IMPORTANT] ID Stability +/// > +/// > Adding vertices does **not** invalidate existing vertex IDs. **However**, property references stored in existing vertex descriptors may be invalidated. +void add_vertex_note(); - // --- definitions --- +/// > [!WARNING] Descriptor and ID Invalidation +/// > +/// > Removing a vertex invalidates: +/// > - All vertex descriptors and IDs for vertices with higher IDs (they shift down). +/// > - All edge descriptors and IDs for edges incident to this vertex. +/// > - All references to vertex and edge properties obtained from the property maps. +/// > - References to vertex properties obtained via `vertex_properties()`. +/// > +/// > Proceed with caution when maintaining external vertex IDs or edge descriptors. +void remove_vertex_wrn(); - /// ### Formal Definition - /// - /// The neighborhood \f$N(v)\f$ of a vertex \f$v\f$ is the set of all its adjacent vertices: - /// - /// \f[ - /// N(v) = - /// \begin{cases} - /// \{u \in V : \{u, v\} \in E\} & \text{if } G \text{ is undirected} - /// \\\\ \{u \in V : (u, v) \in E \lor (v, u) \in E\} & \text{if } G \text{ is directed} - /// \end{cases} - /// \f] - void neighbors(); +/// > [!IMPORTANT] ID Stability +/// > +/// > Adding edges does **not** invalidate vertex or edge IDs. **However**, property references stored in existing edge descriptors may be invalidated. +void add_edge_note(); - /// ### Formal Definition - /// The set of predecessors (in-neighborhood) \f$N_{in}(v)\f$ is defined as: - /// - /// \f[ - /// N_{in}(v) = - /// \begin{cases} - /// N(v) & \text{if } G \text{ is undirected} - /// \\\\ \{u \in V : (u, v) \in E\} & \text{if } G \text{ is directed} - /// \end{cases} - /// \f] - void predecessors(); +/// > [!WARNING] Edge Descriptor Invalidation +/// > +/// > Removing an edge invalidates: +/// > - All edge descriptors and IDs for edges with higher IDs (they shift down). +/// > - References to edge properties obtained via `edge_properties()`. +/// > - References to edge properties obtained from `edge_properties_map()`. +/// > +/// > Vertex descriptors and IDs remain valid. +void remove_edge_wrn(); - /// ### Formal Definition - /// The set of successors (out-neighborhood) \f$N_{out}(v)\f$ is defined as: - /// - /// \f[ - /// N_{out}(v) = - /// \begin{cases} - /// N(v) & \text{if } G \text{ is undirected} - /// \\\\ \{u \in V : (v, u) \in E\} & \text{if } G \text{ is directed} - /// \end{cases} - /// \f] - void successors(); - - /// The degree is the total number of edge endpoints connected to the vertex. - /// For both directed and undirected graphs, a self-loop contributes **2** to the total degree. - /// - /// ### Formal Definition - /// The formal calculation, accounting for the set of loops \f$L(v)\f$, is defined as: - /// - /// \f[ - /// deg(v) = - /// \begin{cases} - /// deg_{in}(v) + deg_{out}(v) & \text{if } G \text{ is directed} - /// \\ 2 \cdot |L(v)| + |E(v) \setminus L(v)| & \text{if } G \text{ is undirected} - /// \end{cases} - /// \f] - void degree(); - - /// The in-degree is the number of edges directed into the vertex. - /// - /// ### Formal Definition - /// - /// \f[ - /// deg_{in}(v) = - /// \begin{cases} - /// deg(v) & \text{if } G \text{ is undirected} - /// \\\\ |E_{in}(v)| = |\{u \in V : (u, v) \in E\}| & \text{if } G \text{ is directed} - /// \end{cases} - /// \f] - void in_degree(); - - /// The out-degree is the number of edges directed out of the vertex. - /// - /// ### Formal Definition - /// - /// \f[ - /// deg_{out}(v) = - /// \begin{cases} - /// deg(v) & \text{if } G \text{ is undirected} - /// \\\\ |E_{out}(v)| = |\{u \in V : (v, u) \in E\}| & \text{if } G \text{ is directed} - /// \end{cases} - /// \f] - void out_degree(); -}; +// --- definitions --- -} // namespace detail +/// ### Formal Definition +/// +/// The neighborhood \f$N(v)\f$ of a vertex \f$v\f$ is the set of all its adjacent vertices: +/// +/// \f[ +/// N(v) = +/// \begin{cases} +/// \{u \in V : \{u, v\} \in E\} & \text{if } G \text{ is undirected} +/// \\\\ \{u \in V : (u, v) \in E \lor (v, u) \in E\} & \text{if } G \text{ is directed} +/// \end{cases} +/// \f] +void neighbors(); + +/// ### Formal Definition +/// The set of predecessors (in-neighborhood) \f$N_{in}(v)\f$ is defined as: +/// +/// \f[ +/// N_{in}(v) = +/// \begin{cases} +/// N(v) & \text{if } G \text{ is undirected} +/// \\\\ \{u \in V : (u, v) \in E\} & \text{if } G \text{ is directed} +/// \end{cases} +/// \f] +void predecessors(); + +/// ### Formal Definition +/// The set of successors (out-neighborhood) \f$N_{out}(v)\f$ is defined as: +/// +/// \f[ +/// N_{out}(v) = +/// \begin{cases} +/// N(v) & \text{if } G \text{ is undirected} +/// \\\\ \{u \in V : (v, u) \in E\} & \text{if } G \text{ is directed} +/// \end{cases} +/// \f] +void successors(); + +/// The degree is the total number of edge endpoints connected to the vertex. +/// For both directed and undirected graphs, a self-loop contributes **2** to the total degree. +/// +/// ### Formal Definition +/// The formal calculation, accounting for the set of loops \f$L(v)\f$, is defined as: +/// +/// \f[ +/// deg(v) = +/// \begin{cases} +/// deg_{in}(v) + deg_{out}(v) & \text{if } G \text{ is directed} +/// \\ 2 \cdot |L(v)| + |E(v) \setminus L(v)| & \text{if } G \text{ is undirected} +/// \end{cases} +/// \f] +void degree(); + +/// The in-degree is the number of edges directed into the vertex. +/// +/// ### Formal Definition +/// +/// \f[ +/// deg_{in}(v) = +/// \begin{cases} +/// deg(v) & \text{if } G \text{ is undirected} +/// \\\\ |E_{in}(v)| = |\{u \in V : (u, v) \in E\}| & \text{if } G \text{ is directed} +/// \end{cases} +/// \f] +void in_degree(); + +/// The out-degree is the number of edges directed out of the vertex. +/// +/// ### Formal Definition +/// +/// \f[ +/// deg_{out}(v) = +/// \begin{cases} +/// deg(v) & \text{if } G \text{ is undirected} +/// \\\\ |E_{out}(v)| = |\{u \in V : (v, u) \in E\}| & \text{if } G \text{ is directed} +/// \end{cases} +/// \f] +void out_degree(); + +} // namespace detail::graph_doc_anchors } // namespace gl diff --git a/include/hgl/hypergraph.hpp b/include/hgl/hypergraph.hpp index 61d7c9f7..37f14b00 100644 --- a/include/hgl/hypergraph.hpp +++ b/include/hgl/hypergraph.hpp @@ -125,101 +125,159 @@ struct to_impl; } // namespace detail /// @ingroup HGL-Core -/// @brief The primary, highly configurable generic container for representing n-ary hypergraphs. +/// @brief The generic hypergraph container using a policy-based design. /// -/// **Module:** Part of the @ref HGL-Core "Core Hypergraph Components" group. -/// -/// Unlike standard graphs where edges connect exactly two vertices, hypergraphs generalize this -/// concept by allowing a single hyperedge to connect any number of vertices simultaneously. -/// The `hgl::hypergraph` template provides a strictly type-safe, highly optimized, and conceptually -/// robust API for modeling both undirected and backward-forward (bf) directed hypergraphs. +/// This class relies on the provided `HypergraphTraits` to determine its behavior, element +/// property types, and the underlying memory representation. It exposes a unified API for +/// adding, removing, and iterating over vertices and hyperedges regardless of the backend. /// /// ### Key Features -/// - **Zero-cost Abstractions**: Core query logic is resolved at compile time through layout tags and static dispatch, removing unnecessary overhead. -/// - **Multiple Internal Representations**: Supports both incidence matrices (optimal for dense, rigid structures) and incidence lists (optimal for sparse, dynamic structures), further separated into standard and flat-memory contiguous variants. -/// - **Arbitrary Properties Injection**: Allows seamless integration of completely custom, user-defined data structures (properties) directly into vertices and hyperedges without inheriting from intrusive base classes. +/// - **Policy-based design**: Behavior and representation are determined by `HypergraphTraits`. +/// - **Zero-cost Abstractions**: Core query logic is resolved at compile time through implementation tags and static dispatch, removing unnecessary overhead. +/// - **Configurable directionality**: Support for both undirected and BF-directed hypergraphs. +/// - **Multiple representations**: Choose the underlying memory model and its layout to achieve the best performance for your needs: +/// - @ref hgl::impl::list_t "list_t": Standard incidence list. +/// - @ref hgl::impl::flat_list_t "flat_list_t": Flattened incidence list. +/// - @ref hgl::impl::matrix_t "matrix_t": Standard incidence matrix. +/// - @ref hgl::impl::flat_matrix_t "flat_matrix_t": Flattened incidence matrix. +/// - **Property support**: Vertices and hyperedges can carry arbitrary properties. +/// - **Unified API**: Consistent interface regardless of the underlying implementation. /// - **Standard Range Support**: Exposes lightweight views compliant with C++20 `std::ranges`, enabling functional-style iteration and algorithms. /// /// ### Basic Definitions -/// - **Hyperedge**: A generalized edge that can connect any subset of vertices. -/// - **Incidence**: The fundamental relationship in a hypergraph. A vertex is "incident to" a hyperedge if it is contained within that hyperedge's set. -/// - **Undirected**: A hyperedge is simply a set of incident vertices. -/// - **BF-Directed**: A hyperedge maps a distinct subset of vertices (the **Tail**) to another distinct subset of vertices (the **Head**). +/// A hypergraph \f$G = (V, E)\f$ consists of a set of vertices \f$V\f$ and a set of hyperedges \f$E\f$. +/// +/// - For undirected graphs, a hyperedge is a subset of the vertex set. Formally \f$E \subseteq 2^V\f$ and \f$e \in E \implies e \subseteq V\f$. +/// - For BF-directed graphs, a hyperedge is an ordered pair of disjoint subsets of the vertex set - the *tail* (sources) and *head* (targets) of the hyperedge. +/// Formally \f$e = (T_e, H_e)\f$ where \f$T_e, H_e \subset V \land T_e \cap H_e = \emptyset\f$. /// /// ### Example Usage /// ```cpp -/// // 1. Define a directed hypergraph with string names on vertices and weights on hyperedges. -/// using traits = hgl::hypergraph_traits< -/// hgl::bf_directed_t, gl::name_property, gl::weight_property -/// >; -/// hgl::hypergraph h; +/// hgl::undirected_hypergraph<> h(4, 2); // (1)! +/// +/// h.bind({0, 1, 2}, 0); // (2)! /// -/// // 2. Add properties via the descriptor dereference operator. -/// auto v0 = h.add_vertex(); v0->name = "Source"; // (1)! -/// auto v1 = h.add_vertex(); v1->name = "Target A"; -/// auto v2 = h.add_vertex(); v2->name = "Target B"; +/// auto v_first = h.vertex(0); // (3)! +/// auto v_last = h.vertex(3); +/// auto e1 = h.hyperedge(1); +/// h.bind({v_first, v_last}, e1); // (4)! /// -/// // 3. For directed hypergraphs, specify Tail vertices and Head vertices. -/// auto e = h.add_hyperedge({v0}, {v1, v2}); // (2)! -/// e->weight = 5.5; +/// std::cout << "Vertices: " << h.n_vertices() << '\n'; // (5)! +/// std::cout << "Hyperedges: " << h.n_hyperedges() << '\n'; /// -/// std::cout << gl::io::verbose << gl::io::with_properties; // (3)! -/// std::cout << "Hypergraph:\n" << h << '\n'; +/// for (auto v : h.vertices()) { // (6)! +/// for (auto e : h.incident_hyperedges(v)) { +/// process(v, e); +/// } +/// } +/// +/// std::cout << "Topology:\n" << h << '\n'; // (7)! /// ``` /// -/// 1. Vertices are created and returned as safe @ref hgl::vertex_descriptor "vertex_descriptor" wrappers. -/// 2. Hyperedges are created and returned as safe @ref hgl::hyperedge_descriptor "hyperedge_descriptor" wrappers. -/// 3. Standard GL stream manipulators format both graph and hypergraph output identically. +/// 1\. Instantiate an undirected hypergraph with 4 vertices and 2 hyperedges. +/// +/// 2\. Bind vertices with IDs 0, 1 and 2 to the hyperedge with ID 0. +/// +/// 3\. Retrieve the vertex and hyperedge descriptors in the hypergraph using the dedicated getters. +/// +/// 4\. Bind the given vertices to the edge using descriptor objects. +/// +/// 5\. Query the hypergraph's properties. +/// +/// 6\. Iterate over the hypergraph's vertices and then over the incident edges of each vertex. +/// +/// 7\. Utilize the builtin I/O stream support of the `hypergraph` class to print its topology to the console. +/// +/// ### API Design: IDs vs. Descriptors +/// The `hypergraph` class exposes a dual API to accommodate different performance and ergonomic needs: +/// +/// - **Inputs**: Most query methods are overloaded to accept either a raw `id_type` or a `vertex_type`/`hyperedge_type` descriptor. They are functionally identical. +/// - **Outputs**: Methods ending in `_ids` (e.g., `incident_vertex_ids`) return views of raw integral IDs. Methods without this suffix (e.g., `incident_vertices`) automatically map those IDs to the proper descriptor objects. +/// - **Performance**: Descriptor-returning methods incur a slight overhead if the hypergraph utilizes properties, as the property reference must be fetched and bound to each descriptor. If you only need topology, prefer the `_ids` variants. +/// +/// ### Descriptor Invalidation Behavior +/// The hypergraph maintains the following invalidation semantics: +/// +/// - **Vertex addition**: Does not invalidate vertex IDs. However, property references stored in existing vertex descriptors may be invalidated. Has no effect hyperedge descriptors. +/// - **Vertex removal**: May invalidate vertex descriptors, IDs, and property references. Has no effect hyperedge descriptors. +/// - **Edge addition**: Does not invalidate hyperedge IDs. However, property references stored in existing hyperedge descriptors may be invalidated. Has no effect on vertex descriptors. +/// - **Edge removal**: Invalidates hyperedge descriptors, IDs, and property references. Has no effect on vertex descriptors. +/// - **Property access**: References to vertex or edge properties obtained from the map may be invalidated by modifications to the hypergraph structure. /// /// ### Template Parameters -/// | Parameter | Description | Default | Constraint | -/// | :-------- | :---------- | :------ | :--------- | -/// | HypergraphTraits | The core configuration object dictating the directionality, properties, identifier types, and internal memory layout of the hypergraph. | @ref hgl::hypergraph_traits "hypergraph_traits<>" | [**c_instantiation_of**](hgl_concepts.md#hgl-traits-c-instantiation-of) | +/// | Parameter | Description | Constraint | +/// | :-------- | :---------- | :--------- | +/// | HypergraphTraits | The core configuration type specifying the behavior and representation of the hypergraph. | [**c_instantiation_of**](gl_concepts.md#gl-traits-c-instantiation-of) | /// /// ### See Also /// - @ref hgl::hypergraph_traits "hypergraph_traits" for configuring the underlying properties and tags. /// - @ref gl::io::options_manip "options_manip" for custom stream formatting options. +/// +/// ### See Also +/// - @ref hgl::undirected_hypergraph "undirected_hypergraph" : Convenience alias for undirected hypergraphs. +/// - @ref hgl::bf_directed_hypergraph "bf_directed_hypergraph" : Convenience alias for BF-directed hypergraphs. +/// - @ref hgl::clone "clone" : Create a deep copy of a hypergraph. +/// - @ref hgl::to "to" : Convert a hypergraph to a different implementation. +/// +/// > [!IMPORTANT] Copy Semantics +/// > +/// > `hypergraph` supports move semantics but disables copy assignment to prevent accidental expensive copies. +/// > Use @ref hgl::clone "clone" to explicitly copy a hypergraph. +/// +/// > [!WARNING] Const Correctness & Properties (API Note) +/// > +/// > Currently, a `const` hypergraph guarantees **structural immutability** (vertices and hyperedges cannot be added or removed). +/// > However, vertex and hyperedge property maps are internally treated as `mutable`. This means that property payloads +/// > can still be modified through a `const hypergraph&`. Strict const-correct overloads for property access are planned +/// > for a future release. Proceed with caution in multi-threaded contexts. template HypergraphTraits> class hypergraph final { public: - /// @brief The configured traits type governing this hypergraph's parameters. + /// @brief The traits type specifying the hypergraph's behavior and representation. using traits_type = HypergraphTraits; - /// @brief The directional tag indicating if the hypergraph is undirected or bf_directed. + + /// @brief Type tag specifying the directionality of the hypergraph. using directional_tag = typename traits_type::directional_tag; - /// @brief The implementation tag defining the internal storage mechanism. + /// @brief Type tag indicating the underlying implementation model. using implementation_tag = typename traits_type::implementation_tag; - /// @brief The instantiated underlying implementation class matching the directional tag. + + /// @brief The underlying implementation type matching the directional tag. using implementation_type = typename implementation_tag::template implementation_type; - /// @brief The underlying integer type used for identifiers. + + /// @brief Integral type used to identify vertices and hyperedges. using id_type = typename traits_type::id_type; - /// @brief The fully resolved type representing a vertex descriptor. + /// @brief The descriptor type representing a vertex. using vertex_type = typename traits_type::vertex_type; /// @brief The user-defined property payload type associated with vertices. using vertex_properties_type = typename traits_type::vertex_properties_type; - /// @brief The container type used for storing vertex properties internally. + /// @brief The container type used for storing the vertex properties mapping. using vertex_properties_map_type = std::conditional_t< traits::c_empty_properties, empty_properties_map, std::vector>; - /// @brief The fully resolved type representing a hyperedge descriptor. + /// @brief The descriptor type representing a hyperedge. using hyperedge_type = typename traits_type::hyperedge_type; /// @brief The user-defined property payload type associated with hyperedges. using hyperedge_properties_type = typename traits_type::hyperedge_properties_type; - /// @brief The container type used for storing hyperedge properties internally. + /// @brief The container type used for storing hyperedge properties mapping. using hyperedge_properties_map_type = std::conditional_t< traits::c_empty_properties, empty_properties_map, std::vector>; - /// @brief Deleted copy assignment operator to prevent trivial overwrites (use `hgl::clone` instead). - hypergraph& operator=(const hypergraph&) = delete; - - /// @brief Constructs an empty hypergraph, optionally pre-allocating capacity. - /// @param n_vertices The initial capacity or fixed count of vertices (especially relevant for matrix implementations). - /// @param n_hyperedges The initial capacity or fixed count of hyperedges. + /// @brief Constructs a hypergraph with the given number of vertices and hyperedges (empty by default). + /// @param n_vertices The initial number of vertices. + /// @param n_hyperedges The initial number of hyperedges. + /// + /// > [!NOTE] Memory Management + /// > + /// > Constructing a non-empty hypergraph and retrieving descriptors of the hypergraph's elements provides + /// > significantly better performance than adding elements one by one (especially for large hypergraphs) + /// > due to the ability to allocate the required memory for the internal incidence representation, which + /// > drastically reduces the overhead associated with reallocations associated with adding elements sequentially. explicit hypergraph(const size_type n_vertices = 0uz, const size_type n_hyperedges = 0uz) : _n_vertices(n_vertices), _n_hyperedges(n_hyperedges), _impl(n_vertices, n_hyperedges) { if constexpr (traits::c_non_empty_properties) @@ -237,24 +295,28 @@ class hypergraph final { /// @brief Default destructor. ~hypergraph() = default; + /// @brief Hypergraph copy assignment is disabled to avoid accidental copies. Use @ref hgl::clone "clone" instead. + hypergraph& operator=(const hypergraph&) = delete; + // --- size methods --- - /// @brief Retrieves the current number of valid vertices in the hypergraph. - /// @return The total vertex count. + /// @brief Returns the total number of vertices in the hypergraph. + /// @return The vertex count: $|V|$. [[nodiscard]] gl_attr_force_inline size_type n_vertices() const noexcept { return this->_n_vertices; } - /// @brief Retrieves the current number of valid hyperedges in the hypergraph. - /// @return The total hyperedge count. + /// @brief Returns the total number of hyperedges in the hypergraph. + /// @return The hyperedge count: $|E|$. [[nodiscard]] gl_attr_force_inline size_type n_hyperedges() const noexcept { return this->_n_hyperedges; } // --- vertex modifiers --- - /// @brief Adds a single new property-less vertex to the hypergraph. - /// @return A safe `vertex_descriptor` pointing to the newly created vertex. + /// @brief Adds a new, default-initialized vertex to the hypergraph. + /// @return A descriptor of the newly created vertex. + /// @copydetails detail::hypergraph_doc_anchors::add_vertex_note() vertex_type add_vertex() { this->_impl.add_vertices(1uz); const auto new_vertex_id = static_cast(this->_n_vertices++); @@ -265,9 +327,10 @@ class hypergraph final { return vertex_type{new_vertex_id}; } - /// @brief Adds a single new vertex to the hypergraph and initializes its properties. - /// @param properties The property payload to assign to the new vertex. - /// @return A safe `vertex_descriptor` pointing to the newly created vertex. + /// @brief Adds a new vertex with the given properties to the hypergraph. + /// @param properties The property payload for the new vertex. + /// @return A descriptor of the newly created vertex. + /// @copydetails detail::hypergraph_doc_anchors::add_vertex_note() vertex_type add_vertex_with(vertex_properties_type properties) requires(traits::c_non_empty_properties) { @@ -278,8 +341,9 @@ class hypergraph final { }; } - /// @brief Efficiently adds multiple property-less vertices to the hypergraph. + /// @brief Adds multiple default-initialized vertices to the hypergraph. /// @param n The number of vertices to add. + /// @copydetails detail::hypergraph_doc_anchors::add_vertex_note() void add_vertices(const size_type n) { this->_impl.add_vertices(n); this->_n_vertices += n; @@ -288,8 +352,9 @@ class hypergraph final { this->_vertex_properties.resize(this->_n_vertices); } - /// @brief Efficiently adds multiple vertices to the hypergraph, initializing them with a range of properties. - /// @param properties_rng A range containing the property payloads to assign to the newly created vertices. + /// @brief Adds multiple vertices based on a range of property payloads. + /// @param properties_rng A forward range of properties to initialize the new vertices with. + /// @copydetails detail::hypergraph_doc_anchors::add_vertex_note() void add_vertices_with( const traits::c_sized_range_of auto& properties_rng ) @@ -308,20 +373,26 @@ class hypergraph final { ); } - /// @brief Safely removes a vertex and completely purges it from all incident hyperedges. - /// @param vertex_id The raw identifier of the vertex to remove. + /// @brief Removes a vertex by its ID, unbinding it from its incident hyperedges. + /// @param vertex_id The ID of the vertex to remove. + /// @throws std::invalid_argument If the ID is invalid. + /// @copydetails detail::hypergraph_doc_anchors::remove_vertex_wrn() gl_attr_force_inline void remove_vertex(const id_type vertex_id) { this->_remove_vertex_impl(vertex_id); } - /// @brief Safely removes a vertex and completely purges it from all incident hyperedges. - /// @param vertex The `vertex_descriptor` wrapping the vertex to remove. - gl_attr_force_inline void remove_vertex(const vertex_type& vertex) { + /// @brief Removes a vertex using its descriptor, unbinding it from its incident hyperedges. + /// @param vertex The descriptor of the vertex to remove. + /// @throws std::invalid_argument If the vertex descriptor is invalid. + /// @copydetails detail::hypergraph_doc_anchors::remove_vertex_wrn() + gl_attr_force_inline void remove_vertex(vertex_type vertex) { this->remove_vertex(vertex.id()); } - /// @brief Removes multiple vertices specified by a range of raw identifiers. - /// @param vertex_id_rng A range of raw vertex identifiers to be removed. + /// @brief Removes a range of vertices using their IDs. + /// @param vertex_id_rng A forward range containing the IDs of vertices to remove. + /// @throws std::invalid_argument If any vertex ID in the range is invalid. + /// @copydetails detail::hypergraph_doc_anchors::remove_vertex_wrn() void remove_vertices(const traits::c_forward_range_of auto& vertex_id_rng) { // sorts ids in a descending n_vertices and removes duplicate ids std::set> vertex_id_set( @@ -333,9 +404,11 @@ class hypergraph final { this->_remove_vertex_impl(vertex_id); } - /// @brief Removes multiple vertices specified by a range of vertex descriptors. - /// @param vertex_rng A range of `vertex_descriptor`s representing the vertices to be removed. - void remove_vertices(const traits::c_sized_range_of auto& vertex_rng) { + /// @brief Removes a range of vertices using their descriptors. + /// @param vertex_rng A forward range containing the descriptors of vertices to remove. + /// @throws std::invalid_argument If any vertex descriptor is invalid. + /// @copydetails detail::hypergraph_doc_anchors::remove_vertex_wrn() + void remove_vertices(const traits::c_forward_range_of auto& vertex_rng) { // sort vertices in a descending n_vertices (by id) and removes duplicate ids std::set> vertex_set( std::ranges::begin(vertex_rng), std::ranges::end(vertex_rng) @@ -348,40 +421,51 @@ class hypergraph final { // --- vertex getters --- - /// @brief Checks if a vertex with the given raw ID exists in the hypergraph. - /// @param vertex_id The raw identifier to check. + /// @brief Checks if a vertex with the given ID exists in the hypergraph. + /// @param vertex_id The ID to check. /// @return `true` if the vertex exists, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool has_vertex(const id_type vertex_id) const { return vertex_id < this->_n_vertices; } /// @brief Checks if the vertex referenced by the provided descriptor exists in the hypergraph. - /// @param vertex The vertex descriptor to check. + /// @param vertex The descriptor to check. /// @return `true` if the vertex exists, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool has_vertex(vertex_type vertex) const { return this->has_vertex(vertex.id()); } - /// @brief Safely wraps a raw vertex ID into a `vertex_descriptor`, bounds-checking the ID. - /// @param vertex_id The raw numeric identifier of the vertex. - /// @return The constructed `vertex_descriptor`. - /// @throws std::invalid_argument If the `vertex_id` is out of bounds. + /// @brief Returns a descriptor of the vertex with the given *bounds-checked* ID. + /// @param vertex_id The ID of the vertex. + /// @return The corresponding vertex descriptor. + /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] vertex_type vertex(const id_type vertex_id) const { this->_verify_vertex_id(vertex_id); return this->vertex_unchecked(vertex_id); } - /// @brief Accesses a `vertex_descriptor` using a tag-based accessor. - /// @param vertex_id The raw numeric identifier of the vertex. - /// @return The constructed `vertex_descriptor`. - /// @throws std::invalid_argument If the `vertex_id` is out of bounds. + /// @brief Returns a descriptor of the vertex with the given bounds-checked ID. + /// + /// > [!NOTE] API Note + /// > + /// > Calling `hypergraph.at(hgl::vertex, id)` is equivalent to calling `hypergraph.vertex(id)`. + /// > However, the `at` methods of the `hypergraph` class are designed to be called in generic + /// > functions that can operate on vertices and hyperedges alike. + /// + /// @param vertex_id The ID of the vertex. + /// @return The corresponding `vertex_descriptor`. + /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] gl_attr_force_inline vertex_type at(vertex_t, const id_type vertex_id) const { return this->vertex(vertex_id); } - /// @brief Wraps a raw vertex ID into a `vertex_descriptor` without performing bounds checking. - /// @param vertex_id The raw numeric identifier of the vertex. - /// @return The constructed `vertex_descriptor`. + /// @brief Returns a descriptor of the vertex with the given ID without bounds checking. + /// @param vertex_id The ID of the vertex. + /// @return The corresponding vertex descriptor. + /// + /// > [!WARNING] Undefined Behavior + /// > + /// > No bounds checking is performed. Passing an invalid ID results in Undefined Behavior. [[nodiscard]] gl_attr_force_inline vertex_type vertex_unchecked(const id_type vertex_id) const { if constexpr (traits::c_non_empty_properties) return vertex_type{vertex_id, this->_vertex_properties[vertex_id]}; @@ -389,30 +473,40 @@ class hypergraph final { return vertex_type{vertex_id}; } - /// @brief Subscript operator to access a `vertex_descriptor` without bounds checking. - /// @param vertex_id The raw numeric identifier of the vertex. - /// @return The constructed `vertex_descriptor`. + /// @brief Returns a descriptor of the vertex with the given ID without bounds checking. + /// @param vertex_id The ID of the vertex. + /// @return The corresponding vertex descriptor. + /// + /// > [!NOTE] API Note + /// > + /// > Calling `hypergraph[hgl::vertex, id]` is equivalent to calling `hypergraph.vertex_unchecked(id)`. + /// > However, the subscript operators of the `hypergraph` class are designed to be called in generic + /// > functions that can operate on vertices and hyperedges alike. + /// + /// > [!WARNING] Undefined Behavior + /// > + /// > No bounds checking is performed. Passing an invalid ID results in Undefined Behavior. [[nodiscard]] gl_attr_force_inline vertex_type operator[](vertex_t, const id_type vertex_id) const { return this->vertex_unchecked(vertex_id); } - /// @brief Returns a transformed range wrapping all valid vertices into `vertex_descriptor`s. - /// @return A lazily evaluated view of all `vertex_descriptor`s. + /// @brief Returns a lazily evaluated, random-access view of all vertex descriptors in the hypergraph. + /// @return A view yielding descriptors for every vertex. [[nodiscard]] gl_attr_force_inline auto vertices() const noexcept { return this->vertex_ids() | std::views::transform(this->_create_vertex_descriptor()); } - /// @brief Returns a lightweight, lazily evaluated range over all valid raw vertex IDs. - /// @return A view of all vertex identifiers. + /// @brief Returns a lazily evaluated, random-access view of all active vertex IDs in the hypergraph. + /// @return A view yielding all valid vertex IDs. [[nodiscard]] gl_attr_force_inline auto vertex_ids() const noexcept { return std::views::iota(initial_id_v, this->_n_vertices); } /// @brief Retrieves a reference to the properties of a specified vertex. - /// @param vertex_id The identifier of the vertex. + /// @param vertex_id The ID of the vertex. /// @return A mutable reference to the vertex's properties. - /// @throws std::invalid_argument If the `vertex_id` is out of bounds. + /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] gl_attr_force_inline vertex_properties_type& vertex_properties( const id_type vertex_id ) const @@ -422,8 +516,8 @@ class hypergraph final { return this->_vertex_properties[vertex_id]; } - /// @brief Returns a view over the internal vertex properties container. - /// @return A standard view over the continuous block of property payloads. + /// @brief Retrieves a lazily evaluated, random-access view over all vertex properties in the hypergraph. + /// @return A view mapping each active vertex index to its property. [[nodiscard]] gl_attr_force_inline auto vertex_properties_map() const noexcept requires(traits::c_non_empty_properties) { @@ -432,8 +526,9 @@ class hypergraph final { // --- hyperedge modifiers --- - /// @brief Adds a single new, empty, property-less hyperedge to the hypergraph. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new, default-initialized hyperedge to the hypergraph. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() hyperedge_type add_hyperedge() { this->_impl.add_hyperedges(1uz); const auto new_hyperedge_id = static_cast(this->_n_hyperedges++); @@ -444,9 +539,10 @@ class hypergraph final { return hyperedge_type{new_hyperedge_id}; } - /// @brief Adds a single new empty hyperedge and initializes its properties. - /// @param properties The property payload to assign to the new hyperedge. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new hyperedge with the given properties to the hypergraph. + /// @param properties The property payload for the new hyperedge. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() hyperedge_type add_hyperedge_with(hyperedge_properties_type properties) requires(traits::c_non_empty_properties) { @@ -457,9 +553,10 @@ class hypergraph final { }; } - /// @brief Adds a new hyperedge and immediately binds a range of vertices to it (undirected). - /// @param vertex_id_rng A range of raw vertex identifiers to incident with the new hyperedge. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *undirected* hyperedge and immediately binds a range of vertices to it. + /// @param vertex_id_rng A forward range of vertex IDs to bind to the new hyperedge. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() hyperedge_type add_hyperedge(const traits::c_forward_range_of auto& vertex_id_rng) requires std::same_as { @@ -468,18 +565,20 @@ class hypergraph final { return he; } - /// @brief Adds a new hyperedge and immediately binds an initializer list of vertices to it (undirected). - /// @param vertex_ids An initializer list of raw vertex identifiers. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *undirected* hyperedge and immediately binds a list of vertices to it. + /// @param vertex_ids An initializer list of vertex IDs to bind to the new hyperedge. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() gl_attr_force_inline hyperedge_type add_hyperedge(std::initializer_list vertex_ids) requires std::same_as { return this->add_hyperedge(std::views::all(vertex_ids)); } - /// @brief Adds a new hyperedge and immediately binds a range of vertex descriptors to it (undirected). - /// @param vertex_rng A range of `vertex_descriptor` objects. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *undirected* hyperedge and immediately binds a range of vertices to it. + /// @param vertex_rng A forward range of vertex descriptors to bind to the new hyperedge. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() gl_attr_force_inline hyperedge_type add_hyperedge(const traits::c_forward_range_of auto& vertex_rng) requires std::same_as @@ -487,19 +586,21 @@ class hypergraph final { return this->add_hyperedge(vertex_rng | std::views::transform(&vertex_type::id)); } - /// @brief Adds a new hyperedge and immediately binds an initializer list of vertex descriptors to it (undirected). - /// @param vertices An initializer list of `vertex_descriptor` objects. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *undirected* hyperedge and immediately binds a list of vertices to it. + /// @param vertices An initializer list of vertex descriptors to bind to the new hyperedge. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() gl_attr_force_inline hyperedge_type add_hyperedge(std::initializer_list vertices) requires std::same_as { return this->add_hyperedge(std::views::all(vertices)); } - /// @brief Adds a new hyperedge, initializes properties, and binds a range of vertices to it (undirected). - /// @param vertex_id_rng A range of raw vertex identifiers. - /// @param properties The property payload to assign. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *undirected* hyperedge with the given properties and immediately binds a range of vertices to it. + /// @param vertex_id_rng A forward range of vertex IDs to bind to the new hyperedge. + /// @param properties The property payload for the new hyperedge. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() hyperedge_type add_hyperedge_with( const traits::c_forward_range_of auto& vertex_id_rng, hyperedge_properties_type properties @@ -511,10 +612,11 @@ class hypergraph final { return he; } - /// @brief Adds a new hyperedge, initializes properties, and binds an initializer list of vertices to it (undirected). - /// @param vertex_ids An initializer list of raw vertex identifiers. - /// @param properties The property payload to assign. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *undirected* hyperedge with the given properties and immediately binds a list of vertices to it. + /// @param vertex_ids An initializer list of vertex IDs to bind to the new hyperedge. + /// @param properties The property payload for the new hyperedge. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() hyperedge_type add_hyperedge_with( std::initializer_list vertex_ids, hyperedge_properties_type properties ) @@ -523,10 +625,11 @@ class hypergraph final { return this->add_hyperedge_with(std::views::all(vertex_ids), std::move(properties)); } - /// @brief Adds a new hyperedge, initializes properties, and binds a range of vertex descriptors to it (undirected). - /// @param vertex_rng A range of `vertex_descriptor` objects. - /// @param properties The property payload to assign. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *undirected* hyperedge with the given properties and immediately binds a range of vertices to it. + /// @param vertex_rng A forward range of vertex descriptors to bind to the new hyperedge. + /// @param properties The property payload for the new hyperedge. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() gl_attr_force_inline hyperedge_type add_hyperedge_with( const traits::c_forward_range_of auto& vertex_rng, hyperedge_properties_type properties @@ -538,10 +641,11 @@ class hypergraph final { ); } - /// @brief Adds a new hyperedge, initializes properties, and binds an initializer list of vertex descriptors to it (undirected). - /// @param vertices An initializer list of `vertex_descriptor` objects. - /// @param properties The property payload to assign. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *undirected* hyperedge with the given properties and immediately binds a list of vertices to it. + /// @param vertices An initializer list of vertex descriptors to bind to the new hyperedge. + /// @param properties The property payload for the new hyperedge. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() hyperedge_type add_hyperedge_with( std::initializer_list vertices, hyperedge_properties_type properties ) @@ -550,10 +654,11 @@ class hypergraph final { return this->add_hyperedge_with(std::views::all(vertices), std::move(properties)); } - /// @brief Adds a new hyperedge and immediately binds tail and head vertices to it (bf-directed). - /// @param tail_id_rng A range of raw vertex identifiers forming the Tail. - /// @param head_id_rng A range of raw vertex identifiers forming the Head. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *BF-directed* hyperedge and immediately binds vertices to its *tail* and *head*. + /// @param tail_id_rng A forward range of vertex IDs to bind to the new hyperedge's *tail*. + /// @param head_id_rng A forward range of vertex IDs to bind to the new hyperedge's *head*. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() hyperedge_type add_hyperedge( const traits::c_forward_range_of auto& tail_id_rng, const traits::c_forward_range_of auto& head_id_rng @@ -566,10 +671,11 @@ class hypergraph final { return he; } - /// @brief Adds a new hyperedge and immediately binds tail and head vertices to it (bf-directed). - /// @param tail_ids An initializer list of raw vertex identifiers forming the Tail. - /// @param head_ids An initializer list of raw vertex identifiers forming the Head. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *BF-directed* hyperedge and immediately binds vertices to its *tail* and *head*. + /// @param tail_ids An initializer of vertex IDs to bind to the new hyperedge's *tail*. + /// @param head_ids An initializer of vertex IDs to bind to the new hyperedge's *head*. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() gl_attr_force_inline hyperedge_type add_hyperedge(std::initializer_list tail_ids, std::initializer_list head_ids) requires std::same_as @@ -577,10 +683,11 @@ class hypergraph final { return this->add_hyperedge(std::views::all(tail_ids), std::views::all(head_ids)); } - /// @brief Adds a new hyperedge and immediately binds tail and head vertices to it (bf-directed). - /// @param tail_rng A range of `vertex_descriptor`s forming the Tail. - /// @param head_rng A range of `vertex_descriptor`s forming the Head. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *BF-directed* hyperedge and immediately binds vertices to its *tail* and *head*. + /// @param tail_rng A forward range of vertex descriptors to bind to the new hyperedge's *tail*. + /// @param head_rng A forward range of vertex descriptors to bind to the new hyperedge's *head*. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() gl_attr_force_inline hyperedge_type add_hyperedge( const traits::c_forward_range_of auto& tail_rng, const traits::c_forward_range_of auto& head_rng @@ -593,10 +700,11 @@ class hypergraph final { ); } - /// @brief Adds a new hyperedge and immediately binds tail and head vertices to it (bf-directed). - /// @param tail An initializer list of `vertex_descriptor`s forming the Tail. - /// @param head An initializer list of `vertex_descriptor`s forming the Head. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *BF-directed* hyperedge and immediately binds vertices to its *tail* and *head*. + /// @param tail An intializer list of vertex descriptors to bind to the new hyperedge's *tail*. + /// @param head An intializer list of vertex descriptors to bind to the new hyperedge's *head*. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() gl_attr_force_inline hyperedge_type add_hyperedge(std::initializer_list tail, std::initializer_list head) requires std::same_as @@ -604,11 +712,12 @@ class hypergraph final { return this->add_hyperedge(std::views::all(tail), std::views::all(head)); } - /// @brief Adds a new hyperedge, initializes properties, and binds tail and head vertices to it (bf-directed). - /// @param tail_id_rng A range of raw vertex identifiers forming the Tail. - /// @param head_id_rng A range of raw vertex identifiers forming the Head. - /// @param properties The property payload to assign. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *BF-directed* hyperedge and immediately binds vertices to its *tail* and *head*. + /// @param tail_id_rng A forward range of vertex IDs to bind to the new hyperedge's *tail*. + /// @param head_id_rng A forward range of vertex IDs to bind to the new hyperedge's *head*. + /// @param properties The property payload for the new hyperedge. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() hyperedge_type add_hyperedge_with( const traits::c_forward_range_of auto& tail_id_rng, const traits::c_forward_range_of auto& head_id_rng, @@ -622,11 +731,12 @@ class hypergraph final { return he; } - /// @brief Adds a new hyperedge, initializes properties, and binds tail and head vertices to it (bf-directed). - /// @param tail_ids An initializer list of raw vertex identifiers forming the Tail. - /// @param head_ids An initializer list of raw vertex identifiers forming the Head. - /// @param properties The property payload to assign. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *BF-directed* hyperedge and immediately binds vertices to its *tail* and *head*. + /// @param tail_ids An initializer list of vertex IDs to bind to the new hyperedge's *tail*. + /// @param head_ids An initializer list of vertex IDs to bind to the new hyperedge's *head*. + /// @param properties The property payload for the new hyperedge. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() gl_attr_force_inline hyperedge_type add_hyperedge_with( std::initializer_list tail_ids, std::initializer_list head_ids, @@ -639,11 +749,12 @@ class hypergraph final { ); } - /// @brief Adds a new hyperedge, initializes properties, and binds tail and head vertices to it (bf-directed). - /// @param tail_rng A range of `vertex_descriptor`s forming the Tail. - /// @param head_rng A range of `vertex_descriptor`s forming the Head. - /// @param properties The property payload to assign. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *BF-directed* hyperedge and immediately binds vertices to its *tail* and *head*. + /// @param tail_rng A forward range of vertex descriptors to bind to the new hyperedge's *tail*. + /// @param head_rng A forward range of vertex descriptors to bind to the new hyperedge's *head*. + /// @param properties The property payload for the new hyperedge. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() gl_attr_force_inline hyperedge_type add_hyperedge_with( const traits::c_forward_range_of auto& tail_rng, const traits::c_forward_range_of auto& head_rng, @@ -658,11 +769,12 @@ class hypergraph final { ); } - /// @brief Adds a new hyperedge, initializes properties, and binds tail and head vertices to it (bf-directed). - /// @param tail An initializer list of `vertex_descriptor`s forming the Tail. - /// @param head An initializer list of `vertex_descriptor`s forming the Head. - /// @param properties The property payload to assign. - /// @return A safe `hyperedge_descriptor` pointing to the newly created hyperedge. + /// @brief Adds a new *BF-directed* hyperedge and immediately binds vertices to its *tail* and *head*. + /// @param tail An initializer list of vertex descriptor to bind to the new hyperedge's *tail*. + /// @param head An initializer list of vertex descriptor to bind to the new hyperedge's *head*. + /// @param properties The property payload for the new hyperedge. + /// @return A descriptor of the newly created hyperedge. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() gl_attr_force_inline hyperedge_type add_hyperedge_with( std::initializer_list tail, std::initializer_list head, @@ -675,8 +787,9 @@ class hypergraph final { ); } - /// @brief Efficiently adds multiple property-less hyperedges to the hypergraph. - /// @param n The number of empty hyperedges to add. + /// @brief Adds multiple default-initialized hyperedges to the hypergraph. + /// @param n The number of hyperedges to add. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() void add_hyperedges(const size_type n) { this->_impl.add_hyperedges(n); this->_n_hyperedges += n; @@ -685,8 +798,9 @@ class hypergraph final { this->_hyperedge_properties.resize(this->_n_hyperedges); } - /// @brief Efficiently adds multiple hyperedges to the hypergraph, initializing them with a range of properties. - /// @param properties_rng A range containing the property payloads to assign to the newly created hyperedges. + /// @brief Adds multiple hyperedges based on a range of property payloads. + /// @param properties_rng A forward range of properties to initialize the new hyperedges with. + /// @copydetails detail::hypergraph_doc_anchors::add_hyperedge_note() void add_hyperedges_with( const traits::c_sized_range_of auto& properties_rng ) @@ -705,20 +819,26 @@ class hypergraph final { ); } - /// @brief Safely removes a hyperedge and completely unbinds it from all incident vertices. - /// @param hyperedge_id The raw identifier of the hyperedge to remove. + /// @brief Removes a hyperedge by its ID, unbinding it from its incident vertices. + /// @param hyperedge_id The ID of the hyperedge to remove. + /// @throws std::invalid_argument If the ID is invalid. + /// @copydetails detail::hypergraph_doc_anchors::remove_hyperedge_wrn() gl_attr_force_inline void remove_hyperedge(const id_type hyperedge_id) { this->_remove_hyperedge_impl(hyperedge_id); } - /// @brief Safely removes a hyperedge and completely unbinds it from all incident vertices. - /// @param hyperedge The `hyperedge_descriptor` wrapping the hyperedge to remove. - gl_attr_force_inline void remove_hyperedge(const hyperedge_type& hyperedge) { + /// @brief Removes a hyperedge using its descriptor, unbinding it from its incident vertices. + /// @param hyperedge The descriptor of the hyperedge to remove. + /// @throws std::invalid_argument If the ID is invalid. + /// @copydetails detail::hypergraph_doc_anchors::remove_hyperedge_wrn() + gl_attr_force_inline void remove_hyperedge(hyperedge_type hyperedge) { this->remove_hyperedge(hyperedge.id()); } - /// @brief Removes multiple hyperedges specified by a range of raw identifiers. - /// @param hyperedge_id_rng A range of raw hyperedge identifiers to be removed. + /// @brief Removes a range of hyperedges using their IDs. + /// @param hyperedge_id_rng A forward range containing the IDs of hyperedges to remove. + /// @throws std::invalid_argument If any hyperedge ID in the range is invalid. + /// @copydetails detail::hypergraph_doc_anchors::remove_hyperedge_wrn() void remove_hyperedges_from(const traits::c_forward_range_of auto& hyperedge_id_rng) { // sorts ids in a descending n_vertices and removes duplicate ids std::set> hyperedge_id_set( @@ -730,9 +850,11 @@ class hypergraph final { this->_remove_hyperedge_impl(hyperedge_id); } - /// @brief Removes multiple hyperedges specified by a range of hyperedge descriptors. - /// @param hyperedge_rng A range of `hyperedge_descriptor`s representing the hyperedges to be removed. - void remove_hyperedges_from(const traits::c_sized_range_of auto& hyperedge_rng + /// @brief Removes a range of hyperedges using their descriptors. + /// @param hyperedge_id_rng A forward range containing the descriptors of hyperedges to remove. + /// @throws std::invalid_argument If any hyperedge ID in the range is invalid. + /// @copydetails detail::hypergraph_doc_anchors::remove_hyperedge_wrn() + void remove_hyperedges_from(const traits::c_forward_range_of auto& hyperedge_rng ) { // sort hyperedges in a descending n_vertices (by id) and removes duplicate ids std::set> hyperedge_set( @@ -746,41 +868,52 @@ class hypergraph final { // --- hyperedge getters --- - /// @brief Checks if a hyperedge with the given raw ID exists in the hypergraph. - /// @param hyperedge_id The raw identifier to check. + /// @brief Checks if a hyperedge with the given ID exists in the hypergraph. + /// @param hyperedge_id The ID to check. /// @return `true` if the hyperedge exists, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool has_hyperedge(const id_type hyperedge_id) const { return hyperedge_id < this->_n_hyperedges; } /// @brief Checks if the hyperedge referenced by the provided descriptor exists in the hypergraph. - /// @param hyperedge The hyperedge descriptor to check. + /// @param hyperedge The descriptor to check. /// @return `true` if the hyperedge exists, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool has_hyperedge(hyperedge_type hyperedge) const { return this->has_hyperedge(hyperedge.id()); } - /// @brief Safely wraps a raw hyperedge ID into a `hyperedge_descriptor`, bounds-checking the ID. - /// @param hyperedge_id The raw numeric identifier of the hyperedge. - /// @return The constructed `hyperedge_descriptor`. - /// @throws std::invalid_argument If the `hyperedge_id` is out of bounds. + /// @brief Returns a descriptor of the hyperedge with the given *bounds-checked* ID. + /// @param hyperedge_id The ID of the hyperedge. + /// @return The corresponding hyperedge descriptor. + /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] hyperedge_type hyperedge(const id_type hyperedge_id) const { this->_verify_hyperedge_id(hyperedge_id); return this->hyperedge_unchecked(hyperedge_id); } - /// @brief Accesses a `hyperedge_descriptor` using a tag-based accessor. - /// @param hyperedge_id The raw numeric identifier of the hyperedge. - /// @return The constructed `hyperedge_descriptor`. - /// @throws std::invalid_argument If the `hyperedge_id` is out of bounds. + /// @brief Returns a descriptor of the hyperedge with the given *bounds-checked* ID. + /// + /// > [!NOTE] API Note + /// > + /// > Calling `hypergraph.at(hgl::hyperedge, id)` is equivalent to calling `hypergraph.hyperedge(id)`. + /// > However, the `at` methods of the `hypergraph` class are designed to be called in generic + /// > functions that can operate on hyperedges and vertices alike. + /// + /// @param hyperedge_id The ID of the hyperedge. + /// @return The corresponding hyperedge descriptor. + /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] gl_attr_force_inline hyperedge_type at(hyperedge_t, const id_type hyperedge_id) const { return this->hyperedge(hyperedge_id); } - /// @brief Wraps a raw hyperedge ID into a `hyperedge_descriptor` without performing bounds checking. - /// @param hyperedge_id The raw numeric identifier of the hyperedge. - /// @return The constructed `hyperedge_descriptor`. + /// @brief Returns a descriptor of the hyperedge with the given ID without bounds checking. + /// @param hyperedge_id The ID of the hyperedge. + /// @return The corresponding hyperedge descriptor. + /// + /// > [!WARNING] Undefined Behavior + /// > + /// > No bounds checking is performed. Passing an invalid ID results in Undefined Behavior. [[nodiscard]] gl_attr_force_inline hyperedge_type hyperedge_unchecked(const id_type hyperedge_id ) const { if constexpr (traits::c_non_empty_properties) @@ -789,41 +922,51 @@ class hypergraph final { return hyperedge_type{hyperedge_id}; } - /// @brief Subscript operator to access a `hyperedge_descriptor` without bounds checking. - /// @param hyperedge_id The raw numeric identifier of the hyperedge. - /// @return The constructed `hyperedge_descriptor`. + /// @brief Returns a descriptor of the hyperedge with the given ID without bounds checking. + /// @param hyperedge_id The ID of the hyperedge. + /// @return The corresponding hyperedge descriptor. + /// + /// > [!NOTE] API Note + /// > + /// > Calling `hypergraph[hgl::hyperedge, id]` is equivalent to calling `hypergraph.hyperedge_unchecked(id)`. + /// > However, the subscript operators of the `hypergraph` class are designed to be called in generic + /// > functions that can operate on hyperedges and vertices alike. + /// + /// > [!WARNING] Undefined Behavior + /// > + /// > No bounds checking is performed. Passing an invalid ID results in Undefined Behavior. [[nodiscard]] gl_attr_force_inline hyperedge_type operator[](hyperedge_t, const id_type hyperedge_id) const { return this->hyperedge_unchecked(hyperedge_id); } - /// @brief Returns a transformed range wrapping all valid hyperedges into `hyperedge_descriptor`s. - /// @return A lazily evaluated view of all `hyperedge_descriptor`s. + /// @brief Returns a lazily evaluated, random-access view of all hyperedge descriptors in the hypergraph. + /// @return A view yielding descriptors for every hyperedge. [[nodiscard]] gl_attr_force_inline auto hyperedges() const noexcept { return this->hyperedge_ids() | std::views::transform(this->_create_hyperedge_descriptor()); } - /// @brief Returns a lightweight, lazily evaluated range over all valid raw hyperedge IDs. - /// @return A view of all hyperedge identifiers. + /// @brief Returns a lazily evaluated, random-access view of all active hyperedge IDs in the hypergraph. + /// @return A view yielding all valid hyperedge IDs. [[nodiscard]] gl_attr_force_inline auto hyperedge_ids() const noexcept { return std::views::iota(initial_id_v, this->_n_hyperedges); } /// @brief Retrieves a reference to the properties of a specified hyperedge. - /// @param id The identifier of the hyperedge. + /// @param hyperedge_id The ID of the hyperedge. /// @return A mutable reference to the hyperedge's properties. - /// @throws std::invalid_argument If the `id` is out of bounds. + /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] gl_attr_force_inline hyperedge_properties_type& hyperedge_properties( - const id_type id + const id_type hyperedge_id ) const requires(traits::c_non_empty_properties) { - this->_verify_hyperedge_id(id); - return this->_hyperedge_properties[id]; + this->_verify_hyperedge_id(hyperedge_id); + return this->_hyperedge_properties[hyperedge_id]; } - /// @brief Returns a view over the internal hyperedge properties container. - /// @return A standard view over the continuous block of property payloads. + /// @brief Retrieves a lazily evaluated, random-access view over all hyperedge properties in the hypergraph. + /// @return A view mapping each active hyperedge index to its property. [[nodiscard]] gl_attr_force_inline auto hyperedge_properties_map() const noexcept requires(traits::c_non_empty_properties) { @@ -833,8 +976,9 @@ class hypergraph final { // --- incidence modifiers --- /// @brief Binds a vertex to a hyperedge in an undirected hypergraph. - /// @param vertex_id The raw identifier of the vertex. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @param vertex_id The ID of the vertex. + /// @param hyperedge_id The ID of the hyperedge. + /// @throws std::invalid_argument If either ID is invalid. void bind(const id_type vertex_id, const id_type hyperedge_id) requires std::same_as { @@ -843,18 +987,20 @@ class hypergraph final { this->_impl.bind(vertex_id, hyperedge_id); } - /// @brief Binds a vertex to a hyperedge in an undirected hypergraph using descriptors. - /// @param vertex The `vertex_descriptor` mapping to the vertex. - /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. - gl_attr_force_inline void bind(const vertex_type& vertex, const hyperedge_type& hyperedge) + /// @brief Binds a vertex to a hyperedge in an undirected hypergraph. + /// @param vertex The descriptor of the vertex. + /// @param hyperedge The descriptor of the hyperedge. + /// @throws std::invalid_argument If either descriptor is invalid. + gl_attr_force_inline void bind(vertex_type vertex, hyperedge_type hyperedge) requires std::same_as { this->bind(vertex.id(), hyperedge.id()); } /// @brief Binds a range of vertices to a single hyperedge in an undirected hypergraph. - /// @param vertex_id_rng A range of raw vertex identifiers. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @param vertex_id_rng A forward range of vertex IDs. + /// @param hyperedge_id The ID of the hyperedge. + /// @throws std::invalid_argument If either the hyperedge ID or any of the vertex IDs is invalid. void bind( const traits::c_forward_range_of auto& vertex_id_rng, const id_type hyperedge_id ) @@ -867,9 +1013,10 @@ class hypergraph final { } } - /// @brief Binds an initializer list of vertices to a single hyperedge in an undirected hypergraph. - /// @param vertex_ids An initializer list of raw vertex identifiers. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Binds a list of vertices to a single hyperedge in an undirected hypergraph. + /// @param vertex_ids An initializer list of vertex IDs. + /// @param hyperedge_id The ID of the hyperedge. + /// @throws std::invalid_argument If either the hyperedge ID or any of the vertex IDs is invalid. gl_attr_force_inline void bind( std::initializer_list vertex_ids, const id_type hyperedge_id ) @@ -878,23 +1025,24 @@ class hypergraph final { this->bind(std::views::all(vertex_ids), hyperedge_id); } - /// @brief Binds a range of vertex descriptors to a single hyperedge in an undirected hypergraph. - /// @param vertex_rng A range of `vertex_descriptor`s. - /// @param hyperedge The `hyperedge_descriptor` wrapping the target hyperedge. + /// @brief Binds a range of vertices to a single hyperedge in an undirected hypergraph. + /// @param vertex_rng A forward range of vertex descriptors. + /// @param hyperedge The descriptor of the hyperedge. + /// @throws std::invalid_argument If either the hyperedge descriptor or any of the vertex descriptors is invalid. gl_attr_force_inline void bind( - const traits::c_forward_range_of auto& vertex_rng, - const hyperedge_type& hyperedge + const traits::c_forward_range_of auto& vertex_rng, hyperedge_type hyperedge ) requires std::same_as { this->bind(vertex_rng | std::views::transform(&vertex_type::id), hyperedge.id()); } - /// @brief Binds an initializer list of vertex descriptors to a single hyperedge in an undirected hypergraph. - /// @param vertices An initializer list of `vertex_descriptor`s. - /// @param hyperedge The `hyperedge_descriptor` wrapping the target hyperedge. + /// @brief Binds a list of vertices to a single hyperedge in an undirected hypergraph. + /// @param vertices An initializer list of vertex descriptors. + /// @param hyperedge_id The ID of the hyperedge. + /// @throws std::invalid_argument If either the hyperedge descriptor or any of the vertex descriptors is invalid. gl_attr_force_inline void bind( - std::initializer_list vertices, const hyperedge_type& hyperedge + std::initializer_list vertices, hyperedge_type hyperedge ) requires std::same_as { @@ -902,8 +1050,9 @@ class hypergraph final { } /// @brief Binds a single vertex to a range of hyperedges in an undirected hypergraph. - /// @param vertex_id The raw identifier of the vertex. - /// @param hyperedge_id_rng A range of raw hyperedge identifiers. + /// @param vertex_id The ID of the vertex. + /// @param hyperedge_id_rng A forward range of hyperedge IDs. + /// @throws std::invalid_argument If either the vertex ID or any of the hyperedge IDs is invalid. void bind( const id_type vertex_id, const traits::c_forward_range_of auto& hyperedge_id_rng ) @@ -916,9 +1065,10 @@ class hypergraph final { } } - /// @brief Binds a single vertex to an initializer list of hyperedges in an undirected hypergraph. - /// @param vertex_id The raw identifier of the vertex. - /// @param hyperedge_ids An initializer list of raw hyperedge identifiers. + /// @brief Binds a single vertex to a list of hyperedges in an undirected hypergraph. + /// @param vertex_id The ID of the vertex. + /// @param hyperedge_id_rng An initializer list of hyperedge IDs. + /// @throws std::invalid_argument If either the vertex ID or any of the hyperedge IDs is invalid. gl_attr_force_inline void bind( const id_type vertex_id, std::initializer_list hyperedge_ids ) @@ -927,32 +1077,34 @@ class hypergraph final { this->bind(vertex_id, std::views::all(hyperedge_ids)); } - /// @brief Binds a single vertex descriptor to a range of hyperedge descriptors in an undirected hypergraph. - /// @param vertex The target `vertex_descriptor`. - /// @param hyperedge_rng A range of `hyperedge_descriptor`s. + /// @brief Binds a single vertex to a range of hyperedges in an undirected hypergraph. + /// @param vertex_id The descriptor of the vertex. + /// @param hyperedge_id_rng A forward range of hyperedge descriptors. + /// @throws std::invalid_argument If either the vertex descriptor or any of the hyperedge descriptors is invalid. gl_attr_force_inline void bind( - const vertex_type& vertex, - const traits::c_forward_range_of auto& hyperedge_rng + vertex_type vertex, const traits::c_forward_range_of auto& hyperedge_rng ) requires std::same_as { this->bind(vertex.id(), hyperedge_rng | std::views::transform(&hyperedge_type::id)); } - /// @brief Binds a single vertex descriptor to an initializer list of hyperedge descriptors in an undirected hypergraph. - /// @param vertex The target `vertex_descriptor`. - /// @param hyperedges An initializer list of `hyperedge_descriptor`s. + /// @brief Binds a single vertex to a list of hyperedges in an undirected hypergraph. + /// @param vertex_id The descriptor of the vertex. + /// @param hyperedge_id_rng An initializer list of hyperedge descriptors. + /// @throws std::invalid_argument If either the vertex descriptor or any of the hyperedge descriptors is invalid. gl_attr_force_inline void bind( - const vertex_type& vertex, std::initializer_list hyperedges + vertex_type vertex, std::initializer_list hyperedges ) requires std::same_as { this->bind(vertex, std::views::all(hyperedges)); } - /// @brief Binds a vertex to the tail (source) of a bf-directed hyperedge. - /// @param vertex_id The raw identifier of the vertex. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Binds a vertex to the *tail* of a hyperedge in a BF-directed hypergraph. + /// @param vertex_id The ID of the vertex. + /// @param hyperedge_id The ID of the hyperedge. + /// @throws std::invalid_argument If either ID is invalid. void bind_tail(const id_type vertex_id, const id_type hyperedge_id) requires std::same_as { @@ -961,18 +1113,20 @@ class hypergraph final { this->_impl.bind_tail(vertex_id, hyperedge_id); } - /// @brief Binds a vertex descriptor to the tail (source) of a bf-directed hyperedge descriptor. - /// @param vertex The `vertex_descriptor` mapping to the vertex. - /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. - gl_attr_force_inline void bind_tail(const vertex_type& vertex, const hyperedge_type& hyperedge) + /// @brief Binds a vertex to the *tail* of a hyperedge in a BF-directed hypergraph. + /// @param vertex The descriptor of the vertex. + /// @param hyperedge The descriptor of the hyperedge. + /// @throws std::invalid_argument If either descriptor is invalid. + gl_attr_force_inline void bind_tail(vertex_type vertex, hyperedge_type hyperedge) requires std::same_as { this->bind_tail(vertex.id(), hyperedge.id()); } - /// @brief Binds a range of vertices to the tail of a single bf-directed hyperedge. - /// @param vertex_id_rng A range of raw vertex identifiers. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Binds a range of vertices to the *tail* of a single hyperedge in a BF-directed hypergraph. + /// @param vertex_id_rng A forward range of vertex IDs. + /// @param hyperedge_id The ID of the hyperedge. + /// @throws std::invalid_argument If either the hyperedge ID or any of the vertex IDs is invalid. void bind_tail( const traits::c_forward_range_of auto& vertex_id_rng, const id_type hyperedge_id ) @@ -985,9 +1139,10 @@ class hypergraph final { } } - /// @brief Binds an initializer list of vertices to the tail of a single bf-directed hyperedge. - /// @param vertex_ids An initializer list of raw vertex identifiers. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Binds a list of vertices to the *tail* of a single hyperedge in a BF-directed hypergraph. + /// @param vertex_ids An initializer list of vertex IDs. + /// @param hyperedge_id The ID of the hyperedge. + /// @throws std::invalid_argument If either the hyperedge ID or any of the vertex IDs is invalid. gl_attr_force_inline void bind_tail( std::initializer_list vertex_ids, const id_type hyperedge_id ) @@ -996,32 +1151,34 @@ class hypergraph final { this->bind_tail(std::views::all(vertex_ids), hyperedge_id); } - /// @brief Binds a range of vertex descriptors to the tail of a single bf-directed hyperedge descriptor. - /// @param vertex_rng A range of `vertex_descriptor`s. - /// @param hyperedge The `hyperedge_descriptor` wrapping the target hyperedge. + /// @brief Binds a range of vertices to the *tail* of a single hyperedge in a BF-directed hypergraph. + /// @param vertex_rng A forward range of vertex descriptors. + /// @param hyperedge The descriptor of the hyperedge. + /// @throws std::invalid_argument If either the hyperedge descriptor or any of the vertex descriptors is invalid. gl_attr_force_inline void bind_tail( - const traits::c_forward_range_of auto& vertex_rng, - const hyperedge_type& hyperedge + const traits::c_forward_range_of auto& vertex_rng, hyperedge_type hyperedge ) requires std::same_as { this->bind_tail(vertex_rng | std::views::transform(&vertex_type::id), hyperedge.id()); } - /// @brief Binds an initializer list of vertex descriptors to the tail of a single bf-directed hyperedge descriptor. - /// @param vertices An initializer list of `vertex_descriptor`s. - /// @param hyperedge The `hyperedge_descriptor` wrapping the target hyperedge. + /// @brief Binds a range of vertices to the *tail* of a single hyperedge in a BF-directed hypergraph. + /// @param vertices An initializer list of vertex descriptors. + /// @param hyperedge The descriptor of the hyperedge. + /// @throws std::invalid_argument If either the hyperedge descriptor or any of the vertex descriptors is invalid. gl_attr_force_inline void bind_tail( - std::initializer_list vertices, const hyperedge_type& hyperedge + std::initializer_list vertices, hyperedge_type hyperedge ) requires std::same_as { this->bind_tail(std::views::all(vertices), hyperedge); } - /// @brief Binds a single vertex to the tails of a range of bf-directed hyperedges. - /// @param vertex_id The raw identifier of the vertex. - /// @param hyperedge_id_rng A range of raw hyperedge identifiers. + /// @brief Binds a single vertex to the *tail* of a range of hyperedges in a BF-directed hypergraph. + /// @param vertex_id The ID of the vertex. + /// @param hyperedge_id_rng A forward range of hyperedge IDs. + /// @throws std::invalid_argument If either the vertex ID or any of the hyperedge IDs is invalid. void bind_tail( const id_type vertex_id, const traits::c_forward_range_of auto& hyperedge_id_rng ) @@ -1034,9 +1191,10 @@ class hypergraph final { } } - /// @brief Binds a single vertex to the tails of an initializer list of bf-directed hyperedges. - /// @param vertex_id The raw identifier of the vertex. - /// @param hyperedge_ids An initializer list of raw hyperedge identifiers. + /// @brief Binds a single vertex to the *tail* of a range of hyperedges in a BF-directed hypergraph. + /// @param vertex_id The ID of the vertex. + /// @param hyperedge_ids An initializer list of hyperedge IDs. + /// @throws std::invalid_argument If either the vertex ID or any of the hyperedge IDs is invalid. gl_attr_force_inline void bind_tail( const id_type vertex_id, std::initializer_list hyperedge_ids ) @@ -1045,32 +1203,34 @@ class hypergraph final { this->bind_tail(vertex_id, std::views::all(hyperedge_ids)); } - /// @brief Binds a single vertex descriptor to the tails of a range of bf-directed hyperedge descriptors. - /// @param vertex The target `vertex_descriptor`. - /// @param hyperedge_rng A range of `hyperedge_descriptor`s. + /// @brief Binds a single vertex to the *tail* of a range of hyperedges in a BF-directed hypergraph. + /// @param vertex The descriptor of the vertex. + /// @param hyperedge_rng A forward range of hyperedge descriptors. + /// @throws std::invalid_argument If either the vertex descriptor or any of the hyperedge descriptors is invalid. gl_attr_force_inline void bind_tail( - const vertex_type& vertex, - const traits::c_forward_range_of auto& hyperedge_rng + vertex_type vertex, const traits::c_forward_range_of auto& hyperedge_rng ) requires std::same_as { this->bind_tail(vertex.id(), hyperedge_rng | std::views::transform(&hyperedge_type::id)); } - /// @brief Binds a single vertex descriptor to the tails of an initializer list of bf-directed hyperedge descriptors. - /// @param vertex The target `vertex_descriptor`. - /// @param hyperedges An initializer list of `hyperedge_descriptor`s. + /// @brief Binds a single vertex to the *tail* of a range of hyperedges in a BF-directed hypergraph. + /// @param vertex The descriptor of the vertex. + /// @param hyperedges An initializer list of hyperedge descriptors. + /// @throws std::invalid_argument If either the vertex descriptor or any of the hyperedge descriptors is invalid. gl_attr_force_inline void bind_tail( - const vertex_type& vertex, std::initializer_list hyperedges + vertex_type vertex, std::initializer_list hyperedges ) requires std::same_as { this->bind_tail(vertex, std::views::all(hyperedges)); } - /// @brief Binds a vertex to the head (destination) of a bf-directed hyperedge. - /// @param vertex_id The raw identifier of the vertex. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Binds a vertex to the *head* of a hyperedge in a BF-directed hypergraph. + /// @param vertex_id The ID of the vertex. + /// @param hyperedge_id The ID of the hyperedge. + /// @throws std::invalid_argument If either ID is invalid. void bind_head(const id_type vertex_id, const id_type hyperedge_id) requires std::same_as { @@ -1079,18 +1239,20 @@ class hypergraph final { this->_impl.bind_head(vertex_id, hyperedge_id); } - /// @brief Binds a vertex descriptor to the head (destination) of a bf-directed hyperedge descriptor. - /// @param vertex The `vertex_descriptor` mapping to the vertex. - /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. - gl_attr_force_inline void bind_head(const vertex_type& vertex, const hyperedge_type& hyperedge) + /// @brief Binds a vertex to the *head* of a hyperedge in a BF-directed hypergraph. + /// @param vertex The descriptor of the vertex. + /// @param hyperedge The descriptor of the hyperedge. + /// @throws std::invalid_argument If either descriptor is invalid. + gl_attr_force_inline void bind_head(vertex_type vertex, hyperedge_type hyperedge) requires std::same_as { this->bind_head(vertex.id(), hyperedge.id()); } - /// @brief Binds a range of vertices to the head of a single bf-directed hyperedge. - /// @param vertex_id_rng A range of raw vertex identifiers. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Binds a range of vertices to the *head* of a single hyperedge in a BF-directed hypergraph. + /// @param vertex_id_rng A forward range of vertex IDs. + /// @param hyperedge_id The ID of the hyperedge. + /// @throws std::invalid_argument If either the hyperedge ID or any of the vertex IDs is invalid. void bind_head( const traits::c_forward_range_of auto& vertex_id_rng, const id_type hyperedge_id ) @@ -1103,9 +1265,10 @@ class hypergraph final { } } - /// @brief Binds an initializer list of vertices to the head of a single bf-directed hyperedge. - /// @param vertex_ids An initializer list of raw vertex identifiers. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Binds a list of vertices to the *head* of a single hyperedge in a BF-directed hypergraph. + /// @param vertex_ids An initializer list of vertex IDs. + /// @param hyperedge_id The ID of the hyperedge. + /// @throws std::invalid_argument If either the hyperedge ID or any of the vertex IDs is invalid. gl_attr_force_inline void bind_head( std::initializer_list vertex_ids, const id_type hyperedge_id ) @@ -1114,32 +1277,34 @@ class hypergraph final { this->bind_head(std::views::all(vertex_ids), hyperedge_id); } - /// @brief Binds a range of vertex descriptors to the head of a single bf-directed hyperedge descriptor. - /// @param vertex_rng A range of `vertex_descriptor`s. - /// @param hyperedge The `hyperedge_descriptor` wrapping the target hyperedge. + /// @brief Binds a range of vertices to the *head* of a single hyperedge in a BF-directed hypergraph. + /// @param vertex_rng A forward range of vertex descriptors. + /// @param hyperedge The descriptor of the hyperedge. + /// @throws std::invalid_argument If either the hyperedge descriptor or any of the vertex descriptors is invalid. gl_attr_force_inline void bind_head( - const traits::c_forward_range_of auto& vertex_rng, - const hyperedge_type& hyperedge + const traits::c_forward_range_of auto& vertex_rng, hyperedge_type hyperedge ) requires std::same_as { this->bind_head(vertex_rng | std::views::transform(&vertex_type::id), hyperedge.id()); } - /// @brief Binds an initializer list of vertex descriptors to the head of a single bf-directed hyperedge descriptor. - /// @param vertices An initializer list of `vertex_descriptor`s. - /// @param hyperedge The `hyperedge_descriptor` wrapping the target hyperedge. + /// @brief Binds a range of vertices to the *head* of a single hyperedge in a BF-directed hypergraph. + /// @param vertices An initializer list of vertex descriptors. + /// @param hyperedge The descriptor of the hyperedge. + /// @throws std::invalid_argument If either the hyperedge descriptor or any of the vertex descriptors is invalid. gl_attr_force_inline void bind_head( - std::initializer_list vertices, const hyperedge_type& hyperedge + std::initializer_list vertices, hyperedge_type hyperedge ) requires std::same_as { this->bind_head(std::views::all(vertices), hyperedge); } - /// @brief Binds a single vertex to the heads of a range of bf-directed hyperedges. - /// @param vertex_id The raw identifier of the vertex. - /// @param hyperedge_id_rng A range of raw hyperedge identifiers. + /// @brief Binds a single vertex to the *head* of a range of hyperedges in a BF-directed hypergraph. + /// @param vertex_id The ID of the vertex. + /// @param hyperedge_id_rng A forward range of hyperedge IDs. + /// @throws std::invalid_argument If either the vertex ID or any of the hyperedge IDs is invalid. void bind_head( const id_type vertex_id, const traits::c_forward_range_of auto& hyperedge_id_rng ) @@ -1152,9 +1317,10 @@ class hypergraph final { } } - /// @brief Binds a single vertex to the heads of an initializer list of bf-directed hyperedges. - /// @param vertex_id The raw identifier of the vertex. - /// @param hyperedge_ids An initializer list of raw hyperedge identifiers. + /// @brief Binds a single vertex to the *head* of a range of hyperedges in a BF-directed hypergraph. + /// @param vertex_id The ID of the vertex. + /// @param hyperedge_ids An initializer list of hyperedge IDs. + /// @throws std::invalid_argument If either the vertex ID or any of the hyperedge IDs is invalid. gl_attr_force_inline void bind_head( const id_type vertex_id, std::initializer_list hyperedge_ids ) @@ -1163,50 +1329,56 @@ class hypergraph final { this->bind_head(vertex_id, std::views::all(hyperedge_ids)); } - /// @brief Binds a single vertex descriptor to the heads of a range of bf-directed hyperedge descriptors. - /// @param vertex The target `vertex_descriptor`. - /// @param hyperedge_rng A range of `hyperedge_descriptor`s. + /// @brief Binds a single vertex to the *head* of a range of hyperedges in a BF-directed hypergraph. + /// @param vertex The descriptor of the vertex. + /// @param hyperedge_rng A forward range of hyperedge descriptors. + /// @throws std::invalid_argument If either the vertex descriptor or any of the hyperedge descriptors is invalid. gl_attr_force_inline void bind_head( - const vertex_type& vertex, - const traits::c_forward_range_of auto& hyperedge_rng + vertex_type vertex, const traits::c_forward_range_of auto& hyperedge_rng ) requires std::same_as { this->bind_head(vertex.id(), hyperedge_rng | std::views::transform(&hyperedge_type::id)); } - /// @brief Binds a single vertex descriptor to the heads of an initializer list of bf-directed hyperedge descriptors. - /// @param vertex The target `vertex_descriptor`. - /// @param hyperedges An initializer list of `hyperedge_descriptor`s. + /// @brief Binds a single vertex to the *head* of a range of hyperedges in a BF-directed hypergraph. + /// @param vertex The descriptor of the vertex. + /// @param hyperedges An initializer list of hyperedge descriptors. + /// @throws std::invalid_argument If either the vertex descriptor or any of the hyperedge descriptors is invalid. gl_attr_force_inline void bind_head( - const vertex_type& vertex, std::initializer_list hyperedges + vertex_type vertex, std::initializer_list hyperedges ) requires std::same_as { this->bind_head(vertex, std::views::all(hyperedges)); } - /// @brief Unbinds a vertex from a hyperedge entirely. - /// @param vertex_id The raw identifier of the vertex. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Unbinds a vertex from a hyperedge. + /// @param vertex_id The ID of the vertex. + /// @param hyperedge_id The ID of the hyperedge. void unbind(const id_type vertex_id, const id_type hyperedge_id) { this->_verify_vertex_id(vertex_id); this->_verify_hyperedge_id(hyperedge_id); this->_impl.unbind(vertex_id, hyperedge_id); } - /// @brief Unbinds a vertex from a hyperedge entirely using descriptors. - /// @param vertex The `vertex_descriptor` mapping to the vertex. - /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. - gl_attr_force_inline void unbind(const vertex_type& vertex, const hyperedge_type& hyperedge) { + /// @brief Unbinds a vertex from a hyperedge. + /// @param vertex The descriptor of the vertex. + /// @param hyperedge The descriptor of the hyperedge. + gl_attr_force_inline void unbind(vertex_type vertex, hyperedge_type hyperedge) { this->unbind(vertex.id(), hyperedge.id()); } // --- incidence validators --- - /// @brief Evaluates whether a vertex and a hyperedge are currently incident. - /// @param vertex_id The raw identifier of the vertex. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Evaluates whether the given vertex and hyperedge are incident. + /// + /// ### Formal Definition + /// - **Undirected Hypergraphs:** A vertex $v$ and a hyperedge $e$ are incident if \f$v \in e\f$. + /// - **BF-directed Hypergraphs:** A vertex $v$ and a hyperedge $e$ are incident if \f$v \in T(e) \lor v \in H(e)\f$. + /// + /// @param vertex_id The ID of the vertex. + /// @param hyperedge_id The ID of the hyperedge. /// @return `true` if the vertex belongs to the hyperedge (in any direction), `false` otherwise. [[nodiscard]] bool are_incident(const id_type vertex_id, const id_type hyperedge_id) const { this->_verify_vertex_id(vertex_id); @@ -1219,15 +1391,18 @@ class hypergraph final { /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. /// @return `true` if the vertex belongs to the hyperedge, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool are_incident( - const vertex_type& vertex, const hyperedge_type& hyperedge + vertex_type vertex, hyperedge_type hyperedge ) const { return this->are_incident(vertex.id(), hyperedge.id()); } - /// @brief Evaluates whether a vertex is in the tail (source) of a bf-directed hyperedge. - /// @param vertex_id The raw identifier of the vertex. - /// @param hyperedge_id The raw identifier of the hyperedge. - /// @return `true` if the vertex is in the tail set, `false` otherwise. + /// @brief Evaluates whether a vertex belongs to the *tail* of a hyperedge in a BF-directed hypergraph. + /// + /// Formally, the function evaluates whether \f$v \in T(e)\f$ or is one of the sources of the hyperedge. + /// + /// @param vertex_id The ID of the vertex. + /// @param hyperedge_id The ID of the hyperedge. + /// @return `true` if the vertex is in the *tail* set of the hyperedge, `false` otherwise. [[nodiscard]] bool is_tail(const id_type vertex_id, const id_type hyperedge_id) const requires std::same_as { @@ -1236,22 +1411,27 @@ class hypergraph final { return this->_impl.is_tail(vertex_id, hyperedge_id); } - /// @brief Evaluates whether a vertex descriptor is in the tail of a bf-directed hyperedge descriptor. + /// @brief Evaluates whether a vertex belongs to the *tail* of a hyperedge in a BF-directed hypergraph. + /// + /// Formally, the function evaluates whether \f$v \in T(e)\f$ or is one of the sources of the hyperedge. + /// /// @param vertex The `vertex_descriptor` mapping to the vertex. /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. - /// @return `true` if the vertex is in the tail set, `false` otherwise. - [[nodiscard]] gl_attr_force_inline bool is_tail( - const vertex_type& vertex, const hyperedge_type& hyperedge - ) const + /// @return `true` if the vertex is in the *tail* set of the hyperedge, `false` otherwise. + [[nodiscard]] gl_attr_force_inline bool is_tail(vertex_type vertex, hyperedge_type hyperedge) + const requires std::same_as { return this->is_tail(vertex.id(), hyperedge.id()); } - /// @brief Evaluates whether a vertex is in the head (destination) of a bf-directed hyperedge. - /// @param vertex_id The raw identifier of the vertex. - /// @param hyperedge_id The raw identifier of the hyperedge. - /// @return `true` if the vertex is in the head set, `false` otherwise. + /// @brief Evaluates whether a vertex belongs to the *head* of a hyperedge in a BF-directed hypergraph. + /// + /// Formally, the function evaluates whether \f$v \in H(e)\f$ or is one of the targets of the hyperedge. + /// + /// @param vertex_id The ID of the vertex. + /// @param hyperedge_id The ID of the hyperedge. + /// @return `true` if the vertex is in the *head* set of the hyperedge, `false` otherwise. [[nodiscard]] bool is_head(const id_type vertex_id, const id_type hyperedge_id) const requires std::same_as { @@ -1260,13 +1440,15 @@ class hypergraph final { return this->_impl.is_head(vertex_id, hyperedge_id); } - /// @brief Evaluates whether a vertex descriptor is in the head of a bf-directed hyperedge descriptor. + /// @brief Evaluates whether a vertex belongs to the *head* of a hyperedge in a BF-directed hypergraph. + /// + /// Formally, the function evaluates whether \f$v \in H(e)\f$ or is one of the targets of the hyperedge. + /// /// @param vertex The `vertex_descriptor` mapping to the vertex. /// @param hyperedge The `hyperedge_descriptor` mapping to the hyperedge. - /// @return `true` if the vertex is in the head set, `false` otherwise. - [[nodiscard]] gl_attr_force_inline bool is_head( - const vertex_type& vertex, const hyperedge_type& hyperedge - ) const + /// @return `true` if the vertex is in the *head* set of the hyperedge, `false` otherwise. + [[nodiscard]] gl_attr_force_inline bool is_head(vertex_type vertex, hyperedge_type hyperedge) + const requires std::same_as { return this->is_head(vertex.id(), hyperedge.id()); @@ -1274,53 +1456,69 @@ class hypergraph final { // --- incidence getters --- - /// @brief Returns a transformed range of fully bound `hyperedge_descriptor`s incident to the specified vertex. - /// @param vertex_id The raw identifier of the vertex. + /// @brief Retrieves all hyperedges incident with a vertex (\f$\{e in E : v \in e\}\f$). + /// @param vertex_id The vertex ID. + /// @return A view representing the set of incident hyperedges. + /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] gl_attr_force_inline auto incident_hyperedges(const id_type vertex_id) { return this->incident_hyperedge_ids(vertex_id) | std::views::transform(this->_create_hyperedge_descriptor()); } - /// @brief Returns a transformed range of fully bound `hyperedge_descriptor`s incident to the specified vertex descriptor. - /// @param vertex The target `vertex_descriptor`. - [[nodiscard]] gl_attr_force_inline auto incident_hyperedges(const vertex_type& vertex) { + /// @brief Retrieves all hyperedges incident with a vertex (\f$\{e in E : v \in e\}\f$). + /// @param vertex The vertex descriptor. + /// @return A view representing the set of incident hyperedges. + /// @throws std::invalid_argument If the vertex descriptor is invalid. + [[nodiscard]] gl_attr_force_inline auto incident_hyperedges(vertex_type vertex) { return this->incident_hyperedges(vertex.id()); } - /// @brief Returns a lightweight range of raw hyperedge IDs incident to the specified vertex. - /// @param vertex_id The raw identifier of the vertex. + /// @brief Retrieves IDs of all hyperedges incident with a vertex (\f$\{e in E : v \in e\}\f$). + /// @param vertex_id The vertex ID. + /// @return A view representing the set of incident hyperedge IDs. + /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] auto incident_hyperedge_ids(const id_type vertex_id) const { this->_verify_vertex_id(vertex_id); return this->_impl.incident_hyperedges(vertex_id); } - /// @brief Returns a lightweight range of raw hyperedge IDs incident to the specified vertex descriptor. - /// @param vertex The target `vertex_descriptor`. - [[nodiscard]] gl_attr_force_inline auto incident_hyperedge_ids(const vertex_type& vertex - ) const { + /// @brief Retrieves IDs of all hyperedges incident with a vertex (\f$\{e in E : v \in e\}\f$). + /// @param vertex The vertex descriptor. + /// @return A view representing the set of incident hyperedge IDs. + /// @throws std::invalid_argument If the vertex descriptor is invalid. + [[nodiscard]] gl_attr_force_inline auto incident_hyperedge_ids(vertex_type vertex) const { return this->incident_hyperedge_ids(vertex.id()); } - /// @brief Retrieves the total degree (number of incident hyperedges) of a vertex. - /// @param vertex_id The raw identifier of the vertex. + /// @brief Calculates the degree of a vertex in the hypergraph. + /// @copydetails detail::hypergraph_doc_anchors::degree() + /// @param vertex_id The ID of the vertex. + /// @return The degree of the vertex. + /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] size_type degree(const id_type vertex_id) const { this->_verify_vertex_id(vertex_id); return this->_impl.degree(vertex_id); } - /// @brief Retrieves the total degree (number of incident hyperedges) of a vertex descriptor. - /// @param vertex The target `vertex_descriptor`. - [[nodiscard]] gl_attr_force_inline size_type degree(const vertex_type& vertex) const { + /// @brief Calculates the degree of a vertex in the hypergraph. + /// @copydetails detail::hypergraph_doc_anchors::degree() + /// @param vertex The descriptor of the vertex. + /// @return The degree of the vertex. + /// @throws std::invalid_argument If the vertex descriptor is invalid. + [[nodiscard]] gl_attr_force_inline size_type degree(vertex_type vertex) const { return this->degree(vertex.id()); } - /// @brief Generates a contiguous degree map for all valid vertices in the hypergraph. + /// @brief Returns a mapped array of degrees for all vertices. + /// @return A vector where the index aligns with the vertex ID containing its degree. [[nodiscard]] std::vector degree_map() const { return this->_impl.degree_map(this->_n_vertices); } - /// @brief Returns a transformed range of fully bound `hyperedge_descriptor`s originating from the specified vertex (bf-directed). - /// @param vertex_id The raw identifier of the vertex. + /// @brief Retrieves all outgoing (tail-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in T(e)\}\f$). + /// @param vertex_id The vertex ID. + /// @return A view representing the set of outgoing hyperedges. + /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] gl_attr_force_inline auto out_hyperedges(const id_type vertex_id) const requires std::same_as { @@ -1328,16 +1526,20 @@ class hypergraph final { | std::views::transform(this->_create_hyperedge_descriptor()); } - /// @brief Returns a transformed range of fully bound `hyperedge_descriptor`s originating from the specified vertex descriptor (bf-directed). - /// @param vertex The target `vertex_descriptor`. - [[nodiscard]] gl_attr_force_inline auto out_hyperedges(const vertex_type& vertex) const + /// @brief Retrieves all outgoing (tail-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in T(e)\}\f$). + /// @param vertex The vertex descriptor. + /// @return A view representing the set of outgoing hyperedges. + /// @throws std::invalid_argument If the vertex descriptor is invalid. + [[nodiscard]] gl_attr_force_inline auto out_hyperedges(vertex_type vertex) const requires std::same_as { return this->out_hyperedges(vertex.id()); } - /// @brief Returns a lightweight range of raw hyperedge IDs originating from the specified vertex (bf-directed). - /// @param vertex_id The raw identifier of the vertex. + /// @brief Retrieves IDs of all outgoing (tail-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in T(e)\}\f$). + /// @param vertex_id The vertex ID. + /// @return A view representing the set of outgoing hyperedge IDs. + /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] auto out_hyperedge_ids(const id_type vertex_id) const requires std::same_as { @@ -1345,16 +1547,21 @@ class hypergraph final { return this->_impl.out_hyperedges(vertex_id); } - /// @brief Returns a lightweight range of raw hyperedge IDs originating from the specified vertex descriptor (bf-directed). - /// @param vertex The target `vertex_descriptor`. - [[nodiscard]] gl_attr_force_inline auto out_hyperedge_ids(const vertex_type& vertex) const + /// @brief Retrieves IDs of all outgoing (tail-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in T(e)\}\f$). + /// @param vertex The vertex descriptor. + /// @return A view representing the set of outgoing hyperedge IDs. + /// @throws std::invalid_argument If the vertex descriptor is invalid. + [[nodiscard]] gl_attr_force_inline auto out_hyperedge_ids(vertex_type vertex) const requires std::same_as { return this->out_hyperedge_ids(vertex.id()); } - /// @brief Retrieves the out-degree (number of hyperedges where the vertex is in the Tail) of a vertex. - /// @param vertex_id The raw identifier of the vertex. + /// @brief Calculates the out-degree of a vertex in the *BF-directed* hypergraph. + /// @copydetails detail::hypergraph_doc_anchors::out_degree() + /// @param vertex_id The ID of the vertex. + /// @return The out-degree of the vertex. + /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] size_type out_degree(const id_type vertex_id) const requires std::same_as { @@ -1362,23 +1569,29 @@ class hypergraph final { return this->_impl.out_degree(vertex_id); } - /// @brief Retrieves the out-degree (number of hyperedges where the vertex is in the Tail) of a vertex descriptor. - /// @param vertex The target `vertex_descriptor`. - [[nodiscard]] gl_attr_force_inline size_type out_degree(const vertex_type& vertex) const + /// @brief Calculates the out-degree of a vertex in the *BF-directed* hypergraph. + /// @copydetails detail::hypergraph_doc_anchors::out_degree() + /// @param vertex The descriptor of the vertex. + /// @return The out-degree of the vertex. + /// @throws std::invalid_argument If the vertex descriptor is invalid. + [[nodiscard]] gl_attr_force_inline size_type out_degree(vertex_type vertex) const requires std::same_as { return this->out_degree(vertex.id()); } - /// @brief Generates a contiguous out-degree map for all valid vertices in the bf-directed hypergraph. + /// @brief Returns a mapped array of out-degrees for all vertices. + /// @return A vector where the index aligns with the vertex ID containing its out-degree. [[nodiscard]] std::vector out_degree_map() const requires std::same_as { return this->_impl.out_degree_map(this->_n_vertices); } - /// @brief Returns a transformed range of fully bound `hyperedge_descriptor`s entering the specified vertex (bf-directed). - /// @param vertex_id The raw identifier of the vertex. + /// @brief Retrieves all incoming (head-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in E(e)\}\f$). + /// @param vertex_id The vertex ID. + /// @return A view representing the set of incoming hyperedges. + /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] gl_attr_force_inline auto in_hyperedges(const id_type vertex_id) const requires std::same_as { @@ -1386,16 +1599,20 @@ class hypergraph final { | std::views::transform(this->_create_hyperedge_descriptor()); } - /// @brief Returns a transformed range of fully bound `hyperedge_descriptor`s entering the specified vertex descriptor (bf-directed). - /// @param vertex The target `vertex_descriptor`. - [[nodiscard]] gl_attr_force_inline auto in_hyperedges(const vertex_type& vertex) const + /// @brief Retrieves all incoming (head-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in E(e)\}\f$). + /// @param vertex The vertex descriptor. + /// @return A view representing the set of incoming hyperedges. + /// @throws std::invalid_argument If the vertex descriptor is invalid. + [[nodiscard]] gl_attr_force_inline auto in_hyperedges(vertex_type vertex) const requires std::same_as { return this->in_hyperedges(vertex.id()); } - /// @brief Returns a lightweight range of raw hyperedge IDs entering the specified vertex (bf-directed). - /// @param vertex_id The raw identifier of the vertex. + /// @brief Retrieves IDs of all incoming (head-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in E(e)\}\f$). + /// @param vertex_id The vertex ID. + /// @return A view representing the set of incoming hyperedge IDs. + /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] auto in_hyperedge_ids(const id_type vertex_id) const requires std::same_as { @@ -1403,16 +1620,21 @@ class hypergraph final { return this->_impl.in_hyperedges(vertex_id); } - /// @brief Returns a lightweight range of raw hyperedge IDs entering the specified vertex descriptor (bf-directed). - /// @param vertex The target `vertex_descriptor`. - [[nodiscard]] gl_attr_force_inline auto in_hyperedge_ids(const vertex_type& vertex) const + /// @brief Retrieves IDs of all incoming (head-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in E(e)\}\f$). + /// @param vertex The vertex descriptor. + /// @return A view representing the set of incoming hyperedge IDs. + /// @throws std::invalid_argument If the vertex descriptor is invalid. + [[nodiscard]] gl_attr_force_inline auto in_hyperedge_ids(vertex_type vertex) const requires std::same_as { return this->in_hyperedge_ids(vertex.id()); } - /// @brief Retrieves the in-degree (number of hyperedges where the vertex is in the Head) of a vertex. - /// @param vertex_id The raw identifier of the vertex. + /// @brief Calculates the in-degree of a vertex in the *BF-directed* hypergraph. + /// @copydetails detail::hypergraph_doc_anchors::in_degree() + /// @param vertex_id The ID of the vertex. + /// @return The in-degree of the vertex. + /// @throws std::invalid_argument If the vertex ID is invalid. [[nodiscard]] size_type in_degree(const id_type vertex_id) const requires std::same_as { @@ -1420,70 +1642,94 @@ class hypergraph final { return this->_impl.in_degree(vertex_id); } - /// @brief Retrieves the in-degree (number of hyperedges where the vertex is in the Head) of a vertex descriptor. - /// @param vertex The target `vertex_descriptor`. - [[nodiscard]] gl_attr_force_inline size_type in_degree(const vertex_type& vertex) const + /// @brief Calculates the in-degree of a vertex in the *BF-directed* hypergraph. + /// @copydetails detail::hypergraph_doc_anchors::in_degree() + /// @param vertex The descriptor of the vertex. + /// @return The in-degree of the vertex. + /// @throws std::invalid_argument If the vertex descriptor is invalid. + [[nodiscard]] gl_attr_force_inline size_type in_degree(vertex_type vertex) const requires std::same_as { return this->in_degree(vertex.id()); } - /// @brief Generates a contiguous in-degree map for all valid vertices in the bf-directed hypergraph. + /// @brief Returns a mapped array of in-degrees for all vertices. + /// @return A vector where the index aligns with the vertex ID containing its in-degree. [[nodiscard]] std::vector in_degree_map() const requires std::same_as { return this->_impl.in_degree_map(this->_n_vertices); } - /// @brief Returns a transformed range of fully bound `vertex_descriptor`s incident to the specified hyperedge. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Retrieves all vertices incident with a hyperedge ($e$). + /// @param hyperedge_id The hyperedge ID. + /// @return A view representing the set of incident vertices. + /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] auto incident_vertices(const id_type hyperedge_id) const { return this->incident_vertex_ids(hyperedge_id) | std::views::transform(this->_create_vertex_descriptor()); } - /// @brief Returns a transformed range of fully bound `vertex_descriptor`s incident to the specified hyperedge descriptor. - /// @param hyperedge The target `hyperedge_descriptor`. - [[nodiscard]] gl_attr_force_inline auto incident_vertices(const hyperedge_type& hyperedge - ) const { + /// @brief Retrieves all vertices incident with a hyperedge ($e$). + /// @param hyperedge The hyperedge descriptor. + /// @return A view representing the set of incident vertices. + /// @throws std::invalid_argument If the hyperedge descriptor is invalid. + [[nodiscard]] gl_attr_force_inline auto incident_vertices(hyperedge_type hyperedge) const { return this->incident_vertices(hyperedge.id()); } - /// @brief Returns a lightweight range of raw vertex IDs incident to the specified hyperedge. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Retrieves IDs of all vertices incident with a hyperedge ($e$). + /// @param hyperedge_id The hyperedge ID. + /// @return A view representing the set of incident vertex IDs. + /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] auto incident_vertex_ids(const id_type hyperedge_id) const { this->_verify_hyperedge_id(hyperedge_id); return this->_impl.incident_vertices(hyperedge_id); } - /// @brief Returns a lightweight range of raw vertex IDs incident to the specified hyperedge descriptor. - /// @param hyperedge The target `hyperedge_descriptor`. - [[nodiscard]] gl_attr_force_inline auto incident_vertex_ids(const hyperedge_type& hyperedge - ) const { + /// @brief Retrieves IDs of all vertices incident with a hyperedge ($e$). + /// @param hyperedge The hyperedge descriptor. + /// @return A view representing the set of incident vertex IDs. + /// @throws std::invalid_argument If the hyperedge descriptor is invalid. + [[nodiscard]] gl_attr_force_inline auto incident_vertex_ids(hyperedge_type hyperedge) const { return this->incident_vertex_ids(hyperedge.id()); } - /// @brief Retrieves the size (total number of incident vertices) of a hyperedge. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Retrieves the size (number of incident vertices) of the given hyperedge. + /// + /// - For undirected hypergraphs this is equivalent to $\vert e \vert$. + /// - For BF-directed hypergraphs this is equivalent to $\vert T(e) \vert + \vert H(e) \vert$ + /// + /// @param hyperedge_id The ID of the hyperedge. + /// @return The size of the hyperedge. + /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] size_type hyperedge_size(const id_type hyperedge_id) const { this->_verify_hyperedge_id(hyperedge_id); return this->_impl.hyperedge_size(hyperedge_id); } - /// @brief Retrieves the size (total number of incident vertices) of a hyperedge descriptor. - /// @param hyperedge The target `hyperedge_descriptor`. - [[nodiscard]] gl_attr_force_inline size_type hyperedge_size(const hyperedge_type& hyperedge - ) const { + /// @brief Retrieves the size (number of incident vertices) of the given hyperedge. + /// + /// - For undirected hypergraphs this is equivalent to $\vert e \vert$. + /// - For BF-directed hypergraphs this is equivalent to $\vert T(e) \vert + \vert H(e) \vert$ + /// + /// @param hyperedge The descriptor of the hyperedge. + /// @return The size of the hyperedge. + /// @throws std::invalid_argument If the hyperedge descriptor is invalid. + [[nodiscard]] gl_attr_force_inline size_type hyperedge_size(hyperedge_type hyperedge) const { return this->hyperedge_size(hyperedge.id()); } - /// @brief Generates a contiguous map of sizes for all valid hyperedges in the hypergraph. + /// @brief Returns a mapped array of hyperedge sizes. + /// @return A vector where the index aligns with the hyperedge ID containing its size. [[nodiscard]] std::vector hyperedge_size_map() const { return this->_impl.hyperedge_size_map(this->_n_hyperedges); } - /// @brief Returns a transformed range of fully bound `vertex_descriptor`s in the tail of the specified bf-directed hyperedge. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Retrieves all vertices in the *tail* set of a hyperedge ($T(e)$). + /// @param hyperedge_id The hypepredge ID. + /// @return A view representing the set of the hyperedge's tail vertices. + /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] gl_attr_force_inline auto tail(const id_type hyperedge_id) const requires std::same_as { @@ -1491,16 +1737,20 @@ class hypergraph final { | std::views::transform(this->_create_vertex_descriptor()); } - /// @brief Returns a transformed range of fully bound `vertex_descriptor`s in the tail of the specified bf-directed hyperedge descriptor. - /// @param hyperedge The target `hyperedge_descriptor`. - [[nodiscard]] gl_attr_force_inline auto tail(const hyperedge_type& hyperedge) const + /// @brief Retrieves all vertices in the *tail* set of a hyperedge ($T(e)$). + /// @param hyperedge The hyperedge descriptor. + /// @return A view representing the set of the hyperedge's tail vertices. + /// @throws std::invalid_argument If the hyperedge descriptor is invalid. + [[nodiscard]] gl_attr_force_inline auto tail(hyperedge_type hyperedge) const requires std::same_as { return this->tail(hyperedge.id()); } - /// @brief Returns a lightweight range of raw vertex IDs in the tail of the specified bf-directed hyperedge. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Retrieves IDs all vertices in the *tail* set of a hyperedge ($T(e)$). + /// @param hyperedge_id The hypepredge ID. + /// @return A view representing the set of the hyperedge's tail vertex IDs. + /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] auto tail_ids(const id_type hyperedge_id) const requires std::same_as { @@ -1508,16 +1758,20 @@ class hypergraph final { return this->_impl.tail(hyperedge_id); } - /// @brief Returns a lightweight range of raw vertex IDs in the tail of the specified bf-directed hyperedge descriptor. - /// @param hyperedge The target `hyperedge_descriptor`. - [[nodiscard]] gl_attr_force_inline auto tail_ids(const hyperedge_type& hyperedge) const + /// @brief Retrieves IDs all vertices in the *tail* set of a hyperedge ($T(e)$). + /// @param hyperedge The hypepredge descriptor. + /// @return A view representing the set of the hyperedge's tail vertex IDs. + /// @throws std::invalid_argument If the hyperedge descriptor is invalid. + [[nodiscard]] gl_attr_force_inline auto tail_ids(hyperedge_type hyperedge) const requires std::same_as { return this->tail_ids(hyperedge.id()); } - /// @brief Retrieves the tail size (number of tail vertices) of a bf-directed hyperedge. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Retrieves the size of the *BF-directed* hyperedge's *tail* set ($\vert T(e) \vert). + /// @param hyperedge_id The ID of the hyperedge. + /// @return The size of the hyperedge's *tail* set. + /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] size_type tail_size(const id_type hyperedge_id) const requires std::same_as { @@ -1525,23 +1779,28 @@ class hypergraph final { return this->_impl.tail_size(hyperedge_id); } - /// @brief Retrieves the tail size (number of tail vertices) of a bf-directed hyperedge descriptor. - /// @param hyperedge The target `hyperedge_descriptor`. - [[nodiscard]] gl_attr_force_inline size_type tail_size(const hyperedge_type& hyperedge) const + /// @brief Retrieves the size of the *BF-directed* hyperedge's *tail* set ($\vert T(e) \vert). + /// @param hyperedge The descriptor of the hyperedge. + /// @return The size of the hyperedge's *tail* set. + /// @throws std::invalid_argument If the hyperedge descriptor is invalid. + [[nodiscard]] gl_attr_force_inline size_type tail_size(hyperedge_type hyperedge) const requires std::same_as { return this->tail_size(hyperedge.id()); } - /// @brief Generates a contiguous map of tail sizes for all valid bf-directed hyperedges. + /// @brief Returns a mapped array of sizes of the *tail* sets of hyperedged in a *BF-directed* hypergraph. + /// @return A vector where the index aligns with the hyperedge ID containing its *tail* set size. [[nodiscard]] std::vector tail_size_map() const requires std::same_as { return this->_impl.tail_size_map(this->_n_hyperedges); } - /// @brief Returns a transformed range of fully bound `vertex_descriptor`s in the head of the specified bf-directed hyperedge. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Retrieves all vertices in the *head* set of a hyperedge ($H(e)$). + /// @param hyperedge_id The hypepredge ID. + /// @return A view representing the set of the hyperedge's head vertices. + /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] gl_attr_force_inline auto head(const id_type hyperedge_id) const requires std::same_as { @@ -1549,16 +1808,20 @@ class hypergraph final { | std::views::transform(this->_create_vertex_descriptor()); } - /// @brief Returns a transformed range of fully bound `vertex_descriptor`s in the head of the specified bf-directed hyperedge descriptor. - /// @param hyperedge The target `hyperedge_descriptor`. - [[nodiscard]] gl_attr_force_inline auto head(const hyperedge_type& hyperedge) const + /// @brief Retrieves all vertices in the *head* set of a hyperedge ($H(e)$). + /// @param hyperedge The hyperedge descriptor. + /// @return A view representing the set of the hyperedge's head vertices. + /// @throws std::invalid_argument If the hyperedge descriptor is invalid. + [[nodiscard]] gl_attr_force_inline auto head(hyperedge_type hyperedge) const requires std::same_as { return this->head(hyperedge.id()); } - /// @brief Returns a lightweight range of raw vertex IDs in the head of the specified bf-directed hyperedge. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Retrieves IDs all vertices in the *head* set of a hyperedge ($H(e)$). + /// @param hyperedge_id The hypepredge ID. + /// @return A view representing the set of the hyperedge's head vertex IDs. + /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] auto head_ids(const id_type hyperedge_id) const requires std::same_as { @@ -1566,16 +1829,20 @@ class hypergraph final { return this->_impl.head(hyperedge_id); } - /// @brief Returns a lightweight range of raw vertex IDs in the head of the specified bf-directed hyperedge descriptor. - /// @param hyperedge The target `hyperedge_descriptor`. - [[nodiscard]] gl_attr_force_inline auto head_ids(const hyperedge_type& hyperedge) const + /// @brief Retrieves IDs all vertices in the *head* set of a hyperedge ($H(e)$). + /// @param hyperedge The hypepredge descriptor. + /// @return A view representing the set of the hyperedge's head vertex IDs. + /// @throws std::invalid_argument If the hyperedge descriptor is invalid. + [[nodiscard]] gl_attr_force_inline auto head_ids(hyperedge_type hyperedge) const requires std::same_as { return this->head_ids(hyperedge.id()); } - /// @brief Retrieves the head size (number of head vertices) of a bf-directed hyperedge. - /// @param hyperedge_id The raw identifier of the hyperedge. + /// @brief Retrieves the size of the *BF-directed* hyperedge's *head* set ($\vert H(e) \vert). + /// @param hyperedge_id The ID of the hyperedge. + /// @return The size of the hyperedge's *head* set. + /// @throws std::invalid_argument If the hyperedge ID is invalid. [[nodiscard]] size_type head_size(const id_type hyperedge_id) const requires std::same_as { @@ -1583,15 +1850,18 @@ class hypergraph final { return this->_impl.head_size(hyperedge_id); } - /// @brief Retrieves the head size (number of head vertices) of a bf-directed hyperedge descriptor. - /// @param hyperedge The target `hyperedge_descriptor`. - [[nodiscard]] gl_attr_force_inline size_type head_size(const hyperedge_type& hyperedge) const + /// @brief Retrieves the size of the *BF-directed* hyperedge's *head* set ($\vert H(e) \vert). + /// @param hyperedge The descriptor of the hyperedge. + /// @return The size of the hyperedge's *head* set. + /// @throws std::invalid_argument If the hyperedge descriptor is invalid. + [[nodiscard]] gl_attr_force_inline size_type head_size(hyperedge_type hyperedge) const requires std::same_as { return this->head_size(hyperedge.id()); } - /// @brief Generates a contiguous map of head sizes for all valid bf-directed hyperedges. + /// @brief Returns a mapped array of sizes of the *head* sets of hyperedged in a *BF-directed* hypergraph. + /// @return A vector where the index aligns with the hyperedge ID containing its *head* set size. [[nodiscard]] std::vector head_size_map() const requires std::same_as { @@ -1600,7 +1870,10 @@ class hypergraph final { // --- comparison --- - /// @brief Compares two hypergraphs for strict equality, checking topologies and properties. + /// @brief Compares two hypergraphs for strict structural and property equality. + /// @param lhs The left operand. + /// @param rhs The right operand. + /// @return `true` if both graphs represent the exact same topology and properties, `false` otherwise. [[nodiscard]] friend bool operator==(const hypergraph& lhs, const hypergraph& rhs) noexcept { if (lhs._n_vertices != rhs._n_vertices or lhs._n_hyperedges != rhs._n_hyperedges) return false; @@ -1618,15 +1891,15 @@ class hypergraph final { // --- I/O utility --- - /// @brief Helper structure used to correctly format an individual hyperedge into an output stream. + /// @brief Helper structure used to properly format an individual hyperedge within the hypergraph's context into an output stream. struct hyperedge_formatter { public: /// @brief The hypergraph owning the hyperedge. const hypergraph& hg; - /// @brief The hyperedge descriptor to be formatted. + /// @brief The hyperedge to be formatted. const hyperedge_type hyperedge; - /// @brief Stream insertion operator for undirected hyperedge formatters. + /// @brief Stream insertion operator for undirected hyperedges. friend std::ostream& operator<<(std::ostream& os, const hyperedge_formatter& proxy) requires std::same_as { @@ -1650,7 +1923,7 @@ class hypergraph final { return os; } - /// @brief Stream insertion operator for bf-directed hyperedge formatters. + /// @brief Stream insertion operator for BF-directed hyperedges. friend std::ostream& operator<<(std::ostream& os, const hyperedge_formatter& proxy) requires std::same_as { @@ -1677,19 +1950,19 @@ class hypergraph final { } }; - /// @brief Retrieves a formatter object that safely encapsulates a hyperedge for stream output. - /// @param hyperedge The target `hyperedge_descriptor`. - /// @return A `hyperedge_formatter` structure prepared for standard stream insertion. - [[nodiscard]] hyperedge_formatter display(const hyperedge_type& hyperedge) const { + /// @brief Returns a formatter object that safely encapsulates a hyperedge for stream output. + /// @param hyperedge The hyperedge to format. + /// @return A @ref hyperedge_formatter structure prepared for standard stream insertion. + [[nodiscard]] hyperedge_formatter display(hyperedge_type hyperedge) const { return hyperedge_formatter{*this, hyperedge}; } /// @brief Formats and outputs the entire hypergraph structure to a standard output stream. /// - /// Behaves dynamically based on whether the `gl::io::verbose` or `gl::io::spec_fmt` stream manipulators are set. + /// The generated string representation of the hypergraph depends on the currently active formatting options of the stream. /// /// @param os The target output stream. - /// @param hg The hypergraph instance to serialize or print. + /// @param hg The hypergraph instance to write. /// @return The stream reference for chaining. friend std::ostream& operator<<(std::ostream& os, const hypergraph& hg) { using enum io::detail::option_bit; @@ -1703,9 +1976,9 @@ class hypergraph final { return hg._concise_write(os); } - /// @brief Reads and deserializes an entire hypergraph from a given stream using HGSF format. - /// @param is The input stream containing HGSF formatted data. - /// @param hg The hypergraph instance to populate. + /// @brief Deserializes hypergraph structure data from an input stream (using the HGSF format). + /// @param is The source input stream. + /// @param g The hypergraph instance to populate. /// @return The stream reference for chaining. friend gl_attr_force_inline std::istream& operator>>(std::istream& is, hypergraph& hg) { return hg._hgsf_read(is); @@ -2032,6 +2305,11 @@ class hypergraph final { // --- general hypergraph utility --- +/// @ingroup HGL-Core +/// @brief Creates a deep copy of the given hypergraph. +/// @tparam Hypergraph The type of the hypergraph. +/// @param source The hypergraph instance to clone. +/// @return A newly constructed hypergraph containing identical vertices, hyperedges and properties (if applicable). template [[nodiscard]] Hypergraph clone(const Hypergraph& source) { return Hypergraph(source); @@ -2047,7 +2325,7 @@ using undirected_hypergraph = hypergraph>; /// @ingroup HGL-Core -/// @brief Convenience alias for a bf-directed hypergraph. +/// @brief Convenience alias for a BF-directed hypergraph. template < traits::c_properties VertexProperties = empty_properties, traits::c_properties HyperedgeProperties = empty_properties, @@ -2056,7 +2334,7 @@ using bf_directed_hypergraph = hypergraph>; /// @ingroup HGL-Core -/// @brief Convenience alias for a hypergraph backed by a standard incidence list. +/// @brief Convenience alias for a hypergraph utilizing a standard incidence list implementation model. template < traits::c_hypergraph_layout_tag LayoutTag = impl::bidirectional_t, traits::c_hypergraph_directional_tag DirectionalTag = undirected_t, @@ -2067,7 +2345,7 @@ using list_hypergraph = hypergraph< list_hypergraph_traits>; /// @ingroup HGL-Core -/// @brief Convenience alias for a hypergraph backed by a flat incidence list. +/// @brief Convenience alias for a hypergraph utilizing a flat incidence list implementation model. template < traits::c_hypergraph_layout_tag LayoutTag = impl::bidirectional_t, traits::c_hypergraph_directional_tag DirectionalTag = undirected_t, @@ -2082,7 +2360,7 @@ using flat_list_hypergraph = hypergraph>; /// @ingroup HGL-Core -/// @brief Convenience alias for a hypergraph backed by a standard incidence matrix. +/// @brief Convenience alias for a hypergraph utilizing a standard incidence matrix implementation model. template < traits::c_hypergraph_layout_tag LayoutTag = impl::bidirectional_t, traits::c_hypergraph_directional_tag DirectionalTag = undirected_t, @@ -2093,7 +2371,7 @@ using matrix_hypergraph = hypergraph< matrix_hypergraph_traits>; /// @ingroup HGL-Core -/// @brief Convenience alias for a hypergraph backed by a flat incidence matrix. +/// @brief Convenience alias for a hypergraph utilizing a flat incidence matrix implementation model. template < traits::c_hypergraph_layout_tag LayoutTag = impl::bidirectional_t, traits::c_hypergraph_directional_tag DirectionalTag = undirected_t, @@ -2124,7 +2402,7 @@ using flat_matrix_hypergraph = hypergraph [!IMPORTANT] ID Stability +/// > +/// > Adding vertices does **not** invalidate existing vertex IDs. **However**, property references stored in existing vertex descriptors may be invalidated. +void add_vertex_note(); + +/// > [!WARNING] Descriptor and ID Invalidation +/// > +/// > Removing a vertex invalidates: +/// > - All vertex descriptors and IDs for vertices with higher IDs (they shift down). +/// > - References to all properties associated with the vertices with IDs shifted as a result of the removal operation. +/// > +/// > Proceed with caution when maintaining external vertex IDs, descriptors or properties. +void remove_vertex_wrn(); + +/// > [!IMPORTANT] ID Stability +/// > +/// > Adding hyperedges does **not** invalidate existing hyperedge IDs. **However**, property references stored in existing hyperedge descriptors may be invalidated. +void add_hyperedge_note(); + +/// > [!WARNING] Descriptor and ID Invalidation +/// > +/// > Removing a hyperedge invalidates: +/// > - All hyperedge descriptors and IDs for hyperedges with higher IDs (they shift down). +/// > - References to all properties associated with the hyperedges with IDs shifted as a result of the removal operation. +/// > +/// > Proceed with caution when maintaining external hyperedge IDs, descriptors, or properties. +void remove_hyperedge_wrn(); + +// --- definitions --- + +/// ### Formal Definition +/// The degree of a vertex in a hypergraph is the total number of hyperedges incident with a vertex. +/// +/// \f[ +/// deg(v) = +/// \begin{cases} +/// \vert\{e \in E : v \in e\}\vert & \text{if } H \text{ is undirected} +/// \\ deg_{in}(v) + deg_{out}(v) & \text{if } H \text{ is BF-directed} +/// \end{cases} +/// \f] +void degree(); + +/// ### Formal Definition +/// The in-degree of a vertex in a *BF-directed* hypergraph is the number of hyperedges forward-incident +/// with the vertex (such that the vertex belongs to the head of the hyperedge). +/// +/// \f[ +/// deg_{in}(v) = \vert\{e \in E : v \in H(e)\}\vert +/// \f] +void in_degree(); + +/// ### Formal Definition +/// The out-degree of a vertex in a *BF-directed* hypergraph is the number of hyperedges backward-incident +/// with the vertex (such that the vertex belongs to the tail of the hyperedge). +/// +/// \f[ +/// deg_{in}(v) = \vert\{e \in E : v \in H(e)\}\vert +/// \f] +void out_degree(); + +} // namespace detail::hypergraph_doc_anchors + } // namespace hgl From af9ec04458c8382db95e917ddb9cdcf699d92ab0 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Tue, 28 Apr 2026 22:39:49 +0200 Subject: [PATCH 09/28] impl tags docs refinement --- include/hgl/hypergraph.hpp | 4 ---- include/hgl/impl/impl_tags.hpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/include/hgl/hypergraph.hpp b/include/hgl/hypergraph.hpp index 37f14b00..0438b7f7 100644 --- a/include/hgl/hypergraph.hpp +++ b/include/hgl/hypergraph.hpp @@ -210,10 +210,6 @@ struct to_impl; /// | HypergraphTraits | The core configuration type specifying the behavior and representation of the hypergraph. | [**c_instantiation_of**](gl_concepts.md#gl-traits-c-instantiation-of) | /// /// ### See Also -/// - @ref hgl::hypergraph_traits "hypergraph_traits" for configuring the underlying properties and tags. -/// - @ref gl::io::options_manip "options_manip" for custom stream formatting options. -/// -/// ### See Also /// - @ref hgl::undirected_hypergraph "undirected_hypergraph" : Convenience alias for undirected hypergraphs. /// - @ref hgl::bf_directed_hypergraph "bf_directed_hypergraph" : Convenience alias for BF-directed hypergraphs. /// - @ref hgl::clone "clone" : Create a deep copy of a hypergraph. diff --git a/include/hgl/impl/impl_tags.hpp b/include/hgl/impl/impl_tags.hpp index e8e74b10..806bdcdc 100644 --- a/include/hgl/impl/impl_tags.hpp +++ b/include/hgl/impl/impl_tags.hpp @@ -19,6 +19,14 @@ namespace hgl::impl { /// @ingroup HGL-Core /// @headerfile hgl/impl/impl_tags.hpp /// @brief Tag struct for the standard incidence list hypergraph implementation. +/// +/// ### Layout Implications +/// The chosen layout significantly impacts memory usage and query performance: +/// +/// - @ref hgl::impl::bidirectional_t "bidirectional_t" (Default): Maintains two internal lists (vertex-to-hyperedges and hyperedge-to-vertices). Provides optimal $O(1)$ degree/size lookups and fast traversals in both directions at the cost of doubled memory consumption. +/// - @ref hgl::impl::vertex_major_t "vertex_major_t": Maintains only a vertex-to-hyperedges list. Highly memory efficient and fast for querying vertex degrees or incident hyperedge sets, but querying hyperedge sizes or incident vertex setss requires expensive full-hypergraph scans. +/// - @ref hgl::impl::hyperedge_major_t "hyperedge_major_t": Maintains only a hyperedge-to-vertices list. Memory efficient and fast for hyperedge-centric queries, but querying vertex degrees or incident hyperedge sets requires full-hypergraph scans. +/// /// @tparam LayoutTag Specifies the memory layout orientation for the underlying data structure. /// @tparam IdType The underlying integer type used for identifiers. template @@ -40,6 +48,14 @@ struct list_t { /// @ingroup HGL-Core /// @headerfile hgl/impl/impl_tags.hpp /// @brief Tag struct for the flattened incidence list hypergraph implementation. +/// +/// ### Layout Implications +/// The chosen layout significantly impacts memory usage and query performance: +/// +/// - @ref hgl::impl::bidirectional_t "bidirectional_t" (Default): Maintains two internal flattened lists (vertex-to-hyperedges and hyperedge-to-vertices). Provides optimal $O(1)$ degree/size lookups and fast traversals in both directions at the cost of doubled memory consumption. +/// - @ref hgl::impl::vertex_major_t "vertex_major_t": Maintains only a vertex-to-hyperedges flattened list. Highly memory efficient and fast for querying vertex degrees or incident hyperedge sets, but querying hyperedge sizes or incident vertex sets requires expensive full-graph scans. +/// - @ref hgl::impl::hyperedge_major_t "hyperedge_major_t": Maintains only a hyperedge-to-vertices flattened list. Memory efficient and fast for hyperedge-centric queries, but querying vertex degrees or incident hyperedge sets requires full-graph scans. +/// /// @tparam LayoutTag Specifies the memory layout orientation for the underlying data structure. /// @tparam IdType The underlying integer type used for identifiers. /// @see @ref gl::flat_jagged_vector "flat_jagged_vector" for the data structure used for the underlying model implementation. @@ -62,6 +78,13 @@ struct flat_list_t { /// @ingroup HGL-Core /// @headerfile hgl/impl/impl_tags.hpp /// @brief Tag struct for the standard incidence matrix hypergraph implementation. +/// +/// ### Layout Implications +/// Matrix implementations strictly require an asymmetric layout tag to define the row and column dimensions of the underlying matrix: +/// +/// - @ref hgl::impl::hyperedge_major_t "hyperedge_major_t" (Default): Stores a \f$\vert E \vert \times \vert V \vert\f$ matrix, where hyperedges are mapped to rows and vertices to columns. Retrieving the vertices incident to a specific hyperedge translates to a fast, cache-friendly contiguous memory read across a single row. +/// - @ref hgl::impl::vertex_major_t "vertex_major_t": Stores a \f$\vert V \vert \times \vert E \vert\f$ matrix, where vertices are mapped to rows and hyperedges to columns. Retrieving the hyperedges incident to a specific vertex translates to a fast, contiguous memory read. +/// /// @tparam LayoutTag Specifies the memory layout orientation for the underlying data structure (must be asymmetric). /// @tparam IdType The underlying integer type used for identifiers. template @@ -83,6 +106,13 @@ struct matrix_t { /// @ingroup HGL-Core /// @headerfile hgl/impl/impl_tags.hpp /// @brief Tag struct for the flattened incidence matrix hypergraph implementation. +/// +/// ### Layout Implications +/// Matrix implementations strictly require an asymmetric layout tag to define the row and column dimensions of the underlying matrix: +/// +/// - @ref hgl::impl::hyperedge_major_t "hyperedge_major_t" (Default): Stores a \f$\vert E \vert \times \vert V \vert\f$ flat matrix, where hyperedges are mapped to rows and vertices to columns. Retrieving the vertices incident to a specific hyperedge translates to a fast, cache-friendly contiguous memory read across a single row. +/// - @ref hgl::impl::vertex_major_t "vertex_major_t": Stores a \f$\vert V \vert \times \vert E \vert\f$ flat matrix, where vertices are mapped to rows and hyperedges to columns. Retrieving the hyperedges incident to a specific vertex translates to a fast, contiguous memory read. +/// /// @tparam LayoutTag Specifies the memory layout orientation for the underlying data structure (must be asymmetric). /// @tparam IdType The underlying integer type used for identifiers. /// @see @ref gl::flat_matrix "flat_matrix" for the data structure used for the underlying model implementation. From f90082d48af835f8b9ed3ada04dfb38a31cc7d94 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Tue, 28 Apr 2026 22:56:28 +0200 Subject: [PATCH 10/28] conversion docs --- include/gl/conversion.hpp | 9 ++- include/hgl/conversion.hpp | 110 ++++++++++++++++++++++++++++++++++++- 2 files changed, 114 insertions(+), 5 deletions(-) diff --git a/include/gl/conversion.hpp b/include/gl/conversion.hpp index a811f669..a2e9e85e 100644 --- a/include/gl/conversion.hpp +++ b/include/gl/conversion.hpp @@ -54,7 +54,7 @@ struct swap_impl_tag>, NewIm }; /// @ingroup GL GL-Traits -/// @brief Alias template for easier usage of the `swap_impl_tag` trait to resolve the swapped type directly. +/// @brief Alias template for easier usage of the @ref gl::traits::swap_impl_tag "swap_impl_tag" trait to resolve the swapped type directly. /// ### See Also: /// - @ref gl::to "to" : For the function that utilizes this trait to perform graph conversions between different implementations. template @@ -162,13 +162,18 @@ struct to_impl { /// @ingroup GL GL-Core /// @headerfile gl/conversion.hpp /// @brief Converts a graph from one implementation model to another. +/// +/// This function efficiently transforms a graph's underlying memory representation (e.g., from a standard adjacency list to a flattened adjacency list) while preserving its exact topology, properties, and identifiers. +/// /// ### Template Parameters /// | Parameter | Description | Constraints | /// | :------------ | :---------- | :---------- | /// | TargetImplTag | The implementation tag of the desired target representation (e.g., `gl::impl::flat_list_t`) | [**c_graph_impl_tag**](gl_concepts.md#gl-traits-c-graph-impl-tag) | /// | Graph | The type of the source graph, which will be automatically deduced from the function argument. | [**c_graph**](gl_concepts.md#gl-traits-c-graph) | +/// /// @param source The graph to convert. After the operation it will be left in a valid, empty state. -/// @return A new graph containing the moved data, structured according to TargetImplTag. +/// @return A new graph containing the moved data, structured according to `TargetImplTag`. +/// /// ### See Also /// - @ref gl::traits::swap_impl_tag "swap_impl_tag" : For the trait used to resolve the target graph type with the swapped implementation tag. template diff --git a/include/hgl/conversion.hpp b/include/hgl/conversion.hpp index 7ce31509..c8719f6b 100644 --- a/include/hgl/conversion.hpp +++ b/include/hgl/conversion.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/conversion.hpp +/// @brief Defines utilities for hypergraph representation model conversion and projection into standard graphs. + #pragma once #include "gl/attributes/force_inline.hpp" @@ -20,10 +23,16 @@ namespace hgl { namespace traits { +/// @ingroup HGL-Traits +/// @brief Utility trait type used to swap the implementation tag of a hypergraph traits or hypergraph type. +/// ### See Also: +/// - @ref hgl::to "to" : For the function that utilizes this trait to perform hypergraph conversions between different implementations. template requires c_hypergraph or c_instantiation_of struct swap_impl_tag; +/// @ingroup HGL-Traits +/// @brief Specialization of @ref hgl::traits::swap_impl_tag "swap_impl_tag" for the @ref hgl::hypergraph_traits "hypergraph_traits" type. template < traits::c_hypergraph_directional_tag Dir, traits::c_properties VP, @@ -34,6 +43,8 @@ struct swap_impl_tag, NewImplTag> { using type = hypergraph_traits; }; +/// @ingroup HGL-Traits +/// @brief Specialization of @ref hgl::traits::swap_impl_tag "swap_impl_tag" for the @ref hgl::hypergraph "hypergraph" type. template < traits::c_hypergraph_directional_tag Dir, traits::c_properties VP, @@ -44,6 +55,10 @@ struct swap_impl_tag>, New using type = hypergraph>; }; +/// @ingroup HGL-Traits +/// @brief Alias template for easier usage of the @ref hl::traits::swap_impl_tag "swap_impl_tag" trait to resolve the swapped type directly. +/// ### See Also: +/// - @ref hgl::to "to" : For the function that utilizes this trait to perform hypergraph conversions between different implementations. template requires c_hypergraph or c_instantiation_of using swap_impl_tag_t = typename swap_impl_tag::type; @@ -201,11 +216,22 @@ struct to_impl, impl::flat_list_t> { } // namespace detail +/// @ingroup HGL-Core /// @brief Converts a hypergraph from one implementation model to another. -/// @tparam TargetImplTag The desired implementation tag (e.g., gl::impl::flat_list_t) -/// @tparam Hypergraph The automatically deduced type of the source hypergraph +/// +/// This function efficiently transforms a hypergraph's underlying memory representation (e.g., from a standard incidence list to a flattened incidence list) while preserving its exact topology, properties, and identifiers. +/// +/// ### Template Parameters +/// | Parameter | Description | Constraint | +/// | :-------- | :---------- | :--------- | +/// | TargetImplTag | The implementation tag of the desired target representation (e.g., `hgl::impl::flat_list_t`). | [**c_hypergraph_impl_tag**](hgl_concepts.md#hgl-traits-c-hypergraph-impl-tag) | +/// | Hypergraph | The type of the source hypergraph, which will be automatically deduced from the function argument. | [**c_hypergraph**](hgl_concepts.md#hgl-traits-c-hypergraph) | +/// /// @param source The hypergraph to convert. After the operation it will be left in a valid, empty state. -/// @return A new hypergraph containing the moved data, structured according to TargetImplTag. +/// @return A new hypergraph containing the moved data, structured according to `TargetImplTag`. +/// +/// ### See Also +/// - @ref hgl::traits::swap_impl_tag "swap_impl_tag" : For the trait used to resolve the target hypergraph type with the swapped implementation tag. template [[nodiscard]] auto to(Hypergraph&& source) { using source_traits = typename Hypergraph::traits_type; @@ -229,6 +255,23 @@ template [!NOTE] Performance for Flat List Graphs +/// > +/// > If the requested target graph `G` satisfies [**c_flat_list_graph**](gl_concepts.md#gl-traits-c-flat-list-graph), +/// > an optimized overload is automatically selected. It internally constructs a standard adjacency list graph +/// > first, and then utilizes the @ref gl::to conversion to flatten it. This is significantly faster than +/// > inserting edges one-by-one into a flat representation. +/// +/// @tparam G The target standard graph type to construct. Must satisfy [**c_undirected_graph**](gl_concepts.md#gl-traits-c-undirected-graph). +/// @param h The source undirected hypergraph. +/// @return A standard undirected graph representing the projection. template [[nodiscard]] G projection(const traits::c_undirected_hypergraph auto& h) { using edge_vertices = homogeneous_pair; @@ -261,6 +304,23 @@ requires std::same_as(projection(h)); } +/// @ingroup HGL-Core +/// @brief Computes the projection of a *BF-directed* hypergraph. +/// +/// Projects the hypergraph onto a standard directed graph. For each hyperedge, directed edges are created +/// from every vertex in the hyperedge's tail (source) to every vertex in its head (destination). +/// Duplicate edges generated by multiple hyperedges overlapping on the same vertices are collapsed into a single edge. +/// +/// > [!NOTE] Performance for Flat List Graphs +/// > +/// > If the requested target graph `G` satisfies [**c_flat_list_graph**](gl_concepts.md#gl-traits-c-flat-list-graph), +/// > an optimized overload is automatically selected. It internally constructs a standard adjacency list graph +/// > first, and then utilizes the @ref gl::to "to" conversion to flatten it. This is significantly faster than +/// > inserting edges one-by-one into a flat representation. +/// +/// @tparam G The target standard graph type to construct. Must satisfy [**c_directed_graph**](gl_concepts.md#gl-traits-c-directed-graph). +/// @param h The source bf-directed hypergraph. +/// @return A standard directed graph representing the projection. template [[nodiscard]] G projection(const traits::c_bf_directed_hypergraph auto& h) { using edge_vertices = homogeneous_pair; @@ -292,6 +352,28 @@ requires std::same_as(projection(h)); } +/// @ingroup HGL-Core +/// @brief Computes the bipartite incidence graph representation of an *undirected* hypergraph. +/// +/// Converts the hypergraph into a standard bipartite graph where both the original vertices and the +/// original hyperedges are represented as standard graph vertices. Undirected edges are created between +/// a vertex node and a hyperedge node if they are incident. +/// +/// > [!NOTE] ID Shifting +/// > +/// > To ensure uniqueness in the resulting graph, the IDs of the hyperedge nodes are shifted by `h.n_vertices()`. +/// > For example, hyperedge ID `0` becomes vertex ID `h.n_vertices() + 0` in the resulting graph. +/// +/// > [!NOTE] Performance for Flat List Graphs +/// > +/// > If the requested target graph `G` satisfies [**c_flat_list_graph**](gl_concepts.md#gl-traits-c-flat-list-graph), +/// > an optimized overload is automatically selected. It internally constructs a standard adjacency list graph +/// > first, and then utilizes the @ref gl::to conversion to flatten it. This is significantly faster than +/// > inserting edges one-by-one into a flat representation. +/// +/// @tparam G The target standard graph type to construct. Must satisfy [**c_undirected_graph**](gl_concepts.md#gl-traits-c-undirected-graph). +/// @param h The source undirected hypergraph. +/// @return A standard undirected bipartite graph representing the incidence structure. template [[nodiscard]] G incidence_graph(const traits::c_undirected_hypergraph auto& h) { using g_id_type = typename G::id_type; @@ -319,6 +401,28 @@ requires std::same_as(incidence_graph(h)); } +/// @ingroup HGL-Core +/// @brief Computes the bipartite incidence graph representation of a *BF-directed* hypergraph. +/// +/// Converts the hypergraph into a standard bipartite directed graph. Directed edges are created +/// from original *tail* vertex nodes to the hyperedge nodes, and from the hyperedge nodes to the +/// original *head* vertex nodes. +/// +/// > [!NOTE] ID Shifting +/// > +/// > To ensure uniqueness in the resulting graph, the IDs of the hyperedge nodes are shifted by `h.n_vertices()`. +/// > For example, hyperedge ID `0` becomes vertex ID `h.n_vertices() + 0` in the resulting graph. +/// +/// > [!NOTE] Performance for Flat List Graphs +/// > +/// > If the requested target graph `G` satisfies [**c_flat_list_graph**](gl_concepts.md#gl-traits-c-flat-list-graph), +/// > an optimized overload is automatically selected. It internally constructs a standard adjacency list graph +/// > first, and then utilizes the @ref gl::to conversion to flatten it. This is significantly faster than +/// > inserting edges one-by-one into a flat representation. +/// +/// @tparam G The target standard graph type to construct. Must satisfy [**c_directed_graph**](gl_concepts.md#gl-traits-c-directed-graph). +/// @param h The source bf-directed hypergraph. +/// @return A standard directed bipartite graph representing the incidence structure. template [[nodiscard]] G incidence_graph(const traits::c_bf_directed_hypergraph auto& h) { using g_id_type = typename G::id_type; From 65b7311ec45604dedb578a7a9c1860be595341d7 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Tue, 28 Apr 2026 23:37:36 +0200 Subject: [PATCH 11/28] io docs --- include/gl/io/graph_fio.hpp | 8 +-- include/hgl/io.hpp | 3 + include/hgl/io/core.hpp | 115 ++++++++++++++++++++++++++---- include/hgl/io/hypergraph_fio.hpp | 40 ++++++++++- include/hgl/traits.hpp | 4 ++ include/hgl/util.hpp | 60 ++++++++++------ 6 files changed, 188 insertions(+), 42 deletions(-) diff --git a/include/gl/io/graph_fio.hpp b/include/gl/io/graph_fio.hpp index 74805e12..16ff03cc 100644 --- a/include/gl/io/graph_fio.hpp +++ b/include/gl/io/graph_fio.hpp @@ -118,7 +118,7 @@ requires(std::same_as) /// Saves the graph topology and optionally its properties using the Graph Specification Format (GSF). /// The function strictly respects the @ref gl::io::write "write" and @ref gl::io::append "append" safety guards. /// -/// @tparam GraphType The underlying type of the graph being saved. +/// @tparam GraphType The concrete type of the graph being saved. Must satisfy [**c_graph**](gl_concepts.md#hgl-traits-c-graph). /// @tparam Mode The save behavior tag (@ref gl::io::write "write" or @ref gl::io::append "append"). Defaults to `write`. /// @param graph The graph instance to serialize. /// @param path The filesystem path where the graph will be saved. Defaults to `"graph.gsf"`. @@ -146,14 +146,14 @@ void save( /// /// Instantiates a new graph populated with the topology and properties read from the target GSF file. /// -/// @tparam GraphType The target graph type to construct. Must match the directional nature of the saved file. -/// @param path The filesystem path from which to load the graph. Defaults to `"graph.gsf"`. +/// @tparam GraphType The target graph type to construct. Must match the directional nature of the saved graph. +/// @param path The filesystem path from which to load the graph. /// @return A newly constructed graph populated with the file's data. /// /// @throws std::filesystem::filesystem_error If the file does not exist or is not a standard file. /// @throws std::ios_base::failure If the file cannot be opened or if the GSF directional discriminator mismatches `GraphType`. template -[[nodiscard]] GraphType load(const std::filesystem::path& path = "graph.gsf") { +[[nodiscard]] GraphType load(const std::filesystem::path& path) { std::ifstream file = detail::open_infile(path); file >> spec_fmt; diff --git a/include/hgl/io.hpp b/include/hgl/io.hpp index 5dd9b753..2a5dec58 100644 --- a/include/hgl/io.hpp +++ b/include/hgl/io.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/io.hpp +/// @brief Includes all I/O-related headers for hypergraph file operations. + #pragma once #include "hgl/io/core.hpp" diff --git a/include/hgl/io/core.hpp b/include/hgl/io/core.hpp index 20a4aa39..ebcce06b 100644 --- a/include/hgl/io/core.hpp +++ b/include/hgl/io/core.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/io/core.hpp +/// @brief Core I/O utilities, stream manipulators, and range formatters for hypergraphs. + #pragma once #include "gl/io/options.hpp" @@ -13,16 +16,56 @@ namespace hgl::io { +/// @ingroup HGL-IO +/// @copybrief gl::io::range_formatter +/// @see gl::io::range_formatter +template +using range_formatter = gl::io::range_formatter; + +/// @ingroup HGL-IO +/// @copybrief gl::io::implicit_range +/// @see gl::io::implicit_range using gl::io::implicit_range; -using gl::io::implicit_range_formatter; -using gl::io::multiline_set_formatter; -using gl::io::range_formatter; + +/// @ingroup HGL-IO +/// @copybrief gl::io::implicit_range_formatter +/// @see gl::io::implicit_range_formatter +template +using implicit_range_formatter = gl::io::implicit_range_formatter; + +/// @ingroup HGL-IO +/// @copybrief gl::io::set_formatter +/// @see gl::io::set_formatter using gl::io::set_formatter; +/// @ingroup HGL-IO +/// @copybrief gl::io::multiline_set_formatter +/// @see gl::io::multiline_set_formatter +using gl::io::multiline_set_formatter; + +/// @ingroup HGL-IO +/// @copybrief gl::io::are_options_set +/// @see gl::io::are_options_set using gl::io::are_options_set; + +/// @ingroup HGL-IO +/// @copybrief gl::io::clear_options +/// @see gl::io::clear_options using gl::io::clear_options; + +/// @ingroup HGL-IO +/// @copybrief gl::io::is_option_set +/// @see gl::io::is_option_set using gl::io::is_option_set; -using gl::io::options_manip; + +/// @ingroup HGL-IO +/// @copybrief gl::io::options_manip +/// @see gl::io::options_manip +using options_manip = gl::io::options_manip; + +/// @ingroup HGL-IO +/// @copybrief gl::io::set_options +/// @see gl::io::set_options using gl::io::set_options; namespace detail { @@ -32,21 +75,65 @@ using gl::io::detail::option_bit; } // namespace detail -using gl::io::concise; -using gl::io::spec_fmt; -using gl::io::verbose; +/// @ingroup HGL-IO +/// @brief @ref hgl::io::options_manip "Stream manipulator" to enable concise hypergraph formatting. +/// +/// Clears all layout-specific flags to default back to a compact representation. +/// +/// @hideinitializer +inline constexpr options_manip concise = gl::io::concise; + +/// @ingroup HGL-IO +/// @brief @ref hgl::io::options_manip "Stream manipulator" to enable verbose hypergraph formatting. +/// +/// Modifies the stream state to output detailed structural information. +/// +/// @hideinitializer +inline constexpr options_manip spec_fmt = gl::io::spec_fmt; -using gl::io::with_vertex_properties; -using gl::io::without_vertex_properties; +/// @ingroup HGL-IO +/// @brief @ref hgl::io::options_manip "Stream manipulator" to enable the Hypergraph Specification Format (HGSF). +/// +/// Modifies the stream to output or expect data matching the precise internal parsing format used for serialization and deserialization. +/// +/// @hideinitializer +inline constexpr options_manip verbose = gl::io::verbose; -inline const options_manip with_hyperedge_properties = +/// @ingroup HGL-IO +/// @brief @ref hgl::io::options_manip "Stream manipulator" to enable the processing of vertex properties. +/// @hideinitializer +inline constexpr options_manip with_vertex_properties = gl::io::with_vertex_properties; + +/// @ingroup HGL-IO +/// @brief @ref hgl::io::options_manip "Stream manipulator" to disable the processing of vertex properties. +/// @hideinitializer +inline constexpr options_manip without_vertex_properties = gl::io::without_vertex_properties; + +/// @ingroup HGL-IO +/// @brief @ref hgl::io::options_manip "Stream manipulator" to enable the processing of hyperedge properties. +/// @hideinitializer +inline constexpr options_manip with_hyperedge_properties = set_options(detail::option_bit::with_connection_properties); -inline const options_manip without_hyperedge_properties = + +/// @ingroup HGL-IO +/// @brief @ref hgl::io::options_manip "Stream manipulator" to disable the processing of hyperedge properties. +/// @hideinitializer +inline constexpr options_manip without_hyperedge_properties = clear_options(detail::option_bit::with_connection_properties); -using gl::io::with_properties; -using gl::io::without_properties; +/// @ingroup HGL-IO +/// @brief @ref hgl::io::options_manip "Stream manipulator" to enable the processing of both vertex and hyperedge properties simultaneously. +/// @hideinitializer +inline constexpr options_manip with_properties = gl::io::with_properties; + +/// @ingroup HGL-IO +/// @brief @ref hgl::io::options_manip "Stream manipulator" to disable the processing of both vertex and hyperedge properties simultaneously. +/// @hideinitializer +inline constexpr options_manip without_properties = gl::io::without_properties; -using gl::io::default_options; +/// @ingroup HGL-IO +/// @brief @ref hgl::io::options_manip "Stream manipulator" to reset all custom graph formatting flags back to their default states. +/// @hideinitializer +inline constexpr options_manip default_options = gl::io::default_options; } // namespace hgl::io diff --git a/include/hgl/io/hypergraph_fio.hpp b/include/hgl/io/hypergraph_fio.hpp index 44558c4e..4ca99426 100644 --- a/include/hgl/io/hypergraph_fio.hpp +++ b/include/hgl/io/hypergraph_fio.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/io/hypergraph_fio.hpp +/// @brief File I/O utilities for safely saving and loading hypergraphs to and from files. + #pragma once #include "gl/io/graph_fio.hpp" @@ -15,8 +18,15 @@ namespace hgl::io { -using gl::io::append; -using gl::io::write; +/// @ingroup HGL-IO +/// @copybrief gl::io::append +/// @copydetails gl::io::append +using append = gl::io::append; + +/// @ingroup HGL-IO +/// @copybrief gl::io::write +/// @copydetails gl::io::write +using write = gl::io::write; namespace detail { @@ -25,6 +35,19 @@ using gl::io::detail::open_outfile; } // namespace detail +/// @ingroup HGL-IO +/// @brief Serializes and saves a hypergraph to a file. +/// +/// Writes the topology and optionally the properties of the hypergraph to the specified file using the Hypergraph Specification Format (HGSF). By default, +/// The function strictly respects the @ref hgl::io::write "write" and @ref hgl::io::append "append" safety guards. +/// +/// @tparam HypergraphType The concrete type of the hypergraph being saved. Must satisfy [**c_hypergraph**](hgl_concepts.md#hgl-traits-c-hypergraph). +/// @tparam Mode The file access mode (e.g., @ref hgl::io::write "write" or @ref hgl::io::append "append"). +/// @param hypergraph The hypergraph instance to serialize. +/// @param path The filesystem path where the hypergraph will be saved. Defaults to `"hypergraph.hgsf"`. +/// @param options An optional initializer list of stream manipulators to configure the output (e.g., `{hgl::io::with_properties}`). +/// @throws std::filesystem::filesystem_error If file safety checks fail (e.g., overwriting an existing file in `write` mode). +/// @throws std::ios_base::failure If the underlying file stream cannot be opened. template void save( const HypergraphType& hypergraph, @@ -40,8 +63,19 @@ void save( file << hypergraph; } +/// @ingroup HGL-IO +/// @brief Deserializes and loads a hypergraph from a file. +/// +/// Instantiates a new hypergraph populated with the topology and properties read from the target HGSF file. +/// +/// @tparam HypergraphType The target hypergraph type to construct. Must match the directional nature of the saved hypergraph. +/// @param path The filesystem path from which to load the hypergraph. +/// @return A newly constructed hypergraph populated with the file's data. +/// +/// @throws std::filesystem::filesystem_error If the file does not exist or is not a regular file. +/// @throws std::ios_base::failure If the file cannot be opened or if the HGSF directional discriminator mismatches `HypergraphType`. template -[[nodiscard]] HypergraphType load(const std::filesystem::path& path = "hypergraph.hgsf") { +[[nodiscard]] HypergraphType load(const std::filesystem::path& path) { std::ifstream file = detail::open_infile(path); file >> gl::io::spec_fmt; diff --git a/include/hgl/traits.hpp b/include/hgl/traits.hpp index 2698ce44..ee0ba831 100644 --- a/include/hgl/traits.hpp +++ b/include/hgl/traits.hpp @@ -19,6 +19,10 @@ namespace hgl { /// and metaprogramming utilities from `gl::traits`. Because hypergraphs share the same underlying /// implementation design and mechanisms as standard graphs, they seamlessly reuse the same /// fundamental C++20 concepts. +/// +/// > [!NOTE] +/// > +/// > To get a detailed overview of these shared utilities, please refer to the GL module's @ref GL-Traits documentation page. namespace traits { using namespace gl::traits; diff --git a/include/hgl/util.hpp b/include/hgl/util.hpp index 17bddef3..766e2a39 100644 --- a/include/hgl/util.hpp +++ b/include/hgl/util.hpp @@ -9,36 +9,54 @@ #include "gl/util/ranges.hpp" -namespace hgl::util { +namespace hgl { /// @ingroup HGL-Util -/// @brief @copybrief gl::util::range_size -/// @see gl::util::range_size -using gl::util::range_size; - -/// @ingroup HGL-Util -/// @brief @copybrief gl::util::is_constant -/// @see gl::util::is_constant -using gl::util::is_constant; +/// @brief General utilities, ranges, and helpers for the HGL module (originating in the GL module). +/// +/// This namespace pulls in practical, domain-agnostic C++ **range** utilities from `gl::util`. +/// Because hypergraphs share the same underlying memory models and algorithmic requirements as +/// standard graphs, they seamlessly reuse the same fundamental C++20 range utilities and helpers. +/// +/// > [!NOTE] +/// > +/// > To get a detailed overview of these shared utilities, please refer to the GL module's @ref GL-Util documentation page. +namespace util { -/// @ingroup HGL-Util -/// @brief @copybrief gl::util::all_equal -/// @see gl::util::all_equal using gl::util::all_equal; +using gl::util::is_constant; +using gl::util::range_size; /// @ingroup HGL-Util /// @brief @copybrief gl::util::concat_view -/// @see gl::util::concat_view -using gl::util::concat_view; +/// @see gl::util::concat_view for the full type definition +template +using concat_view = gl::util::concat_view; /// @ingroup HGL-Util /// @brief @copybrief gl::util::concat_fn -/// @see gl::util::concat_fn -using gl::util::concat_fn; +/// @see gl::util::concat_fn for the full type definition +using concat_fn = gl::util::concat_fn; /// @ingroup HGL-Util -/// @brief @copybrief gl::util::concat -/// @see gl::util::concat -using gl::util::concat; - -} // namespace hgl::util +/// @brief Concatenates two viewable ranges into a `concat_view`. +/// +/// ### Example usage +/// ```cpp +/// std::vector v1 = {1, 2, 3}; +/// std::vector v2 = {4, 5, 6}; +/// auto concatenated = gl::util::concat(v1, v2); +/// for (int x : concatenated) +/// std::cout << x << " "; // Output: 1 2 3 4 5 6 +/// ``` +/// +/// @param r1 First range to concatenate. +/// @param r2 Second range to concatenate. +/// @todo Replace with `std::views::concat` (C++26). +/// ### See Also +/// - @ref hgl::util::concat_view "concat_view": The view type that represents the concatenation of two ranges. +/// - @ref hgl::util::concat_fn "concat_fn": A helper compile-time constant function object for creating `concat_view` instances. +inline constexpr concat_fn concat{}; + +} // namespace util +} // namespace hgl From 1490421adbd35bd6911e28917c9f55b8df9d17da Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 00:20:05 +0200 Subject: [PATCH 12/28] alg core doc --- include/hgl/algorithm/core.hpp | 111 ++++++++++++++++++++++++++++++--- include/hgl/constants.hpp | 14 +++-- 2 files changed, 109 insertions(+), 16 deletions(-) diff --git a/include/hgl/algorithm/core.hpp b/include/hgl/algorithm/core.hpp index 52a34e29..2bb8aa1d 100644 --- a/include/hgl/algorithm/core.hpp +++ b/include/hgl/algorithm/core.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/algorithm/core.hpp +/// @brief Core data structures and types used to control and track hypergraph algorithm execution. + #pragma once #include "gl/algorithm/core.hpp" @@ -20,43 +23,97 @@ namespace algorithm { // -- GL core --- -using gl::algorithm::empty_callback; +/// @ingroup HGL-Algorithm +/// @copybrief gl::algorithm::empty_callback +/// @see gl::algorithm::empty_callback for a more detailed description. +using empty_callback = gl::algorithm::empty_callback; -using gl::algorithm::decision; +/// @ingroup HGL-Algorithm +/// @copybrief gl::algorithm::decision +/// @see gl::algorithm::decision for the full type definition +using decision = gl::algorithm::decision; -using gl::algorithm::result_discriminator; +/// @ingroup HGL-Algorithm +/// @copybrief gl::algorithm::result_discriminator +/// @see gl::algorithm::result_discriminator for the full type definition. +using result_discriminator = gl::algorithm::result_discriminator; using enum result_discriminator; -using gl::algorithm::non_void_result_type; -using gl::algorithm::result_type; - -using gl::algorithm::no_root; -using gl::algorithm::no_root_t; -using gl::algorithm::no_root_v; +/// @ingroup HGL-Algorithm +/// @copybrief gl::algorithm::result_type +/// @see gl::algorithm::result_type for the full type definition. +template +using result_type = gl::algorithm::result_type; + +/// @ingroup HGL-Algorithm +/// @copybrief gl::algorithm::non_void_result_type +/// @see gl::algorithm::non_void_result_type for the full type definition. +template +using non_void_result_type = gl::algorithm::non_void_result_type; + +/// @ingroup HGL-Algorithm +/// @copybrief gl::algorithm::no_root_v +/// @see gl::algorithm::no_root_v +template +inline constexpr IdType no_root_v = gl::algorithm::no_root_v; + +/// @ingroup HGL-Algorithm +/// @copybrief gl::algorithm::no_root_t +/// @see gl::algorithm::no_root_t +using no_root_t = gl::algorithm::no_root_t; + +/// @ingroup HGL-Algorithm +/// @copybrief gl::algorithm::no_root +/// @see gl::algorithm::no_root +inline constexpr no_root_t no_root = gl::algorithm::no_root; // --- traversal types --- +/// @ingroup HGL-Algorithm +/// @brief Represents an active node in a search container (e.g., a BFS queue or DFS stack) for hypergraph traversals. +/// @tparam H The type of the hypergraph being searched. Must satisfy [**c_hypergraph**](hgl_concepts.md#hgl-traits-c-hypergraph). template struct search_node { + /// @brief The identifier type of the hypergraph elements. using id_type = typename H::id_type; + /// @brief Default constructor creates an invalid node. search_node() = default; + /// @brief Constructs a *root* search node (predecessor is itself, no incident hyperedge). + /// @param vertex_id The ID of the root vertex. search_node(id_type vertex_id) : vertex_id(vertex_id), pred_id(vertex_id), hyperedge_id(invalid_id) {} + /// @brief Constructs a search node with an explicit predecessor vertex and the connecting hyperedge. + /// @param vertex_id The ID of the currently reached vertex. + /// @param pred_id The ID of the predecessor vertex from which this vertex was reached. + /// @param hyperedge_id The ID of the hyperedge connecting the predecessor to this vertex. search_node(id_type vertex_id, id_type pred_id, id_type hyperedge_id) : vertex_id(vertex_id), pred_id(pred_id), hyperedge_id(hyperedge_id) {} + /// @brief Checks if this node is the root of a search tree. + /// @return `true` if the node is valid and its predecessor is itself, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool is_root() const noexcept { return this->vertex_id != invalid_id and this->vertex_id == this->pred_id; } + /// @brief The ID of the current vertex. id_type vertex_id = invalid_id; + /// @brief The ID of the predecessor from which this vertex was reached. id_type pred_id = invalid_id; + /// @brief The ID of the hyperedge via which this vertex was reached from the predecessor. id_type hyperedge_id = invalid_id; }; +/// @ingroup HGL-Algorithm +/// @brief A flat, index-mapped representation of a hypergraph search tree. +/// +/// The $i$-th element corresponds to the vertex with `id == i`. The tree topology is formed implicitly, +/// as each @ref hgl::algorithm::search_node "search_node" stores the ID of its predecessor and the +/// connecting hyperedge, enabling $O(1)$ lookups and and $O(\vert V \vert)$ path reconstruction. +/// +/// @tparam H The type of the hypergraph being searched. template using search_tree = std::vector>; @@ -64,6 +121,9 @@ using search_tree = std::vector>; namespace traits { +/// @ingroup HGL-Traits +/// @brief Validates if a type is a valid hypergraph search tree (a random access range of @ref hgl::algorithm::search_node "search_node"s). +/// @tparam T The type to evaluate against the concept. template concept c_search_tree = c_random_access_range @@ -75,39 +135,70 @@ namespace algorithm { // --- generic algorithm traits --- -enum class traversal_direction : bool { forward, backward }; +/// @ingroup HGL-Algorithm +/// @brief Specifies the direction of traversal for *BF-directed* hypergraphs. +/// +/// > [!IMPORTANT] API Simplicity +/// > +/// > To ensure API simplicity, the `traversal_direction` is used for undirected hypergraphs as well by +/// > the generic traversal templates. However, due to the structural nature of undirected hypergraphs, +/// > both direction values implicitly yield the exact same traversal pattern for undirected hypergraphs. +enum class traversal_direction : bool { + forward, ///< Traverse following the forward star (tail to head). + backward ///< Traverse following the backward star (head to tail). +}; +/// @ingroup HGL-Algorithm +/// @brief Policy defining how to extract incident hyperedges and target vertices during traversal. +/// +/// This template is specialized based on the hypergraph's directionality to route standard +/// traversal algorithms over the correct incidence structures (e.g., following tails to heads). +/// +/// @tparam H The type of the hypergraph. +/// @tparam Dir The direction of traversal. template struct traversal_policy; +/// @ingroup HGL-Algorithm +/// @brief Traversal policy specialization for undirected hypergraphs. template struct traversal_policy { + /// @brief Retrieves the hyperedges incident to the given vertex. static auto target_hyperedges(const H& h, typename H::id_type v_id) { return h.incident_hyperedge_ids(v_id); } + /// @brief Retrieves the vertices incident to the given hyperedge. static auto target_vertices(const H& h, typename H::id_type he_id) { return h.incident_vertex_ids(he_id); } }; +/// @ingroup HGL-Algorithm +/// @brief Traversal policy specialization for forward searches on BF-directed hypergraphs. template struct traversal_policy { + /// @brief Retrieves the hyperedges originating from the given vertex (forward star). static auto target_hyperedges(const H& h, typename H::id_type v_id) { return h.out_hyperedge_ids(v_id); // forward star } + /// @brief Retrieves the vertices targeted by the given hyperedge (head nodes). static auto target_vertices(const H& h, typename H::id_type he_id) { return h.head_ids(he_id); } }; +/// @ingroup HGL-Algorithm +/// @brief Traversal policy specialization for backward searches on BF-directed hypergraphs. template struct traversal_policy { + /// @brief Retrieves the hyperedges entering the given vertex (backward star). static auto target_hyperedges(const H& h, typename H::id_type v_id) { return h.in_hyperedge_ids(v_id); // backward star } + /// @brief Retrieves the vertices originating the given hyperedge (tail nodes). static auto target_vertices(const H& h, typename H::id_type he_id) { return h.tail_ids(he_id); } diff --git a/include/hgl/constants.hpp b/include/hgl/constants.hpp index e58826ac..e2a7dbb1 100644 --- a/include/hgl/constants.hpp +++ b/include/hgl/constants.hpp @@ -15,31 +15,33 @@ namespace hgl { /// @ingroup HGL-Core /// @brief A constant representing the initial ID value of 0 for hypergraph elements. /// @see gl::initial_id_v -using gl::initial_id_v; +template +inline constexpr IdType initial_id_v = gl::initial_id_v; /// @ingroup HGL-Core /// @brief A helper type that can be implicitly converted to the initial ID value of 0 for any valid ID type. /// @see gl::initial_id_t -using gl::initial_id_t; +using initial_id_t = gl::initial_id_t; /// @ingroup HGL-Core /// @brief An `initial_id_t` tag constant that can be used to represent the initial ID value of 0 for hypergraph elements in a type-safe manner. /// @see gl::initial_id -using gl::initial_id; +inline constexpr initial_id_t initial_id = gl::initial_id; /// @ingroup HGL-Core /// @brief A constant representing the invalid ID value for hypergraph elements, defined as the maximum value of the specified ID type. /// @see gl::invalid_id_v -using gl::invalid_id_v; +template +inline constexpr IdType invalid_id_v = gl::invalid_id_v; /// @ingroup HGL-Core /// @brief A helper type that can be implicitly converted to the invalid ID value for any valid ID type. /// @see gl::invalid_id_t -using gl::invalid_id_t; +using invalid_id_t = gl::invalid_id_t; /// @ingroup HGL-Core /// @brief An `invalid_id_t` tag constant that can be used to represent the invalid ID value for hypergraph elements in a type-safe manner. /// @see gl::invalid_id -using gl::invalid_id; +inline constexpr invalid_id_t invalid_id = gl::invalid_id; } // namespace hgl From 7f355d9a08703ce9b376a340b744cdd833c9687a Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 01:14:09 +0200 Subject: [PATCH 13/28] init_range -> init_node_range + bfs tmpl doc --- docs/gl/quick_start.md | 6 +- include/gl/algorithm/templates/bfs.hpp | 2 +- include/gl/algorithm/templates/dfs.hpp | 2 +- include/gl/algorithm/templates/pfs.hpp | 2 +- include/gl/algorithm/topology/coloring.hpp | 2 +- .../traversal/breadth_first_search.hpp | 4 +- .../traversal/depth_first_search.hpp | 4 +- include/gl/algorithm/util.hpp | 5 +- include/hgl/algorithm/templates/bfs.hpp | 78 ++++++++++++++++++- .../traversal/breadth_first_search.hpp | 4 +- .../traversal/depth_first_search.hpp | 4 +- include/hgl/algorithm/util.hpp | 2 +- 12 files changed, 94 insertions(+), 21 deletions(-) diff --git a/docs/gl/quick_start.md b/docs/gl/quick_start.md index c09cb505..2394586c 100644 --- a/docs/gl/quick_start.md +++ b/docs/gl/quick_start.md @@ -42,10 +42,12 @@ int main() { return 1; } - auto path_to_target = gl::algorithm::reconstruct_path(paths.predecessors, target_id); // (7)! + auto path_to_target + = gl::algorithm::reconstruct_path(paths.predecessors, target_id); // (7)! std::cout << "Shortest path distance to vertex " << target_id << ": " << paths.distances[target_id] << "\nPath: " - << gl::io::range_formatter(path_to_target, " -> ", "", "") << '\n'; // (8)! + << gl::io::range_formatter(path_to_target, " -> ", "", "") // (8)! + << '\n'; return 0; } diff --git a/include/gl/algorithm/templates/bfs.hpp b/include/gl/algorithm/templates/bfs.hpp index 197493a8..a87e8b83 100644 --- a/include/gl/algorithm/templates/bfs.hpp +++ b/include/gl/algorithm/templates/bfs.hpp @@ -29,7 +29,7 @@ namespace gl::algorithm { /// /// bool completed = gl::algorithm::bfs( /// graph, -/// gl::algorithm::init_range(start_id), // (2)! +/// gl::algorithm::init_node_range(start_id), // (2)! /// gl::algorithm::default_visit_vertex_predicate(visited), // (3)! /// [&](auto v, auto p) { // (4)! /// std::cout << "Visited vertex " << v << '\n'; diff --git a/include/gl/algorithm/templates/dfs.hpp b/include/gl/algorithm/templates/dfs.hpp index f064973c..b9bf8db2 100644 --- a/include/gl/algorithm/templates/dfs.hpp +++ b/include/gl/algorithm/templates/dfs.hpp @@ -28,7 +28,7 @@ namespace gl::algorithm { /// /// bool completed = gl::algorithm::dfs( /// graph, -/// gl::algorithm::init_range(start_id), // (2)! +/// gl::algorithm::init_node_range(start_id), // (2)! /// gl::algorithm::default_visit_vertex_predicate(visited), // (3)! /// [&](auto v, auto p) { // (4)! /// std::cout << "Visited vertex " << v << '\n'; diff --git a/include/gl/algorithm/templates/pfs.hpp b/include/gl/algorithm/templates/pfs.hpp index 668ca899..717befab 100644 --- a/include/gl/algorithm/templates/pfs.hpp +++ b/include/gl/algorithm/templates/pfs.hpp @@ -34,7 +34,7 @@ namespace gl::algorithm { /// [](const auto& lhs, const auto& rhs) { // (2)! /// return lhs.vertex_id > rhs.vertex_id; /// }, -/// gl::algorithm::init_range(start_id), // (3)! +/// gl::algorithm::init_node_range(start_id), // (3)! /// gl::algorithm::default_visit_vertex_predicate(visited // (4)! /// [&](auto v, auto p) { // (5)! /// std::cout << "Priority visited vertex " << v << '\n'; diff --git a/include/gl/algorithm/topology/coloring.hpp b/include/gl/algorithm/topology/coloring.hpp index 5ae622e2..7b7cd49c 100644 --- a/include/gl/algorithm/topology/coloring.hpp +++ b/include/gl/algorithm/topology/coloring.hpp @@ -79,7 +79,7 @@ template < const bool is_bipartite = bfs( graph, - init_range(root_id), + init_node_range(root_id), empty_callback{}, // visit predicate empty_callback{}, // visit callback [&coloring](typename G::id_type vertex_id, const edge_type& in_edge) diff --git a/include/gl/algorithm/traversal/breadth_first_search.hpp b/include/gl/algorithm/traversal/breadth_first_search.hpp index 0293cbc5..f4ed9ad0 100644 --- a/include/gl/algorithm/traversal/breadth_first_search.hpp +++ b/include/gl/algorithm/traversal/breadth_first_search.hpp @@ -87,7 +87,7 @@ result_type> breadth_first_search( if (root_vertex_id != no_root) { bfs( graph, - init_range(root_vertex_id), + init_node_range(root_vertex_id), default_visit_vertex_predicate(visited), default_visit_callback(visited, pred_map), default_enqueue_node_predicate(visited), @@ -99,7 +99,7 @@ result_type> breadth_first_search( for (const auto root_id : graph.vertex_ids()) bfs( graph, - init_range(root_id), + init_node_range(root_id), default_visit_vertex_predicate(visited), default_visit_callback(visited, pred_map), default_enqueue_node_predicate(visited), diff --git a/include/gl/algorithm/traversal/depth_first_search.hpp b/include/gl/algorithm/traversal/depth_first_search.hpp index 12ad1058..7d1c06cd 100644 --- a/include/gl/algorithm/traversal/depth_first_search.hpp +++ b/include/gl/algorithm/traversal/depth_first_search.hpp @@ -95,7 +95,7 @@ result_type> depth_first_search( if (root_vertex_id != no_root) { dfs( graph, - init_range(root_vertex_id), + init_node_range(root_vertex_id), default_visit_vertex_predicate(visited), default_visit_callback(visited, pred_map), default_enqueue_node_predicate(visited), @@ -107,7 +107,7 @@ result_type> depth_first_search( for (const auto root_id : graph.vertex_ids()) dfs( graph, - init_range(root_id), + init_node_range(root_id), default_visit_vertex_predicate(visited), default_visit_callback(visited, pred_map), default_enqueue_node_predicate(visited), diff --git a/include/gl/algorithm/util.hpp b/include/gl/algorithm/util.hpp index 78e389b1..cd22042d 100644 --- a/include/gl/algorithm/util.hpp +++ b/include/gl/algorithm/util.hpp @@ -45,7 +45,7 @@ template } /// @ingroup GL GL-Algorithm -/// @brief Initializes a search container (queue or stack) with the starting root vertex. +/// @brief Initializes a search container with the starting root vertex. /// @tparam G The type of the graph. /// @tparam InitRangeType The underlying container type for the container. /// @param root_vertex_id The ID of the starting vertex. @@ -53,7 +53,8 @@ template template < traits::c_graph G, traits::c_forward_range_of> InitRangeType = std::vector>> -[[nodiscard]] gl_attr_force_inline InitRangeType init_range(typename G::id_type root_vertex_id) { +[[nodiscard]] gl_attr_force_inline InitRangeType init_node_range(typename G::id_type root_vertex_id +) { return InitRangeType{search_node{root_vertex_id}}; } diff --git a/include/hgl/algorithm/templates/bfs.hpp b/include/hgl/algorithm/templates/bfs.hpp index 90c65e27..09726100 100644 --- a/include/hgl/algorithm/templates/bfs.hpp +++ b/include/hgl/algorithm/templates/bfs.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/algorithm/templates/bfs.hpp +/// @brief Generic Breadth-First Search (BFS) template algorithm engine for hypergraphs. + #pragma once #include "hgl/algorithm/core.hpp" @@ -10,7 +13,74 @@ namespace hgl::algorithm { -/// @ingroup hgl_alg +/// @ingroup HGL-Algorithm +/// @brief A highly customizable, generic Breadth-First Search (BFS) algorithm engine for hypergraphs. +/// +/// This template provides the strict structural execution of a queue-based Breadth-First Search +/// over a hypergraph's topology. Because a hypergraph traversal inherently requires a two-step +/// expansion (from a vertex to its incident hyperedges, and then to the adjacent vertices), this +/// engine exposes specific hooks for both steps. Concrete algorithms are constructed by injecting +/// logic into the provided callback and predicate hooks. +/// +/// ### Example Usage +/// ```cpp +/// std::vector visited(hypergraph.n_vertices(), false); // (1)! +/// +/// bool completed = hgl::algorithm::bfs( +/// hypergraph, +/// hgl::algorithm::init_node_range(start_id), // (2)! +/// [&](const auto& node) { return not visited[node.vertex_id]; }, // (3)! +/// [&](const auto& node) { // (4)! +/// visited[node.vertex_id] = true; +/// std::cout << "Visited vertex " << node.vertex_id << '\n'; +/// return true; // Continue search +/// }, +/// hgl::empty_callback{}, // (5)! +/// [&](const auto& tgt_node) { return not visited[tgt_node.vertex_id]; } // (6)! +/// ); +/// ``` +/// +/// 1\. Provide an external state array to track the visited vertices. +/// +/// 2\. Initialize the search queue with a root @ref hgl::algorithm::search_node "search node" representing the starting point. +/// +/// 3\. The *visit predicate* ensures vertices are not processed multiple times. +/// +/// 4\. The concrete vertex visiting logic - marks the vertex as visited and logs it to the console. +/// +/// 5\. Traverse all hyperedges unconditionally. +/// +/// 6\. The *enqueue predicate* filters out already visited adjacent vertices before they enter the queue. +/// +/// ### Template Parameters +/// | Parameter | Description | Constraint | +/// | :-------- | :--- | :--- | +/// | Dir | The @ref hgl::algorithm::traversal_direction "traversal direction" (i.e., `forward` or `backward`). Relevant only for BF-directed hypergraphs. | Defaults to `forward`. | +/// | H | The type of the hypergraph being searched. | Must satisfy the [**c_hypergraph**](hgl_concepts.md#hgl-traits-c-hypergraph) concept. | +/// | InitQueueRangeType | A forward range of `search_node` used to prime the BFS queue. | Must be a *forward range* of @ref hgl::algorithm::search_node "search nodes". | +/// | VisitPredicate | Type of the callable deciding if a popped node should be processed. | Must be one of:
- A `(const search_node&) -> bool` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | VisitCallback | Type of the callable executed when a vertex is officially visited. | Must be one of:
- A `(const search_node&) -> bool` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | TraverseHePredicate | Type of the callable deciding if an incident hyperedge should be traversed. | Must be one of:
- An `(id_type, id_type) -> decision` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | EnqueuePredicate | Type of the callable deciding if a target vertex should be enqueued via a specific hyperedge. | Must be one of:
- A `(const search_node&) -> decision` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PreVisitCallback | Type of the callable executed immediately before `VisitCallback`. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// +/// @param hypergraph The hypergraph to traverse. +/// @param initial_queue_content The initial set of search nodes to begin the traversal from. +/// @param visit_pred Predicate to filter nodes immediately after popping them from the queue. +/// @param visit Primary callback for node processing. +/// @param traverse_he_pred Predicate to determine if an incident hyperedge should be traversed. Returns a @ref hgl::algorithm::decision "decision": +/// - `accept` to traverse the hyperedge, +/// - `reject` to skip the hyperedge, +/// - `abort` to terminate the BFS entirely. +/// @param enqueue_pred Predicate to determine if an adjacent vertex should be queued via a hyperedge. Returns a @ref hgl::algorithm::decision "decision": +/// - `accept` to enqueue the vertex's search node, +/// - `reject` to skip the node, +/// - `abort` to terminate the BFS entirely. +/// @param pre_visit Callback executed prior to the primary visit logic. +/// @param post_visit Callback executed after all adjacent elements of the current node have been processed. +/// @return `true` if the search completed normally, `false` if it was explicitly aborted via a callback. +/// @hideparams template < traversal_direction Dir = traversal_direction::forward, traits::c_hypergraph H, @@ -18,7 +88,7 @@ template < traits::c_optional_predicate&> VisitPredicate = empty_callback, traits::c_optional_predicate&> VisitCallback = empty_callback, traits::c_optional_decision_predicate - TraverseHyperedgePredicate = empty_callback, + TraverseHePredicate = empty_callback, traits::c_decision_predicate&> EnqueuePredicate = empty_callback, traits::c_optional_callback&> PreVisitCallback = empty_callback, traits::c_optional_callback&> PostVisitCallback = empty_callback> @@ -27,7 +97,7 @@ bool bfs( const InitQueueRangeType& initial_queue_content, const VisitPredicate& visit_pred = {}, const VisitCallback& visit = {}, - const TraverseHyperedgePredicate& traverse_he_pred = {}, + const TraverseHePredicate& traverse_he_pred = {}, const EnqueuePredicate& enqueue_pred = {}, const PreVisitCallback& pre_visit = {}, const PostVisitCallback& post_visit = {} @@ -57,7 +127,7 @@ bool bfs( return false; for (const auto he_id : policy::target_hyperedges(hypergraph, curr_node.vertex_id)) { - if constexpr (not traits::c_empty_callback) { + if constexpr (not traits::c_empty_callback) { const auto traverse = traverse_he_pred(he_id, curr_node.vertex_id); if (traverse == decision::abort) return false; diff --git a/include/hgl/algorithm/traversal/breadth_first_search.hpp b/include/hgl/algorithm/traversal/breadth_first_search.hpp index 987f50d7..6ff76f9c 100644 --- a/include/hgl/algorithm/traversal/breadth_first_search.hpp +++ b/include/hgl/algorithm/traversal/breadth_first_search.hpp @@ -31,7 +31,7 @@ result_type> breadth_first_search( if (root_vertex_id != no_root) { bfs( hypergraph, - init_range(root_vertex_id), + init_node_range(root_vertex_id), default_visit_predicate(visited_vertices), default_visit_callback(visited_vertices, stree), default_traverse_hyperedge_predicate(visited_hyperedges), @@ -44,7 +44,7 @@ result_type> breadth_first_search( for (const auto root_id : hypergraph.vertex_ids()) bfs( hypergraph, - init_range(root_id), + init_node_range(root_id), default_visit_predicate(visited_vertices), default_visit_callback(visited_vertices, stree), default_traverse_hyperedge_predicate(visited_hyperedges), diff --git a/include/hgl/algorithm/traversal/depth_first_search.hpp b/include/hgl/algorithm/traversal/depth_first_search.hpp index 23312ec0..e5f7c33d 100644 --- a/include/hgl/algorithm/traversal/depth_first_search.hpp +++ b/include/hgl/algorithm/traversal/depth_first_search.hpp @@ -31,7 +31,7 @@ result_type> depth_first_search( if (root_vertex_id != no_root) { dfs( hypergraph, - init_range(root_vertex_id), + init_node_range(root_vertex_id), default_visit_predicate(visited_vertices), default_visit_callback(visited_vertices, stree), default_traverse_hyperedge_predicate(visited_hyperedges), @@ -44,7 +44,7 @@ result_type> depth_first_search( for (const auto root_id : hypergraph.vertex_ids()) dfs( hypergraph, - init_range(root_id), + init_node_range(root_id), default_visit_predicate(visited_vertices), default_visit_callback(visited_vertices, stree), default_traverse_hyperedge_predicate(visited_hyperedges), diff --git a/include/hgl/algorithm/util.hpp b/include/hgl/algorithm/util.hpp index de9415c6..efc05de5 100644 --- a/include/hgl/algorithm/util.hpp +++ b/include/hgl/algorithm/util.hpp @@ -27,7 +27,7 @@ template } template -[[nodiscard]] gl_attr_force_inline std::vector> init_range( +[[nodiscard]] gl_attr_force_inline std::vector> init_node_range( typename H::id_type root_vertex_id ) { return std::vector>{search_node{root_vertex_id}}; From 7a5d12954e618b4e5ead6563379080efccb62360 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 01:24:07 +0200 Subject: [PATCH 14/28] dfs docs --- include/hgl/algorithm/templates/dfs.hpp | 77 ++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/include/hgl/algorithm/templates/dfs.hpp b/include/hgl/algorithm/templates/dfs.hpp index 1988bd22..8c697a0c 100644 --- a/include/hgl/algorithm/templates/dfs.hpp +++ b/include/hgl/algorithm/templates/dfs.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/algorithm/templates/dfs.hpp +/// @brief Generic Depth-First Search (DFS) template algorithm engine for hypergraphs. + #pragma once #include "hgl/algorithm/core.hpp" @@ -10,6 +13,74 @@ namespace hgl::algorithm { +/// @ingroup HGL-Algorithm +/// @brief A highly customizable, generic Depth-First Search (DFS) algorithm engine for hypergraphs. +/// +/// This template provides the strict structural execution of a stack-based Depth-First Search +/// over a hypergraph's topology. Because a hypergraph traversal inherently requires a two-step +/// expansion (from a vertex to its incident hyperedges, and then to the adjacent vertices), this +/// engine exposes specific hooks for both steps. Concrete algorithms are constructed by injecting +/// logic into the provided callback and predicate hooks. +/// +/// ### Example Usage +/// ```cpp +/// std::vector visited(hypergraph.n_vertices(), false); // (1)! +/// +/// bool completed = hgl::algorithm::dfs( +/// hypergraph, +/// hgl::algorithm::init_node_range(start_id), // (2)! +/// [&](const auto& node) { return not visited[node.vertex_id]; }, // (3)! +/// [&](const auto& node) { // (4)! +/// visited[node.vertex_id] = true; +/// std::cout << "Visited vertex " << node.vertex_id << '\n'; +/// return true; // Continue search +/// }, +/// hgl::empty_callback{}, // (5)! +/// [&](const auto& tgt_node) { return not visited[tgt_node.vertex_id]; } // (6)! +/// ); +/// ``` +/// +/// 1\. Provide an external state array to track the visited vertices. +/// +/// 2\. Initialize the search stack with a root @ref hgl::algorithm::search_node "search node" representing the starting point. +/// +/// 3\. The *visit predicate* ensures vertices are not processed multiple times. +/// +/// 4\. The concrete vertex visiting logic - marks the vertex as visited and logs it to the console. +/// +/// 5\. Traverse all hyperedges unconditionally. +/// +/// 6\. The *enqueue predicate* filters out already visited adjacent vertices before they are pushed to the stack. +/// +/// ### Template Parameters +/// | Parameter | Description | Constraint | +/// | :-------- | :--- | :--- | +/// | Dir | The @ref hgl::algorithm::traversal_direction "traversal direction" (i.e., `forward` or `backward`). Relevant only for BF-directed hypergraphs. | Defaults to `forward`. | +/// | H | The type of the hypergraph being searched. | Must satisfy the [**c_hypergraph**](hgl_concepts.md#hgl-traits-c-hypergraph) concept. | +/// | InitQueueRangeType | A forward range of `search_node` used to prime the DFS stack. | Must be a *forward range* of @ref hgl::algorithm::search_node "search nodes". | +/// | VisitPredicate | Type of the callable deciding if a popped node should be processed. | Must be one of:
- A `(const search_node&) -> bool` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | VisitCallback | Type of the callable executed when a vertex is officially visited. | Must be one of:
- A `(const search_node&) -> bool` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | TraverseHePredicate | Type of the callable deciding if an incident hyperedge should be traversed. | Must be one of:
- An `(id_type, id_type) -> decision` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | EnqueuePredicate | Type of the callable deciding if a target vertex should be pushed to the stack via a specific hyperedge. | Must be one of:
- A `(const search_node&) -> decision` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PreVisitCallback | Type of the callable executed immediately before `VisitCallback`. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// +/// @param hypergraph The hypergraph to traverse. +/// @param initial_queue_content The initial set of search nodes to begin the traversal from. +/// @param visit_pred Predicate to filter nodes immediately after popping them from the stack. +/// @param visit Primary callback for node processing. +/// @param traverse_he_pred Predicate to determine if an incident hyperedge should be traversed. Returns a @ref hgl::algorithm::decision "decision": +/// - `accept` to traverse the hyperedge, +/// - `reject` to skip the hyperedge, +/// - `abort` to terminate the DFS entirely. +/// @param enqueue_pred Predicate to determine if an adjacent vertex should be pushed to the stack via a hyperedge. Returns a @ref hgl::algorithm::decision "decision": +/// - `accept` to push the vertex's search node, +/// - `reject` to skip the node, +/// - `abort` to terminate the DFS entirely. +/// @param pre_visit Callback executed prior to the primary visit logic. +/// @param post_visit Callback executed after all adjacent elements of the current node have been processed. +/// @return `true` if the search completed normally, `false` if it was explicitly aborted via a callback. +/// @hideparams template < traversal_direction Dir = traversal_direction::forward, traits::c_hypergraph H, @@ -17,7 +88,7 @@ template < traits::c_optional_predicate&> VisitPredicate = empty_callback, traits::c_optional_predicate&> VisitCallback = empty_callback, traits::c_optional_decision_predicate - TraverseHyperedgePredicate = empty_callback, + TraverseHePredicate = empty_callback, traits::c_decision_predicate&> EnqueuePredicate = empty_callback, traits::c_optional_callback&> PreVisitCallback = empty_callback, traits::c_optional_callback&> PostVisitCallback = empty_callback> @@ -26,7 +97,7 @@ bool dfs( const InitQueueRangeType& initial_queue_content, const VisitPredicate& visit_pred = {}, const VisitCallback& visit = {}, - const TraverseHyperedgePredicate& traverse_he_pred = {}, + const TraverseHePredicate& traverse_he_pred = {}, const EnqueuePredicate& enqueue_pred = {}, const PreVisitCallback& pre_visit = {}, const PostVisitCallback& post_visit = {} @@ -56,7 +127,7 @@ bool dfs( return false; for (const auto he_id : policy::target_hyperedges(hypergraph, curr_node.vertex_id)) { - if constexpr (not traits::c_empty_callback) { + if constexpr (not traits::c_empty_callback) { const auto traverse = traverse_he_pred(he_id, curr_node.vertex_id); if (traverse == decision::abort) return false; From 1f89fe631c2922fc904b6d727c2cb180b169db39 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 01:28:29 +0200 Subject: [PATCH 15/28] alg utils docs --- include/hgl/algorithm/util.hpp | 57 ++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/include/hgl/algorithm/util.hpp b/include/hgl/algorithm/util.hpp index efc05de5..003d42a5 100644 --- a/include/hgl/algorithm/util.hpp +++ b/include/hgl/algorithm/util.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/algorithm/util.hpp +/// @brief Internal utilities and default behaviors used by hypergraph traversal algorithms. + #pragma once #include "gl/algorithm/util.hpp" @@ -9,6 +12,12 @@ namespace hgl::algorithm { +/// @ingroup HGL-Algorithm +/// @brief Initializes a search tree based on the static result discriminator. +/// @tparam Result The compilation tag determining if the tree should actually be built. +/// @tparam H The type of the hypergraph. +/// @param hypergraph The hypergraph instance to size the search tree against. +/// @return A fully sized and initialized `search_tree` if `Result == ret`, otherwise a dummy `std::monostate`. template [[nodiscard]] gl_attr_force_inline non_void_result_type> init_search_tree( const H& hypergraph @@ -20,12 +29,22 @@ template return return_t(); } +/// @ingroup HGL-Algorithm +/// @brief Checks if a specific vertex was reached during the traversal. +/// @param tree The computed search tree resulting from a traversal. +/// @param vertex_id The identifier of the vertex to check. +/// @return `true` if the vertex has a valid predecessor in the tree, `false` otherwise. [[nodiscard]] gl_attr_force_inline bool is_reachable( const traits::c_search_tree auto& tree, traits::c_id_type auto vertex_id ) noexcept { return tree[to_idx(vertex_id)].pred_id != invalid_id; } +/// @ingroup HGL-Algorithm +/// @brief Initializes a container with a starting set of root search nodes. +/// @tparam H The type of the hypergraph. +/// @param root_vertex_id The ID of the starting vertex. +/// @return A `std::vector` containing a single root @ref hgl::algorithm::search_node "search_node". template [[nodiscard]] gl_attr_force_inline std::vector> init_node_range( typename H::id_type root_vertex_id @@ -33,6 +52,11 @@ template return std::vector>{search_node{root_vertex_id}}; } +/// @ingroup HGL-Algorithm +/// @brief Generates a default lambda predicate that checks if a popped search node has already been visited. +/// @tparam H The type of the hypergraph. +/// @param visited_v A reference to the boolean array tracking visited vertices. +/// @return A callable predicate that returns `true` if the vertex in the node has not been visited, `false` otherwise. template [[nodiscard]] gl_attr_force_inline auto default_visit_predicate(std::vector& visited_v) { return [&](const search_node& node) -> bool { @@ -40,6 +64,17 @@ template }; } +/// @ingroup HGL-Algorithm +/// @brief Generates a default lambda callback executed upon officially visiting a vertex. +/// +/// Marks the vertex as visited. If `Result == ret`, it also records the search node into the provided search tree. +/// +/// @tparam H The type of the hypergraph. +/// @tparam Result The compilation tag dictating whether to populate the search tree. +/// @param visited_v A reference to the boolean array tracking visited vertices. +/// @param pred_map A reference to the search tree being populated (or a dummy if `Result == noret`). +/// @return A callable callback returning `true` to unconditionally continue the traversal. +/// @hideparams template [[nodiscard]] gl_attr_force_inline auto default_visit_callback( std::vector& visited_v, non_void_result_type>& pred_map @@ -53,6 +88,13 @@ template }; } +/// @ingroup HGL-Algorithm +/// @brief Generates a default lambda predicate that tracks traversed hyperedges to prevent redundant exploration. +/// +/// Records hyperedges as they are encountered and rejects any that have already been traversed during the search. +/// +/// @param visited_he A reference to the boolean array tracking visited hyperedges. +/// @return A callable predicate returning a @ref hgl::algorithm::decision "decision" (`accept` if not previously visited, `reject` if already visited). [[nodiscard]] gl_attr_force_inline auto default_traverse_hyperedge_predicate( std::vector& visited_he ) { @@ -66,6 +108,15 @@ template }; } +/// @ingroup HGL-Algorithm +/// @brief Generates a lambda predicate that blocks hyperedge traversal until its associated counter reaches zero. +/// +/// This predicate is particularly useful in topological sorting or multi-dependency algorithms for *BF-directed* +/// hypergraphs, where a hyperedge should only be evaluated once all of its dependencies (e.g., all vertices in +/// its tail) have been saturated or visited. +/// +/// @param counter_map A reference to an array of size or dependency counters mapped to hyperedge IDs. +/// @return A callable predicate returning a @ref hgl::algorithm::decision "decision" (`accept` if the decremented counter reaches 0, `reject` otherwise). [[nodiscard]] gl_attr_force_inline auto blocking_traverse_hyperedge_predicate( std::vector& counter_map ) { @@ -74,6 +125,12 @@ template }; } +/// @ingroup HGL-Algorithm +/// @brief Generates a default lambda predicate that checks if a node corresponding to an adjacent vertex should be enqueued. +/// @tparam H The type of the hypergraph. +/// @tparam AsResult If `true`, the generated predicate returns a @ref hgl::algorithm::decision "decision" instead of a raw boolean. +/// @param visited_v A reference to the boolean array tracking visited vertices. +/// @return A callable predicate that returns `true` (or `decision::accept`) if the adjacent vertex has not been visited. template [[nodiscard]] gl_attr_force_inline auto default_enqueue_predicate(std::vector& visited_v) { using return_t = std::conditional_t; From e508ac89315a4d491492464b4aadbd9c893c79e0 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 01:51:15 +0200 Subject: [PATCH 16/28] concrete bfs docs + @see fix --- .../gl/algorithm/spanning_tree/prim_mst.hpp | 6 ++- include/gl/algorithm/topology/coloring.hpp | 3 +- .../traversal/breadth_first_search.hpp | 5 ++- .../traversal/depth_first_search.hpp | 6 +-- include/gl/impl/impl_tags.hpp | 6 ++- include/gl/io/ranges.hpp | 6 ++- include/hgl/algorithm/core.hpp | 22 +++++---- .../traversal/breadth_first_search.hpp | 45 +++++++++++++++++++ include/hgl/impl/impl_tags.hpp | 6 ++- include/hgl/types.hpp | 24 ++++++---- include/hgl/util.hpp | 6 ++- 11 files changed, 103 insertions(+), 32 deletions(-) diff --git a/include/gl/algorithm/spanning_tree/prim_mst.hpp b/include/gl/algorithm/spanning_tree/prim_mst.hpp index 4814188b..46f34677 100644 --- a/include/gl/algorithm/spanning_tree/prim_mst.hpp +++ b/include/gl/algorithm/spanning_tree/prim_mst.hpp @@ -73,7 +73,8 @@ struct mst_descriptor { /// @param graph The undirected graph to evaluate. /// @param root_id The starting vertex ID for the MST calculation. Defaults to the graph's `initial_id` if `invalid_id` is passed. /// @return A @ref gl::algorithm::mst_descriptor "mst_descriptor" containing the accumulated minimum weight and the sequence of edges forming the tree. -/// @see @ref gl::algorithm::vertex_heap_prim_mst "vertex_heap_prim_mst" For the vertex-heap variant of the Prim's MST finding algorithm. +/// ### See Also +/// - @ref gl::algorithm::vertex_heap_prim_mst "vertex_heap_prim_mst" For the vertex-heap variant of the Prim's MST finding algorithm. /// @hideparams template [[nodiscard]] mst_descriptor edge_heap_prim_mst(const G& graph, typename G::id_type root_id) { @@ -164,7 +165,8 @@ template /// @param graph The undirected graph to evaluate. /// @param root_id The starting vertex ID for the MST calculation. Defaults to the graph's `initial_id` if `invalid_id` is passed. /// @return A @ref gl::algorithm::mst_descriptor "mst_descriptor" containing the accumulated minimum weight and the sequence of edges forming the tree. -/// @see @ref gl::algorithm::edge_heap_prim_mst "edge_heap_prim_mst" For the vertex-heap variant of the Prim's MST finding algorithm. +/// ### See Also +/// - @ref gl::algorithm::edge_heap_prim_mst "edge_heap_prim_mst" For the vertex-heap variant of the Prim's MST finding algorithm. /// @hideparams template requires(traits::c_has_numeric_limits_max>) diff --git a/include/gl/algorithm/topology/coloring.hpp b/include/gl/algorithm/topology/coloring.hpp index 7b7cd49c..fbc5fb1d 100644 --- a/include/gl/algorithm/topology/coloring.hpp +++ b/include/gl/algorithm/topology/coloring.hpp @@ -117,7 +117,8 @@ template < /// @brief Convenience wrapper for the @ref gl::algorithm::bipartite_coloring "bipartite_coloring" algorithm to check if a graph is bipartite without extracting the exact coloring map. /// @param graph The graph to evaluate. /// @return `true` if the graph is bipartite (2-colorable), `false` otherwise. -/// @see @ref gl::algorithm::apply_coloring "apply_coloring" +/// ### See Also +/// - @ref gl::algorithm::apply_coloring "apply_coloring" [[nodiscard]] gl_attr_force_inline bool is_bipartite(const traits::c_graph auto& graph) { return bipartite_coloring(graph).has_value(); } diff --git a/include/gl/algorithm/traversal/breadth_first_search.hpp b/include/gl/algorithm/traversal/breadth_first_search.hpp index f4ed9ad0..8897a8a9 100644 --- a/include/gl/algorithm/traversal/breadth_first_search.hpp +++ b/include/gl/algorithm/traversal/breadth_first_search.hpp @@ -26,7 +26,8 @@ namespace gl::algorithm { /// /// ### Example Usage /// ```cpp -/// auto pred_map = gl::algorithm::breadth_first_search(graph, start_id); // (1)! +/// auto pred_map +/// = gl::algorithm::breadth_first_search(graph, start_id); // (1)! /// /// gl::algorithm::breadth_first_search( // (2)! /// graph, @@ -55,7 +56,7 @@ namespace gl::algorithm { /// ### Template Parameters /// | Parameter | Description | Constraint | /// | :-------- | :--- | :--- | -/// | Result | @ref gl::algorithm::result_discriminator "Discriminator" dictating if the algorithm should return a predecessor map (`ret`) or `void` (`noret`). | Must be a valid @ref gl::algorithm::result_discriminator "result_discriminator" enum value. | +/// | Result | Discriminator dictating if the algorithm should return a predecessor map (`ret`) or `void` (`noret`). | Must be a valid @ref gl::algorithm::result_discriminator "result_discriminator" enum value. | /// | G | The type of the graph being traversed. | Must satisfy the [**c_graph**](gl_concepts.md#gl-traits-c-graph) concept. | /// | PreVisitCallback | Type of the callable executed immediately before a vertex is officially visited. | Must be one of:
- `(id_type) -> void` callable
- An @ref gl::algorithm::empty_callback "empty_callback" | /// | PostVisitCallback | Type of the callable executed after all adjacent edges of a vertex are evaluated. | Must be one of:
- `(id_type) -> void` callable
- An @ref gl::algorithm::empty_callback "empty_callback" | diff --git a/include/gl/algorithm/traversal/depth_first_search.hpp b/include/gl/algorithm/traversal/depth_first_search.hpp index 7d1c06cd..2a8571e7 100644 --- a/include/gl/algorithm/traversal/depth_first_search.hpp +++ b/include/gl/algorithm/traversal/depth_first_search.hpp @@ -34,7 +34,8 @@ namespace gl::algorithm { /// /// ### Example Usage /// ```cpp -/// auto pred_map = gl::algorithm::depth_first_search(graph, start_id); // (1)! +/// auto pred_map +/// = gl::algorithm::depth_first_search(graph, start_id); // (1)! /// /// gl::algorithm::depth_first_search( // (2)! /// graph, @@ -135,7 +136,6 @@ result_type> depth_first_search( /// /// ### Example Usage /// ```cpp -/// // Execution purely for side-effects from a specific root /// gl::algorithm::recursive_depth_first_search( // (1)! /// graph, /// start_id, // (2)! @@ -144,7 +144,7 @@ result_type> depth_first_search( /// ); /// ``` /// -/// 1\. Execution purely for side-effects over the entire graph. Uses the @ref gl::algorithm::result_discriminator "noret" discriminator to completely compile away the predecessor map allocations. +/// 1\. Execution purely for side-effects. Uses the @ref gl::algorithm::result_discriminator "noret" discriminator to completely compile away the predecessor map allocations. /// /// 2\. Initiates the recursion from `start_id`. /// diff --git a/include/gl/impl/impl_tags.hpp b/include/gl/impl/impl_tags.hpp index ecbb25ce..6def0a62 100644 --- a/include/gl/impl/impl_tags.hpp +++ b/include/gl/impl/impl_tags.hpp @@ -28,7 +28,8 @@ struct list_t { /// @ingroup GL GL-Core /// @headerfile gl/impl/impl_tags.hpp /// @brief Tag struct for the flattened adjacency list graph implementation. -/// @see @ref gl::flat_jagged_vector "flat_jagged_vector" : For the data structure used for the underlying model implementation. +/// ### See Also +/// - @ref gl::flat_jagged_vector "flat_jagged_vector" : For the data structure used for the underlying model implementation. struct flat_list_t { /// @brief Type alias for the flattened adjacency list graph implementation based on the provided graph traits. /// @tparam GraphTraits The graph traits for which to define the flattened adjacency list type. @@ -53,7 +54,8 @@ struct matrix_t { /// @ingroup GL GL-Core /// @headerfile gl/impl/impl_tags.hpp /// @brief Tag struct for the flattened adjacency matrix graph implementation. -/// @see @ref gl::flat_matrix "flat_matrix" : For the data structure used for the underlying model implementation. +/// ### See Also +/// - @ref gl::flat_matrix "flat_matrix" : For the data structure used for the underlying model implementation. struct flat_matrix_t { /// @brief Type alias for the flattened adjacency matrix graph implementation based on the provided graph traits. /// @tparam GraphTraits The graph traits for which to define the flattened adjacency matrix type. diff --git a/include/gl/io/ranges.hpp b/include/gl/io/ranges.hpp index 074de4d5..ad1e8834 100644 --- a/include/gl/io/ranges.hpp +++ b/include/gl/io/ranges.hpp @@ -104,7 +104,8 @@ range_formatter(R&& r, std::string_view, std::string_view, std::string_view) /// @param range The range object to format. /// @param sep The separator string between elements. Defaults to `", "`. /// @return A @ref range_formatter configured for set-style output. -/// @see @ref gl::io::multiline_set_formatter "multiline_set_formatter" +/// ### See Also +/// - @ref gl::io::multiline_set_formatter "multiline_set_formatter" template auto set_formatter(R&& range, std::string_view sep = ", ") { using view_type = std::views::all_t; @@ -136,7 +137,8 @@ auto set_formatter(R&& range, std::string_view sep = ", ") { /// @tparam R The type of the range. /// @param range The range object to format. /// @return A @ref range_formatter configured for multiline set-style output. -/// @see @ref gl::io::set_formatter "set_formatter" +/// ### See Also +/// - @ref gl::io::set_formatter "set_formatter" template auto multiline_set_formatter(R&& range) { using view_type = std::views::all_t; diff --git a/include/hgl/algorithm/core.hpp b/include/hgl/algorithm/core.hpp index 2bb8aa1d..fcdc4138 100644 --- a/include/hgl/algorithm/core.hpp +++ b/include/hgl/algorithm/core.hpp @@ -25,35 +25,40 @@ namespace algorithm { /// @ingroup HGL-Algorithm /// @copybrief gl::algorithm::empty_callback -/// @see gl::algorithm::empty_callback for a more detailed description. +/// ### See Also +/// - @ref gl::algorithm::empty_callback : For a more detailed description. using empty_callback = gl::algorithm::empty_callback; /// @ingroup HGL-Algorithm /// @copybrief gl::algorithm::decision -/// @see gl::algorithm::decision for the full type definition +/// ### See Also +/// - @ref gl::algorithm::decision : For the full type definition. using decision = gl::algorithm::decision; /// @ingroup HGL-Algorithm /// @copybrief gl::algorithm::result_discriminator -/// @see gl::algorithm::result_discriminator for the full type definition. +/// ### See Also +/// - @ref gl::algorithm::result_discriminator : For the full type definition. using result_discriminator = gl::algorithm::result_discriminator; using enum result_discriminator; /// @ingroup HGL-Algorithm /// @copybrief gl::algorithm::result_type -/// @see gl::algorithm::result_type for the full type definition. +/// ### See Also +/// - @ref gl::algorithm::result_type : For the full type definition. template using result_type = gl::algorithm::result_type; /// @ingroup HGL-Algorithm /// @copybrief gl::algorithm::non_void_result_type -/// @see gl::algorithm::non_void_result_type for the full type definition. +/// @see gl::algorithm::non_void_result_type : For the full type definition. template using non_void_result_type = gl::algorithm::non_void_result_type; /// @ingroup HGL-Algorithm /// @copybrief gl::algorithm::no_root_v -/// @see gl::algorithm::no_root_v +/// ### See Also +/// - @ref gl::algorithm::no_root_v template inline constexpr IdType no_root_v = gl::algorithm::no_root_v; @@ -64,7 +69,8 @@ using no_root_t = gl::algorithm::no_root_t; /// @ingroup HGL-Algorithm /// @copybrief gl::algorithm::no_root -/// @see gl::algorithm::no_root +/// ### See Also +/// - @ref gl::algorithm::no_root inline constexpr no_root_t no_root = gl::algorithm::no_root; // --- traversal types --- @@ -111,7 +117,7 @@ struct search_node { /// /// The $i$-th element corresponds to the vertex with `id == i`. The tree topology is formed implicitly, /// as each @ref hgl::algorithm::search_node "search_node" stores the ID of its predecessor and the -/// connecting hyperedge, enabling $O(1)$ lookups and and $O(\vert V \vert)$ path reconstruction. +/// connecting hyperedge, enabling \f$O(1)\f$ lookups and and \f$O(\vert V \vert)\f$ path reconstruction. /// /// @tparam H The type of the hypergraph being searched. template diff --git a/include/hgl/algorithm/traversal/breadth_first_search.hpp b/include/hgl/algorithm/traversal/breadth_first_search.hpp index 6ff76f9c..f1b09082 100644 --- a/include/hgl/algorithm/traversal/breadth_first_search.hpp +++ b/include/hgl/algorithm/traversal/breadth_first_search.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/algorithm/traversal/breadth_first_search.hpp +/// @brief Concrete Breadth-First Search (BFS) traversal algorithm implementation for hypergraphs. + #pragma once #include "hgl/algorithm/core.hpp" @@ -10,6 +13,48 @@ namespace hgl::algorithm { +/// @ingroup HGL-Algorithm +/// @brief Executes a concrete Breadth-First Search (BFS) traversal over the hypergraph. +/// +/// This function utilizes the generic @ref hgl::algorithm::bfs "bfs" template to perform a standard queue-based traversal. +/// It automatically manages the visited states (for both vertices and hyperedges), search tree tracking, and queue initialization. +/// +/// If a specific `root_vertex_id` is provided, the algorithm explores only the connected component reachable from that root. +/// If `no_root` is used, it iteratively ensures that every disconnected component in the entire hypergraph is fully traversed. +/// +/// ### Example Usage +/// ```cpp +/// auto search_tree +/// = hgl::algorithm::breadth_first_search(hypergraph, start_id); // (1)! +/// +/// hgl::algorithm::breadth_first_search( // (2)! +/// hypergraph, +/// hgl::algorithm::no_root, // (3)! +/// [](const auto& node) { std::cout << "Pre-visit: " << node.vertex_id << '\n'; }, +/// [](const auto& node) { std::cout << "Post-visit: " << node.vertex_id << '\n'; } +/// ); +/// ``` +/// +/// 1\. Executes a standard BFS returning a search tree mapped to the components reachable from `start_id`. +/// +/// 2\. Executes a BFS purely for side-effects (callbacks) without allocating memory for a search tree. +/// +/// 3\. Passing `no_root` forces the algorithm to iterate over all vertices, ensuring disjoint components are traversed. +/// +/// ### Template Parameters +/// | Parameter | Description | Constraint | +/// | :-------- | :--- | :--- | +/// | Result | Controls whether the algorithm builds and returns a search tree (`ret`) or evaluates purely for side effects (`noret`). | Must be a valid @ref hgl::algorithm::result_discriminator "result_discriminator" enum value. | +/// | H | The type of the hypergraph being searched. | Must satisfy the [**c_hypergraph**](hgl_concepts.md#hgl-traits-c-hypergraph) concept. | +/// | PreVisitCallback | Type of the callable executed immediately before officially visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// +/// @param hypergraph The hypergraph to traverse. +/// @param root_vertex_id The ID of the vertex to start the search from. If `no_root`, searches the entire hypergraph. +/// @param pre_visit Hook executed immediately before officially visiting the vertex. +/// @param post_visit Hook executed after all adjacent hyperedges and target vertices of the current node have been evaluated. +/// @return A @ref hgl::algorithm::search_tree "search_tree" if `Result == ret`, otherwise nothing (`void`). +/// @hideparams template < result_discriminator Result = ret, traits::c_hypergraph H, diff --git a/include/hgl/impl/impl_tags.hpp b/include/hgl/impl/impl_tags.hpp index 806bdcdc..09db9890 100644 --- a/include/hgl/impl/impl_tags.hpp +++ b/include/hgl/impl/impl_tags.hpp @@ -58,7 +58,8 @@ struct list_t { /// /// @tparam LayoutTag Specifies the memory layout orientation for the underlying data structure. /// @tparam IdType The underlying integer type used for identifiers. -/// @see @ref gl::flat_jagged_vector "flat_jagged_vector" for the data structure used for the underlying model implementation. +/// ### See Also +/// - @ref gl::flat_jagged_vector "flat_jagged_vector" for the data structure used for the underlying model implementation. template struct flat_list_t { /// @brief Self type alias. @@ -115,7 +116,8 @@ struct matrix_t { /// /// @tparam LayoutTag Specifies the memory layout orientation for the underlying data structure (must be asymmetric). /// @tparam IdType The underlying integer type used for identifiers. -/// @see @ref gl::flat_matrix "flat_matrix" for the data structure used for the underlying model implementation. +/// ### See Also +/// - @ref gl::flat_matrix "flat_matrix" for the data structure used for the underlying model implementation. template struct flat_matrix_t { /// @brief Self type alias. diff --git a/include/hgl/types.hpp b/include/hgl/types.hpp index dc6ea549..e36b11ca 100644 --- a/include/hgl/types.hpp +++ b/include/hgl/types.hpp @@ -43,19 +43,22 @@ using gl::to_diff; /// @ingroup HGL-Types /// @brief @copybrief gl::flat_jagged_vector -/// @see gl::flat_jagged_vector for the full type definition +/// ### See Also +/// - @ref gl::flat_jagged_vector : For the full type definition. template using flat_jagged_vector = gl::flat_jagged_vector; /// @ingroup HGL-Types /// @brief @copybrief gl::flat_matrix -/// @see gl::flat_matrix for the full type definition +/// ### See Also +/// - @ref gl::flat_matrix "gl::flat_matrix" : For the full type definition. template using flat_matrix = gl::flat_matrix; /// @ingroup HGL-Types /// @brief @copybrief gl::homogeneous_pair -/// @see gl::homogeneous_pair for the full type definition +/// ### See Also +/// - @ref gl::homogeneous_pair : For the full type definition. template using homogeneous_pair = gl::homogeneous_pair; @@ -69,7 +72,8 @@ using homogeneous_pair = gl::homogeneous_pair; /// > This type is used as a default `properties_type` for hypergraph components that do not require any user-defined data. /// > It serves as a marker to indicate that the component is *property-less* and can be optimized accordingly. /// -/// @see gl::empty_properties for the full type definition +/// ### See Also +/// - @ref gl::empty_properties : For the full type definition. using empty_properties = gl::empty_properties; /// @ingroup HGL-Core @@ -79,22 +83,26 @@ using empty_properties = gl::empty_properties; /// > /// > This type is used internally by the library to optimize storage for hypergraph components that have no properties. /// -/// @see gl::empty_properties_map for the full type definition +/// ### See Also +/// - @ref gl::empty_properties_map : For the full type definition. using empty_properties_map = gl::empty_properties_map; /// @ingroup HGL-Core /// @brief @copybrief gl::name_property -/// @see gl::name_property for the full type definition +/// ### See Also +/// - @ref gl::name_property : For the full type definition. using name_property = gl::name_property; /// @ingroup HGL-Core /// @brief @copybrief gl::dynamic_properties -/// @see gl::dynamic_properties for the full type definition +/// ### See Also +/// - @ref gl::dynamic_properties : For the full type definition. using dynamic_properties = gl::dynamic_properties; /// @ingroup HGL-Core /// @brief A property struct providing arithmetic weight for hyperedges or vertices. -/// @see gl::weight_property for the full type definition +/// ### See Also +/// - @ref gl::weight_property : For the full type definition. template using weight_property = gl::weight_property; diff --git a/include/hgl/util.hpp b/include/hgl/util.hpp index 766e2a39..bc78f712 100644 --- a/include/hgl/util.hpp +++ b/include/hgl/util.hpp @@ -29,13 +29,15 @@ using gl::util::range_size; /// @ingroup HGL-Util /// @brief @copybrief gl::util::concat_view -/// @see gl::util::concat_view for the full type definition +/// ### See Also +/// - @ref gl::util::concat_view : For the full type definition. template using concat_view = gl::util::concat_view; /// @ingroup HGL-Util /// @brief @copybrief gl::util::concat_fn -/// @see gl::util::concat_fn for the full type definition +/// ### See Also +/// - @ref gl::util::concat_fn : For the full type definition. using concat_fn = gl::util::concat_fn; /// @ingroup HGL-Util From af48dda01e38fe50f50c480a2abce10b93c2c0aa Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 01:54:13 +0200 Subject: [PATCH 17/28] concrete dfs docs --- .../traversal/depth_first_search.hpp | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/include/hgl/algorithm/traversal/depth_first_search.hpp b/include/hgl/algorithm/traversal/depth_first_search.hpp index e5f7c33d..d834e464 100644 --- a/include/hgl/algorithm/traversal/depth_first_search.hpp +++ b/include/hgl/algorithm/traversal/depth_first_search.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/algorithm/traversal/depth_first_search.hpp +/// @brief Concrete Depth-First Search (DFS) traversal algorithm implementation for hypergraphs. + #pragma once #include "hgl/algorithm/core.hpp" @@ -10,6 +13,48 @@ namespace hgl::algorithm { +/// @ingroup HGL-Algorithm +/// @brief Executes a concrete Depth-First Search (DFS) traversal over the hypergraph. +/// +/// This function utilizes the generic @ref hgl::algorithm::dfs "dfs" template to perform a standard stack-based traversal. +/// It automatically manages the visited states (for both vertices and hyperedges), search tree tracking, and stack initialization. +/// +/// If a specific `root_vertex_id` is provided, the algorithm explores only the connected component reachable from that root. +/// If `no_root` is used, it iteratively ensures that every disconnected component in the entire hypergraph is fully traversed. +/// +/// ### Example Usage +/// ```cpp +/// auto search_tree +/// = hgl::algorithm::depth_first_search(hypergraph, start_id); // (1)! +/// +/// hgl::algorithm::depth_first_search( // (2)! +/// hypergraph, +/// hgl::algorithm::no_root, // (3)! +/// [](const auto& node) { std::cout << "Pre-visit: " << node.vertex_id << '\n'; }, +/// [](const auto& node) { std::cout << "Post-visit: " << node.vertex_id << '\n'; } +/// ); +/// ``` +/// +/// 1\. Executes a standard DFS returning a search tree mapped to the components reachable from `start_id`. +/// +/// 2\. Executes a DFS purely for side-effects (callbacks) without allocating memory for a search tree. +/// +/// 3\. Passing `no_root` forces the algorithm to iterate over all vertices, ensuring disjoint components are traversed. +/// +/// ### Template Parameters +/// | Parameter | Description | Constraint | +/// | :-------- | :--- | :--- | +/// | Result | Controls whether the algorithm builds and returns a search tree (`ret`) or evaluates purely for side effects (`noret`). | Must be a valid @ref hgl::algorithm::result_discriminator "result_discriminator" enum value. | +/// | H | The type of the hypergraph being searched. | Must satisfy the [**c_hypergraph**](hgl_concepts.md#hgl-traits-c-hypergraph) concept. | +/// | PreVisitCallback | Type of the callable executed immediately before officially visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// +/// @param hypergraph The hypergraph to traverse. +/// @param root_vertex_id The ID of the vertex to start the search from. If `no_root`, searches the entire hypergraph. +/// @param pre_visit Hook executed immediately before officially visiting the vertex. +/// @param post_visit Hook executed after all adjacent hyperedges and target vertices of the current node have been evaluated. +/// @return A @ref hgl::algorithm::search_tree "search_tree" if `Result == ret`, otherwise nothing (`void`). +/// @hideparams template < result_discriminator Result = ret, traits::c_hypergraph H, From 007d3023ad93393d93f913a261b176580d10fe6b Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 02:00:20 +0200 Subject: [PATCH 18/28] bsearch and fsearch docs --- .../algorithm/traversal/backward_search.hpp | 83 +++++++++++++++++++ .../traversal/breadth_first_search.hpp | 4 +- .../traversal/depth_first_search.hpp | 4 +- .../algorithm/traversal/forward_search.hpp | 83 +++++++++++++++++++ 4 files changed, 170 insertions(+), 4 deletions(-) diff --git a/include/hgl/algorithm/traversal/backward_search.hpp b/include/hgl/algorithm/traversal/backward_search.hpp index 449739c1..64ac2e26 100644 --- a/include/hgl/algorithm/traversal/backward_search.hpp +++ b/include/hgl/algorithm/traversal/backward_search.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/algorithm/backward_search.hpp +/// @brief Concrete backward B-search traversals (B-BFS and B-DFS) for bf-directed hypergraphs. + #pragma once #include "hgl/algorithm/core.hpp" @@ -12,6 +15,46 @@ namespace hgl::algorithm { +/// @ingroup HGL-Algorithm +/// @brief Executes a Breadth-First B-Search (B-BFS) traversal over a bf-directed hypergraph. +/// +/// This algorithm implements B-reachability semantics using Breadth-First Search for BF-directed hypergraphs. +/// Unlike a standard traversal (where reaching a single tail vertex is sufficient to traverse an outgoing +/// hyperedge), a backward search (B-search) uses a blocking predicate. A hyperedge is only traversed, and +/// its head vertices enqueued, after **all** of its tail (source) vertices have been visited. +/// +/// ### Example Usage +/// ```cpp +/// std::vector roots = { start_id_1, start_id_2 }; +/// auto search_tree = hgl::algorithm::backward_bfs(hypergraph, roots); // (1)! +/// +/// hgl::algorithm::backward_bfs( // (2)! +/// hypergraph, +/// roots, +/// [](const auto& node) { std::cout << "Pre-visit: " << node.vertex_id << '\n'; }, +/// [](const auto& node) { std::cout << "Post-visit: " << node.vertex_id << '\n'; } +/// ); +/// ``` +/// +/// 1\. Executes a B-BFS returning a search tree mapped to the components B-reachable from the specified roots. +/// +/// 2\. Executes a B-BFS purely for side-effects (callbacks) without allocating memory for a search tree. +/// +/// ### Template Parameters +/// | Parameter | Description | Constraint | +/// | :-------- | :--- | :--- | +/// | Result | Controls whether the algorithm builds and returns a search tree (`ret`) or evaluates purely for side effects (`noret`). | Must be a valid @ref hgl::algorithm::result_discriminator "result_discriminator" enum value. | +/// | H | The type of the hypergraph being searched. | Must satisfy the [**c_bf_directed_hypergraph**](hgl_concepts.md#hgl-traits-c-bf-directed-hypergraph) concept. | +/// | RootRange | The type of the container providing the initial roots to enqueue. | Must satisfy [**c_forward_range_of**](gl_concepts.md#gl-traits-c-forward-range-of) over the hypergraph's `id_type`. | +/// | PreVisitCallback | Type of the callable executed immediately before visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// +/// @param hypergraph The bf-directed hypergraph to traverse. +/// @param root_vertices A range of initial vertex IDs to start the search from. +/// @param pre_visit Hook executed immediately before visiting the vertex. +/// @param post_visit Hook executed after all adjacent hyperedges and target vertices of the current node have been evaluated. +/// @return A @ref hgl::algorithm::search_tree "search_tree" if `Result == ret`, otherwise nothing (`void`). +/// @hideparams template < result_discriminator Result = ret, traits::c_bf_directed_hypergraph H, @@ -53,6 +96,46 @@ result_type> backward_bfs( return stree; } +/// @ingroup HGL-Algorithm +/// @brief Executes a Depth-First B-Search (B-DFS) traversal over a bf-directed hypergraph. +/// +/// This algorithm implements B-reachability semantics using a stack-based Depth-First Search for BF-directed +/// hypergraphs. Unlike a standard traversal (where reaching a single tail vertex is sufficient to traverse an +/// outgoing hyperedge), a backward search (B-search) uses a blocking predicate. A hyperedge is only traversed, +/// and its head vertices enqueued, after **all** of its tail (source) vertices have been visited. +/// +/// ### Example Usage +/// ```cpp +/// std::vector roots = { start_id_1, start_id_2 }; +/// auto search_tree = hgl::algorithm::backward_dfs(hypergraph, roots); // (1)! +/// +/// hgl::algorithm::backward_dfs( // (2)! +/// hypergraph, +/// roots, +/// [](const auto& node) { std::cout << "Pre-visit: " << node.vertex_id << '\n'; }, +/// [](const auto& node) { std::cout << "Post-visit: " << node.vertex_id << '\n'; } +/// ); +/// ``` +/// +/// 1\. Executes a B-DFS returning a search tree mapped to the components B-reachable from the specified roots. +/// +/// 2\. Executes a B-DFS purely for side-effects (callbacks) without allocating memory for a search tree. +/// +/// ### Template Parameters +/// | Parameter | Description | Constraint | +/// | :-------- | :--- | :--- | +/// | Result | Controls whether the algorithm builds and returns a search tree (`ret`) or evaluates purely for side effects (`noret`). | Must be a valid @ref hgl::algorithm::result_discriminator "result_discriminator" enum value. | +/// | H | The type of the hypergraph being searched. | Must satisfy the [**c_bf_directed_hypergraph**](hgl_concepts.md#hgl-traits-c-bf-directed-hypergraph) concept. | +/// | RootRange | The type of the container providing the initial roots to enqueue. | Must satisfy [**c_forward_range_of**](gl_concepts.md#gl-traits-c-forward-range-of) over the hypergraph's `id_type`. | +/// | PreVisitCallback | Type of the callable executed immediately before officially visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// +/// @param hypergraph The bf-directed hypergraph to traverse. +/// @param root_vertices A range of initial vertex IDs to start the search from. +/// @param pre_visit Hook executed immediately before officially visiting the vertex. +/// @param post_visit Hook executed after all adjacent hyperedges and target vertices of the current node have been evaluated. +/// @return A @ref hgl::algorithm::search_tree "search_tree" if `Result == ret`, otherwise nothing (`void`). +/// @hideparams template < result_discriminator Result = ret, traits::c_bf_directed_hypergraph H, diff --git a/include/hgl/algorithm/traversal/breadth_first_search.hpp b/include/hgl/algorithm/traversal/breadth_first_search.hpp index f1b09082..41524e40 100644 --- a/include/hgl/algorithm/traversal/breadth_first_search.hpp +++ b/include/hgl/algorithm/traversal/breadth_first_search.hpp @@ -46,12 +46,12 @@ namespace hgl::algorithm { /// | :-------- | :--- | :--- | /// | Result | Controls whether the algorithm builds and returns a search tree (`ret`) or evaluates purely for side effects (`noret`). | Must be a valid @ref hgl::algorithm::result_discriminator "result_discriminator" enum value. | /// | H | The type of the hypergraph being searched. | Must satisfy the [**c_hypergraph**](hgl_concepts.md#hgl-traits-c-hypergraph) concept. | -/// | PreVisitCallback | Type of the callable executed immediately before officially visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PreVisitCallback | Type of the callable executed immediately before visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | /// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | /// /// @param hypergraph The hypergraph to traverse. /// @param root_vertex_id The ID of the vertex to start the search from. If `no_root`, searches the entire hypergraph. -/// @param pre_visit Hook executed immediately before officially visiting the vertex. +/// @param pre_visit Hook executed immediately before visiting the vertex. /// @param post_visit Hook executed after all adjacent hyperedges and target vertices of the current node have been evaluated. /// @return A @ref hgl::algorithm::search_tree "search_tree" if `Result == ret`, otherwise nothing (`void`). /// @hideparams diff --git a/include/hgl/algorithm/traversal/depth_first_search.hpp b/include/hgl/algorithm/traversal/depth_first_search.hpp index d834e464..89e5b263 100644 --- a/include/hgl/algorithm/traversal/depth_first_search.hpp +++ b/include/hgl/algorithm/traversal/depth_first_search.hpp @@ -46,12 +46,12 @@ namespace hgl::algorithm { /// | :-------- | :--- | :--- | /// | Result | Controls whether the algorithm builds and returns a search tree (`ret`) or evaluates purely for side effects (`noret`). | Must be a valid @ref hgl::algorithm::result_discriminator "result_discriminator" enum value. | /// | H | The type of the hypergraph being searched. | Must satisfy the [**c_hypergraph**](hgl_concepts.md#hgl-traits-c-hypergraph) concept. | -/// | PreVisitCallback | Type of the callable executed immediately before officially visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PreVisitCallback | Type of the callable executed immediately before visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | /// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | /// /// @param hypergraph The hypergraph to traverse. /// @param root_vertex_id The ID of the vertex to start the search from. If `no_root`, searches the entire hypergraph. -/// @param pre_visit Hook executed immediately before officially visiting the vertex. +/// @param pre_visit Hook executed immediately before visiting the vertex. /// @param post_visit Hook executed after all adjacent hyperedges and target vertices of the current node have been evaluated. /// @return A @ref hgl::algorithm::search_tree "search_tree" if `Result == ret`, otherwise nothing (`void`). /// @hideparams diff --git a/include/hgl/algorithm/traversal/forward_search.hpp b/include/hgl/algorithm/traversal/forward_search.hpp index 8b35a6b0..1aa4e830 100644 --- a/include/hgl/algorithm/traversal/forward_search.hpp +++ b/include/hgl/algorithm/traversal/forward_search.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file hgl/algorithm/forward_search.hpp +/// @brief Concrete forward F-search traversals (F-BFS and F-DFS) for bf-directed hypergraphs. + #pragma once #include "hgl/algorithm/core.hpp" @@ -12,6 +15,46 @@ namespace hgl::algorithm { +/// @ingroup HGL-Algorithm +/// @brief Executes a Breadth-First F-Search (F-BFS) traversal over a bf-directed hypergraph. +/// +/// This algorithm implements F-reachability semantics using Breadth-First Search for BF-directed hypergraphs. +/// Unlike a standard backward traversal (where reaching a single head vertex is sufficient to traverse an +/// incoming hyperedge), a forward search (F-search) uses a blocking predicate. A hyperedge is only traversed, +/// and its tail vertices enqueued, after **all** of its head (destination) vertices have been visited. +/// +/// ### Example Usage +/// ```cpp +/// std::vector roots = { start_id_1, start_id_2 }; +/// auto search_tree = hgl::algorithm::forward_bfs(hypergraph, roots); // (1)! +/// +/// hgl::algorithm::forward_bfs( // (2)! +/// hypergraph, +/// roots, +/// [](const auto& node) { std::cout << "Pre-visit: " << node.vertex_id << '\n'; }, +/// [](const auto& node) { std::cout << "Post-visit: " << node.vertex_id << '\n'; } +/// ); +/// ``` +/// +/// 1\. Executes an F-BFS returning a search tree mapped to the components F-reachable from the specified roots. +/// +/// 2\. Executes an F-BFS purely for side-effects (callbacks) without allocating memory for a search tree. +/// +/// ### Template Parameters +/// | Parameter | Description | Constraint | +/// | :-------- | :--- | :--- | +/// | Result | Controls whether the algorithm builds and returns a search tree (`ret`) or evaluates purely for side effects (`noret`). | Must be a valid @ref hgl::algorithm::result_discriminator "result_discriminator" enum value. | +/// | H | The type of the hypergraph being searched. | Must satisfy the [**c_bf_directed_hypergraph**](hgl_concepts.md#hgl-traits-c-bf-directed-hypergraph) concept. | +/// | RootRange | The type of the container providing the initial roots to enqueue. | Must satisfy [**c_forward_range_of**](gl_concepts.md#gl-traits-c-forward-range-of) over the hypergraph's `id_type`. | +/// | PreVisitCallback | Type of the callable executed immediately before officially visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// +/// @param hypergraph The bf-directed hypergraph to traverse. +/// @param root_vertices A range of initial vertex IDs to start the search from. +/// @param pre_visit Hook executed immediately before officially visiting the vertex. +/// @param post_visit Hook executed after all adjacent hyperedges and target vertices of the current node have been evaluated. +/// @return A @ref hgl::algorithm::search_tree "search_tree" if `Result == ret`, otherwise nothing (`void`). +/// @hideparams template < result_discriminator Result = ret, traits::c_bf_directed_hypergraph H, @@ -53,6 +96,46 @@ result_type> forward_bfs( return stree; } +/// @ingroup HGL-Algorithm +/// @brief Executes a Depth-First F-Search (F-DFS) traversal over a bf-directed hypergraph. +/// +/// This algorithm implements F-reachability semantics using a stack-based Depth-First Search for BF-directed +/// hypergraphs. Unlike a standard backward traversal (where reaching a single head vertex is sufficient to traverse an +/// incoming hyperedge), a forward search (F-search) uses a blocking predicate. A hyperedge is only traversed, +/// and its tail vertices pushed to the stack, after **all** of its head (destination) vertices have been officially visited. +/// +/// ### Example Usage +/// ```cpp +/// std::vector roots = { start_id_1, start_id_2 }; +/// auto search_tree = hgl::algorithm::forward_dfs(hypergraph, roots); // (1)! +/// +/// hgl::algorithm::forward_dfs( // (2)! +/// hypergraph, +/// roots, +/// [](const auto& node) { std::cout << "Pre-visit: " << node.vertex_id << '\n'; }, +/// [](const auto& node) { std::cout << "Post-visit: " << node.vertex_id << '\n'; } +/// ); +/// ``` +/// +/// 1\. Executes an F-DFS returning a search tree mapped to the components F-reachable from the specified roots. +/// +/// 2\. Executes an F-DFS purely for side-effects (callbacks) without allocating memory for a search tree. +/// +/// ### Template Parameters +/// | Parameter | Description | Constraint | +/// | :-------- | :--- | :--- | +/// | Result | Controls whether the algorithm builds and returns a search tree (`ret`) or evaluates purely for side effects (`noret`). | Must be a valid @ref hgl::algorithm::result_discriminator "result_discriminator" enum value. | +/// | H | The type of the hypergraph being searched. | Must satisfy the [**c_bf_directed_hypergraph**](hgl_concepts.md#hgl-traits-c-bf-directed-hypergraph) concept. | +/// | RootRange | The type of the container providing the initial roots to enqueue. | Must satisfy [**c_forward_range_of**](gl_concepts.md#gl-traits-c-forward-range-of) over the hypergraph's `id_type`. | +/// | PreVisitCallback | Type of the callable executed immediately before officially visiting a vertex. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// | PostVisitCallback | Type of the callable executed after all adjacent elements are evaluated. | Must be one of:
- A `(const search_node&) -> void` callable
- An @ref hgl::algorithm::empty_callback "empty_callback" | +/// +/// @param hypergraph The bf-directed hypergraph to traverse. +/// @param root_vertices A range of initial vertex IDs to start the search from. +/// @param pre_visit Hook executed immediately before officially visiting the vertex. +/// @param post_visit Hook executed after all adjacent hyperedges and target vertices of the current node have been evaluated. +/// @return A @ref hgl::algorithm::search_tree "search_tree" if `Result == ret`, otherwise nothing (`void`). +/// @hideparams template < result_discriminator Result = ret, traits::c_bf_directed_hypergraph H, From 805d2425c8fbeefb699a3a098a4148ad592849ad Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 02:04:56 +0200 Subject: [PATCH 19/28] algo umbrella header docs --- include/hgl/algorithm.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/hgl/algorithm.hpp b/include/hgl/algorithm.hpp index af8e374a..cd0dcfa7 100644 --- a/include/hgl/algorithm.hpp +++ b/include/hgl/algorithm.hpp @@ -2,6 +2,9 @@ // This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). // Licensed under the MIT License. See the LICENSE file in the project root for full license information. +/// @file gl/algorithm.hpp +/// @brief Includes all algorithm-related headers, providing a comprehensive set of hypergraph algorithms for various hypergraph types and configurations. + #pragma once // clang-format off From 5afec4fadea78e211a9aebc6f9b62e92669c3d52 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 12:09:11 +0200 Subject: [PATCH 20/28] hgl quick start page --- docs/gl/quick_start.md | 3 ++ docs/hgl/quick_start.md | 89 ++++++++++++++++++++++++++++++++++ include/hgl/impl/impl_tags.hpp | 4 +- 3 files changed, 94 insertions(+), 2 deletions(-) diff --git a/docs/gl/quick_start.md b/docs/gl/quick_start.md index 2394586c..e2f4c9e3 100644 --- a/docs/gl/quick_start.md +++ b/docs/gl/quick_start.md @@ -71,6 +71,9 @@ Path: 0 -> 2 -> 3 -> 1 -> 4 ### Understanding the Code - **Traits (`gl::list_graph_traits`):** CPP-GL relies heavily on template abstraction. Instead of passing multiple arguments to the `gl::graph` constructor, you pass a single *Traits* struct as its template parameter. This strictly dictates whether the graph uses an adjacency list or matrix, if it is directed, what custom properties (like `gl::weight_property`) exist on its elements and what id type is used for its elements. + - **Property Access (`->weight`):** Adding an edge returns a descriptor handle. You access the specific payload fields associated with that edge by using the overloaded `->` operator, ensuring highly performant and type-safe data manipulation. + - **Algorithm Separation:** Search engines and algorithms (like `dijkstra_shortest_paths`) exist entirely outside the graph class. They accept the graph as a `const` reference, mathematically guaranteeing that algorithms will never accidentally mutate your topology. + - **Formatting (`gl::io::range_formatter`):** The library includes lightweight utility formatters so you can easily stream sequences, paths, and standard ranges directly to the console with complete control over delimiters and brackets. diff --git a/docs/hgl/quick_start.md b/docs/hgl/quick_start.md index 05cf8c1f..e9b8e1b9 100644 --- a/docs/hgl/quick_start.md +++ b/docs/hgl/quick_start.md @@ -1 +1,90 @@ # Quick Start + +This guide provides a rapid introduction to the **HGL** (Hypergraph) module. In just a few minutes, you will define a hypergraph, populate it with vertices and weighted hyperedges, and run a Breadth-First B-Search (B-BFS) algorithm to evaluate reachability across its generalized graph topology. + +## Basic Usage Example + +The following C++ program demonstrates the standard workflow of the CPP-GL hypergraph library: + +1. **Defining Traits:** Selecting the directionality model, memory layout, and payload types. +2. **Building Topology:** Adding vertices and defining hyperedges that map sets of tail vertices to sets of head vertices. +3. **Execution:** Running a decoupled B-reachability traversal algorithm. +4. **Reconstruction & Output:** Querying the resulting search tree and formatting the hypergraph natively to the standard output. + +```cpp +#include // (1)! +#include +#include + +#include +#include + +int main() { + using traits_t = hgl::hypergraph_traits< // (2)! + hgl::bf_directed_t, + gl::name_property, + gl::weight_property, + hgl::impl::list_t>; + + hgl::hypergraph hg(4); // (3)! + + auto v0 = hg.vertex(0); v0->name = "Source"; // (4)! + auto v1 = hg.vertex(1); v1->name = "Router A"; + auto v2 = hg.vertex(2); v2->name = "Router B"; + auto v3 = hg.vertex(3); v3->name = "Target"; + + hg.add_hyperedge({0}, {1, 2})->weight = 10.5; // (5)! + hg.add_hyperedge({v1, v2}, {v3})->weight = 5.0; + + std::vector roots = {0u}; + auto search_tree = hgl::algorithm::backward_bfs(hg, roots); // (7)! + + if (hgl::algorithm::is_reachable(search_tree, 3u)) // (8)! + std::cout << "Target is B-reachable from the Source.\n\n"; + else + std::cout << "Target is NOT B-reachable from the Source.\n\n"; + + std::cout << hgl::io::verbose << hgl::io::with_properties; // (9)! + std::cout << "Hypergraph Topology:\n" << hg << '\n'; + + return 0; +} +``` + +1. Include the necessary core hypergraph, algorithm, and I/O headers. +2. Define the hypergraph's memory layout (bidirectional incidence list), directionality, and element payloads via a single traits structure.
**NOTE:** Alternatively, you can use one of the provided [generic hypergraph type aliases](../cpp-gl/group__HGL-Core.md#public-types). +3. Instantiate the generic `hypergraph` class, pre-allocating it with 4 vertices (IDs 0 through 3), using the defined traits. +4. Retrieve stable vertex descriptors using `.vertex(id)` to assign property payloads safely. +5. BF-directed hyperedges connect a set of *tail* vertices to a set of *head* vertices. An edge can be added using vertex IDs or descriptors.
**NOTE:** You can alternatively instantiate the hypergraph with a given number of hyperedges and simply *bind* vertices to them using the dedicated binding methods.
**IMPORTANT:** Property Access Safety
Assigning properties inline via the returned descriptor is safe here because the temporary descriptor is immediately discarded, meaning no dangling references are kept. The same bahaviour can be achieved using the `add_hyperedge_with` methods. +6. Execute a Breadth-First Backward Search (B-BFS) to compute B-reachability semantics from the source vertex. +7. Query the resulting search tree to validate if the Target vertex was successfully reached. +8. Standard GL stream manipulators inject persistent formatting state to output the hypergraph's structure in a verbose format, including the element properties. + +**Output:** + +```text +Target is B-reachable from the Source. + +Hypergraph Topology: +type: BF-directed, |V| = 4, |E| = 2 +vertices: + - [id: 0 | "Source"] + - [id: 1 | "Router A"] + - [id: 2 | "Router B"] + - [id: 3 | "Target"] +hyperedges: + - [id: 0 | tail: {0}, head: {1, 2} | 10.5] + - [id: 1 | tail: {1, 2}, head: {3} | 5] +``` + +## Understanding the Code + +- **Generic Traits:** Similar to the standard GL module, the HGL module relies entirely on template abstraction. By passing a [**hgl::hypergraph_traits**](../cpp-gl/structhgl_1_1hypergraph__traits.md) struct to the [**hgl::hypergraph**](../cpp-gl/classhgl_1_1hypergraph.md) constructor, you dictate whether the hypergraph is undirected or BF-directed ([**hgl::bf_directed_t**](../cpp-gl/structhgl_1_1bf__directed__t.md)), what custom properties exist on vertices and hyperedges, and which underlying memory architecture it utilizes (e.g., [**hgl::impl::list_t**](../cpp-gl/structhgl_1_1impl_1_1list__t.md)). + +- **Generalized Incidence:** Unlike standard graphs where an edge connects exactly two vertices, hyperedges connect sets of vertices of arbitrary sizes. In a BF-directed hypergraph, `add_hyperedge` accepts an iterable range of *tail* (source) vertices and *head* (destination) vertices and represents a single connection from all tail vertices to all head vertices simultaneously. + +- **Property Access:** Because the internal memory can shift during insertion, it is best practice to pre-allocate memory for vertices and hyperedges in the hypergraph's constructor and then fetch them using accessors like `.vertex(id)`. However, you access specific payload fields directly, e.g. through the overloaded `->` operator on the returned descriptor. + +- **Algorithm Separation:** Algorithms exist entirely outside the hypergraph class. In this example, [**hgl::algorithm::backward_bfs**](../cpp-gl/group__HGL-Algorithm.md#function-backward_bfs) applies strict B-reachability rules: a hyperedge is only fully traversed (and its head vertices discovered) if all of its tail vertices have already been visited. The [**hgl::algorithm::is_reachable**](../cpp-gl/group__HGL-Algorithm.md#function-is_reachable) utility then checks the resulting search tree to evaluate whether the Target vertex is B-reachable in the defined hypergraph in $O(1)$ time. + +- **Formatting:** The HGL module provides custom stream manipulators (e.g. [**hgl::io::verbose**](../cpp-gl/group__HGL-IO.md#variable-verbose), [**hgl::io::with_properties**](../cpp-gl/group__HGL-IO.md#variable-with_properties)) allowing you to effortlessly format or serialize the complex topology and payloads straight to `std::cout` or other I/O streams. diff --git a/include/hgl/impl/impl_tags.hpp b/include/hgl/impl/impl_tags.hpp index 09db9890..aa74fe71 100644 --- a/include/hgl/impl/impl_tags.hpp +++ b/include/hgl/impl/impl_tags.hpp @@ -23,7 +23,7 @@ namespace hgl::impl { /// ### Layout Implications /// The chosen layout significantly impacts memory usage and query performance: /// -/// - @ref hgl::impl::bidirectional_t "bidirectional_t" (Default): Maintains two internal lists (vertex-to-hyperedges and hyperedge-to-vertices). Provides optimal $O(1)$ degree/size lookups and fast traversals in both directions at the cost of doubled memory consumption. +/// - @ref hgl::impl::bidirectional_t "bidirectional_t" (Default): Maintains two internal lists (vertex-to-hyperedges and hyperedge-to-vertices). Provides optimal \f$O(1)\f$ degree/size lookups and fast traversals in both directions at the cost of doubled memory consumption. /// - @ref hgl::impl::vertex_major_t "vertex_major_t": Maintains only a vertex-to-hyperedges list. Highly memory efficient and fast for querying vertex degrees or incident hyperedge sets, but querying hyperedge sizes or incident vertex setss requires expensive full-hypergraph scans. /// - @ref hgl::impl::hyperedge_major_t "hyperedge_major_t": Maintains only a hyperedge-to-vertices list. Memory efficient and fast for hyperedge-centric queries, but querying vertex degrees or incident hyperedge sets requires full-hypergraph scans. /// @@ -52,7 +52,7 @@ struct list_t { /// ### Layout Implications /// The chosen layout significantly impacts memory usage and query performance: /// -/// - @ref hgl::impl::bidirectional_t "bidirectional_t" (Default): Maintains two internal flattened lists (vertex-to-hyperedges and hyperedge-to-vertices). Provides optimal $O(1)$ degree/size lookups and fast traversals in both directions at the cost of doubled memory consumption. +/// - @ref hgl::impl::bidirectional_t "bidirectional_t" (Default): Maintains two internal flattened lists (vertex-to-hyperedges and hyperedge-to-vertices). Provides optimal \f$O(1)\f$ degree/size lookups and fast traversals in both directions at the cost of doubled memory consumption. /// - @ref hgl::impl::vertex_major_t "vertex_major_t": Maintains only a vertex-to-hyperedges flattened list. Highly memory efficient and fast for querying vertex degrees or incident hyperedge sets, but querying hyperedge sizes or incident vertex sets requires expensive full-graph scans. /// - @ref hgl::impl::hyperedge_major_t "hyperedge_major_t": Maintains only a hyperedge-to-vertices flattened list. Memory efficient and fast for hyperedge-centric queries, but querying vertex degrees or incident hyperedge sets requires full-graph scans. /// From 12ef265e18aa666426298f1a1aca3a5f83a2a2b6 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 13:32:16 +0200 Subject: [PATCH 21/28] architecture page --- docs/gl/architecture.md | 4 +- docs/hgl/architecture.md | 256 +++++++++++++++++++++ docs/img/doc/dark/bfdir-hypergraph.svg | 29 +++ docs/img/doc/dark/hypergraph-examples.svg | 51 ++++ docs/img/doc/dark/undir-hypergraph.svg | 22 ++ docs/img/doc/light/bfdir-hypergraph.svg | 29 +++ docs/img/doc/light/hypergraph-examples.svg | 51 ++++ docs/img/doc/light/undir-hypergraph.svg | 22 ++ include/hgl/hypergraph.hpp | 8 +- 9 files changed, 466 insertions(+), 6 deletions(-) create mode 100644 docs/img/doc/dark/bfdir-hypergraph.svg create mode 100644 docs/img/doc/dark/hypergraph-examples.svg create mode 100644 docs/img/doc/dark/undir-hypergraph.svg create mode 100644 docs/img/doc/light/bfdir-hypergraph.svg create mode 100644 docs/img/doc/light/hypergraph-examples.svg create mode 100644 docs/img/doc/light/undir-hypergraph.svg diff --git a/docs/gl/architecture.md b/docs/gl/architecture.md index 50fd74c2..3a661c52 100644 --- a/docs/gl/architecture.md +++ b/docs/gl/architecture.md @@ -6,7 +6,7 @@ The **GL (Graph Library)** module is engineered around a singular philosophy: pr This section explores the core architectural decisions of the GL module: -- [Core Concepts](#core-concepts): Learn how the gl::graph template operates, the difference between IDs and descriptors, and how to navigate topologies. +- [Core Concepts](#core-concepts): Learn how the `gl::graph` template operates, the difference between IDs and descriptors, and how to navigate topologies. - [Graph Representation Models](#graph-representation-models): Understand the diverse memory models available and their performance characteristics. - [Properties & Custom Data](#properties-custom-data): Discover how to inject arbitrary data directly into your graph elements with strict type safety. @@ -82,7 +82,7 @@ When you add new vertices or edges to the graph, the library guarantees that exi > > Because a property-less vertex descriptor is essentially just a wrapper around an ID, there is zero overhead to using it instead of a raw ID, and it will never invalidate when new elements are added. > - > This means that performing operations like the following is completely safe: + > This means that performing operations like the following are completely safe: > ```cpp > const auto v1 = graph.add_vertex(); > const auto v2 = graph.add_vertex(); diff --git a/docs/hgl/architecture.md b/docs/hgl/architecture.md index 81e67ccf..a19e91c3 100644 --- a/docs/hgl/architecture.md +++ b/docs/hgl/architecture.md @@ -1 +1,257 @@ # Architecture & Basics + +## HGL Module Overview + +While standard graphs define edges as simple connections between exactly two vertices, **hypergraphs** generalize this concept. In a hypergraph, a hyperedge can connect an arbitrary number of vertices simultaneously. + +While higher-order connections can technically be modeled using standard bipartite graphs (a technique known as *star expansion*, where standard vertices are used to represent both the actual data elements and the connections between them), doing so pollutes the domain model. It forces developers to introduce artificial labels and boilerplate logic to distinguish between "true" vertices and "connection" vertices. This approach is not true to the natural structure of the relations and fundamentally complicates traversals, degree calculations, and property management. + +The **HGL (Hypergraph Library)** module solves this by treating *both* vertices and hyperedges as true, first-class topological entities. By natively supporting n-ary relationships, HGL is exceptionally powerful for modeling complex, multi-way interactions directly as they exist in the real world—such as chemical reaction networks (multiple reactants forming multiple products), collaboration networks (multiple authors co-authoring a single research paper), or complex set systems and multi-way database joins. + +This section explores the core architectural decisions of the HGL module: + +- [Shared Infrastructure with GL](#shared-infrastructure-with-gl): Understand the strict unidirectional dependency and structural relationship between the GL and HGL modules. +- [Core Concepts](#core-concepts): Learn how the `hgl::hypergraph` template operates, how hyperedges differ mathematically from standard pairwise edges, and how to navigate generalized incidence. +- [Representation & Layouts](#representation-layouts): Discover the diverse memory models available for hypergraphs and how layout orientation impacts query performance. + +--- + +## Shared Infrastructure with GL + +The HGL module is built upon the exact same zero-cost abstraction philosophy as the standard GL module. It strictly utilizes C++20 concepts and template metaprogramming, meaning that all configuration is resolved at compile time. + +Crucially, **the HGL module is built strictly on top of the GL module.** It acts as an architectural extension with a strict unidirectional dependency. HGL heavily relies on GL and directly reuses its core infrastructure—including ID types, type traits and concepts, I/O utilities, and underlying contiguous data structures (like [**gl::flat_jagged_vector**](../cpp-gl/classgl_1_1flat__jagged__vector.md) and [**gl::flat_matrix**](../cpp-gl/classgl_1_1flat__matrix.md)). Therefore, if you are familiar with the GL module, the HGL module's design language will feel immediately natural. + +Conversely, the GL module remains completely standalone and entirely unaware of the HGL module or any of its components. This strict separation ensures that projects requiring only standard graph capabilities can utilize the GL module without incurring any compile-time dependencies, structural complexity, or overhead from the generalized hypergraph extensions. + +--- + +## Core Concepts + +### The `hgl::hypergraph` Template + +The [**hgl::hypergraph**](../cpp-gl/classhgl_1_1hypergraph.md) class is the core element of the HGL module. Its entire structural and behavioral identity is dictated by the [**hgl::hypergraph_traits**](../cpp-gl/structhgl_1_1hypergraph__traits.md) struct, which configures: + +1. **Directionality**: Undirected vs. BF-Directed. +2. **Vertex Properties**: The data payload attached to each vertex. +3. **Hyperedge Properties**: The data payload attached to each hyperedge. +4. **Implementation Tag**: The underlying implementation model of the hypergraph. + - This tag dictates the data structure used for the incidence representation (e.g., Incidence List or Incidence Matrix). + - The implementation tags can be further specialized with: + - **Layout Tag:** The memory layout of the data structure (e.g., Bidirectional (only for list models), Vertex-Major, or Hyperedge-Major). + - **Id Type:** The integral type used for internal indexing (defaults to `std::uint32_t`). + +To reduce boilerplate, the library provides several generic type aliases for the most common configurations: + +- Based on the directional tag: + + - [**hgl::undirected_hypergraph**](../cpp-gl/group__HGL-Core.md#typedef-undirected_hypergraph) + - [**hgl::bf_directed_hypergraph**](../cpp-gl/group__HGL-Core.md#typedef-bf_directed_hypergraph) + +- Based on the implementation tag: + + - [**hgl::list_hypergraph**](../cpp-gl/group__HGL-Core.md#typedef-list_hypergraph) + - [**hgl::flat_list_hypergraph**](../cpp-gl/group__HGL-Core.md#typedef-flat_list_hypergraph) + - [**hgl::matrix_hypergraph**](../cpp-gl/group__HGL-Core.md#typedef-matrix_hypergraph) + - [**hgl::flat_matrix_hypergraph**](../cpp-gl/group__HGL-Core.md#typedef-flat_matrix_hypergraph) + +### Hypergraph Directionality + +The mathematical structure of the [**hgl::hypergraph**](../cpp-gl/classhgl_1_1hypergraph.md) class is defined by the injected directional tag: + +- [**hgl::undirected_t**](../cpp-gl/structhgl_1_1undirected__t.md) : In an undirected hypergraph, a hyperedge is simply a mathematical *set* of incident vertices. There is no distinction between the "source" or "destination" of the connection. + +- [**hgl::bf_directed_t**](../cpp-gl/structhgl_1_1bf__directed__t.md) : Represents a **Backward-Forward directed** hypergraph. In this model, each hyperedge maps a distinct set of *tail* (source/backward) vertices to a distinct set of *head* (destination/forward) vertices. This model is extremely useful in scenarios like chemical reactions (reactants to products) or logic gates (multiple inputs to multiple outputs). + +
+ +![Hypergraph Examples Light](../img/doc/light/hypergraph-examples.svg#only-light){: width="700" } +![Hypergraph Examples Dark](../img/doc/dark/hypergraph-examples.svg#only-dark){: width="700" } + +
+ +### IDs vs. Descriptors + +Just like the GL module, HGL exposes a dual API utilizing raw **IDs** (`id_type`) and lightweight **Descriptors** ([**vertex_descriptor**](../cpp-gl/group__HGL-Core.md#typedef-vertex_descriptor) and [**hyperedge_descriptor**](../cpp-gl/classhgl_1_1hyperedge__descriptor.md)). + +In contrast to the GL module, both vertex and hyperedge descriptors bind only the element's ID and an optional reference to its property payload. + +> [!NOTE] API Performance +> +> If your algorithms only require topological information (like connectivity or degrees), prefer the ID-based API to avoid the slight overhead of constructing descriptors and binding property references. + +### Descriptor Lifetimes and Invalidation + +Because HGL prioritizes fast, cache-friendly contiguous memory allocations, operations that modify the hypergraph's topology may invalidate previously returned descriptors and IDs. Understanding the invalidation rules is critical for writing safe annd stable hypergraph mutating operations. + +#### 1. Insertion Stability (Adding Elements) + +When you add new vertices or hyperedges to the hypergraph, the library guarantees that existing IDs remain perfectly stable. However, descriptor stability depends entirely on your property configuration: + +- **Without Properties:** If your hypergraph elements carry no properties (use `gl::empty_properties`), descriptors are **completely insertion-stable**. + + > [!TIP] + > + > Because a **property-less** descriptors are essentially just a wrapper around an ID, there is zero overhead to using it instead of a raw ID, and it will never invalidate when new elements are added. + > + > This means that performing operations like the following are completely safe: + > ```cpp + > const auto v1 = hg.add_vertex(); + > const auto v2 = hg.add_vertex(); + > const auto e1 = hg.add_hyperedge({v1, v2}); + > const auto v3 = hg.add_vertex(); + > const auto e2 = hg.add_hyperedge(); + > hg.bind({v2, v3}, e2); + > ``` + +- **With Properties:** If your hypergraph elements carry custom properties, adding new elements may trigger a memory reallocation in the underlying property storage containers. While the IDs remain valid, the memory references stored inside existing descriptors will dangle. Therefore, property-bound descriptors are **not insertion-stable**. + +#### 2. Erasure Stability (Removing Elements) + +Because removing elements causes the internal memory to shift and tightly pack to prevent fragmentation, HGL elements are **not erase-stable**. The invalidation rules are symmetric for both element types: + +- **Removing a Vertex:** Invalidates all vertex IDs and vertex descriptors for vertices with higher IDs (they shift down). It also invalidates references to all properties associated with the vertices whose IDs shifted as a result of the removal operation. +- **Removing a Hyperedge:** Invalidates all hyperedge IDs and hyperedge descriptors for hyperedges with higher IDs (they shift down). It also invalidates references to all properties associated with the hyperedges whose IDs shifted as a result of the removal operation. + +> [!IMPORTANT] Best Practice +> +> If you need to store references to hypergraph elements long-term, store the stable element IDs (`id_type`) assuming no erasures occur. Only fetch fresh descriptors via the available getters (e.g., `hg.vertex(id)` or `hg.hyperedge(id)`) at the moment you need to access or mutate the properties or once the entire hypergraph's structure has been initialized. + +### Incidence Operations + +Because hyperedges connect sets of vertices rather than exactly two endpoints, constructing the topology requires generalized incidence operations. You can either construct a hyperedge and bind vertices to it incrementally, or supply the entire set of incident vertices upfront during creation. + +#### Undirected Hypergraphs + +In an undirected hypergraph, you simply provide a collection of vertices that the hyperedge encompasses: + +```cpp +auto e1 = hg.add_hyperedge({v0, v1, v2}); // (1)! + +auto e2 = hg.add_hyperedge(); // (2)! +hg.bind(v1, e2); // (3)! +hg.bind({v2, v3}, e2); // (4)! + +hg.unbind(v1, e2); // (5)! +``` + +1. Creates a hyperedge and instantly connects it to `v0`, `v1`, and `v2` +2. Creates an empty hyperedge that needs to be bound separately. +3. Binds a single vertex to the given hyperedge. +4. Binds a range of vertices to the specified hyperedge. +5. Unbinds the vertex from the hyperedge. + +#### BF-Directed Hypergraphs + +In a BF-directed hypergraph, you must explicitly distinguish between the *tail* (source) vertices and *head* (destination) vertices: + +```cpp +auto e1 = hg.add_hyperedge({v0}, {v1, v2}); // (1)! + +auto e2 = hg.add_hyperedge(); // (2)! +hg.bind_tail(v1, e2); // (3)! +hg.bind_tail(v2, e2); +hg.bind_head({v3, v4}, e2); // (4)! + +hg.unbind(v1, e2); // (5)! +hg.unbind(v3, e2); +``` + +1. Creates a hyperedge where `v0` is the *tail* (source) vertex and the vertices `v1` and `v2` are the *head* (destinations). +2. Creates an empty hyperedge that needs to be bound separately. +3. Binds `v1` and `v2` to the *tail* of the hyperedge individually. +4. Binds `v3` and `v4` to the *head* of the hyperedge simultaneously. +5. Unbinds the given vertices from the hyperedge, regardless of whether they are tail-bound or head-bound.
**NOTE:** A given vertex cannot belong to both tail and head of a single hyperedge at the same time. + +### Basic Iteration + +The `hgl::hypergraph` exposes standard ``-compatible views for generalized traversals: + +```cpp +for (auto vertex : hg.vertices()) { // (1)! + for (auto hyperedge : hg.incident_hyperedges(vertex)) { // (1)! + for (auto adjacent_v : hg.incident_vertices(hyperedge)) { // (2)! + // ... + } + } +} +``` + +1. Iterates over all vertices in the hypergraph. +2. Iterates over all hyperedges connected to a specific vertex. +3. Iterates over all vertices contained within a specific hyperedge. + +While iterating over incident vertices or hyperedges is possible for both undirected and BF-directed hypergraphs, the BF-directed hypergraphs expose additional methods that allow you to iterate over tail-bound or head-bound elements specifically: + +```cpp +for (auto tail_v : hg.tail(hyperedge)) { // (1)! + // ... +} + +for (auto in_hyperedge : hg.in_hyperedges(vertex)) { // (2)! + // ... +} +``` + +1. Iterates over only the *tail* vertices of a BF-directed hyperedge. +2. Iterates over only the *incoming* hyperedges of a vertex (such that the vertex belongs to the *head* of the hyperedge). + +--- + +## Representation & Layouts + +Because hypergraphs are generalizations of graphs, their topological data cannot be stored as simple arrays of pairs. Instead, CPP-GL relies on **Incidence Models**. HGL categorizes its memory representations via `ImplTag` and strictly controls orientation via `LayoutTag`. + +### Incidence Models (Standard & Flat) + +- **Incidence Lists** ([`list_t`](../cpp-gl/structhgl_1_1impl_1_1list__t.md) / [`flat_list_t`](../cpp-gl/structhgl_1_1impl_1_1flat__list__t.md)): Maps elements to dynamic jagged lists. Highly space-efficient for sparse hypergraphs, as memory is only allocated for existing incidence relations. +- **Incidence Matrices** ([`matrix_t`](../cpp-gl/structhgl_1_1impl_1_1matrix__t.md) / [`flat_matrix_t`](../cpp-gl/structhgl_1_1impl_1_1flat__matrix__t.md)): Allocates a full $|V| \times |E|$ 2D grid. Memory intensive ($O(|V| \times |E|)$), but allows instant $O(1)$ verification if a given vertex belongs to a given hyperedge. + +### Layout Tags (Orientation) + +The `LayoutTag` dictates the *primary indexing dimension* of the incidence structure, massively impacting query speeds and memory footprints: + +- [**bidirectional_t**](../cpp-gl/structhgl_1_1impl_1_1bidirectional__t.md): Maintains *two* internal mappings (Vertex-to-Hyperedges AND Hyperedge-to-Vertices). Offers optimal $O(1)$ access for both vertex degrees and hyperedge sizes, at the cost of doubled memory consumption. *(Compatible only with Incidence Lists)*. +- [**vertex_major_t**](../cpp-gl/structhgl_1_1impl_1_1vertex__major__t.md): The primary index is the Vertex. Querying the hyperedges connected to a vertex is instantaneous, but finding which vertices belong to a hyperedge requires an expensive full-graph scan. +- [**hyperedge_major_t**](../cpp-gl/structhgl_1_1impl_1_1hyperedge__major__t.md): The primary index is the Hyperedge. Querying the vertices within a hyperedge is instantaneous, but finding a vertex's degree requires a full-graph scan. + +> [!NOTE] Matrices and Asymmetry +> +> Incidence matrices fundamentally represent an asymmetric $|V| \times |E|$ mathematical grid. Therefore, matrix representations strictly require an asymmetric layout (`vertex_major_t` or `hyperedge_major_t`), mapping rows to the major element and columns to the minor element. + +### Operation Complexity Tables + +Because performance is heavily dictated by the combination of the Representation Model (List vs. Matrix) and the Layout Tag (Bidirectional vs. Major), the complexities are grouped accordingly. + +*Note: In the tables below, $|V|$ is vertex count, $|E|$ is hyperedge count, $deg(v)$ is vertex degree, and $|e|$ is hyperedge size (number of incident vertices).* + +#### 1. Incidence Lists (`list_t` / `flat_list_t`) + +| Operation | `bidirectional_t` | `vertex_major_t` | `hyperedge_major_t` | +| :--------------------------------------- | :------------------------------- | :------------------------------- | :------------------------------- | +| **Check Incidence** $(v, e)$ | $O(\min(deg(v), \vert e \vert))$ | $O(deg(v))$ | $O(\vert e \vert)$ | +| **Iterate Incident Hyperedges** of $v$ | $O(deg(v))$ | $O(deg(v))$ | $O(\vert V \vert + \sum \vert e \vert)$ | +| **Iterate Incident Vertices** of $e$ | $O(\vert e \vert)$ | $O(\vert E \vert + \sum deg(v))$ | $O(\vert e \vert)$ | +| **Get Degree** of $v$ | $O(1)$ | $O(1)$ | $O(\vert V \vert + \sum \vert e \vert)$ | +| **Get Size** of $e$ | $O(1)$ | $O(\vert E \vert + \sum deg(v))$ | $O(1)$ | +| **Memory Footprint (Topology)** | $O(2 \times \sum deg(v))$ | $O(\sum deg(v))$ | $O(\sum \vert e \vert)$ | + +#### 2. Incidence Matrices (`matrix_t` / `flat_matrix_t`) + +| Operation | `vertex_major_t` (Rows = V, Cols = E) | `hyperedge_major_t` (Rows = E, Cols = V) | +| :--------------------------------------- | :------------------------------------ | :--------------------------------------- | +| **Check Incidence** $(v, e)$ | $O(1)$ | $O(1)$ | +| **Iterate Incident Hyperedges** of $v$ | $O(\vert E \vert)$ (Scan Row) | $O(\vert E \vert)$ (Scan Column) | +| **Iterate Incident Vertices** of $e$ | $O(\vert V \vert)$ (Scan Column) | $O(\vert V \vert)$ (Scan Row) | +| **Get Degree** of $v$ | $O(\vert E \vert)$ | $O(\vert E \vert)$ | +| **Get Size** of $e$ | $O(\vert V \vert)$ | $O(\vert V \vert)$ | +| **Memory Footprint (Topology)** | $O(\vert V \vert \times \vert E \vert)$ | $O(\vert V \vert \times \vert E \vert)$ | + +### Choosing the Representation & Layout + +Selecting the correct combination ensures your application hits peak performance: + +- Use **`bidirectional_t`** Lists (the default) for general-purpose hypergraphs where you frequently query in both directions (e.g., "what nodes are in this hyperedge?" AND "what hyperedges is this node in?"). +- Use **`hyperedge_major_t`** Lists when dealing with massive datasets where memory is tight, and your algorithms are strictly edge-centric (e.g., simulating isolated hyperedge reactions). +- Use **`matrix_t`** when your hypergraph is extremely dense, hyperedge sizes are close to $|V|$, and absolute instant incidence verification `are_incident(v, e)` is the algorithmic bottleneck. +``` diff --git a/docs/img/doc/dark/bfdir-hypergraph.svg b/docs/img/doc/dark/bfdir-hypergraph.svg new file mode 100644 index 00000000..2901e39f --- /dev/null +++ b/docs/img/doc/dark/bfdir-hypergraph.svg @@ -0,0 +1,29 @@ + +v +1 +v +2 +v +3 +v +4 +v +5 + + + + + + + + + + + +e +1 +e +2 +e +3 + diff --git a/docs/img/doc/dark/hypergraph-examples.svg b/docs/img/doc/dark/hypergraph-examples.svg new file mode 100644 index 00000000..ac96a185 --- /dev/null +++ b/docs/img/doc/dark/hypergraph-examples.svg @@ -0,0 +1,51 @@ + +UndirectedHypergraph + + + + +v +1 +v +2 +v +3 +v +4 +e +1 +e +2 +e +3 +e +4 +BF-DirectedHypergraph +v +1 +v +2 +v +3 +v +4 +v +5 + + + + + + + + + + + +e +1 +e +2 +e +3 + diff --git a/docs/img/doc/dark/undir-hypergraph.svg b/docs/img/doc/dark/undir-hypergraph.svg new file mode 100644 index 00000000..cb1b7592 --- /dev/null +++ b/docs/img/doc/dark/undir-hypergraph.svg @@ -0,0 +1,22 @@ + + + + + +v +1 +v +2 +v +3 +v +4 +e +1 +e +2 +e +3 +e +4 + diff --git a/docs/img/doc/light/bfdir-hypergraph.svg b/docs/img/doc/light/bfdir-hypergraph.svg new file mode 100644 index 00000000..94c5598c --- /dev/null +++ b/docs/img/doc/light/bfdir-hypergraph.svg @@ -0,0 +1,29 @@ + +v +1 +v +2 +v +3 +v +4 +v +5 + + + + + + + + + + + +e +1 +e +2 +e +3 + diff --git a/docs/img/doc/light/hypergraph-examples.svg b/docs/img/doc/light/hypergraph-examples.svg new file mode 100644 index 00000000..11016d78 --- /dev/null +++ b/docs/img/doc/light/hypergraph-examples.svg @@ -0,0 +1,51 @@ + +UndirectedHypergraph + + + + +v +1 +v +2 +v +3 +v +4 +e +1 +e +2 +e +3 +e +4 +BF-DirectedHypergraph +v +1 +v +2 +v +3 +v +4 +v +5 + + + + + + + + + + + +e +1 +e +2 +e +3 + diff --git a/docs/img/doc/light/undir-hypergraph.svg b/docs/img/doc/light/undir-hypergraph.svg new file mode 100644 index 00000000..76532ce3 --- /dev/null +++ b/docs/img/doc/light/undir-hypergraph.svg @@ -0,0 +1,22 @@ + + + + + +v +1 +v +2 +v +3 +v +4 +e +1 +e +2 +e +3 +e +4 + diff --git a/include/hgl/hypergraph.hpp b/include/hgl/hypergraph.hpp index 0438b7f7..d0a9f55c 100644 --- a/include/hgl/hypergraph.hpp +++ b/include/hgl/hypergraph.hpp @@ -1584,7 +1584,7 @@ class hypergraph final { return this->_impl.out_degree_map(this->_n_vertices); } - /// @brief Retrieves all incoming (head-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in E(e)\}\f$). + /// @brief Retrieves all incoming (head-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in H(e)\}\f$). /// @param vertex_id The vertex ID. /// @return A view representing the set of incoming hyperedges. /// @throws std::invalid_argument If the vertex ID is invalid. @@ -1595,7 +1595,7 @@ class hypergraph final { | std::views::transform(this->_create_hyperedge_descriptor()); } - /// @brief Retrieves all incoming (head-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in E(e)\}\f$). + /// @brief Retrieves all incoming (head-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in H(e)\}\f$). /// @param vertex The vertex descriptor. /// @return A view representing the set of incoming hyperedges. /// @throws std::invalid_argument If the vertex descriptor is invalid. @@ -1605,7 +1605,7 @@ class hypergraph final { return this->in_hyperedges(vertex.id()); } - /// @brief Retrieves IDs of all incoming (head-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in E(e)\}\f$). + /// @brief Retrieves IDs of all incoming (head-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in H(e)\}\f$). /// @param vertex_id The vertex ID. /// @return A view representing the set of incoming hyperedge IDs. /// @throws std::invalid_argument If the vertex ID is invalid. @@ -1616,7 +1616,7 @@ class hypergraph final { return this->_impl.in_hyperedges(vertex_id); } - /// @brief Retrieves IDs of all incoming (head-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in E(e)\}\f$). + /// @brief Retrieves IDs of all incoming (head-bound) hyperedges of a vertex in a *BF-directed* hypergraph (\f$\{e \in E : v \in H(e)\}\f$). /// @param vertex The vertex descriptor. /// @return A view representing the set of incoming hyperedge IDs. /// @throws std::invalid_argument If the vertex descriptor is invalid. From 8336400f7515ab732f4432b10d5f7819e05b9df9 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 16:24:50 +0200 Subject: [PATCH 22/28] architecture page refinement --- docs/gl/architecture.md | 2 +- docs/hgl/architecture.md | 164 +++++++++++--- .../doc/dark/undir-hypergraph-repr-flat.svg | 113 ++++++++++ .../doc/dark/undir-hypergraph-repr-std.svg | 213 ++++++++++++++++++ .../doc/light/undir-hypergraph-repr-flat.svg | 113 ++++++++++ .../doc/light/undir-hypergraph-repr-std.svg | 213 ++++++++++++++++++ 6 files changed, 781 insertions(+), 37 deletions(-) create mode 100644 docs/img/doc/dark/undir-hypergraph-repr-flat.svg create mode 100644 docs/img/doc/dark/undir-hypergraph-repr-std.svg create mode 100644 docs/img/doc/light/undir-hypergraph-repr-flat.svg create mode 100644 docs/img/doc/light/undir-hypergraph-repr-std.svg diff --git a/docs/gl/architecture.md b/docs/gl/architecture.md index 3a661c52..3f41936c 100644 --- a/docs/gl/architecture.md +++ b/docs/gl/architecture.md @@ -232,7 +232,7 @@ By keeping all vertex and edge data in adjacent memory blocks, these models prov > > While the flat adjacency list model is highly efficient for graph storage and traversal, it is highly inefficient to construct element-by-element. The most efficient approach for utilizing flat list graphs is to construct your graph using the standard list model first, and then convert it into the flat list model using the generic [**gl::to**](../cpp-gl/group__GL-Core.md#function-to) conversion function. This exact methodology is utilized internally by the [**graph topology generators**](topologies.md) defined within the library. -### Operation Complexity +### Operation Complexities Depending on the chosen representation model, the computational complexity of standard graph operations will differ. The table below outlines these complexities. diff --git a/docs/hgl/architecture.md b/docs/hgl/architecture.md index a19e91c3..621b5165 100644 --- a/docs/hgl/architecture.md +++ b/docs/hgl/architecture.md @@ -200,58 +200,150 @@ for (auto in_hyperedge : hg.in_hyperedges(vertex)) { // (2)! ## Representation & Layouts -Because hypergraphs are generalizations of graphs, their topological data cannot be stored as simple arrays of pairs. Instead, CPP-GL relies on **Incidence Models**. HGL categorizes its memory representations via `ImplTag` and strictly controls orientation via `LayoutTag`. +Because hypergraphs are generalizations of graphs, their topological data cannot be stored the same way as standard graphs (Adjacency Models). Instead, CPP-HGL relies on **Incidence Models** to capture the higher-order connections between vertices and hyperedges. The HGL module categorizes its memory representations via the `ImplTag` and strictly controls their memory orientation with the `LayoutTag`. -### Incidence Models (Standard & Flat) +### Fundamental Representations -- **Incidence Lists** ([`list_t`](../cpp-gl/structhgl_1_1impl_1_1list__t.md) / [`flat_list_t`](../cpp-gl/structhgl_1_1impl_1_1flat__list__t.md)): Maps elements to dynamic jagged lists. Highly space-efficient for sparse hypergraphs, as memory is only allocated for existing incidence relations. -- **Incidence Matrices** ([`matrix_t`](../cpp-gl/structhgl_1_1impl_1_1matrix__t.md) / [`flat_matrix_t`](../cpp-gl/structhgl_1_1impl_1_1flat__matrix__t.md)): Allocates a full $|V| \times |E|$ 2D grid. Memory intensive ($O(|V| \times |E|)$), but allows instant $O(1)$ verification if a given vertex belongs to a given hyperedge. +At their core, hypergraph data structures differ in how they map vertices to the hyperedges they belong to, or vice versa. The primary incidence models in the HGL module are based on the following architectures: -### Layout Tags (Orientation) +- **Incidence Lists**: This model stores only the active incidence relationships. It works by mapping an element (e.g., a vertex) to a dynamic, list of its associated elements (e.g., the hyperedges it belongs to). This approach is highly space-efficient for sparse hypergraphs and allows rapid iteration over local incidence sets. +- **Incidence Matrices**: This model allocates a full $|V| \times |E|$ 2D grid, where each cell represents a potential connection between a specific vertex and a specific hyperedge. While they consume significantly more memory ($O(|V| \times |E|)$), they provide instant $O(1)$ incidence verification, making them ideal for dense, heavily interconnected hypergraphs. -The `LayoutTag` dictates the *primary indexing dimension* of the incidence structure, massively impacting query speeds and memory footprints: +### Representation Layouts (Orientation) -- [**bidirectional_t**](../cpp-gl/structhgl_1_1impl_1_1bidirectional__t.md): Maintains *two* internal mappings (Vertex-to-Hyperedges AND Hyperedge-to-Vertices). Offers optimal $O(1)$ access for both vertex degrees and hyperedge sizes, at the cost of doubled memory consumption. *(Compatible only with Incidence Lists)*. -- [**vertex_major_t**](../cpp-gl/structhgl_1_1impl_1_1vertex__major__t.md): The primary index is the Vertex. Querying the hyperedges connected to a vertex is instantaneous, but finding which vertices belong to a hyperedge requires an expensive full-graph scan. -- [**hyperedge_major_t**](../cpp-gl/structhgl_1_1impl_1_1hyperedge__major__t.md): The primary index is the Hyperedge. Querying the vertices within a hyperedge is instantaneous, but finding a vertex's degree requires a full-graph scan. +The `LayoutTag` dictates the *primary indexing dimension* of the incidence structure. Because hypergraphs map two distinctly different sets ($V$ and $E$), changing the primary index massively impacts query speeds and memory footprints: + +- [**bidirectional_t**](../cpp-gl/structhgl_1_1impl_1_1bidirectional__t.md): Maintains *two* internal mappings simultaneously (Vertex-to-Hyperedges AND Hyperedge-to-Vertices). Offers optimal $O(1)$ access for both vertex degrees and hyperedge sizes, and fast iteration in both directions, at the cost of doubled memory consumption. **(Compatible only with Incidence Lists)**. +- [**hyperedge_major_t**](../cpp-gl/structhgl_1_1impl_1_1hyperedge__major__t.md): The primary index is the Hyperedge. Querying the vertices within a specific hyperedge is instantaneous, but finding which hyperedges a vertex belongs to may require an expensive full-structure scan. +- [**vertex_major_t**](../cpp-gl/structhgl_1_1impl_1_1vertex__major__t.md): The primary index is the Vertex. Querying the hyperedges connected to a specific vertex is instantaneous, but finding which vertices belong to a specific hyperedge may require an expensive, full-structure scan. > [!NOTE] Matrices and Asymmetry > -> Incidence matrices fundamentally represent an asymmetric $|V| \times |E|$ mathematical grid. Therefore, matrix representations strictly require an asymmetric layout (`vertex_major_t` or `hyperedge_major_t`), mapping rows to the major element and columns to the minor element. +> Incidence matrices fundamentally represent an asymmetric $|V| \times |E|$ grid. Therefore, matrix representations strictly require an asymmetric layout (`vertex_major_t` or `hyperedge_major_t`), mapping rows to the major element and columns to the minor element. + +### Standard Models + +Standard models are heap-allocated, nested structures (e.g., `std::vector>`) that prioritize flexibility and dynamic structural modification. Because the inner containers can grow independently, they handle topological changes gracefully. + +- [**list_t**](../cpp-gl/structhgl_1_1impl_1_1list__t.md): A standard Incidence List model implemented using traditional nested containers. +- [**matrix_t**](../cpp-gl/structhgl_1_1impl_1_1matrix__t.md): A standard Incidence Matrix model implemented using traditional nested containers. + +
+ +![Undirected Hypergraph Standard Representation Light](../img/doc/light/undir-hypergraph-repr-std.svg#only-light){: width="700" } +![Undirected Hypergraph Standard Representation Dark](../img/doc/dark/undir-hypergraph-repr-std.svg#only-dark){: width="700" } + +
+ +> [!NOTE] BF-Directed Representations +> +> The diagrams above illustrate standard representations for an **undirected** hypergraph. For **BF-directed** hypergraphs, the underlying architecture adapts to capture directionality: +> - **Incidence List:** The structure maintains separate inner containers to explicitly distinguish between tail-bound and head-bound incidence relationships. +> - **Incidence Matrix:** The cells of the matrix are no longer simple boolean indicators. Instead, they store specific directional states (conceptually mapped as `-1`, `0`, and `1`) to indicate whether a vertex is in the tail of the hyperedge, not connected, or in the head of the hyperedge, respectively. + +### Flat Models + +To maximize cache locality, the flat representations map the logical 2D structures into contiguous 1D memory blocks. By keeping all incidence data tightly packed, these models provide the absolute maximum traversal speed. However, this cache-friendliness comes at a structural cost: modifying an inner segment often requires shifting the entire remainder of the flat container in memory. + +- [**flat_list_t**](../cpp-gl/structhgl_1_1impl_1_1flat__list__t.md): A flattened Incidence List model implemented using the generic [**gl::flat_jagged_vector**](../cpp-gl/classgl_1_1flat__jagged__vector.md) data structure. +- [**flat_matrix_t**](../cpp-gl/structhgl_1_1impl_1_1flat__matrix__t.md): A flattened Incidence Matrix model implemented using the generic [**gl::flat_matrix**](../cpp-gl/classgl_1_1flat__matrix.md) data structure. + +
-### Operation Complexity Tables +![Undirected Hypergraph Flat Representation Light](../img/doc/light/undir-hypergraph-repr-flat.svg#only-light){: width="700" } +![Undirected Hypergraph Flat Representation Dark](../img/doc/dark/undir-hypergraph-repr-flat.svg#only-dark){: width="700" } -Because performance is heavily dictated by the combination of the Representation Model (List vs. Matrix) and the Layout Tag (Bidirectional vs. Major), the complexities are grouped accordingly. +
-*Note: In the tables below, $|V|$ is vertex count, $|E|$ is hyperedge count, $deg(v)$ is vertex degree, and $|e|$ is hyperedge size (number of incident vertices).* +> [!NOTE] BF-Directed Representations +> +> Similarly to the standard models, flat architectures seamlessly adapt to directionality. A BF-directed *Flat Incidence List* utilizes separated flat storage for tail and head incidence, preserving contiguous memory reads for specific directional traversals. Meanwhile, a *Flat Incidence Matrix* packs the tri-state directional indicators directly into its contiguous 1D grid layout. + +> [!NOTE] Flat List Model Performance +> +> While the flat incidence list model is highly efficient for hypergraph storage and traversal, it is also highly inefficient to construct sequentially (element-by-element and incidence-by-incidence). The most efficient approach for utilizing flat list hypergraphs is to construct your hypergraph using the standard `list_t` model first, and then convert it into the `flat_list_t` model using the generic [**hgl::to**](../cpp-gl/group__HGL-Core.md#function-to) conversion function. -#### 1. Incidence Lists (`list_t` / `flat_list_t`) +### Operation Complexities -| Operation | `bidirectional_t` | `vertex_major_t` | `hyperedge_major_t` | -| :--------------------------------------- | :------------------------------- | :------------------------------- | :------------------------------- | -| **Check Incidence** $(v, e)$ | $O(\min(deg(v), \vert e \vert))$ | $O(deg(v))$ | $O(\vert e \vert)$ | -| **Iterate Incident Hyperedges** of $v$ | $O(deg(v))$ | $O(deg(v))$ | $O(\vert V \vert + \sum \vert e \vert)$ | -| **Iterate Incident Vertices** of $e$ | $O(\vert e \vert)$ | $O(\vert E \vert + \sum deg(v))$ | $O(\vert e \vert)$ | -| **Get Degree** of $v$ | $O(1)$ | $O(1)$ | $O(\vert V \vert + \sum \vert e \vert)$ | -| **Get Size** of $e$ | $O(1)$ | $O(\vert E \vert + \sum deg(v))$ | $O(1)$ | -| **Memory Footprint (Topology)** | $O(2 \times \sum deg(v))$ | $O(\sum deg(v))$ | $O(\sum \vert e \vert)$ | +Because hypergraph performance is heavily dictated by the combination of the Representation Model (List vs. Matrix), the underlying Memory Strategy (Standard vs. Flat), and the Layout Tag (Bidirectional vs. Major), the complexities are grouped accordingly. -#### 2. Incidence Matrices (`matrix_t` / `flat_matrix_t`) +*Note: In the tables below, $|V|$ is the total vertex count, $|E|$ is the total hyperedge count, $deg(v)$ is the degree of a specific vertex, and $|e|$ is the size of a specific hyperedge. $I$ represents the total number of incidences across the entire hypergraph ($I = \sum deg(v) = \sum |e|$).* -| Operation | `vertex_major_t` (Rows = V, Cols = E) | `hyperedge_major_t` (Rows = E, Cols = V) | -| :--------------------------------------- | :------------------------------------ | :--------------------------------------- | -| **Check Incidence** $(v, e)$ | $O(1)$ | $O(1)$ | -| **Iterate Incident Hyperedges** of $v$ | $O(\vert E \vert)$ (Scan Row) | $O(\vert E \vert)$ (Scan Column) | -| **Iterate Incident Vertices** of $e$ | $O(\vert V \vert)$ (Scan Column) | $O(\vert V \vert)$ (Scan Row) | -| **Get Degree** of $v$ | $O(\vert E \vert)$ | $O(\vert E \vert)$ | -| **Get Size** of $e$ | $O(\vert V \vert)$ | $O(\vert V \vert)$ | -| **Memory Footprint (Topology)** | $O(\vert V \vert \times \vert E \vert)$ | $O(\vert V \vert \times \vert E \vert)$ | +> [!NOTE] Directionality and Complexity +> For **BF-directed** hypergraphs, operations like `in_degree`, `out_degree`, `tail_vertices`, and `head_vertices` follow the exact same complexity class as their undirected counterparts (`degree`, `incident_vertices`). They merely operate on isolated sub-containers (in list models) or specific tri-state indicators (in matrix models), keeping the Big-O structurally identical. + +#### 1. Incidence Lists + +**Topological Queries:** + +The complexities of topological queries is identical for standard (`list_t`) and flat (`flat_list_t`) Incidence List models. + +| Query Operation | Bidirectional | Vertex-Major | Hyperedge-Major | +| :--- | :--- | :--- | :--- | +| **Check Incidence** $(v, e)$ | $O(\log(\min(deg(v), \vert e \vert)))$ | $O(\log(deg(v)))$ | $O(\log(\vert e \vert))$ | +| **Iterate Incident Hyperedges** of $v$ | $O(deg(v))$ | $O(deg(v))$ | $O(\vert V \vert + I)$ (Scan All) | +| **Iterate Incident Vertices** of $e$ | $O(\vert e \vert)$ | $O(\vert E \vert + I)$ (Scan All) | $O(\vert e \vert)$ | +| **Get Degree** of $v$ | $O(1)$ | $O(1)$ | $O(\vert V \vert + I)$ | +| **Get Size** of $e$ | $O(1)$ | $O(\vert E \vert + I)$ | $O(1)$ | + +**Structural Mutations (Standard Incidence List, `list_t`):** + +| Mutation Operation | Bidirectional | Vertex-Major |Hyperedge-Major | +| :--- | :--- | :--- | :--- | +| **Add Vertex** | $O(1)$ amortized | $O(1)$ amortized | $O(1)$ amortized | +| **Add Hyperedge** | $O(1)$ amortized | $O(1)$ amortized | $O(1)$ amortized | +| **Remove Vertex** | $O(\vert V \vert + \vert E \vert + I)$ | $O(\vert V \vert)$ *(Major)* | $O(\vert E \vert + I)$ *(Minor)* | +| **Remove Hyperedge** | $O(\vert V \vert + \vert E \vert + I)$ | $O(\vert V \vert + I)$ *(Minor)* | $O(\vert E \vert)$ *(Major)* | +| **Add Incidence** (Bind) | $O(deg(v) + \vert e \vert)$ | $O(deg(v))$ | $O(\vert e \vert)$ | +| **Remove Incidence** (Unbind)| $O(deg(v) + \vert e \vert)$ | $O(deg(v))$ | $O(\vert e \vert)$ | + +**Structural Mutations (Flat Incidence List, `flat_list_t`):** + +| Mutation Operation | Bidirectional | Vertex-Major |Hyperedge-Major | +| :--- | :--- | :--- | :--- | +| **Add Vertex** | $O(1)$ amortized | $O(1)$ amortized | $O(1)$ amortized | +| **Add Hyperedge** | $O(1)$ amortized | $O(1)$ amortized | $O(1)$ amortized | +| **Remove Vertex** | $O(\vert V \vert + \vert E \vert + I)$ | $O(\vert V \vert + I)$ *(Major)* | $O(\vert E \vert + I)$ *(Minor)* | +| **Remove Hyperedge** | $O(\vert V \vert + \vert E \vert + I)$ | $O(\vert V \vert + I)$ *(Minor)* | $O(\vert E \vert + I)$ *(Major)* | +| **Add Incidence** (Bind) | $O(I)$ | $O(I)$ | $O(I)$ | +| **Remove Incidence** (Unbind)| $O(I)$ | $O(I)$ | $O(I)$ | + +*(Note: Adding/removing incidences in flat lists is always $O(I)$ because modifying any internal jagged segment requires shifting the remainder of the massive contiguous 1D data array).* + +#### 2. Incidence Matrices + +**Topological Queries:** + +The complexities of topological queries is identical for standard (`matrix_t`) and flat (`flat_matrix_t`) Incidence Matrix models. + +| Query Operation | Vertex-Major
(Rows: V, Cols: E) |Hyperedge-Major
(Rows: E, Cols: V) | +| :--- | :--- | :--- | +| **Check Incidence** $(v, e)$ | $O(1)$ | $O(1)$ | +| **Iterate Incident Hyperedges** of $v$ | $O(\vert E \vert)$ (Scan Row) | $O(\vert E \vert)$ (Scan Column) | +| **Iterate Incident Vertices** of $e$ | $O(\vert V \vert)$ (Scan Column) | $O(\vert V \vert)$ (Scan Row) | +| **Get Degree** of $v$ | $O(\vert E \vert)$ (Scan Row) | $O(\vert E \vert)$ (Scan Column) | +| **Get Size** of $e$ | $O(\vert V \vert)$ (Scan Column) | $O(\vert V \vert)$ (Scan Row) | + +**Structural Mutations:** + +| Mutation Operation | `matrix_t` (Standard) | `flat_matrix_t` (Flat) | +| :--- | :--- | :--- | +| **Add Vertex** | $O(\vert E \vert)$ amortized if `vertex_major_t`
$O(\vert V \vert \times \vert E \vert)$ if `hyperedge_major_t` | $O(\vert V \vert \times \vert E \vert)$ | +| **Add Hyperedge** | $O(\vert V \vert \times \vert E \vert)$ if `vertex_major_t`
$O(\vert V \vert)$ amortized if `hyperedge_major_t` | $O(\vert V \vert \times \vert E \vert)$ | +| **Remove Vertex / Hyperedge** | $O(\vert V \vert \times \vert E \vert)$ (Shift rows/cols) | $O(\vert V \vert \times \vert E \vert)$ | +| **Add / Remove Incidence** (Bind/Unbind) | $O(1)$ | $O(1)$ | + +> [!NOTE] Matrix Layouts and Cache Locality +> +> While the theoretical Big-O complexities appear identical across matrix layouts, **real-world performance is heavily bound by cache locality.** Matrices map one dimension to contiguous rows and the other to strided columns. +> +> - **Query Direction:** Choose the layout that aligns your most frequent traversal with contiguous memory reads. If your algorithm primarily iterates over the vertices within specific hyperedges, `hyperedge_major_t` will drastically outperform `vertex_major_t` by avoiding cache misses. +> - **Dimension Imbalance:** If your hypergraph is highly asymmetric (e.g., $|V| \gg |E|$), scanning the larger dimension across strided memory can degrade performance. Align the major layout with the dimension you need to scan most efficiently. ### Choosing the Representation & Layout -Selecting the correct combination ensures your application hits peak performance: +Selecting the optimal combination of Representation Model and Layout Tag requires balancing memory constraints, topology density, and your specific traversal patterns to achieve peak performance: -- Use **`bidirectional_t`** Lists (the default) for general-purpose hypergraphs where you frequently query in both directions (e.g., "what nodes are in this hyperedge?" AND "what hyperedges is this node in?"). -- Use **`hyperedge_major_t`** Lists when dealing with massive datasets where memory is tight, and your algorithms are strictly edge-centric (e.g., simulating isolated hyperedge reactions). -- Use **`matrix_t`** when your hypergraph is extremely dense, hyperedge sizes are close to $|V|$, and absolute instant incidence verification `are_incident(v, e)` is the algorithmic bottleneck. -``` +- **`bidirectional_t` Lists (Default):** The most versatile choice for general-purpose hypergraphs. Use this when your algorithms require fast, symmetric traversals (e.g., frequently oscillating between querying "which vertices belong to this hyperedge?" and "which hyperedges contain this vertex?"). It trades a doubled memory footprint for optimal $O(1)$ degree and size lookups, ensuring fluid bidirectional navigation. +- **Single-Major Lists (`hyperedge_major_t` / `vertex_major_t`):** Ideal for massive, sparse datasets operating under strict memory limits. Use these layouts when your algorithm's traversal logic is heavily asymmetrical. For instance, if an algorithm strictly simulates isolated chemical reactions and rarely needs to compute a vertex's degree, `hyperedge_major_t` eliminates the redundant backward-mapping overhead. +- **Matrices (`matrix_t` / `flat_matrix_t`):** Reserved for exceptionally dense hypergraphs where hyperedge sizes consistently approach $|V|$. Choose a matrix representation only when absolute, instant $O(1)$ incidence verification (`are_incident(v, e)`) is the primary algorithmic bottleneck and the $O(|V| \times |E|)$ memory consumption is an acceptable trade-off. diff --git a/docs/img/doc/dark/undir-hypergraph-repr-flat.svg b/docs/img/doc/dark/undir-hypergraph-repr-flat.svg new file mode 100644 index 00000000..b271163f --- /dev/null +++ b/docs/img/doc/dark/undir-hypergraph-repr-flat.svg @@ -0,0 +1,113 @@ + +FlatIncidenceList(Hyperedge-Major) +Offsets + +0 + +1 + +4 + +7 + +9 +Entries + +v +1 + +v +1 + +v +2 + +v +3 + +v +2 + +v +3 + +v +4 + +v +1 + +v +4 + + + + + + +e +1 +segment + +e +2 +segment + +e +3 +segment + +e +4 +segment +FlatIncidenceMatrix(Hyperedge-Major) +Cells + +1 + +0 + +0 + +0 + +1 + +1 + +1 + +0 + +0 + +1 + +1 + +1 + +1 + +0 + +0 + +1 + +e +1 +row + +e +2 +row + +e +3 +row + +e +4 +row + diff --git a/docs/img/doc/dark/undir-hypergraph-repr-std.svg b/docs/img/doc/dark/undir-hypergraph-repr-std.svg new file mode 100644 index 00000000..01dcb943 --- /dev/null +++ b/docs/img/doc/dark/undir-hypergraph-repr-std.svg @@ -0,0 +1,213 @@ + +IncidenceList +IncidenceMatrix + +v +1 + +v +2 + +v +3 + +v +4 + +e +1 + +e +2 + +e +4 + +e +2 + +e +3 + +e +2 + +e +3 + +e +3 + +e +4 + + + + + + + + + + + + +e +1 +e +2 +e +3 +e +4 +v +1 + +1 + +1 + +0 + +1 +v +2 + +0 + +1 + +1 + +0 +v +3 + +0 + +1 + +1 + +0 +v +4 + +0 + +0 + +1 + +1 + + +e +1 + +e +2 + +e +3 + +e +4 + +v +1 + +v +1 + +v +2 + +v +3 + +v +2 + +v +3 + +v +4 + +v +1 + +v +4 + + + + + + + + + + + + +v +1 +v +2 +v +3 +v +4 +e +1 + +1 + +0 + +0 + +0 +e +2 + +1 + +1 + +1 + +0 +e +3 + +0 + +1 + +1 + +1 +e +4 + +1 + +0 + +0 + +1 + +Vertex-Major + +Hyperedge-Major + +Bidirectional + +Vertex-Major + +Hyperedge-Major + diff --git a/docs/img/doc/light/undir-hypergraph-repr-flat.svg b/docs/img/doc/light/undir-hypergraph-repr-flat.svg new file mode 100644 index 00000000..2dee2926 --- /dev/null +++ b/docs/img/doc/light/undir-hypergraph-repr-flat.svg @@ -0,0 +1,113 @@ + +FlatIncidenceList(Hyperedge-Major) +Offsets + +0 + +1 + +4 + +7 + +9 +Entries + +v +1 + +v +1 + +v +2 + +v +3 + +v +2 + +v +3 + +v +4 + +v +1 + +v +4 + + + + + + +e +1 +segment + +e +2 +segment + +e +3 +segment + +e +4 +segment +FlatIncidenceMatrix(Hyperedge-Major) +Cells + +1 + +0 + +0 + +0 + +1 + +1 + +1 + +0 + +0 + +1 + +1 + +1 + +1 + +0 + +0 + +1 + +e +1 +row + +e +2 +row + +e +3 +row + +e +4 +row + diff --git a/docs/img/doc/light/undir-hypergraph-repr-std.svg b/docs/img/doc/light/undir-hypergraph-repr-std.svg new file mode 100644 index 00000000..7ee0a44a --- /dev/null +++ b/docs/img/doc/light/undir-hypergraph-repr-std.svg @@ -0,0 +1,213 @@ + +IncidenceList +IncidenceMatrix + +v +1 + +v +2 + +v +3 + +v +4 + +e +1 + +e +2 + +e +4 + +e +2 + +e +3 + +e +2 + +e +3 + +e +3 + +e +4 + + + + + + + + + + + + +e +1 +e +2 +e +3 +e +4 +v +1 + +1 + +1 + +0 + +1 +v +2 + +0 + +1 + +1 + +0 +v +3 + +0 + +1 + +1 + +0 +v +4 + +0 + +0 + +1 + +1 + + +e +1 + +e +2 + +e +3 + +e +4 + +v +1 + +v +1 + +v +2 + +v +3 + +v +2 + +v +3 + +v +4 + +v +1 + +v +4 + + + + + + + + + + + + +v +1 +v +2 +v +3 +v +4 +e +1 + +1 + +0 + +0 + +0 +e +2 + +1 + +1 + +1 + +0 +e +3 + +0 + +1 + +1 + +1 +e +4 + +1 + +0 + +0 + +1 + +Vertex-Major + +Hyperedge-Major + +Bidirectional + +Vertex-Major + +Hyperedge-Major + From f422335fe693a03fd1366af03cdbf3fca5f91716 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 20:07:52 +0200 Subject: [PATCH 23/28] conversion page + self-loops in clique exp --- docs/hgl/conversions.md | 139 ++++++++++++++++++++++++ docs/img/doc/dark/bfdir-flow-graph.svg | 61 +++++++++++ docs/img/doc/dark/bfdir-star-exp.svg | 88 +++++++++++++++ docs/img/doc/dark/undir-clique-exp.svg | 43 ++++++++ docs/img/doc/dark/undir-star-exp.svg | 67 ++++++++++++ docs/img/doc/light/bfdir-flow-graph.svg | 61 +++++++++++ docs/img/doc/light/bfdir-star-exp.svg | 88 +++++++++++++++ docs/img/doc/light/undir-clique-exp.svg | 43 ++++++++ docs/img/doc/light/undir-star-exp.svg | 67 ++++++++++++ include/hgl/conversion.hpp | 6 + tests/source/hgl/test_conversion.cpp | 3 +- 11 files changed, 665 insertions(+), 1 deletion(-) create mode 100644 docs/img/doc/dark/bfdir-flow-graph.svg create mode 100644 docs/img/doc/dark/bfdir-star-exp.svg create mode 100644 docs/img/doc/dark/undir-clique-exp.svg create mode 100644 docs/img/doc/dark/undir-star-exp.svg create mode 100644 docs/img/doc/light/bfdir-flow-graph.svg create mode 100644 docs/img/doc/light/bfdir-star-exp.svg create mode 100644 docs/img/doc/light/undir-clique-exp.svg create mode 100644 docs/img/doc/light/undir-star-exp.svg diff --git a/docs/hgl/conversions.md b/docs/hgl/conversions.md index d707152e..ffef8344 100644 --- a/docs/hgl/conversions.md +++ b/docs/hgl/conversions.md @@ -1 +1,140 @@ # Graph Conversions + +While the **HGL** module treats hypergraphs as first-class, n-ary topological entities, there are many scenarios where you may need to interface with traditional graph algorithms, export data for standard graph visualization tools, or simplify relations. + +To bridge this gap, CPP-GL provides zero-cost abstractions to convert or project hypergraphs from the HGL module into standard graphs from the **GL** module. + +This section covers the two primary conversion methodologies and the fundamental differences in how they preserve or discard structural data: + +- [Incidence Graph Conversion](#incidence-graph-conversion): Lossless conversion to a bipartite graph. +- [Projection Conversions](#projection-conversions): Lossy conversion that collapses hyperedges. + +--- + +## Information Loss & Structural Integrity + +When converting a hypergraph to a standard graph, you must decide how to handle the n-ary nature of hyperedges. + +--- + +## Incidence Graph Conversion + +The incidence graph conversion (also known as *star expansion*) translates a hypergraph $H = (V, E)$ into a standard bipartite graph $G = (V', E')$, where $V' = V \cup E$. Every hyperedge in the original hypergraph becomes a distinct vertex in the new graph, and standard edges are drawn between the original vertices and the new "hyperedge" vertices based on their incidence. + +> [!IMPORTANT] Topological Preservation +> +> **Incidence Graph Conversions** are **lossless**. They preserve the exact mathematical topology of the hypergraph by converting hyperedges into secondary "dummy" vertices, creating a strict Bipartite Graph. + +### Undirected Hypergraphs + +For an undirected hypergraph, the resulting incidence graph is a standard **Undirected Bipartite Graph**. If a vertex $v$ belongs to a hyperedge $e$, an undirected edge $\{v, e\}$ is created. + +#### Formal Definition: + +Given an undirected hypergraph $H = (V, E)$, its incidence graph is an undirected bipartite graph $G = (V \cup E, E')$, where the new edge set is defined as: + +$$ +E' = \big\{ \{v, e\} : v \in V \land e \in E \land v \in e \big\} +$$ + +
+ +![Undirected Hypergraph - Star Expansion - Light](../img/doc/light/undir-star-exp.svg#only-light){: width="700" } +![Undirected Hypergraph - Star Expansion - Dark](../img/doc/dark/undir-star-exp.svg#only-dark){: width="700" } + +
+ +### BF-Directed Hypergraphs + +For a BF-directed hypergraph, the resulting incidence graph is a **Directed Bipartite Graph**. The directional flow is strictly preserved: +- If a vertex $v$ is in the **tail** of $e$, a directed edge $(v, e)$ is created. +- If a vertex $v$ is in the **head** of $e$, a directed edge $(e, v)$ is created. + +#### Formal Definition: + +Given a BF-directed hypergraph $H = (V, E)$, its incidence graph is a directed bipartite graph $G = (V \cup E, E')$, where the new directed edge set $E'$ connects *tail* vertices to hyperedge-nodes and hyperedge-nodes to *head* vertices: + +$$ +E' = \big\{ (v, e) : v \in V \land e \in E \land v \in T(e) \big\} \cup \big\{ (e, v) : v \in V \land e \in E \land v \in H(e) \big\} +$$ + +
+ +![BF-Directed Hypergraph - Star Expansion - Light](../img/doc/light/bfdir-star-exp.svg#only-light){: width="700" } +![BF-Directed Hypergraph - Star Expansion - Dark](../img/doc/dark/bfdir-star-exp.svg#only-dark){: width="700" } + +
+ +### Example Usage + +The [**hgl::incidence_graph**](../cpp-gl/group__HGL-Core.md#function-incidence_graph) utility handles this transformation automatically, ensuring IDs are safely shifted to prevent collisions between the original vertices and the newly promoted hyperedge vertices. + +```cpp +#include + +auto bipartite_graph = hgl::incidence_graph>(hg); // (1)! +``` + +1. We assume `hg` is an instance of a *undirected* hypergraph. + +--- + +## Projection Conversions + +Projections are useful when you only care about the direct relationships between the actual data elements and want to discard the concept of the hyperedges entirely. + +> [!IMPORTANT] Topological Preservation +> +> **Projection Conversions** are **lossy**. They collapse the hyperedges, creating direct edges between the original vertices. This inherently destroys the higher-order grouping information (e.g., you can no longer tell if three vertices were connected by one 3-ary hyperedge or three separate pairwise edges). + +### Clique Expansion (Undirected Hypergraphs) + +When projecting an undirected hypergraph, every hyperedge is replaced by a "clique" (a fully connected subgraph) amongst its incident vertices. If vertices $v_1, v_2, v_3$ share a hyperedge, the projection generates pairwise edges $\{v_1, v_2\}, \{v_2, v_3\}$, and $\{v_1, v_3\}$. Hyperedges containing only a single vertex typically project to a self-loop. + +#### Formal Definition: + +Given an undirected hypergraph $H = (V, E)$, its clique expansion is an undirected graph $G = (V, E')$, where every hyperedge is replaced by a fully connected subgraph of its incident vertices: + +$$ +E' = \big\{ \{u, v\} : (\exists e \in E)(u, v \in e)\big\} +$$ + +
+ +![Undirected Hypergraph - Clique Expansion - Light](../img/doc/light/undir-clique-exp.svg#only-light){: width="700" } +![Undirected Hypergraph - Clique Expansion - Dark](../img/doc/dark/undir-clique-exp.svg#only-dark){: width="700" } + +
+ +### Flow Graph / Bipartite Projection (BF-Directed) + +When projecting a BF-directed hypergraph, the hyperedge acts as a directional bridge. The projection generates a standard directed edge from every vertex in the hyperedge's *tail* to every vertex in its *head*. + +If a hyperedge has tail vertices $\{v_1, v_2\}$ and head vertices $\{v_3, v_4\}$, the projection yields four directed edges: $(v_1, v_3), (v_1, v_4), (v_2, v_3)$, and $(v_2, v_4)$. + +#### Formal Definition: + +Given a BF-directed hypergraph $H = (V, E)$, its flow graph projection is a directed graph $G = (V, E')$, where every hyperedge creates a directed Cartesian product from its tail vertices to its head vertices: + +$$ +E' = \big\{ (u, v) : (\exists e \in E)(u \in T(e) \land v \in H(e))\big\} +$$ + +
+ +![BF-Directed Hypergraph - Flow Graph - Light](../img/doc/light/bfdir-flow-graph.svg#only-light){: width="700" } +![BF-Directed Hypergraph - Flow Graph - Dark](../img/doc/dark/bfdir-flow-graph.svg#only-dark){: width="700" } + +
+ +### Example Usage + +The [**hgl::projection**](../cpp-gl/group__HGL-Core.md#function-projection) utility handles this transformation automatically, ensuring no duplicate edges and properly handling self-loops (for undirected hypergraphs). + +```cpp +#include + +auto flow_graph = hgl::projection>(hg); // (1)! +``` + +1. We assume `hg` is an instance of a *BF-directed* hypergraph. diff --git a/docs/img/doc/dark/bfdir-flow-graph.svg b/docs/img/doc/dark/bfdir-flow-graph.svg new file mode 100644 index 00000000..a20e6c9e --- /dev/null +++ b/docs/img/doc/dark/bfdir-flow-graph.svg @@ -0,0 +1,61 @@ + +OriginalHypergraph +v +1 +v +2 +v +3 +v +4 +v +5 + + + + + + + + + + + +e +1 +e +2 +e +3 +FlowGraphProjection + +v +1 + +v +2 + +v +3 + +v +4 + +v +5 + + + + + + + + + + + + + + + + diff --git a/docs/img/doc/dark/bfdir-star-exp.svg b/docs/img/doc/dark/bfdir-star-exp.svg new file mode 100644 index 00000000..83f9512d --- /dev/null +++ b/docs/img/doc/dark/bfdir-star-exp.svg @@ -0,0 +1,88 @@ + +OriginalHypergraph +v +1 +v +2 +v +3 +v +4 +v +5 + + + + + + + + + + + +e +1 +e +2 +e +3 +IncidenceGraph + +v +1 + +v +2 + +v +3 + +v +4 + +v +5 +Set +V + + +e +1 + + +e +2 + + +e +3 +Set +E + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/img/doc/dark/undir-clique-exp.svg b/docs/img/doc/dark/undir-clique-exp.svg new file mode 100644 index 00000000..b2b7323c --- /dev/null +++ b/docs/img/doc/dark/undir-clique-exp.svg @@ -0,0 +1,43 @@ + +OriginalHypergraph + + + + +v +1 +v +2 +v +3 +v +4 +e +1 +e +2 +e +3 +e +4 +CliqueProjection + + + + + + + + +v +1 + +v +2 + +v +3 + +v +4 + diff --git a/docs/img/doc/dark/undir-star-exp.svg b/docs/img/doc/dark/undir-star-exp.svg new file mode 100644 index 00000000..2529562a --- /dev/null +++ b/docs/img/doc/dark/undir-star-exp.svg @@ -0,0 +1,67 @@ + +OriginalHypergraph + + + + +v +1 +v +2 +v +3 +v +4 +e +1 +e +2 +e +3 +e +4 +IncidenceGraph + +v +1 + +v +2 + +v +3 + +v +4 +Set +V + + +e +1 + + +e +2 + + +e +3 + + +e +4 +Set +E + + + + + + + + + + + + diff --git a/docs/img/doc/light/bfdir-flow-graph.svg b/docs/img/doc/light/bfdir-flow-graph.svg new file mode 100644 index 00000000..f69b526d --- /dev/null +++ b/docs/img/doc/light/bfdir-flow-graph.svg @@ -0,0 +1,61 @@ + +OriginalHypergraph +v +1 +v +2 +v +3 +v +4 +v +5 + + + + + + + + + + + +e +1 +e +2 +e +3 +FlowGraphProjection + +v +1 + +v +2 + +v +3 + +v +4 + +v +5 + + + + + + + + + + + + + + + + diff --git a/docs/img/doc/light/bfdir-star-exp.svg b/docs/img/doc/light/bfdir-star-exp.svg new file mode 100644 index 00000000..2ed5c729 --- /dev/null +++ b/docs/img/doc/light/bfdir-star-exp.svg @@ -0,0 +1,88 @@ + +OriginalHypergraph +v +1 +v +2 +v +3 +v +4 +v +5 + + + + + + + + + + + +e +1 +e +2 +e +3 +IncidenceGraph + +v +1 + +v +2 + +v +3 + +v +4 + +v +5 +Set +V + + +e +1 + + +e +2 + + +e +3 +Set +E + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/img/doc/light/undir-clique-exp.svg b/docs/img/doc/light/undir-clique-exp.svg new file mode 100644 index 00000000..588359b6 --- /dev/null +++ b/docs/img/doc/light/undir-clique-exp.svg @@ -0,0 +1,43 @@ + +OriginalHypergraph + + + + +v +1 +v +2 +v +3 +v +4 +e +1 +e +2 +e +3 +e +4 +CliqueProjection + + + + + + + + +v +1 + +v +2 + +v +3 + +v +4 + diff --git a/docs/img/doc/light/undir-star-exp.svg b/docs/img/doc/light/undir-star-exp.svg new file mode 100644 index 00000000..4388b794 --- /dev/null +++ b/docs/img/doc/light/undir-star-exp.svg @@ -0,0 +1,67 @@ + +OriginalHypergraph + + + + +v +1 +v +2 +v +3 +v +4 +e +1 +e +2 +e +3 +e +4 +IncidenceGraph + +v +1 + +v +2 + +v +3 + +v +4 +Set +V + + +e +1 + + +e +2 + + +e +3 + + +e +4 +Set +E + + + + + + + + + + + + diff --git a/include/hgl/conversion.hpp b/include/hgl/conversion.hpp index c8719f6b..10c7e904 100644 --- a/include/hgl/conversion.hpp +++ b/include/hgl/conversion.hpp @@ -279,6 +279,12 @@ template for (const auto eid : h.hyperedge_ids()) { const auto clique_vertices = h.incident_vertex_ids(eid) | std::ranges::to(); + if (clique_vertices.size() == 1uz) { + const auto v = clique_vertices.front(); + edges.emplace_back(v, v); + continue; + } + for (auto i = 0uz; i < clique_vertices.size(); i++) { for (auto j = 0uz; j < i; j++) { const auto [u, v] = std::minmax(clique_vertices[i], clique_vertices[j]); diff --git a/tests/source/hgl/test_conversion.cpp b/tests/source/hgl/test_conversion.cpp index 2283f1fa..2830f624 100644 --- a/tests/source/hgl/test_conversion.cpp +++ b/tests/source/hgl/test_conversion.cpp @@ -311,7 +311,8 @@ TEST_CASE_TEMPLATE_DEFINE( // e1: (1,2) - exists, (1,3), (2,3) {2uz, 3uz}, {0uz, 3uz}, - // e3: none + // e3: self-loop (0,0) + {0uz, 0uz} }; auto test_conversion_for = From cefc1ff6a1a59447a430a1ad13b6e80f855ab565 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 21:18:45 +0200 Subject: [PATCH 24/28] io page --- docs/gl/io.md | 10 +-- docs/hgl/io.md | 228 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 232 insertions(+), 6 deletions(-) diff --git a/docs/gl/io.md b/docs/gl/io.md index 63cf537e..1b56c161 100644 --- a/docs/gl/io.md +++ b/docs/gl/io.md @@ -21,8 +21,8 @@ int main() { auto e = g.add_edge(v0, v1); std::cout << "Vertex: " << v0 << '\n'; // (1)! - std::cout << "Edge: " << e << '\n'; // (2)! - std::cout << "Graph:\n" << g << '\n'; // (3)! + std::cout << "Edge: " << e << '\n'; // (2)! + std::cout << "Graph:\n" << g << '\n'; // (3)! } ``` @@ -87,12 +87,11 @@ int main() { using graph_type = gl::directed_graph; auto graph = gl::topology::biclique(2uz, 3uz); // (1)! - std::size_t v_idx = 0uz, e_idx = 0uz; for (const auto& vertex : graph.vertices()) { // (2)! - vertex->name = std::format("vertex_{}", ++v_idx); + vertex->name = std::format("vertex_{}", vertex.id() + 1); for (const auto& edge : graph.out_edges(vertex)) if (edge->name.empty()) - edge->name = std::format("edge_{}", ++e_idx); + edge->name = std::format("edge_{}", edge.id() + 1); } std::cout << graph << std::endl; // (3)! @@ -170,7 +169,6 @@ For disk storage and network transmission, CPP-GL defines the **Graph Specificat ``` - | **Element** | **Description** | | :----------------- | :-------------- | | `dir-spec` | `1` if the graph is directed
`0` if the graph is undirected | diff --git a/docs/hgl/io.md b/docs/hgl/io.md index ce86cfe6..85d7398e 100644 --- a/docs/hgl/io.md +++ b/docs/hgl/io.md @@ -1 +1,229 @@ # I/O Utility + +The HGL module provides a robust, customizable, and type-safe I/O system for formatting, serializing, and deserializing hypergraphs. Because the HGL module is built strictly on top of the GL module, it directly reuses its entire I/O infrastructure. Most of the stream manipulators and general range formatters defined in `gl::io` are imported directly into the `hgl::io` namespace. + +> [!NOTE] Range Formatting +> +> This document focuses specifically on formatting hypergraph topologies. For a detailed guide on the general-purpose range and set formatters utilized under the hood (e.g., `range_formatter`, `implicit_range`), please refer to the [**GL I/O Utility**](../gl/io.md) page. + +--- + +## Basic I/O Operations + +At its core, the library overloads the standard `operator<<` for the [**hgl::hypergraph**](../cpp-gl/classhgl_1_1hypergraph.md) and element descriptors. By default, printing any of these objects yields a compact/concise mathematical representation. + +> [!NOTE] Hyperedge Formatting +> +> Because a hyperedge descriptor is a simple wrapper for the hyperedge's ID and optional properties, printing it directly would result in the exact same format as for vertex descriptors. In order to print the hyperedge as the set(s) of its incident vertices, you need to use the dedicated [**display**](../cpp-gl/classhgl_1_1hypergraph.md#function-display) method of the `hypergraph` class. + +```cpp +#include +#include + +int main() { + hgl::undirected_hypergraph<> hg(4); + auto e1 = hg.add_hyperedge({0, 1, 2}); + + auto v0 = hg.vertex(0); + + std::cout << "Vertex: " << v0 << '\n'; // (1)! + std::cout << "Hyperedge: " << hg.display(e1) << '\n'; // (2)! + std::cout << "Hypergraph:\n" << hg << '\n'; // (3)! +} +``` + +1. Prints the vertex ID. +2. Prints the hyperedge incidence utilizing the owning hypergraph's topological context. +3. Prints the incidence structure of the entire hypergraph in a compact format. + +**Output:** + +```text +Vertex: 0 +Hyperedge: {0, 1, 2} +Hypergraph: +V = {0, ..., 3} +E = { + {0, 1, 2} +} +``` + +--- + +## Stream Options Manipulators + +To customize how hypergraphs and their elements are formatted, the library uses Stream Options Manipulators. These manipulators inject custom formatting states directly into standard I/O streams (like `std::cout`). + +### Behavior Notes + +> [!WARNING] State Persistence +> +> Stream options are **persistent**. Once you apply a manipulator to a stream, that formatting state remains active for all subsequent operations on that stream until it is explicitly changed or cleared using another manipulator (e.g., `hgl::io::default_options`). + +> [!IMPORTANT] Shared State Between Modules +> +> The stream manipulator system is completely shared between the GL and HGL modules. Setting an option like `hgl::io::verbose` or `hgl::io::with_properties` on a stream will affect the formatting of *both* standard graphs and hypergraphs printed to that stream. +> +> **Note:** While HGL defines a `hgl::io::with_hyperedge_properties` option for semantic readability, it maps to the exact same underlying bitmask as GL's `gl::io::with_edge_properties` manipulator. + +### Available Manipulators + +| Manipulator | Description | +| :---------- | :---------- | +| **`hgl::io::concise`** | (Default) Sets the layout to a compact, mathematical set notation format. | +| **`hgl::io::verbose`** | Sets the layout to a detailed, multi-line format showing full structural hierarchy. | +| **`hgl::io::spec_fmt`** | Enables the Hypergraph Specification Format (HGSF) used for raw serialization/deserialization. | +| **`hgl::io::with_vertex_properties`** | Instructs the stream to print/read custom payload properties attached to vertices. | +| **`hgl::io::without_vertex_properties`** | Disables vertex property formatting. | +| **`hgl::io::with_hyperedge_properties`** | Instructs the stream to print/read custom payload properties attached to hyperedges. | +| **`hgl::io::without_hyperedge_properties`** | Disables hyperedge property formatting. | +| **`hgl::io::with_properties`** | Enables both vertex and hyperedge properties simultaneously. | +| **`hgl::io::without_properties`** | Disables both vertex and hyperedge properties simultaneously. | +| **`hgl::io::default_options`** | Resets the stream to its default state (concise, no properties). | + +--- + +## Formatting Examples + +The exact output of a hypergraph heavily depends on its directionality model and the currently active stream manipulators. + +```cpp +#include +#include +#include +#include + +int main() { + using traits_t = hgl::undirected_hypergraph; + + hgl::hypergraph hg(4); // (1)! + hg.add_hyperedge({0, 1, 2}); + hg.add_hyperedge({1, 2, 3}); + hg.add_hyperedge({0, 3}); + + for (const auto& vertex : hg.vertices()) // (2)! + vertex->name = std::format("vertex_{}", vertex.id() + 1); + for (const auto& hyperedge : hg.hyperedges()) // (3)! + hyperedge->name = std::format("hyperedge_{}", hyperedge.id() + 1); + + std::cout << hg << std::endl; // (4)! + std::cout << hgl::io::with_properties << hg << std::endl; // (5)! + std::cout << hgl::io::verbose << hg << std::endl; // (6)! +} +``` + +1. Initialize the hypergraph. +2. Set the names of the hypergraph's vertices. +3. Set the names of the hypergraph's hyperedges. +4. Prints the hypergraph using the default options (concise format, without element properties). +5. Prints the hypergraph with both vertex and hyperedge properties, but still in a concise format (options persistency). +6. Prints the hypergraph in a verbose format, with element properties (options persistency). + +**Output (Concise, Without Properties):** +```text +V = {0, ..., 3} +E = { + {0, 1, 2}, + {1, 2, 3}, + {0, 3} +} +``` + +**Output (Concise, With Properties):** +```text +V = { + 0["vertex_1"], + 1["vertex_2"], + 2["vertex_3"], + 3["vertex_4"] +} +E = { + {0, 1, 2}["hyperedge_1"], + {1, 2, 3}["hyperedge_2"], + {0, 3}["hyperedge_3"] +} +``` + +**Output (Verbose, With Properties):** +```text +type: undirected, |V| = 4, |E| = 3 +vertices: + - [id: 0 | "vertex_1"] + - [id: 1 | "vertex_2"] + - [id: 2 | "vertex_3"] + - [id: 3 | "vertex_4"] +hyperedges: + - [id: 0 | vertices: {0, 1, 2} | "hyperedge_1"] + - [id: 1 | vertices: {1, 2, 3} | "hyperedge_2"] + - [id: 2 | vertices: {0, 3} | "hyperedge_3"] +``` + +> [!NOTE] Directionality Differences +> +> If the same formatting principles are applied to a **BF-directed** hypergraph, the output visually splits the connections into *tail* and *head* components. For example, a BF-directed concise hyperedge without properties formats as `({0, 1} -> {2})`, and a verbose one formats as `[id: 0 | tail: {0, 1}, head: {2}]`. + +--- + +## Hypergraph Specification Format (HGSF) + +For disk storage and network transmission, HGL utilizes the **Hypergraph Specification Format (HGSF)**. This is a lightweight, flat text format triggered by the `hgl::io::spec_fmt` manipulator. + +### Format Structure + +The HGSF structure mirrors the standard GL GSF format but adapts the edge-list block to accommodate generalized hyperedge mappings: + +```text + + + +``` + +| **Element** | **Description** | +| :---------- | :-------------- | +| `dir-spec` | `0` if the hypergraph is undirected
`1` if the hypergraph is BF-directed | +| `n-vertices` | The total number of vertices in the hypergraph | +| `n-hyperedges`| The total number of hyperedges in the hypergraph | +| `vprop-marker`| `1` if vertex properties are present in the specification, `0` otherwise | +| `eprop-marker`| `1` if hyperedge properties are present in the specification, `0` otherwise | +| `vertex-prop-list` | A sequential list of length `n-vertices` where each element represents the properties of a vertex. *Omitted if `vprop-marker == 0`.* | +| `hyperedge-list` | A list of length `n-hyperedges` where each line defines a single hyperedge. The structure of these lines depends on the directionality tag (see below). | + +### Hyperedge Definitions + +The specification structure of each hyperedge in the `` is encoded based on the hypergraph's directionality: + +**Undirected Hypergraphs:** + +```text + +``` + +*(e.g., `3 0 1 2 "hyperedge_1"` represents an undirected hyperedge connecting vertices 0, 1, and 2, with an attached string property).* + +**BF-Directed Hypergraphs:** + +```text + +``` + +*(e.g., `2 1 0 1 2 "hyperedge_1"` represents a BF-directed hyperedge with a tail size of 2 (vertices 0, 1) and a head size of 1 (vertex 2), with an attached string property).* + +### Example HGSF Output + +Outputting the fully-propertied undirected hypergraph from the previous examples using `hgl::io::spec_fmt`: + +```cpp +std::cout << hgl::io::spec_fmt << hgl::io::with_properties << hg << std::endl; +``` + +**Output:** +```text +0 4 3 1 1 +"vertex_0" +"vertex_1" +"vertex_2" +"vertex_3" +3 0 1 2 "hyperedge_0" +3 1 2 3 "hyperedge_1" +2 0 3 "hyperedge_2" +``` From 0c3410f18320aab1ff09371e9c50ad425ba6c7cf Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 22:48:33 +0200 Subject: [PATCH 25/28] alg overview page --- docs/gl/algorithms/overview.md | 10 +++- docs/hgl/algorithms/overview.md | 82 +++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/docs/gl/algorithms/overview.md b/docs/gl/algorithms/overview.md index 31c1ef40..82abbbde 100644 --- a/docs/gl/algorithms/overview.md +++ b/docs/gl/algorithms/overview.md @@ -4,6 +4,8 @@ The CPP-GL algorithms module is designed with a strict architectural separation This separation ensures maximum code reuse, provides strict zero-cost abstractions, and allows power users to inject highly customized logic directly into the traversal control flow. +--- + ## The Architectural Separation ### 1. The Generic Templates (Engines) @@ -19,13 +21,15 @@ The core generic templates include: ### 2. The Concrete Algorithms -Concrete algorithms (like `dijkstra_shortest_paths`, `topological_sort`, or `breadth_first_search`) are essentially lightweight wrappers around the generic engines. They are responsible for: +Concrete algorithms (like `dijkstra_shortest_paths`, `topological_sort`, or `breadth_first_search`) are essentially simple wrappers around the generic engines. They are responsible for: - Allocating and managing memory for algorithm state tracking (e.g., `std::vector visited` arrays, distance maps). - Defining the specific logic inside the lambda callbacks and predicates. - Managing the final return types and structures. By separating these layers, CPP-GL guarantees that all algorithms inherently benefit from the exact same optimized design and element management. +--- + ## Core Algorithm Elements To interact with the generic templates or understand the concrete algorithms, you must be familiar with a few foundational types and tags used uniformly across the library. @@ -63,7 +67,9 @@ CPP-GL manages this via the [**gl::algorithm::result_discriminator**](../../cpp- - `ret`: The algorithm will allocate memory and return a stateful object (e.g., `predecessors_map`). - `noret`: The algorithm will execute purely for side-effects and return `void` (or a simple success boolean). -## Available Concrete Algorithms +--- + +## Available Algorithms Explore the specific layers of the algorithm module below: diff --git a/docs/hgl/algorithms/overview.md b/docs/hgl/algorithms/overview.md index 2e35af60..d3015772 100644 --- a/docs/hgl/algorithms/overview.md +++ b/docs/hgl/algorithms/overview.md @@ -1 +1,83 @@ # Algorithms Overview + +The HGL algorithms module adapts the strict architectural separation of concerns established by the GL module to the generalized topology of hypergraphs. Rather than writing monolithic functions that handle both search mechanics and specific problem-solving logic, the library splits hypergraph processing into two distinct layers: **Generic Templates (Engines)** and **Concrete Algorithms**. + +This separation ensures maximum code reuse, guarantees zero-cost abstractions, and handles the inherent complexity of hypergraph traversals—where moving between nodes requires a two-step expansion (from a vertex to an incident hyperedge, and then to the adjacent vertices). + +> [!NOTE] Shared Infrastructure with GL +> Because the HGL module is built strictly on top of the GL module, the algorithm architecture is highly symmetric. HGL directly imports and reuses much of the core algorithm utility from `gl::algorithm`, including callback concepts, zero-cost tags, and control-flow structs. + +--- + +## The Architectural Separation + +### 1. The Generic Templates (Engines) + +Located at the lowest level, the generic templates define the strict structural execution of a search. They know nothing about specific reachability rules or the hypergraph's topology. Their only responsibility is to manage the pending elements (a queue or stack) and invoke a series of user-provided callback hooks at specific moments during the traversal. + +Because traversing a hyperedge maps one-to-many (or many-to-many), the HGL engines expose specific hooks for *both* steps of the expansion. The core generic templates include: + +- [**`bfs`**](templates.md#the-core-engines): A queue-based Breadth-First Search engine. +- [**`dfs`**](templates.md#the-core-engines): A stack-based, iterative Depth-First Search engine. + +### 2. The Concrete Algorithms + +Concrete algorithms (like `breadth_first_search`, `backward_bfs`, or `forward_dfs`) are simple wrappers around the generic engines. They are responsible for: +- Allocating and managing memory for algorithm state tracking (e.g., allocating `std::vector` arrays for tracking both visited vertices *and* visited hyperedges). +- Defining the specific logic inside the lambda callbacks and predicates. +- Managing the final return types and structures. + +By separating these layers, CPP-HGL guarantees that all algorithms inherently benefit from the exact same optimized design and element management. + +--- + +## Core Algorithm Elements + +To interact with the generic templates or understand the concrete algorithms, you must be familiar with a few foundational types and tags used across the library. + +### Zero-Cost Callbacks (Imported from GL) + +The generic engines accept several different callbacks and predicates per invocation. However, forcing the compiler to execute or optimize away empty lambdas (e.g., `[]{}`) can add overhead in debug builds and slow down compilation. + +To solve this, HGL reuses the [**empty_callback**](../../cpp-gl/structgl_1_1algorithm_1_1empty__callback.md) tag from the GL module. If a hook is not needed, the engine receives this tag, and compile-time evaluation (`if constexpr`) completely optimizes away that branch of execution. + +### Tri-State Control Flow (Imported from GL) + +When evaluating hyperedges or adjacent vertices during a search, algorithms often need more control than a simple `true`/`false`. HGL reuses the [**decision**](../../cpp-gl/structgl_1_1algorithm_1_1decision.md) struct to provide a tri-state evaluation: + +- `decision::accept`: Proceed with the element (e.g., traverse the hyperedge, push the vertex to the queue). +- `decision::reject`: Skip this specific element, but continue the search. +- `decision::abort`: Instantly terminate the entire algorithm. + +### The Hypergraph Search Node + +Unlike a standard graph search node (which only tracks the current vertex and its predecessor), traversing a hypergraph requires knowing *how* a vertex was reached. By default, the active container of an HGL search engine stores `hgl::algorithm::search_node` structures. This is a lightweight triplet containing: + +1. `vertex_id`: The vertex currently being visited. +2. `pred_id`: The vertex from which this current vertex was reached (its parent in the traversal tree). +3. `he_id`: The specific hyperedge that was traversed to reach this vertex. + +If a vertex is the starting point of a search, the library uses the imported `no_root` tag, setting the `pred_id` to itself and the `he_id` to an invalid state. + +### The Result Discriminator (Imported from GL) + +Certain algorithms can either return a fully constructed result (like a `search_tree` mapping every vertex to its predecessor and incident hyperedge) or run purely for side-effects (like executing a custom lambda on every visited node). + +HGL manages this via the imported `result_discriminator` enum: +- `ret`: The algorithm will allocate memory and return a stateful object (e.g., `search_tree`). +- `noret`: The algorithm will execute purely for side-effects and return `void` (or a simple success boolean). + +### Traversal Policies + +To cleanly abstract traversals across undirected and BF-directed hypergraphs, HGL utilizes a `traversal_policy` injected with a `traversal_direction` tag (`forward` or `backward`). This policy dictates how the engine retrieves incident hyperedges (e.g., reading the "forward star" vs. the "backward star") and subsequent adjacent vertices (e.g., reading head nodes vs. tail nodes). + +--- + +## Available Algorithms + +Explore the specific layers of the algorithm module below: + +- [**The Generic Templates**](templates.md): A detailed explanation of the core traversal engines and their two-step execution sequence. +- [**Standard Traversals**](traversal.md#standard-traversals): Unrestricted wrappers for basic component discovery across any hypergraph topology (`breadth_first_search`, `depth_first_search`). +- [**Backward Searches**](traversal.md#backward-search): Traversals enforcing strict B-reachability semantics on BF-directed hypergraphs (`backward_bfs`, `backward_dfs`), where a hyperedge is only traversed once *all* of its tail vertices are visited. +- [**Forward Searches**](traversal.md#forward-search): Traversals enforcing strict F-reachability semantics on BF-directed hypergraphs (`forward_bfs`, `forward_dfs`), where a hyperedge is only traversed once *all* of its head vertices are visited. From 9de28ca0094b0f1b644da7df7c1eb533a185a2f9 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 23:12:09 +0200 Subject: [PATCH 26/28] alg templates page --- docs/gl/algorithms/templates.md | 2 +- docs/hgl/algorithms/templates.md | 123 +++++++++++++++++++++++++++++++ include/gl/io/graph_fio.hpp | 2 +- include/hgl/algorithm/core.hpp | 8 +- 4 files changed, 131 insertions(+), 4 deletions(-) diff --git a/docs/gl/algorithms/templates.md b/docs/gl/algorithms/templates.md index 67c3d530..9c9fb740 100644 --- a/docs/gl/algorithms/templates.md +++ b/docs/gl/algorithms/templates.md @@ -15,7 +15,7 @@ The library provides four primary traversal engines. ## The Callback Sequence -The true power of the generic templates lies in their callback/predicate hooks. Every iteration of the engine loop rigidly follows a defined sequence. By injecting custom lambdas (or omitting them via the [**empty_callback**](../../cpp-gl/structgl_1_1algorithm_1_1empty__callback.md)), you dictate the algorithm's behavior. +The true power of the generic templates lies in their callback/predicate hooks. Every iteration of the engine loop rigidly follows a defined sequence. By injecting custom callbacks (or omitting them via the [**empty_callback**](../../cpp-gl/structgl_1_1algorithm_1_1empty__callback.md)), you dictate the algorithm's behavior. ### Execution Flowchart diff --git a/docs/hgl/algorithms/templates.md b/docs/hgl/algorithms/templates.md index c4dd9fec..c4443e83 100644 --- a/docs/hgl/algorithms/templates.md +++ b/docs/hgl/algorithms/templates.md @@ -1 +1,124 @@ # The Generic Templates + +The generic templates are the workhorse engines of the HGL algorithm module. Rather than implementing distinct algorithms, they implement the strict structural iteration required to safely navigate a hypergraph's topology. + +Traversing a hypergraph is fundamentally more complex than traversing a standard graph. Instead of moving directly from vertex to vertex, a hypergraph traversal requires a **two-step expansion**: + +1. Expand from the current vertex to its incident hyperedges. +2. Expand from those hyperedges to their *target* vertices. + +All engines operate on this principle: pop a search node from a pending container, evaluate it, execute the two-step expansion across valid hyperedges and vertices, and push the discovered targets back into the container. + +## The Core Engines + +The library provides two primary traversal engines for hypergraphs: + +- [**`bfs` (Breadth-First Search)**](../../cpp-gl/group__HGL-Algorithm.md#function-bfs): Uses a `std::queue`. Explores the hypergraph level by level, expanding uniformly outward from the initial range. +- [**`dfs` (Depth-First Search)**](../../cpp-gl/group__HGL-Algorithm.md#function-dfs): Uses a `std::stack`. Dives as deeply as possible along a branch before backtracking. + +## Traversal Directions & Policies + +Because **BF-directed hypergraphs** distinguish between *tail* (source) and *head* (destination) vertices, the generic engines must know how to flow through them. This is controlled via the [**traversal_direction**](../../cpp-gl/group__HGL-Algorithm.md#enum-traversal_direction) template parameter (which defaults to `forward`) and resolved by an internal `traversal_policy`. + +> [!NOTE] API Note +> +> The library utilizes C++20's `using enum` feature for the `traversal_direction` enum type, allowing you to access these tags directly via `hgl::algorithm::forward` and `hgl::algorithm::backward`. + +- **`forward`**: + + - Retrieves hyperedges originating from the current vertex (the **forward star** / `out_hyperedge_ids`). + - Retrieves the vertices targeted by those hyperedges (the **head nodes** / `head_ids`). + +- **`backward`**: + + - Retrieves hyperedges entering the current vertex (the **backward star** / `in_hyperedge_ids`). + - Retrieves the vertices originating those hyperedges (the **tail nodes** / `tail_ids`). + +> [!NOTE] Undirected Hypergraphs +> +> For undirected hypergraphs, the traversal policy seamlessly falls back to retrieving all `incident_hyperedge_ids` and `incident_vertex_ids` regardless of the traversal direction. + +## The Callback Sequence + +The true power of the generic templates lies in their callback and predicate hooks. Every iteration of the engine loop rigidly follows a defined sequence. By injecting custom callbacks (or omitting them via the imported [**empty_callback**](../../cpp-gl/group__HGL-Algorithm.md#typedef-empty_callback)), you dictate the algorithm's exact behavior. + +### Execution Flowchart + +For a single popped `curr_node` in the `bfs` or `dfs` templates, the execution flow looks exactly like this: + +1. **`visit_pred(curr_node)`** + Evaluated immediately after popping the node. If it returns `false`, the node is skipped entirely, and the loop moves to the next node in the queue or stack. + +2. **`pre_visit(curr_node)`** + A state-modification hook executed right before the vertex is officially processed. + +3. **`visit(curr_node)`** + The primary callback. If this returns `false`, the entire search is immediately aborted. + +4. **Hyperedge Expansion (Step 1)** + The engine queries the `traversal_policy` for the target hyperedges. For each `he_id`: + + - **`traverse_he_pred(he_id, curr_node.vertex_id)`** + Evaluates whether the hyperedge should be traversed. Returns a `decision`: + - `abort`: Kills the entire algorithm. + - `reject`: Ignores this hyperedge and moves to the next. + - `accept`: Proceeds to evaluate the hyperedge's vertices. + +5. **Vertex Expansion (Step 2)** + If the hyperedge was accepted, the engine queries the `traversal_policy` for the target vertices within that hyperedge. For each `target_id` (skipping the one we just came from): + + - **`enqueue_pred(tgt_node)`** + Evaluates whether the newly constructed `tgt_node` (containing the `target_id`, `curr_node.vertex_id`, and `he_id`) should be pushed to the active container. Returns a `decision`: + - `abort`: Kills the entire algorithm. + - `reject`: Ignores this specific target vertex. + - `accept`: Pushes the `tgt_node` to the queue or stack. + +6. **`post_visit(curr_node)`** + Executed after all adjacent hyperedges and their target vertices have been evaluated and processed. + +## Customizing the Traversal + +By wiring up these 6 hooks, you can build highly specific reachability algorithms. For instance, to ensure we do not get stuck in infinite loops, we need to track both visited vertices and visited hyperedges. + +### Example: Custom Forward BFS Engine + +```cpp +#include +#include +#include + +// Assume 'hg' is a populated BF-directed hypergraph and 'start_id' is valid. +std::vector visited_v(hg.n_vertices(), false); // (1)! +std::vector visited_he(hg.n_hyperedges(), false); + +using search_node = hgl::algorithm::search_node; +std::vector init_nodes = {search_node{start_id}}; // (2)! + +bool success = hgl::algorithm::bfs( // (3)! + hg, + init_nodes, + [&](const auto& node) { return not visited_v[node.vertex_id]; }, // (4)! + [&](const auto& node) { // (5)! + visited_v[node.vertex_id] = true; + return true; // continue search + }, + [&](auto he_id, auto /*source_id*/) -> hgl::algorithm::decision { // (6)! + if (visited_he[he_id]) + return hgl::algorithm::decision::reject; + + visited_he[he_id] = true; + return hgl::algorithm::decision::accept; + }, + [&](const auto& tgt_node) -> hgl::algorithm::decision { // (7)! + return !visited_v[tgt_node.vertex_id]; + } +); +``` + +1. Initialize state-tracking vectors for both vertices and hyperedges. +2. Set up the initial queue range with a root node. +3. Explicitly invoke the template with `traversal_direction::forward` (the default, but explicitly shown here for clarity). +4. **`visit_pred`**: Reject nodes in the queue if they were already visited by an earlier, faster branch. +5. **`visit`**: Mark the vertex as visited. +6. **`traverse_he_pred`**: Check if the hyperedge was already traversed. If not, mark it traversed and `accept` it. Returning a `decision` type here is required by the generic engines. +7. **`enqueue_pred`**: Only enqueue adjacent vertices that haven't been visited yet (implicitly casts the boolean condition to a `decision::accept` / `decision::reject`). diff --git a/include/gl/io/graph_fio.hpp b/include/gl/io/graph_fio.hpp index 16ff03cc..b6e68e9a 100644 --- a/include/gl/io/graph_fio.hpp +++ b/include/gl/io/graph_fio.hpp @@ -118,7 +118,7 @@ requires(std::same_as) /// Saves the graph topology and optionally its properties using the Graph Specification Format (GSF). /// The function strictly respects the @ref gl::io::write "write" and @ref gl::io::append "append" safety guards. /// -/// @tparam GraphType The concrete type of the graph being saved. Must satisfy [**c_graph**](gl_concepts.md#hgl-traits-c-graph). +/// @tparam GraphType The concrete type of the graph being saved. Must satisfy [**c_graph**](gl_concepts.md#gl-traits-c-graph). /// @tparam Mode The save behavior tag (@ref gl::io::write "write" or @ref gl::io::append "append"). Defaults to `write`. /// @param graph The graph instance to serialize. /// @param path The filesystem path where the graph will be saved. Defaults to `"graph.gsf"`. diff --git a/include/hgl/algorithm/core.hpp b/include/hgl/algorithm/core.hpp index fcdc4138..92b35fb9 100644 --- a/include/hgl/algorithm/core.hpp +++ b/include/hgl/algorithm/core.hpp @@ -144,15 +144,19 @@ namespace algorithm { /// @ingroup HGL-Algorithm /// @brief Specifies the direction of traversal for *BF-directed* hypergraphs. /// -/// > [!IMPORTANT] API Simplicity +/// > [!IMPORTANT] API Note /// > -/// > To ensure API simplicity, the `traversal_direction` is used for undirected hypergraphs as well by +/// > - To ensure API simplicity, the `traversal_direction` is used for undirected hypergraphs as well by /// > the generic traversal templates. However, due to the structural nature of undirected hypergraphs, /// > both direction values implicitly yield the exact same traversal pattern for undirected hypergraphs. +/// > +/// > - The library utilizes C++20's `using enum` feature for the `traversal_direction` enum type, allowing +/// > you to access these tags directly via `hgl::algorithm::forward` and `hgl::algorithm::backward`. enum class traversal_direction : bool { forward, ///< Traverse following the forward star (tail to head). backward ///< Traverse following the backward star (head to tail). }; +using enum traversal_direction; /// @ingroup HGL-Algorithm /// @brief Policy defining how to extract incident hyperedges and target vertices during traversal. From 0c08429ce715e203b7de8d261b61d15ac33cbcd0 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Wed, 29 Apr 2026 23:30:03 +0200 Subject: [PATCH 27/28] alg traversal doc --- docs/gl/algorithms/pathfinding.md | 4 +- docs/gl/algorithms/topological_algs.md | 8 +- docs/gl/algorithms/traversal.md | 6 +- docs/hgl/algorithms/overview.md | 4 +- docs/hgl/algorithms/traversal.md | 120 ++++++++++++++++ docs/hgl/architecture.md | 6 +- docs/img/doc/dark/traversal-example.svg | 174 ++++++++++++----------- docs/img/doc/light/traversal-example.svg | 174 ++++++++++++----------- 8 files changed, 319 insertions(+), 177 deletions(-) diff --git a/docs/gl/algorithms/pathfinding.md b/docs/gl/algorithms/pathfinding.md index ba679461..69eb2ccb 100644 --- a/docs/gl/algorithms/pathfinding.md +++ b/docs/gl/algorithms/pathfinding.md @@ -66,8 +66,8 @@ std::cout << "Path: " > [!INFO] Algorithmic Complexity > > The time complexity depends heavily on the underlying representation of GraphType and the priority queue overhead: -> - **Adjacency List Representations:** $\mathcal{O}((|V| + |E|) \log |V|)$. The queue operations scale with the number of edges, making this optimal for sparse graphs. -> - **Adjacency Matrix Representations:** $\mathcal{O}(|V|^2 + |E| \log |V|)$. Iterating over adjacent vertices requires scanning the entire $|V|$-length matrix row, shifting the bottleneck to row traversal. +> - **Adjacency List Representations:** $O((|V| + |E|) \log |V|)$. The queue operations scale with the number of edges, making this optimal for sparse graphs. +> - **Adjacency Matrix Representations:** $O(|V|^2 + |E| \log |V|)$. Iterating over adjacent vertices requires scanning the entire $|V|$-length matrix row, shifting the bottleneck to row traversal. --- diff --git a/docs/gl/algorithms/topological_algs.md b/docs/gl/algorithms/topological_algs.md index 06c5f130..290c5599 100644 --- a/docs/gl/algorithms/topological_algs.md +++ b/docs/gl/algorithms/topological_algs.md @@ -48,8 +48,8 @@ else { > [!INFO] Algorithmic Complexity > > The time complexity depends on the underlying representation of `GraphType`: -> - **Adjacency List Representations:** $\mathcal{O}(|V| + |E|)$. -> - **Adjacency Matrix Representations:** $\mathcal{O}(|V|^2)$. Iterating over adjacent vertices requires scanning the entire $|V|$-length matrix row. +> - **Adjacency List Representations:** $O(|V| + |E|)$. +> - **Adjacency Matrix Representations:** $O(|V|^2)$. Iterating over adjacent vertices requires scanning the entire $|V|$-length matrix row. --- @@ -87,5 +87,5 @@ else { > [!INFO] Algorithmic Complexity > > The time complexity depends on the underlying representation of `GraphType`: -> - **Adjacency List Representations:** $\mathcal{O}(|V| + |E|)$. -> - **Adjacency Matrix Representations:** $\mathcal{O}(|V|^2)$. Iterating over adjacent vertices requires scanning the entire $|V|$-length matrix row. +> - **Adjacency List Representations:** $O(|V| + |E|)$. +> - **Adjacency Matrix Representations:** $O(|V|^2)$. Iterating over adjacent vertices requires scanning the entire $|V|$-length matrix row. diff --git a/docs/gl/algorithms/traversal.md b/docs/gl/algorithms/traversal.md index ddf35449..29e3d270 100644 --- a/docs/gl/algorithms/traversal.md +++ b/docs/gl/algorithms/traversal.md @@ -51,8 +51,6 @@ gl::algorithm::breadth_first_search( // (2)! 3. Traverses all vertices in the graph, automatically jumping to new roots if disconnected components are found. 4. A custom PreVisitCallback executed exactly when a vertex is marked as visited. - - --- ## Depth-First Search (DFS) @@ -110,6 +108,6 @@ The following diagram illustrates the fundamental difference in exploration orde The time complexity for all three traversals (BFS, Iterative DFS, and Recursive DFS) depends entirely on the underlying representation of `GraphType`: -- **Adjacency List Representations:** $\mathcal{O}(|V| + |E|)$. The algorithm strictly evaluates existing edges, making this optimal for sparse graphs. +- **Adjacency List Representations:** $O(|V| + |E|)$. The algorithm strictly evaluates existing edges, making this optimal for sparse graphs. -- **Adjacency Matrix Representations:** $\mathcal{O}(|V|^2)$. To find adjacent unvisited vertices, the algorithm must scan the entire $|V|$-length matrix row for every visited vertex, shifting the bottleneck from edge evaluation to row traversal. +- **Adjacency Matrix Representations:** $O(|V|^2)$. To find adjacent unvisited vertices, the algorithm must scan the entire $|V|$-length matrix row for every visited vertex, shifting the bottleneck from edge evaluation to row traversal. diff --git a/docs/hgl/algorithms/overview.md b/docs/hgl/algorithms/overview.md index d3015772..cb5745e0 100644 --- a/docs/hgl/algorithms/overview.md +++ b/docs/hgl/algorithms/overview.md @@ -79,5 +79,5 @@ Explore the specific layers of the algorithm module below: - [**The Generic Templates**](templates.md): A detailed explanation of the core traversal engines and their two-step execution sequence. - [**Standard Traversals**](traversal.md#standard-traversals): Unrestricted wrappers for basic component discovery across any hypergraph topology (`breadth_first_search`, `depth_first_search`). -- [**Backward Searches**](traversal.md#backward-search): Traversals enforcing strict B-reachability semantics on BF-directed hypergraphs (`backward_bfs`, `backward_dfs`), where a hyperedge is only traversed once *all* of its tail vertices are visited. -- [**Forward Searches**](traversal.md#forward-search): Traversals enforcing strict F-reachability semantics on BF-directed hypergraphs (`forward_bfs`, `forward_dfs`), where a hyperedge is only traversed once *all* of its head vertices are visited. +- [**Backward Searches**](traversal.md#backward-searches-b-reachability): Traversals enforcing strict B-reachability semantics on BF-directed hypergraphs (`backward_bfs`, `backward_dfs`), where a hyperedge is only traversed once *all* of its tail vertices are visited. +- [**Forward Searches**](traversal.md#forward-searches-f-reachability): Traversals enforcing strict F-reachability semantics on BF-directed hypergraphs (`forward_bfs`, `forward_dfs`), where a hyperedge is only traversed once *all* of its head vertices are visited. diff --git a/docs/hgl/algorithms/traversal.md b/docs/hgl/algorithms/traversal.md index c64653fb..77761ec5 100644 --- a/docs/hgl/algorithms/traversal.md +++ b/docs/hgl/algorithms/traversal.md @@ -1 +1,121 @@ # Concrete Traversals + +While the [Generic Templates](templates.md) provide immense power and flexibility, manually setting up the `visited` arrays, initialization ranges, and multi-step expansion predicates for simple tasks can be overly verbose. + +To solve this, HGL provides concrete traversal algorithms: **Breadth-First Search (BFS)** and **Depth-First Search (DFS)**, alongside specialized directional reachability wrappers. These functions act as simple wrappers around the core generic engines. They automatically allocate and manage the necessary state-tracking containers for both vertices and hyperedges, while still allowing you to inject custom logic via `pre_visit` and `post_visit` callbacks. + +--- + +## Shared Behaviors and Mechanics + +All concrete traversals in the HGL module share a unified design philosophy regarding return types and component handling, mirroring the GL module. + +### The Result Discriminator + +By default, concrete traversals are designed to build a traversal tree. However, sometimes you only want to traverse a hypergraph for its side effects (e.g., modifying vertex properties) without paying the memory allocation cost of building a predecessor map. + +You can explicitly control this using the template's `Result` discriminator: + +- [**`hgl::algorithm::ret`**](../../cpp-gl/group__HGL-Algorithm.md#enum-result_discriminator) (Default): The algorithm allocates and populates an [**hgl::algorithm::search_tree**](../../cpp-gl/group__HGL-Algorithm.md#typedef-search_tree). This structure records both the predecessor vertex and the specific hyperedge that was traversed to discover each vertex. +- [**`hgl::algorithm::noret`**](../../cpp-gl/group__HGL-Algorithm.md#enum-result_discriminator): The algorithm executes purely for side effects. The search tree allocation is completely optimized away at compile time, and the function returns `void`. + +### Handling Disconnected Components + +Hypergraphs are rarely fully connected. Standard traversal wrappers gracefully handle this using the `root_vertex_id` parameter: + +- **Specific Root:** If you pass a valid `vertex_id`, the algorithm will explore *only* the connected component reachable from that specific root. +- [**`hgl::algorithm::no_root`**](../../cpp-gl/group__HGL-Algorithm.md#variable-no_root) (Default): If you omit the root ID, the algorithm automatically iterates through the entire hypergraph, sequentially triggering searches on unvisited vertices to ensure that *every disconnected component* is fully traversed. + +--- + +## Standard Traversals + +The standard [**`breadth_first_search`**](../../cpp-gl/group__HGL-Algorithm.md#function-breadth_first_search) and [**`depth_first_search`**](../../cpp-gl/group__HGL-Algorithm.md#function-depth_first_search) algorithms provide unrestricted exploration of a hypergraph's topology. + +For **undirected hypergraphs**, these algorithms simply flood through all incident hyperedges and vertices. + +For **BF-directed hypergraphs**, they execute a naive directional traversal: a hyperedge is traversed immediately upon discovering *any* of its tail vertices, instantly reaching all of its head vertices. + +### Example Usage + +```cpp +#include +#include +#include + +auto search_tree = hgl::algorithm::breadth_first_search(hypergraph, start_id); // (1)! +hgl::algorithm::depth_first_search( // (2)! + hypergraph, + hgl::algorithm::no_root, + [](const auto& node) { // (3)! + std::cout << "Discovered vertex: " << node.vertex_id << '\n'; + } +); +``` + +1. Standard execution. Returns a search_tree representing the traversal from start_id. +2. Execution purely for side-effects over the entire hypergraph. +3. The `pre_visit` callback logs the discovery/visitation of a vertex to the console. + +--- + +## Backward Searches (B-Reachability) + +In many domains (such as chemical reaction networks or logic circuits), reaching a single input is not enough to trigger an event. The HGL module provides [**`backward_bfs`**](../../cpp-gl/group__HGL-Algorithm.md#function-backward_bfs) and [**`backward_dfs`**](../../cpp-gl/group__HGL-Algorithm.md#function-backward_dfs) to enforce strict **B-reachability** semantics on BF-directed hypergraphs. + +This search travels forward through the network (using the forward star / outgoing hyperedges). Unlike standard traversals, a backward search utilizes a blocking predicate: **A hyperedge is only fully traversed (and its head vertices enqueued) after *all* of its tail (source) vertices have been visited.** This is exceptionally useful for forward propagation or cascading activation, determining the complete set of subsequent states or products that can be successfully activated given a specific set of initial conditions. + +Because complex reachability often requires multiple initial inputs, these algorithms accept a `RootRange` of initial vertices instead of a single root. + +### Example Usage + +```cpp +#include + +std::vector roots = { start_id_1, start_id_2 }; // (1)! +auto b_reachability_tree = hgl::algorithm::backward_bfs(hypergraph, roots); // (2)! +``` + +1. Supply multiple initial conditions (or inputs) to the network. +2. The algorithm blocks at hyperedges until all tail dependencies are met. + +--- + +## Forward Searches (F-Reachability) + +Conversely, [**`forward_bfs`**](../../cpp-gl/group__HGL-Algorithm.md#function-forward_bfs) and [**`forward_dfs`**](../../cpp-gl/group__HGL-Algorithm.md#function-forward_dfs) enforce strict **F-reachability** semantics on BF-directed hypergraphs. + +This search travels in reverse through the network (using the backward star / incoming hyperedges). **A hyperedge is only traversed (and its tail vertices enqueued) after *all* of its head (destination) vertices have been visited.** This is exceptionally useful for backward chaining, determining what minimal set of inputs is strictly required to achieve a specific target state. + +### Example Usage + +```cpp +#include + +std::vector targets = { target_id_1 }; // (1)! +auto f_reachability_tree = hgl::algorithm::forward_dfs(hypergraph, targets); +``` + +1. Define the target outputs we want to trace back from. + +--- + +## Algorithmic Complexity and Layout Impact + +Traversing a hypergraph involves evaluating vertices, hyperedges, and the full span of hyperedge incidences ($I$, defined as the total number of connections across the entire hypergraph: $I = \sum deg(v) = \sum |e|$). + +Because the HGL generic templates utilize a two-step expansion (querying incident hyperedges, then querying adjacent vertices), **the algorithmic complexity of your traversal is drastically impacted by your chosen Representation Model and Layout Tag.** + +### 1. Incidence Lists (`list_t` / `flat_list_t`) + +- **`bidirectional_t` Layout (Optimal):** $O(|V| + |E| + I)$. Because the bidirectional layout natively supports $O(1)$ iteration from a vertex to its hyperedges AND from a hyperedge to its vertices, the engine traverses the topology effortlessly. **This is the only incidence list layout recommended for traversals.** +- **`vertex_major_t` Layout:** $O(|V| + |E| \cdot (|V| + I))$. Finding hyperedges from a vertex is fast, but step 2 (finding vertices within that hyperedge) requires a full-structure scan. Traversal performance is severely degraded. +- **`hyperedge_major_t` Layout:** $O(|E| + |V| \cdot (|E| + I))$. Finding vertices from a hyperedge is fast, but step 1 (finding incident hyperedges from a vertex) requires a full-structure scan. Traversal performance is severely degraded. + +> [!WARNING] Asymmetric Layout Traversals +> +> If you are using a strictly single-major layout (`vertex_major_t` or `hyperedge_major_t`) due to memory constraints, executing a native BFS or DFS over the hypergraph will be excessively slow due to the full-structure scans required to jump between major and minor elements. If traversal is required, consider converting to a `bidirectional_t` list or using an Incidence Graph Projection via the GL module. + +### 2. Incidence Matrices (`matrix_t` / `flat_matrix_t`) + +- **Any Layout:** $O(|V| \cdot |E|)$. To find incident hyperedges, the algorithm must scan across $|E|$ elements for a given vertex. To find adjacent vertices, it must scan across $|V|$ elements for a given hyperedge. diff --git a/docs/hgl/architecture.md b/docs/hgl/architecture.md index 621b5165..1ac7cec2 100644 --- a/docs/hgl/architecture.md +++ b/docs/hgl/architecture.md @@ -328,9 +328,9 @@ The complexities of topological queries is identical for standard (`matrix_t`) a | Mutation Operation | `matrix_t` (Standard) | `flat_matrix_t` (Flat) | | :--- | :--- | :--- | -| **Add Vertex** | $O(\vert E \vert)$ amortized if `vertex_major_t`
$O(\vert V \vert \times \vert E \vert)$ if `hyperedge_major_t` | $O(\vert V \vert \times \vert E \vert)$ | -| **Add Hyperedge** | $O(\vert V \vert \times \vert E \vert)$ if `vertex_major_t`
$O(\vert V \vert)$ amortized if `hyperedge_major_t` | $O(\vert V \vert \times \vert E \vert)$ | -| **Remove Vertex / Hyperedge** | $O(\vert V \vert \times \vert E \vert)$ (Shift rows/cols) | $O(\vert V \vert \times \vert E \vert)$ | +| **Add Vertex** | $O(\vert E \vert)$ amortized if `vertex_major_t`
$O(\vert V \vert \cdot \vert E \vert)$ if `hyperedge_major_t` | $O(\vert V \vert \cdot \vert E \vert)$ | +| **Add Hyperedge** | $O(\vert V \vert \cdot \vert E \vert)$ if `vertex_major_t`
$O(\vert V \vert)$ amortized if `hyperedge_major_t` | $O(\vert V \vert \cdot \vert E \vert)$ | +| **Remove Vertex / Hyperedge** | $O(\vert V \vert \cdot \vert E \vert)$ (Shift rows/cols) | $O(\vert V \vert \cdot \vert E \vert)$ | | **Add / Remove Incidence** (Bind/Unbind) | $O(1)$ | $O(1)$ | > [!NOTE] Matrix Layouts and Cache Locality diff --git a/docs/img/doc/dark/traversal-example.svg b/docs/img/doc/dark/traversal-example.svg index 6ff9ffb6..9aaa6f7e 100644 --- a/docs/img/doc/dark/traversal-example.svg +++ b/docs/img/doc/dark/traversal-example.svg @@ -1,82 +1,94 @@ - - -v -0 -1 - -v -1 -2 - -v -2 -3 - -v -3 -4 - -v -4 -5 - -v -5 -6 - - - - - - - - - - - - - - - -Breadth-FirstSearch(LevelbyLevel) - -v -0 -1 - -v -1 -2 - -v -2 -5 - -v -3 -3 - -v -4 -4 - -v -5 -6 - - - - - - - - - - - - - - - -Depth-FirstSearch(BranchbyBranch) + + +v +0 + +v +1 + +v +2 + +v +3 + +v +4 + +v +5 + + + + + + + + + + + + + + + + +1 + +2 + +3 + +4 + +5 + +6 +Breadth-FirstSearch(LevelbyLevel) + +v +0 + +v +1 + +v +2 + +v +3 + +v +4 + +v +5 + + + + + + + + + + + + + + + + +1 + +2 + +5 + +3 + +4 + +6 +Depth-FirstSearch(BranchbyBranch) diff --git a/docs/img/doc/light/traversal-example.svg b/docs/img/doc/light/traversal-example.svg index 96032a6e..32c021fe 100644 --- a/docs/img/doc/light/traversal-example.svg +++ b/docs/img/doc/light/traversal-example.svg @@ -1,82 +1,94 @@ - - -v -0 -1 - -v -1 -2 - -v -2 -3 - -v -3 -4 - -v -4 -5 - -v -5 -6 - - - - - - - - - - - - - - - -Breadth-FirstSearch(LevelbyLevel) - -v -0 -1 - -v -1 -2 - -v -2 -5 - -v -3 -3 - -v -4 -4 - -v -5 -6 - - - - - - - - - - - - - - - -Depth-FirstSearch(BranchbyBranch) + + +v +0 + +v +1 + +v +2 + +v +3 + +v +4 + +v +5 + + + + + + + + + + + + + + + + +1 + +2 + +3 + +4 + +5 + +6 +Breadth-FirstSearch(LevelbyLevel) + +v +0 + +v +1 + +v +2 + +v +3 + +v +4 + +v +5 + + + + + + + + + + + + + + + + +1 + +2 + +5 + +3 + +4 + +6 +Depth-FirstSearch(BranchbyBranch) From 6c4ce5edde41784a9bf6025195d0ed80310943c8 Mon Sep 17 00:00:00 2001 From: SpectraL519 Date: Thu, 30 Apr 2026 00:42:31 +0200 Subject: [PATCH 28/28] alg properties doc + moved property functions to algorithm/properties.hpp --- docs/hgl/algorithms/overview.md | 1 + docs/hgl/algorithms/properties.md | 119 +++++++++++ docs/hgl/properties.md | 1 - include/hgl/algorithm.hpp | 2 + include/hgl/algorithm/properties.hpp | 202 ++++++++++++++++++ include/hgl/hypergraph.hpp | 188 ----------------- mkdocs.yml | 2 +- tests/source/hgl/test_alg_properties.cpp | 247 +++++++++++++++++++++++ tests/source/hgl/test_hypergraph.cpp | 236 ---------------------- 9 files changed, 572 insertions(+), 426 deletions(-) create mode 100644 docs/hgl/algorithms/properties.md delete mode 100644 docs/hgl/properties.md create mode 100644 include/hgl/algorithm/properties.hpp create mode 100644 tests/source/hgl/test_alg_properties.cpp diff --git a/docs/hgl/algorithms/overview.md b/docs/hgl/algorithms/overview.md index cb5745e0..c493d13d 100644 --- a/docs/hgl/algorithms/overview.md +++ b/docs/hgl/algorithms/overview.md @@ -81,3 +81,4 @@ Explore the specific layers of the algorithm module below: - [**Standard Traversals**](traversal.md#standard-traversals): Unrestricted wrappers for basic component discovery across any hypergraph topology (`breadth_first_search`, `depth_first_search`). - [**Backward Searches**](traversal.md#backward-searches-b-reachability): Traversals enforcing strict B-reachability semantics on BF-directed hypergraphs (`backward_bfs`, `backward_dfs`), where a hyperedge is only traversed once *all* of its tail vertices are visited. - [**Forward Searches**](traversal.md#forward-searches-f-reachability): Traversals enforcing strict F-reachability semantics on BF-directed hypergraphs (`forward_bfs`, `forward_dfs`), where a hyperedge is only traversed once *all* of its head vertices are visited. +- [**Hypergraph Properties**](properties.md): Lightweight functional utilities for evaluating global structural characteristics, including degree bounds, rank, corank, regularity, and uniformity. diff --git a/docs/hgl/algorithms/properties.md b/docs/hgl/algorithms/properties.md new file mode 100644 index 00000000..b1b70f61 --- /dev/null +++ b/docs/hgl/algorithms/properties.md @@ -0,0 +1,119 @@ +# Hypergraph Properties + +Beyond hypergraph traversals, the HGL algorithms module provides a suite of lightweight, functional utilities to evaluate the global structural properties of a hypergraph. These algorithms allow you to quickly determine bounds, uniformity, and regularity across the topology. + +These algorithms are grouped into four primary categories: + +- [Vertex Degree Bounds](#vertex-degree-bounds): Functions for evaluating the minimum and maximum connectivity of vertices. +- [Hyperedge Size Bounds](#hyperedge-size-bounds-rank--corank): Functions for evaluating the scale of hyperedges (Rank and Corank). +- [Regularity](#regularity): Functions to check if all vertices share the same degree. +- [Uniformity](#uniformity): Functions to check if all hyperedges contain the same number of vertices. + +> [!NOTE] Algorithmic Complexity +> +> All property algorithms in this module operate by requesting a `degree_map()` or `hyperedge_size_map()` from the underlying hypergraph structure, and then performing a linear $\mathcal{O}(N)$ reduction (like `std::ranges::max_element` or a constant-check loop). Thus, the actual performance of these algorithms is entirely dictated by the complexity of generating the size/degree map, which varies depending on your chosen [Representation Layout](../architecture.md#representation-layouts). + +--- + +## Vertex Degree Bounds + +These functions evaluate the minimum and maximum degrees present within the hypergraph. + +### Formal Definitions + +For an undirected hypergraph $H = (V, E)$: + +- **Maximum Degree** $\Delta(H)$: The largest degree of any vertex in the hypergraph. $\Delta(H) = \max\limits_{v \in V} deg(v)$ +- **Minimum Degree** $\delta(H)$: The smallest degree of any vertex in the hypergraph. $\delta(H) = \min\limits_{v \in V} deg(v)$ + +For BF-directed hypergraphs, these concepts are further split into **in-degree** (the number of incoming/head hyperedges) and **out-degree** (the number of outgoing/tail hyperedges). + +### Available Functions + +**Undirected & Directed (Total Degree):** + +- [**max_degree(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-max_degree): Returns the maximum degree $\Delta(H)$ among all vertices. +- [**min_degree(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-min_degree): Returns the minimum degree $\delta(H)$ among all vertices. + +**BF-Directed Specifically:** + +- [**max_out_degree(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-max_out_degree): Returns the maximum out-degree among all vertices. +- [**min_out_degree(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-min_out_degree): Returns the minimum out-degree among all vertices. +- [**max_in_degree(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-max_in_degree): Returns the maximum in-degree among all vertices. +- [**min_in_degree(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-min_in_degree): Returns the minimum in-degree among all vertices. + +**Note:** If the hypergraph is empty, these functions safely return `0`. + +--- + +## Hyperedge Size Bounds (Rank & Corank) + +Unlike standard graphs where all edges have a size of exactly 2, hyperedges can encompass any number of vertices. The bounds of these sizes define the hypergraph's Rank and Corank. + +### Formal Definitions + +For an undirected hypergraph $H = (V, E)$: + +- **Rank** $r(H)$: The maximum size (cardinality) of any hyperedge in the hypergraph. $r(H) = \max\limits_{e \in E} |e|$ +- **Corank** $cr(H)$: The minimum size (cardinality) of any hyperedge in the hypergraph. $cr(H) = \min\limits_{e \in E} |e|$ + +### Available Functions + +**Undirected & Directed (Total Size):** + +- [**rank(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-rank): Calculates the maximum size of any hyperedge. +- [**corank(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-corank): Calculates the minimum size of any hyperedge. + +**BF-Directed Specifically:** + +- [**max_tail_size(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-max_tail_size): Calculates the maximum number of tail (source) vertices among all hyperedges. +- [**min_tail_size(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-min_tail_size): Calculates the minimum number of tail (source) vertices among all hyperedges. +- [**max_head_size(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-max_head_size): Calculates the maximum number of head (destination) vertices among all hyperedges. +- [**min_head_size(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-min_head_size): Calculates the minimum number of head (destination) vertices among all hyperedges. + +--- + +## Regularity + +A hypergraph is evaluated based on the consistency of its vertices' connectivity. + +### Formal Definitions + +- **$k$-Regular:** A hypergraph $H$ is $k$-regular if every vertex $v \in V$ is incident to exactly $k$ hyperedges: $(\forall v \in V)(deg(v) = k)$ +- **Regular:** A hypergraph is structurally regular if there exists some integer $k$ such that the hypergraph is $k$-regular (i.e., all vertices have the exact same degree): $(\exists k \in \mathbb{N})(\forall v \in V)(deg(v) = k)$ + +### Available Functions + +**Undirected & Directed (Total Degree):** + +- [**is_regular(hypergraph, k)**](../../cpp-gl/group__HGL-Algorithm.md#function-is_regular): Evaluates if all vertices have a degree of exactly `k`. +- [**is_regular(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-is_regular): Evaluates if all vertices share the same degree, regardless of what that specific degree is. + +**BF-Directed Specifically:** + +- [**is_out_regular(hypergraph, k)**](../../cpp-gl/group__HGL-Algorithm.md#function-is_out_regular) / [**is_out_regular(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-is_out_regular): Evaluates if the hypergraph is out-$k$-regular or structurally out-regular (based strictly on out-degrees). +- [**is_in_regular(hypergraph, k)**](../../cpp-gl/group__HGL-Algorithm.md#function-is_in_regular) / [**is_in_regular(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-is_in_regular): Evaluates if the hypergraph is in-$k$-regular or structurally in-regular (based strictly on in-degrees). + +--- + +## Uniformity + +A hypergraph is evaluated based on the consistency of its hyperedges' sizes. + +### Formal Definitions + +- **$k$-Uniform:** A hypergraph $H$ is $k$-uniform if every hyperedge $e \in E$ contains exactly $k$ vertices: $(\forall e \in E)(|e| = k)$ + *Note: A 2-uniform undirected hypergraph is equivalent to a standard undirected graph.* +- **Uniform:** A hypergraph is structurally uniform if there exists some integer $k$ such that the hypergraph is $k$-uniform (i.e., all hyperedges have the exact same size): $(\exists k \in \mathbb{N})(\forall e \in E)(|e| = k)$ + +### Available Functions + +**Undirected & Directed (Total Size):** + +- [**is_uniform(hypergraph, k)**](../../cpp-gl/group__HGL-Algorithm.md#function-is_uniform): Evaluates if all hyperedges contain exactly `k` vertices. +- [**is_uniform(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-is_uniform): Evaluates if all hyperedges are the exact same size. + +**BF-Directed Specifically:** + +- [**is_tail_uniform(hypergraph, k)**](../../cpp-gl/group__HGL-Algorithm.md#function-is_tail_uniform) / [**is_tail_uniform(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-is_tail_uniform): Evaluates if all hyperedges have a tail size of exactly `k`, or share the same tail size. +- [**is_head_uniform(hypergraph, k)**](../../cpp-gl/group__HGL-Algorithm.md#function-is_head_uniform) / [**is_head_uniform(hypergraph)**](../../cpp-gl/group__HGL-Algorithm.md#function-is_head_uniform): Evaluates if all hyperedges have a head size of exactly `k`, or share the same head size. diff --git a/docs/hgl/properties.md b/docs/hgl/properties.md deleted file mode 100644 index f727c2e5..00000000 --- a/docs/hgl/properties.md +++ /dev/null @@ -1 +0,0 @@ -# Hypergraph Properties diff --git a/include/hgl/algorithm.hpp b/include/hgl/algorithm.hpp index cd0dcfa7..8ae7e647 100644 --- a/include/hgl/algorithm.hpp +++ b/include/hgl/algorithm.hpp @@ -20,4 +20,6 @@ #include "hgl/algorithm/traversal/backward_search.hpp" #include "hgl/algorithm/traversal/forward_search.hpp" +#include "hgl/algorithm/properties.hpp" + // clang-format on diff --git a/include/hgl/algorithm/properties.hpp b/include/hgl/algorithm/properties.hpp new file mode 100644 index 00000000..97feb823 --- /dev/null +++ b/include/hgl/algorithm/properties.hpp @@ -0,0 +1,202 @@ +// Copyright (c) 2024-2026 Jakub MusiaƂ +// This file is part of the CPP-GL project (https://github.com/SpectraL519/cpp-gl). +// Licensed under the MIT License. See the LICENSE file in the project root for full license information. + +/// @file hgl/algorithm/properties.hpp +/// @brief Defines lightweight, functional utilities to evaluate the global structural properties of a hypergraph. + +#pragma once + +#include "hgl/hypergraph.hpp" +#include "hgl/util.hpp" + +namespace hgl::algorithm { + +// --- degree bounds --- + +/// @ingroup HGL-Algorithm +/// @brief Calculates the maximum degree among all vertices in a hypergraph. +[[nodiscard]] size_type max_degree(const traits::c_hypergraph auto& hypergraph) noexcept { + const auto degrees = hypergraph.degree_map(); + return degrees.empty() ? 0uz : *std::ranges::max_element(degrees); +} + +/// @ingroup HGL-Algorithm +/// @brief Calculates the minimum degree among all vertices in a hypergraph. +[[nodiscard]] size_type min_degree(const traits::c_hypergraph auto& hypergraph) noexcept { + const auto degrees = hypergraph.degree_map(); + return degrees.empty() ? 0uz : *std::ranges::min_element(degrees); +} + +/// @ingroup HGL-Algorithm +/// @brief Calculates the maximum out-degree among all vertices in a *BF-directed* hypergraph. +[[nodiscard]] size_type max_out_degree(const traits::c_bf_directed_hypergraph auto& hypergraph +) noexcept { + const auto degrees = hypergraph.out_degree_map(); + return degrees.empty() ? 0uz : *std::ranges::max_element(degrees); +} + +/// @ingroup HGL-Algorithm +/// @brief Calculates the minimum out-degree among all vertices in a *BF-directed* hypergraph. +[[nodiscard]] size_type min_out_degree(const traits::c_bf_directed_hypergraph auto& hypergraph +) noexcept { + const auto degrees = hypergraph.out_degree_map(); + return degrees.empty() ? 0uz : *std::ranges::min_element(degrees); +} + +/// @ingroup HGL-Algorithm +/// @brief Calculates the maximum in-degree among all vertices in a *BF-directed* hypergraph. +[[nodiscard]] size_type max_in_degree(const traits::c_bf_directed_hypergraph auto& hypergraph +) noexcept { + const auto degrees = hypergraph.in_degree_map(); + return degrees.empty() ? 0uz : *std::ranges::max_element(degrees); +} + +/// @ingroup HGL-Algorithm +/// @brief Calculates the minimum in-degree among all vertices in a *BF-directed* hypergraph. +[[nodiscard]] size_type min_in_degree(const traits::c_bf_directed_hypergraph auto& hypergraph +) noexcept { + const auto degrees = hypergraph.in_degree_map(); + return degrees.empty() ? 0uz : *std::ranges::min_element(degrees); +} + +// --- hyperedge size bounds --- + +/// @ingroup HGL-Algorithm +/// @brief Calculates the rank (maximum size of any hyperedge) of a hypergraph. +[[nodiscard]] size_type rank(const traits::c_hypergraph auto& hypergraph) noexcept { + const auto sizes = hypergraph.hyperedge_size_map(); + return sizes.empty() ? 0uz : *std::ranges::max_element(sizes); +} + +/// @ingroup HGL-Algorithm +/// @brief Calculates the corank (minimum size of any hyperedge) of a hypergraph. +[[nodiscard]] size_type corank(const traits::c_hypergraph auto& hypergraph) noexcept { + const auto sizes = hypergraph.hyperedge_size_map(); + return sizes.empty() ? 0uz : *std::ranges::min_element(sizes); +} + +/// @ingroup HGL-Algorithm +/// @brief Calculates the maximum tail size among all hyperedges in a *BF-directed* hypergraph. +[[nodiscard]] size_type max_tail_size(const traits::c_bf_directed_hypergraph auto& hypergraph +) noexcept { + const auto sizes = hypergraph.tail_size_map(); + return sizes.empty() ? 0uz : *std::ranges::max_element(sizes); +} + +/// @ingroup HGL-Algorithm +/// @brief Calculates the minimum tail size among all hyperedges in a *BF-directed* hypergraph. +[[nodiscard]] size_type min_tail_size(const traits::c_bf_directed_hypergraph auto& hypergraph +) noexcept { + const auto sizes = hypergraph.tail_size_map(); + return sizes.empty() ? 0uz : *std::ranges::min_element(sizes); +} + +/// @ingroup HGL-Algorithm +/// @brief Calculates the maximum head size among all hyperedges in a *BF-directed* hypergraph. +[[nodiscard]] size_type max_head_size(const traits::c_bf_directed_hypergraph auto& hypergraph +) noexcept { + const auto sizes = hypergraph.head_size_map(); + return sizes.empty() ? 0uz : *std::ranges::max_element(sizes); +} + +/// @ingroup HGL-Algorithm +/// @brief Calculates the minimum head size among all hyperedges in a *BF-directed* hypergraph. +[[nodiscard]] size_type min_head_size(const traits::c_bf_directed_hypergraph auto& hypergraph +) noexcept { + const auto sizes = hypergraph.head_size_map(); + return sizes.empty() ? 0uz : *std::ranges::min_element(sizes); +} + +// --- regularity --- + +/// @ingroup HGL-Algorithm +/// @brief Evaluates whether the given hypergraph is $k$-regular (all vertices have a degree of $k$). +[[nodiscard]] bool is_regular( + const traits::c_hypergraph auto& hypergraph, const size_type k +) noexcept { + return util::all_equal(hypergraph.degree_map(), k); +} + +/// @ingroup HGL-Algorithm +/// @brief Evaluates whether the given hypergraph is structurally regular (all vertices have the same degree). +[[nodiscard]] bool is_regular(const traits::c_hypergraph auto& hypergraph) noexcept { + return util::is_constant(hypergraph.degree_map()); +} + +/// @ingroup HGL-Algorithm +/// @brief Evaluates whether the given *BF-directed* hypergraph is out-$k$-regular (all vertices have an out-degree of $k$). +[[nodiscard]] bool is_out_regular( + const traits::c_bf_directed_hypergraph auto& hypergraph, const size_type k +) noexcept { + return util::all_equal(hypergraph.out_degree_map(), k); +} + +/// @ingroup HGL-Algorithm +/// @brief Evaluates whether the given *BF-directed* hypergraph is out-regular (all vertices have the same out-degree). +[[nodiscard]] bool is_out_regular(const traits::c_bf_directed_hypergraph auto& hypergraph +) noexcept { + return util::is_constant(hypergraph.out_degree_map()); +} + +/// @ingroup HGL-Algorithm +/// @brief Evaluates whether the given *BF-directed* hypergraph is in-$k$-regular (all vertices have an in-degree of $k$). +[[nodiscard]] bool is_in_regular( + const traits::c_bf_directed_hypergraph auto& hypergraph, const size_type k +) noexcept { + return util::all_equal(hypergraph.in_degree_map(), k); +} + +/// @ingroup HGL-Algorithm +/// @brief Evaluates whether the given *BF-directed* hypergraph is in-regular (all vertices have the same in-degree). +[[nodiscard]] bool is_in_regular(const traits::c_bf_directed_hypergraph auto& hypergraph) noexcept { + return util::is_constant(hypergraph.in_degree_map()); +} + +// --- uniformity --- + +/// @ingroup HGL-Algorithm +/// @brief Evaluates whether the given hypergraph is $k$-uniform (all hyperedges have a size of $k$). +[[nodiscard]] bool is_uniform( + const traits::c_hypergraph auto& hypergraph, const size_type k +) noexcept { + return util::all_equal(hypergraph.hyperedge_size_map(), k); +} + +/// @ingroup HGL-Algorithm +/// @brief Evaluates whether the given hypergraph is structurally uniform (all hyperedges have the exact same size). +[[nodiscard]] bool is_uniform(const traits::c_hypergraph auto& hypergraph) noexcept { + return util::is_constant(hypergraph.hyperedge_size_map()); +} + +/// @ingroup HGL-Algorithm +/// @brief Evaluates whether the given *BF-directed* hypergraph is tail-$k$-uniform (all hyperedges have a tail size of $k$). +[[nodiscard]] bool is_tail_uniform( + const traits::c_bf_directed_hypergraph auto& hypergraph, const size_type k +) noexcept { + return util::all_equal(hypergraph.tail_size_map(), k); +} + +/// @ingroup HGL-Algorithm +/// @brief Evaluates whether the given *BF-directed* hypergraph is tail-uniform (all hyperedges have the exact same tail size). +[[nodiscard]] bool is_tail_uniform(const traits::c_bf_directed_hypergraph auto& hypergraph +) noexcept { + return util::is_constant(hypergraph.tail_size_map()); +} + +/// @ingroup HGL-Algorithm +/// @brief Evaluates whether the given *BF-directed* hypergraph is head-$k$-uniform (all hyperedges have a head size of $k$). +[[nodiscard]] bool is_head_uniform( + const traits::c_bf_directed_hypergraph auto& hypergraph, const size_type k +) noexcept { + return util::all_equal(hypergraph.head_size_map(), k); +} + +/// @ingroup HGL-Algorithm +/// @brief Evaluates whether the given *BF-directed* hypergraph is head-uniform (all hyperedges have the exact same head size). +[[nodiscard]] bool is_head_uniform(const traits::c_bf_directed_hypergraph auto& hypergraph +) noexcept { + return util::is_constant(hypergraph.head_size_map()); +} + +} // namespace hgl::algorithm diff --git a/include/hgl/hypergraph.hpp b/include/hgl/hypergraph.hpp index d0a9f55c..7fed08a6 100644 --- a/include/hgl/hypergraph.hpp +++ b/include/hgl/hypergraph.hpp @@ -16,7 +16,6 @@ #include "hgl/impl/impl_tags.hpp" #include "hgl/io/core.hpp" #include "hgl/io/hypergraph_fmt_traits.hpp" -#include "hgl/util.hpp" #include #include @@ -2381,193 +2380,6 @@ using flat_matrix_hypergraph = hypergraph>; -// --- degree bounds --- - -/// @ingroup HGL-Algorithm -/// @brief Calculates the maximum degree among all vertices in a hypergraph. -[[nodiscard]] size_type max_degree(const traits::c_hypergraph auto& hypergraph) noexcept { - const auto degrees = hypergraph.degree_map(); - return degrees.empty() ? 0uz : *std::ranges::max_element(degrees); -} - -/// @ingroup HGL-Algorithm -/// @brief Calculates the minimum degree among all vertices in a hypergraph. -[[nodiscard]] size_type min_degree(const traits::c_hypergraph auto& hypergraph) noexcept { - const auto degrees = hypergraph.degree_map(); - return degrees.empty() ? 0uz : *std::ranges::min_element(degrees); -} - -/// @ingroup HGL-Algorithm -/// @brief Calculates the maximum out-degree among all vertices in a *BF-directed* hypergraph. -[[nodiscard]] size_type max_out_degree(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { - const auto degrees = hypergraph.out_degree_map(); - return degrees.empty() ? 0uz : *std::ranges::max_element(degrees); -} - -/// @ingroup HGL-Algorithm -/// @brief Calculates the minimum out-degree among all vertices in a *BF-directed* hypergraph. -[[nodiscard]] size_type min_out_degree(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { - const auto degrees = hypergraph.out_degree_map(); - return degrees.empty() ? 0uz : *std::ranges::min_element(degrees); -} - -/// @ingroup HGL-Algorithm -/// @brief Calculates the maximum in-degree among all vertices in a *BF-directed* hypergraph. -[[nodiscard]] size_type max_in_degree(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { - const auto degrees = hypergraph.in_degree_map(); - return degrees.empty() ? 0uz : *std::ranges::max_element(degrees); -} - -/// @ingroup HGL-Algorithm -/// @brief Calculates the minimum in-degree among all vertices in a *BF-directed* hypergraph. -[[nodiscard]] size_type min_in_degree(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { - const auto degrees = hypergraph.in_degree_map(); - return degrees.empty() ? 0uz : *std::ranges::min_element(degrees); -} - -// --- hyperedge size bounds --- - -/// @ingroup HGL-Algorithm -/// @brief Calculates the rank (maximum size of any hyperedge) of a hypergraph. -[[nodiscard]] size_type rank(const traits::c_hypergraph auto& hypergraph) noexcept { - const auto sizes = hypergraph.hyperedge_size_map(); - return sizes.empty() ? 0uz : *std::ranges::max_element(sizes); -} - -/// @ingroup HGL-Algorithm -/// @brief Calculates the corank (minimum size of any hyperedge) of a hypergraph. -[[nodiscard]] size_type corank(const traits::c_hypergraph auto& hypergraph) noexcept { - const auto sizes = hypergraph.hyperedge_size_map(); - return sizes.empty() ? 0uz : *std::ranges::min_element(sizes); -} - -/// @ingroup HGL-Algorithm -/// @brief Calculates the maximum tail size among all hyperedges in a *BF-directed* hypergraph. -[[nodiscard]] size_type max_tail_size(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { - const auto sizes = hypergraph.tail_size_map(); - return sizes.empty() ? 0uz : *std::ranges::max_element(sizes); -} - -/// @ingroup HGL-Algorithm -/// @brief Calculates the minimum tail size among all hyperedges in a *BF-directed* hypergraph. -[[nodiscard]] size_type min_tail_size(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { - const auto sizes = hypergraph.tail_size_map(); - return sizes.empty() ? 0uz : *std::ranges::min_element(sizes); -} - -/// @ingroup HGL-Algorithm -/// @brief Calculates the maximum head size among all hyperedges in a *BF-directed* hypergraph. -[[nodiscard]] size_type max_head_size(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { - const auto sizes = hypergraph.head_size_map(); - return sizes.empty() ? 0uz : *std::ranges::max_element(sizes); -} - -/// @ingroup HGL-Algorithm -/// @brief Calculates the minimum head size among all hyperedges in a *BF-directed* hypergraph. -[[nodiscard]] size_type min_head_size(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { - const auto sizes = hypergraph.head_size_map(); - return sizes.empty() ? 0uz : *std::ranges::min_element(sizes); -} - -// --- regularity --- - -/// @ingroup HGL-Algorithm -/// @brief Evaluates whether the given hypergraph is $k$-regular (all vertices have a degree of $k$). -[[nodiscard]] bool is_regular( - const traits::c_hypergraph auto& hypergraph, const size_type k -) noexcept { - return util::all_equal(hypergraph.degree_map(), k); -} - -/// @ingroup HGL-Algorithm -/// @brief Evaluates whether the given hypergraph is structurally regular (all vertices have the same degree). -[[nodiscard]] bool is_regular(const traits::c_hypergraph auto& hypergraph) noexcept { - return util::is_constant(hypergraph.degree_map()); -} - -/// @ingroup HGL-Algorithm -/// @brief Evaluates whether the given *BF-directed* hypergraph is out-$k$-regular (all vertices have an out-degree of $k$). -[[nodiscard]] bool is_out_regular( - const traits::c_bf_directed_hypergraph auto& hypergraph, const size_type k -) noexcept { - return util::all_equal(hypergraph.out_degree_map(), k); -} - -/// @ingroup HGL-Algorithm -/// @brief Evaluates whether the given *BF-directed* hypergraph is out-regular (all vertices have the same out-degree). -[[nodiscard]] bool is_out_regular(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { - return util::is_constant(hypergraph.out_degree_map()); -} - -/// @ingroup HGL-Algorithm -/// @brief Evaluates whether the given *BF-directed* hypergraph is in-$k$-regular (all vertices have an in-degree of $k$). -[[nodiscard]] bool is_in_regular( - const traits::c_bf_directed_hypergraph auto& hypergraph, const size_type k -) noexcept { - return util::all_equal(hypergraph.in_degree_map(), k); -} - -/// @ingroup HGL-Algorithm -/// @brief Evaluates whether the given *BF-directed* hypergraph is in-regular (all vertices have the same in-degree). -[[nodiscard]] bool is_in_regular(const traits::c_bf_directed_hypergraph auto& hypergraph) noexcept { - return util::is_constant(hypergraph.in_degree_map()); -} - -// --- uniformity --- - -/// @ingroup HGL-Algorithm -/// @brief Evaluates whether the given hypergraph is $k$-uniform (all hyperedges have a size of $k$). -[[nodiscard]] bool is_uniform( - const traits::c_hypergraph auto& hypergraph, const size_type k -) noexcept { - return util::all_equal(hypergraph.hyperedge_size_map(), k); -} - -/// @ingroup HGL-Algorithm -/// @brief Evaluates whether the given hypergraph is structurally uniform (all hyperedges have the exact same size). -[[nodiscard]] bool is_uniform(const traits::c_hypergraph auto& hypergraph) noexcept { - return util::is_constant(hypergraph.hyperedge_size_map()); -} - -/// @ingroup HGL-Algorithm -/// @brief Evaluates whether the given *BF-directed* hypergraph is tail-$k$-uniform (all hyperedges have a tail size of $k$). -[[nodiscard]] bool is_tail_uniform( - const traits::c_bf_directed_hypergraph auto& hypergraph, const size_type k -) noexcept { - return util::all_equal(hypergraph.tail_size_map(), k); -} - -/// @ingroup HGL-Algorithm -/// @brief Evaluates whether the given *BF-directed* hypergraph is tail-uniform (all hyperedges have the exact same tail size). -[[nodiscard]] bool is_tail_uniform(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { - return util::is_constant(hypergraph.tail_size_map()); -} - -/// @ingroup HGL-Algorithm -/// @brief Evaluates whether the given *BF-directed* hypergraph is head-$k$-uniform (all hyperedges have a head size of $k$). -[[nodiscard]] bool is_head_uniform( - const traits::c_bf_directed_hypergraph auto& hypergraph, const size_type k -) noexcept { - return util::all_equal(hypergraph.head_size_map(), k); -} - -/// @ingroup HGL-Algorithm -/// @brief Evaluates whether the given *BF-directed* hypergraph is head-uniform (all hyperedges have the exact same head size). -[[nodiscard]] bool is_head_uniform(const traits::c_bf_directed_hypergraph auto& hypergraph -) noexcept { - return util::is_constant(hypergraph.head_size_map()); -} - namespace detail::hypergraph_doc_anchors { /// > [!IMPORTANT] ID Stability diff --git a/mkdocs.yml b/mkdocs.yml index c4b096a9..a8ea102a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -29,12 +29,12 @@ nav: - HGL: - Quick Start: hgl/quick_start.md - Architecture & Basics: hgl/architecture.md - - Hypergraph Properties: hgl/properties.md - Graph Conversions: hgl/conversions.md - Algorithms: - Overview: hgl/algorithms/overview.md - Generic Templates: hgl/algorithms/templates.md - Concrete Traversals: hgl/algorithms/traversal.md + - Hypergraph Properties: hgl/algorithms/properties.md - I/O Utility: hgl/io.md - API Reference: - Overview: cpp-gl/group__HGL.md diff --git a/tests/source/hgl/test_alg_properties.cpp b/tests/source/hgl/test_alg_properties.cpp new file mode 100644 index 00000000..bf2fee9c --- /dev/null +++ b/tests/source/hgl/test_alg_properties.cpp @@ -0,0 +1,247 @@ +#include "doctest.h" + +#include + +namespace hgl_testing { + +TEST_SUITE_BEGIN("test_alg_properties"); + +TEST_CASE_TEMPLATE_DEFINE( + "hypergraph size utility tests", HypergraphTraits, hypergraph_traits_alg_props_template +) { + using sut_type = hgl::hypergraph; + using directional_tag = typename sut_type::directional_tag; + + SUBCASE("utilities on empty hypergraph should return zero or true") { + sut_type sut; + + // --- Degree Bounds --- + CHECK_EQ(hgl::algorithm::min_degree(sut), 0uz); + CHECK_EQ(hgl::algorithm::max_degree(sut), 0uz); + + // --- Size Bounds --- + CHECK_EQ(hgl::algorithm::rank(sut), 0uz); + CHECK_EQ(hgl::algorithm::corank(sut), 0uz); + + // --- Regularity/Uniformity --- + CHECK(hgl::algorithm::is_regular(sut)); + CHECK(hgl::algorithm::is_regular(sut, 0uz)); + CHECK(hgl::algorithm::is_uniform(sut)); + CHECK(hgl::algorithm::is_uniform(sut, 0uz)); + + if constexpr (std::same_as) { + // --- Degree Bounds --- + CHECK_EQ(hgl::algorithm::min_out_degree(sut), 0uz); + CHECK_EQ(hgl::algorithm::max_out_degree(sut), 0uz); + CHECK_EQ(hgl::algorithm::min_in_degree(sut), 0uz); + CHECK_EQ(hgl::algorithm::max_in_degree(sut), 0uz); + + // --- Size Bounds --- + CHECK_EQ(hgl::algorithm::min_tail_size(sut), 0uz); + CHECK_EQ(hgl::algorithm::max_tail_size(sut), 0uz); + CHECK_EQ(hgl::algorithm::min_head_size(sut), 0uz); + CHECK_EQ(hgl::algorithm::max_head_size(sut), 0uz); + + // --- Regularity/Uniformity --- + CHECK(hgl::algorithm::is_out_regular(sut)); + CHECK(hgl::algorithm::is_in_regular(sut)); + CHECK(hgl::algorithm::is_tail_uniform(sut)); + CHECK(hgl::algorithm::is_head_uniform(sut)); + } + } + + SUBCASE("utilities on a symmetric topology (Cycle C3) should report constant properties") { + // Setup: 3 Vertices, 3 Hyperedges forming a cycle. + // Undirected: Edges are {0,1}, {1,2}, {2,0}. + // Directed: Edges are 0->1, 1->2, 2->0. + constexpr auto n_elements = 3uz; + sut_type sut{n_elements, n_elements}; + + if constexpr (std::same_as) { + sut.bind(0uz, 0uz); + sut.bind(1uz, 0uz); // e0: {0,1} + sut.bind(1uz, 1uz); + sut.bind(2uz, 1uz); // e1: {1,2} + sut.bind(2uz, 2uz); + sut.bind(0uz, 2uz); // e2: {2,0} + + // Expected: 2-regular, 2-uniform + CHECK_EQ(hgl::algorithm::max_degree(sut), 2uz); + CHECK_EQ(hgl::algorithm::min_degree(sut), 2uz); + CHECK(hgl::algorithm::is_regular(sut)); + CHECK(hgl::algorithm::is_regular(sut, 2uz)); + CHECK_FALSE(hgl::algorithm::is_regular(sut, 1uz)); + + CHECK_EQ(hgl::algorithm::rank(sut), 2uz); + CHECK_EQ(hgl::algorithm::corank(sut), 2uz); + CHECK(hgl::algorithm::is_uniform(sut)); + CHECK(hgl::algorithm::is_uniform(sut, 2uz)); + CHECK_FALSE(hgl::algorithm::is_uniform(sut, 1uz)); + } + + if constexpr (std::same_as) { + sut.bind_tail(0, 0); + sut.bind_head(1, 0); // e0: 0 -> 1 + sut.bind_tail(1, 1); + sut.bind_head(2, 1); // e1: 1 -> 2 + sut.bind_tail(2, 2); + sut.bind_head(0, 2); // e2: 2 -> 0 + + // Expected General: Degree 2 (1 in + 1 out), Size 2 (1 tail + 1 head) + CHECK_EQ(hgl::algorithm::min_degree(sut), 2uz); + CHECK_EQ(hgl::algorithm::max_degree(sut), 2uz); + CHECK(hgl::algorithm::is_regular(sut, 2uz)); + CHECK_FALSE(hgl::algorithm::is_regular(sut, 1uz)); + CHECK(hgl::algorithm::is_uniform(sut, 2uz)); + CHECK_FALSE(hgl::algorithm::is_uniform(sut, 1uz)); + + // Expected Directed: 1-out-regular, 1-in-regular + CHECK_EQ(hgl::algorithm::min_out_degree(sut), 1uz); + CHECK_EQ(hgl::algorithm::max_out_degree(sut), 1uz); + CHECK(hgl::algorithm::is_out_regular(sut, 1uz)); + CHECK_FALSE(hgl::algorithm::is_out_regular(sut, 2uz)); + + CHECK_EQ(hgl::algorithm::min_in_degree(sut), 1uz); + CHECK_EQ(hgl::algorithm::max_in_degree(sut), 1uz); + CHECK(hgl::algorithm::is_in_regular(sut, 1uz)); + CHECK_FALSE(hgl::algorithm::is_in_regular(sut, 2uz)); + + // Expected Directed Sizes: 1-tail, 1-head + CHECK_EQ(hgl::algorithm::min_tail_size(sut), 1uz); + CHECK_EQ(hgl::algorithm::max_tail_size(sut), 1uz); + CHECK(hgl::algorithm::is_tail_uniform(sut, 1uz)); + CHECK_FALSE(hgl::algorithm::is_tail_uniform(sut, 2uz)); + + CHECK_EQ(hgl::algorithm::min_head_size(sut), 1uz); + CHECK_EQ(hgl::algorithm::max_head_size(sut), 1uz); + CHECK(hgl::algorithm::is_head_uniform(sut, 1uz)); + CHECK_FALSE(hgl::algorithm::is_head_uniform(sut, 2uz)); + } + } + + SUBCASE("utilities on asymmetric topology should report divergent bounds and false checks") { + // Setup: 3 Vertices, 2 Hyperedges. + // Undirected: e0={0,1,2} (size 3), e1={0} (size 1) + // Directed: e0: 0 -> {1,2} (1 tail, 2 heads), e1: {0,1} -> 2 (2 tails, 1 head) + sut_type sut{3, 2}; + + if constexpr (std::same_as) { + sut.bind(0, 0); + sut.bind(1, 0); + sut.bind(2, 0); // e0 size 3 + sut.bind(0, 1); // e1 size 1 + + // Sizes: 3, 1 -> Non-uniform + CHECK_EQ(hgl::algorithm::rank(sut), 3uz); + CHECK_EQ(hgl::algorithm::corank(sut), 1uz); + CHECK_FALSE(hgl::algorithm::is_uniform(sut)); + + // Degrees: v0=2, v1=1, v2=1 -> Irregular + CHECK_EQ(hgl::algorithm::max_degree(sut), 2uz); + CHECK_EQ(hgl::algorithm::min_degree(sut), 1uz); + CHECK_FALSE(hgl::algorithm::is_regular(sut)); + } + + if constexpr (std::same_as) { + // e0: 0 -> {1, 2} + sut.bind_tail(0, 0); + sut.bind_head(1, 0); + sut.bind_head(2, 0); + + // e1: {0, 1} -> 2 + sut.bind_tail(0, 1); + sut.bind_tail(1, 1); + sut.bind_head(2, 1); + + // --- Size Checks --- + // Tail sizes: e0=1, e1=2 + CHECK_EQ(hgl::algorithm::max_tail_size(sut), 2uz); + CHECK_EQ(hgl::algorithm::min_tail_size(sut), 1uz); + CHECK_FALSE(hgl::algorithm::is_tail_uniform(sut)); + + // Head sizes: e0=2, e1=1 + CHECK_EQ(hgl::algorithm::max_head_size(sut), 2uz); + CHECK_EQ(hgl::algorithm::min_head_size(sut), 1uz); + CHECK_FALSE(hgl::algorithm::is_head_uniform(sut)); + + // --- Degree Checks --- + // Out degrees: v0(2), v1(1), v2(0) + CHECK_EQ(hgl::algorithm::max_out_degree(sut), 2uz); + CHECK_EQ(hgl::algorithm::min_out_degree(sut), 0uz); + CHECK_FALSE(hgl::algorithm::is_out_regular(sut)); + + // In degrees: v0(0), v1(1), v2(2) + CHECK_EQ(hgl::algorithm::max_in_degree(sut), 2uz); + CHECK_EQ(hgl::algorithm::min_in_degree(sut), 0uz); + CHECK_FALSE(hgl::algorithm::is_in_regular(sut)); + } + } +} + +TEST_CASE_TEMPLATE_INSTANTIATE( + hypergraph_traits_alg_props_template, + hgl::list_hypergraph_traits< + hgl::impl::bidirectional_t, + hgl::undirected_t>, // undirected bidirectional incidence list + hgl::list_hypergraph_traits< + hgl::impl::hyperedge_major_t, + hgl::undirected_t>, // undirected hyperedge-major incidence list + hgl::list_hypergraph_traits< + hgl::impl::vertex_major_t, + hgl::undirected_t>, // undirected vertex-major incidence list + hgl::flat_list_hypergraph_traits< + hgl::impl::bidirectional_t, + hgl::undirected_t>, // undirected bidirectional flat incidence list + hgl::flat_list_hypergraph_traits< + hgl::impl::hyperedge_major_t, + hgl::undirected_t>, // undirected hyperedge-major flat incidence list + hgl::flat_list_hypergraph_traits< + hgl::impl::vertex_major_t, + hgl::undirected_t>, // undirected vertex-major flat incidence list + hgl::matrix_hypergraph_traits< + hgl::impl::hyperedge_major_t, + hgl::undirected_t>, // undirected hyperedge-major incidence matrix + hgl::matrix_hypergraph_traits< + hgl::impl::vertex_major_t, + hgl::undirected_t>, // undirected vertex-major incidence matrix + hgl::flat_matrix_hypergraph_traits< + hgl::impl::hyperedge_major_t, + hgl::undirected_t>, // undirected hyperedge-major flat incidence matrix + hgl::flat_matrix_hypergraph_traits< + hgl::impl::vertex_major_t, + hgl::undirected_t>, // undirected vertex-major flat incidence matrix + hgl::list_hypergraph_traits< + hgl::impl::bidirectional_t, + hgl::bf_directed_t>, // bf-directed bidirectional incidence list + hgl::list_hypergraph_traits< + hgl::impl::hyperedge_major_t, + hgl::bf_directed_t>, // bf-directed hyperedge-major incidence list + hgl::list_hypergraph_traits< + hgl::impl::vertex_major_t, + hgl::bf_directed_t>, // bf-directed vertex-major incidence list + hgl::flat_list_hypergraph_traits< + hgl::impl::bidirectional_t, + hgl::bf_directed_t>, // bf-directed bidirectional flat incidence list + hgl::flat_list_hypergraph_traits< + hgl::impl::hyperedge_major_t, + hgl::bf_directed_t>, // bf-directed hyperedge-major flat incidence list + hgl::flat_list_hypergraph_traits< + hgl::impl::vertex_major_t, + hgl::bf_directed_t>, // bf-directed vertex-major flat incidence list + hgl::matrix_hypergraph_traits< + hgl::impl::hyperedge_major_t, + hgl::bf_directed_t>, // bf-directed hyperedge-major incidence matrix + hgl::matrix_hypergraph_traits< + hgl::impl::vertex_major_t, + hgl::bf_directed_t>, // bf-directed vertex-major incidence matrix + hgl::flat_matrix_hypergraph_traits< + hgl::impl::hyperedge_major_t, + hgl::bf_directed_t>, // bf-directed hyperedge-major flat incidence matrix + hgl::flat_matrix_hypergraph_traits< + hgl::impl::vertex_major_t, + hgl::bf_directed_t> // bf-directed vertex-major flat incidence matrix +); + +TEST_SUITE_END; // test_alg_properties + +} // namespace hgl_testing diff --git a/tests/source/hgl/test_hypergraph.cpp b/tests/source/hgl/test_hypergraph.cpp index 40dc071e..fa6a021f 100644 --- a/tests/source/hgl/test_hypergraph.cpp +++ b/tests/source/hgl/test_hypergraph.cpp @@ -1719,242 +1719,6 @@ TEST_CASE_TEMPLATE_INSTANTIATE( hgl::name_property> // bf-directed vertex-major flat incidence matrix ); -TEST_CASE_TEMPLATE_DEFINE( - "hypergraph size utility tests", HypergraphTraits, hypergraph_traits_util_template -) { - using sut_type = hgl::hypergraph; - using directional_tag = typename sut_type::directional_tag; - - SUBCASE("utilities on empty hypergraph should return zero or true") { - sut_type sut; - - // --- Degree Bounds --- - CHECK_EQ(hgl::min_degree(sut), 0uz); - CHECK_EQ(hgl::max_degree(sut), 0uz); - - // --- Size Bounds --- - CHECK_EQ(hgl::rank(sut), 0uz); - CHECK_EQ(hgl::corank(sut), 0uz); - - // --- Regularity/Uniformity --- - CHECK(hgl::is_regular(sut)); - CHECK(hgl::is_regular(sut, 0uz)); - CHECK(hgl::is_uniform(sut)); - CHECK(hgl::is_uniform(sut, 0uz)); - - if constexpr (std::same_as) { - // --- Degree Bounds --- - CHECK_EQ(hgl::min_out_degree(sut), 0uz); - CHECK_EQ(hgl::max_out_degree(sut), 0uz); - CHECK_EQ(hgl::min_in_degree(sut), 0uz); - CHECK_EQ(hgl::max_in_degree(sut), 0uz); - - // --- Size Bounds --- - CHECK_EQ(hgl::min_tail_size(sut), 0uz); - CHECK_EQ(hgl::max_tail_size(sut), 0uz); - CHECK_EQ(hgl::min_head_size(sut), 0uz); - CHECK_EQ(hgl::max_head_size(sut), 0uz); - - // --- Regularity/Uniformity --- - CHECK(hgl::is_out_regular(sut)); - CHECK(hgl::is_in_regular(sut)); - CHECK(hgl::is_tail_uniform(sut)); - CHECK(hgl::is_head_uniform(sut)); - } - } - - SUBCASE("utilities on a symmetric topology (Cycle C3) should report constant properties") { - // Setup: 3 Vertices, 3 Hyperedges forming a cycle. - // Undirected: Edges are {0,1}, {1,2}, {2,0}. - // Directed: Edges are 0->1, 1->2, 2->0. - constexpr auto n_elements = 3uz; - sut_type sut{n_elements, n_elements}; - - if constexpr (std::same_as) { - sut.bind(0uz, 0uz); - sut.bind(1uz, 0uz); // e0: {0,1} - sut.bind(1uz, 1uz); - sut.bind(2uz, 1uz); // e1: {1,2} - sut.bind(2uz, 2uz); - sut.bind(0uz, 2uz); // e2: {2,0} - - // Expected: 2-regular, 2-uniform - CHECK_EQ(hgl::max_degree(sut), 2uz); - CHECK_EQ(hgl::min_degree(sut), 2uz); - CHECK(hgl::is_regular(sut)); - CHECK(hgl::is_regular(sut, 2uz)); - CHECK_FALSE(hgl::is_regular(sut, 1uz)); - - CHECK_EQ(hgl::rank(sut), 2uz); - CHECK_EQ(hgl::corank(sut), 2uz); - CHECK(hgl::is_uniform(sut)); - CHECK(hgl::is_uniform(sut, 2uz)); - CHECK_FALSE(hgl::is_uniform(sut, 1uz)); - } - - if constexpr (std::same_as) { - sut.bind_tail(0, 0); - sut.bind_head(1, 0); // e0: 0 -> 1 - sut.bind_tail(1, 1); - sut.bind_head(2, 1); // e1: 1 -> 2 - sut.bind_tail(2, 2); - sut.bind_head(0, 2); // e2: 2 -> 0 - - // Expected General: Degree 2 (1 in + 1 out), Size 2 (1 tail + 1 head) - CHECK_EQ(hgl::min_degree(sut), 2uz); - CHECK_EQ(hgl::max_degree(sut), 2uz); - CHECK(hgl::is_regular(sut, 2uz)); - CHECK_FALSE(hgl::is_regular(sut, 1uz)); - CHECK(hgl::is_uniform(sut, 2uz)); - CHECK_FALSE(hgl::is_uniform(sut, 1uz)); - - // Expected Directed: 1-out-regular, 1-in-regular - CHECK_EQ(hgl::min_out_degree(sut), 1uz); - CHECK_EQ(hgl::max_out_degree(sut), 1uz); - CHECK(hgl::is_out_regular(sut, 1uz)); - CHECK_FALSE(hgl::is_out_regular(sut, 2uz)); - - CHECK_EQ(hgl::min_in_degree(sut), 1uz); - CHECK_EQ(hgl::max_in_degree(sut), 1uz); - CHECK(hgl::is_in_regular(sut, 1uz)); - CHECK_FALSE(hgl::is_in_regular(sut, 2uz)); - - // Expected Directed Sizes: 1-tail, 1-head - CHECK_EQ(hgl::min_tail_size(sut), 1uz); - CHECK_EQ(hgl::max_tail_size(sut), 1uz); - CHECK(hgl::is_tail_uniform(sut, 1uz)); - CHECK_FALSE(hgl::is_tail_uniform(sut, 2uz)); - - CHECK_EQ(hgl::min_head_size(sut), 1uz); - CHECK_EQ(hgl::max_head_size(sut), 1uz); - CHECK(hgl::is_head_uniform(sut, 1uz)); - CHECK_FALSE(hgl::is_head_uniform(sut, 2uz)); - } - } - - SUBCASE("utilities on asymmetric topology should report divergent bounds and false checks") { - // Setup: 3 Vertices, 2 Hyperedges. - // Undirected: e0={0,1,2} (size 3), e1={0} (size 1) - // Directed: e0: 0 -> {1,2} (1 tail, 2 heads), e1: {0,1} -> 2 (2 tails, 1 head) - sut_type sut{3, 2}; - - if constexpr (std::same_as) { - sut.bind(0, 0); - sut.bind(1, 0); - sut.bind(2, 0); // e0 size 3 - sut.bind(0, 1); // e1 size 1 - - // Sizes: 3, 1 -> Non-uniform - CHECK_EQ(hgl::rank(sut), 3uz); - CHECK_EQ(hgl::corank(sut), 1uz); - CHECK_FALSE(hgl::is_uniform(sut)); - - // Degrees: v0=2, v1=1, v2=1 -> Irregular - CHECK_EQ(hgl::max_degree(sut), 2uz); - CHECK_EQ(hgl::min_degree(sut), 1uz); - CHECK_FALSE(hgl::is_regular(sut)); - } - - if constexpr (std::same_as) { - // e0: 0 -> {1, 2} - sut.bind_tail(0, 0); - sut.bind_head(1, 0); - sut.bind_head(2, 0); - - // e1: {0, 1} -> 2 - sut.bind_tail(0, 1); - sut.bind_tail(1, 1); - sut.bind_head(2, 1); - - // --- Size Checks --- - // Tail sizes: e0=1, e1=2 - CHECK_EQ(hgl::max_tail_size(sut), 2uz); - CHECK_EQ(hgl::min_tail_size(sut), 1uz); - CHECK_FALSE(hgl::is_tail_uniform(sut)); - - // Head sizes: e0=2, e1=1 - CHECK_EQ(hgl::max_head_size(sut), 2uz); - CHECK_EQ(hgl::min_head_size(sut), 1uz); - CHECK_FALSE(hgl::is_head_uniform(sut)); - - // --- Degree Checks --- - // Out degrees: v0(2), v1(1), v2(0) - CHECK_EQ(hgl::max_out_degree(sut), 2uz); - CHECK_EQ(hgl::min_out_degree(sut), 0uz); - CHECK_FALSE(hgl::is_out_regular(sut)); - - // In degrees: v0(0), v1(1), v2(2) - CHECK_EQ(hgl::max_in_degree(sut), 2uz); - CHECK_EQ(hgl::min_in_degree(sut), 0uz); - CHECK_FALSE(hgl::is_in_regular(sut)); - } - } -} - -TEST_CASE_TEMPLATE_INSTANTIATE( - hypergraph_traits_util_template, - hgl::list_hypergraph_traits< - hgl::impl::bidirectional_t, - hgl::undirected_t>, // undirected bidirectional incidence list - hgl::list_hypergraph_traits< - hgl::impl::hyperedge_major_t, - hgl::undirected_t>, // undirected hyperedge-major incidence list - hgl::list_hypergraph_traits< - hgl::impl::vertex_major_t, - hgl::undirected_t>, // undirected vertex-major incidence list - hgl::flat_list_hypergraph_traits< - hgl::impl::bidirectional_t, - hgl::undirected_t>, // undirected bidirectional flat incidence list - hgl::flat_list_hypergraph_traits< - hgl::impl::hyperedge_major_t, - hgl::undirected_t>, // undirected hyperedge-major flat incidence list - hgl::flat_list_hypergraph_traits< - hgl::impl::vertex_major_t, - hgl::undirected_t>, // undirected vertex-major flat incidence list - hgl::matrix_hypergraph_traits< - hgl::impl::hyperedge_major_t, - hgl::undirected_t>, // undirected hyperedge-major incidence matrix - hgl::matrix_hypergraph_traits< - hgl::impl::vertex_major_t, - hgl::undirected_t>, // undirected vertex-major incidence matrix - hgl::flat_matrix_hypergraph_traits< - hgl::impl::hyperedge_major_t, - hgl::undirected_t>, // undirected hyperedge-major flat incidence matrix - hgl::flat_matrix_hypergraph_traits< - hgl::impl::vertex_major_t, - hgl::undirected_t>, // undirected vertex-major flat incidence matrix - hgl::list_hypergraph_traits< - hgl::impl::bidirectional_t, - hgl::bf_directed_t>, // bf-directed bidirectional incidence list - hgl::list_hypergraph_traits< - hgl::impl::hyperedge_major_t, - hgl::bf_directed_t>, // bf-directed hyperedge-major incidence list - hgl::list_hypergraph_traits< - hgl::impl::vertex_major_t, - hgl::bf_directed_t>, // bf-directed vertex-major incidence list - hgl::flat_list_hypergraph_traits< - hgl::impl::bidirectional_t, - hgl::bf_directed_t>, // bf-directed bidirectional flat incidence list - hgl::flat_list_hypergraph_traits< - hgl::impl::hyperedge_major_t, - hgl::bf_directed_t>, // bf-directed hyperedge-major flat incidence list - hgl::flat_list_hypergraph_traits< - hgl::impl::vertex_major_t, - hgl::bf_directed_t>, // bf-directed vertex-major flat incidence list - hgl::matrix_hypergraph_traits< - hgl::impl::hyperedge_major_t, - hgl::bf_directed_t>, // bf-directed hyperedge-major incidence matrix - hgl::matrix_hypergraph_traits< - hgl::impl::vertex_major_t, - hgl::bf_directed_t>, // bf-directed vertex-major incidence matrix - hgl::flat_matrix_hypergraph_traits< - hgl::impl::hyperedge_major_t, - hgl::bf_directed_t>, // bf-directed hyperedge-major flat incidence matrix - hgl::flat_matrix_hypergraph_traits< - hgl::impl::vertex_major_t, - hgl::bf_directed_t> // bf-directed vertex-major flat incidence matrix -); - TEST_SUITE_END(); // test_hypergraph } // namespace hgl_testing