Skip to content

Commit 4e4d698

Browse files
authored
YT-CPPHGL-22: Implement basic hypergraph utility functions
- Defined basic hypergraph type concepts - Extended the hypergraph class and internal implementation structures with degree and hyperedge size map getters - Added common utility functions for calculating: - The maximum and minimum vertex degree + in/out for bf-directed hypergraphs - The rank and corank of a hypergraph + maximum and minimum tail/head size for bf-directed hypergraphs
1 parent a139d1f commit 4e4d698

8 files changed

Lines changed: 1147 additions & 97 deletions

File tree

include/gl/util/ranges.hpp

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

55
#pragma once
66

7+
#include <algorithm>
78
#include <ranges>
89

910
namespace gl::util {
@@ -20,4 +21,22 @@ constexpr auto range_size(R&& r) {
2021
return std::ranges::distance(std::begin(r), std::end(r));
2122
}
2223

24+
template <std::ranges::forward_range R>
25+
[[nodiscard]] constexpr bool is_constant(R&& range) noexcept {
26+
if (std::ranges::empty(range))
27+
return true;
28+
29+
return std::ranges::all_of(range, [target = *std::ranges::begin(range)](const auto& val) {
30+
return val == target;
31+
});
32+
}
33+
34+
template <std::ranges::forward_range R>
35+
[[nodiscard]] constexpr bool all_equal(R&& range, const std::ranges::range_value_t<R>& k) noexcept {
36+
if (std::ranges::empty(range))
37+
return true;
38+
39+
return std::ranges::all_of(range, [&k](const auto& val) { return val == k; });
40+
}
41+
2342
} // namespace gl::util

include/hgl/hypergraph.hpp

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
#pragma once
66

7+
#include "gl/types/types.hpp"
78
#include "hgl/constants.hpp"
89
#include "hgl/directional_tags.hpp"
910
#include "hgl/hypergraph_traits.hpp"
1011
#include "hgl/util.hpp"
1112

13+
#include <algorithm>
1214
#include <memory>
1315
#include <set>
1416
#include <type_traits>
@@ -452,6 +454,10 @@ class hypergraph final {
452454
return this->degree(vertex.id());
453455
}
454456

