Skip to content
33 changes: 25 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
# Default to 'dev' if TAGS is not specified on the command line
TAGS ?= dev

.PHONY: docs build-docs serve-docs clean-docs clean
# Detect if running in GitHub Actions (or any standard CI)
ifeq ($(CI),true)
QUIET :=
else
QUIET := > /dev/null 2>&1
endif

.PHONY: docs build-docs serve-docs clean-docs clean sync-gh-pages

# Target for CI Pull Requests: Just builds to verify everything works (no deployment)
build-docs: clean-docs
Expand All @@ -10,12 +17,14 @@ build-docs: clean-docs
uv run python docs/scripts/gen_concept_docs.py --config docs/config/concepts.json --xml documentation/xml --out docs/cpp-gl
uv run mkdocs build --strict

# Example usage: `make docs TAGS="v2.0.0 latest --update-aliases --push"`
# Example usage:
# - Building the docs for a single tag locally: `make docs TAGS="v<num>-dev"`
# - Building and deploying the docs: `make docs TAGS="v<num> latest --update-aliases --push"`
docs: clean-docs
@echo "==> Deploying MkDocs documentation (Tags: $(TAGS))..."
doxygen Doxyfile
uv run python docs/scripts/gen_concept_docs.py --config docs/config/concepts.json --xml documentation/xml --out docs/cpp-gl
uv run mike deploy $(TAGS)
@doxygen Doxyfile $(QUIET)
@uv run python docs/scripts/gen_concept_docs.py --config docs/config/concepts.json --xml documentation/xml --out docs/cpp-gl $(QUIET)
@uv run mike deploy $(TAGS) $(QUIET)
@echo "==> Documentation deployed to local gh-pages branch."

serve-docs: docs
Expand All @@ -24,8 +33,16 @@ serve-docs: docs

clean-docs:
@echo "==> Cleaning documentation build directories..."
rm -rf docs/cpp-gl/
rm -rf site/
rm -rf documentation/
@rm -rf docs/cpp-gl/
@rm -rf site/
@rm -rf documentation/
@echo "==> Documentation build cleaning complete!"

clean: clean-docs

sync-gh-pages:
@echo "==> Fetching latest gh-pages from remote..."
@git fetch origin gh-pages
@echo "==> Force-updating local gh-pages to match origin..."
@git branch -f gh-pages origin/gh-pages
@echo "==> gh-pages sync complete!"
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,21 @@ The documentation build process utilizes the following toolchain:
To build and serve the documentation locally:

```bash
make serve-docs
make serve-docs # automatically builds and serves the documentation page using the `dev` tag
```

To clean the documentation build directories:
```bash
make clean-docs
```

> [!TIP]
>
> If your local `gh-branches` is out of sync with the `origin/gh-pages` branch, you can run the following command to force sync it to match the state of the origin branch:
> ```bash
> make sync-gh-pages
> ```

