Skip to content
Merged
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ if (CPP_GL_IS_TOP_LEVEL_PROJECT AND BUILD_TESTS)
add_subdirectory(tests)
endif()

# Include test directory if cpp-gl is a top level project
# Include benchmarks directory if cpp-gl is a top level project
if (CPP_GL_IS_TOP_LEVEL_PROJECT AND BUILD_BENCHMARKS)
add_subdirectory(benchmarks)
endif()
8 changes: 4 additions & 4 deletions benchmarks/suites/hg_b_bfs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ void bm_hgl_backward_bfs(benchmark::State& state) {
/// @brief Executes a Backward BFS equivalent on an Incidence Graph.
template <gl::traits::c_directed_graph IncidenceGraph>
bool incidence_backward_bfs(
const IncidenceGraph& ig,
const std::vector<typename IncidenceGraph::id_type>& roots,
const typename IncidenceGraph::id_type original_n_vertices
IncidenceGraph&& ig,
const std::vector<gl::id_t<IncidenceGraph>>& roots,
const gl::size_type original_n_vertices
) {
using id_type = typename IncidenceGraph::id_type;
using id_type = gl::id_t<IncidenceGraph>;

std::vector<bool> visited_v(original_n_vertices, false);
auto tail_unvisited =
Expand Down
12 changes: 6 additions & 6 deletions docs/hgl/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,10 @@ The complexities of topological queries is identical for standard (`list_t`) and
| 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)$ |
| **Iterate Incident Hyperedges** of $v$ | $O(deg(v))$ | $O(deg(v))$ | $O(\vert E \vert + I)$ (Scan All) |
| **Iterate Incident Vertices** of $e$ | $O(\vert e \vert)$ | $O(\vert V \vert + I)$ (Scan All) | $O(\vert e \vert)$ |
| **Get Degree** of $v$ | $O(1)$ | $O(1)$ | $O(\vert E \vert + I)$ |
| **Get Size** of $e$ | $O(1)$ | $O(\vert V \vert + I)$ | $O(1)$ |

**Structural Mutations (Standard Incidence List, `list_t`):**

Expand Down Expand Up @@ -328,8 +328,8 @@ 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`<br>$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`<br>$O(\vert V \vert)$ amortized if `hyperedge_major_t` | $O(\vert V \vert \cdot \vert E \vert)$ |
| **Add Vertex** | $O(\vert E \vert)$ amortized | $O(\vert V \vert \cdot \vert E \vert)$ |
| **Add Hyperedge** | $O(\vert V \vert)$ amortized | $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)$ |

Expand Down
8 changes: 4 additions & 4 deletions include/gl/algorithm/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,15 @@ using non_void_result_type =
/// @ingroup GL-Algorithm
/// @brief Maps a vertex ID to its predecessor's ID in a traversal tree.
/// @tparam GraphType The type of the graph being traversed.
template <traits::c_graph GraphType>
using predecessors_map = std::vector<typename GraphType::id_type>;
template <traits::c_graph G>
using predecessors_map = std::vector<id_t<G>>;

