diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 0f561cc..0868bf7 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -54,6 +54,18 @@ jobs:
run: cargo check
working-directory: fuzz
+ check-doc:
+ name: Rustdoc check
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
+ - uses: Swatinem/rust-cache@v2
+ - name: Cargo doc
+ env:
+ RUSTDOCFLAGS: -D warnings
+ run: cargo doc --no-deps --document-private-items --workspace
+
test:
name: Test (${{ matrix.rust }})
runs-on: ubuntu-latest
diff --git a/DESIGN.md b/DESIGN.md
index 86659b0..e63b1b5 100644
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -343,8 +343,8 @@ Iterative traversals are preferred over recursive traversals. The main advantage
is that iterative traversals are lazy, thus allowing to stop the traversal
without "hacking" the control flow semantics into callback's return type, and
can be "detached" from the graph so that the graph can be manipulated during
-traversal (:handshake: :hammer:). Iterative traversal is also not limited by the
-size of the program stack. However, in some cases this choice imposes an
+traversal ( :handshake: :hammer: ). Iterative traversal is also not limited by
+the size of the program stack. However, in some cases this choice imposes an
efficiency penalty. See implementation of `RawDfsExtra` for the most notable
example.
diff --git a/README.md b/README.md
index 3ad12d8..85988c7 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# gryf
+
> Graph data structure library aspiring to be convenient, versatile, correct and performant.
@@ -34,10 +34,12 @@ fn main() {
graph.extend_with_edges([
(prague, bratislava, 328u32),
- (prague, nuremberg, 293),
+ (prague, nuremberg, 297),
+ (prague, vienna, 293),
(bratislava, vienna, 79),
(nuremberg, munich, 170),
(vienna, munich, 402),
+ (vienna, florence, 863),
(munich, florence, 646),
(florence, rome, 278),
]);
@@ -53,43 +55,38 @@ fn main() {
.join(" - ");
println!("{distance} km from Prague through {path}");
- // 1387 km from Prague through Nuremberg - Munich - Florence - Rome
+ // 1391 km from Prague through Nuremberg - Munich - Florence - Rome
}
```
-## Overview
+## Goals
-The main goal of gryf is to be
+The main goals of gryf are to be
-* **convenient** -- "make the common case straightforward and natural1",
-* **versatile** -- "offer simplicity as well as flexibility and strive for a
- good balance if in conflict",
-* **correct** -- "use extensive fuzzing and property-based testing to increase
- confidence about correctness", and
-* **performant** -- "write the code with performance and memory efficiency in
- mind".
+* _convenient_, that is, "making the common case straightforward and natural",
+* _versatile_, that is, "offering simplicity as well as flexibility and striving
+ for a good balance if in conflict",
+* _correct_, that is, "using extensive fuzzing and property-based testing to
+ increase confidence about correctness", and
+* _performant_, that is, "writing the code with performance and memory
+ efficiency in mind".
-The algorithms are [organized into the
-problems](#problems-instead-of-algorithms) they solve. For specifying the
-options of an algorithm the [builder pattern](#builder-pattern-for-algorithms)
-is utilized. Graphs can use different storage [under the
-hood](#separation-of-graph-storage-and-semantics).
+Failing in any of these should be considered an issue to be reported.
-1Failing in this should be considered a bug and reported.
-
-## Details
+## Design
_For more details, see the [design doc](./DESIGN.md)._
### Problems instead of algorithms
-For users without much experience or knowledge in graph theory and algorithms,
-it may not be obvious which algorithm should (or even can) be used to solve the
-given problem at hand. Instead, gryf organizes the algorithms into the problem
-they solve (e.g., `ShortestPaths`) instead of exposing the algorithms directly
-(`dijkstra`, `bellman_ford`).
+It may not be obvious which algorithm should (or even can) be used to solve the
+given problem at hand, especially for users without much experience or knowledge
+in graph theory and algorithms. Instead, gryf organizes the algorithms into the
+problem they solve (e.g., `ShortestPaths`) instead of requiring to call the
+algorithms directly (`dijkstra`, `bellman_ford`).
-This brings a number of benefits, among which the most important are:
+Organizing algorithms into problems brings a number of benefits, among which the
+most important are:
* It is convenient for the user, especially if they are a beginner. It allows
them not to care about details if they don't want to care.
@@ -107,7 +104,7 @@ let shortest_paths = ShortestPaths::on(&graph).run(rome).unwrap();
### Builder pattern for algorithms
Specifying arguments for algorithms is done using the builder pattern. This
-avoids the need of passing dummy values (like `None`) to parameters that are not
+avoids the need to pass dummy values (like `None`) to parameters that are not
useful for the use case. On the other hand, it allows tweaking the algorithm
with many optional arguments. Moreover, new optional parameters can be added in
a backward-compatible way. A lot of care is taken to make the error feedback
@@ -123,15 +120,15 @@ let shortest_paths = ShortestPaths::on(&graph)
### Separation of graph storage and semantics
-In gryf, high-level semantics provided by user-facing types are strictly
-separated from the underlying storage/representation. The graph data can be
-stored in a common representation (e.g., adjacency list or adjacency matrix),
-but it can as well be stored in or represented by a custom, problem-tailored
-implementation, as long as it implements provided interfaces.
+High-level semantics provided by user-facing types are strictly separated from
+the underlying storage/representation. The graph data can be stored in a common
+representation (e.g., adjacency list or adjacency matrix), but it can also be
+stored in or represented by a custom, problem-tailored implementation, as long
+as it implements provided interfaces.
-On top of a storage, there is an encapsulation with clear semantics. The most
-general is a generic graph, but restricted forms include simple graph (without
-parallel edges), path, bipartite graph and so on. Among the advantages of
+On top of storage, there is an encapsulation with clear semantics. The most
+general is a generic graph, but restricted forms include simple graphs (without
+parallel edges), paths, bipartite graphs and so on. Among the advantages of
restrictive encapsulations are:
* The type of graph clearly communicates the intention and structure.
@@ -155,7 +152,9 @@ let mut graph = Graph::new_undirected_in(AdjMatrix::default());
* [graphlib](https://crates.io/crates/graphlib)
* [graphific](https://crates.io/crates/graphific)
-See the differences between them and gryf in [this comparison repository](https://github.com/pnevyk/rusty-graphs).
+Check the [rusty graphs](https://github.com/pnevyk/rusty-graphs) repository for
+a detailed comparison of gryf and other graph libraries available for Rust with
+examples and commentary.
## License
diff --git a/assets/logo-gryf-text.png b/assets/logo-gryf-text.png
new file mode 100644
index 0000000..1bf7010
Binary files /dev/null and b/assets/logo-gryf-text.png differ
diff --git a/assets/logo-gryf.png b/assets/logo-gryf.png
new file mode 100644
index 0000000..0260761
Binary files /dev/null and b/assets/logo-gryf.png differ
diff --git a/assets/logo-gryf.xcf b/assets/logo-gryf.xcf
new file mode 100644
index 0000000..9a0fd67
Binary files /dev/null and b/assets/logo-gryf.xcf differ
diff --git a/gryf-derive/LICENSE b/gryf-derive/LICENSE
new file mode 120000
index 0000000..ea5b606
--- /dev/null
+++ b/gryf-derive/LICENSE
@@ -0,0 +1 @@
+../LICENSE
\ No newline at end of file
diff --git a/gryf-derive/README.md b/gryf-derive/README.md
new file mode 100644
index 0000000..71f46e0
--- /dev/null
+++ b/gryf-derive/README.md
@@ -0,0 +1,8 @@
+# gryf-derive
+
+Derive macros for [gryf](https://github.com/pnevyk/gryf).
+
+## License
+
+Dual-licensed under [MIT](LICENSE) and [UNLICENSE](UNLICENSE). Feel free to use
+it, contribute or spread the word.
diff --git a/gryf-derive/UNLICENSE b/gryf-derive/UNLICENSE
new file mode 120000
index 0000000..b948d67
--- /dev/null
+++ b/gryf-derive/UNLICENSE
@@ -0,0 +1 @@
+../UNLICENSE
\ No newline at end of file
diff --git a/gryf/LICENSE b/gryf/LICENSE
new file mode 120000
index 0000000..ea5b606
--- /dev/null
+++ b/gryf/LICENSE
@@ -0,0 +1 @@
+../LICENSE
\ No newline at end of file
diff --git a/gryf/README.md b/gryf/README.md
new file mode 120000
index 0000000..32d46ee
--- /dev/null
+++ b/gryf/README.md
@@ -0,0 +1 @@
+../README.md
\ No newline at end of file
diff --git a/gryf/UNLICENSE b/gryf/UNLICENSE
new file mode 120000
index 0000000..b948d67
--- /dev/null
+++ b/gryf/UNLICENSE
@@ -0,0 +1 @@
+../UNLICENSE
\ No newline at end of file
diff --git a/gryf/docs/assets/main_example.excalidraw.svg b/gryf/docs/assets/main_example.excalidraw.svg
new file mode 100644
index 0000000..4cdea0a
--- /dev/null
+++ b/gryf/docs/assets/main_example.excalidraw.svg
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/gryf/docs/assets/trait_hierarchy.excalidraw.svg b/gryf/docs/assets/trait_hierarchy.excalidraw.svg
new file mode 100644
index 0000000..c3d00bf
--- /dev/null
+++ b/gryf/docs/assets/trait_hierarchy.excalidraw.svg
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/gryf/docs/include/algo.on.md b/gryf/docs/include/algo.on.md
new file mode 100644
index 0000000..b4aa2d0
--- /dev/null
+++ b/gryf/docs/include/algo.on.md
@@ -0,0 +1 @@
+Initializes the algorithm to be performed on given graph.
diff --git a/gryf/docs/include/algo.run.md b/gryf/docs/include/algo.run.md
new file mode 100644
index 0000000..d44741f
--- /dev/null
+++ b/gryf/docs/include/algo.run.md
@@ -0,0 +1 @@
+Runs the algorithm.
diff --git a/gryf/docs/include/algo.using.md b/gryf/docs/include/algo.using.md
new file mode 100644
index 0000000..6ea6876
--- /dev/null
+++ b/gryf/docs/include/algo.using.md
@@ -0,0 +1 @@
+Chooses the algorithm given in the argument.
diff --git a/gryf/docs/include/algo.using_opt.md b/gryf/docs/include/algo.using_opt.md
new file mode 100644
index 0000000..994ef79
--- /dev/null
+++ b/gryf/docs/include/algo.using_opt.md
@@ -0,0 +1,2 @@
+Chooses the algorithm given in the argument or leaves it unspecified if `None`
+is passed.
diff --git a/gryf/docs/include/connect_vertices.connect_vertices.md b/gryf/docs/include/connect_vertices.connect_vertices.md
new file mode 100644
index 0000000..cf0b3c7
--- /dev/null
+++ b/gryf/docs/include/connect_vertices.connect_vertices.md
@@ -0,0 +1 @@
+Adds an edge between _(u, v)_ whenever the predicate returns `Some`.
diff --git a/gryf/docs/include/create.empty.md b/gryf/docs/include/create.empty.md
new file mode 100644
index 0000000..5d135e2
--- /dev/null
+++ b/gryf/docs/include/create.empty.md
@@ -0,0 +1 @@
+Creates a graph with zero capacities.
diff --git a/gryf/docs/include/create.with_capacity.md b/gryf/docs/include/create.with_capacity.md
new file mode 100644
index 0000000..69b1c3d
--- /dev/null
+++ b/gryf/docs/include/create.with_capacity.md
@@ -0,0 +1 @@
+Creates a graph with given vertex and edge capacities.
diff --git a/gryf/docs/include/edge_set.contains_edge.md b/gryf/docs/include/edge_set.contains_edge.md
new file mode 100644
index 0000000..71aece0
--- /dev/null
+++ b/gryf/docs/include/edge_set.contains_edge.md
@@ -0,0 +1 @@
+Returns `true` if the graph contains the given edge.
diff --git a/gryf/docs/include/edge_set.contains_edge_between.md b/gryf/docs/include/edge_set.contains_edge_between.md
new file mode 100644
index 0000000..a6df439
--- /dev/null
+++ b/gryf/docs/include/edge_set.contains_edge_between.md
@@ -0,0 +1 @@
+Returns `true` if the graph contains an edge between two vertices.
diff --git a/gryf/docs/include/edge_set.edge_bound.md b/gryf/docs/include/edge_set.edge_bound.md
new file mode 100644
index 0000000..45615a0
--- /dev/null
+++ b/gryf/docs/include/edge_set.edge_bound.md
@@ -0,0 +1,6 @@
+Returns the highest edge ID currently in use.
+
+This might be useful in algorithms that use arrays. Before using this, check
+whether [`CompactIdMap`](crate::core::id::CompactIdMap) returned by
+[`edge_id_map`](crate::core::EdgeSet::edge_id_map) doesn't satisfy your needs
+first.
diff --git a/gryf/docs/include/edge_set.edge_count.md b/gryf/docs/include/edge_set.edge_count.md
new file mode 100644
index 0000000..ac18063
--- /dev/null
+++ b/gryf/docs/include/edge_set.edge_count.md
@@ -0,0 +1 @@
+Returns the number of edges in the graph.
diff --git a/gryf/docs/include/edge_set.edge_id.md b/gryf/docs/include/edge_set.edge_id.md
new file mode 100644
index 0000000..42c9985
--- /dev/null
+++ b/gryf/docs/include/edge_set.edge_id.md
@@ -0,0 +1,3 @@
+Returns all edges between two vertices.
+
+Returns an empty iterator if either of the endpoints don't exist.
diff --git a/gryf/docs/include/edge_set.edge_id_any.md b/gryf/docs/include/edge_set.edge_id_any.md
new file mode 100644
index 0000000..4a547df
--- /dev/null
+++ b/gryf/docs/include/edge_set.edge_id_any.md
@@ -0,0 +1 @@
+Returns an edge between two vertices, if any exist.
diff --git a/gryf/docs/include/edge_set.edge_id_map.md b/gryf/docs/include/edge_set.edge_id_map.md
new file mode 100644
index 0000000..0cf91f6
--- /dev/null
+++ b/gryf/docs/include/edge_set.edge_id_map.md
@@ -0,0 +1,3 @@
+Returns a mapping of edge IDs to virtual IDs representing a contiguous sequence.
+
+This might be useful in algorithms that use arrays.
diff --git a/gryf/docs/include/edge_set.edges_by_id.md b/gryf/docs/include/edge_set.edges_by_id.md
new file mode 100644
index 0000000..3348ce4
--- /dev/null
+++ b/gryf/docs/include/edge_set.edges_by_id.md
@@ -0,0 +1 @@
+Returns all edges in the graph.
diff --git a/gryf/docs/include/edge_set.endpoints.md b/gryf/docs/include/edge_set.endpoints.md
new file mode 100644
index 0000000..70ee707
--- /dev/null
+++ b/gryf/docs/include/edge_set.endpoints.md
@@ -0,0 +1 @@
+Returns the endpoints of the given edge, if it exists.
diff --git a/gryf/docs/include/extend_with_edges.extend_with_edges.md b/gryf/docs/include/extend_with_edges.extend_with_edges.md
new file mode 100644
index 0000000..10dcde5
--- /dev/null
+++ b/gryf/docs/include/extend_with_edges.extend_with_edges.md
@@ -0,0 +1 @@
+Adds edges in given iterator to the graph.
diff --git a/gryf/docs/include/extend_with_vertices.extend_with_vertices.md b/gryf/docs/include/extend_with_vertices.extend_with_vertices.md
new file mode 100644
index 0000000..4f75175
--- /dev/null
+++ b/gryf/docs/include/extend_with_vertices.extend_with_vertices.md
@@ -0,0 +1 @@
+Adds vertices in given iterator to the graph.
diff --git a/gryf/docs/include/extend_with_vertices.from_vertices.md b/gryf/docs/include/extend_with_vertices.from_vertices.md
new file mode 100644
index 0000000..3b6feda
--- /dev/null
+++ b/gryf/docs/include/extend_with_vertices.from_vertices.md
@@ -0,0 +1 @@
+Creates a graph with vertices from given iterator.
diff --git a/gryf/docs/include/graph_add.add_edge.md b/gryf/docs/include/graph_add.add_edge.md
new file mode 100644
index 0000000..75645f8
--- /dev/null
+++ b/gryf/docs/include/graph_add.add_edge.md
@@ -0,0 +1,5 @@
+Adds a new edge between two vertices.
+
+# Panics
+
+Panics if the adding operation fails.
diff --git a/gryf/docs/include/graph_add.add_edge_connecting.md b/gryf/docs/include/graph_add.add_edge_connecting.md
new file mode 100644
index 0000000..eb7f981
--- /dev/null
+++ b/gryf/docs/include/graph_add.add_edge_connecting.md
@@ -0,0 +1,5 @@
+Adds a new edge between two vertices, adding the vertices if necessary.
+
+# Panics
+
+Panics if the adding operation fails.
diff --git a/gryf/docs/include/graph_add.add_vertex.md b/gryf/docs/include/graph_add.add_vertex.md
new file mode 100644
index 0000000..6b0b568
--- /dev/null
+++ b/gryf/docs/include/graph_add.add_vertex.md
@@ -0,0 +1,5 @@
+Adds a new vertex to the graph.
+
+# Panics
+
+Panics if the adding operation fails.
diff --git a/gryf/docs/include/graph_add.get_or_add_vertex.md b/gryf/docs/include/graph_add.get_or_add_vertex.md
new file mode 100644
index 0000000..b374234
--- /dev/null
+++ b/gryf/docs/include/graph_add.get_or_add_vertex.md
@@ -0,0 +1,5 @@
+Finds and returns a vertex by its attribute or adds it if not found.
+
+# Panics
+
+Panics if the adding operation fails.
diff --git a/gryf/docs/include/graph_add.try_add_edge.md b/gryf/docs/include/graph_add.try_add_edge.md
new file mode 100644
index 0000000..0617144
--- /dev/null
+++ b/gryf/docs/include/graph_add.try_add_edge.md
@@ -0,0 +1 @@
+Adds a new edge between two vertices, returning error in case of failure.
diff --git a/gryf/docs/include/graph_add.try_add_edge_connecting.md b/gryf/docs/include/graph_add.try_add_edge_connecting.md
new file mode 100644
index 0000000..f3635c4
--- /dev/null
+++ b/gryf/docs/include/graph_add.try_add_edge_connecting.md
@@ -0,0 +1 @@
+Adds a new edge between two vertices, adding the vertices if necessary.
diff --git a/gryf/docs/include/graph_add.try_add_vertex.md b/gryf/docs/include/graph_add.try_add_vertex.md
new file mode 100644
index 0000000..78fee5a
--- /dev/null
+++ b/gryf/docs/include/graph_add.try_add_vertex.md
@@ -0,0 +1 @@
+Adds a new vertex to the graph, returning error in case of failure.
diff --git a/gryf/docs/include/graph_add.try_get_or_add_vertex.md b/gryf/docs/include/graph_add.try_get_or_add_vertex.md
new file mode 100644
index 0000000..15567d8
--- /dev/null
+++ b/gryf/docs/include/graph_add.try_get_or_add_vertex.md
@@ -0,0 +1 @@
+Finds and returns a vertex by its attribute or adds it if not found.
diff --git a/gryf/docs/include/graph_base.edge_count_hint.md b/gryf/docs/include/graph_base.edge_count_hint.md
new file mode 100644
index 0000000..56de984
--- /dev/null
+++ b/gryf/docs/include/graph_base.edge_count_hint.md
@@ -0,0 +1 @@
+Returns the _upper bound_ of the edge count, if available.
diff --git a/gryf/docs/include/graph_base.is_directed.md b/gryf/docs/include/graph_base.is_directed.md
new file mode 100644
index 0000000..700159c
--- /dev/null
+++ b/gryf/docs/include/graph_base.is_directed.md
@@ -0,0 +1 @@
+Returns `true` if the graph is directed.
diff --git a/gryf/docs/include/graph_base.vertex_count_hint.md b/gryf/docs/include/graph_base.vertex_count_hint.md
new file mode 100644
index 0000000..d189e51
--- /dev/null
+++ b/gryf/docs/include/graph_base.vertex_count_hint.md
@@ -0,0 +1 @@
+Returns the _upper bound_ of the vertex count, if available.
diff --git a/gryf/docs/include/graph_full.clear.md b/gryf/docs/include/graph_full.clear.md
new file mode 100644
index 0000000..b081bcb
--- /dev/null
+++ b/gryf/docs/include/graph_full.clear.md
@@ -0,0 +1 @@
+Removes all vertices and edges from the graph.
diff --git a/gryf/docs/include/graph_full.clear_edges.md b/gryf/docs/include/graph_full.clear_edges.md
new file mode 100644
index 0000000..b247264
--- /dev/null
+++ b/gryf/docs/include/graph_full.clear_edges.md
@@ -0,0 +1 @@
+Removes all edges from the graph.
diff --git a/gryf/docs/include/graph_full.remove_edge.md b/gryf/docs/include/graph_full.remove_edge.md
new file mode 100644
index 0000000..4f7a631
--- /dev/null
+++ b/gryf/docs/include/graph_full.remove_edge.md
@@ -0,0 +1,3 @@
+Removes an edge from the graph.
+
+Returns the edge attribute if the edge was present, or `None` otherwise.
diff --git a/gryf/docs/include/graph_full.remove_edge_any_between.md b/gryf/docs/include/graph_full.remove_edge_any_between.md
new file mode 100644
index 0000000..8605cc0
--- /dev/null
+++ b/gryf/docs/include/graph_full.remove_edge_any_between.md
@@ -0,0 +1,3 @@
+Removes an edge between two vertices, if any exist.
+
+Returns the edge attribute if an edge was present, or `None` otherwise.
diff --git a/gryf/docs/include/graph_full.remove_edges_between.md b/gryf/docs/include/graph_full.remove_edges_between.md
new file mode 100644
index 0000000..a0cf524
--- /dev/null
+++ b/gryf/docs/include/graph_full.remove_edges_between.md
@@ -0,0 +1 @@
+Removes all edges between two vertices.
diff --git a/gryf/docs/include/graph_full.remove_vertex.md b/gryf/docs/include/graph_full.remove_vertex.md
new file mode 100644
index 0000000..86a4a0d
--- /dev/null
+++ b/gryf/docs/include/graph_full.remove_vertex.md
@@ -0,0 +1,3 @@
+Removes a vertex from the graph.
+
+Returns the vertex attribute if the vertex was present, or `None` otherwise.
diff --git a/gryf/docs/include/graph_mut.edge_mut.md b/gryf/docs/include/graph_mut.edge_mut.md
new file mode 100644
index 0000000..5c620db
--- /dev/null
+++ b/gryf/docs/include/graph_mut.edge_mut.md
@@ -0,0 +1 @@
+Returns a mutable reference to the edge attribute, if it exists.
diff --git a/gryf/docs/include/graph_mut.replace_edge.md b/gryf/docs/include/graph_mut.replace_edge.md
new file mode 100644
index 0000000..03de965
--- /dev/null
+++ b/gryf/docs/include/graph_mut.replace_edge.md
@@ -0,0 +1,5 @@
+Replaces the attribute of an edge with a new value, returning the old one.
+
+# Panics
+
+Panics if the edge does not exist.
diff --git a/gryf/docs/include/graph_mut.replace_vertex.md b/gryf/docs/include/graph_mut.replace_vertex.md
new file mode 100644
index 0000000..3eb20f3
--- /dev/null
+++ b/gryf/docs/include/graph_mut.replace_vertex.md
@@ -0,0 +1,5 @@
+Replaces the attribute of a vertex with a new value, returning the old one.
+
+# Panics
+
+Panics if the vertex does not exist.
diff --git a/gryf/docs/include/graph_mut.try_replace_edge.md b/gryf/docs/include/graph_mut.try_replace_edge.md
new file mode 100644
index 0000000..2caa2f9
--- /dev/null
+++ b/gryf/docs/include/graph_mut.try_replace_edge.md
@@ -0,0 +1,3 @@
+Replaces the attribute of an edge with a new value, returning the old one.
+
+If the edge does not exist, an error is returned.
diff --git a/gryf/docs/include/graph_mut.try_replace_vertex.md b/gryf/docs/include/graph_mut.try_replace_vertex.md
new file mode 100644
index 0000000..ce844d6
--- /dev/null
+++ b/gryf/docs/include/graph_mut.try_replace_vertex.md
@@ -0,0 +1,3 @@
+Replaces the attribute of a vertex with a new value, returning the old one.
+
+If the vertex does not exist, an error is returned.
diff --git a/gryf/docs/include/graph_mut.vertex_mut.md b/gryf/docs/include/graph_mut.vertex_mut.md
new file mode 100644
index 0000000..672c80e
--- /dev/null
+++ b/gryf/docs/include/graph_mut.vertex_mut.md
@@ -0,0 +1 @@
+Returns a mutable reference to the vertex attribute, if it exists.
diff --git a/gryf/docs/include/graph_ref.edge.md b/gryf/docs/include/graph_ref.edge.md
new file mode 100644
index 0000000..28a6cb7
--- /dev/null
+++ b/gryf/docs/include/graph_ref.edge.md
@@ -0,0 +1 @@
+Returns a reference to the edge attribute, if it exists.
diff --git a/gryf/docs/include/graph_ref.edges.md b/gryf/docs/include/graph_ref.edges.md
new file mode 100644
index 0000000..8122f05
--- /dev/null
+++ b/gryf/docs/include/graph_ref.edges.md
@@ -0,0 +1 @@
+Returns all edges in the graph with their attributes and endpoints.
diff --git a/gryf/docs/include/graph_ref.find_vertex.md b/gryf/docs/include/graph_ref.find_vertex.md
new file mode 100644
index 0000000..e1d885a
--- /dev/null
+++ b/gryf/docs/include/graph_ref.find_vertex.md
@@ -0,0 +1 @@
+Finds a vertex by its attribute.
diff --git a/gryf/docs/include/graph_ref.vertex.md b/gryf/docs/include/graph_ref.vertex.md
new file mode 100644
index 0000000..a114897
--- /dev/null
+++ b/gryf/docs/include/graph_ref.vertex.md
@@ -0,0 +1 @@
+Returns a reference to the vertex attribute, if it exists.
diff --git a/gryf/docs/include/graph_ref.vertices.md b/gryf/docs/include/graph_ref.vertices.md
new file mode 100644
index 0000000..7678073
--- /dev/null
+++ b/gryf/docs/include/graph_ref.vertices.md
@@ -0,0 +1 @@
+Returns all vertices in the graph with their attributes.
diff --git a/gryf/docs/include/graph_weak.edge_weak.md b/gryf/docs/include/graph_weak.edge_weak.md
new file mode 100644
index 0000000..28a6cb7
--- /dev/null
+++ b/gryf/docs/include/graph_weak.edge_weak.md
@@ -0,0 +1 @@
+Returns a reference to the edge attribute, if it exists.
diff --git a/gryf/docs/include/graph_weak.vertex_weak.md b/gryf/docs/include/graph_weak.vertex_weak.md
new file mode 100644
index 0000000..a114897
--- /dev/null
+++ b/gryf/docs/include/graph_weak.vertex_weak.md
@@ -0,0 +1 @@
+Returns a reference to the vertex attribute, if it exists.
diff --git a/gryf/docs/include/neighbors.degree_directed.md b/gryf/docs/include/neighbors.degree_directed.md
new file mode 100644
index 0000000..716b71c
--- /dev/null
+++ b/gryf/docs/include/neighbors.degree_directed.md
@@ -0,0 +1,9 @@
+Returns the out or in degree of the given vertex, depending on the edge
+direction argument.
+
+In undirected graphs the direction is ignored and the returned value is the same
+as from [degree_undirected](Self::degree_undirected).
+
+# Panics
+
+Panics if the vertex does not exist.
diff --git a/gryf/docs/include/neighbors.degree_undirected.md b/gryf/docs/include/neighbors.degree_undirected.md
new file mode 100644
index 0000000..9d1567e
--- /dev/null
+++ b/gryf/docs/include/neighbors.degree_undirected.md
@@ -0,0 +1,5 @@
+Returns the total degree of the given vertex.
+
+# Panics
+
+Panics if the vertex does not exist.
diff --git a/gryf/docs/include/neighbors.neighbors_directed.md b/gryf/docs/include/neighbors.neighbors_directed.md
new file mode 100644
index 0000000..ac230f3
--- /dev/null
+++ b/gryf/docs/include/neighbors.neighbors_directed.md
@@ -0,0 +1,8 @@
+Returns all neighbors of the given vertex in the given direction.
+
+In undirected graphs the edge direction is ignored and the returned items are
+the same as from [neighbors_undirected](Self::neighbors_undirected).
+
+# Panics
+
+Panics if the vertex does not exist.
diff --git a/gryf/docs/include/neighbors.neighbors_undirected.md b/gryf/docs/include/neighbors.neighbors_undirected.md
new file mode 100644
index 0000000..a9c49c5
--- /dev/null
+++ b/gryf/docs/include/neighbors.neighbors_undirected.md
@@ -0,0 +1,7 @@
+Returns all neighbors of the given vertex, regardless of edge direction.
+
+In directed graphs, this means outgoing as well as incoming edges.
+
+# Panics
+
+Panics if the vertex does not exist.
diff --git a/gryf/docs/include/vertex_set.contains_vertex.md b/gryf/docs/include/vertex_set.contains_vertex.md
new file mode 100644
index 0000000..80233ba
--- /dev/null
+++ b/gryf/docs/include/vertex_set.contains_vertex.md
@@ -0,0 +1 @@
+Returns `true` if the graph contains the given vertex.
diff --git a/gryf/docs/include/vertex_set.vertex_bound.md b/gryf/docs/include/vertex_set.vertex_bound.md
new file mode 100644
index 0000000..877367c
--- /dev/null
+++ b/gryf/docs/include/vertex_set.vertex_bound.md
@@ -0,0 +1,6 @@
+Returns the highest vertex ID currently in use.
+
+This might be useful in algorithms that use arrays. Before using this, check
+whether [`CompactIdMap`](crate::core::id::CompactIdMap) returned by
+[`vertex_id_map`](crate::core::VertexSet::vertex_id_map) doesn't satisfy your
+needs first.
diff --git a/gryf/docs/include/vertex_set.vertex_count.md b/gryf/docs/include/vertex_set.vertex_count.md
new file mode 100644
index 0000000..837c7ab
--- /dev/null
+++ b/gryf/docs/include/vertex_set.vertex_count.md
@@ -0,0 +1 @@
+Returns the number of vertices in the graph.
diff --git a/gryf/docs/include/vertex_set.vertex_id_map.md b/gryf/docs/include/vertex_set.vertex_id_map.md
new file mode 100644
index 0000000..efa009f
--- /dev/null
+++ b/gryf/docs/include/vertex_set.vertex_id_map.md
@@ -0,0 +1,4 @@
+Returns a mapping of vertex IDs to virtual IDs representing a contiguous
+sequence.
+
+This might be useful in algorithms that use arrays.
diff --git a/gryf/docs/include/vertex_set.vertices_by_id.md b/gryf/docs/include/vertex_set.vertices_by_id.md
new file mode 100644
index 0000000..a33e555
--- /dev/null
+++ b/gryf/docs/include/vertex_set.vertices_by_id.md
@@ -0,0 +1 @@
+Returns all vertices in the graph.
diff --git a/gryf/docs/include/visit.new.md b/gryf/docs/include/visit.new.md
new file mode 100644
index 0000000..f9adfa4
--- /dev/null
+++ b/gryf/docs/include/visit.new.md
@@ -0,0 +1 @@
+Initializes the traversal state from the given graph.
diff --git a/gryf/docs/include/visit.reset.md b/gryf/docs/include/visit.reset.md
new file mode 100644
index 0000000..16c565d
--- /dev/null
+++ b/gryf/docs/include/visit.reset.md
@@ -0,0 +1 @@
+Resets the traversal state.
diff --git a/gryf/docs/include/visit.start.md b/gryf/docs/include/visit.start.md
new file mode 100644
index 0000000..2ba40cb
--- /dev/null
+++ b/gryf/docs/include/visit.start.md
@@ -0,0 +1 @@
+Starts the traversal traversal from a single root.
diff --git a/gryf/docs/include/visit.start_all.md b/gryf/docs/include/visit.start_all.md
new file mode 100644
index 0000000..39e0912
--- /dev/null
+++ b/gryf/docs/include/visit.start_all.md
@@ -0,0 +1 @@
+Performs the traversal traversal from all vertices of the graph.
diff --git a/gryf/docs/include/visit.start_multi.md b/gryf/docs/include/visit.start_multi.md
new file mode 100644
index 0000000..799012d
--- /dev/null
+++ b/gryf/docs/include/visit.start_multi.md
@@ -0,0 +1 @@
+Performs the traversal traversal from given vertices in the graph.
diff --git a/gryf/docs/include/visit.visited.md b/gryf/docs/include/visit.visited.md
new file mode 100644
index 0000000..db6a308
--- /dev/null
+++ b/gryf/docs/include/visit.visited.md
@@ -0,0 +1 @@
+Returns the set of vertices visited so far.
diff --git a/gryf/examples/shortest_path.rs b/gryf/examples/shortest_path.rs
index 66c8c83..24c95c6 100644
--- a/gryf/examples/shortest_path.rs
+++ b/gryf/examples/shortest_path.rs
@@ -15,10 +15,12 @@ fn main() {
graph.extend_with_edges([
(prague, bratislava, 328u32),
- (prague, nuremberg, 293),
+ (prague, nuremberg, 297),
+ (prague, vienna, 293),
(bratislava, vienna, 79),
(nuremberg, munich, 170),
(vienna, munich, 402),
+ (vienna, florence, 863),
(munich, florence, 646),
(florence, rome, 278),
]);
@@ -34,5 +36,5 @@ fn main() {
.join(" - ");
println!("{distance} km from Prague through {path}");
- // 1387 km from Prague through Nuremberg - Munich - Florence - Rome
+ // 1391 km from Prague through Nuremberg - Munich - Florence - Rome
}
diff --git a/gryf/src/adapt.rs b/gryf/src/adapt.rs
index 9a064a2..d090aea 100644
--- a/gryf/src/adapt.rs
+++ b/gryf/src/adapt.rs
@@ -1,3 +1,5 @@
+//! Various graph adapters.
+
#[doc(hidden)]
pub mod complement;
#[doc(hidden)]
diff --git a/gryf/src/adapt/subgraph.rs b/gryf/src/adapt/subgraph.rs
index 38ceaf9..8f23d70 100644
--- a/gryf/src/adapt/subgraph.rs
+++ b/gryf/src/adapt/subgraph.rs
@@ -1,3 +1,7 @@
+//! A [subgraph] of a graph as determined by the vertex and edge predicates.
+//!
+//! [subgraph]: https://en.wikipedia.org/wiki/Glossary_of_graph_theory#subgraph
+
use crate::core::{
base::{EdgeReference, NeighborReference, VertexReference},
id::IntegerIdType,
@@ -7,6 +11,40 @@ use crate::core::{
use gryf_derive::GraphBase;
+/// A [subgraph] of a graph as determined by the vertex and edge predicates.
+///
+/// The vertex or edge presence determination is done lazily. This has a
+/// performance impact on some operations that are usually very fast.
+///
+/// [subgraph]: https://en.wikipedia.org/wiki/Glossary_of_graph_theory#subgraph
+///
+/// # Examples
+///
+/// ```
+/// use gryf::{
+/// adapt::Subgraph,
+/// core::{facts, marker::Undirected, EdgeSet, VertexSet},
+/// graph::Graph,
+/// };
+///
+/// let vertices = 0..10;
+///
+/// let mut graph = Graph::::with_capacity(
+/// vertices.len(),
+/// facts::complete_graph_edge_count::(vertices.len()),
+/// );
+/// graph.extend_with_vertices(vertices);
+/// graph.connect_vertices(|_, _| Some(()));
+///
+/// assert_eq!(graph.vertex_count(), 10);
+/// assert_eq!(graph.edge_count(), 55);
+///
+/// // Subgraph with only "even" vertices.
+/// let subgraph = Subgraph::new(&graph).filter_vertex(|v, g, _| g[v] % 2 == 0);
+///
+/// assert_eq!(subgraph.vertex_count(), 5);
+/// assert_eq!(subgraph.edge_count(), 15);
+/// ```
#[derive(GraphBase)]
#[gryf_crate]
pub struct Subgraph
@@ -26,6 +64,15 @@ impl Subgraph
where
G: GraphBase,
{
+ /// Creates a new subgraph of the given graph.
+ ///
+ /// Specify the subset of vertices and/or edges by
+ /// [`filter_vertex`](Subgraph::filter_vertex) and
+ /// [`filter_edge`](Subgraph::filter_edge), respectively. If neither is
+ /// defined, the subgraph represents the full original graph.
+ ///
+ /// If you need an owned additional state for determining the subsets, use
+ /// the [`Subgraph::with_state`] constructor.
pub fn new(graph: G) -> Self {
Self::with_state(graph, ())
}
@@ -35,6 +82,15 @@ impl Subgraph
where
G: GraphBase,
{
+ /// Creates a new subgraph of the given graph.
+ ///
+ /// Specify the subset of vertices and/or edges by
+ /// [`filter_vertex`](Subgraph::filter_vertex) and
+ /// [`filter_edge`](Subgraph::filter_edge), respectively. If neither is
+ /// defined, the subgraph represents the full original graph.
+ ///
+ /// If you don't need any state for determining the subsets, use the
+ /// [`Subgraph::new`] constructor.
pub fn with_state(graph: G, state: S) -> Self {
Self {
graph,
@@ -44,10 +100,15 @@ where
}
}
+ /// Returns the original graph.
pub fn into_inner(self) -> G {
self.graph
}
+ /// Specifies the subset of vertices by a predicate.
+ ///
+ /// The predicate takes the vertex ID, the original graph and the state that
+ /// was passed in via [`Subgraph::with_state`] (if any).
pub fn filter_vertex(self, predicate: F) -> Self
where
F: Fn(&G::VertexId, &G, &S) -> bool + 'static,
@@ -58,6 +119,10 @@ where
}
}
+ /// Specifies the subset of edges by a predicate.
+ ///
+ /// The predicate takes the edge ID, the original graph and the state that
+ /// was passed in via [`Subgraph::with_state`] (if any).
pub fn filter_edge(self, predicate: F) -> Self
where
F: Fn(&G::EdgeId, &G, &S) -> bool + 'static,
diff --git a/gryf/src/algo.rs b/gryf/src/algo.rs
index f3c1e66..e6c7f30 100644
--- a/gryf/src/algo.rs
+++ b/gryf/src/algo.rs
@@ -1,9 +1,31 @@
+//! Collection of graph algorithms.
+//!
+//! Algorithms are organized into problems they solve and these problems are
+//! represented by a single [type](#structs). The actual algorithm to solve the
+//! problem is **automatically selected** by `gryf`, unless explicitly specified
+//! otherwise.
+//!
+//! Parametrization of an algorithm is done using a [builder pattern]. The
+//! general approach is to call `Problem::on(graph)` method, followed by setters
+//! of optional algorithm parameters and completed by a `run` method with
+//! required algorithm parameters. The returned value is the type that
+//! represents the problem solution which provides API for properties of the
+//! solution.
+//!
+//! For simple cases, there are aliased in form of a [function](#functions).
+//!
+//! [builder pattern]:
+//! https://rust-unofficial.github.io/patterns/patterns/creational/builder.html
+
pub mod connected;
pub mod cycle;
pub mod shortest_paths;
pub mod toposort;
-pub use connected::{is_connected, is_path_between, Connected};
-pub use cycle::{is_cyclic, is_cyclic_undirected, Cycle};
-pub use shortest_paths::ShortestPaths;
-pub use toposort::TopoSort;
+#[doc(inline)]
+pub use self::{
+ connected::{is_connected, is_path_between, Connected},
+ cycle::{is_cyclic, is_cyclic_undirected, Cycle},
+ shortest_paths::ShortestPaths,
+ toposort::TopoSort,
+};
diff --git a/gryf/src/algo/connected.rs b/gryf/src/algo/connected.rs
index 962015a..2e1169e 100644
--- a/gryf/src/algo/connected.rs
+++ b/gryf/src/algo/connected.rs
@@ -1,3 +1,31 @@
+//! Determine whether a graph is [connected].
+//!
+//! See available parameters [here](ConnectedBuilder#implementations).
+//!
+//! [connected]: https://en.wikipedia.org/wiki/Connectivity_(graph_theory)
+//!
+//! # Examples
+//!
+//! ```
+//! use gryf::{algo::is_connected, graph::Graph};
+//!
+//! let mut graph = Graph::new_undirected();
+//!
+//! let a = graph.add_vertex("a");
+//! let b = graph.add_vertex("b");
+//! let c = graph.add_vertex("c");
+//! let d = graph.add_vertex("d");
+//!
+//! graph.add_edge(a, b, ());
+//! graph.add_edge(c, d, ());
+//!
+//! assert!(!is_connected(&graph));
+//!
+//! graph.add_edge(b, c, ());
+//!
+//! assert!(is_connected(&graph));
+//! ```
+
use crate::core::{GraphBase, Neighbors, VertexSet};
mod builder;
@@ -5,6 +33,9 @@ mod dfs;
pub use builder::ConnectedBuilder;
+/// Information whether a graph or two vertices are connected.
+///
+/// See [module](self) documentation for more details and example.
pub struct Connected
where
G: GraphBase,
@@ -17,19 +48,25 @@ impl Connected
where
G: GraphBase,
{
+ /// Returns `true` if the graph is connected.
pub fn is(&self) -> bool {
self.disconnected_any.is_none()
}
+ /// If the graph is not connected, returns a pair of vertices from
+ /// disconnected components.
pub fn disconnected_any(&self) -> Option<(&G::VertexId, &G::VertexId)> {
self.disconnected_any.as_ref().map(|(ref u, ref v)| (u, v))
}
+ /// Returns `true` if the connectivity was determined with the information
+ /// about edge directions ignored.
pub fn as_undirected(&self) -> bool {
self.as_undirected
}
}
+/// Returns `true` if the graph is connected.
pub fn is_connected(graph: &G) -> bool
where
G: Neighbors + VertexSet,
@@ -37,6 +74,7 @@ where
Connected::on(graph).run().is()
}
+/// Returns `true` if there is a path between given vertices.
pub fn is_path_between(graph: &G, from: &G::VertexId, to: &G::VertexId) -> bool
where
G: Neighbors + VertexSet,
diff --git a/gryf/src/algo/connected/builder.rs b/gryf/src/algo/connected/builder.rs
index 993814a..184187a 100644
--- a/gryf/src/algo/connected/builder.rs
+++ b/gryf/src/algo/connected/builder.rs
@@ -2,6 +2,7 @@ use crate::core::{GraphBase, Neighbors, VertexSet};
use super::{dfs::dfs, Connected};
+/// Builder for [`Connected`].
pub struct ConnectedBuilder<'a, G>
where
G: GraphBase,
@@ -15,6 +16,7 @@ impl Connected
where
G: GraphBase,
{
+ #[doc = include_str!("../../../docs/include/algo.on.md")]
pub fn on(graph: &G) -> ConnectedBuilder<'_, G> {
ConnectedBuilder {
graph,
@@ -28,6 +30,7 @@ impl<'a, G> ConnectedBuilder<'a, G>
where
G: GraphBase,
{
+ /// Narrows the connectivity check to only these two vertices.
pub fn between(self, from: &'a G::VertexId, to: &'a G::VertexId) -> Self {
Self {
between: Some((from, to)),
@@ -35,6 +38,7 @@ where
}
}
+ /// Instructs the algorithm to ignore the direction of the edges.
pub fn as_undirected(self) -> Self {
Self {
as_undirected: true,
@@ -47,6 +51,7 @@ impl<'a, G> ConnectedBuilder<'a, G>
where
G: GraphBase,
{
+ #[doc = include_str!("../../../docs/include/algo.run.md")]
pub fn run(self) -> Connected
where
G: Neighbors + VertexSet,
diff --git a/gryf/src/algo/cycle.rs b/gryf/src/algo/cycle.rs
index f658597..7477f02 100644
--- a/gryf/src/algo/cycle.rs
+++ b/gryf/src/algo/cycle.rs
@@ -1,3 +1,32 @@
+//! Find a [cycle] in a graph.
+//!
+//! See available parameters [here](CycleBuilder#implementations).
+//!
+//! [cycle]: https://en.wikipedia.org/wiki/Cycle_(graph_theory)
+//!
+//! # Examples
+//!
+//! ```
+//! use gryf::{algo::is_cyclic, graph::Graph};
+//!
+//! let mut graph = Graph::new_undirected();
+//!
+//! let a = graph.add_vertex("a");
+//! let b = graph.add_vertex("b");
+//! let c = graph.add_vertex("c");
+//! let d = graph.add_vertex("d");
+//!
+//! graph.add_edge(a, b, ());
+//! graph.add_edge(b, c, ());
+//! graph.add_edge(c, d, ());
+//!
+//! assert!(!is_cyclic(&graph));
+//!
+//! graph.add_edge(d, a, ());
+//!
+//! assert!(is_cyclic(&graph));
+//! ```
+
use std::fmt;
use crate::core::{EdgeSet, GraphBase, Neighbors, VertexSet};
@@ -10,7 +39,11 @@ mod dfs;
pub use builder::CycleBuilder;
+/// Cycle in a graph.
+///
+/// See [module](self) documentation for more details and example.
pub struct Cycle {
+ /// An edge that is part of the cycle.
pub edge: G::EdgeId,
as_undirected: bool,
}
@@ -58,6 +91,7 @@ impl Cycle {
}
}
+ /// Collects the whole cycle in the graph.
pub fn collect(self, graph: &G) -> Vec
where
G: Neighbors + VertexSet + EdgeSet,
@@ -67,6 +101,7 @@ impl Cycle {
}
}
+/// Returns `true` if the graph is cyclic.
pub fn is_cyclic(graph: &G) -> bool
where
G: Neighbors + VertexSet + EdgeSet,
@@ -74,6 +109,7 @@ where
Cycle::on(graph).run().is_some()
}
+/// Returns `true` if the graph is cyclic, ignoring the direction of the edges.
pub fn is_cyclic_undirected(graph: &G) -> bool
where
G: Neighbors + VertexSet + EdgeSet,
diff --git a/gryf/src/algo/cycle/builder.rs b/gryf/src/algo/cycle/builder.rs
index 769886e..ca1b7bb 100644
--- a/gryf/src/algo/cycle/builder.rs
+++ b/gryf/src/algo/cycle/builder.rs
@@ -2,6 +2,7 @@ use crate::core::{GraphBase, Neighbors, VertexSet};
use super::{dfs::dfs_find, Cycle};
+/// Builder for [`Cycle`].
pub struct CycleBuilder<'a, G>
where
G: GraphBase,
@@ -14,6 +15,7 @@ impl Cycle
where
G: GraphBase,
{
+ #[doc = include_str!("../../../docs/include/algo.on.md")]
pub fn on(graph: &G) -> CycleBuilder<'_, G> {
CycleBuilder {
graph,
@@ -26,6 +28,7 @@ impl<'a, G> CycleBuilder<'a, G>
where
G: GraphBase,
{
+ /// Instructs the algorithm to ignore the direction of the edges.
#[allow(clippy::wrong_self_convention)]
pub fn as_undirected(self) -> Self {
Self {
@@ -39,6 +42,7 @@ impl<'a, G> CycleBuilder<'a, G>
where
G: GraphBase,
{
+ #[doc = include_str!("../../../docs/include/algo.run.md")]
pub fn run(self) -> Option>
where
G: Neighbors + VertexSet,
diff --git a/gryf/src/algo/shortest_paths.rs b/gryf/src/algo/shortest_paths.rs
index 4d830a3..642b6bb 100644
--- a/gryf/src/algo/shortest_paths.rs
+++ b/gryf/src/algo/shortest_paths.rs
@@ -1,3 +1,54 @@
+//! Find [single source shortest paths] and their distances in a graph.
+//!
+//! See available parameters [here](ShortestPathsBuilder#implementations).
+//!
+//! Note that more efficient algorithms can be applied if the edges do not have
+//! negative weights. If you have a graph where nonnegative weights can be
+//! guaranteed at compile time, make sure to use an [unsigned
+//! type](crate::core::weight::Weight::is_unsigned) like `u8`, `u32` or
+//! [`uf64`](crate::core::weight::uf64).
+//!
+//! [single source shortest paths]:
+//! https://en.wikipedia.org/wiki/Shortest_path_problem#Single-source_shortest_paths
+//!
+//! # Examples
+//!
+//! ```
+//! use gryf::{algo::ShortestPaths, graph::Graph};
+//!
+//! let mut graph = Graph::new_undirected();
+//!
+//! let prague = graph.add_vertex("Prague");
+//! let bratislava = graph.add_vertex("Bratislava");
+//! let vienna = graph.add_vertex("Vienna");
+//! let munich = graph.add_vertex("Munich");
+//! let nuremberg = graph.add_vertex("Nuremberg");
+//! let florence = graph.add_vertex("Florence");
+//! let rome = graph.add_vertex("Rome");
+//!
+//! graph.extend_with_edges([
+//! (prague, bratislava, 328u32),
+//! (prague, nuremberg, 297),
+//! (prague, vienna, 293),
+//! (bratislava, vienna, 79),
+//! (nuremberg, munich, 170),
+//! (vienna, munich, 402),
+//! (vienna, florence, 863),
+//! (munich, florence, 646),
+//! (florence, rome, 278),
+//! ]);
+//!
+//! let shortest_paths = ShortestPaths::on(&graph).goal(prague).run(rome).unwrap();
+//! let distance = shortest_paths[prague];
+//! let path = shortest_paths
+//! .reconstruct(prague)
+//! .map(|v| graph[v])
+//! .collect::>()
+//! .join(" - ");
+//!
+//! println!("{distance} km from Prague through {path}");
+//! ```
+
use std::{borrow::Borrow, ops::Index};
use rustc_hash::FxHashMap;
@@ -12,6 +63,9 @@ mod dijkstra;
pub use builder::ShortestPathsBuilder;
+/// Shortest paths and their distances from a single source vertex.
+///
+/// See [module](self) documentation for more details and example.
#[derive(Debug)]
pub struct ShortestPaths {
source: G::VertexId,
@@ -26,10 +80,18 @@ impl ShortestPaths
where
G: GraphBase,
{
+ /// Source vertex where the search was started.
pub fn source(&self) -> &G::VertexId {
&self.source
}
+ /// Returns the path distance between the source vertex and the given
+ /// vertex, or `None` if it's not known.
+ ///
+ /// There are two causes why the distance between two vertices is not known:
+ /// (1) the vertices are not connected, or (2) the
+ /// [goal](ShortestPathsBuilder::goal) was reached before visiting the given
+ /// vertex.
pub fn dist(&self, to: VI) -> Option<&W>
where
VI: Borrow,
@@ -37,6 +99,13 @@ where
self.dist.get(to.borrow())
}
+ /// Returns an iterator over vertices on the path between the given vertex
+ /// and the source vertex, in this order, or `None` if it wasn't found.
+ ///
+ /// There are two causes why the distance between two vertices is not known:
+ /// (1) the vertices are not connected, or (2) the
+ /// [goal](ShortestPathsBuilder::goal) was reached before visiting the given
+ /// vertex.
pub fn reconstruct(&self, to: G::VertexId) -> PathReconstruction<'_, G> {
PathReconstruction {
curr: to,
@@ -57,10 +126,38 @@ where
}
}
+/// Algorithm for [`ShortestPaths`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Algo {
+ /// [Dijkstra's
+ /// algorithm](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm)
+ ///
+ /// Dijkstra's algorithm is a popular method on a graph with non-negative
+ /// edge weights. It operates by iteratively selecting the vertex with the
+ /// smallest known distance from the source and updating the distances of
+ /// its neighbors.
+ ///
+ /// # Use cases
+ ///
+ /// * Finding the shortest path in road networks.
+ /// * Optimizing routing in communication networks.
+ /// * Navigation and GPS systems.
Dijkstra,
+
+ /// [Bellman–Ford
+ /// algorithm](https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm).
+ ///
+ /// The Bellman-Ford algorithm can handle graphs with negative edge weights
+ /// and can detect negative weight cycles in a graph. However, it is
+ /// generally slower than Dijkstra's algorithm.
+ ///
+ /// # Use cases
+ ///
+ /// * Finding shortest paths in graphs that may contain negative weight
+ /// edges.
+ /// * Detecting negative weight cycles in financial models.
+ /// * Network routing protocols like RIP (Routing Information Protocol).
BellmanFord,
}
@@ -83,18 +180,32 @@ mod algo {
pub struct Bfs;
}
+/// The error encountered during a [`ShortestPaths`] run.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
pub enum Error {
+ /// An edge with negative weight encountered.
#[error("edge with negative weight encountered")]
NegativeWeight,
+
+ /// A negative cycle encountered.
#[error("negative cycle encountered")]
NegativeCycle,
+
+ /// The specified goal not reached.
+ #[error("specified goal not reached")]
+ GoalNotReached,
+
+ /// An edge not available.
+ ///
+ /// This error should not happen in normal circumstances. If it does, it
+ /// indicates a bad implementation of the graph.
#[error("edge not available")]
EdgeNotAvailable,
- #[error("goal not reached")]
- GoalNotReached,
}
+/// Iterator over the vertices on the path from a vertex to the source vertex.
+///
+/// Returned by [`ShortestPaths::reconstruct`].
pub struct PathReconstruction<'a, G: GraphBase> {
curr: G::VertexId,
pred: &'a FxHashMap,
diff --git a/gryf/src/algo/shortest_paths/builder.rs b/gryf/src/algo/shortest_paths/builder.rs
index b5902ce..b54d8f7 100644
--- a/gryf/src/algo/shortest_paths/builder.rs
+++ b/gryf/src/algo/shortest_paths/builder.rs
@@ -15,6 +15,7 @@ enum AlgoExt {
Bfs,
}
+/// Builder for [`ShortestPaths`].
pub struct ShortestPathsBuilder<'a, W, G, F, A>
where
G: GraphBase,
@@ -30,6 +31,7 @@ impl ShortestPaths
where
G: GraphBase,
{
+ #[doc = include_str!("../../../docs/include/algo.on.md")]
pub fn on(graph: &G) -> ShortestPathsBuilder<'_, W, G, weight::Identity, algo::AnyAlgo> {
ShortestPathsBuilder {
graph,
@@ -45,6 +47,10 @@ impl<'a, W, G, F, A> ShortestPathsBuilder<'a, W, G, F, A>
where
G: GraphBase,
{
+ /// Specifies a goal vertex.
+ ///
+ /// When this vertex is reached during the search, the algorithm stops
+ /// without visiting the rest of the vertices.
pub fn goal(self, goal: G::VertexId) -> Self {
Self {
goal: Some(goal),
@@ -52,6 +58,12 @@ where
}
}
+ /// Specifies the [mapping](GetWeight) from edge attributes to
+ /// [weight](Weight).
+ ///
+ /// By default, the edge attribute itself is used. If you want the mapping
+ /// to be a function or closure use
+ /// [`edge_weight_fn`](ShortestPathsBuilder::edge_weight_fn) instead.
pub fn edge_weight(self, edge_weight: F2) -> ShortestPathsBuilder<'a, W, G, F2, A>
where
G: GraphRef,
@@ -70,6 +82,13 @@ where
// Using closures in `edge_weight` gives "type annotations needed" for the
// closure argument. This method that uses explicit Fn signature circumvents
// the problem.
+ ///
+ /// Specifies the [mapping](GetWeight) from edge attributes to
+ /// [weight](Weight).
+ ///
+ /// By default, the edge attribute itself is used. If you want the mapping
+ /// to be a type implementing the [`GetWeight`] trait, use
+ /// [`edge_weight`](ShortestPathsBuilder::edge_weight) instead.
pub fn edge_weight_fn(self, edge_weight: F2) -> ShortestPathsBuilder<'a, W, G, F2, A>
where
G: GraphRef,
@@ -79,6 +98,16 @@ where
self.edge_weight(edge_weight)
}
+ /// Specifies that every edge has weight equal to `1`.
+ ///
+ /// Make sure to use this builder method instead of e.g., `|_| 1` because
+ /// this unlocks new algorithms that can be used on graphs with constant
+ /// weights.
+ ///
+ /// If you want to specify a custom [mapping](GetWeight) from edge
+ /// attributes to [weight](Weight), check
+ /// [`edge_weight`](ShortestPathsBuilder::edge_weight) and
+ /// [`edge_weight_fn`](ShortestPathsBuilder::edge_weight_fn).
pub fn unit_weight(self) -> ShortestPathsBuilder<'a, W, G, weight::Unit, A> {
ShortestPathsBuilder {
edge_weight: weight::Unit,
@@ -89,6 +118,9 @@ where
}
}
+ /// Chooses the Dijkstra's algorithm.
+ ///
+ /// See [`Algo::Dijkstra`] for details.
pub fn dijkstra(self) -> ShortestPathsBuilder<'a, W, G, F, algo::Dijkstra>
where
G: Neighbors + GraphWeak,
@@ -102,6 +134,9 @@ where
}
}
+ /// Chooses the Bellman–Ford algorithm.
+ ///
+ /// See [`Algo::BellmanFord`] for details.
pub fn bellman_ford(self) -> ShortestPathsBuilder<'a, W, G, F, algo::BellmanFord>
where
G: GraphRef,
@@ -116,6 +151,10 @@ where
}
}
+ /// Chooses the breadth-first search algorithm.
+ ///
+ /// This only works on constant weights. Unlock this algorithm with
+ /// [unit_weight](ShortestPathsBuilder::unit_weight).
pub fn bfs(self) -> ShortestPathsBuilder<'a, W, G, F, algo::Bfs>
where
G: Neighbors,
@@ -130,6 +169,7 @@ where
}
}
+ #[doc = include_str!("../../../docs/include/algo.using.md")]
pub fn using(self, algo: Algo) -> ShortestPathsBuilder<'a, W, G, F, algo::SpecificAlgo>
where
G: Neighbors + GraphRef,
@@ -144,6 +184,7 @@ where
}
}
+ #[doc = include_str!("../../../docs/include/algo.using_opt.md")]
pub fn using_opt(
self,
algo: Option,
@@ -166,6 +207,7 @@ impl<'a, W, G, F> ShortestPathsBuilder<'a, W, G, F, algo::AnyAlgo>
where
G: GraphBase,
{
+ #[doc = include_str!("../../../docs/include/algo.run.md")]
pub fn run(self, source: G::VertexId) -> Result, Error>
where
G: Neighbors + GraphRef,
@@ -193,6 +235,7 @@ impl<'a, W, G, F> ShortestPathsBuilder<'a, W, G, F, algo::Dijkstra>
where
G: GraphBase,
{
+ #[doc = include_str!("../../../docs/include/algo.run.md")]
pub fn run(self, source: G::VertexId) -> Result, Error>
where
G: Neighbors + GraphWeak,
@@ -214,6 +257,7 @@ impl<'a, W, G, F> ShortestPathsBuilder<'a, W, G, F, algo::BellmanFord>
where
G: GraphBase,
{
+ #[doc = include_str!("../../../docs/include/algo.run.md")]
pub fn run(self, source: G::VertexId) -> Result, Error>
where
G: GraphRef,
@@ -236,6 +280,7 @@ impl<'a, W, G, F> ShortestPathsBuilder<'a, W, G, F, algo::Bfs>
where
G: GraphBase,
{
+ #[doc = include_str!("../../../docs/include/algo.run.md")]
pub fn run(self, source: G::VertexId) -> Result, Error>
where
G: Neighbors,
@@ -256,6 +301,7 @@ impl<'a, W, G, F> ShortestPathsBuilder<'a, W, G, F, algo::SpecificAlgo>
where
G: GraphBase,
{
+ #[doc = include_str!("../../../docs/include/algo.run.md")]
pub fn run(self, source: G::VertexId) -> Result, Error>
where
G: Neighbors + GraphRef,
diff --git a/gryf/src/algo/toposort.rs b/gryf/src/algo/toposort.rs
index 4e81125..ce30031 100644
--- a/gryf/src/algo/toposort.rs
+++ b/gryf/src/algo/toposort.rs
@@ -1,3 +1,48 @@
+//! Find a [topologically sorted] collection of vertices on a [directed acyclic
+//! graph] (DAG).
+//!
+//! See available parameters [here](TopoSortBuilder#implementations).
+//!
+//! The exact order in which the vertices are reported is not specified and
+//! should not be relied upon.
+//!
+//! [topologically sorted]: https://en.wikipedia.org/wiki/Topological_sorting
+//! [directed acyclic graph]:
+//! https://en.wikipedia.org/wiki/Directed_acyclic_graph
+//!
+//! # Examples
+//!
+//! ```
+//! use gryf::{algo::TopoSort, graph::Graph};
+//!
+//! let mut dependency_tree = Graph::<_, (), _>::new_directed();
+//!
+//! let cargo = dependency_tree.add_vertex("cargo");
+//! let cargo_credential = dependency_tree.add_vertex("cargo_credential");
+//! let serde = dependency_tree.add_vertex("serde");
+//! let serde_json = dependency_tree.add_vertex("serde_json");
+//! let time = dependency_tree.add_vertex("time");
+//! let cargo_util = dependency_tree.add_vertex("cargo_util");
+//! let libc = dependency_tree.add_vertex("libc");
+//!
+//! // Edge direction in "must be compiled before" relation.
+//! dependency_tree.extend_with_edges([
+//! (cargo_credential, cargo),
+//! (serde, cargo_credential),
+//! (serde_json, cargo_credential),
+//! (serde, serde_json),
+//! (time, cargo_credential),
+//! (libc, time),
+//! (serde, time),
+//! (cargo_util, cargo),
+//! (libc, cargo_util),
+//! ]);
+//!
+//! for package in TopoSort::on(&dependency_tree).run().map(Result::unwrap) {
+//! // Compile package
+//! }
+//! ```
+
use std::fmt;
use thiserror::Error;
@@ -17,6 +62,14 @@ pub use builder::TopoSortBuilder;
pub use dfs::DfsVisit;
use kahn::KahnIter;
+/// Topologically sorted collection of vertices on a directed acyclic graph
+/// (DAG).
+///
+/// See [module](self) documentation for more details and example.
+///
+/// This type implements the [`Iterator`] trait and, depending on the
+/// [algorithm](Algo), it could be **lazy**. If you want the resulting [`Vec`]
+/// of the vertices, use [`into_vec`](TopoSort::into_vec).
pub struct TopoSort<'a, G>
where
G: VertexSet,
@@ -41,16 +94,48 @@ where
G: GraphBase + Neighbors + VertexSet,
G::VertexId: IntegerIdType,
{
+ /// Returns the topologically sorted collection of vertices as [`Vec`],
+ /// `Err` if a cycle is detected.
pub fn into_vec(self) -> Result, Error> {
self.collect()
}
}
+/// Algorithm for [`TopoSort`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Algo {
- Dfs,
+ /// [Kahn's
+ /// algorithm](https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm).
+ ///
+ /// Kahn's algorithm is a method that iteratively removes vertices with no
+ /// incoming edges (indegree of zero) and adds them to the sorted
+ /// collection. The algorithm is efficient.
+ ///
+ /// The implementation is lazy and produces an [iterator](Iterator).
+ ///
+ /// # Use cases
+ ///
+ /// * Scheduling tasks that have dependencies.
+ /// * Determining the order of compilation in build systems.
Kahn,
+
+ /// A variant on the [depth-first
+ /// search](https://en.wikipedia.org/wiki/Depth-first_search) traversal.
+ ///
+ /// This implementation visits all vertices with reversed direction of all
+ /// edges and reports vertices when they are being
+ /// [closed](crate::visit::DfsEvent::Close).
+ ///
+ /// The implementation is lazy and produces an [iterator](Iterator).
+ /// Moreover, it can also produce a [visitor](crate::visit::Visitor) with
+ /// all its advantages. To utilize this possibility, make sure to [choose
+ /// the DFS algorithm](TopoSortBuilder::dfs) specifically.
+ ///
+ /// # Use cases
+ ///
+ /// * Same as Kahn's algorithm.
+ Dfs,
}
mod algo {
@@ -69,12 +154,16 @@ mod algo {
pub struct Kahn;
}
+/// The error encountered during a [`TopoSort`] run.
#[derive(Error)]
pub enum Error
where
G: GraphBase,
{
- #[error("the graph contains cycle")]
+ /// The graph contains a cycle.
+ ///
+ /// Graphs with cycles don't have a topological order.
+ #[error("graph contains cycle")]
Cycle(Cycle),
}
diff --git a/gryf/src/algo/toposort/builder.rs b/gryf/src/algo/toposort/builder.rs
index 99fee11..a798b9c 100644
--- a/gryf/src/algo/toposort/builder.rs
+++ b/gryf/src/algo/toposort/builder.rs
@@ -10,6 +10,7 @@ use super::{
Algo, TopoSort, TopoSortInner,
};
+/// Builder for [`TopoSort`].
pub struct TopoSortBuilder<'a, G, A> {
graph: &'a G,
algo: A,
@@ -19,6 +20,7 @@ impl TopoSort<'_, G>
where
G: GraphBase + VertexSet,
{
+ #[doc = include_str!("../../../docs/include/algo.on.md")]
pub fn on(graph: &G) -> TopoSortBuilder<'_, G, algo::AnyAlgo> {
TopoSortBuilder {
graph,
@@ -28,6 +30,9 @@ where
}
impl<'a, G, A> TopoSortBuilder<'a, G, A> {
+ /// Chooses the DFS algorithm.
+ ///
+ /// See [`Algo::Dfs`] for details.
pub fn dfs(self) -> TopoSortBuilder<'a, G, algo::Dfs> {
TopoSortBuilder {
graph: self.graph,
@@ -35,6 +40,9 @@ impl<'a, G, A> TopoSortBuilder<'a, G, A> {
}
}
+ /// Chooses the Kahn's algorithm.
+ ///
+ /// See [`Algo::Kahn`] for details.
pub fn kahn(self) -> TopoSortBuilder<'a, G, algo::Kahn>
where
G: GraphBase,
@@ -46,6 +54,7 @@ impl<'a, G, A> TopoSortBuilder<'a, G, A> {
}
}
+ #[doc = include_str!("../../../docs/include/algo.using.md")]
pub fn using(self, algo: Algo) -> TopoSortBuilder<'a, G, algo::SpecificAlgo>
where
G: GraphBase,
@@ -57,6 +66,7 @@ impl<'a, G, A> TopoSortBuilder<'a, G, A> {
}
}
+ #[doc = include_str!("../../../docs/include/algo.using_opt.md")]
pub fn using_opt(self, algo: Option) -> TopoSortBuilder<'a, G, algo::SpecificAlgo>
where
G: GraphBase,
@@ -73,6 +83,7 @@ impl<'a, G> TopoSortBuilder<'a, G, algo::AnyAlgo>
where
G: GraphBase + VertexSet,
{
+ #[doc = include_str!("../../../docs/include/algo.run.md")]
pub fn run(self) -> TopoSort<'a, G>
where
G: Neighbors,
@@ -88,6 +99,7 @@ impl<'a, G> TopoSortBuilder<'a, G, algo::Dfs>
where
G: GraphBase + VertexSet,
{
+ #[doc = include_str!("../../../docs/include/algo.run.md")]
pub fn run(self) -> DfsVisit<'a, G> {
dfs_visit(self.graph)
}
@@ -97,6 +109,7 @@ impl<'a, G> TopoSortBuilder<'a, G, algo::Kahn>
where
G: GraphBase + VertexSet,
{
+ #[doc = include_str!("../../../docs/include/algo.run.md")]
pub fn run(self) -> TopoSort<'a, G>
where
G: Neighbors,
@@ -112,6 +125,7 @@ impl<'a, G> TopoSortBuilder<'a, G, algo::SpecificAlgo>
where
G: GraphBase + VertexSet,
{
+ #[doc = include_str!("../../../docs/include/algo.run.md")]
pub fn run(self) -> TopoSort<'a, G>
where
G: Neighbors,
diff --git a/gryf/src/algo/toposort/dfs.rs b/gryf/src/algo/toposort/dfs.rs
index 976ddc6..63e0de0 100644
--- a/gryf/src/algo/toposort/dfs.rs
+++ b/gryf/src/algo/toposort/dfs.rs
@@ -29,6 +29,10 @@ where
}
}
+/// A [visitor](Visitor) that visits the vertices in topological order.
+///
+/// Returned from running the [`TopoSort`](crate::algo::TopoSort) with
+/// [DFS](crate::algo::toposort::TopoSortBuilder::dfs) algorithm.
pub struct DfsVisit<'a, G>
where
G: VertexSet + 'a,
diff --git a/gryf/src/core.rs b/gryf/src/core.rs
index c4a8360..9956311 100644
--- a/gryf/src/core.rs
+++ b/gryf/src/core.rs
@@ -1,3 +1,159 @@
+//! Core traits and types used in gryf.
+//!
+//! The module mostly contains interfaces that storages and graph
+//! representations implement. The main traits that represent different levels
+//! of graph functionality are
+//!
+//! * [GraphBase]
+//! * [Neighbors]
+//! * [VertexSet]
+//! * [EdgeSet]
+//! * [GraphRef]
+//! * [GraphMut]
+//! * [GraphAdd]
+//! * [GraphFull]
+//!
+//! The hierarchy and relations between these are depicted on the diagram below
+//! (expand the _Trait hierarchy_ box).
+//!
+//!
+//! Trait hierarchy
+//!
+//!
+#![doc = include_str!("../docs/assets/trait_hierarchy.excalidraw.svg")]
+//!
+//!
+//!
+//! # Common operations
+//!
+//! ## Create an empty graph
+//!
+//! ```
+//! use gryf::{core::marker::Directed, graph::Graph};
+//!
+//! // Directed graph with `&str` vertex attributes and `u32` edge attributes.
+//! let graph = Graph::<&str, u32, Directed>::new();
+//!
+//! // Prefer this if the attribute types (`&str`, `u32`) can be inferred
+//! // from the vertices and edges addition.
+//! let graph = Graph::<&str, u32, _>::new_directed();
+//! ```
+//!
+//! ## Create an empty graph in a specific storage
+//!
+//! ```
+//! use gryf::{core::marker::Undirected, graph::Graph, storage::AdjMatrix};
+//!
+//! // The last generic type corresponds to the types of vertex/edge IDs.
+//! // Using `::default()` chooses the default ID pair.
+//! let graph = Graph::<&str, u32, _, _>::new_in(AdjMatrix::<&str, u32, Undirected, _>::default());
+//! ```
+//!
+//! ## Add vertices and edges
+//!
+//! ```
+//! use gryf::graph::Graph;
+//!
+//! let mut graph = Graph::new_directed();
+//!
+//! let foo = graph.add_vertex("foo");
+//! let bar = graph.add_vertex("bar");
+//! let baz = graph.add_vertex("baz");
+//!
+//! let foobar = graph.add_edge(foo, bar, 3);
+//! graph.extend_with_edges([
+//! (foo, baz, 5),
+//! (bar, baz, 8),
+//! ]);
+//! ```
+//!
+//! ```
+//! use gryf::{core::marker::Directed, graph::Graph};
+//!
+//! let vertices = ["foo", "bar", "baz"];
+//! let edges = [(0, 1, 3), (0, 2, 5), (1, 2, 8)];
+//!
+//! let mut graph = Graph::<&str, u32, Directed>::with_capacity(vertices.len(), edges.len());
+//! graph.extend_with_vertices(vertices);
+//! graph.extend_with_edges(edges);
+//! ```
+//!
+//! ## Basic properties
+//!
+//! ```
+//! use gryf::{
+//! core::marker::{Incoming, Outgoing},
+//! graph::Graph,
+//! };
+//!
+//! let mut graph = Graph::new_directed();
+//!
+//! let foo = graph.add_vertex("foo");
+//! let bar = graph.add_vertex("bar");
+//! let baz = graph.add_vertex("baz");
+//! graph.add_edge(foo, bar, 3);
+//! graph.add_edge(bar, baz, 5);
+//!
+//! assert!(graph.is_directed());
+//! assert_eq!(graph.vertex_count(), 3);
+//! assert_eq!(graph.edge_count(), 2);
+//! assert_eq!(graph.degree_directed(bar, Outgoing), 1);
+//! assert_eq!(graph.degree_directed(bar, Incoming), 1);
+//! assert_eq!(graph.degree_undirected(bar), 2);
+//! assert!(graph.contains_vertex(baz));
+//! assert!(graph.contains_edge_between(foo, bar));
+//! assert!(graph.edge_id(foo, bar).next().is_some());
+//! ```
+//!
+//! ## Elements iteration
+//!
+//! ```
+//! use gryf::{core::id::VertexId, graph::Graph};
+//!
+//! let mut graph = Graph::new_directed();
+//!
+//! let foo = graph.add_vertex("foo");
+//! let bar = graph.add_vertex("bar");
+//! graph.add_edge(foo, bar, 3);
+//!
+//! let by_id: Vec = graph.vertices_by_id().collect();
+//! let attrs: Vec<&&str> = graph.vertices().map(|v| v.attr).collect();
+//! ```
+//!
+//! ## Graph traversal
+//!
+//! ```
+//! use gryf::{core::marker::Outgoing, graph::Graph};
+//!
+//! let mut graph = Graph::new_directed();
+//!
+//! let foo = graph.add_vertex("foo");
+//! let bar = graph.add_vertex("bar");
+//! graph.add_edge(foo, bar, 3);
+//!
+//! assert_eq!(
+//! graph.neighbors_directed(foo, Outgoing).map(|n| n.id).next(),
+//! Some(bar)
+//! );
+//! ```
+//!
+//! ## Attributes
+//!
+//! ```
+//! use gryf::graph::Graph;
+//!
+//! let mut graph = Graph::new_directed();
+//!
+//! let foo = graph.add_vertex("foo");
+//! let bar = graph.add_vertex("bar");
+//! let foobar = graph.add_edge(foo, bar, 3);
+//!
+//! assert_eq!(graph.vertex(foo), Some(&"foo"));
+//! assert_eq!(graph.edge(foobar), Some(&3));
+//! assert_eq!(graph.find_vertex("bar"), Some(bar));
+//! *graph.edge_mut(foobar).unwrap() = 5;
+//! ```
+
pub mod base;
pub mod borrow;
pub mod connect;
diff --git a/gryf/src/core/base.rs b/gryf/src/core/base.rs
index 8b4e60e..4d9dfe7 100644
--- a/gryf/src/core/base.rs
+++ b/gryf/src/core/base.rs
@@ -1,48 +1,124 @@
+//! Helper traits for giving semantics to existing types.
+
use super::{
borrow::OwnableRef,
id::{IdPair, IdType},
marker::Direction,
};
+// Default [reference](VertexReference) to a vertex with an attribute in a
+// graph.
+#[doc(alias = "Node")]
pub struct VertexRef<'a, VI: IdType, V> {
+ /// Vertex ID.
pub id: VI,
+
+ /// Vertex attribute.
pub attr: &'a V,
}
+/// Generic reference to a vertex with an attribute in a graph.
pub trait VertexReference {
+ /// Vertex ID.
fn id(&self) -> &VI;
+
+ /// Vertex attribute.
fn attr(&self) -> &V;
}
+/// Default reference to an edge with an attribute in a graph.
pub struct EdgeRef<'a, VI: IdType, EI: IdType, E> {
+ /// Edge ID.
pub id: EI,
+
+ /// Edge attribute.
pub attr: &'a E,
+
+ /// First endpoint of the edge. In directed graphs, this is the tail
+ /// (source, origin).
pub from: VI,
+
+ /// Second endpoint of the edge. In directed graphs, this is the head
+ /// (target, destination).
pub to: VI,
}
+/// Generic reference to an edge with an attribute in a graph.
pub trait EdgeReference {
+ /// Edge ID.
fn id(&self) -> &EI;
+
+ /// Edge attribute.
fn attr(&self) -> &E;
+
+ /// First endpoint of the edge. In directed graphs, this is the tail
+ /// (source, origin).
fn from(&self) -> &VI;
+
+ /// Second endpoint of the edge. In directed graphs, this is the head
+ /// (target, destination).
fn to(&self) -> &VI;
}
+/// Default reference to a neighbor in a graph.
pub struct NeighborRef {
+ /// Vertex ID.
pub id: VI,
+
+ /// Edge ID that led to the vertex.
pub edge: EI,
+
+ /// The other endpoint of the edge led to the vertex.
pub pred: VI,
+
+ /// Direction of the edge that led to the vertex.
pub dir: Direction,
}
+/// Generic reference to a neighbor in a graph.
pub trait NeighborReference {
+ /// Vertex ID.
fn id(&self) -> OwnableRef<'_, VI>;
+
+ /// Edge ID that led to the vertex.
fn edge(&self) -> OwnableRef<'_, EI>;
+
+ /// The other endpoint of the edge led to the vertex.
fn pred(&self) -> OwnableRef<'_, VI>;
+
+ /// Direction of the edge that led to the vertex.
fn dir(&self) -> Direction;
}
+/// Helper trait for representing an edge for convenience for edges with default
+/// attributes or without attributes.
+///
+/// # Examples
+///
+/// ```
+/// use gryf::core::{
+/// base::IntoEdge,
+/// id::{DefaultId, IdType, VertexId},
+/// };
+///
+/// fn take_edge(e: impl IntoEdge) -> E {
+/// let (_from, _to, attr) = e.unpack();
+/// attr
+/// }
+///
+/// let from = VertexId::from_usize(0);
+/// let to = VertexId::from_usize(1);
+///
+/// let attr: i32 = take_edge((from, to, 42));
+/// assert_eq!(attr, 42);
+///
+/// let attr: i32 = take_edge((from, to));
+/// assert_eq!(attr, 0);
+///
+/// let attr: () = take_edge((from, to));
+/// ```
pub trait IntoEdge {
+ /// Takes the edge representation and returns its endpoints and attribute.
fn unpack(self) -> (Id::VertexId, Id::VertexId, E);
}
diff --git a/gryf/src/core/borrow.rs b/gryf/src/core/borrow.rs
index aaa1ae8..883808e 100644
--- a/gryf/src/core/borrow.rs
+++ b/gryf/src/core/borrow.rs
@@ -1,3 +1,5 @@
+//! Module for hiding the difference between owned and borrowed data.
+
use core::{
borrow::Borrow,
cmp::Ordering,
@@ -5,6 +7,14 @@ use core::{
ops::Deref,
};
+/// `OwnableRef` is a type that represents either a
+/// [borrowed](OwnableRef::Borrowed) or [owned](OwnableRef::Owned) value of
+/// which a shared reference can be taken.
+///
+/// This type is very similar to [`std::borrow::Cow`] type, but is not intended
+/// for copy-on-write semantics. The main justification for using a custom type
+/// is that [`OwnableRef::into_owned`] requires `T: Clone` instead of `T:
+/// ToOwned`.
#[derive(Debug, Clone)]
pub enum OwnableRef<'a, T> {
Borrowed(&'a T),
@@ -12,6 +22,7 @@ pub enum OwnableRef<'a, T> {
}
impl OwnableRef<'_, T> {
+ /// Extracts the owned data or clones it in case of a borrow.
pub fn into_owned(self) -> T {
match self {
OwnableRef::Borrowed(attr) => attr.clone(),
diff --git a/gryf/src/core/connect.rs b/gryf/src/core/connect.rs
index 911cca5..42a9df6 100644
--- a/gryf/src/core/connect.rs
+++ b/gryf/src/core/connect.rs
@@ -1,6 +1,15 @@
+//! Convenient and efficient insertion of edges between vertices.
+
use super::graph::GraphAdd;
+/// Trait for convenient and efficient insertion of edges between vertices using
+/// a predicate.
pub trait ConnectVertices: GraphAdd {
+ #[doc = include_str!("../../docs/include/connect_vertices.connect_vertices.md")]
+ ///
+ /// # Examples
+ ///
+ /// See [Graph::connect_vertices](crate::graph::Graph::connect_vertices).
fn connect_vertices(&mut self, connect: F)
where
// NOTE: Ideally the API here would be `F: FnMut(Self::VertexRef<'_>, Self::VertexRef<'_>) -> Option`,
diff --git a/gryf/src/core/create.rs b/gryf/src/core/create.rs
index 8c68ca0..8063d13 100644
--- a/gryf/src/core/create.rs
+++ b/gryf/src/core/create.rs
@@ -1,18 +1,26 @@
+//! Initialization of graphs.
+
use super::{base::IntoEdge, graph::GraphAdd};
+/// Trait for creating a graph with known or estimated capacity.
pub trait Create: GraphAdd + Sized {
+ #[doc = include_str!("../../docs/include/create.with_capacity.md")]
fn with_capacity(vertex_capacity: usize, edge_capacity: usize) -> Self;
+ #[doc = include_str!("../../docs/include/create.empty.md")]
fn empty() -> Self {
Self::with_capacity(0, 0)
}
}
+/// Trait for extending graph with or creating graph from an iterator of vertices.
pub trait ExtendWithVertices: Create {
+ #[doc = include_str!("../../docs/include/extend_with_vertices.extend_with_vertices.md")]
fn extend_with_vertices(&mut self, iter: I)
where
I: IntoIterator;
+ #[doc = include_str!("../../docs/include/extend_with_vertices.from_vertices.md")]
fn from_vertices(iter: I) -> Self
where
I: IntoIterator,
@@ -40,10 +48,12 @@ where
}
}
+/// Trait for extending graph with an iterator of edges.
pub trait ExtendWithEdges: Create
where
T: IntoEdge,
{
+ #[doc = include_str!("../../docs/include/extend_with_edges.extend_with_edges.md")]
fn extend_with_edges(&mut self, iter: I)
where
I: IntoIterator;
diff --git a/gryf/src/core/error.rs b/gryf/src/core/error.rs
index c634a5c..b252473 100644
--- a/gryf/src/core/error.rs
+++ b/gryf/src/core/error.rs
@@ -1,22 +1,31 @@
+//! Collection of graph operations errors.
+
use std::fmt;
use thiserror::Error;
+/// The error type for vertex addition operations.
#[derive(Debug, Error, PartialEq)]
#[error("adding vertex failed: {kind}")]
pub struct AddVertexError {
+ /// Vertex attribute that could not be added.
pub attr: V,
+ /// Specific error kind.
pub kind: AddVertexErrorKind,
}
impl AddVertexError {
+ /// Creates the failed addition error with the vertex attribute and error
+ /// kind.
pub fn new(attr: V, kind: AddVertexErrorKind) -> Self {
Self { attr, kind }
}
}
+/// Vertex addition error kind.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AddVertexErrorKind {
+ /// The graph has exhausted its capacity.
CapacityOverflow,
}
@@ -29,61 +38,77 @@ impl fmt::Display for AddVertexErrorKind {
}
}
+/// The error type for vertex replacement operations.
#[derive(Debug, Error)]
#[error("replacing vertex failed: {kind}")]
pub struct ReplaceVertexError {
+ /// Vertex attribute that could not be replaced with.
pub attr: V,
+ /// Specific error kind.
pub kind: ReplaceVertexErrorKind,
}
impl ReplaceVertexError {
+ /// Creates the failed replacement error with the vertex attribute and error
+ /// kind.
pub fn new(attr: V, kind: ReplaceVertexErrorKind) -> Self {
Self { attr, kind }
}
}
+/// Vertex replacement error kind.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ReplaceVertexErrorKind {
+ /// The vertex does not exist.
VertexAbsent,
}
impl fmt::Display for ReplaceVertexErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let reason = match self {
- ReplaceVertexErrorKind::VertexAbsent => "vertex does not exist",
+ ReplaceVertexErrorKind::VertexAbsent => "the vertex does not exist",
};
f.write_str(reason)
}
}
+/// The error type for edge addition operations.
#[derive(Debug, Error, PartialEq)]
#[error("adding edge failed: {kind}")]
pub struct AddEdgeError {
+ /// Edge attribute that could not be added.
pub attr: E,
+ /// Specific error kind.
pub kind: AddEdgeErrorKind,
}
impl AddEdgeError {
+ /// Creates the failed addition error with the edge attribute and error kind.
pub fn new(attr: E, kind: AddEdgeErrorKind) -> Self {
Self { attr, kind }
}
}
+/// Edge addition error kind.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AddEdgeErrorKind {
+ /// The tail vertex of the edge does not exist.
TailAbsent,
+ /// The head vertex of the edge does not exist.
HeadAbsent,
+ /// An edge already exists, and the graph does not allow multiple edges.
MultiEdge,
+ /// The graph has exhausted its capacity.
CapacityOverflow,
}
impl fmt::Display for AddEdgeErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let reason = match self {
- AddEdgeErrorKind::TailAbsent => "edge tail vertex does not exist",
- AddEdgeErrorKind::HeadAbsent => "edge head vertex does not exist",
+ AddEdgeErrorKind::TailAbsent => "the tail vertex of the edge does not exist",
+ AddEdgeErrorKind::HeadAbsent => "the head vertex of the edge does not exist",
AddEdgeErrorKind::MultiEdge => {
- "an edge already exists and the graph does not allow multi edges"
+ "an edge already exists, and the graph does not allow multi edges"
}
AddEdgeErrorKind::CapacityOverflow => "the graph has exhausted its capacity",
};
@@ -91,37 +116,48 @@ impl fmt::Display for AddEdgeErrorKind {
}
}
+/// The error type for edge replacement operations.
#[derive(Debug, Error)]
#[error("replacing edge failed: {kind}")]
pub struct ReplaceEdgeError {
+ /// Edge attribute that could not be replaced with.
pub attr: V,
+ /// Specific error kind.
pub kind: ReplaceEdgeErrorKind,
}
impl ReplaceEdgeError {
+ /// Creates the failed replacement error with the edge attribute and error
+ /// kind.
pub fn new(attr: V, kind: ReplaceEdgeErrorKind) -> Self {
Self { attr, kind }
}
}
+/// Edge replacement error kind.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ReplaceEdgeErrorKind {
+ /// The edge does not exist.
EdgeAbsent,
}
impl fmt::Display for ReplaceEdgeErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let reason = match self {
- ReplaceEdgeErrorKind::EdgeAbsent => "edge does not exist",
+ ReplaceEdgeErrorKind::EdgeAbsent => "the edge does not exist",
};
f.write_str(reason)
}
}
+/// The error type for operations that involve adding vertices and connecting
+/// the with an edge.
#[derive(Debug, Error, PartialEq)]
pub enum AddEdgeConnectingError {
+ /// Error that occurred during vertex addition.
#[error("{0}")]
AddVertex(#[from] AddVertexError),
+ /// Error that occurred during edge addition.
#[error("{0}")]
AddEdge(#[from] AddEdgeError),
}
diff --git a/gryf/src/core/facts.rs b/gryf/src/core/facts.rs
index dd9c6ee..601c0c2 100644
--- a/gryf/src/core/facts.rs
+++ b/gryf/src/core/facts.rs
@@ -1,5 +1,19 @@
+//! Collection of simple utilities for various properties and calculations.
+
use super::marker::EdgeType;
+/// Returns the number of edges in a [complete graph] given the vertex count and
+/// directionality.
+///
+/// [complete graph]: https://en.wikipedia.org/wiki/Complete_graph
+///
+/// # Examples
+///
+/// ```
+/// use gryf::core::{facts::complete_graph_edge_count, marker::Undirected};
+///
+/// assert_eq!(complete_graph_edge_count::(5), 10);
+/// ```
pub fn complete_graph_edge_count(vertex_count: usize) -> usize {
if Ty::is_directed() {
vertex_count * (vertex_count - 1)
diff --git a/gryf/src/core/graph.rs b/gryf/src/core/graph.rs
index ada12f1..f62c1a8 100644
--- a/gryf/src/core/graph.rs
+++ b/gryf/src/core/graph.rs
@@ -11,38 +11,70 @@ use super::{
marker::{Direction, EdgeType},
};
+///
+/// Base trait for all graphs that provides core properties and functionality.
+///
+/// # Examples
+///
+/// ```
+/// use std::collections::HashSet;
+///
+/// use gryf::core::GraphBase;
+///
+/// fn algorithm(graph: &G) {
+/// let visited =
+/// HashSet::::with_capacity(graph.vertex_count_hint().unwrap_or(32));
+/// }
+/// ```
+///
+/// # Implementation notes
pub trait GraphBase {
+ /// Vertex ID type of the graph.
type VertexId: IdType;
+
+ /// Edge ID type of the graph.
type EdgeId: IdType;
+
+ /// Directionality of the graph.
type EdgeType: EdgeType;
+ #[doc = include_str!("../../docs/include/graph_base.is_directed.md")]
fn is_directed(&self) -> bool {
Self::EdgeType::is_directed()
}
- // Upper bound, if known.
+ #[doc = include_str!("../../docs/include/graph_base.vertex_count_hint.md")]
fn vertex_count_hint(&self) -> Option {
None
}
- // Upper bound, if known.
+ #[doc = include_str!("../../docs/include/graph_base.edge_count_hint.md")]
fn edge_count_hint(&self) -> Option {
None
}
}
+/// Trait for traversing vertex neighbors in a graph.
+///
+/// # Implementation notes
pub trait Neighbors: GraphBase {
+ /// Reference to a neighbor.
type NeighborRef<'a>: NeighborReference
where
Self: 'a;
+ /// Iterator over neighbors of a vertex.
type NeighborsIter<'a>: Iterator>
where
Self: 'a;
+ #[doc = include_str!("../../docs/include/neighbors.neighbors_undirected.md")]
fn neighbors_undirected(&self, from: &Self::VertexId) -> Self::NeighborsIter<'_>;
+
+ #[doc = include_str!("../../docs/include/neighbors.neighbors_directed.md")]
fn neighbors_directed(&self, from: &Self::VertexId, dir: Direction) -> Self::NeighborsIter<'_>;
+ #[doc = include_str!("../../docs/include/neighbors.degree_undirected.md")]
fn degree_undirected(&self, id: &Self::VertexId) -> usize {
if Self::EdgeType::is_directed() {
self.degree_directed(id, Direction::Outgoing)
@@ -52,6 +84,7 @@ pub trait Neighbors: GraphBase {
}
}
+ #[doc = include_str!("../../docs/include/neighbors.degree_directed.md")]
fn degree_directed(&self, id: &Self::VertexId, dir: Direction) -> usize {
if Self::EdgeType::is_directed() {
self.neighbors_directed(id, dir).count()
@@ -74,17 +107,25 @@ pub trait Neighbors: GraphBase {
}
}
+/// Trait representing a finite set of vertices.
+///
+/// # Implementation notes
pub trait VertexSet: GraphBase {
+ /// Iterator over vertex IDs.
type VerticesByIdIter<'a>: Iterator
where
Self: 'a;
+ #[doc = include_str!("../../docs/include/vertex_set.vertices_by_id.md")]
fn vertices_by_id(&self) -> Self::VerticesByIdIter<'_>;
+ #[doc = include_str!("../../docs/include/vertex_set.vertex_count.md")]
fn vertex_count(&self) -> usize {
self.vertices_by_id().count()
}
+ #[allow(rustdoc::redundant_explicit_links)]
+ #[doc = include_str!("../../docs/include/vertex_set.vertex_bound.md")]
fn vertex_bound(&self) -> usize
where
Self::VertexId: IntegerIdType,
@@ -95,10 +136,12 @@ pub trait VertexSet: GraphBase {
.unwrap_or_default()
}
+ #[doc = include_str!("../../docs/include/vertex_set.contains_vertex.md")]
fn contains_vertex(&self, id: &Self::VertexId) -> bool {
self.vertices_by_id().any(|v| &v == id)
}
+ #[doc = include_str!("../../docs/include/vertex_set.vertex_id_map.md")]
fn vertex_id_map(&self) -> CompactIdMap
where
Self::VertexId: IntegerIdType,
@@ -108,25 +151,36 @@ pub trait VertexSet: GraphBase {
}
}
+/// Trait representing a finite set of edges and the graph structure.
+///
+/// # Implementation notes
pub trait EdgeSet: GraphBase {
+ /// Iterator over edge IDs.
type EdgesByIdIter<'a>: Iterator
where
Self: 'a;
+ /// Iterator over edge IDs between two vertices.
type EdgeIdIter<'a>: Iterator
where
Self: 'a;
+ #[doc = include_str!("../../docs/include/edge_set.edges_by_id.md")]
fn edges_by_id(&self) -> Self::EdgesByIdIter<'_>;
+ #[doc = include_str!("../../docs/include/edge_set.edge_id.md")]
fn edge_id(&self, from: &Self::VertexId, to: &Self::VertexId) -> Self::EdgeIdIter<'_>;
+ #[doc = include_str!("../../docs/include/edge_set.endpoints.md")]
fn endpoints(&self, id: &Self::EdgeId) -> Option<(Self::VertexId, Self::VertexId)>;
+ #[doc = include_str!("../../docs/include/edge_set.edge_count.md")]
fn edge_count(&self) -> usize {
self.edges_by_id().count()
}
+ #[allow(rustdoc::redundant_explicit_links)]
+ #[doc = include_str!("../../docs/include/edge_set.edge_bound.md")]
fn edge_bound(&self) -> usize
where
Self::EdgeId: IntegerIdType,
@@ -137,18 +191,22 @@ pub trait EdgeSet: GraphBase {
.unwrap_or_default()
}
+ #[doc = include_str!("../../docs/include/edge_set.contains_edge.md")]
fn contains_edge(&self, id: &Self::EdgeId) -> bool {
self.edges_by_id().any(|e| &e == id)
}
+ #[doc = include_str!("../../docs/include/edge_set.contains_edge_between.md")]
fn contains_edge_between(&self, from: &Self::VertexId, to: &Self::VertexId) -> bool {
self.edge_id_any(from, to).is_some()
}
+ #[doc = include_str!("../../docs/include/edge_set.edge_id_any.md")]
fn edge_id_any(&self, from: &Self::VertexId, to: &Self::VertexId) -> Option {
self.edge_id(from, to).next()
}
+ #[doc = include_str!("../../docs/include/edge_set.edge_id_map.md")]
fn edge_id_map(&self) -> CompactIdMap
where
Self::EdgeId: IntegerIdType,
@@ -158,33 +216,47 @@ pub trait EdgeSet: GraphBase {
}
}
+/// Trait for read-only access to graph attributes.
+///
+/// # Implementation notes
pub trait GraphRef: VertexSet + EdgeSet {
+ /// Reference to a vertex.
type VertexRef<'a>: VertexReference
where
Self: 'a,
V: 'a;
+ /// Iterator over vertices.
type VerticesIter<'a>: Iterator>
where
Self: 'a,
V: 'a;
+ /// Reference to an edge.
type EdgeRef<'a>: EdgeReference
where
Self: 'a,
E: 'a;
+ /// Iterator over edges.
type EdgesIter<'a>: Iterator>
where
Self: 'a,
E: 'a;
+ #[doc = include_str!("../../docs/include/graph_ref.vertices.md")]
fn vertices(&self) -> Self::VerticesIter<'_>;
+
+ #[doc = include_str!("../../docs/include/graph_ref.edges.md")]
fn edges(&self) -> Self::EdgesIter<'_>;
+ #[doc = include_str!("../../docs/include/graph_ref.vertex.md")]
fn vertex(&self, id: &Self::VertexId) -> Option<&V>;
+
+ #[doc = include_str!("../../docs/include/graph_ref.edge.md")]
fn edge(&self, id: &Self::EdgeId) -> Option<&E>;
+ #[doc = include_str!("../../docs/include/graph_ref.find_vertex.md")]
fn find_vertex(&self, vertex: &V) -> Option
where
V: Eq,
@@ -199,15 +271,34 @@ pub trait GraphRef: VertexSet + EdgeSet {
}
}
+/// Trait for read-only access to graph attributes of potentially [implicit]
+/// graphs.
+///
+/// This trait should be preferred over [`GraphRef`] whenever possible, because
+/// it allows accepting a wider spectrum of graphs, including implicit ones.
+///
+/// [implicit]: https://en.wikipedia.org/wiki/Implicit_graph
+///
+/// # Implementation notes
pub trait GraphWeak: GraphBase {
+ #[doc = include_str!("../../docs/include/graph_weak.vertex_weak.md")]
fn vertex_weak(&self, id: &Self::VertexId) -> Option>;
+
+ #[doc = include_str!("../../docs/include/graph_weak.edge_weak.md")]
fn edge_weak(&self, id: &Self::EdgeId) -> Option>;
}
+/// Trait for mutable access to graph attributes.
+///
+/// # Implementation notes
pub trait GraphMut: GraphRef {
+ #[doc = include_str!("../../docs/include/graph_mut.vertex_mut.md")]
fn vertex_mut(&mut self, id: &Self::VertexId) -> Option<&mut V>;
+
+ #[doc = include_str!("../../docs/include/graph_mut.edge_mut.md")]
fn edge_mut(&mut self, id: &Self::EdgeId) -> Option<&mut E>;
+ #[doc = include_str!("../../docs/include/graph_mut.try_replace_vertex.md")]
fn try_replace_vertex(
&mut self,
id: &Self::VertexId,
@@ -222,6 +313,7 @@ pub trait GraphMut: GraphRef {
}
}
+ #[doc = include_str!("../../docs/include/graph_mut.replace_vertex.md")]
fn replace_vertex(&mut self, id: &Self::VertexId, vertex: V) -> V {
match self.try_replace_vertex(id, vertex) {
Ok(original) => original,
@@ -229,6 +321,7 @@ pub trait GraphMut: GraphRef {
}
}
+ #[doc = include_str!("../../docs/include/graph_mut.try_replace_edge.md")]
fn try_replace_edge(&mut self, id: &Self::EdgeId, edge: E) -> Result> {
match self.edge_mut(id) {
Some(slot) => Ok(mem::replace(slot, edge)),
@@ -239,6 +332,7 @@ pub trait GraphMut: GraphRef {
}
}
+ #[doc = include_str!("../../docs/include/graph_mut.replace_edge.md")]
fn replace_edge(&mut self, id: &Self::EdgeId, edge: E) -> E {
match self.try_replace_edge(id, edge) {
Ok(original) => original,
@@ -247,8 +341,14 @@ pub trait GraphMut: GraphRef {
}
}
+/// Trait for adding new vertices and edges to a graph.
+///
+/// # Implementation notes
pub trait GraphAdd: GraphMut {
+ #[doc = include_str!("../../docs/include/graph_add.try_add_vertex.md")]
fn try_add_vertex(&mut self, vertex: V) -> Result>;
+
+ #[doc = include_str!("../../docs/include/graph_add.try_add_edge.md")]
fn try_add_edge(
&mut self,
from: &Self::VertexId,
@@ -256,6 +356,7 @@ pub trait GraphAdd: GraphMut {
edge: E,
) -> Result>;
+ #[doc = include_str!("../../docs/include/graph_add.add_vertex.md")]
fn add_vertex(&mut self, vertex: V) -> Self::VertexId {
match self.try_add_vertex(vertex) {
Ok(id) => id,
@@ -263,6 +364,7 @@ pub trait GraphAdd: GraphMut {
}
}
+ #[doc = include_str!("../../docs/include/graph_add.try_get_or_add_vertex.md")]
fn try_get_or_add_vertex(&mut self, vertex: V) -> Result>
where
V: Eq,
@@ -273,6 +375,7 @@ pub trait GraphAdd: GraphMut {
}
}
+ #[doc = include_str!("../../docs/include/graph_add.get_or_add_vertex.md")]
fn get_or_add_vertex(&mut self, vertex: V) -> Self::VertexId
where
V: Eq,
@@ -283,6 +386,7 @@ pub trait GraphAdd: GraphMut {
}
}
+ #[doc = include_str!("../../docs/include/graph_add.add_edge.md")]
fn add_edge(&mut self, from: &Self::VertexId, to: &Self::VertexId, edge: E) -> Self::EdgeId {
match self.try_add_edge(from, to, edge) {
Ok(id) => id,
@@ -290,6 +394,7 @@ pub trait GraphAdd: GraphMut {
}
}
+ #[doc = include_str!("../../docs/include/graph_add.try_add_edge_connecting.md")]
fn try_add_edge_connecting(
&mut self,
from: V,
@@ -305,6 +410,7 @@ pub trait GraphAdd: GraphMut {
Ok(edge)
}
+ #[doc = include_str!("../../docs/include/graph_add.add_edge_connecting.md")]
fn add_edge_connecting(&mut self, from: V, to: V, edge: E) -> Self::EdgeId
where
V: Eq,
@@ -316,10 +422,17 @@ pub trait GraphAdd: GraphMut {
}
}
+/// Trait for removing vertices and edges from a graph.
+///
+/// # Implementation notes
pub trait GraphFull: GraphAdd {
+ #[doc = include_str!("../../docs/include/graph_full.remove_vertex.md")]
fn remove_vertex(&mut self, id: &Self::VertexId) -> Option;
+
+ #[doc = include_str!("../../docs/include/graph_full.remove_edge.md")]
fn remove_edge(&mut self, id: &Self::EdgeId) -> Option;
+ #[doc = include_str!("../../docs/include/graph_full.clear.md")]
fn clear(&mut self) {
let mut vertices = self.vertices_by_id().collect::>();
vertices.reverse();
@@ -329,15 +442,18 @@ pub trait GraphFull: GraphAdd {
}
}
+ #[doc = include_str!("../../docs/include/graph_full.remove_edges_between.md")]
fn remove_edges_between(&mut self, from: &Self::VertexId, to: &Self::VertexId) {
while self.remove_edge_any_between(from, to).is_some() {}
}
+ #[doc = include_str!("../../docs/include/graph_full.remove_edge_any_between.md")]
fn remove_edge_any_between(&mut self, from: &Self::VertexId, to: &Self::VertexId) -> Option {
let id = self.edge_id_any(from, to)?;
self.remove_edge(&id)
}
+ #[doc = include_str!("../../docs/include/graph_full.clear_edges.md")]
fn clear_edges(&mut self) {
let mut edges = self.edges_by_id().collect::>();
edges.reverse();
diff --git a/gryf/src/core/id.rs b/gryf/src/core/id.rs
index 8d7fcca..5ece970 100644
--- a/gryf/src/core/id.rs
+++ b/gryf/src/core/id.rs
@@ -1,3 +1,12 @@
+//! Traits and types used for identifying vertices and edges in graphs.
+//!
+//! All types that are supposed to be used as vertex/edge identifiers must
+//! implement [`IdType`] trait. For better performance and more functionality,
+//! they should also implement [`IntegerIdType`] if possible.
+//!
+//! The default ID types are [`VertexId`] and [`EdgeId`]. They are of size `u64`
+//! by default, but this can be changed via their generic parameter `N`.
+
mod compact_id_map;
pub use compact_id_map::CompactIdMap;
@@ -8,63 +17,101 @@ use super::borrow::OwnableRef;
/// A unique identification of a vertex or edge in a graph.
///
-/// In standard graph representations, the id type is an integer. Conceptually,
-/// such an integer id is of type `usize`, but one can choose a smaller integer
-/// type (such as u8 or u16) to lower the memory footprint. In these cases, the
-/// algorithms can treat the id as usize with all the benefits (e.g., indexing
-/// to a contiguous array).
+/// In standard graph representations, the ID type is an integer. Conceptually,
+/// such an integer ID is of type `usize`, but one can choose a smaller integer
+/// type (such as u8 or u16) to lower the memory footprint. In the cases of
+/// integer ID, the algorithms can treat the ID as usize with all the benefits
+/// (e.g., indexing to a contiguous array).
///
-/// For implicit graphs, an id can be of any form as long as it implements
-/// required interface and super traits. In general, such ids can't be treated
+/// For implicit graphs, an ID can be of any form as long as it implements
+/// required interface and super traits. In general, such IDs can't be treated
/// as integers and require a different handling, usually with overhead.
///
-/// Any id must also have a representation for a "sentinel" value. For integers,
+/// Any ID must also have a representation for a "sentinel" value. For integers,
/// we use the maximum value of the corresponding type for the sentinel, so we
/// don't introduce the overhead of using `Option` and can use 0 as the
-/// first index (as is natural).
+/// first index as is natural.
+#[doc(alias = "IndexType")]
pub trait IdType: Clone + Ord + Hash + Debug {
- /// Conceptually `None` in `Option`, but without using `Option`.
+ /// Conceptually `None` in `Option`, but without using `Option`.
fn sentinel() -> Self;
- /// Determines if the id type is `usize`-compatible.
+ /// Determines if the ID type is representable by an integer. See
+ /// [IntegerIdType] for more details.
///
- /// Types that are not `usize`-compatible require a special, often less
- /// efficient handling.
+ /// Types that are not integers require a special, often less efficient
+ /// handling.
fn is_integer() -> bool;
- /// Converts an id into the corresponding `u64`.
+ /// Converts an ID into the corresponding `u64`.
+ ///
+ /// # Panics
///
- /// Types for which `is_integer() == false` should panic in this function.
+ /// Types for which [`Self::is_integer`](IdType::is_integer) returns `false`
+ /// should panic.
fn as_bits(&self) -> u64;
- /// Converts an `u64` into the corresponding id.
+ /// Converts an `u64` into the corresponding ID.
+ ///
+ /// # Panics
///
- /// Types for which `is_integer() == false` should panic in this function.
+ /// Types for which [`Self::is_integer`](IdType::is_integer) returns `false`
+ /// should panic.
fn from_bits(bits: u64) -> Self;
- /// Converts an id into the corresponding `usize`.
+ /// Converts an ID into the corresponding `usize`.
///
- /// Types for which `is_integer() == false` should panic in this function.
+ /// # Panics
+ ///
+ /// Types for which [`Self::is_integer`](IdType::is_integer) returns `false`
+ /// should panic.
fn as_usize(&self) -> usize {
self.as_bits() as usize
}
- /// Converts an `usize` into the corresponding id.
+ /// Converts an `usize` into the corresponding ID.
+ ///
+ /// # Panics
///
- /// Types for which `is_integer() == false` should panic in this function.
- fn from_usize(index: usize) -> Self {
- Self::from_bits(index as u64)
+ /// Types for which [`Self::is_integer`](IdType::is_integer) returns `false`
+ /// should panic.
+ fn from_usize(id: usize) -> Self {
+ Self::from_bits(id as u64)
}
+ /// Returns `true` if the value represents the sentinel value.
fn is_sentinel(&self) -> bool {
self == &Self::sentinel()
}
}
-/// Type-level specification that an id type is integer-like.
+/// Type-level specification that an ID type is representable by integer.
+///
+/// Types that implement this trait must return `true` in [`IdType::is_integer`]
+/// and support all integer-related conversions.
///
-/// All types that implement this trait must return `true` in
-/// `IdType::is_integer`.
+/// All integer values up to some upper bound should be valid IDs and there
+/// should be no discontinuity. For example, check the two integer conversions
+/// of a chess square below, where one is correct and one is wrong.
+///
+/// ```
+/// struct ChessSquare {
+/// file: u8,
+/// rank: u8
+/// }
+///
+/// impl ChessSquare {
+/// fn as_bits_good(&self) -> u64 {
+/// // Continuous space up to 63
+/// (self.rank * 8 + self.file) as u64
+/// }
+///
+/// fn as_bits_bad(&self) -> u64 {
+/// // Discontinuity between (7, 0) == 7 and (0, 1) == 256.
+/// (self.rank as u64) << 8 | (self.file as u64)
+/// }
+/// }
+/// ```
pub trait IntegerIdType: IdType + Copy + From + Into {}
// For edge ids that are represented as a pair of vertex ids.
@@ -86,16 +133,56 @@ impl IdType for (T, U) {
}
}
+/// Used to support `I`, `&I` and `usize` as vertex/edge IDs in function
+/// arguments.
+///
+/// For integer ID types, passing the ID by value feels more natural and can
+/// possibly lead to a more efficient generated code. For non-integer ID types,
+/// cloning is an unnecessary and potentially costly operation and passing by
+/// reference is preferred.
+///
+/// Using `usize` as the ID value is supported mainly for convenience during
+/// graph building, but should be avoided after that.
+///
+/// Types in [`graph`](crate::graph) module use this trait for the ID parameters
+/// so that the following works:
+///
+/// ```
+/// use gryf::graph::Graph;
+///
+/// let mut graph = Graph::<_, (), _>::new_undirected();
+///
+/// let v = graph.add_vertex(42);
+///
+/// graph.vertex(v); // by value
+/// graph.vertex(&v); // by reference
+/// graph.vertex(0); // usize
+/// ```
pub trait AsIdRef {
+ /// Converts itself to the ID type `I`, either as owned value or a
+ /// reference.
fn as_id(&self) -> OwnableRef<'_, I>;
}
+/// The default representation of an integer index for vertices. Generic type
+/// `N` can be used to control the byte size of the backing integer (`u64` by
+/// default).
+#[doc(alias = "NodeIndex")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct VertexId(N);
+/// The default representation of an integer index for edges. Generic type `N`
+/// can be used to control the byte size of the backing integer (`u64` by
+/// default).
+#[doc(alias = "EdgeIndex")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EdgeId(N);
+/// An alternative representation of an real ID in specific contexts. It is
+/// always an integer type, even if the real ID is not.
+///
+/// In [`CompactIdMap`], it is used to represent a contiguous array of IDs of a
+/// graph, even if the numbering of vertices or edges in the graph has "holes".
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Virtual(u64, PhantomData);
@@ -143,11 +230,19 @@ impl From> for u64 {
impl IntegerIdType for Virtual {}
+/// Specification of vertex and edge ID types pair.
+///
+/// The main purpose is a reduction of the number of generic parameters from two
+/// to one (accepting the increase of associated types).
pub trait IdPair {
+ /// ID type for vertices.
type VertexId: IdType;
+
+ /// ID type for edges.
type EdgeId: IdType;
}
+/// Default indexing using [`VertexId`] and [`EdgeId`] as the ID pair.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum DefaultId {}
@@ -156,6 +251,25 @@ impl IdPair for DefaultId {
type EdgeId = EdgeId;
}
+/// Custom indexing using `VI` and `EI` generic types as the ID pair.
+///
+/// # Examples
+///
+/// ```
+/// use gryf::{
+/// core::id::{CustomId, EdgeId, VertexId},
+/// graph::Graph,
+/// storage::AdjList,
+/// };
+///
+/// let mut graph =
+/// Graph::new_directed_in(AdjList::with_id::, EdgeId>>());
+///
+/// let hello = graph.add_vertex("hello");
+/// let world = graph.add_vertex("world");
+///
+/// graph.add_edge(hello, world, ());
+/// ```
pub struct CustomId {
ty: PhantomData (VI, EI)>,
}
diff --git a/gryf/src/core/id/compact_id_map.rs b/gryf/src/core/id/compact_id_map.rs
index cb76549..eed6c67 100644
--- a/gryf/src/core/id/compact_id_map.rs
+++ b/gryf/src/core/id/compact_id_map.rs
@@ -2,10 +2,20 @@ use std::cmp::min;
use crate::core::id::{IdType, IntegerIdType, Virtual};
-// For compact storages, the space and time for both directions is constant (use
-// `isomorphic`). For storages with holes, the space is O(|V|), virtual to real
-// is O(1) and real to virtual is O(log(|V|)) with heuristics that make it O(1)
-// in many cases.
+/// Mapping from graph IDs to a contiguous sequence of [virtual](Virtual) IDs
+/// that can be used in algorithms.
+///
+/// For compact storages (e.g., [`AdjList`](crate::storage::AdjList)) that do
+/// not have any holes in ID sequences even after removing, the mapping is a
+/// noop and doesn't take any memory. Use [`CompactIdMap::isomorphic`]
+/// constructor in these cases.
+///
+/// For storages with holes and _N_ vertices or edges, the time and space
+/// properties are:
+///
+/// * memory used: _O(N)_
+/// * virtual to real mapping: _O(1)_
+/// * real to virtual mapping: _O(log(N))_
#[derive(Debug)]
pub struct CompactIdMap {
map: Vec,
@@ -13,6 +23,7 @@ pub struct CompactIdMap {
}
impl CompactIdMap {
+ /// Constructs the map from the iterator of IDs.
pub fn new(iter: A) -> Self
where
A: Iterator,
@@ -24,6 +35,7 @@ impl CompactIdMap {
Self { map, len }
}
+ /// Constructs a noop map where real IDs are already in contiguous sequence.
pub fn isomorphic(len: usize) -> Self {
Self {
map: Vec::new(),
@@ -31,22 +43,27 @@ impl CompactIdMap {
}
}
+ /// Returns the number of IDs in the map.
pub fn len(&self) -> usize {
self.len
}
+ /// Returns `true` if the map contains no IDs.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
+ /// Returns `true` if the mapping is noop.
pub fn is_isomorphic(&self) -> bool {
self.map.len() != self.len
}
+ /// Maps given virtual ID to the corresponding real ID in the original graph.
+ ///
+ /// `Into>` is used instead of `Virtual` because the
+ /// algorithms often work with `usize` and that can be used instead of
+ /// explicitly constructing the `Virtual` type.
pub fn to_real>>(&self, id: V) -> Option {
- // Into> is used instead of Virtual because the algorithms
- // will usually work with numeric ids with their data structures and so
- // it is more convenient to use this.
let id: Virtual = id.into();
if self.is_isomorphic() {
@@ -56,6 +73,8 @@ impl CompactIdMap {
}
}
+ /// Maps given real ID from the original graph to a virtual ID in the
+ /// contiguous sequence.
pub fn to_virt(&self, id: I) -> Option> {
if self.is_isomorphic() {
(id.as_usize() < self.len()).then(|| Virtual::from_bits(id.as_bits()))
diff --git a/gryf/src/core/marker.rs b/gryf/src/core/marker.rs
index 0e22ad3..d7fcdff 100644
--- a/gryf/src/core/marker.rs
+++ b/gryf/src/core/marker.rs
@@ -1,12 +1,28 @@
+//! Traits and types representing basic properties of graphs and related types.
+
+/// Edge direction with respect to a vertex.
+///
+/// The variants are exported to the scope so that they can be used directly
+/// like [`gryf::core::marker::Outgoing`](crate::core::marker::Outgoing).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
+ /// The edge is directed away from a vertex.
Outgoing,
+
+ /// The edge is directed towards a vertex.
Incoming,
}
pub use Direction::*;
impl Direction {
+ /// Returns the index value representing the direction.
+ ///
+ /// This is useful for storing data specific to a direction into a
+ /// two-element array.
+ ///
+ /// * `Outgoing` → 0
+ /// * `Incoming` → 1
#[inline]
pub fn index(&self) -> usize {
match self {
@@ -15,6 +31,8 @@ impl Direction {
}
}
+ /// Parses the direction from the index value produced by
+ /// [`Direction::index`].
#[inline]
pub fn from_index(index: usize) -> Self {
if index % 2 == 0 {
@@ -24,6 +42,10 @@ impl Direction {
}
}
+ /// Returns the opposite direction.
+ ///
+ /// * `Outgoing` → `Incoming`
+ /// * `Incoming` → `Outgoing`
#[inline]
#[must_use]
pub fn opposite(&self) -> Self {
@@ -34,14 +56,28 @@ impl Direction {
}
}
+/// Represents undirected graphs.
+///
+/// Check [`EdgeType`] for more details.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Undirected {}
+/// Represents directed graphs.
+///
+/// Check [`EdgeType`] for more details.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Directed {}
+/// Directionality of the graph. Can be either [undirected](Undirected) or
+/// [directed](Directed).
pub trait EdgeType: private::Sealed + 'static {
+ /// Returns `true` if the graph is directed.
fn is_directed() -> bool;
+
+ /// Returns the array of relevant [directions](Direction) for the directionality.
+ ///
+ /// * `Undirected` → `[Outgoing]`
+ /// * `Directed` → `[Outgoing, Incoming]`
fn directions() -> &'static [Direction];
}
diff --git a/gryf/src/core/matrix.rs b/gryf/src/core/matrix.rs
index 786a375..38d36ab 100644
--- a/gryf/src/core/matrix.rs
+++ b/gryf/src/core/matrix.rs
@@ -1,14 +1,42 @@
+//! Helper utilities for managing an adjacency matrix representation.
+
use crate::core::marker::EdgeType;
+/// Underlying storage for the adjacency matrix.
+///
+/// This trait allows different "backends" for an adjacency matrix abstraction
+/// tailored to a specific use case (e.g., bit vector for binary adjacency
+/// matrix or special representations for large sparse matrices).
#[allow(clippy::len_without_is_empty)]
pub trait MatrixLinearStorage: Default {
+ /// Initializes the storage with given capacity for entries.
+ ///
+ /// Note that the entries capacity value of the _linear_ storage is
+ /// expected, not a higher-level value like the number of vertices. It is
+ /// the responsibility of the caller to calculate the appropriate capacity
+ /// for given number of vertices.
fn with_capacity(capacity: usize) -> Self;
+
+ /// Resizes the storage to the new length. If the new length is higher than
+ /// previous, "None" is used for filling the new entries.
fn resize_with_none(&mut self, new_len: usize);
+
+ /// Appends the edge attribute to the back of the storage.
+ ///
+ /// The storage is automatically resized if is at full capacity before
+ /// appending the edge.
fn push(&mut self, value: Option);
+
+ /// Returns the number of entries in the storage.
fn len(&self) -> usize;
+
+ /// Returns the iterator over all entries in the storage, being it `Some(E)`
+ /// or `None`.
fn into_entries(self) -> impl Iterator>;
}
+/// Calculates the linear storage capacity needed to store a certain number of
+/// vertices, respecting the graph directionality.
pub fn linear_len(vertex_capacity: usize) -> usize {
if Ty::is_directed() {
vertex_capacity * vertex_capacity
@@ -17,6 +45,11 @@ pub fn linear_len(vertex_capacity: usize) -> usize {
}
}
+/// Resizes the underlying matrix storage to a new capacity to hold a certain
+/// number of vertices, respecting the graph directionality, while correctly
+/// shifting existing entries to appropriate place.
+///
+/// If the new length is lower than the current length, nothing is done.
pub fn resize>(prev: &mut M, vertex_capacity: usize) {
let prev_len = prev.len();
let len = linear_len::(vertex_capacity);
@@ -51,6 +84,8 @@ pub fn resize>(prev: &mut M, vertex_c
}
}
+/// Returns the (linear) index of a matrix element coordinates, respecting the
+/// directionality of the graph.
pub fn index(row: usize, col: usize, vertex_capacity: usize) -> usize {
if Ty::is_directed() {
row * vertex_capacity + col
@@ -62,6 +97,8 @@ pub fn index(row: usize, col: usize, vertex_capacity: usize) -> us
}
}
+/// Returns the matrix element coordinates from the (linear) index, respecting
+/// the directionality of the graph.
pub fn coords(index: usize, vertex_capacity: usize) -> (usize, usize) {
if Ty::is_directed() {
let col = index % vertex_capacity;
diff --git a/gryf/src/core/props.rs b/gryf/src/core/props.rs
index c2a2941..45463d3 100644
--- a/gryf/src/core/props.rs
+++ b/gryf/src/core/props.rs
@@ -1,14 +1,28 @@
+//! Traits representing semantic properties of graphs.
+
use super::id::IdType;
+/// Type-level information that the implementing graph supports multiple edges
+/// between two vertices.
pub trait MultiEdge {}
+/// Represents the level of ID stability.
+///
+/// _Stable IDs_ means that the IDs of existing vertices or edges don't change
+/// even when other vertices or edges are removed from the graph. This is a
+/// property of the graph (storage). It is important for cases which depend on
+/// stability of IDs kept around.
pub trait Stability: private::Sealed + 'static {
+ /// Returns true if the stability level allows to replace IDs of removed
+ /// elements with _new_ elements.
fn can_replace_removed() -> bool;
}
+/// [Stability] level disallowing any assignment of IDs of removed elements.
#[derive(Debug, Clone, Copy)]
pub enum NoReplace {}
+/// [Stability] level allowing assigning IDs of removed elements to new elements.
#[derive(Debug, Clone, Copy)]
pub enum ReplaceRemoved {}
@@ -24,36 +38,84 @@ impl Stability for ReplaceRemoved {
}
}
+/// Type-level information that the implementing graph has stable IDs for given
+/// ID type (vertices or edges) with given [stability](Stability) level.
pub trait StableId {}
+/// Trait for various graph properties known at compile-time.
+///
+/// Compile-time properties can be utilized for performance-oriented choices
+/// that can exploit the properties. For example, there can be a more efficient
+/// algorithm that works only for a specific class of graphs.
+///
+/// The default implementation returns `false` for all properties which should
+/// always be a safe option.
pub trait Guarantee {
+ /// Returns `true` if the graph does not contain loops (edges from a vertex
+ /// to itself).
+ ///
+ /// Note that loops are not [cycles].
+ ///
+ /// [cycles]: https://en.wikipedia.org/wiki/Cycle_(graph_theory)
fn is_loop_free() -> bool {
false
}
+ /// Returns `true` if the graph contains only [path] subgraphs (one or
+ /// more).
+ ///
+ /// Note that the graph doesn't need to be
+ /// [connected](Guarantee::is_connected).
+ ///
+ /// [path]: https://en.wikipedia.org/wiki/Path_(graph_theory)
fn has_paths_only() -> bool {
false
}
+ /// Returns `true` if the graph contains only [tree] subgraphs (one or
+ /// more).
+ ///
+ /// Note that the graph doesn't need to be
+ /// [connected](Guarantee::is_connected).
+ ///
+ /// [tree]: https://en.wikipedia.org/wiki/Tree_(graph_theory)
fn has_trees_only() -> bool {
// Paths are also trees by definition.
Self::has_paths_only()
}
+ /// Returns `true` if the graph contains only [bipartite] subgraphs (one or
+ /// more).
+ ///
+ /// Note that the graph doesn't need to be
+ /// [connected](Guarantee::is_connected).
+ ///
+ /// [bipartite]: https://en.wikipedia.org/wiki/Bipartite_graph
fn has_bipartite_only() -> bool {
// Paths and trees are bipartite by definition.
Self::has_paths_only() || Self::has_trees_only()
}
+ /// Returns `true` if the graph is connected, that is, there exists a path
+ /// between any two vertices.
fn is_connected() -> bool {
false
}
}
+/// Trait for controlled construction of constrained classes of graphs.
pub trait Constrained {
+ /// Error representing the violated semantic property of the graph class.
type Error;
+ /// Returns `Ok` if the passed graph satisfies all semantic properties of
+ /// the graph class. Otherwise, returns an error corresponding to the
+ /// violated property.
fn check(graph: &G) -> Result<(), Self::Error>;
+
+ /// Returns `Ok` with the graph class wrapper if the passed graph satisfies
+ /// all semantic properties of the graph class. Otherwise, returns an error
+ /// corresponding to the violated property.
fn constrain(graph: G) -> Result
where
Self: Sized;
diff --git a/gryf/src/core/weight.rs b/gryf/src/core/weight.rs
index 5bcee21..f23c625 100644
--- a/gryf/src/core/weight.rs
+++ b/gryf/src/core/weight.rs
@@ -1,3 +1,10 @@
+//! Traits and types for edge weights.
+//!
+//! `gryf` distinguishes between _attributes_ (any data) and _weights_
+//! (measurable and comparable values). This distinction makes the usage
+//! flexible where the weight function can be different even if the graph
+//! attributes are the same.
+
use std::{cmp::Ordering, ops::Add};
mod ordered_float;
@@ -6,29 +13,68 @@ mod unsigned_float;
use ordered_float::OrderedFloat;
pub use unsigned_float::*;
+/// Type that can be treated as a weight.
+#[doc(alias = "Measure")]
pub trait Weight: PartialOrd + Add + Clone + Sized {
+ /// Associated type that is equivalent to `Self` but implements the [`Ord`]
+ /// trait as the weight itself is required to implement only [`PartialOrd`].
+ ///
+ /// The main and only motivation for this extra level of abstraction is to
+ /// have convenient support for floats from the standard library that don't
+ /// implement [`Ord`]. It's a compromise between not requiring users to use
+ /// special types or wrappers in their graphs and giving the possibility to
+ /// use `Ord`-based functions and data structures for algorithm developers.
type Ord: Ord + From + Into;
+ /// Returns value that represents the weight equal to zero.
fn zero() -> Self;
+
+ /// Returns value that represents the weight equal to infinity.
fn inf() -> Self;
+
+ /// Returns `true` if the weight is guaranteed to be unsigned at compile
+ /// time.
+ ///
+ /// This information can be utilized for performance-oriented choices that
+ /// can exploit it. For example, there can be a more efficient algorithm
+ /// that works only for unsigned weights.
fn is_unsigned() -> bool;
}
+/// Mapping from an edge attribute to corresponding edge weight.
+///
+/// The list of mappings supported out of the box:
+///
+/// * [identity](Identity): `E → E`
+/// * [unit](Unit): `E → 1`
+/// * [from weighted](FromWeighted): `(E, W) → W`
+/// * anything that implements `Fn(&E) -> W`
pub trait GetWeight
where
W: Weight,
{
+ /// Maps the edge attribute to the corresponding weight.
fn get(&self, edge: &E) -> W;
+ /// Returns the weight regardless of the edge attribute, if it's the same
+ /// for all edges.
+ ///
+ /// Algorithms should try this first before calling [`get`](GetWeight::get)
+ /// because does less work in cases where the weight is known (e.g.,
+ /// [unit](`Unit`) weight).
fn get_const(&self) -> Option {
None
}
+ /// Returns `true` when the mapping represents a constant weight, that is,
+ /// [`get_const`](GetWeight::get_const) always returns `Some`.
fn is_const(&self) -> bool {
self.get_const().is_some()
}
}
+/// Type-level information that the [`GetWeight::get_const`] always returns
+/// `Some`.
pub trait IsConstWeight {}
impl GetWeight for F
@@ -41,6 +87,7 @@ where
}
}
+/// Identity mapping `E → E` used when the edge attribute is the weight itself.
#[derive(Debug)]
pub struct Identity;
@@ -67,6 +114,7 @@ impl GetWeight<(), u8> for Identity {
}
}
+/// Unit weight `E → 1` used when the edge weight doesn't matter.
#[derive(Debug)]
pub struct Unit;
@@ -82,6 +130,8 @@ impl GetWeight for Unit {
impl IsConstWeight for Unit {}
+/// Mapping from tuple `(E, W) → W` used when the weight is stored together with
+/// the edge attribute in the graph.
#[derive(Debug)]
pub struct FromWeighted;
@@ -94,6 +144,7 @@ where
}
}
+/// Helper type for holding the weight together with the edge attribute.
#[derive(Debug, Clone, Copy)]
pub struct Weighted(pub T, pub W);
diff --git a/gryf/src/core/weight/unsigned_float.rs b/gryf/src/core/weight/unsigned_float.rs
index 4f5cb4b..97febf6 100644
--- a/gryf/src/core/weight/unsigned_float.rs
+++ b/gryf/src/core/weight/unsigned_float.rs
@@ -85,6 +85,9 @@ macro_rules! impl_binop {
macro_rules! declare_unsigned_float {
($name:ident, $ty:ty, $int_ty:ty) => {
+ #[doc = r"Unsigned variant of ["]
+ #[doc = stringify!($ty)]
+ #[doc = r"] type, useful for [is_unsigned](super::Weight::is_unsigned) property."]
#[allow(non_camel_case_types)]
#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
#[repr(transparent)]
diff --git a/gryf/src/graph.rs b/gryf/src/graph.rs
index fcc7c9d..72c3bbf 100644
--- a/gryf/src/graph.rs
+++ b/gryf/src/graph.rs
@@ -1,3 +1,8 @@
+//! Collection of graph storage encapsulations with high-level API.
+//!
+//! These types usually wrap a [storage](crate::storage) and provide additional,
+//! higher-level semantics and type-level guarantees.
+
mod generic;
mod path;
diff --git a/gryf/src/graph/generic.rs b/gryf/src/graph/generic.rs
index 17cee69..fb4095c 100644
--- a/gryf/src/graph/generic.rs
+++ b/gryf/src/graph/generic.rs
@@ -25,6 +25,32 @@ use gryf_derive::{
VertexSet,
};
+/// Graph without any constraints.
+///
+/// # Examples
+///
+/// ```
+/// use gryf::{core::marker::Outgoing, graph::Graph};
+///
+/// let mut graph = Graph::new_directed();
+///
+/// let a = graph.add_vertex("a");
+/// let b = graph.add_vertex("b");
+/// let c = graph.add_vertex("c");
+/// let d = graph.add_vertex("d");
+///
+/// graph.extend_with_edges([
+/// (a, b, 1.0),
+/// (b, c, 2.7),
+/// (c, d, 13.0),
+/// (d, a, 0.99),
+/// (b, d, -3.0),
+/// ]);
+///
+/// assert_eq!(graph.vertex_count(), 4);
+/// assert_eq!(graph.edge_count(), 5);
+/// assert_eq!(graph.degree_directed(d, Outgoing), 1);
+/// ```
#[derive(
Debug,
Clone,
@@ -47,16 +73,19 @@ pub struct Graph> {
}
impl Graph {
+ /// Creates an empty graph.
pub fn new() -> Self {
Self::new_in(AdjList::new())
}
+ /// Creates an empty graph with given capacities.
pub fn with_capacity(vertex_capacity: usize, edge_capacity: usize) -> Self {
Self::new_in(AdjList::with_capacity(vertex_capacity, edge_capacity))
}
}
impl Graph {
+ /// Creates an empty undirected graph.
pub fn new_undirected() -> Self {
Self::new()
}
@@ -66,12 +95,14 @@ impl Graph
where
G: GraphBase,
{
+ /// Creates a new undirected graph wrapping given storage.
pub fn new_undirected_in(storage: G) -> Self {
Self::new_in(storage)
}
}
impl Graph {
+ /// Creates an empty directed graph.
pub fn new_directed() -> Self {
Self::new()
}
@@ -81,6 +112,7 @@ impl Graph
where
G: GraphBase