Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/hgl/hypergraph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ class hypergraph final {
this->_n_vertices--;
if constexpr (type_traits::c_non_empty_properties<vertex_properties_type>)
this->_vertex_properties.erase(this->_vertex_properties.begin() + vertex_id);
// TODO: impl::remove_vertex(vertex_id)
}

// --- hyperedge methods ---
Expand Down
8 changes: 4 additions & 4 deletions include/hgl/hypergraph_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ template <
type_traits::c_hypergraph_directional_tag DirectionalTag = undirected_t,
type_traits::c_properties VertexProperties = types::empty_properties,
type_traits::c_properties HyperedgeProperties = types::empty_properties>
using adjacency_list_hg_traits =
hypergraph_traits<DirectionalTag, VertexProperties, HyperedgeProperties, impl::adjacency_list_t>;
using vertex_list_hg_traits =
hypergraph_traits<DirectionalTag, VertexProperties, HyperedgeProperties, impl::vertex_list_t>;

template <
type_traits::c_hypergraph_directional_tag DirectionalTag = undirected_t,
Expand Down Expand Up @@ -69,9 +69,9 @@ concept c_hyperedge_list_hg_traits =
and std::same_as<typename TraitsType::implementation_tag, impl::hyperedge_list_t>;

template <typename TraitsType>
concept c_adjacency_list_hg_traits =
concept c_vertex_list_hg_traits =
c_instantiation_of<TraitsType, hypergraph_traits>
and std::same_as<typename TraitsType::implementation_tag, impl::adjacency_list_t>;
and std::same_as<typename TraitsType::implementation_tag, impl::vertex_list_t>;

template <typename TraitsType>
concept c_incidence_matrix_hg_traits =
Expand Down
79 changes: 53 additions & 26 deletions include/hgl/impl/hyperedge_list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "hgl/types/types.hpp"

#include <algorithm>
#include <ranges>
#include <vector>

