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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
- `rr_adaptor.hpp` — generic range-of-ranges graph adaptor; exposes graph-v3 CPO interface (vertices, edges, target_id, vertex_value, edge_value, find_vertex) using descriptor-based friend functions; data members declared before friend trailing-return-type declarations to satisfy C++ class scope rules
- `graphviz_output.hpp` — Graphviz `.gv` file writers using vertexlist, incidence, and edges_dfs views
- `germany_routes_example.cpp` — builds a Germany routes graph, traverses it, and runs Dijkstra twice (segment count and km distance)
- **`adjacency_matrix` container** (`container/adjacency_matrix.hpp`) — added dense `n x n` graph container (C++20) with optional C++23 `md_adjacency_matrix` `mdspan` view variant; supports weighted/unweighted graphs and models graph-v3 adjacency CPO concepts.

### Changed
- **`undirected_adjacency_list` mutation API renamed** to match `dynamic_graph` and BGL conventions: `create_vertex` → `add_vertex`, `create_edge` → `add_edge`, `erase_edge` → `remove_edge`. The old member names were removed (no backward-compatible aliases); update call sites accordingly.
Expand Down
13 changes: 6 additions & 7 deletions agents/bgl_migration_strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A comprehensive analysis of the Boost Graph Library (BGL) and graph-v3, identifying migration paths, gaps, and recommended extensions to enable a smooth upgrade transition.

> **Last reviewed:** 2026-05-30 against `include/graph/` source tree.
> **Last reviewed:** 2026-06-01 against `include/graph/` source tree.

---

