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
2 changes: 1 addition & 1 deletion docs/user-guide/adjacency-lists.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ Extracts the per-vertex value type from a property map container:

// Works with any adjacency_list graph — index or mapped
auto distances = make_vertex_property_map<G, int>(g,
shortest_path_infinite_distance<int>());
infinite_distance<int>());
auto predecessors = make_vertex_property_map<G, vertex_id_t<G>>(g,
vertex_id_t<G>{});

Expand Down
6 changes: 3 additions & 3 deletions docs/user-guide/algorithms.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ dijkstra_shortest_paths(g, 0, distance, predecessor,

// Map-based graph: use vertex property maps
using G = /* some mapped_adjacency_list graph */;
auto distances = make_vertex_property_map<G, int>(g, shortest_path_infinite_distance<int>());
auto distances = make_vertex_property_map<G, int>(g, infinite_distance<int>());
auto predecessors = make_vertex_property_map<G, vertex_id_t<G>>(g, vertex_id_t<G>{});
for (auto&& [uid, u] : views::vertexlist(g))
predecessors[uid] = uid;
Expand Down Expand Up @@ -299,8 +299,8 @@ All shortest-path algorithms share utilities from `traversal_common.hpp`:
|---------|---------|
| `init_shortest_paths(distances)` | Set all distances to infinity |
| `init_shortest_paths(distances, predecessors)` | Set distances to infinity, predecessors to self |
| `shortest_path_infinite_distance<T>()` | Returns the "infinity" sentinel for type `T` |
| `shortest_path_zero<T>()` | Returns the additive identity for type `T` |
| `infinite_distance<T>()` | Returns the "infinity" sentinel for type `T` |
| `zero_distance<T>()` | Returns the additive identity for type `T` |

### Visitors

Expand Down
45 changes: 24 additions & 21 deletions docs/user-guide/algorithms/bellman_ford.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ from the predecessor array.
// Multi-source, distances + predecessors
[[nodiscard]] constexpr optional<vertex_id_t<G>>
bellman_ford_shortest_paths(G&& g, const Sources& sources,
Distances& distances, Predecessors& predecessors,
DistanceFn&& distance, PredecessorFn&& predecessor,
WF&& weight = /* default returns 1 */,
Visitor&& visitor = empty_visitor(),
Compare&& compare = less<>{},
Expand All @@ -89,7 +89,7 @@ bellman_ford_shortest_paths(G&& g, const Sources& sources,
// Single-source, distances + predecessors
[[nodiscard]] constexpr optional<vertex_id_t<G>>
bellman_ford_shortest_paths(G&& g, const vertex_id_t<G>& source,
Distances& distances, Predecessors& predecessors,
DistanceFn&& distance, PredecessorFn&& predecessor,
WF&& weight,
Visitor&& visitor = empty_visitor(),
Compare&& compare = less<>{},
Expand All @@ -98,7 +98,7 @@ bellman_ford_shortest_paths(G&& g, const vertex_id_t<G>& source,
// Multi-source, distances only
[[nodiscard]] constexpr optional<vertex_id_t<G>>
bellman_ford_shortest_distances(G&& g, const Sources& sources,
Distances& distances,
DistanceFn&& distance,
WF&& weight,
Visitor&& visitor = empty_visitor(),
Compare&& compare = less<>{},
Expand All @@ -107,7 +107,7 @@ bellman_ford_shortest_distances(G&& g, const Sources& sources,
// Single-source, distances only
[[nodiscard]] constexpr optional<vertex_id_t<G>>
bellman_ford_shortest_distances(G&& g, const vertex_id_t<G>& source,
Distances& distances,
DistanceFn&& distance,
WF&& weight,
Visitor&& visitor = empty_visitor(),
Compare&& compare = less<>{},
Expand All @@ -125,8 +125,8 @@ void find_negative_cycle(G& g, const Predecessors& predecessor,
|-----------|-------------|
| `g` | Graph satisfying `adjacency_list` |
| `source` / `sources` | Source vertex ID or range of source vertex IDs |
| `distances` | Subscriptable by `vertex_id_t<G>`. For index graphs, a pre-sized `std::vector`; for mapped graphs, use `make_vertex_property_map<G, T>(g, init)`. Must satisfy `vertex_property_map_for<Distances, G>`. |
| `predecessors` | Subscriptable by `vertex_id_t<G>`. For index graphs, a pre-sized `std::vector`; for mapped graphs, use `make_vertex_property_map<G, T>(g, init)`. Must satisfy `vertex_property_map_for<Predecessors, G>`. |
| `distance` | Callable `(const G&, vertex_id_t<G>) -> Distance&` returning a mutable reference to the per-vertex distance. For containers: wrap with `container_value_fn(dist)`. Must satisfy `distance_fn_for<DistanceFn, G>`. |
| `predecessor` | Callable `(const G&, vertex_id_t<G>) -> Predecessor&` returning a mutable reference to the per-vertex predecessor. For containers: wrap with `container_value_fn(pred)`. Must satisfy `predecessor_fn_for<PredecessorFn, G>`. Use `_null_predecessor` when path reconstruction is not needed. |
| `weight` | Callable `WF(g, uv)` returning edge weight (may be negative). Must satisfy `basic_edge_weight_function`. |
| `visitor` | Optional visitor struct with callback methods (see below). Default: `empty_visitor{}`. |
| `compare` | Comparison function for distance values. Default: `std::less<>{}`. |
Expand Down Expand Up @@ -176,11 +176,12 @@ each edge satisfies the triangle inequality.
- ✅ DAGs (works correctly, though topological-order relaxation is faster)
- ⚠️ Negative-weight cycles — detected and reported; distances undefined for affected vertices

**Container Requirements:**
**Property Function Requirements:**
- Required: `adjacency_list<G>`
- `distances` must satisfy `vertex_property_map_for<Distances, G>`
- `predecessors` must satisfy `vertex_property_map_for<Predecessors, G>`
- `distance` must satisfy `distance_fn_for<DistanceFn, G>`
- `predecessor` must satisfy `predecessor_fn_for<PredecessorFn, G>`
- `weight` must satisfy `basic_edge_weight_function`
- Use `container_value_fn(vec)` to adapt a `std::vector` or similar container

## Examples

Expand All @@ -205,12 +206,13 @@ std::vector<uint32_t> pred(num_vertices(g));

init_shortest_paths(dist, pred);

auto cycle = bellman_ford_shortest_paths(g, 0u, dist, pred,
auto cycle = bellman_ford_shortest_paths(g, 0u, container_value_fn(dist), container_value_fn(pred),
[](const auto& g, const auto& uv) { return edge_value(g, uv); });

if (!cycle) {
// No negative cycle — dist[v] is the shortest distance from source 0
// dist[0] = 0, dist[1] = 6, dist[2] = 7, dist[3] = 2, dist[4] = 4
// Unreachable vertices retain infinite_distance<int>()
}
```

Expand All @@ -228,7 +230,7 @@ std::vector<uint32_t> pred(num_vertices(g));

init_shortest_paths(dist, pred);

auto cycle = bellman_ford_shortest_paths(g, 0u, dist, pred,
auto cycle = bellman_ford_shortest_paths(g, 0u, container_value_fn(dist), container_value_fn(pred),
[](const auto& g, const auto& uv) { return edge_value(g, uv); });

if (cycle) {
Expand All @@ -245,7 +247,7 @@ if (cycle) {
Use `find_negative_cycle` to obtain the full cycle from the predecessor array.

```cpp
auto cycle = bellman_ford_shortest_paths(g, 0u, dist, pred,
auto cycle = bellman_ford_shortest_paths(g, 0u, container_value_fn(dist), container_value_fn(pred),
[](const auto& g, const auto& uv) { return edge_value(g, uv); });

if (cycle) {
Expand All @@ -272,7 +274,7 @@ std::vector<uint32_t> pred(num_vertices(g));
init_shortest_paths(dist, pred);

std::array sources{0u, 3u};
auto cycle = bellman_ford_shortest_paths(g, sources, dist, pred,
auto cycle = bellman_ford_shortest_paths(g, sources, container_value_fn(dist), container_value_fn(pred),
[](const auto& g, const auto& uv) { return edge_value(g, uv); });

// dist[v] = shortest distance from nearest source to v
Expand All @@ -287,11 +289,12 @@ When you don't need predecessors (and can't reconstruct paths), use
std::vector<int> dist(num_vertices(g));
init_shortest_paths(dist);

auto cycle = bellman_ford_shortest_distances(g, 0u, dist,
auto cycle = bellman_ford_shortest_distances(g, 0u, container_value_fn(dist),
[](const auto& g, const auto& uv) { return edge_value(g, uv); });

if (!cycle) {
// dist[v] = shortest distance from source 0 to v
// Unreachable vertices retain infinite_distance<int>()
// No path reconstruction available (no predecessors)
}
```
Expand Down Expand Up @@ -320,7 +323,7 @@ struct NegativeCycleInspector {
};

NegativeCycleInspector inspector;
auto cycle = bellman_ford_shortest_paths(g, 0u, dist, pred,
auto cycle = bellman_ford_shortest_paths(g, 0u, container_value_fn(dist), container_value_fn(pred),
[](const auto& g, const auto& uv) { return edge_value(g, uv); },
inspector);

Expand All @@ -330,24 +333,24 @@ auto cycle = bellman_ford_shortest_paths(g, 0u, dist, pred,
## Mandates

- `G` must satisfy `adjacency_list<G>`
- `Distances` must satisfy `vertex_property_map_for<Distances, G>`
- `Predecessors` must satisfy `vertex_property_map_for<Predecessors, G>`
- `DistanceFn` must satisfy `distance_fn_for<DistanceFn, G>`
- `PredecessorFn` must satisfy `predecessor_fn_for<PredecessorFn, G>` (or use `_null_predecessor`)
- `WF` must satisfy `basic_edge_weight_function`
- All overloads are `[[nodiscard]]` — the compiler warns if the return value is discarded

## Preconditions

- `distances` and `predecessors` must be pre-sized for all vertex IDs in `g`
- Call `init_shortest_paths(distances, predecessors)` before invoking the algorithm
- `distance(g, uid)` must be valid for all vertex IDs in `g`
- Call `init_shortest_paths(dist, pred)` on the underlying containers before invoking the algorithm
- All source vertex IDs must be valid vertex IDs in `g`
- **Always check the return value** — if a negative cycle exists, distances are
undefined for affected vertices

## Effects

- Writes shortest-path distances to `distances[v]` for all reachable vertices
- Writes shortest-path distances via `distance(g, v)` for all reachable vertices
(when no negative cycle exists)
- Writes predecessor vertex IDs to `predecessors[v]` for path reconstruction
- Writes predecessor vertex IDs via `predecessor(g, v)` for path reconstruction
(`bellman_ford_shortest_paths` only)
- Does not modify the graph `g`
- Invokes visitor callbacks during relaxation and verification passes
Expand Down
45 changes: 26 additions & 19 deletions docs/user-guide/algorithms/connected_components.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,24 @@ vertex v.
### `connected_components` — undirected

```cpp
size_t connected_components(G&& g, Component& component);
size_t connected_components(G&& g, ComponentFn&& component);
```

DFS-based algorithm for undirected graphs. Returns the number of connected
components. Assigns `component[v]` = component ID (0-based).
components. `component(g, uid)` is called with each vertex ID to assign its
component ID (0-based).

### `kosaraju` — strongly connected components

```cpp
void kosaraju(G&& g, GT&& g_transpose, Component& component);
void kosaraju(G&& g, GT&& g_transpose, ComponentFn&& component);

// Bidirectional overload — no transpose graph needed
void kosaraju(G&& g, ComponentFn&& component);
```

Kosaraju's two-pass DFS algorithm for directed graphs. Requires both the
original graph `g` and its transpose `g_transpose` (edges reversed). Fills
`component[v]` with the SCC ID.
Kosaraju's two-pass DFS algorithm for directed graphs. Fills
`component(g, uid)` with the SCC ID for each vertex.

### `afforest` — union-find with neighbor sampling

Expand All @@ -105,19 +108,19 @@ void afforest(G&& g, GT&& g_transpose, Component& component,
Union-find-based algorithm with neighbor sampling for large or parallel-friendly
workloads. The `neighbor_rounds` parameter controls how many initial sampling
rounds are performed before falling back to full edge iteration. Call
`compress(component)` afterwards for canonical (root) component IDs on a
single-machine.
`compress(component)` afterwards for canonical (root) component IDs.

The two-graph variant accepts a transpose `g_transpose` for directed-graph
support. The transpose must also satisfy `adjacency_list`.
> **Note:** `afforest` retains a container-based `Component&` interface (not the
> function-based API) because its internal union-find helpers (`link`, `compress`,
> `sample_frequent_element`) require direct subscript access to the component array.

## Parameters

| Parameter | Description |
|-----------|-------------|
| `g` | Graph satisfying `adjacency_list` |
| `g_transpose` | Transpose graph (for `kosaraju` and `afforest` with transpose). `kosaraju` requires `adjacency_list`; `afforest` requires `adjacency_list`. |
| `component` | Subscriptable by `vertex_id_t<G>`. For index graphs, a pre-sized `std::vector`; for mapped graphs, use `make_vertex_property_map<G, T>(g, init)`. Must satisfy `vertex_property_map_for<Component, G>`. |
| `g_transpose` | Transpose graph (for `kosaraju` and `afforest` with transpose). Must satisfy `adjacency_list`. |
| `component` | For `connected_components` and `kosaraju`: callable `(const G&, vertex_id_t<G>) -> ComponentID&` returning a mutable reference. For containers: wrap with `container_value_fn(comp)`. Must satisfy `vertex_property_fn_for<ComponentFn, G>`. For `afforest`: a subscriptable container (`vector` or `unordered_map`), still using the container API. |
| `neighbor_rounds` | Number of neighbor-sampling rounds for `afforest` (default: 2) |

**Return value (`connected_components` only):** `size_t` — number of connected
Expand All @@ -143,7 +146,8 @@ components. `kosaraju` and `afforest` return `void`.

**Container Requirements:**
- Required: `adjacency_list<G>`
- `component` must satisfy `vertex_property_map_for<Component, G>`
- `component` (`connected_components` and `kosaraju`) must satisfy `vertex_property_fn_for<ComponentFn, G>`
- `component` (`afforest`) must satisfy `vertex_property_map_for<Component, G>`
- `kosaraju`: transpose graph must also satisfy `adjacency_list`

## Examples
Expand All @@ -167,7 +171,7 @@ using Graph = container::dynamic_graph<void, void, void, uint32_t, false,
Graph g({{0, 1}, {1, 0}, {1, 2}, {2, 1}, {3, 4}, {4, 3}});

std::vector<uint32_t> comp(num_vertices(g));
size_t num_cc = connected_components(g, comp);
size_t num_cc = connected_components(g, container_value_fn(comp));

// num_cc == 2
// comp[0] == comp[1] == comp[2] (same component)
Expand All @@ -188,7 +192,7 @@ Graph g({{0, 1}, {1, 2}, {2, 0}, {2, 3}});
Graph g_t({{1, 0}, {2, 1}, {0, 2}, {3, 2}});

std::vector<uint32_t> comp(num_vertices(g));
kosaraju(g, g_t, comp);
kosaraju(g, g_t, container_value_fn(comp));

// comp[0] == comp[1] == comp[2] (cycle forms an SCC)
// comp[3] != comp[0] (3 is its own SCC — not on any cycle)
Expand Down Expand Up @@ -217,7 +221,7 @@ bidirectional edge pairs.
undirected_adjacency_list<int, int> g({{0, 1, 1}, {1, 2, 1}, {3, 4, 1}});

std::vector<uint32_t> comp(num_vertices(g));
size_t num_cc = connected_components(g, comp);
size_t num_cc = connected_components(g, container_value_fn(comp));

// num_cc == 2
// Same results as vov with bidirectional edges, but simpler construction
Expand Down Expand Up @@ -270,19 +274,22 @@ compress(comp);
## Mandates

- `G` must satisfy `adjacency_list<G>`
- `Component` must satisfy `vertex_property_map_for<Component, G>`
- `connected_components` and `kosaraju`: `ComponentFn` must satisfy `vertex_property_fn_for<ComponentFn, G>`
- `afforest`: `Component` must satisfy `vertex_property_map_for<Component, G>`
- For `kosaraju`: `GT` must satisfy `adjacency_list<GT>`

## Preconditions

- `component` must be pre-sized for all vertex IDs in `g`
- `component(g, uid)` must be valid for all vertex IDs in `g` (`connected_components`, `kosaraju`)
- `component` must be pre-sized for all vertex IDs in `g` (`afforest`)
- For `connected_components`: undirected graphs must store both directions of
each edge (or use `undirected_adjacency_list`)
- For `kosaraju`: the transpose graph must contain all edges reversed

## Effects

- Writes component IDs to `component[v]` for all vertices
- Writes component IDs via `component(g, uid)` for all vertices (`connected_components`, `kosaraju`)
- Writes component IDs to `component[uid]` for all vertices (`afforest`)
- Does not modify the graph `g` (or `g_transpose`)
- For `afforest`: call `compress(component)` afterwards for canonical root IDs

Expand Down
Loading
Loading