> [!NOTE]
>
> The documentation build scripts rely on **[uv](https://docs.astral.sh/uv/)** to simplify Python dependency management within the project. Ensure `uv` is installed on the system before attempting to build the documentation.
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/suites/hg_b_bfs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ bool incidence_backward_bfs(

auto root_nodes =
roots | std::views::transform([](const id_type root_id) {
return gl::algorithm::search_node<IncidenceGraph>{root_id};
return gl::algorithm::search_node<gl::val_t<IncidenceGraph>>{root_id};
})
| std::ranges::to<std::vector>();

Expand Down
2 changes: 1 addition & 1 deletion docs/gl/algorithms/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ For convenience, `decision` implicitly constructs from a boolean, where `true` m

### The Search Node

By default, the active container of a search engine stores [**gl::algorithm::search_node<G>**](../../cpp-gl/structgl_1_1algorithm_1_1search__node.md) structures. This is a lightweight pair containing:
By default, the active container of a search engine stores [**gl::algorithm::search_node**](../../cpp-gl/structgl_1_1algorithm_1_1search__node.md) structures. This is a lightweight pair 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).
Expand Down
2 changes: 1 addition & 1 deletion docs/gl/algorithms/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ For a single popped node in `bfs`, `dfs`, or `pfs`, the execution flow looks exa

## Custom Node Injection (PFS)

While BFS and DFS templates strictly operate on the lightweight [**gl::algorithm::search_node<G>**](../../cpp-gl/structgl_1_1algorithm_1_1search__node.md), the Priority-First Search template often requires tracking dynamic state alongside the vertex ID.
While BFS and DFS templates strictly operate on the lightweight [**gl::algorithm::search_node**](../../cpp-gl/structgl_1_1algorithm_1_1search__node.md), the Priority-First Search template often requires tracking dynamic state alongside the vertex ID.

For instance, in Dijkstra's algorithm, the priority queue must sort nodes based on their accumulated distance from the starting point. You cannot sort based purely on the vertex ID.

Expand Down
6 changes: 6 additions & 0 deletions docs/hgl/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ The HGL module is built upon the exact same zero-cost abstraction philosophy as

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.

> [!IMPORTANT]
>
> The HGL module imports all generic elements (such as type traits, concepts, functions, etc.) from the `gl` namespaces to the `hgl` namespaces. The documentation for these imported elements is available in the [GL module's documentation page](../cpp-gl/group__GL.md).
>
> **NOTE:** Only elements that are **NOT graph-specific** are imported.

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.

---
Expand Down
4 changes: 2 additions & 2 deletions docs/hgl/io.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ At its core, the library overloads the standard `operator<<` for the [**hgl::hyp

> [!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.
> 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 [**fmt**](../cpp-gl/classhgl_1_1hypergraph.md#function-fmt) method of the `hypergraph` class.

```cpp
#include <hgl/hypergraph.hpp>
Expand All @@ -27,7 +27,7 @@ int main() {
auto v0 = hg.vertex(0);

std::cout << "Vertex: " << v0 << '\n'; // (1)!
std::cout << "Hyperedge: " << hg.display(e1) << '\n'; // (2)!
std::cout << "Hyperedge: " << hg.fmt(e1) << '\n'; // (2)!
std::cout << "Hypergraph:\n" << hg << '\n'; // (3)!
}
```
Expand Down
2 changes: 1 addition & 1 deletion include/gl/algorithm/spanning_tree/prim_mst.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace gl::algorithm {
template <traits::c_undirected_graph G>
struct mst_descriptor {
/// @brief The type of the graph.
using graph_type = graph_val_t<G>;
using graph_type = val_t<G>;
/// @brief The type of the edges stored in the graph.
using edge_type = edge_t<G>;
/// @brief The numeric type used to represent accumulated tree weights.
Expand Down
5 changes: 3 additions & 2 deletions include/gl/algorithm/templates/bfs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ namespace gl::algorithm {
/// @hideparams
template <
traits::c_graph G,
traits::c_forward_range_of<search_node<G>> InitQueueRangeType = std::vector<search_node<G>>,
traits::c_forward_range_of<search_node<val_t<G>>> InitQueueRangeType =
std::vector<search_node<val_t<G>>>,
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,
Expand All @@ -93,7 +94,7 @@ bool bfs(
return false;

// prepare the node queue
std::queue<search_node<G>> q;
std::queue<search_node<val_t<G>>> q;
for (const auto& node : initial_queue_content)
q.push(node);

Expand Down
5 changes: 3 additions & 2 deletions include/gl/algorithm/templates/dfs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ namespace gl::algorithm {
/// @hideparams
template <
traits::c_graph G,
traits::c_forward_range_of<search_node<G>> InitStackRangeType = std::vector<search_node<G>>,
traits::c_forward_range_of<search_node<val_t<G>>> InitStackRangeType =
std::vector<search_node<val_t<G>>>,
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,
Expand All @@ -92,7 +93,7 @@ bool dfs(
return false;

// prepare the node stack
std::stack<search_node<G>> s;
std::stack<search_node<val_t<G>>> s;
for (const auto& node : initial_stack_content)
s.push(node);

Expand Down
2 changes: 1 addition & 1 deletion include/gl/algorithm/templates/pfs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ namespace gl::algorithm {
template <
traits::c_graph G,
typename PQCmp,
typename InitQueueRangeType = std::vector<search_node<G>>,
typename InitQueueRangeType = std::vector<search_node<val_t<G>>>,
typename NodeType = std::ranges::range_value_t<InitQueueRangeType>,
traits::c_optional_predicate<NodeType> VisitVertexPredicate = empty_callback,
traits::c_optional_predicate<id_t<G>, id_t<G>> VisitCallback = empty_callback,
Expand Down
2 changes: 1 addition & 1 deletion include/gl/algorithm/topology/topological_sort.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ template <
std::vector<size_type> in_degree_map = graph.in_degree_map();

// prepare the initial queue content (source vertices)
std::vector<search_node<G>> source_vertex_list;
std::vector<search_node<val_t<G>>> source_vertex_list;
source_vertex_list.reserve(graph.n_vertices());
for (const auto id : graph.vertex_ids())
if (in_degree_map[to_idx(id)] == 0uz)
Expand Down
5 changes: 3 additions & 2 deletions include/gl/algorithm/util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ template <traits::c_id_type IdType>
/// @return A container initialized with a single @ref search_node for the root vertex.
template <
traits::c_graph G,
traits::c_forward_range_of<search_node<G>> InitRangeType = std::vector<search_node<G>>>
traits::c_forward_range_of<search_node<val_t<G>>> InitRangeType =
std::vector<search_node<val_t<G>>>>
[[nodiscard]] gl_attr_force_inline InitRangeType init_node_range(id_t<G> root_vertex_id) {
return InitRangeType{search_node<G>{root_vertex_id}};
return InitRangeType{search_node<val_t<G>>{root_vertex_id}};
}

/// @ingroup GL-Algorithm
Expand Down
12 changes: 6 additions & 6 deletions include/gl/edge_descriptor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ class edge_descriptor final {
: _id(id), _vertices(source, target), _properties(properties) {}

/// @brief Implicit converting constructor from a non-const descriptor to a const descriptor.
/// @tparam NonConstProps The non-const property type.
/// @tparam MutProperties The mutable property type.
/// @param other The edge descriptor to convert from.
template <typename NonConstProperties>
requires(std::same_as<Properties, const NonConstProperties>)
edge_descriptor(const edge_descriptor<NonConstProperties, IdType>& other) noexcept
template <typename MutProperties>
requires(std::same_as<Properties, const MutProperties>)
edge_descriptor(const edge_descriptor<MutProperties, id_type>& other) noexcept
: _id(other.id()), _vertices(other._vertices) {
if constexpr (traits::c_non_empty_properties<Properties>) {
this->_properties = other.properties();
Expand Down Expand Up @@ -154,7 +154,7 @@ class edge_descriptor final {
template <traits::c_properties OtherProperties>
requires(traits::c_directed_edge<type> and std::same_as<std::remove_cv_t<properties_type>, std::remove_cv_t<OtherProperties>>)
[[nodiscard]] bool operator==(
const edge_descriptor<DirectionalTag, OtherProperties, IdType>& other
const edge_descriptor<DirectionalTag, OtherProperties, id_type>& other
) const noexcept {
return this->_id == other.id() and (this->_vertices == other.incident_vertices());
}
Expand All @@ -166,7 +166,7 @@ class edge_descriptor final {
template <traits::c_properties OtherProperties>
requires(traits::c_undirected_edge<type> and std::same_as<std::remove_cv_t<properties_type>, std::remove_cv_t<OtherProperties>>)
[[nodiscard]] bool operator==(
const edge_descriptor<DirectionalTag, OtherProperties, IdType>& other
const edge_descriptor<DirectionalTag, OtherProperties, id_type>& other
) const noexcept {
return this->_id == other.id()
and (this->_vertices == other.incident_vertices()
Expand Down
Loading
Loading