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 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO2dV1PjyFx1MDAxNoDf91dMsa+LbuewbzhcdTAwMTCHZNLA1i2XsIUtcMKBMLf2v99uXHUwMDAzVmrJkoPQ1GBXTVxysixcdTAwMWa3TvjO6dPt//3x7dvG+HXgbPz9bcN5adhcdTAwMWS3ObSfN/7Sx5+c4cjt99RLaPr3qD9cdTAwMTk2pme2x+PB6O///MdcdTAwMWVcZizvXVaj3317p9Nxuk5vPFLn/qP+/vbtf9N/1StuU7+/WapcXI1cdTAwMGX3R7RbI/Y9rdz06GRz+tbpSVx1MDAxZlx1MDAwMlxyncbY7rU6jvfSizpcdTAwMGUhg7NcdTAwMDOv6lx1MDAwMGV49vez21x1MDAxY7f1SVx1MDAwNFjS/yCzU9qO22qP1TmMWsD/8K769ql/f1x1MDAwM7Mjo/Gw/+CU+53+UIv2J5RcdTAwMWM2kCfYrd14aFxy+5Nec3bOeGj3Rlx1MDAwM3uoRsI7787tdM7Gr9Orq1x1MDAxMVUjt1x1MDAxMfqMq/dvgELH496lPrTV7jkjPdreN+hcdTAwMGbshjvWo1x1MDAwM4H3LbSEg73m9Mb815NpaHedPX1nepNOZ3bY7TVcdTAwMWQ93lx1MDAxYjZcYnxar/n+aVx1MDAxZnfVu2X4/ci/nuyOoy9MXHUwMDExl1x1MDAxMEHAZy942lx1MDAwNTFcZlx1MDAxZj3q96aapmRHXHUwMDFjXCLBqSfVqKLUazy96p3dXHUwMDE5Od4t0KJVw6rnV7+Ado2dXHUwMDE37774lHNz77Q62dx8qZ43m/X6/fH9T8nvN2bn/fuX+bJvb665nYPXc37x4yd5fXg5/l7Zv7x+XG5+ysfn28Nh/zntdeVz97CKX8uPz1X09H1Yvrhqt6rprvv+P+92T1x1MDAwNk37bfwgR1hcci+hhEo5e73j9lx1MDAxZcK60Ok3XHUwMDFlvCH/wydwyLTNo1x1MDAxNzHtwOC/W7VAXHUwMDE2oEHDXHUwMDE2UcOWwpJRU0b0y3bNtlx1MDAwYs22XHUwMDFiOPvdSCVcIlx1MDAxYyhrNFx1MDAxYSmKNVJEXGKRXHUwMDEwXHUwMDEyuICNXHUwMDA2xIgoJsSUS5RBMT090/qlvn5paI/dUcd+sn23sd9cdTAwMWKfuT+nooPA0W2763amilx1MDAxN7jQVsdt6THYaCipneGGfyDGrlxuf7NcdTAwMTO6brPpXHUwMDBmV1xydVHb7TnDvTRxrz90W27P7pwnXGJvT8b9mjN6XHUwMDEzfzycOP7RcXY/7Fx1MDAwMVqIXHUwMDFhrTQhtno3W8Q7ZMlcdTAwMTjHgPpipadqPpOK14A3N1E6uuCVSq9MNunVgdORt/3OznNRLC3qQfT341x1MDAxMlhB96QsxVx1MDAxMth7ILZyl7NcdTAwMWGcebdvqFxmlVx1MDAwYoyI51UzOZWssX9FXHUwMDAx+fpcdTAwMTJcdTAwMWVU6MvDvri4PD45vG/s19yntIFzz2mNJu3e9SGhT9dP1Vx1MDAxZj9OjuFoXHUwMDA1XHUwMDAxeV2BvtZ/OqtIvvn4Mlx1MDAxOJb5mbxrnVFUrEBvjuGpvFx1MDAwNydYMCakyXvg9N7DrFx1MDAxMYX2XHUwMDFlXHUwMDAycYtcdTAwMDKft1xielx1MDAxMpybJ2HMkv6P8lx1MDAwNDHB1IfnkETJLlx1MDAxMfS+/MKeI/BCxEUkai/AmGTQ3sUjvlx1MDAxZcWTod2a+MLjSklgTvxcdTAwMGKTQFiWXFwoXHUwMDAwolxiXGJ+XHUwMDE4slx1MDAwNFx1MDAxMiApvNc9MybpzXhSOlx1MDAxZXc3+yUhWk83L+dHI3HwXFwrtlx1MDAxOStEXHUwMDBlQVx1MDAwMCVB08UhWYrGXHUwMDAwXHUwMDFjKO/CXHUwMDEwXYEl58lcdTAwMDDnP7vN7cH+gOzYT/zsrlLe5d9zSJ5cdTAwMTOvW59UqFxyzmmj+nJwu9c4q/Sve3efy1x1MDAwMInXvbwtV+rD8U+Ka+f7Qrw2L1x1MDAxZeuvv1x1MDAxNlskpKFQUiBcdTAwMTlcdTAwMDe+moHnlWhcdTAwMDa4MKpasb1cdTAwMTIjXHUwMDE2p55cdTAwMTfyPvmtPJqXh2LAXCKBTzJ4KFx1MDAwM1xcQMqRWEVSslx1MDAxY1rwXGbKu1x1MDAxY1pcXLpOr2evXHQt5kTVMFqEZclcdTAwMDctgIgzY51cdTAwMWVIxKDJill6K1x1MDAxZWyDevn5uH+AXHUwMDA2d9uXVz97O+T0othWTHWKXHUwMDEwtFxcXHUwMDBlfym2IIxKijn/tdiiXd5cdTAwMWK47ilvXHUwMDFlg0mJwdJcdTAwMGYyLPfTxtRcdTAwMWZcdTAwMDePo9tqrTo5/DnCm49Xz1x1MDAwZvDiscBssXOyNThcdTAwMWPvXFzUXHUwMDA0OH788aIg/vr75S/GXHUwMDAwXHUwMDAwh4/OXHUwMDE4gEnIMFx1MDAxNj4hPO/B03tcdTAwMGazSlx1MDAxNNt7qMRehTH14FxmSSxDnkTm5UlcdTAwMTi2gHYggnCiZ/xMnsTAXHUwMDAwVDBdYeDejfskXG5cdTAwMTBcdTAwMTnUdzlcbjic9NxGe01cdTAwMTQwJ/6FKSAsSz5cdTAwMTTgXHUwMDAz1fCcXHUwMDEyXCJcdTAwMTJcdTAwMDOOTYVCkYFcdTAwMDLqz1uvZNy8P92qXHUwMDBmXHUwMDFlJ6Xa+XZrVGw7JpJbkPlcdTAwMWWhSiGPfbmYTKB8McdcdTAwMTiAX2vOgbedvYf9cke6u7btVlx1MDAxZrdPXkfnnz3nsFx1MDAwNGvMj92ccOar3K8rdif0enCVXHUwMDAzXG7o11x1MDAxNM/mZXqbN9+6Qts8hdLi2pipsjkuOFx1MDAwZpp8vEdYtclLalx0bcZcdTAwMThQyCn36UNC7Fx1MDAxNkRcdTAwMDDiq1x1MDAxNedcdTAwMWa4M+vucoH7aDJ0urfOsLWu2J1cdTAwMWO1wrHbIE4+4ZvS8NHZ/Fx1MDAwMFx1MDAxNFx1MDAxYzFcdTAwMTOEb6U35OPy01x1MDAxZFx1MDAxYT53XHUwMDFmXFx5XXJbXHUwMDEw4dpZqdiGzIBcZqXwnFxu35FihmhcdTAwMDYgXHUwMDExXHUwMDEwrcKEc1xm0dvtXHUwMDFlPL1cdTAwMWVcItKv7z6MXsfDauf1NIc0OPG69UewN+KVyU7/8qz/vcfLN6c37lfp/s1dxKbtiFwixFx1MDAxOKCmyF9K7zDMXHUwMDFhUWyHoVxmXHUwMDE4+Cr3nv1OnVx1MDAwN1+n8+DSXHUwMDEymTtcdTAwMDGwYISLlUxcdTAwMWYuXHUwMDFj7lx1MDAxN29cdTAwMGJcXCjcb6vBdLSirifaz1x0c+FoXHUwMDFmlSanZoDYYFx1MDAwZoHkilx1MDAxOFx0NCXr5fTme9m67pT5K75kt6P6YW+72r23XHUwMDBmXG5uvjJcdTAwMWPvZaCIX8h4LyRcdTAwMDXa6/9aXHUwMDE5eU06XHUwMDA37tVZv8tqk9b5w/XpzW2lk0NYLkz49DmskFx1MDAwMVx1MDAxMiio7ukw8XYlvf2ZR7jQ9sdcdFHKXHUwMDFl11UnyTptkcJQI126QjdgXHUwMDEyXHUwMDEzScln5ss5XHUwMDA30Fq/u67gOSdmhINnUJJlXHUwMDAzZ5pqX8ivXHUwMDA0tVdcdTAwMTBcdTAwMGIjXHUwMDAwqURcdTAwMDJApVx1MDAxM1x1MDAwMe3FKnPEXFx4T0M0QFx1MDAxNkFcZkEpXHUwMDA0Q5RBXHUwMDFh1UB1XG4gXHUwMDFjqjRUUIiZr8lh/jpcdTAwMTl0J1x1MDAxZEKyW0h6T0FCxz9xnUx1w2hj0Vxih1wiXHUwMDEx7t20iVB3QPBohXNjTt9cdTAwMTLiUt1ov/te91x1MDAxYbfW/bh99bRzOFx1MDAxMre1nUP3XHUwMDA1yJObaqagJyBcdTAwMDR0oY6ZQd9ccsv+j09C4Fx1MDAxN1x1MDAxN8z+/9+/jGdvxtuAfkS037te5Ct27NG43O923bH6oidayPBcdTAwMTdcdTAwMWGN7eG4pFTG7bWCqvG+4DVNb+/UXTYmelx1MDAwMICFOOaAUCAxRlx1MDAxMlxi4TupZVx1MDAwZjTMWZBLQVx1MDAxNK9JoU5cZpxx575cdTAwMDRcdTAwMDWNKKbTa85cdTAwMTc3udpcdTAwMThcdTAwMTRcdTAwMTdzJa5CSqGZXHUwMDA3Slx1MDAxNpFcdTAwMTdbXHUwMDFjeU9cdTAwMDFoJnmnXHUwMDAzvKVcdTAwMWRl27Ejtqq+Tfxrndv+c6pcdTAwMDWKZtWPuOvoXHUwMDAyRY6pXHUwMDA1MaZcYlx1MDAwYlx1MDAwNVx1MDAxNlxmhry1ylx1MDAwNHzRflx1MDAwNlx01FwiunavLFxcIIyIXHUwMDE3dD1GSIKc/F1ygZYuVi/NPtm0dlx1MDAxMWLAOVK2XHUwMDFlSVOngTI+d1x1MDAwNURKjHzueUWLXHUwMDE3oVT3XS7UTfDBTUjyb1x1MDAwZl3fTfTgXHUwMDBissDRdS9cXJzDOGHMXG5cdTAwMGK+XHUwMDFh0EpecpVcdTAwMDRaXHUwMDEyXHUwMDEyi1xiqPwsxZRcdTAwMDHqRdqp6WpcZjMvvvkwY6SSaqq8nlx1MDAwNIRcdTAwMTEsXHTwTpnZMWTc8s/ScU/pvpYkXHUwMDA37Hp7adRS0KxsXHUwMDE2Y25iLVx1MDAxYYtaXHUwMDFjXG7EqfT5iLWT1pDVcXk46Te26jv34+9X53fHL/WM5Vx1MDAwNYAx81vMp5BWvFx06EdU+Vx1MDAwYkZaXHUwMDAwIKngULt7lVhhXHUwMDAyo+xcdTAwMDKtbLSSiq6Sl3xcdTAwMDfpXG5MM1x1MDAwMSaQVm7h7ZXhSVxiM1x0mFx1MDAwN06Z9TtcdTAwMDVOQZXxKp6SVM/fKzpcbm7pQnw+esZSzGK6wVx1MDAxMVKk0iwhTU44XHUwMDEzTP1eTjdcdTAwMDNM6U5cdTAwMTRCmVx0pWT42IykhG48VXnWqkFqXHUwMDA1u0BgJFxuXHUwMDAyUnNcdTAwMTgmXGZSYcFXXHUwMDAzUsnr4ZJAXG4q21x1MDAwYtgp9d2YXHUwMDBmOzV1XHJRiyuVkiqHUlx1MDAwNkxcYvNG/ctMXHUwMDAzZrq7NFx1MDAxYjGm+6HMaERiV/TqXHUwMDFlXHUwMDEwKlx1MDAxOM5cdTAwMTGNYGvv+6TBq7y7u9NcdTAwMTKt2sVRd3yWXHUwMDFkjVxuUIRcdTAwMTLQf/5mRNnzYKHUoLGpYEjdbahcdTAwMTIhpmhDkVx1MDAxMIzWnaT/jashoeR1aUFYg1x1MDAxOFx1MDAwMqRSaKKSNVx1MDAwMKTX8ThcdTAwMTOQZoS1PFjIrNCpWEhSXHUwMDBiTZNPRTaMsPAyMP8s84ebJcSi03RV6G2bODC1b3zRUJybzVJaonp6XHUwMDE1SmNhP3ZcdTAwMWQjpLpcdTAwMWWFOCpcIlx1MDAxMHFZXHUwMDEwXHUwMDFlmoNcImFcdTAwMWVcbsm9XHUwMDFhXHUwMDFjSm6rT8IhyoElOZJA2SBXLjVYV1wiRFiU+57Rulx1MDAxMmRcdTAwMTZXd1x1MDAxM1xuSVXAoMhUVkLQXCLC98xSVvqtZvD2li8rMcqkulxyxlx1MDAxYXJ0W7xcdTAwMTk7XHUwMDExKFx1MDAwNFx1MDAwN3SRXHUwMDFh8oLsRG+e97c2sV26uqzvbJ7tXlx1MDAxZJZcdTAwMWVG2dZ7MFx1MDAwNshC5emVslOsXHUwMDA1TF+M6H5cdTAwMWUklWFCXGYgjlx1MDAwMVDcLLlgXHUwMDA0UW9cbv+bN4NHgPSeeFxyNabktX4h9OOEceWZOJRcdTAwMTJhwWREYGhx/1x1MDAxY56X8Fx1MDAxNYazzMqfgrNcdTAwMTjEXHUwMDE2mM7HXHUwMDAxXGaE8K31nIY40/xcdTAwMWS05Nf83aJcdTAwMGU5XHUwMDAzZFx0lS5xio01J1x1MDAxONv5pt6h0FmQlTNcdTAwMTaUXHUwMDEwXHUwMDEzvlx1MDAxNGNBXHUwMDBlXG5cdTAwMDJZc1x1MDAwMCdcZllhwVdDWclcdTAwMWJcdTAwMWEkz95cdTAwMDXXw1EhdJdcdTAwMDEhKm1cdTAwMDJcbr2iVuvvbfVAiltcXPe9UlxyapIpc/5Kjsx2u780SFx0QdVdIzCy4kNcdTAwMTNjfCM5plx1MDAxMmBcdGCOnVCVw/ZcdTAwMDOub1xyXHUwMDBmNrfEnruFXt2zXHUwMDA2zl6EytL+u1x1MDAxZZDa9Cm9fkTVPVx1MDAwZnZKXeTRKKKyWJVWXHUwMDBiKJFeXHUwMDAxoygqwlwifFx1MDAxZFNyqXFJT8khyYGAalx1MDAxOKVUjFx1MDAxN6WlXCLWocw6nYKPOONcdTAwMTac8lx1MDAxMYFcYlNcdTAwMWMqQym/K1x1MDAwNWNYjcq07Fx1MDAxNKUlXHLzmpYgp0iqoVuSln4vr5ulJKVX21x1MDAwMG04XHUwMDA2XHUwMDA3XHUwMDFiO0XHVXKLfTelOPUoXHUwMDAyUEFYaVx1MDAwZaaEWSks+GpYKXl1alx1MDAxMiupnN5cdTAwMTJcdTAwMThJhlx1MDAwMdPOP9ikqFdcdTAwMWL6O52iv65AhFx1MDAwNVx1MDAxOVx1MDAwN5hJZeCcUGaqSEGweKPTb1WROlhcdTAwMWGkIOdEqNjNTSSF4ovPiDBKOCSLXHUwMDE4+4IktdffvnePKm3e20fyzrm4O6pvXvyKJFx1MDAxNW9cdTAwMDP6XHUwMDEx1f48uCpcdTAwMDOzqNiAXHUwMDA1w1x1MDAxMkogOEB+PPno0V5cdTAwMDNUJa9iXHKCn1x1MDAxYUJdgZGIMVx1MDAwNfwyKmFW7MtcdTAwMDOqzOqdXHUwMDAyqlx1MDAxOIBcdTAwMTaeUlx1MDAxMaWMM1x1MDAxNExmmWCGuT1KLayVS9k+I5wwQ0fFV9kp1utmXHUwMDAwKVx1MDAwNLCAXHUwMDAyQGO3RHyvXHUwMDEzIFx1MDAwNFx1MDAxMFx1MDAwMNk66k5cdTAwMDD64vJcdTAwMDIsxVxiK1xiS83BmDBLhVx1MDAwNV9R3Slx6W9cIkvJID1cdENjODdYpvzioVx1MDAxOMv8vjxcdTAwMGZcdEqx3inS9Fx1MDAxYlXxe4spMydcdTAwMTIy5N9bbN08NHi5kdc/+o+STcpX+7uUiVx1MDAxMjnOwkOQXHUwMDAz4F83+Fk8xFx1MDAwM91NMlx1MDAxN95JjVx1MDAxM4omMFx1MDAxMCpcdTAwMWJGkEiKXHUwMDExh1GcQOtcdTAwMDCe5JXHXHUwMDAxXHRcdTAwMTGjiE03TpGYc1x1MDAwZZavc+VcdTAwMDE8Zv1NXHUwMDAxPFxcXHUwMDAxXHUwMDBm5TRmJ1ph+LFOhTuCzNsz9ot3YrzqYYbGbs71XvswXHUwMDFi7VDMJadELuI8k2lncVx1MDAwZjdbI8eL0to9XHUwMDA3NFwia+T4Olq7k386JIl2hN5cdTAwMGVcbjGpPKnKO3jolzhTrJHTu1RcdTAwMTKEpEpXlC1Db6myf4ncV/U3xoiPlkYjXHQ41kvETbZcdTAwMWS/d4xKwFU2XHUwMDFl+EW2dYPR1fZ9rT46hq82e9o9YVxiXHUwMDFl9sVN9kLR5/cuxWq8fihdz1x1MDAwM5SyLIGDXFxwPXsjXHUwMDAwVnGW5rRcdTAwMDIuS983QSqvRlJvXHUwMDA2r1x1MDAxM3NcdTAwMTiRMGvtKlx1MDAwZlAy63NcblCSXHUwMDAwWFjGTbdcdTAwMTFDvyhlXHUwMDE2+ppgW9TFZur5lnpfXHUwMDE2XHUwMDEwbVZIJCW9XHUwMDE3XHUwMDE4XHUwMDEyXHUwMDE0XHUwMDE2cY5NoXhBSGlcdTAwMGWkRHdcdTAwMTPAayCl5Fx1MDAxZFVcdTAwMTP7kVx1MDAwNNRcdTAwMWK3YFx1MDAwMqVQaEyCXYRcZpI5c2xcYjFcdTAwMGJIqDdcdTAwMTJcdTAwMDFY14RNoCSVofueXz9vXHUwMDFlY9THS3OTgFx1MDAwMjLi64T1byVcdTAwMTDfqyQppYRcdTAwMDCWY69cdTAwMTL8MeJcdTAwMTeNXHUwMDFlZJelXHUwMDA391SI9lx1MDAxMzzPuMk/R9LXL/d5vUpxJqBcdTAwMWZR5c9cdTAwMDOksnQuqdxNO1x1MDAwMMpUWqS7vvNcdTAwMDGpXGY1MZXkXHUwMDEzqfu8uSSEwGjbXHUwMDEysZjW+48nziRtLmvpjLqeXHUwMDAyqlx1MDAwNIFcdTAwMTbU/WTv6+KCzllcIvNsXHUwMDFi0zs5XHUwMDEyTFx1MDAwMVx1MDAxNXTp6tNv5YBPMlSfMJbS35ubXHUwMDAyqVx1MDAwNFx1MDAxM0S7gJXXnlZAVIJcdTAwMTWFqObATJiowoKvhqiOLvcnotauXtxcdTAwMWb14d2jc4bZvmFbXHUwMDAxp9NxXHUwMDA3IydotcryXHUwMDAyhooomldvwt47ZmaKM5mpo5/rNFNcdTAwMTg6vqiZLm2ktaUpSffpUkZRdFx1MDAxM/+NaUdYLCdcdTAwMTHCtKWh1Vx1MDAxYjDhSqQsq/+Nulx1MDAxYquW/lx1MDAxZi1KWPpcdTAwMDdcdTAwMDTCkJu2XvYt/Z639XLrXHUwMDAyvVx1MDAxY59dyapbenR3KrVcdTAwMDbaPTwuhsaZN15Wt1x1MDAxNVrGLOctrPi22FqV2aW0+9lvkUKil3j55oRXuMt51Ewy/c4gx1x1MDAwMmah8DfF/eOdezfsweBsrC45XHUwMDEzbePJdZ5L0XH882760CnCVFKtSM70XHUwMDFi/fvHv/9cdTAwMDddYzi+In0= + + + + + BratislavaPragueViennaMunichNurembergFlorenceRome297 km328 km79 km170 km402 km646 km278 km293 km863 km \ 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 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1daVfiytb+3r/C1ffrIafm4XxzbHFEcX7vXa5cdTAwMDBcdTAwMDFBJiFcbnrX+e93XHUwMDE3tiSBSox2gHj6TfdCzVhU9vPsoXbt+u+3tbXv/nPf+/7X2ndvXFx1283awFx1MDAxZH3/w+x/8lx1MDAwNsNmr1x1MDAwYofI5O9h73FQnZx55/v94V9//un2+05wlVPtdV6v9Npex+v6Qzj3/+DvtbX/Tj5Dz1x1MDAxYXhV3+022t7kgsmh4HGcsNm9R73u5NFcdTAwMTgjjFx1MDAxMMdcdTAwMDJNz2h2a97Y3PPmKbhZc7hcdTAwMDWN8L1cdTAwMWFcdTAwMWOou+2hXHUwMDE3XHUwMDFjMbu++6P1nfHzTuWm93BzP94+uOv5V9Xg8nqz3S77z+3Xb+tW71x1MDAxZVx1MDAwN6GWXHUwMDBl/UHv3rts1vy7t85cdO2fXldzh3fQgOnhQe+xcdf1hqZb8HRvr+9Wm/6z2YeCL/XaN3+tXHUwMDA1e8xXJEJLR1PBkXz95NPD5lx1MDAwNlx1MDAxNGuHXHUwMDBiriV53ZCYadpmr91cdTAwMWKYpv2L1LXHWNC2ilu9b0BcdTAwMDO7tek5/sDtXHUwMDBl++5cdTAwMDBeZHDe6OeXxlxmIyd4+p3XbNz5poVIOaFWXHK9yVx1MDAxYiCSYCE5UdNcdTAwMDPmWf1ibVwiIf9cdLp94Ha8ormi+9huhzuuW/vZcZFcdTAwMDNcdTAwMTVzYDskbMGtXHUwMDFl+zX39fVjSSiiVFxuwYWeXHUwMDFlbze797O3a/eq94HETPb+/cdn5FehWPnlXGa6glx1MDAwYqQs8jtKL7+P1fHgXHRVyN5VvbZdplvX/uZm/SvIr3awipNfzqLyi1x1MDAxNyi+SNrEXHUwMDE3S+xcdTAwMTCmg03OirJkhCghtPg9RJkxXHUwMDE1K8pcblrCXHUwMDE0JTZRXHUwMDFlp1x1MDAxN+XjxtnGeqvnepW7obd3dMJcXK/x8jVEWSCKJXv9jIqypI5maHlMjKZcdTAwMWKel2o7KWOqXHUwMDE5hqs5+T1EmaB4USZcZktJuLBI8nN6SVx1MDAxZZ3fbW3fnPlqvX/gcrGxXVKDja8gydxhsUZFXHUwMDAx+M6hSCNcIl83zVx1MDAxNinLwmpVXGKbXHUwMDAwc06ZuWJF4ku0wIIoxMRSxJcqXHUwMDFkJ76cYqEkY1x1MDAxNul9SS+95+vFnrjDvduD8/PtXHUwMDEy5S1/XHUwMDEzt2Kkd9hcdTAwMDOTP1x1MDAwZrKrmXJcYqdaqMmnlFHZXHUwMDA1P8HhXHUwMDFjkE9fN1x1MDAxOSu6daSqXGJ9XnQlcCxccm1cdTAwMTZcdTAwMWE2ZOuAvE63eTtcdTAwMTlcdTAwMTGNKEMqXHUwMDAzQ/lNulx1MDAwMvmiP/f8/SmmXHUwMDA2RY9DXHUwMDE4XFykqFx1MDAwYlx1MDAxZc/UXHUwMDE43iVWiliEvYJIemnfu7nFta31c7zVuXt6rpxcXDxfy1r+uVpcIpLgXHUwMDAwSkZcdTAwMWSKXHUwMDAzXHUwMDAzerZlmVx1MDAxYdD0k1x1MDAwNjToeyQyXHUwMDEx8S9hdYSMillZ1lx1MDAxY/RcdTAwMDdl2mJ2VJD4gN1xXFw6ZWflfq2IL8jl0+ZT+6ogM2XumauyIG6pXHUwMDFjXHUwMDEx61x0XHUwMDE2MMZcdTAwMGXigdGhaKwoY8/8S1x1MDAxNOV/ubym6vV5MVx1MDAwNrp1dHizSHTIon6jaUY1XHUwMDExmKBfXHUwMDE34U+x9PSa4OqQOPreOFx1MDAwMGxIStrefmv7lFx1MDAwZna80Y/TM3Wx541cdTAwMWWK36fn/f2H/bavXHUwMDE3u2j3WexeuiXWok+DvW6peDS4jz7l7fnuYNBcdTAwMWKlve/hXHUwMDA13b48bdFWv35cXNz1XHUwMDBljlptytPd9+dvSTBcdTAwMDejXHUwMDAw0axgXHUwMDFl6ddcYsJ5XHUwMDFjwimCVljdilxukunxbX9zucY3RZw7UmCOmFJMMj1jmFx1MDAwMfqzg3eypqLCUcY/XHUwMDE2XHUwMDE4UY6BcucxTud9XHUwMDBihSRRQmVcdTAwMDDxT2spjlx1MDAxOddcdTAwMWZcdTAwMTHfoFW9rl9uvpjXXHUwMDEyis6YvTtup9l+jrzWiVxcQy9eeFx1MDAwM/i17IX60Fx1MDAxY1pvN1x1MDAxYkbMv1ehyd4gglx1MDAwML9ZddvTXHUwMDEzOs1aLaxcdTAwMDSr8Dy32fVcdTAwMDbFNFqqN2g2ml23fVx1MDAxNt9cdTAwMWP30e+desPXL+ZcdTAwMGZcdTAwMWW9cN94u1PGXHUwMDA2j+BcdTAwMTdcdTAwMTS2ilfYXGK8LKxjXHUwMDE0tkpcdTAwMGZob/36Qvvtm0M5vGtcdTAwMTZkedy/vWjmXHUwMDFj0LCFQ7dcIuiDn1xuXHUwMDFiRaJcdTAwMDS5UthgrVxuXCKzXHR1LVFjb7VqdOdQdvdvNvDmj1uMLsr1RlrNWrp9uqnelvb2hThcdTAwMWXeXHUwMDFmlTf7ldNhXHUwMDA2XHUwMDFhe8u9XHUwMDFjj+leY9jYJFuVk9LxwVx1MDAxMU9pXHSk0dhcdTAwMWO0xcI1to5cdTAwMWSf0YwxUFx1MDAxNpLYXHUwMDEwrtMj3P7q8o1wRojDJ9hm2OBlVmXLXGZcdTAwMDGerLIxgUfBXHUwMDA2Vlx1MDAwNJZcXCpLMGVeZWOiMdKYXCL26yD/XHUwMDA1na1YKHqxaJ29XWt4i9PY76ipWY0915il6Gsh8ezeQF9cdTAwMTPKwP6zXHK2VtB2ejQ/PFV35Xj4sn5ZXHUwMDFj3lx1MDAxMuRcbrf2lHd9jYVM0NeYckfoYIBcbsdHRpevrlx1MDAxOdJCXHUwMDEy+bWUdfdcbm0/NkbH7t2BeHlg3SNJOttplerBw8bm41nFL166T8/iZjx6QFx1MDAwN4erda9XaFx1MDAwNGRcdTAwMWGdizNcdTAwMDKEoLN7g8BcdTAwMWNcdTAwMDZEcPi08cZOet6wy0S+eYMw0PNcdTAwMTjH8YZQXHUwMDE58sY7jjtcblx1MDAwZvolaH4twV6RhK3SWf+w0P6a4v8xcPt3p159QZr/XHUwMDFkhTer+edbs1x1MDAxY9WvY4dEhSaEgjFr1fy76Vx1MDAxMbxf9VHbVeuli+v2Zrl61tg6alRyjmAsdILmp1xuOziE4Pgxolx1MDAxNfjpmFx1MDAxMSSMXHUwMDBm+KVUPypcdTAwMGJfr2+NO53dzWfVXHUwMDFldU67V/6qVf/t0e3FXHIrXVx1MDAxZZ62S1xyvb+1u9dvXFxkqaIlXG5jelx1MDAxMSpaotjIOsaUUqWtecBcdTAwMTVUTFx1MDAwZnD7u8s3wI2KliGAR8fOXHUwMDE4ZtlcdTAwMDH8XHUwMDFkXHJNXHUwMDFkNVx1MDAwZux5XHUwMDA1zThcdTAwMTbwf+Vjvlx1MDAxZlx1MDAxMtlcZlx1MDAxNPTh46JcXPN39JJVQUdak5WCfuVcdTAwMGVcdTAwMGJ4MWjfWPSCba1cYsLUXHUwMDFhSN9Lj95k8swnesG4XHUwMDBlj3zPqGfCRFx1MDAwNL1cdTAwMGI0sIN2XHUwMDA1OplcdTAwMTLHkpVcdTAwMDS+kqQsi8Gwea1M3tfKyahcdTAwMTachWU3LaqHvjvwN0Dsmt1GtGE/Z6+ksYQnPFB9NK1EXHUwMDBlXHUwMDAy00VcdTAwMTLFwfKkQlxuLVXotIbbN55K0LtcdTAwMTPxXHUwMDFje7VSr9n1f7Z5riu8bu39JiZzQaiJXHUwMDA1aCO8TKw4XHUwMDE3XHUwMDEyYUSRpmyujeSDbWy7Q3+z1+k0fT96ZrSr11xyUdx57px4wHdcZlx1MDAxZptllL65Y9Q4XHUwMDBifltcdTAwMGKEePLH9Pf//PH+2Vx1MDAxM2FcdTAwMGau+Fx1MDAxNv75uWR5XHUwMDFky3iKMMpcdTAwMTmzR1wi99NcdTAwMTPeZVFenJdcdTAwMWG481xcuH8pYylOe6WbnFx1MDAxM1x1MDAxZVx1MDAxNixp5JBh5fBwLkCeXHUwMDFjXHUwMDEySbRCWNGvXHUwMDE1i9xcdTAwMWN64kGtX11V98rXm2S/UGIga4GBn+g4LCrVZ1FcdTAwMDOScqReWL8xfNxcdTAwMWOdtPaKJ7voxetncN/jxo/BLm5cdTAwMWXsaoWr+1x1MDAxY196582UjtlcdTAwMWKV2PVcdTAwMTbRXHUwMDEyaY04/ojeSmSlOFx1MDAwN4rJ2ERaXG5uNmWIWf2ng/SEZFx1MDAxN7V8XHUwMDEzXHUwMDEyMX5cdTAwMGJjimFcInQo1jChI6p4dnSU7D5xY1xihnLGg5YkXHUwMDA1Oyk2untlXHUwMDEz0KbSu+Rg54Y7XGb1dabO1DtK1epMRZuzcG9Ks9iUXHUwMDA1jLHAXGZcZlxmq21xmFx1MDAxZcrJxJ9TKGvqII2ElGBuXHUwMDBiymZsXHUwMDBiikQ0z3BxYFx1MDAxNtThXHUwMDAy+FRpjJVcbuV8XHUwMDA29oVcdTAwMTLzzlx1MDAxNSZcdTAwMTIplc3c6CydK4A5xmCx6o9M+fioc5VcZryo54JcculQXHJ9i7HUoaTbqeNcdTAwMDJ+9Vx1MDAwMryr5LTFaFx1MDAxYlx0Y6ByOaNCYII1nW+j+lx1MDAwNztXhVhcZphtXCL9wS2+hX9+mFx1MDAwZVx1MDAxNZKze6d0yFx1MDAxNEKcSHtk+Cg9XHUwMDFkJtur+aRDioFcdTAwMGWpVlx1MDAxNPRcdTAwMDWRms+kcFHElkaHykFcdTAwMDTYQ3OQXHUwMDA3RkjQklx1MDAxMFx1MDAxZkrQh3P2jeBcdTAwMWOsXCKRs2DTK1x1MDAxZlx1MDAxMsk/MttzYXxcYlRcdTAwMDOGo6aMXHUwMDExgTBcdTAwMTM0yFx1MDAxM1x1MDAwZuiQLoJcdTAwMGWTc8JmmlxiVlwikYSbxFtcdTAwMTCBeTZcdTAwMTT/YDaMR8Dk6ET2P0iHicEnmVx1MDAxNG43pCyUslqIx+kpcVitjX3Wujlw/a1iXHUwMDBiyfZcdTAwMDDVRc4p0cxIjI8+cU1yO1x1MDAxYU5MrSNccqr0a1x1MDAwNZ/K9dLLxt5G7fple9S/6vCbwebR0Vx1MDAxMkatXHUwMDEz7zsqb+9cdTAwMTXWXHUwMDFmj0rN662jytb18TFer6a771x1MDAxYjTtemGJo+EqPmHNmJxYXHUwMDBiO8BL6Vx1MDAwMW5/d/lcdTAwMDY44chRsVx0a4LwpY2G45T5amZoSILsrDpfbdnD4eu10JvPNILzjmKyRnBcIq1Zdb5cdTAwMWG4tFx1MDAxMoGWts9cdTAwMTU9SY/gRqN2Xr+4fG7WMNtz0Vx1MDAwNq2cnuY9n1x1MDAwNUQyQUVLJXKrolx1MDAxOVx1MDAwMtNcdTAwMTZcdTAwMWOtr6WhT25PO6M993m7clU9OLs94S4qP/9OmpTG45BgIcCnstdcdTAwMTc5TY9De1x1MDAxZudcdTAwMWKH4Kk5eKJJsaRcXESDXHUwMDA3XG7rZSlSXCJcdTAwMWRsZmxcdTAwMDO2uJLKXHUwMDAyRUuKXHUwMDE5Mlx0gasvK7JsnbpcdTAwMDN3X5BSfUeVWJVqtDnLSDKLL1x1MDAxNYSoZEIjap3FUU5cdTAwMGblZJ8kn1CmmETKq0SgzJSOTOJYIJTnoVx1MDAxYqpo8FZrgSFOXHUwMDA0XkzSd87Ty1Lnbpn0Ms1cYiNUakFcdTAwMTBcdTAwMDKPL3TWazhNLlwi4JdsWq9Fxj9cdTAwMTBcdTAwMDWRYyZsgcGWldwySPPRXGa4r1x1MDAxNPIr4PDpmlx1MDAwNud/XHUwMDBi/8w2lVZywTHH0uo4nKVnuWS7Lq8sh+JZTlwivDqWXHUwMDBiXHUwMDBmrVx1MDAwNNE8TpjKYpbaQmhukVx1MDAwM72pSVx1MDAwNDiEXHRoXHUwMDEzvDaFzVxcflx1MDAxNsLUJ8dQU9FcXLKxM0tzXHUwMDAymTre4DMysJXFfFx1MDAwZe0/emAjfLZUXHUwMDFmJLnkXHUwMDA0Wlx1MDAxMTvhR2OFuKTEOqp7np7mesX+/o43apW7srsxrFx1MDAxN8rrd1x1MDAxYnnPV5MyOqEvQnNcdTAwMDVTLYPntVRcdTAwMWXDVELDMkhWW2J8ZPBygPTZ81Hzoj/S91tod3fY2F1COupcdTAwMWKA7ERt6lxyXHUwMDEzU1cts1wirLFpo6FcdTAwMWFp83FKLkWk9mFcYodcdTAwMTfpcWjv43zjUFx04mCi3kq+k9lcdTAwMDI5OkNcdTAwMWO+XHUwMDEzIeFcdTAwMGVcdTAwMGVcdTAwMTVcdTAwMTJcdTAwMTaWbDNL5qhcdTAwMTDKzE5ZVX2cT1x0cNCqz4RIjkxvVHqDUN5OpiGSd7TJbIjE0pzFh0hwQuooM3E+Qe2FLi7TozmZ9PKJZjNcdTAwMTH+1Xmgglx1MDAxMDGXKqVcdTAwMWNccopXS8xcdTAwMDHvcnHT8JhkjkJcdTAwMDRYXHUwMDA1I1x1MDAxZFx1MDAxOVx1MDAwMVxuZUpcdTAwMDG1MKo5M9kkpi7kLLYp1oKB152/LNJJsjjJRVx1MDAxNqlZJ0OZjFx1MDAxY1PZc1x1MDAxMlq2+Vx1MDAxNspMjFx1MDAxM1x1MDAwNFx1MDAxOFx1MDAxZWEswrfJxtNI5oyoL0RcdGZKXHQzhVxuiFswW1xulZJcdTAwMWNcdTAwMDGQXHUwMDE1XHUwMDEzVDGqPtTgr+R3UEJcdTAwMWRtlJ6p9KKRYJH0UmmGLojGXHUwMDAwWUKVXCLv3S1cdTAwMTZ1ZpvHW3C7b+Gfn8vUilx1MDAxZlx1MDAwNqZcdTAwMDJcYoco+6ycq/R8vM3Hp83+zcmoPrhcdTAwMWHe4JeNi4de/r1cdTAwMWORVLBcZt5+dvOiM/ZyqMBmJFwiizk5S3Rz9r1OXHUwMDFmSOSx++PioXp5fnq1sVFcdTAwMTArn1x1MDAxZLe8YWBcdTAwMTU/dkRcdTAwMTBDiMVkY1xcp8ehvYvzjUPFpIOwts6Ow3xp1Vx0MNORXHUwMDA1VdLNjlx1MDAwM9UghcYrX2lw2cPAl557vyBcdTAwMWbnXHUwMDFkXWJcdTAwMWRcdTAwMDaONmfxPlx1MDAwZYqvwY/hINAztvs4N+mxnMx4+cQyXHUwMDA1LGOz/lx1MDAxYyZcYuxHPjvXdWnT45gmjmRUXHUwMDEyrY2ixJY8SVNcdTAwMDfAskpcdTAwMWT6aa/nc9jkk0BfwHxcdTAwMTCJjTcvJVx1MDAwMqeBUVx1MDAxMnZcXFx1MDAxNjk9LplcdTAwMWTWXCLjJqaaXHUwMDAy+Cogjlx1MDAxMr5cdTAwMGKbXHUwMDFmNlx0ryn7z/NfzFx1MDAwYlx1MDAxMFxmMCcpI1xmXHUwMDBiXHUwMDFlvlx1MDAxY2tcdTAwMGXS/949YmE0OThcdTAwMDFQcItv4Z9cdTAwMWY1jsIxhrmFPyXYXHUwMDA14Fx1MDAxNVlz5Nz0jDqWx33yqG5KO0cv5afOSXdUPmOZMupcIlbgwklLMFx1MDAxN8AkilBqwmqJv24hXHUwMDExcGFDZdLnOTXETtNcdTAwMDFcdTAwMTlcdTAwMGXnq5XlyU2CwFxcKb68hU2g31x1MDAxZqv+48Bt/7vbXHUwMDFm9PrG/vHiXCLCba/uJ9hKfq9cdTAwMWZnKEXaP2tcdTAwMTW914asTKTYIVx1MDAxZFx1MDAxNJ/yqlx05liEXHLsXHUwMDEwnKvp4VxcqJyU91x1MDAxZYpH9XP/4faqTO4unk9w7uFMUMLipyr1Mr5ZrHmg33F35tFcZrYt+KmRJeSW7u9os15GqPjEouE8XHUwMDAwXd3rtp//3XWrVdP0pVx1MDAwMjn+6Vx1MDAwYoewjp//JUErcVx1MDAxNU6hXGZBuJZcdTAwMWXCg9K40Lulo+MuauyMXHUwMDE5195uMdtxnKVDmJLoovKLxDC4U5Z57lx1MDAxNtiaYKc0WZKrg+2yw1x1MDAxNJ1H3620vdWgNvbhi1x1MDAwNi1cdTAwMGZcdTAwMDWLZ0ErwGaTktpB66VcdTAwMDctPtZ7XHUwMDA14Vx1MDAxNm/VdvFhZ7/53H5pkfyDXHUwMDE2J6z/yXlcdTAwMTS0eKZlXHUwMDE5YlYrh5pRJXDNNDNcdTAwMGKFpVx1MDAwMjAoPeBcXPU7XHUwMDAx2K2ZUMC/u6+g9IZ/erXGss3o99qwcDiL2ERs0MBcZlS0PTGqnlx1MDAxZc27nctOxTt/PN1Q4/7Vidp7+tH+XHUwMDAyVjRPUMGSp12WejVo5pRxocgqjehlg3ngdXpPK4fz+63IXG7Q5mxcdTAwMWKg421qSYlheftan630eOYv/vjwQFx1MDAxY9Kh7t1cdTAwMWZ2hkVKr+K0c45cdTAwMTaaZyrJqObKYen84jpSVYQ+N1x1MDAxOI81tVZUwJayUZhJU50pk2Ipn1x1MDAwNzH4waHl6D4/IDBcdTAwMWKkjjmScfg7cizbOVx1MDAwM5E3abZcdTAwMDL+eP2j2PE/llx1MDAxMNyCR8M7sVeDu09cdTAwMGZjeeDu4t7N9b5qtFx1MDAwYtX69f56++wyY1x1MDAxOGc+/kdcdTAwMDQnXHTBaiojg/mLQnFcdTAwMTitXHUwMDAxhrGcL4bJXHUwMDEw44rzLKZcdTAwMGL8rlx1MDAxOF7wXHUwMDA0R4fMoNi8xqxgXHUwMDFjTKSay8fB4CazmJqO7fQoPiqeP23Xq7TT39292ug9nu29tE6/OIolT1x1MDAxYqP+XHUwMDE1XHUwMDE021x1MDAxNlx1MDAwYkHzo0ucXCLNM1m68/9cdTAwMDH8eniB61+g0PmpkFx1MDAxYjtYXHUwMDFjX2dcdTAwMWWb1HqBXGK22tGd9NC99Fx1MDAwN+fr7LB/Udzq3m61dq9cdTAwMDdcdTAwMDeNXHUwMDFmuYcukyrBjFx1MDAwNj5cci+prVm8Z/xLXHUwMDFh2KxcdTAwMTdmZlx0XHUwMDEx8MHBNbbpY8t0Ic6EXHUwMDE2K/aKP43lT3nF3d5aw1x1MDAwM8+1WV2yI2x98KKDWYTEV1x1MDAxNSCcmyRcdTAwMDFrPcFuetBeXbauN69cbs/idlxcrFxm6nLvuLdTyz1oOWZcdTAwMGVoMmA1LCjnoYl8k4qCalx1MDAxOfpcdTAwMTYj7kxm+DFsXHUwMDE2MuU41Vx1MDAxND8sXGKSQmG0qjl+K1x1MDAwMO1qXHUwMDEwu3y4gv1cdTAwMWJcdTAwMDdXITllYHfZ0PqUXHUwMDFlrd72wcnW9t1m637ngJ93uu398ct5XGZac1x1MDAxM3rWijlcdTAwMWFQXGJcdTAwMWQw+YzO49NcdTAwMTQ50HH6bbIsn2nZh8CaXHUwMDFjepam9Hp4S1x1MDAwNVglod0kvF7EPyz2XHUwMDFjfOU3wNab3abvrTVMoviSUVx1MDAxYvPohSeoM1x1MDAxZT9cdTAwMDdXXHUwMDExyWgkXHUwMDE3IEAvpunRe3hBty9PW7TVr1x1MDAxZlx1MDAxN3e9g6NWm/Kc56djXCKgT+Wbczubnu5M12iCT0BKLHh/edxcYpzoNyvc5MFcdTAwMTNLLqWp74vD21x1MDAxY5ZcdFhNmojQ185NqrqmXHUwMDEyXHUwMDA07TNcdTAwMTBPmaqeeplcdTAwMTTkUHirXHUwMDEy/iNCzVwiXFw6VFx1MDAxOD8on1x1MDAwM7SIfjpNmoczxTPKW//Aup5cdTAwMTRzglx1MDAxMFx1MDAwMZNcdTAwMGZTU9ZsPm9dOaB5tFIm9mHqs32ouV8pflx1MDAxMI+TydFZhFx1MDAwNLf7NnNbeFx1MDAxOZXeKOVcdTAwMDS+eFqlXHTTfrB5XHUwMDE30rrEMGbpaXXLvVx1MDAxY4/pXmPY2CRblZPS8cFcdTAwMTHP+6pYTFx1MDAxOepEmCtJ4XO2toFwTFx1MDAxZI63o1ItcFx1MDAxOVx1MDAxOK7B/FxuNltWq1lFfH7eXHUwMDBmQ2balmY5LFxuqbCp1b1ALk29xkpcdTAwMDHIXHQ4lFx1MDAxMkykgM6lfL4oJDWTqlx1MDAxMcZcdTAwMWGYXHUwMDBivMNVUqlZIUsxQlx1MDAxONOCgctcdTAwMDI9+ftSaSFcdTAwMWVcdTAwMWJme0XFR1x09NvPM767/X7ZXHUwMDA3sZ2+tu9PTW+0YVx0QNQnm5mePelHQ2jeXHUwMDA0NX9/+/t/XHUwMDBmNbfYIn0= + + + + + VertexSetEdgeSetGraphRefGraphMutGraphBaseGraphAddGraphFullNeighborsGraphWeakstructuralpropertiesreadonlyaccessmutableaccessaddingvertices/edgesremovingvertices/edgesno genericsgenericsfinite graphs \ 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, { + /// Creates a new directed graph wrapping given storage. pub fn new_directed_in(storage: G) -> Self { Self::new_in(storage) } @@ -108,6 +140,7 @@ impl Graph where G: GraphBase, { + /// Creates a new graph wrapping given storage. pub fn new_in(storage: G) -> Self { Self { storage, @@ -115,6 +148,7 @@ where } } + #[doc = include_str!("../../docs/include/extend_with_edges.extend_with_edges.md")] pub fn extend_with_edges(&mut self, iter: I) where T: IntoEdge, @@ -124,6 +158,7 @@ where self.storage.extend_with_edges(iter) } + #[doc = include_str!("../../docs/include/extend_with_vertices.extend_with_vertices.md")] pub fn extend_with_vertices(&mut self, iter: I) where I: IntoIterator, @@ -132,6 +167,7 @@ where self.storage.extend_with_vertices(iter) } + #[doc = include_str!("../../docs/include/extend_with_vertices.from_vertices.md")] pub fn from_vertices(iter: I) -> Self where I: IntoIterator, @@ -140,6 +176,7 @@ where Self::new_in(G::from_vertices(iter)) } + #[doc = include_str!("../../docs/include/vertex_set.vertex_count.md")] pub fn vertex_count(&self) -> usize where G: VertexSet, @@ -147,6 +184,7 @@ where self.storage.vertex_count() } + #[doc = include_str!("../../docs/include/graph_base.vertex_count_hint.md")] pub fn vertex_count_hint(&self) -> Option where G: GraphBase, @@ -154,6 +192,7 @@ where self.storage.vertex_count_hint() } + #[doc = include_str!("../../docs/include/vertex_set.vertex_bound.md")] pub fn vertex_bound(&self) -> usize where G: VertexSet, @@ -162,6 +201,7 @@ where self.storage.vertex_bound() } + #[doc = include_str!("../../docs/include/vertex_set.vertices_by_id.md")] pub fn vertices_by_id(&self) -> G::VerticesByIdIter<'_> where G: VertexSet, @@ -169,6 +209,7 @@ where self.storage.vertices_by_id() } + #[doc = include_str!("../../docs/include/vertex_set.contains_vertex.md")] pub fn contains_vertex(&self, id: VI) -> bool where G: GraphRef, @@ -177,6 +218,7 @@ where self.storage.contains_vertex(id.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/graph_ref.vertex.md")] pub fn vertex(&self, id: VI) -> Option<&V> where G: GraphRef, @@ -185,6 +227,7 @@ where self.storage.vertex(id.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/graph_ref.vertices.md")] pub fn vertices(&self) -> G::VerticesIter<'_> where G: GraphRef, @@ -192,6 +235,7 @@ where self.storage.vertices() } + #[doc = include_str!("../../docs/include/graph_ref.find_vertex.md")] pub fn find_vertex(&self, vertex: Q) -> Option where G: GraphRef, @@ -201,6 +245,7 @@ where self.storage.find_vertex(vertex.borrow()) } + #[doc = include_str!("../../docs/include/graph_mut.vertex_mut.md")] pub fn vertex_mut(&mut self, id: VI) -> Option<&mut V> where G: GraphMut, @@ -209,6 +254,7 @@ where self.storage.vertex_mut(id.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/graph_add.add_vertex.md")] pub fn add_vertex(&mut self, vertex: V) -> G::VertexId where G: GraphAdd, @@ -216,6 +262,7 @@ where self.storage.add_vertex(vertex) } + #[doc = include_str!("../../docs/include/graph_add.try_add_vertex.md")] pub fn try_add_vertex(&mut self, vertex: V) -> Result> where G: GraphAdd, @@ -223,6 +270,7 @@ where self.storage.try_add_vertex(vertex) } + #[doc = include_str!("../../docs/include/graph_full.remove_vertex.md")] pub fn remove_vertex(&mut self, id: VI) -> Option where G: GraphFull, @@ -231,6 +279,7 @@ where self.storage.remove_vertex(id.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/graph_mut.replace_vertex.md")] pub fn replace_vertex(&mut self, id: VI, vertex: V) -> V where G: GraphMut, @@ -239,6 +288,7 @@ where self.storage.replace_vertex(id.as_id().as_ref(), vertex) } + #[doc = include_str!("../../docs/include/graph_mut.try_replace_vertex.md")] pub fn try_replace_vertex(&mut self, id: VI, vertex: V) -> Result> where G: GraphMut, @@ -247,6 +297,7 @@ where self.storage.try_replace_vertex(id.as_id().as_ref(), vertex) } + #[doc = include_str!("../../docs/include/graph_full.clear.md")] pub fn clear(&mut self) where G: GraphFull, @@ -254,6 +305,7 @@ where self.storage.clear() } + #[doc = include_str!("../../docs/include/graph_add.try_get_or_add_vertex.md")] pub fn try_get_or_add_vertex(&mut self, vertex: V) -> Result> where G: GraphAdd, @@ -262,6 +314,7 @@ where self.storage.try_get_or_add_vertex(vertex) } + #[doc = include_str!("../../docs/include/graph_add.get_or_add_vertex.md")] pub fn get_or_add_vertex(&mut self, vertex: V) -> G::VertexId where G: GraphAdd, @@ -270,6 +323,7 @@ where self.storage.get_or_add_vertex(vertex) } + #[doc = include_str!("../../docs/include/edge_set.edge_count.md")] pub fn edge_count(&self) -> usize where G: EdgeSet, @@ -277,6 +331,7 @@ where self.storage.edge_count() } + #[doc = include_str!("../../docs/include/graph_base.edge_count_hint.md")] pub fn edge_count_hint(&self) -> Option where G: GraphBase, @@ -284,6 +339,7 @@ where self.storage.edge_count_hint() } + #[doc = include_str!("../../docs/include/edge_set.edge_bound.md")] pub fn edge_bound(&self) -> usize where G: EdgeSet, @@ -292,6 +348,7 @@ where self.storage.edge_bound() } + #[doc = include_str!("../../docs/include/edge_set.endpoints.md")] pub fn endpoints(&self, id: EI) -> Option<(G::VertexId, G::VertexId)> where G: EdgeSet, @@ -300,6 +357,7 @@ where self.storage.endpoints(id.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/edge_set.edge_id.md")] pub fn edge_id(&self, from: VI, to: VI) -> G::EdgeIdIter<'_> where G: EdgeSet, @@ -309,6 +367,7 @@ where .edge_id(from.as_id().as_ref(), to.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/edge_set.edge_id_any.md")] pub fn edge_id_any(&self, from: VI, to: VI) -> Option where G: EdgeSet, @@ -318,6 +377,7 @@ where .edge_id_any(from.as_id().as_ref(), to.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/edge_set.edges_by_id.md")] pub fn edges_by_id(&self) -> G::EdgesByIdIter<'_> where G: EdgeSet, @@ -325,6 +385,7 @@ where self.storage.edges_by_id() } + #[doc = include_str!("../../docs/include/edge_set.contains_edge.md")] pub fn contains_edge(&self, id: EI) -> bool where G: EdgeSet, @@ -333,6 +394,7 @@ where self.storage.contains_edge(id.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/edge_set.contains_edge_between.md")] pub fn contains_edge_between(&self, from: VI, to: VI) -> bool where G: EdgeSet, @@ -342,6 +404,7 @@ where .contains_edge_between(from.as_id().as_ref(), to.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/graph_base.is_directed.md")] pub fn is_directed(&self) -> bool where G: GraphBase, @@ -349,6 +412,7 @@ where self.storage.is_directed() } + #[doc = include_str!("../../docs/include/graph_ref.edge.md")] pub fn edge(&self, id: EI) -> Option<&E> where G: GraphRef, @@ -357,6 +421,7 @@ where self.storage.edge(id.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/graph_ref.edges.md")] pub fn edges(&self) -> G::EdgesIter<'_> where G: GraphRef, @@ -364,6 +429,7 @@ where self.storage.edges() } + #[doc = include_str!("../../docs/include/graph_mut.edge_mut.md")] pub fn edge_mut(&mut self, id: EI) -> Option<&mut E> where G: GraphMut, @@ -372,6 +438,7 @@ where self.storage.edge_mut(id.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/graph_add.add_edge.md")] pub fn add_edge(&mut self, from: VI, to: VI, edge: E) -> G::EdgeId where G: GraphAdd, @@ -381,6 +448,7 @@ where .add_edge(from.as_id().as_ref(), to.as_id().as_ref(), edge) } + #[doc = include_str!("../../docs/include/graph_add.try_add_edge.md")] pub fn try_add_edge( &mut self, from: VI, @@ -395,6 +463,7 @@ where .try_add_edge(from.as_id().as_ref(), to.as_id().as_ref(), edge) } + #[doc = include_str!("../../docs/include/graph_add.try_add_edge_connecting.md")] pub fn try_add_edge_connecting( &mut self, from: V, @@ -408,6 +477,7 @@ where self.storage.try_add_edge_connecting(from, to, edge) } + #[doc = include_str!("../../docs/include/graph_add.add_edge_connecting.md")] pub fn add_edge_connecting(&mut self, from: V, to: V, edge: E) -> G::EdgeId where G: GraphAdd, @@ -416,6 +486,7 @@ where self.storage.add_edge_connecting(from, to, edge) } + #[doc = include_str!("../../docs/include/graph_full.remove_edge.md")] pub fn remove_edge(&mut self, id: EI) -> Option where G: GraphFull, @@ -424,6 +495,7 @@ where self.storage.remove_edge(id.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/graph_full.remove_edges_between.md")] pub fn remove_edges_between(&mut self, from: VI, to: VI) where G: GraphFull, @@ -433,6 +505,7 @@ where .remove_edges_between(from.as_id().as_ref(), to.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/graph_full.remove_edge_any_between.md")] pub fn remove_edge_any_between(&mut self, from: VI, to: VI) -> Option where G: GraphFull, @@ -442,6 +515,7 @@ where .remove_edge_any_between(from.as_id().as_ref(), to.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/graph_mut.replace_edge.md")] pub fn replace_edge(&mut self, id: EI, edge: E) -> E where G: GraphMut, @@ -450,6 +524,7 @@ where self.storage.replace_edge(id.as_id().as_ref(), edge) } + #[doc = include_str!("../../docs/include/graph_mut.try_replace_edge.md")] pub fn try_replace_edge(&mut self, id: EI, edge: E) -> Result> where G: GraphMut, @@ -458,6 +533,7 @@ where self.storage.try_replace_edge(id.as_id().as_ref(), edge) } + #[doc = include_str!("../../docs/include/graph_full.clear_edges.md")] pub fn clear_edges(&mut self) where G: GraphFull, @@ -465,6 +541,7 @@ where self.storage.clear_edges() } + #[doc = include_str!("../../docs/include/neighbors.neighbors_undirected.md")] pub fn neighbors_undirected(&self, from: VI) -> G::NeighborsIter<'_> where G: Neighbors, @@ -473,6 +550,7 @@ where self.storage.neighbors_undirected(from.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/neighbors.neighbors_directed.md")] pub fn neighbors_directed(&self, from: VI, dir: Direction) -> G::NeighborsIter<'_> where G: Neighbors, @@ -481,6 +559,7 @@ where self.storage.neighbors_directed(from.as_id().as_ref(), dir) } + #[doc = include_str!("../../docs/include/neighbors.degree_undirected.md")] pub fn degree_undirected(&self, from: VI) -> usize where G: Neighbors, @@ -489,6 +568,7 @@ where self.storage.degree_undirected(from.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/neighbors.degree_directed.md")] pub fn degree_directed(&self, from: VI, dir: Direction) -> usize where G: Neighbors, @@ -497,6 +577,7 @@ where self.storage.degree_directed(from.as_id().as_ref(), dir) } + /// Makes the vertex and edge IDs [stable](crate::core::props::Stability). pub fn stabilize(self) -> Graph> where G: GraphBase, @@ -509,6 +590,17 @@ where Graph::new_in(Frozen::new(self.storage)) } + #[doc = include_str!("../../docs/include/connect_vertices.connect_vertices.md")] + /// + /// # Examples + /// + /// ``` + /// use gryf::graph::Graph; + /// + /// let mut graph = Graph::new_undirected(); + /// graph.extend_with_vertices(0u32..=100); + /// graph.connect_vertices(|u, v| (u != v && u + v % 3 == 0).then_some(u.max(v) - u.min(v))); + /// ``` pub fn connect_vertices(&mut self, connect: F) where G: ConnectVertices, diff --git a/gryf/src/graph/path.rs b/gryf/src/graph/path.rs index 617bb58..43a6082 100644 --- a/gryf/src/graph/path.rs +++ b/gryf/src/graph/path.rs @@ -26,6 +26,26 @@ use gryf_derive::{EdgeSet, GraphBase, GraphMut, GraphRef, Neighbors, VertexSet}; use super::generic::Graph; +/// Graph that is a [path]. +/// +/// [path]: https://en.wikipedia.org/wiki/Path_(graph_theory) +/// +/// # Examples +/// +/// ``` +/// use gryf::graph::{Graph, Path}; +/// +/// let mut graph = Graph::new_undirected(); +/// let a = graph.add_vertex("a"); +/// +/// let mut path = Path::new_undirected_in(graph).unwrap(); +/// let b = path.try_add_vertex("b", Some(1), a).unwrap(); +/// let c = path.try_add_vertex("c", Some(2), b).unwrap(); +/// +/// let bc = path.edge_id_any(b, c).unwrap(); +/// +/// assert_eq!(path.edge(bc).unwrap(), &2); +/// ``` #[derive(Debug, Clone, GraphBase, Neighbors, VertexSet, EdgeSet, GraphRef, GraphMut)] #[gryf_crate] pub struct Path> @@ -38,18 +58,30 @@ where ty: PhantomData<(V, E, Ty)>, } +/// The error type for path manipulation operations. #[derive(Debug, Error)] pub enum PathError { - #[error("the graph has higher degree than valid")] + /// The graph has higher degree than is valid. + #[error("the graph has higher degree than is valid")] HigherDegree, + + /// The graph contains cycle. #[error("the graph contains cycle")] Cycle, + + /// The graph is not connected. #[error("the graph is not connected")] Disconnected, - #[error("the path has mixed direction")] + + /// The path has mixed direction of edges. + #[error("the path has mixed direction of edges")] Direction, + + /// Adding a vertex to the underlying storage failed. #[error("{0}")] AddVertex(AddVertexError), + + /// Adding an edge to the underlying storage failed. #[error("{0}")] AddEdge(AddEdgeError), } @@ -67,16 +99,19 @@ impl From> for PathError { } impl Path { + /// Creates an empty graph. pub fn new() -> Self { Self::new_unchecked(AdjList::new(), None) } + /// Creates an empty graph with given capacities. pub fn with_capacity(vertex_capacity: usize, edge_capacity: usize) -> Self { Self::new_unchecked(AdjList::with_capacity(vertex_capacity, edge_capacity), None) } } impl Path { + /// Creates an empty undirected graph. pub fn new_undirected() -> Self { Self::new_unchecked(AdjList::new(), None) } @@ -86,12 +121,14 @@ impl Path where G: GraphBase + Neighbors + VertexSet + Guarantee, { + /// Creates a new undirected graph wrapping given storage. pub fn new_undirected_in(storage: G) -> Result> { Self::new_in(storage) } } impl Path> { + /// Creates an empty directed graph. pub fn new_directed() -> Self { Self::new_unchecked(AdjList::new(), None) } @@ -101,6 +138,7 @@ impl Path where G: GraphBase + Neighbors + VertexSet + Guarantee, { + /// Creates a new directed graph wrapping given storage. pub fn new_directed_in(storage: G) -> Result> { Self::new_in(storage) } @@ -236,6 +274,7 @@ where Ok(Some(ends)) } + /// Creates a new graph wrapping given storage. pub fn new_in(storage: G) -> Result> where G: Neighbors + VertexSet + Guarantee, @@ -243,6 +282,7 @@ where Self::constrain(storage) } + #[doc = include_str!("../../docs/include/vertex_set.vertex_count.md")] pub fn vertex_count(&self) -> usize where G: VertexSet, @@ -250,6 +290,7 @@ where self.storage.vertex_count() } + #[doc = include_str!("../../docs/include/graph_base.vertex_count_hint.md")] pub fn vertex_count_hint(&self) -> Option where G: GraphBase, @@ -257,6 +298,7 @@ where self.storage.vertex_count_hint() } + #[doc = include_str!("../../docs/include/vertex_set.vertex_bound.md")] pub fn vertex_bound(&self) -> usize where G: VertexSet, @@ -265,6 +307,7 @@ where self.storage.vertex_bound() } + #[doc = include_str!("../../docs/include/vertex_set.vertices_by_id.md")] pub fn vertices_by_id(&self) -> G::VerticesByIdIter<'_> where G: VertexSet, @@ -272,6 +315,7 @@ where self.storage.vertices_by_id() } + #[doc = include_str!("../../docs/include/vertex_set.contains_vertex.md")] pub fn contains_vertex(&self, id: VI) -> bool where G: GraphRef, @@ -280,6 +324,7 @@ where self.storage.contains_vertex(id.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/graph_ref.vertex.md")] pub fn vertex(&self, id: VI) -> Option<&V> where G: GraphRef, @@ -288,6 +333,7 @@ where self.storage.vertex(id.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/graph_ref.vertices.md")] pub fn vertices(&self) -> G::VerticesIter<'_> where G: GraphRef, @@ -295,6 +341,7 @@ where self.storage.vertices() } + #[doc = include_str!("../../docs/include/graph_ref.find_vertex.md")] pub fn find_vertex(&self, vertex: Q) -> Option where G: GraphRef, @@ -304,6 +351,7 @@ where self.storage.find_vertex(vertex.borrow()) } + #[doc = include_str!("../../docs/include/graph_mut.vertex_mut.md")] pub fn vertex_mut(&mut self, id: VI) -> Option<&mut V> where G: GraphMut, @@ -312,6 +360,27 @@ where self.storage.vertex_mut(id.as_id().as_ref()) } + /// Connects a new vertex to given end with given edge, returning error in + /// case of failure. + /// + /// # Examples + /// + /// ``` + /// use gryf::{ + /// core::id::{IdType, VertexId}, + /// graph::Path, + /// }; + /// + /// let mut graph = Path::new_directed(); + /// + /// let u = graph + /// .try_add_vertex("hello", None, VertexId::sentinel()) + /// .unwrap(); + /// let v = graph.try_add_vertex("world", Some(42), u).unwrap(); + /// + /// assert_eq!(graph.vertex_count(), 2); + /// assert_eq!(graph.ends(), Some(&[u, v])); + /// ``` pub fn try_add_vertex( &mut self, vertex: V, @@ -369,6 +438,32 @@ where } } + /// Connects a new vertex to given end with a default edge. + /// + /// # Examples + /// + /// ``` + /// use gryf::graph::{Graph, Path}; + /// + /// let mut graph = Graph::new_directed(); + /// let a = graph.add_vertex("a"); + /// let b = graph.add_vertex("b"); + /// graph.add_edge(a, b, 42); + /// + /// let mut graph = Path::new_directed_in(graph).unwrap(); + /// + /// let c = graph.add_vertex("c", b); + /// + /// let ab = graph.edge_id_any(a, b).unwrap(); + /// let bc = graph.edge_id_any(b, c).unwrap(); + /// + /// assert_eq!(graph.edge(ab), Some(&42)); + /// assert_eq!(graph.edge(bc), Some(&0)); + /// ``` + /// + /// # Panics + /// + /// Panics if the underlying storage fails to add the vertex. pub fn add_vertex(&mut self, vertex: V, end: VI) -> G::VertexId where E: Default, @@ -398,6 +493,11 @@ where } } + /// Removes a vertex from the graph, connecting thr two neighbors with given + /// edge if the removed vertex was an inner vertex. + /// + /// Returns the vertex attribute if the vertex was present, or `None` + /// otherwise. pub fn remove_vertex(&mut self, id: VI, edge: Option) -> Option where G: Neighbors + GraphFull, @@ -476,6 +576,7 @@ where } } + #[doc = include_str!("../../docs/include/graph_mut.replace_vertex.md")] pub fn replace_vertex(&mut self, id: VI, vertex: V) -> V where G: GraphMut, @@ -484,6 +585,7 @@ where self.storage.replace_vertex(id.as_id().as_ref(), vertex) } + #[doc = include_str!("../../docs/include/graph_mut.try_replace_vertex.md")] pub fn try_replace_vertex(&mut self, id: VI, vertex: V) -> Result> where G: GraphMut, @@ -492,6 +594,7 @@ where self.storage.try_replace_vertex(id.as_id().as_ref(), vertex) } + #[doc = include_str!("../../docs/include/graph_full.clear.md")] pub fn clear(&mut self) where G: GraphFull, @@ -500,6 +603,7 @@ where self.ends = None; } + #[doc = include_str!("../../docs/include/edge_set.edge_count.md")] pub fn edge_count(&self) -> usize where G: EdgeSet, @@ -507,6 +611,7 @@ where self.storage.edge_count() } + #[doc = include_str!("../../docs/include/graph_base.edge_count_hint.md")] pub fn edge_count_hint(&self) -> Option where G: GraphBase, @@ -514,6 +619,7 @@ where self.storage.edge_count_hint() } + #[doc = include_str!("../../docs/include/edge_set.edge_bound.md")] pub fn edge_bound(&self) -> usize where G: EdgeSet, @@ -522,6 +628,7 @@ where self.storage.edge_bound() } + #[doc = include_str!("../../docs/include/edge_set.endpoints.md")] pub fn endpoints(&self, id: EI) -> Option<(G::VertexId, G::VertexId)> where G: EdgeSet, @@ -530,6 +637,7 @@ where self.storage.endpoints(id.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/edge_set.edge_id.md")] pub fn edge_id(&self, from: VI, to: VI) -> G::EdgeIdIter<'_> where G: EdgeSet, @@ -539,6 +647,7 @@ where .edge_id(from.as_id().as_ref(), to.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/edge_set.edge_id_any.md")] pub fn edge_id_any(&self, from: VI, to: VI) -> Option where G: EdgeSet, @@ -548,6 +657,7 @@ where .edge_id_any(from.as_id().as_ref(), to.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/edge_set.edges_by_id.md")] pub fn edges_by_id(&self) -> G::EdgesByIdIter<'_> where G: EdgeSet, @@ -555,6 +665,7 @@ where self.storage.edges_by_id() } + #[doc = include_str!("../../docs/include/edge_set.contains_edge.md")] pub fn contains_edge(&self, id: EI) -> bool where G: EdgeSet, @@ -563,6 +674,7 @@ where self.storage.contains_edge(id.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/edge_set.contains_edge_between.md")] pub fn contains_edge_between(&self, from: VI, to: VI) -> bool where G: EdgeSet, @@ -572,6 +684,7 @@ where .contains_edge_between(from.as_id().as_ref(), to.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/graph_base.is_directed.md")] pub fn is_directed(&self) -> bool where G: GraphBase, @@ -579,6 +692,7 @@ where self.storage.is_directed() } + #[doc = include_str!("../../docs/include/graph_ref.edge.md")] pub fn edge(&self, id: EI) -> Option<&E> where G: GraphRef, @@ -587,6 +701,7 @@ where self.storage.edge(id.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/graph_ref.edges.md")] pub fn edges(&self) -> G::EdgesIter<'_> where G: GraphRef, @@ -594,6 +709,7 @@ where self.storage.edges() } + #[doc = include_str!("../../docs/include/graph_mut.edge_mut.md")] pub fn edge_mut(&mut self, id: EI) -> Option<&mut E> where G: GraphMut, @@ -602,6 +718,7 @@ where self.storage.edge_mut(id.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/graph_mut.replace_edge.md")] pub fn replace_edge(&mut self, id: EI, edge: E) -> E where G: GraphMut, @@ -610,6 +727,7 @@ where self.storage.replace_edge(id.as_id().as_ref(), edge) } + #[doc = include_str!("../../docs/include/graph_mut.try_replace_edge.md")] pub fn try_replace_edge(&mut self, id: EI, edge: E) -> Result> where G: GraphMut, @@ -618,6 +736,7 @@ where self.storage.try_replace_edge(id.as_id().as_ref(), edge) } + #[doc = include_str!("../../docs/include/neighbors.neighbors_undirected.md")] pub fn neighbors_undirected(&self, from: VI) -> G::NeighborsIter<'_> where G: Neighbors, @@ -626,6 +745,7 @@ where self.storage.neighbors_undirected(from.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/neighbors.neighbors_directed.md")] pub fn neighbors_directed(&self, from: VI, dir: Direction) -> G::NeighborsIter<'_> where G: Neighbors, @@ -634,6 +754,7 @@ where self.storage.neighbors_directed(from.as_id().as_ref(), dir) } + #[doc = include_str!("../../docs/include/neighbors.degree_undirected.md")] pub fn degree_undirected(&self, from: VI) -> usize where G: Neighbors, @@ -642,6 +763,7 @@ where self.storage.degree_undirected(from.as_id().as_ref()) } + #[doc = include_str!("../../docs/include/neighbors.degree_directed.md")] pub fn degree_directed(&self, from: VI, dir: Direction) -> usize where G: Neighbors, @@ -650,10 +772,15 @@ where self.storage.degree_directed(from.as_id().as_ref(), dir) } + /// Returns the pair of path ends, if the path is not empty. + /// + /// Note that if there is only one vertex v in the path, then (v, v) is + /// returned. pub fn ends(&self) -> Option<&[G::VertexId; 2]> { self.ends.as_ref() } + /// Makes the vertex and edge IDs [stable](crate::core::props::Stability). pub fn stabilize(self) -> Path> { Path::new_unchecked(Stable::new(self.storage), self.ends) } diff --git a/gryf/src/lib.rs b/gryf/src/lib.rs index 62c4eb7..cb694a8 100644 --- a/gryf/src/lib.rs +++ b/gryf/src/lib.rs @@ -1,3 +1,222 @@ +//! Gryf is a [graph] data structure library aspiring to be convenient, +//! versatile, correct and performant. +//! +//! A graph is made up of _vertices_ (also called nodes) which are connected by +//! _edges_. Both vertices and edges might have _attributes_. Graphs can be used +//! to model pairwise relations between objects and have many [applications] in +//! various areas like _computer science_ (packet routing in the internet), +//! _transportation_ (navigation in a city), _linguistics_ (lexical semantics +//! relationships), _physics and chemistry_ (processing of molecular structures) +//! or _social sciences_ (social network analysis), among others. +//! +//! Gryf implements various [storages](storage) to hold the graph data and +//! structure and [encapsulations](graph) that guarantee specific semantics. +//! Then it provides common [graph traversal](visit) methods and a collection of +//! [algorithms](algo) on graphs. The algorithms are [organized into the +//! problems](#problems-instead-of-algorithms) they solve. For specifying the +//! parameters of an algorithm the [builder +//! pattern](#builder-pattern-for-algorithms) is utilized. +//! +//! +#![doc = include_str!("../docs/assets/main_example.excalidraw.svg")] +//! +//! ``` +//! use gryf::{algo::ShortestPaths, graph::Graph}; +//! +//! // Default storage is adjacency list, but that can be simply changed by +//! // using `Graph::new_undirected_in`. +//! 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), +//! ]); +//! +//! // As the edge weights are unsigned and there is a specific goal, Dijktra's +//! // algorithm is applied. For signed edges, Bellman-Ford would be used. +//! 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}"); +//! // 1391 km from Prague through Nuremberg - Munich - Florence - Rome +//! ``` +//! +//! [graph]: https://en.wikipedia.org/wiki/Graph_theory +//! [applications]: https://en.wikipedia.org/wiki/Graph_theory#Applications +//! +//! # Common operations +//! +//! See the [core] module documentation. +//! +//! # Goals +//! +//! The main goals of gryf are to be +//! +//! * _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". +//! +//! Failing in any of these should be considered an issue to be reported. +//! +//! # Design +//! +//! _For more details, see the [design document]_. +//! +//! ## Problems instead of algorithms +//! +//! 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`](algo::ShortestPaths)) instead of requiring to call the +//! algorithms directly (`dijkstra`, `bellman_ford`). +//! +//! 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. +//! * Having a specific type instead of a generic one such as `Vec` or `HashMap` +//! gives the opportunity to provide additional functionality (like path +//! reconstruction for shortest paths or "is perfect?" query on matching). +//! * Not specifying the algorithm enables the use of automatic algorithm +//! selection, which makes the decision based on the properties of the input +//! graph. +//! +//! ``` +//! # use gryf::{algo::ShortestPaths, graph::Graph}; +//! # let mut graph = Graph::new_undirected(); +//! # let prague = graph.add_vertex("Prague"); +//! # let rome = graph.add_vertex("Rome"); +//! # graph.add_edge(prague, rome, 0); +//! 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 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 from the compiler helpful and obvious. +//! +//! ``` +//! # use gryf::{algo::ShortestPaths, graph::Graph}; +//! # struct Edge { distance: u32 } +//! # let mut graph = Graph::new_undirected(); +//! # let prague = graph.add_vertex("Prague"); +//! # let rome = graph.add_vertex("Rome"); +//! # graph.add_edge(prague, rome, Edge { distance: 0 }); +//! let shortest_paths = ShortestPaths::on(&graph) +//! .edge_weight_fn(|e| e.distance) +//! .goal(prague) +//! .run(rome) +//! .unwrap(); +//! ``` +//! +//! ## Separation of graph storage and semantics +//! +//! 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](storage::adj_list) or +//! [adjacency matrix](storage::adj_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 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. +//! * The API is limited such that it is impossible to violate the rules of the +//! user-desired class of graph. +//! * The guaranteed properties of a restricted graph can be utilized in +//! choosing a more efficient algorithm. +//! +//! ``` +//! # use gryf::graph::Graph; +//! use gryf::storage::AdjMatrix; +//! +//! let mut graph = Graph::new_undirected_in(AdjMatrix::default()); +//! # let a = graph.add_vertex("a"); +//! # let b = graph.add_vertex("b"); +//! # graph.add_edge(a, b, ()); +//! ``` +//! +//! ## Graph interfaces and generic algorithms +//! +//! There is a set of [core traits](crate::core) that represent different levels +//! of functionality that is supported by a graph representation. The levels +//! range from basic properties like directionality of the graph over structural +//! properties like vertex/edge count and vertex neighbors to different kinds of +//! manipulation capability (readonly, append-only, removable). The traits +//! require only a bare minimum of methods and provide default, overridable +//! implementations (even if inefficient) for the remaining API to reduce the +//! implementation burden. +//! +//! Algorithms then constrain the input graph only with traits that are +//! necessary for its function and don't make any assumption on the specific +//! graph representation used. +//! +//! ## Iteration over recursion +//! +//! Iterative graph traversals are preferred over recursion. The main benefits +//! of this choice are: +//! +//! * Traversal is lazy and can be stopped without tricks. +//! * Traversal state is independent on the graph itself, allowing mutations +//! during traversal. +//! * Traversal is not limited by the size of the program stack. +//! +//! ``` +//! use gryf::visit::{DfsEvent, DfsEvents, Visitor}; +//! # use gryf::graph::Graph; +//! # let mut graph = Graph::new_directed(); +//! # let root = graph.add_vertex("root"); +//! # let v = graph.add_vertex("v"); +//! # graph.add_edge(root, v, 0); +//! +//! let is_cyclic = DfsEvents::new(&graph) +//! .start(root) +//! .into_iter(&graph) +//! .any(|event| matches!(event, DfsEvent::BackEdge { .. })); +//! ``` +//! +//! [design document]: https://github.com/pnevyk/gryf/blob/main/DESIGN.md +//! +//! # Comparison with alternatives +//! +//! 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. + pub mod adapt; pub mod algo; pub mod core; diff --git a/gryf/src/storage.rs b/gryf/src/storage.rs index 7105aaa..ecb3413 100644 --- a/gryf/src/storage.rs +++ b/gryf/src/storage.rs @@ -1,3 +1,44 @@ +//! Implementations of various graph storages. +//! +//! A _storage_ is an implementation of the graph representation. It implements +//! traits from the [`core`](crate::core) module to provide the needed +//! functionality. On top of storage, there is usually an +//! [encapsulation](crate::graph) that provides additional, higher-level +//! semantics. +//! +//! # Storages and their properties +//! +//! The available storages are: +//! +//! * [Adjacency list](adj_list) +//! * [Adjacency matrix](adj_matrix) +//! * [Edge list](edge_list) +//! +//! The **adjacency list** provides fast vertex and edge insertion and very fast +//! graph traversal algorithms, especially on sparser graphs. The **adjacency +//! matrix** provides very fast edge insertion and removal and efficient +//! algorithms on dense graphs. The **edge list** is generally not recommended. +//! +//! Available storages and their properties are summarized in the table below. +//! +//! | | **[AdjList]** | **[AdjMatrix]** | **[EdgeList]** | +//! |----------------|----------------|-----------------|----------------| +//! | add vertex | _O*(1)_ | _O*(1)_ | _O*(1)_ | +//! | add edge | _O*(1)_ | _O(1)_ | _O*(1)_ | +//! | get neighbors | _O(d)_ | _O(V)_ | _O(E)_ | +//! | lookup vertex | _O(1)_ | _O(1)_ | _O(1)_ | +//! | lookup edge | _O(1)_ | _O(1)_ | _O(1)_ | +//! | remove vertex | _O(V + E)_ | _O(V)_ | _O(E)_ | +//! | remove edge | _O(d)_ | _O(1)_ | _O(E)_ | +//! | space | _O(V + E)_ | _O(V²)_ | _O(V + E)_ | +//! | multi edge | YES | NO | YES | +//! | stable IDs | NO | NO | NO | +//! +//! * _V_ – vertex count +//! * _E_ – edge count +//! * _d_ – vertex degree +//! * _O*(..)_ – amortized complexity + pub mod adj_list; pub mod adj_matrix; pub mod edge_list; @@ -6,11 +47,8 @@ pub mod stable; #[doc(hidden)] pub mod frozen; - -pub use adj_list::AdjList; -pub use adj_matrix::AdjMatrix; -pub use edge_list::EdgeList; -pub use stable::Stable; +#[doc(inline)] +pub use self::{adj_list::AdjList, adj_matrix::AdjMatrix, edge_list::EdgeList, stable::Stable}; #[doc(hidden)] pub use frozen::Frozen; diff --git a/gryf/src/storage/adj_list.rs b/gryf/src/storage/adj_list.rs index f979045..7dc7880 100644 --- a/gryf/src/storage/adj_list.rs +++ b/gryf/src/storage/adj_list.rs @@ -1,3 +1,10 @@ +//! [Adjacency list] graph representation. +//! +//! See [module](crate::storage) documentation for comparison with other +//! storages. +//! +//! [Adjacency list]: https://en.wikipedia.org/wiki/Adjacency_list + use std::marker::PhantomData; use crate::core::{ @@ -17,6 +24,9 @@ pub use super::shared::{ RangeIds as VertexIds, }; +/// [Adjacency list] graph representation. +/// +/// [Adjacency list]: https://en.wikipedia.org/wiki/Adjacency_list #[derive(Debug, Clone, PartialEq, Eq)] pub struct AdjList { vertices: Vec>, @@ -26,6 +36,7 @@ pub struct AdjList { } impl AdjList { + /// Creates a new empty storage. pub fn new() -> Self { Self { vertices: Vec::new(), @@ -37,6 +48,7 @@ impl AdjList { } impl AdjList { + /// Creates a new empty storage with given ID pair. pub fn with_id() -> AdjList { AdjList::new() } diff --git a/gryf/src/storage/adj_matrix.rs b/gryf/src/storage/adj_matrix.rs index 6b9f5cf..f5a2d08 100644 --- a/gryf/src/storage/adj_matrix.rs +++ b/gryf/src/storage/adj_matrix.rs @@ -1,3 +1,10 @@ +//! [Adjacency matrix] graph representation. +//! +//! See [module](crate::storage) documentation for comparison with other +//! storages. +//! +//! [Adjacency matrix]: https://en.wikipedia.org/wiki/Adjacency_matrix + use std::marker::PhantomData; use crate::core::{ @@ -14,6 +21,9 @@ use crate::core::{ use super::shared; pub use super::shared::{RangeIds as VertexIds, VerticesIter}; +/// [Adjacency matrix] graph representation. +/// +/// [Adjacency matrix]: https://en.wikipedia.org/wiki/Adjacency_matrix #[derive(Debug)] pub struct AdjMatrix { matrix: raw::Matrix, @@ -22,6 +32,7 @@ pub struct AdjMatrix { } impl AdjMatrix { + /// Creates a new empty storage. pub fn new() -> Self { Self { matrix: raw::Matrix::with_capacity(8), @@ -32,6 +43,7 @@ impl AdjMatrix { } impl AdjMatrix { + /// Creates a new empty storage with given ID pair. pub fn with_id() -> AdjMatrix { AdjMatrix::new() } diff --git a/gryf/src/storage/edge_list.rs b/gryf/src/storage/edge_list.rs index d91526f..01b843a 100644 --- a/gryf/src/storage/edge_list.rs +++ b/gryf/src/storage/edge_list.rs @@ -1,3 +1,10 @@ +//! [Edge list] graph representation. +//! +//! See [module](crate::storage) documentation for comparison with other +//! storages. +//! +//! [Edge list]: https://en.wikipedia.org/wiki/Edge_list + use std::{iter::Enumerate, marker::PhantomData, slice}; use crate::core::{ @@ -14,6 +21,9 @@ use crate::core::{ use super::shared; pub use super::shared::{EdgesIter, RangeIds as VertexIds, RangeIds as EdgeIds, VerticesIter}; +/// [Edge list] graph representation. +/// +/// [Edge list]: https://en.wikipedia.org/wiki/Edge_list #[derive(Debug)] pub struct EdgeList { vertices: Vec, @@ -23,6 +33,7 @@ pub struct EdgeList { } impl EdgeList { + /// Creates a new empty storage. pub fn new() -> Self { Self { vertices: Vec::new(), @@ -34,6 +45,7 @@ impl EdgeList { } impl EdgeList { + /// Creates a new empty storage with given ID pair. pub fn with_id() -> EdgeList { EdgeList::new() } diff --git a/gryf/src/storage/stable.rs b/gryf/src/storage/stable.rs index a9eedc3..9603721 100644 --- a/gryf/src/storage/stable.rs +++ b/gryf/src/storage/stable.rs @@ -1,3 +1,9 @@ +//! Graph storage wrapper that makes the IDs +//! [stable](crate::core::props::Stability). +//! +//! The performance properties are inherited from the underlying storage, with +//! extra overhead of keeping track of removed elements. + use std::collections::BTreeSet; use crate::core::{ @@ -13,6 +19,8 @@ use crate::core::{ use gryf_derive::{GraphBase, Guarantee}; +/// Graph storage wrapper that makes the IDs +/// [stable](crate::core::props::Stability). #[derive(Debug, GraphBase, Guarantee)] #[gryf_crate] pub struct Stable { @@ -26,6 +34,7 @@ impl Stable where G: GraphBase, { + /// Wraps a storage. pub fn new(inner: G) -> Self { Self { inner, @@ -34,6 +43,10 @@ where } } + /// Returns the underlying storage with all operations properly applied. + /// + /// Note that there is no guarantee about ID stability of the returned + /// storage. pub fn apply(self) -> G where G: GraphFull, diff --git a/gryf/src/visit.rs b/gryf/src/visit.rs index 150ff5f..7ef2a47 100644 --- a/gryf/src/visit.rs +++ b/gryf/src/visit.rs @@ -1,12 +1,31 @@ +//! Implementations of graph traversal methods. +//! +//! All traversal implementations in this module are **iterative**, that is, +//! they don't use recursion. This means that +//! +//! * 👍 visitor is lazy and can be stopped without tricks, +//! * 👍 visitor state is independent on the graph itself, allowing +//! mutations during traversal, +//! * 👍 traversal is not limited by the size of the program stack, +//! * 👎 there is sometimes extra cost if the [traversal semantics ought +//! to be +//! respected](https://11011110.github.io/blog/2013/12/17/stack-based-graph-traversal.html). +//! +//! The order in which the neighbors of a vertex are discovered is not specified +//! and should not be relied upon. + pub mod bfs; pub mod dfs; pub(crate) mod raw; mod visit_set; -pub use bfs::Bfs; -pub use dfs::{Dfs, DfsEvents, DfsNoBacktrack, DfsPostOrder}; -pub use visit_set::{TypedBitSet, VisitSet}; +#[doc(inline)] +pub use self::{ + bfs::Bfs, + dfs::{Dfs, DfsEvents, DfsNoBacktrack, DfsPostOrder}, + visit_set::{TypedBitSet, VisitSet}, +}; use std::{ collections::{HashSet, VecDeque}, @@ -22,11 +41,61 @@ use crate::core::{ GraphBase, Neighbors, VertexSet, }; +/// Trait for a specific graph traversal approach. +#[doc(alias = "Walker")] pub trait Visitor { + /// The type of the elements being visited. type Item; + /// Advances the visitor and returns the next visited element in given + /// graph. + /// + /// The difference from the [`Iterator::next`] is that the visitor doesn't + /// hold a reference to the graph and thus allows modifications to the graph + /// between individual visitor steps or passing the visitor around without + /// lifetime problems. + /// + /// # Examples + /// + /// ``` + /// use gryf::{ + /// algo::is_cyclic, + /// graph::Graph, + /// visit::{DfsEvent, DfsEvents, Visitor}, + /// }; + /// + /// let mut graph = Graph::<_, (), _>::new_directed(); + /// + /// graph.extend_with_vertices(["a", "b", "c", "d", "e", "f"]); + /// graph.extend_with_edges([(0, 1), (1, 2), (2, 3), (3, 0), (3, 4), (4, 5)]); + /// + /// // There is a cycle. + /// assert!(is_cyclic(&graph)); + /// assert_eq!(graph.edge_count(), 6); + /// + /// let root = graph.find_vertex("a").unwrap(); + /// + /// let mut dfs = DfsEvents::new(&graph); + /// let mut visitor = dfs.start(root); + /// + /// while let Some(event) = visitor.visit_next(&graph) { + /// match event { + /// DfsEvent::BackEdge { edge, .. } => { + /// // Remove edge that would otherwise create a cycle in the graph. + /// graph.remove_edge(edge); + /// } + /// _ => {} + /// } + /// } + /// + /// // There is no cycle anymore. + /// assert!(!is_cyclic(&graph)); + /// assert_eq!(graph.edge_count(), 5); + /// ``` fn visit_next(&mut self, graph: &G) -> Option; + /// Returns an [iterator](Iterator) that uses the visitor to iterate over + /// the elements in given graph. fn iter<'a>(&'a mut self, graph: &'a G) -> Iter<'a, Self, G> where Self: Sized, @@ -37,6 +106,8 @@ pub trait Visitor { } } + /// Converts the visitor into an iterator to visit the elements in given + /// graph. fn into_iter(self, graph: &G) -> IntoIter<'_, Self, G> where Self: Sized, @@ -48,6 +119,7 @@ pub trait Visitor { } } +/// Visitor iterator returned from [`Visitor::iter`]. pub struct Iter<'a, V, G> { visitor: &'a mut V, graph: &'a G, @@ -64,6 +136,7 @@ where } } +/// Visitor iterator returned from [`Visitor::into_iter`]. pub struct IntoIter<'a, V, G> { visitor: V, graph: &'a G, @@ -80,12 +153,28 @@ where } } +/// A collection of starting vertices for a graph traversal. +/// +/// This trait is implemented for any [`Iterator`]. pub trait VisitRoots { + /// Returns next ID to start the traversal from. + /// + /// Note that the returned ID might have already been visited. It is the + /// responsibility of the visitor to ignore such elements. fn next_root(&mut self) -> Option; + /// Returns `true` if the collection can determine that all remaining roots + /// have already been visited based on the currently visited set. + /// + /// This is an optimization for early return without the need of iterating + /// over all roots in cases where this can be determined to be unnecessary. + /// An example is [`VisitAll`] which checks for [`VisitSet::visited_count`] + /// being equal to the number of vertices in the original graph. + /// + /// By default, `false` is returned which effectively delegates the + /// indication of being done for [`VisitRoots::next_root`] by returning + /// `None`. fn is_done(&mut self, _visited: &impl VisitSet) -> bool { - // By default, delegate the indication of being done for `Self::next` by - // returning `None`. false } } @@ -99,6 +188,7 @@ where } } +/// A [`VisitRoots`] collection for visiting all vertices in a graph. pub struct VisitAll<'a, G> where G: VertexSet, @@ -111,6 +201,7 @@ impl<'a, G> VisitAll<'a, G> where G: VertexSet, { + /// Creates the collection from the given graph. pub fn new(graph: &'a G) -> Self { Self { graph, @@ -138,35 +229,87 @@ where } } +/// Strictly monotonically increasing numbering of graph traversal events. +/// +/// This is useful to some algorithms that base their decision on the event time +/// comparison. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Time(pub usize); +/// Depth-first search visitor event. +/// +/// Use [`DfsEvents`] visitor to traverse a graph by reporting DFS events. +/// +/// # Examples +/// +/// See [`DfsEvents`] for examples. #[derive(Debug, Clone, PartialEq, Eq)] pub enum DfsEvent where G: GraphBase, { + /// A new vertex was discovered. Open { + /// Discovered vertex. vertex: G::VertexId, + + /// Discovering time. time: Time, }, + + /// An edge of the tree formed by the traversal. TreeEdge { + /// Source endpoint of the edge. from: G::VertexId, + + /// Target endpoint of the edge. to: G::VertexId, + + /// Edge ID. edge: G::EdgeId, }, + + /// An edge to an already [closed](DfsEvent::Close) vertex. + /// + /// Presence of a back edge indicates a cycle in the graph. BackEdge { + /// Source endpoint of the edge. from: G::VertexId, + + /// Target (closed) endpoint of the edge. to: G::VertexId, + + /// Edge ID. edge: G::EdgeId, }, + + /// An edge to an already [discovered](DfsEvent::Open) but not yet + /// [closed](DfsEvent::Close) vertex. + /// + /// Cross edge is an edge between vertices in different "branches" of the + /// traversal tree. Forward edge is an edge between vertices in the same + /// "branch" of the traversal tree. When the [discover + /// time](DfsEvent::Open::time) of the `from` vertex is higher than `to` + /// vertex, it's cross edge, otherwise it's forward edge. + /// + /// In undirected graphs there is no concept of cross or forward edges. CrossForwardEdge { + /// Source endpoint of the edge. from: G::VertexId, + + /// Target (closed) endpoint of the edge. to: G::VertexId, + + /// Edge ID. edge: G::EdgeId, }, + + /// All edges from the vertex have been reported. Close { + /// Closed vertex. vertex: G::VertexId, + + /// Closing time. time: Time, }, } diff --git a/gryf/src/visit/bfs.rs b/gryf/src/visit/bfs.rs index 5ded482..0283601 100644 --- a/gryf/src/visit/bfs.rs +++ b/gryf/src/visit/bfs.rs @@ -1,5 +1,10 @@ +//! Implementations of [breadth-first search] (BFS) algorithm. +//! +//! [breadth-first search]: https://en.wikipedia.org/wiki/Breadth-first_search + use super::*; +/// Standard BFS traversal over the vertices of a graph. pub struct Bfs where G: GraphBase, @@ -7,6 +12,7 @@ where raw: RawVisit, } +/// [`Bfs`] rooted in a single vertex. pub struct BfsRooted<'a, G> where G: GraphBase, @@ -14,6 +20,7 @@ where raw: &'a mut RawVisit, } +/// [`Bfs`] with possibly multiple roots. pub struct BfsMulti<'a, G, S> where G: GraphBase, @@ -27,6 +34,7 @@ impl Bfs where G: GraphBase, { + #[doc = include_str!("../../docs/include/visit.new.md")] pub fn new(graph: &G) -> Self where G: GraphBase, @@ -36,11 +44,13 @@ where } } + #[doc = include_str!("../../docs/include/visit.start.md")] pub fn start(&mut self, root: G::VertexId) -> BfsRooted<'_, G> { self.raw.start(root); BfsRooted { raw: &mut self.raw } } + #[doc = include_str!("../../docs/include/visit.start_all.md")] pub fn start_all<'a>(&'a mut self, graph: &'a G) -> BfsMulti<'a, G, VisitAll> where G: VertexSet, @@ -51,6 +61,7 @@ where } } + #[doc = include_str!("../../docs/include/visit.start_multi.md")] pub fn start_multi(&mut self, roots: S) -> BfsMulti<'_, G, S> where S: VisitRoots, @@ -61,10 +72,12 @@ where } } + #[doc = include_str!("../../docs/include/visit.reset.md")] pub fn reset(&mut self) { self.raw.reset(); } + #[doc = include_str!("../../docs/include/visit.visited.md")] pub fn visited(&self) -> &impl VisitSet { &self.raw.visited } diff --git a/gryf/src/visit/dfs.rs b/gryf/src/visit/dfs.rs index fec87b6..85ab94c 100644 --- a/gryf/src/visit/dfs.rs +++ b/gryf/src/visit/dfs.rs @@ -1,5 +1,10 @@ +//! Implementations of [depth-first search] (DFS) algorithm. +//! +//! [depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search + use super::*; +/// Standard DFS traversal over the vertices of a graph. pub struct Dfs where G: GraphBase, @@ -7,6 +12,7 @@ where raw: RawVisit, } +/// [`Dfs`] rooted in a single vertex. pub struct DfsRooted<'a, G> where G: GraphBase, @@ -14,6 +20,7 @@ where raw: &'a mut RawVisit, } +/// [`Dfs`] with possibly multiple roots. pub struct DfsMulti<'a, G, S> where G: GraphBase, @@ -27,6 +34,7 @@ impl Dfs where G: GraphBase, { + #[doc = include_str!("../../docs/include/visit.new.md")] pub fn new(graph: &G) -> Self where G: GraphBase, @@ -36,11 +44,13 @@ where } } + #[doc = include_str!("../../docs/include/visit.start.md")] pub fn start(&mut self, root: G::VertexId) -> DfsRooted<'_, G> { self.raw.start(root); DfsRooted { raw: &mut self.raw } } + #[doc = include_str!("../../docs/include/visit.start_all.md")] pub fn start_all<'a>(&'a mut self, graph: &'a G) -> DfsMulti<'a, G, VisitAll> where G: VertexSet, @@ -51,6 +61,7 @@ where } } + #[doc = include_str!("../../docs/include/visit.start_multi.md")] pub fn start_multi(&mut self, roots: S) -> DfsMulti<'_, G, S> where S: VisitRoots, @@ -61,10 +72,12 @@ where } } + #[doc = include_str!("../../docs/include/visit.reset.md")] pub fn reset(&mut self) { self.raw.reset(); } + #[doc = include_str!("../../docs/include/visit.visited.md")] pub fn visited(&self) -> &impl VisitSet { &self.raw.visited } @@ -97,6 +110,60 @@ where } } +/// Standard DFS traversal that produces [`DfsEvent`]s. +/// +/// # Examples +/// +/// ``` +/// use std::collections::BTreeSet; +/// +/// use gryf::{ +/// adapt::Subgraph, +/// algo::{is_connected, is_cyclic}, +/// graph::Graph, +/// visit::{DfsEvent, DfsEvents, Visitor}, +/// }; +/// +/// let mut graph = Graph::<_, (), _>::new_directed(); +/// +/// graph.extend_with_vertices(["a", "b", "c", "d", "e", "f", "g", "h"]); +/// graph.extend_with_edges([ +/// (0, 1), +/// (0, 2), +/// (0, 7), +/// (1, 3), +/// (2, 4), +/// (3, 5), +/// (4, 3), +/// (4, 6), +/// (4, 7), +/// (5, 1), +/// ]); +/// +/// // There is a cycle. +/// assert!(is_cyclic(&graph)); +/// +/// let root = graph.find_vertex("a").unwrap(); +/// +/// let spanning_tree_edges = DfsEvents::new(&graph) +/// .start(root) +/// .into_iter(&graph) +/// .filter_map(|event| { +/// if let DfsEvent::TreeEdge { edge, .. } = event { +/// Some(edge) +/// } else { +/// None +/// } +/// }) +/// .collect::>(); +/// +/// let spanning_tree = Subgraph::with_state(graph, spanning_tree_edges) +/// .filter_edge(|edge, _, state| state.contains(edge)); +/// +/// // The subgraph is a spanning tree. +/// assert!(!is_cyclic(&spanning_tree)); +/// assert!(is_connected(&spanning_tree)); +/// ``` pub struct DfsEvents where G: GraphBase, @@ -106,6 +173,7 @@ where is_directed: bool, } +/// [`DfsEvents`] rooted in a single vertex. pub struct DfsEventsRooted<'a, G> where G: GraphBase, @@ -117,6 +185,7 @@ where is_directed: bool, } +/// [`DfsEvents`] with possibly multiple roots. pub struct DfsEventsMulti<'a, G, S> where G: GraphBase, @@ -134,6 +203,7 @@ impl DfsEvents where G: GraphBase, { + #[doc = include_str!("../../docs/include/visit.new.md")] pub fn new(graph: &G) -> Self where G: GraphBase, @@ -158,6 +228,7 @@ where } } + #[doc = include_str!("../../docs/include/visit.start.md")] pub fn start(&mut self, root: G::VertexId) -> DfsEventsRooted<'_, G> { self.raw.start(RawDfsExtraItem::start(root)); DfsEventsRooted { @@ -169,6 +240,7 @@ where } } + #[doc = include_str!("../../docs/include/visit.start_all.md")] pub fn start_all<'a>(&'a mut self, graph: &'a G) -> DfsEventsMulti<'a, G, VisitAll> where G: VertexSet, @@ -183,6 +255,7 @@ where } } + #[doc = include_str!("../../docs/include/visit.start_multi.md")] pub fn start_multi(&mut self, roots: S) -> DfsEventsMulti<'_, G, S> where S: VisitRoots, @@ -197,10 +270,12 @@ where } } + #[doc = include_str!("../../docs/include/visit.reset.md")] pub fn reset(&mut self) { self.raw.reset(); } + #[doc = include_str!("../../docs/include/visit.visited.md")] pub fn visited(&self) -> &impl VisitSet { &self.raw.visited } @@ -365,6 +440,33 @@ where } } +/// [Post-order DFS] traversal over vertices of a graph. +/// +/// [Post-order DFS]: https://en.wikipedia.org/wiki/Tree_traversal#Post-order,_LRN +/// +/// # Examples +/// +/// ``` +/// use gryf::{ +/// graph::Graph, +/// visit::{DfsPostOrder, Visitor}, +/// }; +/// +/// let mut graph = Graph::<_, (), _>::new_directed(); +/// +/// graph.extend_with_vertices(["a", "b", "c"]); +/// graph.extend_with_edges([(0, 1), (0, 2)]); +/// +/// let root = graph.find_vertex("a").unwrap(); +/// +/// let post_order_last = DfsPostOrder::new(&graph) +/// .start(root) +/// .into_iter(&graph) +/// .last() +/// .unwrap(); +/// +/// assert_eq!(post_order_last, root); +/// ``` pub struct DfsPostOrder where G: GraphBase, @@ -372,6 +474,7 @@ where raw: RawVisit, } +/// [`DfsPostOrder`] rooted in a single vertex. pub struct DfsPostOrderRooted<'a, G> where G: GraphBase, @@ -379,6 +482,7 @@ where raw: &'a mut RawVisit, } +/// [`DfsPostOrder`] with possibly multiple roots. pub struct DfsPostOrderMulti<'a, G, S> where G: GraphBase, @@ -392,6 +496,7 @@ impl DfsPostOrder where G: GraphBase, { + #[doc = include_str!("../../docs/include/visit.new.md")] pub fn new(graph: &G) -> Self where G: GraphBase, @@ -401,11 +506,13 @@ where } } + #[doc = include_str!("../../docs/include/visit.start.md")] pub fn start(&mut self, root: G::VertexId) -> DfsPostOrderRooted<'_, G> { self.raw.start(RawDfsExtraItem::start(root)); DfsPostOrderRooted { raw: &mut self.raw } } + #[doc = include_str!("../../docs/include/visit.start_all.md")] pub fn start_all<'a>(&'a mut self, graph: &'a G) -> DfsPostOrderMulti<'a, G, VisitAll> where G: VertexSet, @@ -416,6 +523,7 @@ where } } + #[doc = include_str!("../../docs/include/visit.start_multi.md")] pub fn start_multi(&mut self, roots: S) -> DfsPostOrderMulti<'_, G, S> where S: VisitRoots, @@ -426,10 +534,12 @@ where } } + #[doc = include_str!("../../docs/include/visit.reset.md")] pub fn reset(&mut self) { self.raw.reset(); } + #[doc = include_str!("../../docs/include/visit.visited.md")] pub fn visited(&self) -> &impl VisitSet { &self.raw.visited } @@ -473,6 +583,15 @@ where } } +/// DFS traversal that does not backtrack to explore other branches of the +/// traversal tree after the first one is ended. +/// +/// In other words, this traversal implementation always visits only a single +/// neighbor of a vertex, ignoring the other neighbors. This means that visiting +/// all vertices in the graph is **not guaranteed**. +/// +/// This traversal algorithm is useful in a greedy initialization of some +/// algorithms before switching to the proper yet costly procedure. pub struct DfsNoBacktrack where G: GraphBase, @@ -480,6 +599,7 @@ where raw: RawVisit, } +/// [`DfsNoBacktrack`] rooted in a single vertex. pub struct DfsNoBacktrackRooted<'a, G> where G: GraphBase, @@ -487,6 +607,7 @@ where raw: &'a mut RawVisit, } +/// [`DfsNoBacktrack`] with possibly multiple roots. pub struct DfsNoBacktrackMulti<'a, G, S> where G: GraphBase, @@ -500,6 +621,7 @@ impl DfsNoBacktrack where G: GraphBase, { + #[doc = include_str!("../../docs/include/visit.new.md")] pub fn new(graph: &G) -> Self where G: GraphBase, @@ -509,11 +631,13 @@ where } } + #[doc = include_str!("../../docs/include/visit.start.md")] pub fn start(&mut self, root: G::VertexId) -> DfsNoBacktrackRooted<'_, G> { self.raw.start(root); DfsNoBacktrackRooted { raw: &mut self.raw } } + #[doc = include_str!("../../docs/include/visit.start_all.md")] pub fn start_all<'a>(&'a mut self, graph: &'a G) -> DfsNoBacktrackMulti<'a, G, VisitAll> where G: VertexSet, @@ -524,6 +648,7 @@ where } } + #[doc = include_str!("../../docs/include/visit.start_multi.md")] pub fn start_multi(&mut self, roots: S) -> DfsNoBacktrackMulti<'_, G, S> where S: VisitRoots, @@ -534,10 +659,12 @@ where } } + #[doc = include_str!("../../docs/include/visit.reset.md")] pub fn reset(&mut self) { self.raw.reset(); } + #[doc = include_str!("../../docs/include/visit.visited.md")] pub fn visited(&self) -> &impl VisitSet { &self.raw.visited } diff --git a/gryf/src/visit/visit_set.rs b/gryf/src/visit/visit_set.rs index 4b85d79..5ce3aa6 100644 --- a/gryf/src/visit/visit_set.rs +++ b/gryf/src/visit/visit_set.rs @@ -9,10 +9,21 @@ use fixedbitset::FixedBitSet; use crate::core::id::{IdType, IntegerIdType}; +/// A set of visited vertices or edges. +#[doc(alias = "VisitMap")] pub trait VisitSet { + /// Marks the element as visited. + /// + /// Returns `true` when this is the first time the element is visited. fn visit(&mut self, id: I) -> bool; + + /// Returns `true` if the element is marked as visited. fn is_visited(&self, id: &I) -> bool; + + /// Returns the number of visited elements. fn visited_count(&self) -> usize; + + /// Resets the set of visited elements to be empty. fn reset_visited(&mut self); } @@ -91,12 +102,15 @@ impl VisitSet for TypedBitSet { } } +/// Tiny [`FixedBitSet`] wrapper adding a generic type of the elements the set +/// holds. pub struct TypedBitSet { inner: FixedBitSet, ty: PhantomData, } impl TypedBitSet { + /// Creates a new empty bit set. pub fn new() -> Self { Self { inner: FixedBitSet::new(), @@ -104,6 +118,7 @@ impl TypedBitSet { } } + /// Creates a new empty bit set with given capacity. pub fn with_capacity(capacity: usize) -> Self { Self { inner: FixedBitSet::with_capacity(capacity),