Skip to content

Commit 9de28ca

Browse files
committed
alg templates page
1 parent 0c3410f commit 9de28ca

4 files changed

Lines changed: 131 additions & 4 deletions

File tree

docs/gl/algorithms/templates.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ The library provides four primary traversal engines.
1515

1616
## The Callback Sequence
1717

18-
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.
18+
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.
1919

2020
### Execution Flowchart
2121

docs/hgl/algorithms/templates.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,124 @@
11
# The Generic Templates
2+
3+
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.
4+
5+
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**:
6+
7+
1. Expand from the current vertex to its incident hyperedges.
8+
2. Expand from those hyperedges to their *target* vertices.
9+
10+
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.
11+
12+
## The Core Engines
13+
14+
The library provides two primary traversal engines for hypergraphs:
15+
16+
- [**`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.
17+
- [**`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.
18+
19+
## Traversal Directions & Policies
20+
21+
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`.
22+
23+
> [!NOTE] API Note
24+
>
25+
> 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`.
26+
27+
- **`forward`**:
28+
29+
- Retrieves hyperedges originating from the current vertex (the **forward star** / `out_hyperedge_ids`).
30+
- Retrieves the vertices targeted by those hyperedges (the **head nodes** / `head_ids`).
31+
32+
- **`backward`**:
33+
34+
- Retrieves hyperedges entering the current vertex (the **backward star** / `in_hyperedge_ids`).
35+
- Retrieves the vertices originating those hyperedges (the **tail nodes** / `tail_ids`).
36+
37+
> [!NOTE] Undirected Hypergraphs
38+
>
39+
> For undirected hypergraphs, the traversal policy seamlessly falls back to retrieving all `incident_hyperedge_ids` and `incident_vertex_ids` regardless of the traversal direction.
40+
41+
## The Callback Sequence
42+
43+
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.
44+
45+
### Execution Flowchart
46+
47+
For a single popped `curr_node` in the `bfs` or `dfs` templates, the execution flow looks exactly like this:
48+
49+
1. **`visit_pred(curr_node)`**
50+
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.
51+
52+
2. **`pre_visit(curr_node)`**
53+
A state-modification hook executed right before the vertex is officially processed.
54+
55+
3. **`visit(curr_node)`**
56+
The primary callback. If this returns `false`, the entire search is immediately aborted.
57+
58+
4. **Hyperedge Expansion (Step 1)**
59+
The engine queries the `traversal_policy` for the target hyperedges. For each `he_id`:
60+
61+
- **`traverse_he_pred(he_id, curr_node.vertex_id)`**
62+
Evaluates whether the hyperedge should be traversed. Returns a `decision`:
63+
- `abort`: Kills the entire algorithm.
64+
- `reject`: Ignores this hyperedge and moves to the next.
65+
- `accept`: Proceeds to evaluate the hyperedge's vertices.
66+
67+
5. **Vertex Expansion (Step 2)**
68+
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):
69+
70+
- **`enqueue_pred(tgt_node)`**
71+
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`:
72+
- `abort`: Kills the entire algorithm.
73+
- `reject`: Ignores this specific target vertex.
74+
- `accept`: Pushes the `tgt_node` to the queue or stack.
75+
76+
6. **`post_visit(curr_node)`**
77+
Executed after all adjacent hyperedges and their target vertices have been evaluated and processed.
78+
79+
## Customizing the Traversal
80+
81+
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.
82+
83+
### Example: Custom Forward BFS Engine
84+
85+
```cpp
86+
#include <hgl/algorithm/templates/bfs.hpp>
87+
#include <hgl/algorithm/core.hpp>
88+
#include <vector>
89+
90+
// Assume 'hg' is a populated BF-directed hypergraph and 'start_id' is valid.
91+
std::vector<bool> visited_v(hg.n_vertices(), false); // (1)!
92+
std::vector<bool> visited_he(hg.n_hyperedges(), false);
93+
94+
using search_node = hgl::algorithm::search_node<decltype(hg)>;
95+
std::vector<search_node> init_nodes = {search_node{start_id}}; // (2)!
96+
97+
bool success = hgl::algorithm::bfs<hgl::algorithm::forward>( // (3)!
98+
hg,
99+
init_nodes,
100+
[&](const auto& node) { return not visited_v[node.vertex_id]; }, // (4)!
101+
[&](const auto& node) { // (5)!
102+
visited_v[node.vertex_id] = true;
103+
return true; // continue search
104+
},
105+
[&](auto he_id, auto /*source_id*/) -> hgl::algorithm::decision { // (6)!
106+
if (visited_he[he_id])
107+
return hgl::algorithm::decision::reject;
108+
109+
visited_he[he_id] = true;
110+
return hgl::algorithm::decision::accept;
111+
},
112+
[&](const auto& tgt_node) -> hgl::algorithm::decision { // (7)!
113+
return !visited_v[tgt_node.vertex_id];
114+
}
115+
);
116+
```
117+
118+
1. Initialize state-tracking vectors for both vertices and hyperedges.
119+
2. Set up the initial queue range with a root node.
120+
3. Explicitly invoke the template with `traversal_direction::forward` (the default, but explicitly shown here for clarity).
121+
4. **`visit_pred`**: Reject nodes in the queue if they were already visited by an earlier, faster branch.
122+
5. **`visit`**: Mark the vertex as visited.
123+
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.
124+
7. **`enqueue_pred`**: Only enqueue adjacent vertices that haven't been visited yet (implicitly casts the boolean condition to a `decision::accept` / `decision::reject`).

include/gl/io/graph_fio.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ requires(std::same_as<Mode, append>)
118118
/// Saves the graph topology and optionally its properties using the Graph Specification Format (GSF).
119119
/// The function strictly respects the @ref gl::io::write "write" and @ref gl::io::append "append" safety guards.
120120
///
121-
/// @tparam GraphType The concrete type of the graph being saved. Must satisfy [**c_graph**](gl_concepts.md#hgl-traits-c-graph).
121+
/// @tparam GraphType The concrete type of the graph being saved. Must satisfy [**c_graph**](gl_concepts.md#gl-traits-c-graph).
122122
/// @tparam Mode The save behavior tag (@ref gl::io::write "write" or @ref gl::io::append "append"). Defaults to `write`.
123123
/// @param graph The graph instance to serialize.
124124
/// @param path The filesystem path where the graph will be saved. Defaults to `"graph.gsf"`.

include/hgl/algorithm/core.hpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,15 +144,19 @@ namespace algorithm {
144144
/// @ingroup HGL-Algorithm
145145
/// @brief Specifies the direction of traversal for *BF-directed* hypergraphs.
146146
///
147-
/// > [!IMPORTANT] API Simplicity
147+
/// > [!IMPORTANT] API Note
148148
/// >
149-
/// > To ensure API simplicity, the `traversal_direction` is used for undirected hypergraphs as well by
149+
/// > - To ensure API simplicity, the `traversal_direction` is used for undirected hypergraphs as well by
150150
/// > the generic traversal templates. However, due to the structural nature of undirected hypergraphs,
151151
/// > both direction values implicitly yield the exact same traversal pattern for undirected hypergraphs.
152+
/// >
153+
/// > - The library utilizes C++20's `using enum` feature for the `traversal_direction` enum type, allowing
154+
/// > you to access these tags directly via `hgl::algorithm::forward` and `hgl::algorithm::backward`.
152155
enum class traversal_direction : bool {
153156
forward, ///< Traverse following the forward star (tail to head).
154157
backward ///< Traverse following the backward star (head to tail).
155158
};
159+
using enum traversal_direction;
156160

157161
/// @ingroup HGL-Algorithm
158162
/// @brief Policy defining how to extract incident hyperedges and target vertices during traversal.

0 commit comments

Comments
 (0)