Skip to content

Commit e95bc5e

Browse files
authored
Merge pull request #158 from akrzemi1/initial_docs
docs: initial documentation
2 parents 5d69193 + 76a7ddc commit e95bc5e

File tree

10 files changed

+1092
-0
lines changed

10 files changed

+1092
-0
lines changed

doc/Identifiers.docx

-17.1 KB
Binary file not shown.

doc/README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Introduction
2+
3+
This is a C++20 graph library with the following capabilities:
4+
5+
1. Generic algorithms operating on graph representations.
6+
2. Generic views that allow the traversal of your graphs, or parts thereof, in a sequential manner.
7+
3. Customization of your graph representation, so that it can be used with our algorithms and views.
8+
4. A number of graph containers.
9+
10+
11+
## Example
12+
13+
The following example shows how one can compute for each vertex of a given graph `g` its distance,
14+
measured in the number of edges, from the indicated vertex with index 0.
15+
1. We need an [adjacency list](https://en.wikipedia.org/wiki/Adjacency_list) representation of a graph.
16+
2. Vertices are identified by indices of a vector.
17+
18+
```c++
19+
#include <cassert>
20+
#include <vector>
21+
#include <graph/views/breadth_first_search.hpp>
22+
23+
// `vector<vector<int>>` is recognized as an adjacency list by this library
24+
std::vector<std::vector<int>> g { //
25+
/*0*/ {1, 3}, //
26+
/*1*/ {0, 2, 4}, // (0) ----- (1) ----- (2)
27+
/*2*/ {1, 5}, // | | |
28+
/*3*/ {0, 4}, // | | |
29+
/*4*/ {1, 3}, // | | |
30+
/*5*/ {2, 6}, // (3) ----- (4) (5) ----- (6)
31+
/*6*/ {5} //
32+
}; //
33+
34+
int main()
35+
{
36+
std::vector<int> distances(g.size(), 0); // fill with zeros
37+
38+
// a view of edges as they appear in the breadth-first order, from vertex 0.
39+
auto bfs_view = graph::views::sourced_edges_breadth_first_search(g, 0);
40+
41+
for (auto const& [uid, vid, _] : bfs_view) // a directed edge (u, v)
42+
distances[vid] = distances[uid] + 1;
43+
44+
assert((distances == std::vector{0, 1, 2, 1, 2, 3, 4}));
45+
}
46+
```
47+
48+
## Design
49+
50+
Algorithms and views in this library operate on graph representations via the [*Graph Container Interface*](./tutorial/graph_container_interface.md),
51+
which is a set of *customization point objects* (CPO). In order to plug your graph container into this library you need to make sure that all the
52+
necessary CPOs have been customized for your container.
53+
54+
The generic algorithms and views in this library are constrained with concepts, which are expressed in terms of the above CPOs.
55+
56+
This library comes with two graph containers, encoding different engineering trade-offs. Also, some sufficiently simple nested ranges are automatically considered
57+
compliant with the Graph Container Interface, such as:
58+
59+
* `vector<vector<int>>`,
60+
* `vector<vector<tuple<int, ...>>>`.
61+
62+
The algorithms in this library do not mutate the graphs. There is not support for graph rewriting.
63+
64+
# Next steps
65+
66+
For a more detailed overview of the library, see section [tutorial](./tutorial). <br>
67+
For a reference documentaiton, see section [reference](./reference).

doc/reference/algorithms.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Algorithms
2+
3+
## Definitions
4+
5+
A graph path _p_ is a possibly empty sequence of graph edges (_e_<sub>0</sub>, _e_<sub>1</sub>, ..., _e_<sub>_N_</sub>) where:
6+
* _e_<sub>_i_</sub> ≠ _e_<sub>_j_</sub> for _i__j_,
7+
* target(_e_<sub>_i_</sub>) = source(_e_<sub>_i_+1</sub>),
8+
* source(_e_<sub>_i_</sub>) != source(_e_<sub>_j_</sub>) for _i__j_.
9+
10+
<code><i>path-source</i>(<i>p</i>)</code> = source(_e_<sub>0</sub>). <code><i>path-target</i>(<i>p</i>)</code> = target(_e_<sub>_N_</sub>).
11+
12+
<code><i>distance(p)</i></code> is a sum over _i_ of <code>weight</code>(_e_<sub>_i_</sub>).
13+
14+
<code><i>shortest-path</i>(g, u, v)</code> is a path in the set of all paths `p` in graph `g` with <code><i>path-source</i>(<i>p</i>)</code> = `u`
15+
and <code><i>path-target</i>(<i>p</i>)</code> = v that has the smallest value of <code><i>distance(p)</i></code>.
16+
17+
<code><i>shortest-path-distance</i>(g, u, v)</code> is <code><i>distance</i>(<i>shortest-path</i>(g, u, v))</code> if it exists and _infinite-distance_ otherwise.
18+
19+
<code><i>shortest-path-predecessor</i>(g, u, v)</code>, in the set of all shortest paths <code><i>shortest-path</i>(g, u, v)</code> for any `v`:
20+
* if there exists an edge _e_ with target(_e_) = v, then it is source(_e_),
21+
* otherwise it is `v`.
22+
23+
24+
25+
## `dijkstra_shortest_paths`
26+
27+
The shortest paths algorithm builds on the idea that each edge in a graph has its associated _weight_.
28+
A path _distance_ is determined by the composition of weights of edges that constitute the path.
29+
30+
By default the composition of the edge weights is summation and the default weight is 1,
31+
so the path distance is the number of edges that it comprises.
32+
33+
Dijkstra's shortest paths algorithm also makes an assumption that appending an edge to a path _increases_
34+
the path's distance. In terms of the default composition and weight this assumption is expressed as `weight(uv) >= 0`.
35+
36+
The distances of each path are returned directly vie the output function argument.
37+
The paths themselves, if requested, are only returned indirectly by providing for each vertex
38+
its predecessor in any shortest path.
39+
40+
41+
### The single source version
42+
43+
Header `<graph/algorithm/dijkstra_shortest_paths.hpp>`
44+
45+
```c++
46+
template <index_adjacency_list G,
47+
std::ranges::random_access_range Distances,
48+
std::ranges::random_access_range Predecessors,
49+
class WF = function<std::ranges::range_value_t<Distances>(edge_reference_t<G>)>,
50+
class Visitor = empty_visitor,
51+
class Compare = less<std::ranges::range_value_t<Distances>>,
52+
class Combine = plus<std::ranges::range_value_t<Distances>>>
53+
requires std::is_arithmetic_v<std::ranges::range_value_t<Distances>> &&
54+
std::ranges::sized_range<Distances> &&
55+
std::ranges::sized_range<Predecessors> &&
56+
convertible_to<vertex_id_t<G>, std::ranges::range_value_t<Predecessors>> &&
57+
basic_edge_weight_function<G, WF, std::ranges::range_value_t<Distances>, Compare, Combine>
58+
constexpr void dijkstra_shortest_distances(
59+
G&& g,
60+
vertex_id_t<G> source,
61+
Distances& distances,
62+
Predecessors& predecessor,
63+
WF&& weight = [](edge_reference_t<G> uv) { return std::ranges::range_value_t<Distances>(1); },
64+
Visitor&& visitor = empty_visitor(),
65+
Compare&& compare = less<std::ranges::range_value_t<Distances>>(),
66+
Combine&& combine = plus<std::ranges::range_value_t<Distances>>());
67+
```
68+
69+
*Preconditions:*
70+
* <code>distances[<i>i</i>] == shortest_path_infinite_distance&lt;range_value_t&lt;Distances&gt;&gt;()</code> for each <code><i>i</i></code> in range [`0`; `num_vertices(g)`),
71+
* <code>predecessor[<i>i</i>] == <i>i</i></code> for each <code><i>i</i></code> in range [`0`; `num_vertices(g)`),
72+
* `weight` returns non-negative values.
73+
* `visitor` adheres to the _GraphVisitor_ requirements.
74+
75+
*Hardened preconditions:*
76+
* `0 <= source && source < num_vertices(g)` is `true`,
77+
* `std::size(distances) >= num_vertices(g)` is `true`,
78+
* `std::size(predecessor) >= num_vertices(g)` is `true`.
79+
80+
*Effects:* Supports the following [visitation](./visitors.md) events: `on_initialize_vertex`, `on_discover_vertex`,
81+
`on_examine_vertex`, `on_finish_vertex`, `on_examine_edge`, `on_edge_relaxed`, and `on_edge_not_relaxed`.
82+
83+
*Postconditions:* For each <code><i>i</i></code> in range [`0`; `num_vertices(g)`):
84+
* <code>distances[<i>i</i>]</code> is <code><i>shortest-path-distance</i>(g, source, <i>i</i>)</code>,
85+
* <code>predecessor[<i>i</i>]</code> is <code><i>shortest-path-predecessor</i>(g, source, <i>i</i>)</code>.
86+
87+
*Throws:* `std::bad_alloc` if memory for the internal data structures cannot be allocated.
88+
89+
*Complexity:* Either 𝒪((|_E_| + |_V_|)⋅log |_V_|) or 𝒪(|_E_| + |_V_|⋅log |_V_|), depending on the implementation.
90+
91+
*Remarks:* Duplicate sources do not affect the algorithm’s complexity or correctness.
92+
93+
94+
## TODO
95+
96+
Document all other algorithms...
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# Customization Points
2+
3+
The algorithms and views in this library operate on graph representations via _Customization Point Objects_ (CPO).
4+
A user-defined graph representation `G` is adapted for use with this library by making sure that the necessary CPOs are _valid_ for `G`.
5+
6+
A CPO is a function object, so it can be passed as an argument to functions.
7+
8+
Each customization point specifies individually what it takes to make it valid.
9+
A customization point can be made valid in a number of ways.
10+
For each customization point we provide an ordered list of ways in which it can be made valid.
11+
The order in this list matters: the match for validity is performed in order,
12+
and if a given customization is determined to be valid, the subsequent ways, even if they would be valid, are ignored.
13+
14+
Often, the last item from the list serves the purpose of a "fallback" or "default" customization.
15+
16+
If none of the customization ways is valid for a given type, or set of types, the customization point is considered _invalid_ for this set of types.
17+
The property or being valid or invalid can be statically tested in the program via SFINAE (like `enable_if`) tricks or `requires`-expressions.
18+
19+
All the customization points in this library are defined in namespace `::graph` and brought into the program code via including header `<graph/graph.hpp>`.
20+
21+
22+
## The list of customization points
23+
24+
We use the following notation to represent the customization points:
25+
26+
27+
| Symbol | Type | Meaning |
28+
|--------|--------------------------------|------------------------------------------|
29+
| `G` | | the type of the graph representation |
30+
| `g` | `G` | the graph representation |
31+
| `u` | `graph::vertex_reference_t<G>` | vertex in `g` |
32+
| `ui` | `graph::vertex_iterator_t<G>` | iterator to a vertex in `g` |
33+
| `uid` | `graph::vertex_id_t<G>` | _id_ of a vertex in `g` (often an index) |
34+
| `uv` | `graph::edge_reference_t<G>` | an edge in `g` |
35+
36+
37+
### `vertices`
38+
39+
The CPO `vertices(g)` is used to obtain the range of all vertices, in form of a `std::ranges::random_access_range`, from the graph-representing object `g`.
40+
We also use its return type to determine the type of the vertex: `vertex_t<G>`.
41+
42+
#### Customization
43+
44+
1. Returns `g.vertices()`, if such member function exists and returns a `std::move_constructible` type.
45+
2. Returns `vertices(g)`, if such function is ADL-discoverable and returns a `std::move_constructible` type.
46+
3. Returns `g`, if it is a `std::ranges::random_access_range`.
47+
48+
49+
### `vertex_id`
50+
51+
The CPO `vertex_id(g, ui)` is used obtain the _id_ of the vertex, given the iterator. <br>
52+
We also use its return type to determine the type of the vertex id: `vertex_id_t<G>`.
53+
54+
#### Customization
55+
56+
1. Returns `ui->vertex_id(g)`, if this expression is valid and its type is `std::move_constructible`.
57+
2. Returns `vertex_id(g, ui)`, if this expression is valid and its type is `std::move_constructible`.
58+
3. Returns <code>static_cast&lt;<em>vertex-id-t</em>&lt;G&gt;&gt;(ui - begin(vertices(g)))</code>,
59+
if `std::ranges::random_access_range<vertex_range_t<G>>` is `true`, where <code><em>vertex-id-t</em></code> is defined as:
60+
61+
* `I`, when the type of `G` matches pattern `ranges::forward_list<ranges::forward_list<I>>` and `I` is `std::integral`,
62+
* `I0`, when the type of `G` matches pattern <code>ranges::forward_list&lt;ranges::forward_list&lt;<em>tuple-like</em>&lt;I0, ...&gt;&gt;&gt;</code> and `I0` is `std::integral`,
63+
* `std::size_t` otherwise.
64+
65+
66+
### `find_vertex`
67+
68+
TODO `find_vertex(g, uid)`
69+
70+
71+
### `edges(g, u)`
72+
73+
The CPO `edges(g, u)` is used to obtain the sequence of outgoing edges for a vertex
74+
denoted by reference `u`. <br>
75+
We also use its return type to determine the type of the edge type: `edge_t<G>`.
76+
77+
#### Customization
78+
79+
1. Returns `u.edges(g)`, if this expression is valid and its type is `std::move_constructible`.
80+
2. Returns `edges(g, u)`, if this expression is valid and its type is `std::move_constructible`.
81+
3. `u`, if `G` is a user-defined type and type `vertex_t<G>` is `std::ranges::forward_range;
82+
83+
84+
### `edges(g, uid)`
85+
86+
The CPO `edges(g, uid)` is used to obtain the sequence of outgoing edges for a vertex
87+
denoted by _id_ `uid`.
88+
89+
#### Customization
90+
91+
1. Returns `edges(g, uid)`, if this expression is valid and its type is `std::move_constructible`.
92+
2. Returns `*find_vertex(g, uid)`, if
93+
* `vertex_t<G>` is `std::ranges::forward_range`, and
94+
* expression `find_vertex(g, uid)` is valid and its type is `std::move_constructible`.
95+
96+
97+
### `num_edges(g)`
98+
99+
TODO
100+
101+
102+
### `target_id(g, uv)`
103+
104+
The CPO `target_id(g, uv)` is used to obtain the _id_ of the target vertex of edge `uv`.
105+
106+
#### Customization
107+
108+
1. Returns `uv.target_id(g)`, if this expression is valid and its type is `std::move_constructible`.
109+
2. Returns `target_id(g, uv)`, if this expression is valid and its type is `std::move_constructible`.
110+
3. Returns `uv`, if
111+
* `G` is `std::ranges::forward_range`, and
112+
* `std::ranges::range_value_t<G>` is `std::ranges::forward_range`, and
113+
* `std::ranges::range_value_t<std::ranges::range_value_t<G>>` is `std::integral`.
114+
4. Returns `get<0>(uv)`, if
115+
* `G` is `std::ranges::forward_range`, and
116+
* `std::ranges::range_value_t<G>` is `std::ranges::forward_range`, and
117+
* `std::ranges::range_value_t<std::ranges::range_value_t<G>>` is <code><em>tuple-like</em></code>, and
118+
* `std::tuple_element_t<0, std::ranges::range_value_t<std::ranges::range_value_t<G>>>` is `std::integral`.
119+
120+
121+
### `target_id(e)`
122+
123+
### `source_id(g, uv)`
124+
125+
### `source_id(e)`
126+
127+
128+
### `target(g, uv)`
129+
130+
CPO `target(g, uv)` is used to access the target vertex of a given edge `uv`.
131+
132+
#### Customization
133+
134+
1. Returns `target(g, uv)`, if this expression is valid and its type is `std::move_constructible`.
135+
2. Returns `*find_vertex(g, target_id(g, uv))`, if
136+
137+
* `vertex_range_t<G>` is a `std::ranges::random_access_range`, and
138+
* `find_vertex(g, uid)` is valid and its type is `std::move_constructible`, and
139+
* `target_id(g, uv)` is valid and its type is `std::integral`.
140+
141+
142+
### `source(g, uv)`
143+
144+
### `find_vertex_edge(g, u, vid)`
145+
146+
### `find_vertex_edge(g, uid, vid)`
147+
148+
### `contains_edge(g, uid, vid)`
149+
150+
### `partition_id(g, u)`
151+
152+
### `partition_id(g, uid)`
153+
154+
### `num_vertices(g, pid)`
155+
156+
### `num_vertices(g)`
157+
158+
### `degree(g, u)`
159+
160+
### `degree(g, uid)`
161+
162+
### `vertex_value(g, u)`
163+
164+
### `edge_value(g, uv)`
165+
166+
### `edge_value(e)`
167+
168+
### `graph_value(g)`
169+
170+
### `num_partitions(g)`
171+
172+
### `has_edge(g)`
173+

doc/reference/utilities.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Utilities
2+
3+
Some components in this library, such as graph traversal views or visitors,
4+
need to represent a number of properties of a graph vertex at once, like vertex id,
5+
vertex reference, and vertex value. For this purpose class template `vertex_info` is used.
6+
7+
```c++
8+
// header <graph/graph_info.hpp>
9+
10+
namespace graph {
11+
12+
template <class VId, class V, class VV>
13+
struct vertex_info {
14+
using id_type = VId; // usually vertex_id_t<G>
15+
using vertex_type = V; // usually vertex_reference_t<G>
16+
using value_type = VV; // result of computing the value of a vertex
17+
18+
id_type id; // absent when `VId` is `void`
19+
vertex_type vertex; // absent when `V` is `void`
20+
value_type value; // absent when `VV` is `void`
21+
};
22+
23+
}
24+
```
25+
26+
This class template comes with a number of specializations which make certain data members disappear when the corresponding template parameter is `void`.
27+
28+
There is an analogous utility class for representing edge information.
29+
30+
```c++
31+
// header <graph/graph_info.hpp>
32+
33+
namespace graph {
34+
35+
template <class VId, bool Sourced, class E, class EV>
36+
struct edge_info {
37+
using source_id_type = VId; // this type is `void` when `Sourced` is `false`
38+
using target_id_type = VId; // usually vertex_id_t<G>
39+
using edge_type = E; // usually edge_reference_t<G>
40+
using value_type = EV; //
41+
42+
source_id_type source_id; // absent when `Sourced` is `false`
43+
target_id_type target_id; // absent when `VId` is `void`
44+
edge_type edge; // absent when `E` is `void`
45+
value_type value; // absent when `EV` is `void`
46+
};
47+
48+
}
49+
```

0 commit comments

Comments
 (0)