/// @ingroup GL-Algorithm
/// @brief Represents an active node in a search container (e.g., a BFS queue or DFS stack).
/// @tparam GraphType The type of the graph being searched.
template <traits::c_graph GraphType>
template <traits::c_graph G>
struct search_node {
using id_type = typename GraphType::id_type;
using id_type = id_t<G>;

/// @brief Constructs a search node acting as a root (predecessor is itself).
/// @param vertex_id The ID of the vertex.
Expand Down
23 changes: 10 additions & 13 deletions include/gl/algorithm/pathfinding/dijkstra.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace gl::algorithm {
/// | VertexDistanceType | The numeric type used to represent accumulated path weights/distances. | Must satisfy the [**c_arithmetic**](gl_concepts.md#gl-traits-c-arithmetic) concept. |
template <traits::c_graph G, traits::c_arithmetic VertexDistanceType>
struct paths_descriptor {
using id_type = typename G::id_type;
using id_type = id_t<G>;
using distance_type = VertexDistanceType;

/// @brief Constructs a descriptor sized for the given number of vertices.
Expand All @@ -43,7 +43,7 @@ struct paths_descriptor {
/// @brief An alias for @ref gl::algorithm::paths_descriptor "paths_descriptor" that automatically deduces the appropriate distance type for the graph.
/// @tparam G The type of the graph.
template <traits::c_graph G>
using paths_descriptor_type = paths_descriptor<G, vertex_distance_type<G>>;
using paths_descriptor_type = paths_descriptor<G, vertex_distance_t<G>>;

/// @ingroup GL-Algorithm
/// @brief Factory function to create an initialized paths descriptor sized for the given graph.
Expand All @@ -66,11 +66,11 @@ template <traits::c_graph G>
template <traits::c_graph G>
struct dijkstra_search_node {
/// @brief The type of the vertex ID.
using id_type = typename G::id_type;
using id_type = id_t<G>;

id_type vertex_id; ///< @brief The ID of the vertex represented by this node.
id_type pred_id; ///< The ID of the predecessor vertex used to reach this node.
vertex_distance_type<G>
vertex_distance_t<G>
distance; ///< The accumulated distance from the source to this vertex at the time of enqueueing.
};

Expand Down Expand Up @@ -126,17 +126,14 @@ struct dijkstra_search_node {
/// @hideparams
template <
traits::c_graph G,
traits::c_optional_callback<void, typename G::id_type> PreVisitCallback = empty_callback,
traits::c_optional_callback<void, typename G::id_type> PostVisitCallback = empty_callback>
traits::c_optional_callback<void, id_t<G>> PreVisitCallback = empty_callback,
traits::c_optional_callback<void, id_t<G>> PostVisitCallback = empty_callback>
[[nodiscard]] paths_descriptor_type<G> dijkstra_shortest_paths(
const G& graph,
typename G::id_type source_id,
PreVisitCallback pre_visit = {},
PostVisitCallback post_visit = {}
G&& graph, id_t<G> source_id, PreVisitCallback pre_visit = {}, PostVisitCallback post_visit = {}
) {
using id_type = typename G::id_type;
using edge_type = typename G::edge_type;
using distance_type = vertex_distance_type<G>;
using id_type = id_t<G>;
using edge_type = edge_t<G>;
using distance_type = vertex_distance_t<G>;

auto paths = make_paths_descriptor<G>(graph);

Expand Down
22 changes: 11 additions & 11 deletions include/gl/algorithm/spanning_tree/prim_mst.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ namespace gl::algorithm {
template <traits::c_undirected_graph G>
struct mst_descriptor {
/// @brief The type of the graph.
using graph_type = G;
using graph_type = graph_val_t<G>;
/// @brief The type of the edges stored in the graph.
using edge_type = typename graph_type::edge_type;
using edge_type = edge_t<G>;
/// @brief The numeric type used to represent accumulated tree weights.
using weight_type = vertex_distance_type<graph_type>;
using weight_type = vertex_distance_t<graph_type>;

/// @brief Constructs a descriptor sized to hold the resulting tree edges.
/// @param n_vertices The total number of vertices in the graph.
Expand Down Expand Up @@ -77,9 +77,9 @@ struct mst_descriptor {
/// - @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 <traits::c_undirected_graph G>
[[nodiscard]] mst_descriptor<G> edge_heap_prim_mst(const G& graph, typename G::id_type root_id) {
[[nodiscard]] mst_descriptor<G> edge_heap_prim_mst(G&& graph, id_t<G> root_id) {
// type definitions
using edge_type = typename G::edge_type;
using edge_type = edge_t<G>;

struct edge_comparator {
[[nodiscard]] gl_attr_force_inline bool operator()(
Expand Down Expand Up @@ -160,7 +160,7 @@ template <traits::c_undirected_graph G>
/// ### Template Parameters
/// | Parameter | Description | Constraint |
/// | :-------- | :--- | :--- |
/// | G | The type of the undirected graph being traversed. | Must satisfy the [**c_undirected_graph**](gl_concepts.md#gl-traits-c-undirected-graph) concept and its @ref gl::vertex_distance_type "distance type" must satisfy [**c_has_numeric_limits_max**](gl_concepts.md#gl-traits-c-has-numeric-limits-max).
/// | G | The type of the undirected graph being traversed. | Must satisfy the [**c_undirected_graph**](gl_concepts.md#gl-traits-c-undirected-graph) concept and its @ref gl::vertex_distance_t "distance type" must satisfy [**c_has_numeric_limits_max**](gl_concepts.md#gl-traits-c-has-numeric-limits-max).
///
/// @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.
Expand All @@ -169,12 +169,12 @@ template <traits::c_undirected_graph G>
/// - @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 <traits::c_undirected_graph G>
requires(traits::c_has_numeric_limits_max<vertex_distance_type<G>>)
[[nodiscard]] mst_descriptor<G> vertex_heap_prim_mst(const G& graph, typename G::id_type root_id) {
requires(traits::c_has_numeric_limits_max<vertex_distance_t<G>>)
[[nodiscard]] mst_descriptor<G> vertex_heap_prim_mst(G&& graph, id_t<G> root_id) {
// type definitions
using id_type = typename G::id_type;
using edge_type = typename G::edge_type;
using distance_type = vertex_distance_type<G>;
using id_type = id_t<G>;
using edge_type = edge_t<G>;
using distance_type = vertex_distance_t<G>;

// Prepare the necessary utility
const auto n_vertices = graph.n_vertices();
Expand Down
16 changes: 7 additions & 9 deletions include/gl/algorithm/templates/bfs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,13 @@ namespace gl::algorithm {
template <
traits::c_graph G,
traits::c_forward_range_of<search_node<G>> InitQueueRangeType = std::vector<search_node<G>>,
traits::c_optional_predicate<typename G::id_type> VisitVertexPredicate = empty_callback,
traits::c_optional_predicate<typename G::id_type, typename G::id_type> VisitCallback =
empty_callback,
traits::c_decision_predicate<typename G::id_type, const typename G::edge_type&>
EnqueueNodePred = empty_callback,
traits::c_optional_callback<void, typename G::id_type> PreVisitCallback = empty_callback,
traits::c_optional_callback<void, typename G::id_type> PostVisitCallback = empty_callback>
traits::c_optional_predicate<id_t<G>> VisitVertexPredicate = empty_callback,
traits::c_optional_predicate<id_t<G>, id_t<G>> VisitCallback = empty_callback,
traits::c_decision_predicate<id_t<G>, const edge_t<G>&> EnqueueNodePred = empty_callback,
traits::c_optional_callback<void, id_t<G>> PreVisitCallback = empty_callback,
traits::c_optional_callback<void, id_t<G>> PostVisitCallback = empty_callback>
bool bfs(
const G& graph,
G&& graph,
const InitQueueRangeType& initial_queue_content,
VisitVertexPredicate visit_vertex_pred = {},
VisitCallback visit = {},
Expand All @@ -101,7 +99,7 @@ bool bfs(

// search the graph
while (not q.empty()) {
const search_node node = q.front();
const auto node = q.front();
q.pop();

if constexpr (not traits::c_empty_callback<VisitVertexPredicate>)
Expand Down
30 changes: 14 additions & 16 deletions include/gl/algorithm/templates/dfs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,13 @@ namespace gl::algorithm {
template <
traits::c_graph G,
traits::c_forward_range_of<search_node<G>> InitStackRangeType = std::vector<search_node<G>>,
traits::c_optional_predicate<typename G::id_type> VisitVertexPredicate = empty_callback,
traits::c_optional_predicate<typename G::id_type, typename G::id_type> VisitCallback =
empty_callback,
traits::c_decision_predicate<typename G::id_type, const typename G::edge_type&>
EnqueueNodePred = empty_callback,
traits::c_optional_callback<void, typename G::id_type> PreVisitCallback = empty_callback,
traits::c_optional_callback<void, typename G::id_type> PostVisitCallback = empty_callback>
traits::c_optional_predicate<id_t<G>> VisitVertexPredicate = empty_callback,
traits::c_optional_predicate<id_t<G>, id_t<G>> VisitCallback = empty_callback,
traits::c_decision_predicate<id_t<G>, const edge_t<G>&> EnqueueNodePred = empty_callback,
traits::c_optional_callback<void, id_t<G>> PreVisitCallback = empty_callback,
traits::c_optional_callback<void, id_t<G>> PostVisitCallback = empty_callback>
bool dfs(
const G& graph,
G&& graph,
const InitStackRangeType& initial_stack_content,
VisitVertexPredicate visit_vertex_pred = {},
VisitCallback visit = {},
Expand Down Expand Up @@ -187,15 +185,15 @@ bool dfs(
/// @hideparams
template <
traits::c_graph G,
traits::c_optional_predicate<typename G::id_type> VisitVertexPredicate,
traits::c_optional_predicate<typename G::id_type, typename G::id_type> VisitCallback,
traits::c_decision_predicate<typename G::id_type, const typename G::edge_type&> EnqueueNodePred,
traits::c_optional_callback<void, typename G::id_type> PreVisitCallback = empty_callback,
traits::c_optional_callback<void, typename G::id_type> PostVisitCallback = empty_callback>
traits::c_optional_predicate<id_t<G>> VisitVertexPredicate,
traits::c_optional_predicate<id_t<G>, id_t<G>> VisitCallback,
traits::c_decision_predicate<id_t<G>, const edge_t<G>&> EnqueueNodePred,
traits::c_optional_callback<void, id_t<G>> PreVisitCallback = empty_callback,
traits::c_optional_callback<void, id_t<G>> PostVisitCallback = empty_callback>
void r_dfs(
const G& graph,
const typename G::id_type vertex_id,
const typename G::id_type pred_id,
G&& graph,
const id_t<G> vertex_id,
const id_t<G> pred_id,
VisitVertexPredicate visit_vertex_pred,
VisitCallback visit,
EnqueueNodePred enqueue_node_pred,
Expand Down
19 changes: 7 additions & 12 deletions include/gl/algorithm/templates/pfs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,20 +90,15 @@ template <
typename InitQueueRangeType = std::vector<search_node<G>>,
typename NodeType = std::ranges::range_value_t<InitQueueRangeType>,
traits::c_optional_predicate<NodeType> VisitVertexPredicate = empty_callback,
traits::c_optional_predicate<typename G::id_type, typename G::id_type> VisitCallback =
traits::c_optional_predicate<id_t<G>, id_t<G>> VisitCallback = empty_callback,
traits::c_decision_predicate<id_t<G>, const edge_t<G>&> EnqueueNodePred = empty_callback,
traits::c_optional_callback<NodeType, id_t<G>, id_t<G>, const edge_t<G>&> MakeNodeCallback =
empty_callback,
traits::c_decision_predicate<typename G::id_type, const typename G::edge_type&>
EnqueueNodePred = empty_callback,
traits::c_optional_callback<
NodeType,
typename G::id_type,
typename G::id_type,
const typename G::edge_type&> MakeNodeCallback = empty_callback,
traits::c_optional_callback<void, typename G::id_type> PreVisitCallback = empty_callback,
traits::c_optional_callback<void, typename G::id_type> PostVisitCallback = empty_callback>
traits::c_optional_callback<void, id_t<G>> PreVisitCallback = empty_callback,
traits::c_optional_callback<void, id_t<G>> PostVisitCallback = empty_callback>
requires traits::c_predicate<PQCmp, NodeType, NodeType>
bool pfs(
const G& graph,
G&& graph,
const PQCmp& pq_cmp,
const InitQueueRangeType& initial_queue_content,
VisitVertexPredicate visit_vertex_pred = {},
Expand Down Expand Up @@ -152,7 +147,7 @@ bool pfs(
}
else {
static_assert(
std::constructible_from<NodeType, typename G::id_type, typename G::id_type>,
std::constructible_from<NodeType, id_t<G>, id_t<G>>,
"[gl::algorithm::pfs] Custom NodeType provided without a MakeNodeCallback. "
"The NodeType must be constructible from (target_id, pred_id), or you must "
"provide a MakeNodeCallback!"
Expand Down
18 changes: 8 additions & 10 deletions include/gl/algorithm/topology/coloring.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,11 @@ using bicoloring_type = std::vector<binary_color>;
/// @hideparams
template <
traits::c_graph G,
traits::c_optional_callback<void, typename G::id_type> PreVisitCallback = empty_callback,
traits::c_optional_callback<void, typename G::id_type> PostVisitCallback = empty_callback>
traits::c_optional_callback<void, id_t<G>> PreVisitCallback = empty_callback,
traits::c_optional_callback<void, id_t<G>> PostVisitCallback = empty_callback>
[[nodiscard]] std::optional<bicoloring_type> bipartite_coloring(
const G& graph, PreVisitCallback pre_visit = {}, PostVisitCallback post_visit = {}
G&& graph, PreVisitCallback pre_visit = {}, PostVisitCallback post_visit = {}
) {
using edge_type = typename G::edge_type;

bicoloring_type coloring(graph.n_vertices(), binary_color::value::unset);
for (const auto root_id : graph.vertex_ids()) {
if (coloring[root_id].is_set())
Expand All @@ -82,7 +80,7 @@ template <
init_node_range<G>(root_id),
empty_callback{}, // visit predicate
empty_callback{}, // visit callback
[&coloring](typename G::id_type vertex_id, const edge_type& in_edge)
[&coloring](id_t<G> vertex_id, const edge_t<G>& in_edge)
-> decision { // enqueue predicate
if (in_edge.is_loop())
return decision::abort; // graph is not bipartite
Expand Down Expand Up @@ -119,7 +117,7 @@ template <
/// @return `true` if the graph is bipartite (2-colorable), `false` otherwise.
/// ### See Also
/// - @ref gl::algorithm::apply_coloring "apply_coloring"
[[nodiscard]] gl_attr_force_inline bool is_bipartite(const traits::c_graph auto& graph) {
[[nodiscard]] gl_attr_force_inline bool is_bipartite(traits::c_graph auto&& graph) {
return bipartite_coloring(graph).has_value();
}

Expand All @@ -129,7 +127,7 @@ template <
/// ### Template Parameters
/// | Parameter | Description | Constraint |
/// | :-------- | :--- | :--- |
/// | G | The type of the graph to modify. Must have compatible color properties. | Must satisfy the [**c_graph**](gl_concepts.md#gl-traits-c-graph) concept, and its properties must satisfy [**c_binary_color_properties_type**](gl_concepts.md#gl-traits-c-binary-color-properties-type). |
/// | G | The type of the graph to modify. Must have compatible color properties. | Must satisfy the [**c_mut_graph**](gl_concepts.md#gl-traits-c-mut-graph) concept, and its properties must satisfy [**c_binary_color_properties_type**](gl_concepts.md#gl-traits-c-binary-color-properties-type). |
/// | ColorRange | The type of the range containing the computed colors. | Must satisfy the [**c_sized_range_of**](gl_concepts.md#gl-traits-c-sized-range-of) concept for `binary_color`. |
///
/// @param graph The mutable graph instance whose properties will be updated.
Expand All @@ -138,8 +136,8 @@ template <
/// ### See Also
/// - @ref gl::algorithm::bipartite_coloring "bipartite_coloring"
/// - @ref gl::algorithm::is_bipartite "is_bipartite"
template <traits::c_graph G, traits::c_sized_range_of<binary_color> ColorRange>
requires(traits::c_binary_color_properties_type<typename G::vertex_properties_type>)
template <traits::c_mut_graph G, traits::c_sized_range_of<binary_color> ColorRange>
requires(traits::c_binary_color_properties_type<vertex_properties_t<G>>)
bool apply_coloring(G& graph, const ColorRange& color_range) {
if (std::ranges::size(color_range) != graph.n_vertices())
return false;
Expand Down
Loading
Loading