#ifdef HGL_TESTING
Expand Down Expand Up @@ -45,11 +46,36 @@ class undirected_hyperedge_list final {

// --- vertex methods ---

gl_attr_force_inline void add_vertices(const types::size_type) const noexcept {}
// clang-format off

gl_attr_force_inline void add_vertices([[maybe_unused]] const types::size_type) const noexcept {}

// clang-format on

void remove_vertex(const types::id_type vertex_id) noexcept {
for (auto& hyperedge_vertices : this->_storage)
this->_unbind_impl(hyperedge_vertices, vertex_id);
for (auto& hyperedge : this->_storage) {
auto vertex_it = std::ranges::lower_bound(hyperedge, vertex_id);
if (vertex_it != hyperedge.end() and *vertex_it == vertex_id)
vertex_it = hyperedge.erase(vertex_it); // unbind the vertex
while (vertex_it != hyperedge.end())
--(*vertex_it++); // decrement ids > vertex_id
}
}

[[nodiscard]] gl_attr_force_inline auto incident_hyperedges(const types::id_type vertex_id
) const noexcept {
return std::views::iota(0uz, this->_storage.size())
| std::views::filter([this, vertex_id](types::id_type hid) {
return this->_are_bound_impl(this->_storage[hid], vertex_id);
});
}

[[nodiscard]] types::size_type degree(const types::id_type vertex_id) const noexcept {
types::size_type deg = 0uz;
for (const auto& hyperedge : this->_storage)
if (this->_are_bound_impl(hyperedge, vertex_id))
++deg;
return deg;
}

// --- hyperedge methods ---
Expand All @@ -62,52 +88,53 @@ class undirected_hyperedge_list final {
this->_storage.erase(this->_storage.begin() + hyperedge_id);
}

[[nodiscard]] gl_attr_force_inline types::size_type hyperedge_size(
const types::id_type hyperedge_id
[[nodiscard]] gl_attr_force_inline auto incident_vertices(const types::id_type hyperedge_id
) const noexcept {
return this->_storage[hyperedge_id].size();
return std::views::all(this->_storage[hyperedge_id]);
}

[[nodiscard]] gl_attr_force_inline auto hyperedge_vertices(const types::id_type hyperedge_id
[[nodiscard]] gl_attr_force_inline types::size_type hyperedge_size(
const types::id_type hyperedge_id
) const noexcept {
return std::views::all(this->_storage[hyperedge_id]);
return this->_storage[hyperedge_id].size();
}

// --- binding methods ---

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

// insert the id at the correct position to keep the vertex-id collection sorted
const auto it = std::ranges::lower_bound(hyperedge_vertices, vertex_id);
if (it == hyperedge_vertices.end() or *it != vertex_id)
hyperedge_vertices.insert(it, vertex_id);
const auto it = std::ranges::lower_bound(hyperedge, vertex_id);
if (it == hyperedge.end() or *it != vertex_id)
hyperedge.insert(it, vertex_id);
}

gl_attr_force_inline void unbind(
const types::id_type hyperedge_id, const types::id_type vertex_id
const types::id_type vertex_id, const types::id_type hyperedge_id
) noexcept {
this->_unbind_impl(this->_storage[hyperedge_id], vertex_id);
auto& hyperedge = this->_storage[hyperedge_id];
const auto vertex_it = std::ranges::lower_bound(hyperedge, vertex_id);
if (vertex_it != hyperedge.end() and *vertex_it == vertex_id)
hyperedge.erase(vertex_it);
}

[[nodiscard]] bool are_bound(const types::id_type hyperedge_id, const types::id_type vertex_id)
const noexcept {
auto& hyperedge_vertices = this->_storage[hyperedge_id];
const auto vertex_it = std::ranges::lower_bound(hyperedge_vertices, vertex_id);
return vertex_it != hyperedge_vertices.end() and *vertex_it == vertex_id;
[[nodiscard]] gl_attr_force_inline bool are_bound(
const types::id_type vertex_id, const types::id_type hyperedge_id
) const noexcept {
return this->_are_bound_impl(this->_storage[hyperedge_id], vertex_id);
}

#ifdef HGL_TESTING
friend struct hgl_testing::test_hyperedge_list;
#endif

private:
void _unbind_impl(
hyperedge_storage_type& hyperedge_vertices, const types::id_type vertex_id
) noexcept {
const auto vertex_it = std::ranges::lower_bound(hyperedge_vertices, vertex_id);
if (vertex_it != hyperedge_vertices.end() and *vertex_it == vertex_id)
hyperedge_vertices.erase(vertex_it);
[[nodiscard]] bool _are_bound_impl(
const hyperedge_storage_type& hyperedge, const types::id_type vertex_id
) const noexcept {
const auto vertex_it = std::ranges::lower_bound(hyperedge, vertex_id);
return vertex_it != hyperedge.end() and *vertex_it == vertex_id;
}

hypergraph_storage_type _storage;
Expand Down
4 changes: 2 additions & 2 deletions include/hgl/impl/impl_tags.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace impl {

struct hyperedge_list_t {};

struct adjacency_list_t {};
struct vertex_list_t {};

struct incidence_matrix_t {};

Expand All @@ -22,7 +22,7 @@ namespace type_traits {

template <typename T>
concept c_hypergraph_impl_tag =
c_one_of<T, impl::hyperedge_list_t, impl::adjacency_list_t, impl::incidence_matrix_t>;
c_one_of<T, impl::hyperedge_list_t, impl::vertex_list_t, impl::incidence_matrix_t>;

} // namespace type_traits

Expand Down
138 changes: 138 additions & 0 deletions include/hgl/impl/vertex_list.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// 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.

#pragma once

#include "hgl/types/types.hpp"

#include <algorithm>
#include <ranges>
#include <vector>

#ifdef HGL_TESTING
namespace hgl_testing {
struct test_vertex_list;
} // namespace hgl_testing
#endif

namespace hgl::impl {

class undirected_vertex_list final {
public:
using vertex_storage_type = std::vector<types::id_type>;
using hypergraph_storage_type = std::vector<vertex_storage_type>;

undirected_vertex_list(const undirected_vertex_list&) = delete;
undirected_vertex_list& operator=(const undirected_vertex_list&) = delete;

undirected_vertex_list() = default;

undirected_vertex_list(
const types::size_type n_vertices, [[maybe_unused]] const types::size_type n_hyperedges
)
: _storage{n_vertices} {}

undirected_vertex_list(undirected_vertex_list&&) = default;
undirected_vertex_list& operator=(undirected_vertex_list&&) = default;

~undirected_vertex_list() = default;

// --- vertex methods ---

gl_attr_force_inline void add_vertices(const types::size_type n) noexcept {
this->_storage.resize(this->_storage.size() + n);
}

gl_attr_force_inline void remove_vertex(const types::id_type vertex_id) noexcept {
this->_storage.erase(this->_storage.begin() + vertex_id);
}

[[nodiscard]] gl_attr_force_inline auto incident_hyperedges(const types::id_type vertex_id
) const noexcept {
return std::views::all(this->_storage[vertex_id]);
}

[[nodiscard]] gl_attr_force_inline types::size_type degree(const types::id_type vertex_id
) const noexcept {
return this->_storage[vertex_id].size();
}

// --- hyperedge methods ---

// clang-format off

gl_attr_force_inline void add_hyperedges([[maybe_unused]] const types::size_type) const noexcept {}

// clang-format on

void remove_hyperedge(const types::id_type hyperedge_id) noexcept {
for (auto& vertex : this->_storage) {
auto hyperedge_it = std::ranges::lower_bound(vertex, hyperedge_id);
if (hyperedge_it != vertex.end() and *hyperedge_it == hyperedge_id)
hyperedge_it = vertex.erase(hyperedge_it); // unbind the hyperedge
while (hyperedge_it != vertex.end())
--(*hyperedge_it++); // decrement ids > hyperedge_id
}
}

[[nodiscard]] gl_attr_force_inline auto incident_vertices(const types::id_type hyperedge_id
) const noexcept {
return std::views::iota(0uz, this->_storage.size())
| std::views::filter([this, hyperedge_id](types::id_type vid) {
return this->_are_bound_impl(this->_storage[vid], hyperedge_id);
});
}

[[nodiscard]] types::size_type hyperedge_size(const types::id_type hyperedge_id
) const noexcept {
types::size_type size = 0uz;
for (const auto& vertex : this->_storage)
if (this->_are_bound_impl(vertex, hyperedge_id))
++size;
return size;
}

// --- binding methods ---

void bind(const types::id_type vertex_id, const types::id_type hyperedge_id) noexcept {
auto& vertex = this->_storage[vertex_id];

// insert the id at the correct position to keep the hyperedge-id collection sorted
const auto it = std::ranges::lower_bound(vertex, hyperedge_id);
if (it == vertex.end() or *it != hyperedge_id)
vertex.insert(it, hyperedge_id);
}

gl_attr_force_inline void unbind(
const types::id_type vertex_id, const types::id_type hyperedge_id
) noexcept {
auto& vertex = this->_storage[vertex_id];

const auto hyperedge_it = std::ranges::lower_bound(vertex, hyperedge_id);
if (hyperedge_it != vertex.end() and *hyperedge_it == hyperedge_id)
vertex.erase(hyperedge_it);
}

[[nodiscard]] gl_attr_force_inline bool are_bound(
const types::id_type vertex_id, const types::id_type hyperedge_id
) const noexcept {
return this->_are_bound_impl(this->_storage[vertex_id], hyperedge_id);
}

#ifdef HGL_TESTING
friend struct hgl_testing::test_vertex_list;
#endif

private:
[[nodiscard]] bool _are_bound_impl(
const vertex_storage_type& vertex, const types::id_type hyperedge_id
) const noexcept {
const auto hyperedge_it = std::ranges::lower_bound(vertex, hyperedge_id);
return hyperedge_it != vertex.end() and *hyperedge_it == hyperedge_id;
}

hypergraph_storage_type _storage;
};

} // namespace hgl::impl
Loading