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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,11 @@
- `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.
- **`basic_edge<G, E>` concept** (`detail/edge_cpo.hpp`, in `namespace graph`) — shared edge floor requiring only `source_id(g, e)` and `target_id(g, e)`. Used by both the adjacency-list `edge` concept and `edge_list::basic_sourced_edgelist`, tying the two abstract data types together at the edge level.
- **`vertex_value(g, uid)` convenience overload** — id-based form of the `vertex_value` CPO. Mirrors the descriptor dispatch: prefers a member `g.vertex_value(uid)` or ADL `vertex_value(g, uid)` taking the id directly, falling back to `vertex_value(g, *find_vertex(g, uid))` only when neither exists.

### Changed
- **`edge<G, E>` concept split into `basic_edge` + `edge`** — the adjacency-list `edge` now refines the shared `graph::basic_edge` (source_id/target_id) and adds the `source(g, e)` / `target(g, e)` vertex descriptors. Bare edge-list elements (tuples/pairs/`edge_data`) satisfy `basic_edge` but not `edge`. `edge_list::basic_sourced_edgelist` now requires `basic_edge` and drops its previous `target_id`→`source_id` return-type convertibility clause (return types are intentionally unconstrained, matching the adjacency-list side).
- **`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.
- **`edge_descriptor` simplified to iterator-only storage** — removed the `conditional_t<random_access_iterator, size_t, EdgeIter>` dual-storage path; edges always store the iterator directly since edges always have physical containers. Eliminates 38 `if constexpr` branches across 6 files (~500 lines removed).
- **`compressed_graph::vertices(g)` returns `iota_view`** — simplified to `std::ranges::iota_view<size_t, size_t>(0, num_vertices())`, which the `vertices` CPO wraps automatically via `_wrap_if_needed`.
Expand Down
9 changes: 7 additions & 2 deletions docs/assets/adjacency_list_concepts.dot
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ digraph ConceptHierarchy {

node [fillcolor="#cce5ff"] // light blue

edge_c [label="edge<G,E>\nsource_id · target_id"]
basic_edge_c [label="basic_edge<G,E>\nsource_id · target_id\n(shared with edge_list)"]
edge_c [label="edge<G,E>\nbasic_edge\n+ source · target"]
vertex_c [label="vertex<G,V>\nvertex_descriptor\nvertex_id · find_vertex"]
hashable_vertex_id [label="hashable_vertex_id<G>\nhash(vertex_id_t<G>)"]

Expand Down Expand Up @@ -50,6 +51,9 @@ digraph ConceptHierarchy {

// ── Edges (arrows = "is required by" / "refines") ─────────────────────────

// Shared edge floor → adjacency-list edge refinement
basic_edge_c -> edge_c

// Primitives → ranges
edge_c -> out_edge_range
edge_c -> in_edge_range
Expand Down Expand Up @@ -83,7 +87,8 @@ digraph ConceptHierarchy {
mapped_vertex_range -> mapped_bidirectional_adjacency_list

// ── Rank hints for cleaner layout ─────────────────────────────────────────
{ rank=same; edge_c; vertex_c; hashable_vertex_id }
{ rank=same; basic_edge_c; vertex_c; hashable_vertex_id }
{ rank=same; edge_c }
{ rank=same; out_edge_range; in_edge_range; vertex_range }
{ rank=same; index_vertex_range; mapped_vertex_range }
{ rank=same; adjacency_list }
Expand Down
507 changes: 261 additions & 246 deletions docs/assets/adjacency_list_concepts.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion docs/assets/edge_list_concepts.dot
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ digraph EdgeListConceptHierarchy {

node [fillcolor="#cce5ff"] // light blue

basic_sourced_edgelist [label="basic_sourced_edgelist<EL>\ninput_range\n!range-of-ranges\nsource_id · target_id\n(compatible return types)"]
basic_edge [label="basic_edge<EL,E>\nsource_id · target_id\n(shared with adj_list)"]
basic_sourced_edgelist [label="basic_sourced_edgelist<EL>\ninput_range\n!range-of-ranges\nbasic_edge<EL, range_value_t<EL>>"]

// ── Refinements ───────────────────────────────────────────────────────────

Expand All @@ -22,6 +23,7 @@ digraph EdgeListConceptHierarchy {

// ── Edges ─────────────────────────────────────────────────────────────────

basic_edge -> basic_sourced_edgelist
basic_sourced_edgelist -> basic_sourced_index_edgelist
basic_sourced_edgelist -> has_edge_value

Expand Down
117 changes: 65 additions & 52 deletions docs/assets/edge_list_concepts.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion docs/reference/adjacency-list-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ All concepts are in `graph::adj_list`, re-exported to `graph`.

| Concept | Parameters | Description |
|---------|------------|-------------|
| `edge<G, E>` | Graph `G`, edge `E` | `E` is an `edge_descriptor`; `source_id(g,e)`, `source(g,e)`, `target_id(g,e)`, `target(g,e)` are valid |
| `basic_edge<G, E>` | Graph `G`, edge `E` | Shared edge floor (in `namespace graph`): `source_id(g,e)` and `target_id(g,e)` are valid. Also satisfied by edge-list elements. |
| `edge<G, E>` | Graph `G`, edge `E` | Refines `basic_edge<G,E>` and additionally requires the `source(g,e)` and `target(g,e)` vertex descriptors. Always satisfied by adjacency-list edge descriptors. |

### Edge Range Concepts

Expand Down
38 changes: 32 additions & 6 deletions docs/reference/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Header: `<graph/adj_list/adjacency_list_concepts.hpp>`

```
Primitives
edge<G, E> · vertex<G, V> · hashable_vertex_id<G>
basic_edge<G, E> · edge<G, E> · vertex<G, V> · hashable_vertex_id<G>

Ranges (parameterised on a range type R)
out_edge_range<R, G> requires edge<G, E>
Expand All @@ -49,17 +49,43 @@ Compound concepts

![Adjacency List Concept Hierarchy](../assets/adjacency_list_concepts.svg)

### `edge<G, E>`
### `basic_edge<G, E>`

An edge exposes at least a target vertex ID.
The shared edge floor used by **both** adjacency lists and edge lists. An edge
exposes a source and a target vertex ID. It lives in `namespace graph` (alongside
the shared `source_id` / `target_id` CPOs) and is the concept required by
`edge_list::basic_sourced_edgelist`.

```cpp
template <class G, class E = void>
concept edge = requires(G& g, E& e) {
{ target_id(g, e) } -> std::convertible_to<vertex_id_t<G>>;
template <class G, class E>
concept basic_edge = requires(G& g, const E& e) {
source_id(g, e);
target_id(g, e);
};
```

> Return types are intentionally left unconstrained so the compiler reports the
> actual mismatch at the point of use (same rationale as `std::ranges::sized_range`).

### `edge<G, E>`

The adjacency-list refinement of `basic_edge`. In addition to the source/target
IDs it requires the source and target vertex **descriptors** via the `source` and
`target` CPOs. Within an `adjacency_list` these are always available — the default
`source`/`target` resolve to `*find_vertex(g, source_id/target_id(g, e))` — so the
requirement is free for conforming graphs while keeping bare edge-list elements
(which have no vertex container) out of this concept.

```cpp
template <class G, class E>
concept edge =
basic_edge<G, E> &&
requires(G& g, const E& e) {
source(g, e);
target(g, e);
};
```

### `out_edge_range<R, G>`

A forward range of outgoing edges adjacent to a vertex.
Expand Down
12 changes: 9 additions & 3 deletions docs/reference/cpo-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ All CPOs listed below are available in `namespace graph` after
| `contains_out_edge` | `(g, uid, vid)` | `bool` | O(deg) | Yes |
| `contains_in_edge` | `(g, uid, vid)` | `bool` | O(deg) | Yes |
| `has_edges` | `(g)` | `bool` | O(V) | Yes |
| `vertex_value` | `(g, u)` | `decltype(auto)` | O(1) | No |
| `vertex_value` | `(g, u)` / `(g, uid)` | `decltype(auto)` | O(1) | No |
| `edge_value` | `(g, uv)` | `decltype(auto)` | O(1) | Yes |
| `graph_value` | `(g)` | `decltype(auto)` | O(1) | No |
| `partition_id` | `(g, u)` | `partition_id_t<G>` | O(1) | No |
Expand Down Expand Up @@ -379,16 +379,22 @@ decay or copy.
### `vertex_value(g, u)`

```cpp
auto vertex_value(G& g, vertex_t<G>& u) -> /* decltype(auto) */;
auto vertex_value(G& g, vertex_t<G>& u) -> /* decltype(auto) */; // by descriptor
auto vertex_value(G& g, vertex_id_t<G> uid) -> /* decltype(auto) */; // by id (convenience)
```

Returns the user-defined value associated with vertex `u`. **No default** —
the graph must provide this via member or ADL.

The `(g, uid)` overload is a convenience form that mirrors the descriptor
dispatch: a member `g.vertex_value(uid)` or ADL `vertex_value(g, uid)` taking the
id directly is preferred, and only when neither exists does it fall back to
`vertex_value(g, *find_vertex(g, uid))`.

| Property | Value |
|----------|-------|
| **Return type** | `decltype(auto)` — preserves by-value, by-ref, by-const-ref, and by-rvalue-ref |
| **Complexity** | O(1) |
| **Complexity** | O(1) for the descriptor form; the `uid` fallback adds `find_vertex` cost (O(1) index, O(log n)/O(1) avg mapped) |

### `edge_value(g, uv)`

Expand Down
15 changes: 11 additions & 4 deletions docs/reference/edge-list-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,23 @@ namespace graph::edge_list { ... }

| Concept | Parameters | Description |
|---------|------------|-------------|
| `basic_sourced_edgelist<EL>` | Edge list `EL` | `input_range` of non-nested elements; `source_id(el, uv)` and `target_id(el, uv)` are valid. Vertex ID type can be any type. |
| `basic_sourced_edgelist<EL>` | Edge list `EL` | `input_range` of non-nested elements whose element satisfies the shared `basic_edge` concept (`source_id(el, uv)` and `target_id(el, uv)` are valid). Vertex ID type can be any type. |
| `basic_sourced_index_edgelist<EL>` | Edge list `EL` | Refines `basic_sourced_edgelist`; `source_id` and `target_id` return `std::integral` types. |
| `has_edge_value<EL>` | Edge list `EL` | Refines `basic_sourced_edgelist`; `edge_value(el, uv)` is valid. |

> **Shared edge floor:** `basic_sourced_edgelist` rests on the same
> `graph::basic_edge<EL, range_value_t<EL>>` concept that the adjacency-list
> `edge` concept refines, tying both abstract data types together at the edge
> level. The adjacency-list `edge` additionally requires the `source`/`target`
> vertex descriptors, which edge-list elements do not provide.

### Concept Hierarchy

```
basic_sourced_edgelist<EL>
├── basic_sourced_index_edgelist<EL> (+ integral vertex IDs)
└── has_edge_value<EL> (+ edge_value(el, uv))
basic_edge<EL, E> (shared with adj_list: source_id · target_id)
└── basic_sourced_edgelist<EL>
├── basic_sourced_index_edgelist<EL> (+ integral vertex IDs)
└── has_edge_value<EL> (+ edge_value(el, uv))
```

---
Expand Down
Loading
Loading