-
Notifications
You must be signed in to change notification settings - Fork 27
docs: initial documentation #158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
64efcc7
794d998
8d82a37
ed24215
5509c7e
118b9a2
b850d0d
9e19945
898c5c5
4a270f9
76e7f43
fe5e510
461d132
a4d03b7
b41752b
8e56bc4
2f2f9e1
ec26ff0
a1aed63
76a7ddc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| # Algorithms | ||
|
|
||
| ## Definitions | ||
|
|
||
| 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: | ||
| * _e_<sub>_i_</sub> ≠ _e_<sub>_j_</sub> for _i_ ≠ _j_, | ||
| * target(_e_<sub>_i_</sub>) = source(_e_<sub>_i_+1</sub>), | ||
| * source(_e_<sub>_i_</sub>) != source(_e_<sub>_j_</sub>) for _i_ ≠ _j_. | ||
|
|
||
| <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>). | ||
|
|
||
| <code><i>distance(p)</i></code> is a sum over _i_ of <code>weight</code>(_e_<sub>_i_</sub>). | ||
|
|
||
| <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` | ||
| and <code><i>path-target</i>(<i>p</i>)</code> = v that has the smallest value of <code><i>distance(p)</i></code>. | ||
|
|
||
| <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. | ||
|
|
||
| <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`: | ||
| * if there exists an edge _e_ with target(_e_) = v, then it is source(_e_), | ||
| * otherwise it is `v`. | ||
|
|
||
| ## Visitors | ||
|
|
||
| TODO: explain the _GraphVisitor_ requirements | ||
|
|
||
| TODO: list and describe all possible visitation events | ||
|
|
||
|
|
||
| ## `dijkstra_shortest_paths` (single source) | ||
|
|
||
| Header `<graph/algorithm/dijkstra_shortest_paths.hpp>` | ||
|
|
||
| ```c++ | ||
| template <class G, | ||
| class Distances, | ||
| class Predecessors, | ||
| class WF = function<range_value_t<Distances>(edge_reference_t<G>)>, | ||
| class Visitor = empty_visitor, | ||
| class Compare = less<range_value_t<Distances>>, | ||
| class Combine = plus<range_value_t<Distances>>> | ||
| constexpr void dijkstra_shortest_distances( | ||
| G&& g, | ||
| vertex_id_t<G> source, | ||
| Distances& distances, | ||
| Predecessors& predecessor, | ||
| WF&& weight = [](edge_reference_t<G> uv) { return range_value_t<Distances>(1); }, // default weight(uv) -> 1 | ||
| Visitor&& visitor = empty_visitor(), | ||
| Compare&& compare = less<range_value_t<Distances>>(), | ||
| Combine&& combine = plus<range_value_t<Distances>>()); | ||
| ``` | ||
| *Constraints:* | ||
| * `index_adjacency_list<G>` is `true`, | ||
| * `std::ranges::random_access_range<Distances>` is `true`, | ||
| * `std::ranges::sized_range<Distances>` is `true`, | ||
| * `std::ranges::random_access_range<Predecessors>` is `true`, | ||
| * `std::ranges::sized_range<Predecessors>` is `true`, | ||
| * `std::is_arithmetic_v<std::ranges::range_value_t<Distances>>` is `true`, | ||
| * `std::convertible_to<vertex_id_t<G>, std::ranges::range_value_t<Predecessors>>` is `true`, | ||
| * `basic_edge_weight_function<G, WF, std::ranges::range_value_t<Distances>, Compare, Combine>` is `true`. | ||
|
|
||
akrzemi1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| *Preconditions:* | ||
| * <code>distances[<i>i</i>] == shortest_path_infinite_distance<range_value_t<Distances>>()</code> for each <code><i>i</i></code> in range [`0`; `num_vertices(g)`), | ||
| * <code>predecessor[<i>i</i>] == <i>i</i></code> for each <code><i>i</i></code> in range [`0`; `num_vertices(g)`), | ||
| * `weight` returns non-negative values. | ||
| * `visitor` adheres to the _GraphVisitor_ requirements. | ||
|
|
||
| *Hardened preconditions:* | ||
| * `0 <= source && source < num_vertices(g)` is `true`, | ||
| * `std::size(distances) >= num_vertices(g)` is `true`, | ||
| * `std::size(predecessor) >= num_vertices(g)` is `true`. | ||
|
|
||
| *Effects:* Supports the following visitation events: `on_initialize_vertex`, `on_discover_vertex`, | ||
| `on_examine_vertex`, `on_finish_vertex`, `on_examine_edge`, `on_edge_relaxed`, and `on_edge_not_relaxed`. | ||
|
|
||
| *Postconditions:* For each <code><i>i</i></code> in range [`0`; `num_vertices(g)`): | ||
| * <code>distances[<i>i</i>]</code> is <code><i>shortest-path-distance</i>(g, source, <i>i</i>)</code>, | ||
| * <code>predecessor[<i>i</i>]</code> is <code><i>shortest-path-predecessor</i>(g, source, <i>i</i>)</code>. | ||
|
|
||
| *Throws:* `std::bad_alloc` if memory for the internal data structures cannot be allocated. | ||
|
|
||
| *Complexity:* Either 𝒪((|_E_| + |_V_|)⋅log |_V_|) or 𝒪(|_E_| + |_V_|⋅log |_V_|), depending on the implementation. | ||
|
|
||
| *Remarks:* Duplicate sources do not affect the algorithm’s complexity or correctness. | ||
|
|
||
| ## TODO | ||
|
|
||
| Document all other algorithms... | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| # Customization Points | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I find it interesting that you label this as Customization Points. In contrast, I've labeled it as the Graph Container Interface (GCI) because I want the user to focus on the fact that all the free functions are used to describe the interface for a Graph Container. I use "Container" to associate & distinguish it from the standard STL Containers. It is a range-of-ranges container with unique properties. To me, Customization Points are the means to achieve the functionality I want to be able to support, namely the ability to define and override the functions for a specific graph data structure. Toward that end, I will describe the functions in the GCI and state that they are Customization Points to help those in the know what's going on, but after that I can just tell people to define the function for their graph data structure and it all just works. If I try to go beyond that then I risk confusing the uninitiated to CPOs. Beyond that, I also say there are reasonable defaults for all the GCI functions and give examples like vector<vector> and vector<vector<pair<int, double>>> that works by default. There are likely gaps in what I've done. It hasn't been scrutinized very much.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. I will try to separate the docs clearly into the narrative part and the reference section, and use the term Graph Container Interface in the former.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I expanded the docs structure. This should address most of your remarks. |
||
| The algorithms and views in this library operate on graph representations via _Customization Point Objects_ (CPO). | ||
| A user-defined graph representation `G` is adapted for use with this library by making sure that the necessary CPOs are _valid_ for `G`. | ||
|
|
||
| Each customization point specifies individually what it takes to make it valid. | ||
| A customization point can be made valid in a number of ways. | ||
| For each customization point we provide an ordered list of ways in which it can be made valid. The order in this list matters: the match for validity is performed in order and if a given customization is determined to be valid, the subsequent ways, even if they would be valid, are ignored. | ||
|
|
||
| Often, the last item from the list serves the purpose of a "fallback" or "default" customization. | ||
|
|
||
| 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. | ||
| The property or being valid or invalid can be statically tested in the program via SFINAE (like `enable_if`) tricks or `requires`-expressions. | ||
|
|
||
| All the customization points in this library are defined in namespace `::graph` and brought into the program code via including header `<graph/graph.hpp>`. | ||
|
|
||
| ## The list of customization points | ||
|
|
||
| We use the following notation to represent the customization points: | ||
|
|
||
|
|
||
| | Symbol | Type | Meaning | | ||
| |--------|--------------------------------|------------------------------------------| | ||
| | `G` | | the type of the graph representation | | ||
| | `g` | `G` | the graph representation | | ||
| | `u` | `graph::vertex_reference_t<G>` | vertex in `g` | | ||
| | `ui` | `graph::vertex_iterator_t<G>` | iterator to a vertex in `g` | | ||
| | `uid` | `graph::vertex_id_t<G>` | _id_ of a vertex in `g` (often an index) | | ||
| | `uv` | `graph::edge_reference_t<G>` | an edge in `g` | | ||
|
|
||
|
|
||
| ### `vertices` | ||
|
|
||
| The CPO `vertices(g)` is used to obtain the list of all vertices, in form of a `std::ranges::random_access_range`, from the graph-representing object `g`. | ||
| We also use its return type to determine the type of the vertex: `vertex_t<G>`. | ||
|
|
||
akrzemi1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| #### Customization | ||
|
|
||
| 1. Returns `g.vertices()`, if such member function exists and returns a `std::move_constructible` type. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. move_constructible implies that ownership of the internal vertices would be moved to the caller, which is not what we want. It should be returning a random_access_range (or future, bidirectional_range) either as a reference or to something like a subrange that is returned as a value (not a reference).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. struct X { X(X&&) = delete;};
static_assert(std::move_constructible<X&>);Returning a reference satisfies the |
||
| 2. Returns `vertices(g)`, if such function is ADL-discoverable and returns a `std::move_constructible` type. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as previous. |
||
| 3. Returns `g`, if it is a `std::ranges::random_access_range`. | ||
|
|
||
|
|
||
| ### `vertex_id` | ||
|
|
||
| The CPO `vertex_id(g, ui)` is used obtain the _id_ of the vertex, given the iterator. | ||
| We also use its return type to determine the type of the vertex id: `vertex_id_t<G>`. | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is fine for now. With the addition of descriptors, this is the only function signature that changes. Once that's available, ui becomes u (descriptor). |
||
| #### Coustomization | ||
|
|
||
| 1. Returns `ui->vertex_id(g)`, if this expression is valid and its type is `std::move_constructible`. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if this will remain valid when descriptors are used. It means that vertex_id(g) would need to be defined on the descriptor, which may be possible. |
||
| 2. Returns `vertex_id(g, ui)`, if this expression is valid and its type is `std::move_constructible`. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the return type is simple like an integral id then this is fine. If we want to extend the id type to be more than integral, like a user-defined type or a string, then we won't want to require move_constructible.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that my goal is to document what is in the current library (modulo bugs), rather than a vision for the future. After a bit of investigation, I conclude that the current Graph Container Interface requires the IDs to be copy_constructible! Suppose that I have my own graph representation that needs to use non-copyable, non-movable IDs. How am I supposed to customize ID const& vertex_id(Graph const& g, Graph::const_iterator it) {
return it->first;
}Shall I return by value or by reference to const? If by value, then I need to copy. If by reference, then the CPO
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filed #174. |
||
| 3. Returns <code>static_cast<<em>vertex-id-t</em><G>>(ui - begin(vertices(g)))</code>, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. vertex_id_(g,u) is used to define vertex_id_t, so there may be a circular definition here (I hope that's not because of the current definition in graph-v2). I think this needs to be flushed out a little more to define the vertex type based on vertex_t and whether it is a range or not (for #1 & #2 below). |
||
| if `std::ranges::random_access_range<vertex_range_t<G>>` is `true`, where <code><em>vertex-id-t</em></code> is defined as: | ||
|
|
||
| * `I`, when the type of `G` matches pattern `ranges::forward_list<ranges::forward_list<I>>` and `I` is `std::integral`, | ||
| * `I0`, when the type of `G` matches pattern <code>ranges::forward_list<ranges::forward_list<<em>tuple-like</em><I0, ...>>></code> and `I0` is `std::integral`, | ||
| * `std::size_t` otherwise. | ||
|
|
||
| ### `find_vertex` | ||
|
|
||
| TODO `find_vertex(g, uid)` | ||
|
|
||
| ### `edges(g, u)` | ||
|
|
||
| ### `edges(g, uid)` | ||
|
|
||
| ### `num_edges(g)` | ||
|
|
||
| ### `target_id(g, uv)` | ||
|
|
||
| ### `target_id(e)` | ||
|
|
||
| ### `source_id(g, uv)` | ||
|
|
||
| ### `source_id(e)` | ||
|
|
||
| ### `target(g, uv)` | ||
|
|
||
| ### `source(g, uv)` | ||
|
|
||
| ### `find_vertex_edge(g, u, vid)` | ||
|
|
||
| ### `find_vertex_edge(g, uid, vid)` | ||
|
|
||
| ### `contains_edge(g, uid, vid)` | ||
|
|
||
| ### `partition_id(g, u)` | ||
|
|
||
| ### `partition_id(g, uid)` | ||
|
|
||
| ### `num_vertices(g, pid)` | ||
|
|
||
| ### `num_vertices(g)` | ||
|
|
||
| ### `degree(g, u)` | ||
|
|
||
| ### `degree(g, uid)` | ||
|
|
||
| ### `vertex_value(g, u)` | ||
|
|
||
| ### `edge_value(g, uv)` | ||
|
|
||
| ### `edge_value(e)` | ||
|
|
||
| ### `graph_value(g)` | ||
|
|
||
| ### `num_partitions(g)` | ||
|
|
||
| ### `has_edge(g)` | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,192 @@ | ||
| # Overview | ||
|
|
||
| What this library has to offer: | ||
|
|
||
| 1. A number of ready-to-use algoritms, like Dijkstra's shortest paths computation. | ||
| 2. Make it easier for you to write your own graph algorithms: we provide a _view_ for traversing your graph in the preferred order (depth-first, breadth-first, topological), and you specify what is processed in each traversal step. | ||
| 3. Customization of your graph representation, so that it can be used with our algorithms and views. | ||
| 4. Our own container for representing a graph. | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here's my take on the intro statement. Take it for what it's worth. It offers the following features:
|
||
| ## Concepts | ||
|
|
||
| This is a _generic_ library. Graph algorithms operate on various graph representations through a well defined interface: a concept. The primary concept in this library is `index_adjacency_list`. | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. index_adjacency_list just specifies that there's an integral vertex id and vertices in a random-access container. adjacency_list is also important long-term, supporting the use of map to store vertices. I don't know if that's important to state right now or now. |
||
| ```c++ | ||
| #include <graph/graph.hpp> // (1) | ||
|
|
||
| static_assert(graph::index_adjacency_list<MyType>); // (2) (3) | ||
| ``` | ||
| Notes: | ||
| 1. Graph concepts are defined in header `<graph/graph.hpp>`. | ||
| 2. All declarations reside in namespace `::graph`. | ||
| 3. Use concept `graph::index_adjacency_list` to test if your type satisfies the syntactic requirements of an index adjacency list. | ||
|
|
||
| The graph representation that is most commonly used in this library is [_adjacency list_](https://en.wikipedia.org/wiki/Adjacency_list). | ||
| Very conceptually, we call it a random access range (corresponding to vertices) of forward ranges (corresponding to outgoing edges of a vertex). | ||
|
|
||
| This representation allows the algorithms to: | ||
|
|
||
| 1. Perform an iteration over vertices, and to count them. | ||
| 2. For each vertex, perform an iteration over its outgoing edges. | ||
| 3. For each edge to to look up its target vertex in constant time. | ||
|
|
||
| Algorithms in this library express the requirements on the adjacency list representations via concept `graph::index_adjacency_list`. | ||
| This concept expresses its syntactic requirments mostly via [_customization points_](./customization_points.md). | ||
| We use the following notation to represent the constraints: | ||
|
|
||
| | Symbol | Meaning | | ||
| |--------|---------------------------------------------------------| | ||
| | `G` | the type of the graph representation | | ||
| | `g` | lvalue or lvalue reference of type `G` | | ||
| | `u` | lvalue reference of type `graph::vertex_reference_t<G>` | | ||
| | `ui` | value of type `graph::vertex_iterator_t<G>` | | ||
| | `uid` | value of type `graph::vertex_id_t<G>` | | ||
| | `uv` | lvalue reference of type `graph::edge_reference_t<G>` | | ||
|
|
||
| ### Random access to vertices | ||
|
|
||
| Customization point `graph::vertices(g)` must be valid and its return type must satisfy type-requirements `std::ranges::sized_range` and `std::ranges::random_access_range`. | ||
|
|
||
| The algorithms will use it to access the vertices of graph represented by `g` in form of a random-access range. | ||
|
|
||
| Customization point `graph::vertex_id(g, ui)` must be valid and its return type must satisfy type-requirements `std::integral`. | ||
| The algorithms will use this function to convert the iterator pointing to a vertex to the _id_ of the vertex. | ||
|
|
||
|
|
||
| ### Forward access to target edges | ||
|
|
||
| The following customization points must be valid and their return type shall satisfy type requirements `std::ranges::forward_range`: | ||
|
|
||
| * `graph::edges(g, uid)`, | ||
| * `graph::edges(g, u)`. | ||
|
|
||
| The algorithms will use this function to iterate over out edges of the vertex represended by either `uid` or `u`. | ||
|
|
||
|
|
||
| ### Linking from target edges back to vertices | ||
|
|
||
| Customization point `graph::target_id(g, uv)` must be valid and its return type must satisfy type-requirements `std::integral`. | ||
|
|
||
| The algorithms will use this value to access a vertex in `graph::vertices(g)`. | ||
| Therefore we have a _semantic_ constraint: that the look up of the value returned from `graph::target_id(g, uv)` returns value `uid` that satisfies the condition | ||
| `0 <= uid && uid < graph::num_vertices(g)`. | ||
|
|
||
|
|
||
| ### Associated types | ||
|
|
||
| Based on the customization points the library provides a number of associated types in namespace `graph`: | ||
|
|
||
| | Associated type | Definition | | ||
| |-----------------------------|----------------------------------------------------------| | ||
| | `vertex_range_t<G>` | `decltype(graph::vertices(g))` | | ||
| | `vertex_iterator_t<G>` | `std::ranges::iterator_t<vertex_range_t<G>>` | | ||
| | `vertex_t<G>` | `std::ranges::range_value_t<vertex_range_t<G>>` | | ||
| | `vertex_reference_t<G>` | `std::ranges::range_reference_t<vertex_range_t<G>>` | | ||
| | `vertex_id_t<G>` | `decltype(graph::vertex_id(g, ui))` | | ||
| | `vertex_edge_range_t<G>` | `decltype(graph::edges(g, u))` | | ||
| | `vertex_edge_iterator_t<G>` | `std::ranges::iterator_t<vertex_edge_range_t<G>>` | | ||
| | `edge_t<G>` | `std::ranges::range_value_t<vertex_edge_range_t<G>>` | | ||
| | `edge_reference_t<G>` | `std::ranges::range_reference_t<vertex_edge_range_t<G>>` | | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are a number of missing types here. This document is a mix of P3126 Overview, P3130 Graph Container Interface and P3131 Graph Containers. It's not clear what your goal is here. I think you either need to include all the types and definitions, or consider breaking things out into separate documents. |
||
|
|
||
| ## Views | ||
|
|
||
| This library can help you write your own algorithms via _views_ that represent graph traversal patterns as C++ ranges. | ||
|
|
||
| Suppose, your task is to compute the distance, in edges, from a given vertex _u_ in your graph _g_, to all vertices in _g_. | ||
| We will represent the adjacency list as a vector of vectors: | ||
|
|
||
|
|
||
| ```c++ | ||
| std::vector<std::vector<int>> g { // | ||
| /*0*/ {1, 3}, // | ||
| /*1*/ {0, 2, 4}, // (0) ----- (1) ----- (2) | ||
| /*2*/ {1, 5}, // | | | | ||
| /*3*/ {0, 4}, // | | | | ||
| /*4*/ {1, 3}, // | | | | ||
| /*5*/ {2, 6}, // (3) ----- (4) (5) ----- (6) | ||
| /*6*/ {5} // | ||
| }; // | ||
| ``` | ||
|
|
||
| The algorithm is simple: you start by assigning value zero to the start vertex, | ||
| then go through all the graph edges in the breadth-first order and for each edge (_u_, _v_) | ||
| you will assign the value for _v_ as the value for _u_ plus 1. | ||
|
|
||
| In order to do this, you can employ a depth-first search view from this library: | ||
|
|
||
| ```c++ | ||
| #include <graph/views/breadth_first_search.hpp> | ||
|
|
||
| int main() | ||
| { | ||
| std::vector<int> distances(g.size(), 0); // fill with zeros | ||
|
|
||
| for (auto const& [uid, vid, _] : graph::views::sourced_edges_breadth_first_search(g, 0)) | ||
| distances[vid] = distances[uid] + 1; | ||
|
|
||
| assert((distances == std::vector{0, 1, 2, 1, 2, 3, 4})); | ||
| } | ||
| ``` | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should you be introducing views with BFS? graph-savvy people will understand it, but it might be a bit of a stretch for newbies. Would incidence and neighbors be a better choice? |
||
| ## Algorithms | ||
|
|
||
| This library offers also full fledged algorithms. | ||
| However, due to the nature of graph algorithms, the way they communicate the results requires some additional code. | ||
| Let's, reuse the same graph topology, but use a different adjacency-list representation that is also able to store a weight for each edge: | ||
|
|
||
| ```c++ | ||
| std::vector<std::vector<std::tuple<int, double>>> g { | ||
| /*0*/ {{(1), 9.1}, {(3), 1.1} }, // 9.1 2.2 | ||
| /*1*/ {{(0), 9.1}, {(2), 2.2}, {(4), 3.5}}, // (0)-------(1)-------(2) | ||
| /*2*/ {{(1), 2.2}, {(5), 1.0} }, // | | | | ||
| /*3*/ {{(0), 1.1}, {(4), 2.0} }, // |1.1 |3.5 |1.0 | ||
| /*4*/ {{(1), 3.5}, {(3), 2.0} }, // | 2.0 | | 0.5 | ||
| /*5*/ {{(2), 1.0}, {(6), 0.5} }, // (3)-------(4) (5)-------(6) | ||
| /*6*/ {{(5), 0.5}} // | ||
| }; | ||
| ``` | ||
|
|
||
| Now, let's use Dijkstra's Shortest Paths algorithm to determine the distance from vertex `0` to each vertex in `g`, and also to determine these paths. The pathst are not obtained directly, but instead the list of predecessors is returned for each vertex: | ||
|
|
||
| ```c++ | ||
| auto weight = [](std::tuple<int, double> const& uv) { | ||
| return std::get<1>(uv); | ||
| }; | ||
|
|
||
| std::vector<int> predecessors(g.size()); // we will store the predecessor of each vertex here | ||
|
|
||
| std::vector<double> distances(g.size()); // we will store the distance to each vertex here | ||
| graph::init_shortest_paths(distances); // fill with `infinity` | ||
|
|
||
akrzemi1 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| graph::dijkstra_shortest_paths(g, 0, distances, predecessors, weight); // from vertex 0 | ||
|
|
||
| assert((distances == std::vector{0.0, 6.6, 8.8, 1.1, 3.1, 9.8, 10.3})); | ||
| assert((predecessors == std::vector{0, 4, 1, 0, 3, 2, 5})); | ||
| ``` | ||
|
|
||
| If you need to know the sequence of vertices in the path from, say, `0` to `5`, you have to compute it yourself: | ||
|
|
||
| ```c++ | ||
| auto path = [&predecessors](int from, int to) | ||
| { | ||
| std::vector<int> result; | ||
| for (; to != from; to = predecessors[to]) { | ||
| assert(to < predecessors.size()); | ||
| result.push_back(to); | ||
| } | ||
| std::reverse(result.begin(), result.end()); | ||
| return result; | ||
| }; | ||
|
|
||
akrzemi1 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| assert((path(0, 5) == std::vector{3, 4, 1, 2, 5})); | ||
| ``` | ||
| The algorithms in this library are described in section [Algorithms](./algorithms.md). | ||
|
|
||
|
|
||
| ------ | ||
|
|
||
| TODO: | ||
|
|
||
| - Adapting | ||
| - use our container | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some points that might useful to people.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Applied #1 and #3. Not sure about #2. First, strictly speaking this cannot be true. The Boost.Graph names are without the
on_prefix. Second, I wouldn't like to give an impression that one needs to know Boost.Graph to understand this library.