Expand Down Expand Up @@ -60,7 +60,6 @@ graph-v3 is a ground-up C++20 redesign targeting ISO standardization (P3126–P3
**Key gaps requiring attention for BGL migration:**
- Dozens of missing algorithms across flow, matching, coloring, planarity, isomorphism, centrality, layout, and related areas
- No `subgraph` hierarchy with descriptor mapping
- No `adjacency_matrix` container
- No `copy_graph` utility with cross-type and property mapping support
- No `labeled_graph` adaptor (string labels → vertex mapping)
- No named parameter interface (BGL users must learn new positional API)
Expand Down Expand Up @@ -107,6 +106,8 @@ graph-v3 is a ground-up C++20 redesign targeting ISO standardization (P3126–P3
| `compressed_sparse_row_graph<directedS, VP, EP>` | `compressed_graph<EV, VV, GV, VId, EIndex>` | Both CSR; graph-v3 uses projection-based loading |
| `edge_list<Iter>` | Any `input_range` satisfying `basic_sourced_edgelist` | graph-v3 is more flexible — any range of edge-like tuples works |

> **`adjacency_matrix` API note (2026-06):** use `exists(u, v)` (or `has_edge(u, v)` alias) for edge presence checks. For weighted matrices, direct value read access is `const operator()(u, v)`. The older `weight(u, v)` helper and mutable `operator()(u, v)` are removed.

### Closest Selector Analogues

These are behavioral analogues, not strict one-to-one translations. In particular, classic BGL `mapS` / `hash_mapS` selectors map to set-like containers, whereas graph-v3 `om` / `oum` traits use true map / unordered_map edge containers keyed by target vertex ID.
Expand All @@ -131,7 +132,7 @@ These are behavioral analogues, not strict one-to-one translations. In particula

| BGL Container | Status in graph-v3 | Migration Path |
|---------------|--------------------|--------------------|
| **`adjacency_matrix`** | ❌ Not available | Use `dynamic_graph` with `os`/`ous` edge container for O(log n)/O(1) edge lookup; or implement `adjacency_matrix` |
| **`adjacency_matrix`** | ✅ `adjacency_matrix` | Direct replacement — dense `n x n` container (C++20; C++23 `md_adjacency_matrix` adds an `mdspan` view). Build from an order or an edge range + projection |
| **`directed_graph`** | ❌ Not available | Use `dynamic_graph<EV, VV, GV, VId, false>` for a directed graph, or `true` when you also need in-edge traversal |
| **`undirected_graph`** | ✅ `undirected_adjacency_list` | Direct replacement |
| **`labeled_graph`** | ❌ Not available | Use map-based `dynamic_graph` with `mo*` traits and string keys; or implement as an adaptor |
Expand Down Expand Up @@ -1201,13 +1202,12 @@ These items block migration for the largest number of BGL users:

| Item | Type | Effort | Rationale |
|------|------|--------|-----------|
| **`adjacency_matrix` container** | Container | High | BGL users relying on matrix storage have no direct equivalent |
| **A\* Search** | Algorithm | Medium | Heavily used in pathfinding, robotics, game AI |
| **`copy_graph` utility** | Utility | Low | Cross-type graph copy with property mapping |
| **Betweenness Centrality** | Algorithm | Medium | Core network analysis metric |
| **PageRank** | Algorithm | Low | Widely used iterative algorithm |

> **Done since the previous revision of this plan:** `filtered_graph` adaptor, DOT/GraphML/JSON I/O plus full BGL I/O parity (DIMACS via `dimacs.hpp`, METIS via `metis.hpp`, adjacency-list text via `adjacency_list_text.hpp`), Erdős-Rényi G(n,p)/G(n,m) / Barabási-Albert / 2D grid / path / complete-graph / Watts-Strogatz / R-MAT / PLOD / SSCA#2 generators, `kosaraju` + `tarjan_scc`, `afforest`, library-shipped BGL adaptor (`include/graph/adaptors/bgl/`), composable visitor toolkit (`visitor_factory.hpp`: `make_visitor`, single-event adaptors, `predecessor_recorder`, `distance_recorder`, `time_stamper`), `valid_visitor` strict concept with `static_assert` diagnostics in BFS/DFS/Dijkstra/Bellman-Ford.
> **Done since the previous revision of this plan:** `adjacency_matrix` dense container (C++20 + C++23 `mdspan` variant), `filtered_graph` adaptor, DOT/GraphML/JSON I/O plus full BGL I/O parity (DIMACS via `dimacs.hpp`, METIS via `metis.hpp`, adjacency-list text via `adjacency_list_text.hpp`), Erdős-Rényi G(n,p)/G(n,m) / Barabási-Albert / 2D grid / path / complete-graph / Watts-Strogatz / R-MAT / PLOD / SSCA#2 generators, `kosaraju` + `tarjan_scc`, `afforest`, library-shipped BGL adaptor (`include/graph/adaptors/bgl/`), composable visitor toolkit (`visitor_factory.hpp`: `make_visitor`, single-event adaptors, `predecessor_recorder`, `distance_recorder`, `time_stamper`), `valid_visitor` strict concept with `static_assert` diagnostics in BFS/DFS/Dijkstra/Bellman-Ford.

### Phase 2: Common Algorithm Coverage

Expand All @@ -1228,7 +1228,6 @@ These items block migration for the largest number of BGL users:

| Item | Type | Effort | Rationale |
|------|------|--------|-----------|
| **`adjacency_matrix`** | Container | Medium | Dense graph storage |
| **`subgraph_view`** | Adaptor | High | Hierarchical subgraph with descriptor mapping |
| **`labeled_graph` adaptor** | Adaptor | Medium | String label → vertex mapping |
| **Boyer-Myrvold Planarity** | Algorithm | High | Planarity testing |
Expand Down Expand Up @@ -1322,7 +1321,7 @@ The scores below are directional editorial estimates, not audited counts.

| Category | BGL Features | graph-v3 Coverage | Score |
|----------|-------------|-------------------|-------|
| **Core graph types** | 8 containers | 3 containers + zero-config | 60% |
| **Core graph types** | 8 containers | 4 containers + zero-config | 70% |
| **Concepts & traits** | 18+ concepts | 9 concepts (broader) | 80% (by design) |
| **Property system** | Interior + exterior + bundled (tag-dispatched) | Single `VV`/`EV` value (struct for multiple fields) + `container_value_fn` / `vertex_property_map` for external maps; tag dispatch eliminated by design | 100% (by design) |
| **Traversal algorithms** | BFS, DFS, undirected DFS, topological sort | BFS, DFS, topological sort + lazy views | 100% |
Expand Down
170 changes: 150 additions & 20 deletions docs/user-guide/containers.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ adjacency list concepts so all CPOs, views, and algorithms work interchangeably.
| [`dynamic_graph`](#1-dynamic_graph) | Traits-configured vertex + edge containers | Mutable | General purpose, flexible container choice |
| [`compressed_graph`](#2-compressed_graph) | CSR (Compressed Sparse Row) | Immutable after construction | Read-only, high performance, memory-compact |
| [`undirected_adjacency_list`](#3-undirected_adjacency_list) | Dual doubly-linked lists per edge | Mutable, O(1) edge removal | Undirected graphs, frequent edge insertion/removal |
| [`adjacency_matrix`](#4-adjacency_matrix) | Dense `n x n` cell array | Fixed order, mutable cells | Small/dense graphs, O(1) edge existence queries |

All three live in `graph::container`.
All four live in `graph::container`.

You can also use graphs without any library container:

- **[Range-of-Ranges Graphs](#4-range-of-ranges-graphs-no-library-graph-container-required)** — use standard containers (e.g. `vector<vector<int>>`) directly as graphs, zero-copy
- **[Custom Graphs](#5-custom-graphs)** — adapt your own graph data structure by overriding graph CPOs via ADL
- **[Container Selection Guide](#6-container-selection-guide)** — decision tree and comparison matrix
- **[Range-of-Ranges Graphs](#5-range-of-ranges-graphs-no-library-graph-container-required)** — use standard containers (e.g. `vector<vector<int>>`) directly as graphs, zero-copy
- **[Custom Graphs](#6-custom-graphs)** — adapt your own graph data structure by overriding graph CPOs via ADL
- **[Container Selection Guide](#7-container-selection-guide)** — decision tree and comparison matrix

---

Expand Down Expand Up @@ -529,7 +530,133 @@ g.remove_vertex(2u); // O(V+E): renumbers higher vertex ids

---

## 4. Range-of-Ranges Graphs (No Library Graph Container Required)
## 4. `adjacency_matrix`

```cpp
#include <graph/container/adjacency_matrix.hpp>

namespace graph::container {
template <class EV = void, // edge value (weight) type
class VId = uint32_t, // vertex id / index type (integral)
bool Directed = true> // false adds the reciprocal edge too
class adjacency_matrix;
}
```

`adjacency_matrix` is a **dense** container: it owns an `n x n` block of cells,
one per ordered vertex pair. This makes edge-existence and weight lookups O(1)
at the cost of O(n²) space, so it suits **small or dense** graphs and algorithms
that probe arbitrary `(u, v)` pairs (e.g. Floyd–Warshall, transitive closure).

The number of vertices (the matrix **order**) is fixed at construction. Edges
are added by setting cells; storage never reallocates afterwards, so iterators
and views stay valid for the matrix's lifetime.

Like the other containers, it satisfies `index_adjacency_list`, so all CPOs,
views, and algorithms work. `vertices(g)` yields the integral row indices, and
`out_edges(g, u)` lazily scans row `u`, skipping absent cells.

### Properties

| Property | Value |
|----------|-------|
| Vertex ID assignment | Contiguous (0 .. order-1) |
| Vertex range | Random access |
| Edge range per vertex | Forward (filtered row view) |
| Order | Fixed at construction |
| Append vertices | No |
| Append edges | Yes (set cells) |

### Complexity guarantees

| Operation | Complexity |
|-----------|------------|
| `exists(u, v)` / `has_edge(u, v)` | O(1) |
| `operator()(u, v)` (weighted, const) | O(1) |
| `add_edge(u, v[, val])` | O(1) |
| Iterate `out_edges(g, u)` | O(order) (whole row scanned) |
| `num_edges()` / `num_vertices()` | O(1) |
| Space | O(order²) |

### Quick usage

```cpp
#include <graph/container/adjacency_matrix.hpp>
using namespace graph::container;

// Unweighted, directed, 4 vertices
adjacency_matrix<> g(4);
g.add_edge(0, 1);
g.add_edge(0, 2);
g.add_edge(2, 3);

bool e = g.has_edge(0, 1); // O(1)
bool e2 = g.exists(0, 1); // O(1)

// Weighted (double) — value recovered via edge_value CPO or const operator()(u, v)
adjacency_matrix<double> w(3);
w.add_edge(0, 1, 1.5);
const auto& cw = w;
double wt = cw(0, 1); // 1.5

// Undirected — adds the reciprocal edge automatically
adjacency_matrix<void, std::uint32_t, /*Directed=*/false> u(3);
u.add_edge(0, 1); // both (0,1) and (1,0) now present
```

### Construction from an edge range

As with `dynamic_graph` and `compressed_graph`, an `adjacency_matrix` can be
built directly from a range of edges plus a projection to
`copyable_edge_t<VId, EV>` (`{source_id, target_id [, value]}`). The matrix is
sized to the supplied order; endpoints are **not** scanned to grow it, so every
projected id must be `< order`.

```cpp
#include <graph/container/adjacency_matrix.hpp>
using namespace graph::container;

// Already copyable_edge_t — use std::identity (the default)
std::vector<graph::copyable_edge_t<std::uint32_t>> ee = {{0,1},{0,2},{1,2},{2,3}};
adjacency_matrix<> g(4, ee);

// Project from your own edge type
struct raw_edge { int from, to; double w; };
std::vector<raw_edge> raw = {{0,1,1.5},{0,2,2.5},{1,2,3.5}};
adjacency_matrix<double> wg(3, raw, [](const raw_edge& e) {
return graph::copyable_edge_t<std::uint32_t, double>{
static_cast<std::uint32_t>(e.from), static_cast<std::uint32_t>(e.to), e.w};
});
```

### Template parameters

| Parameter | Default | Meaning |
|-----------|---------|---------|
| `EV` | `void` | Edge value (weight) type; `void` for an unweighted graph |
| `VId` | `uint32_t` | Vertex id / index type (must be integral) |
| `Directed` | `true` | When `false`, `add_edge(u, v)` also adds `v -> u` (symmetric matrix) |

### C++23 `mdspan` variant

When compiled as C++23 with `<mdspan>` available, the header also defines
`md_adjacency_matrix<EV, VId, Directed>`. It has identical concept wiring and
owning storage, but additionally exposes the dense presence plane as a 2-D
`std::mdspan` (`presence()`) and offers natural `m(u, v)` element access. The
`mdspan` is a *view* over the owned storage — it never owns the data.

```cpp
#if defined(__cpp_lib_mdspan)
md_adjacency_matrix<double> g(3);
g.add_edge(0, 1, 2.5);
bool present = g.exists(0, 1); // true
auto plane = g.presence(); // std::mdspan<const std::uint8_t, dextents<size_t,2>>
#endif
```

---

## 5. Range-of-Ranges Graphs (No Library Graph Container Required)

You do not need `dynamic_graph`, `compressed_graph`, or any library container
to use graph-v3. Any **range-of-ranges** whose elements follow a recognised
Expand Down Expand Up @@ -711,7 +838,7 @@ for all range-of-ranges graphs without any extra annotation.

---

## 5. Custom Graphs
## 6. Custom Graphs

If you have an existing graph data structure that does not model a range-of-ranges, you can
still use it with all library views and algorithms by overriding the graph CPOs for your type.
Expand Down Expand Up @@ -779,7 +906,7 @@ order and advanced customization patterns.

---

## 6. Container Selection Guide
## 7. Container Selection Guide

```
┌─ Already have data in → range-of-ranges
Expand All @@ -792,6 +919,9 @@ order and advanced customization patterns.
│ with O(1) removal or
│ mutable edge properties?
Start ────────┤
├─ Small or dense graph with → adjacency_matrix
│ O(1) (u,v) existence queries? (O(n²) space, fixed order)
├─ Graph is read-only → compressed_graph
│ after construction? (smallest memory, best cache)
Expand All @@ -805,19 +935,19 @@ edges must be duplicated (e.g. edges A/B and B/A for vertices A and B). Properti
duplicated, or handled in a way that avoids duplication (e.g. a `std::shared_ptr` to a property
struct), if they are mutable.

| Criterion | `dynamic_graph` | `compressed_graph` | `undirected_adjacency_list` | range-of-ranges |
|-----------|-----------------|--------------------|-----------------------------|------------------|
| Add/remove vertices | Yes | No | Yes | Depends on outer container |
| Add/remove edges | Yes | No | Yes (O(1) remove) | Depends on inner container |
| Directed | Yes | Yes | No (undirected) | Yes |
| Undirected | Yes (duplicated edges) | Yes (duplicated edges) | Yes | Yes (duplicated edges) |
| Mutable Properties | Directed (Yes), Undirected (No) | Directed (Yes), Undirected (No) | Yes | Directed (Yes), Undirected (No); your data |
| Memory efficiency | Medium | Best (CSR) | Highest overhead | Zero overhead (existing data) |
| Cache locality | Depends on trait | Excellent | Poor (linked-list) | Depends on containers used |
| Multi-partite | No | Yes | No | No |
| Container flexibility | 27 trait combos | Fixed (CSR) | Configurable random access vertex container | Any forward_range of forward_ranges |

**Custom graphs.** See [Section 5 (Custom Graphs)](#5-custom-graphs) for how to use your own
| Criterion | `dynamic_graph` | `compressed_graph` | `undirected_adjacency_list` | `adjacency_matrix` | range-of-ranges |
|-----------|-----------------|--------------------|-----------------------------|--------------------|------------------|
| Add/remove vertices | Yes | No | Yes | No (fixed order) | Depends on outer container |
| Add/remove edges | Yes | No | Yes (O(1) remove) | Add only (set cells) | Depends on inner container |
| Directed | Yes | Yes | No (undirected) | Yes | Yes |
| Undirected | Yes (duplicated edges) | Yes (duplicated edges) | Yes | Yes (`Directed=false`) | Yes (duplicated edges) |
| Mutable Properties | Directed (Yes), Undirected (No) | Directed (Yes), Undirected (No) | Yes | Yes (cells) | Directed (Yes), Undirected (No); your data |
| Memory efficiency | Medium | Best (CSR) | Highest overhead | O(n²) (dense) | Zero overhead (existing data) |
| Cache locality | Depends on trait | Excellent | Poor (linked-list) | Excellent (contiguous) | Depends on containers used |
| Multi-partite | No | Yes | No | No | No |
| Container flexibility | 27 trait combos | Fixed (CSR) | Configurable random access vertex container | Fixed (dense) | Any forward_range of forward_ranges |

**Custom graphs.** See [Section 6 (Custom Graphs)](#6-custom-graphs) for how to use your own
graph data structure with all library views and algorithms by overriding graph CPOs.

---
Expand Down
Loading
Loading