457+
[[nodiscard]] std::vector<types::size_type> degree_map() const {
458+
return this->_impl.degree_map(this->_n_vertices);
459+
}
460+
455461
[[nodiscard]] auto outgoing_hyperedges(const types::id_type vertex_id)
456462
requires std::same_as<directional_tag, bf_directed_t>
457463
{
@@ -479,6 +485,12 @@ class hypergraph final {
479485
return this->out_degree(vertex.id());
480486
}
481487

488+
[[nodiscard]] std::vector<types::size_type> out_degree_map() const
489+
requires std::same_as<directional_tag, bf_directed_t>
490+
{
491+
return this->_impl.out_degree_map(this->_n_vertices);
492+
}
493+
482494
[[nodiscard]] auto incoming_hyperedges(const types::id_type vertex_id)
483495
requires std::same_as<directional_tag, bf_directed_t>
484496
{
@@ -506,6 +518,12 @@ class hypergraph final {
506518
return this->in_degree(vertex.id());
507519
}
508520

521+
[[nodiscard]] std::vector<types::size_type> in_degree_map() const
522+
requires std::same_as<directional_tag, bf_directed_t>
523+
{
524+
return this->_impl.in_degree_map(this->_n_vertices);
525+
}
526+
509527
[[nodiscard]] auto incident_vertices(const types::id_type hyperedge_id) {
510528
this->_verify_hyperedge_id(hyperedge_id);
511529
return this->_impl.incident_vertices(hyperedge_id)
@@ -527,6 +545,10 @@ class hypergraph final {
527545
return this->hyperedge_size(hyperedge.id());
528546
}
529547

548+
[[nodiscard]] std::vector<types::size_type> hyperedge_size_map() const {
549+
return this->_impl.hyperedge_size_map(this->_n_hyperedges);
550+
}
551+
530552
[[nodiscard]] auto tail_vertices(const types::id_type hyperedge_id)
531553
requires std::same_as<directional_tag, bf_directed_t>
532554
{
@@ -555,6 +577,12 @@ class hypergraph final {
555577
return this->tail_size(hyperedge.id());
556578
}
557579

580+
[[nodiscard]] std::vector<types::size_type> tail_size_map() const
581+
requires std::same_as<directional_tag, bf_directed_t>
582+
{
583+
return this->_impl.tail_size_map(this->_n_hyperedges);
584+
}
585+
558586
[[nodiscard]] auto head_vertices(const types::id_type hyperedge_id)
559587
requires std::same_as<directional_tag, bf_directed_t>
560588
{
@@ -583,6 +611,12 @@ class hypergraph final {
583611
return this->head_size(hyperedge.id());
584612
}
585613

614+
[[nodiscard]] std::vector<types::size_type> head_size_map() const
615+
requires std::same_as<directional_tag, bf_directed_t>
616+
{
617+
return this->_impl.head_size_map(this->_n_hyperedges);
618+
}
619+
586620
private:
587621
// --- vertex methods ---
588622

@@ -659,4 +693,171 @@ class hypergraph final {
659693
[[no_unique_address]] hyperedge_properties_map_type _hyperedge_properties{};
660694
};
661695

696+
// --- general hypergraph utility ---
697+
698+
namespace type_traits {
699+
700+
template <typename G>
701+
concept c_hypergraph = c_instantiation_of<G, hypergraph>;
702+
703+
template <typename G>
704+
concept c_undirected_hypergraph =
705+
c_hypergraph<G> and std::same_as<typename G::directional_tag, undirected_t>;
706+
707+
template <typename G>
708+
concept c_bf_directed_hypergraph =
709+
c_hypergraph<G> and std::same_as<typename G::directional_tag, bf_directed_t>;
710+
711+
} // namespace type_traits
712+
713+
// --- degree bounds ---
714+
715+
[[nodiscard]] types::size_type max_degree(const type_traits::c_hypergraph auto& hypergraph
716+
) noexcept {
717+
const auto degrees = hypergraph.degree_map();
718+
return degrees.empty() ? 0uz : *std::ranges::max_element(degrees);
719+
}
720+
721+
[[nodiscard]] types::size_type min_degree(const type_traits::c_hypergraph auto& hypergraph
722+
) noexcept {
723+
const auto degrees = hypergraph.degree_map();
724+
return degrees.empty() ? 0uz : *std::ranges::min_element(degrees);
725+
}
726+
727+
[[nodiscard]] types::size_type max_out_degree(
728+
const type_traits::c_bf_directed_hypergraph auto& hypergraph
729+
) noexcept {
730+
const auto degrees = hypergraph.out_degree_map();
731+
return degrees.empty() ? 0uz : *std::ranges::max_element(degrees);
732+
}
733+
734+
[[nodiscard]] types::size_type min_out_degree(
735+
const type_traits::c_bf_directed_hypergraph auto& hypergraph
736+
) noexcept {
737+
const auto degrees = hypergraph.out_degree_map();
738+
return degrees.empty() ? 0uz : *std::ranges::min_element(degrees);
739+
}
740+
741+
[[nodiscard]] types::size_type max_in_degree(
742+
const type_traits::c_bf_directed_hypergraph auto& hypergraph
743+
) noexcept {
744+
const auto degrees = hypergraph.in_degree_map();
745+
return degrees.empty() ? 0uz : *std::ranges::max_element(degrees);
746+
}
747+
748+
[[nodiscard]] types::size_type min_in_degree(
749+
const type_traits::c_bf_directed_hypergraph auto& hypergraph
750+
) noexcept {
751+
const auto degrees = hypergraph.in_degree_map();
752+
return degrees.empty() ? 0uz : *std::ranges::min_element(degrees);
753+
}
754+
755+
// --- hyperedge size bounds ---
756+
757+
[[nodiscard]] types::size_type rank(const type_traits::c_hypergraph auto& hypergraph) noexcept {
758+
const auto sizes = hypergraph.hyperedge_size_map();
759+
return sizes.empty() ? 0uz : *std::ranges::max_element(sizes);
760+
}
761+
762+
[[nodiscard]] types::size_type corank(const type_traits::c_hypergraph auto& hypergraph) noexcept {
763+
const auto sizes = hypergraph.hyperedge_size_map();
764+
return sizes.empty() ? 0uz : *std::ranges::min_element(sizes);
765+
}
766+
767+
[[nodiscard]] types::size_type max_tail_size(
768+
const type_traits::c_bf_directed_hypergraph auto& hypergraph
769+
) noexcept {
770+
const auto sizes = hypergraph.tail_size_map();
771+
return sizes.empty() ? 0uz : *std::ranges::max_element(sizes);
772+
}
773+
774+
[[nodiscard]] types::size_type min_tail_size(
775+
const type_traits::c_bf_directed_hypergraph auto& hypergraph
776+
) noexcept {
777+
const auto sizes = hypergraph.tail_size_map();
778+
return sizes.empty() ? 0uz : *std::ranges::min_element(sizes);
779+
}
780+
781+
[[nodiscard]] types::size_type max_head_size(
782+
const type_traits::c_bf_directed_hypergraph auto& hypergraph
783+
) noexcept {
784+
const auto sizes = hypergraph.head_size_map();
785+
return sizes.empty() ? 0uz : *std::ranges::max_element(sizes);
786+
}
787+
788+
[[nodiscard]] types::size_type min_head_size(
789+
const type_traits::c_bf_directed_hypergraph auto& hypergraph
790+
) noexcept {
791+
const auto sizes = hypergraph.head_size_map();
792+
return sizes.empty() ? 0uz : *std::ranges::min_element(sizes);
793+
}
794+
795+
// --- regularity ---
796+
797+
[[nodiscard]] bool is_regular(
798+
const type_traits::c_hypergraph auto& hypergraph, const types::size_type k
799+
) noexcept {
800+
return util::all_equal(hypergraph.degree_map(), k);
801+
}
802+
803+
[[nodiscard]] bool is_regular(const type_traits::c_hypergraph auto& hypergraph) noexcept {
804+
return util::is_constant(hypergraph.degree_map());
805+
}
806+
807+
[[nodiscard]] bool is_out_regular(
808+
const type_traits::c_bf_directed_hypergraph auto& hypergraph, const types::size_type k
809+
) noexcept {
810+
return util::all_equal(hypergraph.out_degree_map(), k);
811+
}
812+
813+
[[nodiscard]] bool is_out_regular(const type_traits::c_bf_directed_hypergraph auto& hypergraph
814+
) noexcept {
815+
return util::is_constant(hypergraph.out_degree_map());
816+
}
817+
818+
[[nodiscard]] bool is_in_regular(
819+
const type_traits::c_bf_directed_hypergraph auto& hypergraph, const types::size_type k
820+
) noexcept {
821+
return util::all_equal(hypergraph.in_degree_map(), k);
822+
}
823+
824+
[[nodiscard]] bool is_in_regular(const type_traits::c_bf_directed_hypergraph auto& hypergraph
825+
) noexcept {
826+
return util::is_constant(hypergraph.in_degree_map());
827+
}
828+
829+
// --- uniformity ---
830+
831+
[[nodiscard]] bool is_uniform(
832+
const type_traits::c_hypergraph auto& hypergraph, const types::size_type k
833+
) noexcept {
834+
return util::all_equal(hypergraph.hyperedge_size_map(), k);
835+
}
836+
837+
[[nodiscard]] bool is_uniform(const type_traits::c_hypergraph auto& hypergraph) noexcept {
838+
return util::is_constant(hypergraph.hyperedge_size_map());
839+
}
840+
841+
[[nodiscard]] bool is_tail_uniform(
842+
const type_traits::c_bf_directed_hypergraph auto& hypergraph, const types::size_type k
843+
) noexcept {
844+
return util::all_equal(hypergraph.tail_size_map(), k);
845+
}
846+
847+
[[nodiscard]] bool is_tail_uniform(const type_traits::c_bf_directed_hypergraph auto& hypergraph
848+
) noexcept {
849+
return util::is_constant(hypergraph.tail_size_map());
850+
}
851+
852+
[[nodiscard]] bool is_head_uniform(
853+
const type_traits::c_bf_directed_hypergraph auto& hypergraph, const types::size_type k
854+
) noexcept {
855+
return util::all_equal(hypergraph.head_size_map(), k);
856+
}
857+
858+
[[nodiscard]] bool is_head_uniform(const type_traits::c_bf_directed_hypergraph auto& hypergraph
859+
) noexcept {
860+
return util::is_constant(hypergraph.head_size_map());
861+
}
862+
662863
} // namespace hgl

0 commit comments

Comments
 (0)