diff --git a/.JuliaFormatter.toml b/.JuliaFormatter.toml new file mode 100644 index 000000000..c7439503e --- /dev/null +++ b/.JuliaFormatter.toml @@ -0,0 +1 @@ +style = "blue" \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 99f218d61..93aa9462e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,23 +5,32 @@ Following these guidelines will reduce friction and improve the speed at which y ## Bug reports -If you notice code that crashes, is incorrect, or is too slow, please file a bug report. The report should be raised as a github issue with a minimal working example that reproduces the condition. +If you notice code that crashes, is incorrect, or is too slow, please file a bug report. The report should be raised as a GitHub issue with a minimal working example that reproduces the condition. The example should include any data needed. If the problem is incorrectness, then please post the correct result along with an incorrect result. Please include version numbers of all relevant libraries and Julia itself. ## Development guidelines -- Correctness is a necessary requirement; efficiency is desirable. Once you have a correct implementation, make a Pull Request (PR) so we can help improve performance. -- PRs should contain one logical enhancement to the codebase. -- Squash commits in a PR. -- If you want to introduce a new feature, open an issue to discuss a feature before you start coding (this maximizes the likelihood of patch acceptance). -- Minimize dependencies on external packages, and avoid introducing new dependencies that would increase the compilation time by a lot. +Here are a few principles to keep in mind when writing a Pull Request (PR). + +### Correctness + +- Correctness is a necessary requirement. Add tests to make sure that any new function displays the right behavior. +- Since Graphs.jl supports multiple implementations of the graph data structure using the `AbstractGraph` [type](https://juliagraphs.github.io/Graphs.jl/latest/types.html#AbstractGraph-Type-1), you should refrain from using the internal fields of structs such as `fadjlist`. Instead, you should use the functions provided in the API. Code that is instrumental to defining a concrete graph type can use the internal structure of that type. - Put type assertions on all function arguments where conflict may arise (use abstract types, `Union`, or `Any` if necessary). -- If the algorithm was presented in a paper, include a reference to the paper (_e.g._, a proper academic citation along with an eprint link). - Take steps to ensure that code works correctly and efficiently on edge cases (disconnected graphs, empty graphs, ...). - We can accept code that does not work for directed graphs as long as it comes with an explanation of what it would take to make it work for directed graphs. -- Prefer the short circuiting conditional over `if`/`else` when convenient, and where state is not explicitly being mutated (*e.g.*, `condition && error("message")` is good; `condition && i += 1` is not). + +### Style + +- Write your code using Invenia's [BlueStyle](https://github.com/invenia/BlueStyle) +- Format it with [JuliaFormatter](https://github.com/domluna/JuliaFormatter.jl) before pushing + +### Efficiency + +- Once you have a correct implementation, make a PR so we can help improve performance. +- Minimize dependencies on external packages, and avoid introducing new dependencies that would increase the compilation time by a lot. - Write code to reuse memory wherever possible. For example: ```julia @@ -34,7 +43,9 @@ function f(g, v) return sum(storage) end ``` + should be rewritten as two functions + ```julia function f(g::AbstractGraph, v::Integer) storage = Vector{Int}(undef, nv(g)) @@ -49,37 +60,42 @@ function f!(g::AbstractGraph, v::Integer, storage::AbstractVector{Int}) return sum(storage) end ``` + This gives users the option of reusing memory and improving performance. -### Minimizing use of internal struct fields +### Misc -Since Graphs supports multiple implementations of the graph data structure using the `AbstractGraph` [type](https://juliagraphs.github.io/Graphs.jl/latest/types.html#AbstractGraph-Type-1), you should refrain from using the internal fields of structs such as `fadjlist`. Instead, you should use the functions provided in the API. -Code that is instrumental to defining a concrete graph type can use the internal structure of that type. +- If the algorithm was presented in a paper, include a reference to the paper (_e.g._, a proper academic citation along with an eprint link). -## Git usage +## Git(Hub) usage ### Getting started on a package contribution -In order to make it easier for you to contribute and review Pull Requests (PRs), -it would be better to be familiar with git fundamentals. - +In order to make it easier for you to contribute and review PRs, it would be better to be familiar with Git fundamentals. Most importantly: + - clone the repository from JuliaGraphs/Graphs.jl -- fork the repository on your own github account +- fork the repository on your own GitHub account - make the modification to the repository, test and document all your changes - push to the fork you created - open a PR. See the [JuMP documentation](https://jump.dev/JuMP.jl/dev/developers/contributing/) for a more detailed guide. -### Advanced: visualize opened PRs locally: +### PR hygiene + +- PRs should contain one logical enhancement to the codebase. +- Squash commits in a PR. +- If you want to introduce a new feature, open an issue to discuss a feature before you start coding (this maximizes the likelihood of patch acceptance). + +### Advanced: visualize opened PRs locally -In order to make it easier for you to review Pull Requests (PRs), you can add this to your git config file, which should be located at `PACKAGE_LOCATION/.git/config`, where `PACKAGE_LOCATION` is where the Graphs.jl was cloned. +In order to make it easier for you to review PRs, you can add this to your git config file, which should be located at `PACKAGE_LOCATION/.git/config`, where `PACKAGE_LOCATION` is where the Graphs.jl was cloned. If you added the package with the `] dev` command, it is likely at `$HOME/.julia/dev/Graphs`. These instructions were taken from [this gist](https://gist.github.com/piscisaureus/3342247). -Locate the section for your github remote in the `.git/config` file. It looks like this: +Locate the section for your GitHub remote in the `.git/config` file. It looks like this: ``` [remote "origin"] @@ -87,7 +103,7 @@ Locate the section for your github remote in the `.git/config` file. It looks li url = git@github.com:JuliaGraphs/Graphs.jl.git ``` -Now add the line `fetch = +refs/pull/*/head:refs/remotes/origin/pr/*` to this section. Obviously, change the github url to match your project's URL. It ends up looking like this: +Now add the line `fetch = +refs/pull/*/head:refs/remotes/origin/pr/*` to this section. Obviously, change the GitHub URL to match your project's URL. It ends up looking like this: ``` [remote "origin"] @@ -96,7 +112,7 @@ Now add the line `fetch = +refs/pull/*/head:refs/remotes/origin/pr/*` to this se fetch = +refs/pull/*/head:refs/remotes/origin/pr/* ``` -Now fetch all the pull requests: +Now fetch all the PRs: ``` $ git fetch origin @@ -108,7 +124,7 @@ From github.com:JuliaGraphs/Graphs.jl ... ``` -To check out a particular pull request: +To check out a particular PR: ``` $ git checkout pr/999 diff --git a/Project.toml b/Project.toml index 0c6ee5839..3aa9256f9 100644 --- a/Project.toml +++ b/Project.toml @@ -16,18 +16,25 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [compat] +Aqua = "0.5" ArnoldiMethod = "0.1, 0.2" Compat = "3.40, 4" DataStructures = "0.17, 0.18" +Documenter = "0.27" Inflate = "0.1.3" +JuliaFormatter = "1" SimpleTraits = "0.9" +StableRNGs = "1" julia = "1.6" [extras] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Base64", "DelimitedFiles", "StableRNGs", "Test"] +test = ["Aqua", "Base64", "DelimitedFiles", "Documenter", "JuliaFormatter", "StableRNGs", "Test"] diff --git a/README.md b/README.md index 4eed5f17e..6d9e20a88 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # Graphs.jl -[![Build Status](https://github.com/JuliaGraphs/Graphs.jl/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/JuliaGraphs/Graphs.jl/actions/workflows/ci.yml?query=branch%3Amaster) [![codecov.io](http://codecov.io/github/JuliaGraphs/Graphs.jl/coverage.svg?branch=master)](http://codecov.io/github/JuliaGraphs/Graphs.jl?branch=master) [![](https://img.shields.io/badge/docs-latest-blue.svg)](https://juliagraphs.org/Graphs.jl/dev/) +[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://juliagraphs.org/Graphs.jl/dev/) +[![Build status](https://github.com/JuliaGraphs/Graphs.jl/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/JuliaGraphs/Graphs.jl/actions/workflows/ci.yml?query=branch%3Amaster) +[![Code coverage](http://codecov.io/github/JuliaGraphs/Graphs.jl/coverage.svg?branch=master)](http://codecov.io/github/JuliaGraphs/Graphs.jl?branch=master) +[![Code style: Blue](https://img.shields.io/badge/code%20style-blue-4495d1.svg)](https://github.com/invenia/BlueStyle) ## Overview diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index 017b6a4ce..76b9c6fef 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -1,22 +1,18 @@ using BenchmarkTools using Graphs - DIGRAPHS = Dict{String,DiGraph}( - "complete100" => complete_digraph(100), - "path500" => path_digraph(500) + "complete100" => complete_digraph(100), "path500" => path_digraph(500) ) GRAPHS = Dict{String,Graph}( - "complete100" => complete_graph(100), - "tutte" => smallgraph(:tutte), - "path500" => path_graph(500) + "complete100" => complete_graph(100), + "tutte" => smallgraph(:tutte), + "path500" => path_graph(500), ) - suite = BenchmarkGroup() include("core.jl") - tune!(suite); -results = run(suite, verbose = true, seconds = 10) +results = run(suite; verbose=true, seconds=10) diff --git a/benchmark/centrality.jl b/benchmark/centrality.jl index 531041c9e..edbd8accb 100644 --- a/benchmark/centrality.jl +++ b/benchmark/centrality.jl @@ -1,26 +1,26 @@ @benchgroup "centrality" begin - @benchgroup "graphs" begin - for (name, g) in GRAPHS - @bench "$(name): degree" Graphs.degree_centrality($g) - @bench "$(name): closeness" Graphs.closeness_centrality($g) - if nv(g) < 1000 - @bench "$(name): betweenness" Graphs.betweenness_centrality($g) - @bench "$(name): katz" Graphs.katz_centrality($g) - end - end - end #graphs - @benchgroup "digraphs" begin - for (name, g) in DIGRAPHS - @bench "$(name): degree" Graphs.degree_centrality($g) - @bench "$(name): closeness" Graphs.closeness_centrality($g) - if nv(g) < 1000 - @bench "$(name): betweenness" Graphs.betweenness_centrality($g) - @bench "$(name): katz" Graphs.katz_centrality($g) - end - if nv(g) < 500 - @bench "$(name): pagerank" Graphs.pagerank($g) - end - end - end # digraphs + @benchgroup "graphs" begin + for (name, g) in GRAPHS + @bench "$(name): degree" Graphs.degree_centrality($g) + @bench "$(name): closeness" Graphs.closeness_centrality($g) + if nv(g) < 1000 + @bench "$(name): betweenness" Graphs.betweenness_centrality($g) + @bench "$(name): katz" Graphs.katz_centrality($g) + end + end + end # graphs + @benchgroup "digraphs" begin + for (name, g) in DIGRAPHS + @bench "$(name): degree" Graphs.degree_centrality($g) + @bench "$(name): closeness" Graphs.closeness_centrality($g) + if nv(g) < 1000 + @bench "$(name): betweenness" Graphs.betweenness_centrality($g) + @bench "$(name): katz" Graphs.katz_centrality($g) + end + if nv(g) < 500 + @bench "$(name): pagerank" Graphs.pagerank($g) + end + end + end # digraphs end # centrality diff --git a/benchmark/connectivity.jl b/benchmark/connectivity.jl index ecf4bfdf0..4ba340919 100644 --- a/benchmark/connectivity.jl +++ b/benchmark/connectivity.jl @@ -1,12 +1,14 @@ @benchgroup "connectivity" begin - @benchgroup "digraphs" begin - for (name, g) in DIGRAPHS - @bench "$(name): strongly_connected_components" Graphs.strongly_connected_components($g) - end - end # digraphs - @benchgroup "graphs" begin - for (name, g) in GRAPHS - @bench "$(name): connected_components" Graphs.connected_components($g) - end - end # graphs + @benchgroup "digraphs" begin + for (name, g) in DIGRAPHS + @bench "$(name): strongly_connected_components" Graphs.strongly_connected_components( + $g + ) + end + end # digraphs + @benchgroup "graphs" begin + for (name, g) in GRAPHS + @bench "$(name): connected_components" Graphs.connected_components($g) + end + end # graphs end # connectivity diff --git a/benchmark/core.jl b/benchmark/core.jl index 10cfceb59..c01e8128d 100644 --- a/benchmark/core.jl +++ b/benchmark/core.jl @@ -1,10 +1,9 @@ suite["core"] = BenchmarkGroup(["nv", "edges", "has_edge"]) - # nv suite["core"]["nv"] = BenchmarkGroup(["graphs", "digraphs"]) -suite["core"]["nv"]["graphs"] = @benchmarkable [nv(g) for (n,g) in $GRAPHS] -suite["core"]["nv"]["digraphs"] = @benchmarkable [nv(g) for (n,g) in $DIGRAPHS] +suite["core"]["nv"]["graphs"] = @benchmarkable [nv(g) for (n, g) in $GRAPHS] +suite["core"]["nv"]["digraphs"] = @benchmarkable [nv(g) for (n, g) in $DIGRAPHS] # iterate edges function iter_edges(g::AbstractGraph) @@ -16,8 +15,8 @@ function iter_edges(g::AbstractGraph) end suite["core"]["edges"] = BenchmarkGroup(["graphs", "digraphs"]) -suite["core"]["edges"]["graphs"] = @benchmarkable [iter_edges(g) for (n,g) in $GRAPHS] -suite["core"]["edges"]["digraphs"] = @benchmarkable [iter_edges(g) for (n,g) in $DIGRAPHS] +suite["core"]["edges"]["graphs"] = @benchmarkable [iter_edges(g) for (n, g) in $GRAPHS] +suite["core"]["edges"]["digraphs"] = @benchmarkable [iter_edges(g) for (n, g) in $DIGRAPHS] # has edge function all_has_edge(g::AbstractGraph) @@ -34,5 +33,5 @@ function all_has_edge(g::AbstractGraph) end suite["core"]["has_edge"] = BenchmarkGroup(["graphs", "digraphs"]) -suite["core"]["has_edge"]["graphs"] = @benchmark [all_has_edge(g) for (n,g) in $GRAPHS] -suite["core"]["has_edge"]["digraphs"] = @benchmark [all_has_edge(g) for (n,g) in $DIGRAPHS] +suite["core"]["has_edge"]["graphs"] = @benchmark [all_has_edge(g) for (n, g) in $GRAPHS] +suite["core"]["has_edge"]["digraphs"] = @benchmark [all_has_edge(g) for (n, g) in $DIGRAPHS] diff --git a/benchmark/edges.jl b/benchmark/edges.jl index daf6ca122..c35329d4d 100644 --- a/benchmark/edges.jl +++ b/benchmark/edges.jl @@ -32,9 +32,9 @@ end n = 10000 @benchgroup "edges" begin - @bench "$(n): fille" fille($n) - @bench "$(n): fillp" fillp($n) - a, b = fille(n), fillp(n) - @bench "$(n): tsume" tsum($a) - @bench "$(n): tsump" tsum($b) + @bench "$(n): fille" fille($n) + @bench "$(n): fillp" fillp($n) + a, b = fille(n), fillp(n) + @bench "$(n): tsume" tsum($a) + @bench "$(n): tsump" tsum($b) end # edges diff --git a/benchmark/insertions.jl b/benchmark/insertions.jl index b81437886..3fdc292a9 100644 --- a/benchmark/insertions.jl +++ b/benchmark/insertions.jl @@ -1,4 +1,4 @@ @benchgroup "insertions" begin - n = 10000 - @bench "ER Generation" g = SimpleGraph($n, 16 * $n) + n = 10000 + @bench "ER Generation" g = SimpleGraph($n, 16 * $n) end diff --git a/benchmark/parallel/egonets.jl b/benchmark/parallel/egonets.jl index 40d318d0a..7faf3a3cf 100644 --- a/benchmark/parallel/egonets.jl +++ b/benchmark/parallel/egonets.jl @@ -22,7 +22,6 @@ using BenchmarkTools return a end - function mapvertices(f, g::Graph) n = nv(g) a = zeros(Int, n) @@ -43,13 +42,13 @@ using BenchmarkTools function comparison(f, g) println("Mulithreaded on $(Threads.nthreads())") - b1 = @benchmark mapvertices($f, $g) + b1 = @benchmark mapvertices($f, $g) println(b1) println("singlethreaded") b2 = @benchmark mapvertices_single($f, $g) println(b2) - println("done") + return println("done") end nv_ = 10000 diff --git a/benchmark/traversals.jl b/benchmark/traversals.jl index eb83e9f17..97b511173 100644 --- a/benchmark/traversals.jl +++ b/benchmark/traversals.jl @@ -1,14 +1,14 @@ @benchgroup "traversals" begin - @benchgroup "graphs" begin - for (name, g) in GRAPHS - @bench "$(name): bfs_tree" Graphs.bfs_tree($g, 1) - @bench "$(name): dfs_tree" Graphs.dfs_tree($g, 1) - end - end # graphs - @benchgroup "digraphs" begin - for (name, g) in DIGRAPHS - @bench "$(name): bfs_tree" Graphs.bfs_tree($g, 1) - @bench "$(name): dfs_tree" Graphs.dfs_tree($g, 1) - end - end # digraphs + @benchgroup "graphs" begin + for (name, g) in GRAPHS + @bench "$(name): bfs_tree" Graphs.bfs_tree($g, 1) + @bench "$(name): dfs_tree" Graphs.dfs_tree($g, 1) + end + end # graphs + @benchgroup "digraphs" begin + for (name, g) in DIGRAPHS + @bench "$(name): bfs_tree" Graphs.bfs_tree($g, 1) + @bench "$(name): dfs_tree" Graphs.dfs_tree($g, 1) + end + end # digraphs end # traversals diff --git a/docs/Manifest.toml b/docs/Manifest.toml index c38e12e1b..d52a93d35 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -1,10 +1,8 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.7.2" +julia_version = "1.8.2" manifest_format = "2.0" - -[[deps.ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +project_hash = "4fb688147559360c201b0f727fe4bd07d6143f40" [[deps.ArnoldiMethod]] deps = ["LinearAlgebra", "Random", "StaticArrays"] @@ -19,29 +17,26 @@ uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" [[deps.Compat]] -deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "b153278a25dd42c65abbf4e62344f9d22e59191b" +deps = ["Dates", "LinearAlgebra", "UUIDs"] +git-tree-sha1 = "aaabba4ce1b7f8a9b34c015053d3b1edf60fa49c" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "3.43.0" +version = "4.4.0" [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "0.5.2+0" [[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "cc1a8e22627f33c789ab60b36a9132ac050bbf75" +git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.12" +version = "0.18.13" [[deps.Dates]] deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" -[[deps.DelimitedFiles]] -deps = ["Mmap"] -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" - [[deps.Distributed]] deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" @@ -58,15 +53,11 @@ git-tree-sha1 = "3ebb967819b284dc1e3c0422229b58a40a255649" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" version = "0.26.3" -[[deps.Downloads]] -deps = ["ArgTools", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" - [[deps.Graphs]] deps = ["ArnoldiMethod", "Compat", "DataStructures", "Distributed", "Inflate", "LinearAlgebra", "Random", "SharedArrays", "SimpleTraits", "SparseArrays", "Statistics"] path = ".." uuid = "86223c79-3864-5bf0-83f7-82e725a168b6" -version = "1.6.0" +version = "1.7.4" [[deps.IOCapture]] deps = ["Logging"] @@ -75,9 +66,9 @@ uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" version = "0.1.1" [[deps.Inflate]] -git-tree-sha1 = "f5fc07d4e706b84f72d54eedcc1c13d92fb0871c" +git-tree-sha1 = "5cd07aab533df5170988219191dfad0519391428" uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" -version = "0.1.2" +version = "0.1.3" [[deps.InteractiveUtils]] deps = ["Markdown"] @@ -89,22 +80,10 @@ git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" version = "0.21.3" -[[deps.LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" - -[[deps.LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" - [[deps.LibGit2]] deps = ["Base64", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" -[[deps.LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "MbedTLS_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" - [[deps.Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" @@ -117,30 +96,25 @@ uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" [[deps.MacroTools]] deps = ["Markdown", "Random"] -git-tree-sha1 = "3d3e902b31198a27340d0bf00d6ac452866021cf" +git-tree-sha1 = "42324d08725e200c23d4dfb549e0d5d89dede2d2" uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.9" +version = "0.5.10" [[deps.Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" -[[deps.MbedTLS_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" - [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" -[[deps.MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" - [[deps.NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.20+0" [[deps.OrderedCollections]] git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" @@ -148,14 +122,10 @@ uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" version = "1.4.1" [[deps.Parsers]] -deps = ["Dates"] -git-tree-sha1 = "1285416549ccfcdf0c50d4997a94331e88d68413" +deps = ["Dates", "SnoopPrecompile"] +git-tree-sha1 = "b64719e8b4504983c7fca6cc9db3ebc8acc2a4d6" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.3.1" - -[[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "2.5.1" [[deps.Printf]] deps = ["Unicode"] @@ -171,6 +141,7 @@ uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" @@ -185,6 +156,11 @@ git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" version = "0.9.4" +[[deps.SnoopPrecompile]] +git-tree-sha1 = "f604441450a3c0569830946e5b33b78c928e1a85" +uuid = "66db9d55-30c0-4569-8b51-7e840670fc0c" +version = "1.0.1" + [[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" @@ -193,23 +169,20 @@ deps = ["LinearAlgebra", "Random"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [[deps.StaticArrays]] -deps = ["LinearAlgebra", "Random", "Statistics"] -git-tree-sha1 = "cd56bf18ed715e8b09f06ef8c6b781e6cdc49911" +deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"] +git-tree-sha1 = "4e051b85454b4e4f66e6a6b7bdc452ad9da3dcf6" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.4.4" +version = "1.5.10" + +[[deps.StaticArraysCore]] +git-tree-sha1 = "6b7ba252635a5eff6a0b0664a41ee140a1c9e72a" +uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" +version = "1.4.0" [[deps.Statistics]] deps = ["LinearAlgebra", "SparseArrays"] uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -[[deps.TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" - -[[deps.Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" - [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" @@ -221,18 +194,7 @@ uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" [[deps.Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" -[[deps.Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" - [[deps.libblastrampoline_jll]] deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" - -[[deps.nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" - -[[deps.p7zip_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "5.1.1+0" diff --git a/docs/make.jl b/docs/make.jl index e9dd06a9f..7e40b3a68 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -35,10 +35,7 @@ pages_files = [ "first_steps/plotting.md", "first_steps/persistence.md", ], - "Ecosystem" => [ - "ecosystem/graphtypes.md", - "ecosystem/interface.md", - ], + "Ecosystem" => ["ecosystem/graphtypes.md", "ecosystem/interface.md"], "Core API" => [ "core_functions/core.md", "core_functions/interface.md", @@ -78,7 +75,7 @@ pages_files = [ ], ] -pages=[ +pages = [ section_name => [ get_title(joinpath(normpath(@__FILE__, ".."), "src", file)) => file for file in section_files diff --git a/src/Experimental/Experimental.jl b/src/Experimental/Experimental.jl index b4c6aeaec..9506e47ee 100644 --- a/src/Experimental/Experimental.jl +++ b/src/Experimental/Experimental.jl @@ -4,12 +4,22 @@ using Graphs using Graphs.SimpleGraphs export description, - #isomorphism - VF2, vf2, IsomorphismProblem, SubgraphIsomorphismProblem, InducedSubgraphIsomorphismProblem, - could_have_isomorph, has_isomorph, all_isomorph, count_isomorph, - has_induced_subgraphisomorph, count_induced_subgraphisomorph, all_induced_subgraphisomorph, - has_subgraphisomorph, count_subgraphisomorph, all_subgraphisomorph, - + # isomorphism + VF2, + vf2, + IsomorphismProblem, + SubGraphIsomorphismProblem, + InducedSubGraphIsomorphismProblem, + could_have_isomorph, + has_isomorph, + all_isomorph, + count_isomorph, + has_induced_subgraphisomorph, + count_induced_subgraphisomorph, + all_induced_subgraphisomorph, + has_subgraphisomorph, + count_subgraphisomorph, + all_subgraphisomorph, ShortestPaths description() = "This module contains experimental graph functions." diff --git a/src/Experimental/Parallel/traversals/gdistances.jl b/src/Experimental/Parallel/traversals/gdistances.jl index 41eddeb11..722e63025 100644 --- a/src/Experimental/Parallel/traversals/gdistances.jl +++ b/src/Experimental/Parallel/traversals/gdistances.jl @@ -7,15 +7,12 @@ partition_sources!(queue_list, sources) Partition `sources` using [`Graphs.unweighted_contiguous_partition`](@ref) and place -the i^{th} partition into `queue_list[i]` and set to empty_list[i] to true if the +the i^{th} partition into `queue_list[i]` and set to empty_list[i] to true if the i^{th} partition is empty. """ function partition_sources!( - queue_list::Vector{Vector{T}}, - sources::Vector{<:Integer}, - empty_list::Vector{Bool} - ) where T<:Integer - + queue_list::Vector{Vector{T}}, sources::Vector{<:Integer}, empty_list::Vector{Bool} +) where {T<:Integer} partitions = Graphs.unweighted_contiguous_partition(length(sources), length(queue_list)) for (i, p) in enumerate(partitions) append!(queue_list[i], sources[p]) @@ -35,16 +32,15 @@ at a time. For graphs with uniform degree, a larger value of `queue_segment_size improve performance. ### References -- [Avoiding Locks and Atomic Instructions in Shared-Memory Parallel BFS Using Optimistic +- [Avoiding Locks and Atomic Instructions in Shared-Memory Parallel BFS Using Optimistic Parallelization](https://www.computer.org/csdl/proceedings/ipdpsw/2013/4979/00/4979b628-abs.html). """ function gdistances!( - g::AbstractGraph{T}, + g::AbstractGraph{T}, sources::Vector{<:Integer}, vert_level::Vector{T}; - queue_segment_size::Integer=20 - ) where T <:Integer - + queue_segment_size::Integer=20, +) where {T<:Integer} @warn "Graphs.Experimental.Parallel.gdistances is very likely broken at the moment and can return unreliable results." nvg = nv(g) @@ -53,13 +49,13 @@ function gdistances!( fill!(vert_level, typemax(T)) visited = zeros(Bool, nvg) - #bitVector not thread safe + # bitVector not thread safe next_level_t = [sizehint!(Vector{T}(), cld(nvg, n_t)) for _ in Base.OneTo(n_t)] cur_level_t = [sizehint!(Vector{T}(), cld(nvg, n_t)) for _ in Base.OneTo(n_t)] cur_front_t = ones(T, n_t) queue_explored_t = zeros(Bool, n_t) - for s in sources + for s in sources visited[s] = true vert_level[s] = zero(T) end @@ -70,43 +66,43 @@ function gdistances!( while !is_cur_level_t_empty n_level += one(T) - let n_level=n_level # let block used due to bug #15276 - @threads for thread_id in Base.OneTo(n_t) - #Explore current level in parallel - @inbounds next_level = next_level_t[thread_id] - - @inbounds for t_range in (thread_id:n_t, 1:(thread_id-1)), t in t_range - queue_explored_t[t] && continue - cur_level = cur_level_t[t] - cur_len = length(cur_level) - - # Explore cur_level_t[t] one segment at a time. - while true - local_front = cur_front_t[t] # Data race, but first read always succeeds - cur_front_t[t] += segment_size # Failure of increment is acceptable - - (local_front > cur_len || local_front <= zero(T)) && break - while local_front <= cur_len && cur_level[local_front] != zero(T) - v = cur_level[local_front] - cur_level[local_front] = zero(T) - local_front += one(T) - - # Check if v was successfully read. - (visited[v] && vert_level[v] == n_level-one(T)) || continue - for i in outneighbors(g, v) - # Data race, but first read on visited[i] always succeeds - if !visited[i] - vert_level[i] = n_level - #Concurrent visited[i] = true always succeeds - visited[i] = true - push!(next_level, i) + let n_level = n_level # let block used due to bug #15276 + @threads for thread_id in Base.OneTo(n_t) + # Explore current level in parallel + @inbounds next_level = next_level_t[thread_id] + + @inbounds for t_range in (thread_id:n_t, 1:(thread_id - 1)), t in t_range + queue_explored_t[t] && continue + cur_level = cur_level_t[t] + cur_len = length(cur_level) + + # Explore cur_level_t[t] one segment at a time. + while true + local_front = cur_front_t[t] # Data race, but first read always succeeds + cur_front_t[t] += segment_size # Failure of increment is acceptable + + (local_front > cur_len || local_front <= zero(T)) && break + while local_front <= cur_len && cur_level[local_front] != zero(T) + v = cur_level[local_front] + cur_level[local_front] = zero(T) + local_front += one(T) + + # Check if v was successfully read. + (visited[v] && vert_level[v] == n_level - one(T)) || continue + for i in outneighbors(g, v) + # Data race, but first read on visited[i] always succeeds + if !visited[i] + vert_level[i] = n_level + # Concurrent visited[i] = true always succeeds + visited[i] = true + push!(next_level, i) + end end end - end + end + queue_explored_t[t] = true end - queue_explored_t[t] = true - end - end + end end is_cur_level_t_empty = true @@ -116,14 +112,20 @@ function gdistances!( empty!(next_level_t[t]) queue_explored_t[t] = isempty(cur_level_t[t]) is_cur_level_t_empty = is_cur_level_t_empty && queue_explored_t[t] - end + end end return vert_level end -gdistances!(g::AbstractGraph{T}, source::Integer, vert_level::Vector{T}; queue_segment_size::Integer=20) where T<:Integer = -gdistances!(g, [source,], vert_level; queue_segment_size=20) +function gdistances!( + g::AbstractGraph{T}, + source::Integer, + vert_level::Vector{T}; + queue_segment_size::Integer=20, +) where {T<:Integer} + return gdistances!(g, [source], vert_level; queue_segment_size=20) +end """ gdistances(g, sources; queue_segment_size=20) @@ -136,11 +138,17 @@ Parallel implementation of [`Graphs.gdistances!`](@ref) with dynamic load balanc For denser graphs, a smaller value of `queue_segment_size` could improve performance. ### References -- [Avoiding Locks and Atomic Instructions in Shared-Memory Parallel BFS Using Optimistic +- [Avoiding Locks and Atomic Instructions in Shared-Memory Parallel BFS Using Optimistic Parallelization](https://www.computer.org/csdl/proceedings/ipdpsw/2013/4979/00/4979b628-abs.html). """ -gdistances(g::AbstractGraph{T}, sources::Vector{<:Integer}; queue_segment_size::Integer=20) where T<:Integer = -gdistances!(g, sources, Vector{T}(undef, nv(g)); queue_segment_size=20) +function gdistances( + g::AbstractGraph{T}, sources::Vector{<:Integer}; queue_segment_size::Integer=20 +) where {T<:Integer} + return gdistances!(g, sources, Vector{T}(undef, nv(g)); queue_segment_size=20) +end -gdistances(g::AbstractGraph{T}, source::Integer; queue_segment_size::Integer=20) where T<:Integer = -gdistances!(g, [source,], Vector{T}(undef, nv(g)); queue_segment_size=20) +function gdistances( + g::AbstractGraph{T}, source::Integer; queue_segment_size::Integer=20 +) where {T<:Integer} + return gdistances!(g, [source], Vector{T}(undef, nv(g)); queue_segment_size=20) +end diff --git a/src/Experimental/ShortestPaths/ShortestPaths.jl b/src/Experimental/ShortestPaths/ShortestPaths.jl index e8efed193..d6d690c0f 100644 --- a/src/Experimental/ShortestPaths/ShortestPaths.jl +++ b/src/Experimental/ShortestPaths/ShortestPaths.jl @@ -4,9 +4,10 @@ using Graphs using Graphs.Experimental.Traversals using Graphs: AbstractGraph, AbstractEdge using Graphs.SimpleGraphs: AbstractSimpleGraph -using DataStructures:PriorityQueue, enqueue!, dequeue! +using DataStructures: PriorityQueue, enqueue!, dequeue! -import Graphs.Experimental.Traversals: initfn!, previsitfn!, newvisitfn!, visitfn!, postvisitfn!, postlevelfn! +import Graphs.Experimental.Traversals: + initfn!, previsitfn!, newvisitfn!, visitfn!, postvisitfn!, postlevelfn! # TODO: figure out how we keep environmental params. # struct LGEnvironment @@ -32,7 +33,6 @@ calculation. """ abstract type ShortestPathResult <: AbstractGraphResult end - """ ShortestPathAlgorithm <: AbstractGraphAlgorithm @@ -57,7 +57,6 @@ include("floyd-warshall.jl") include("johnson.jl") include("spfa.jl") - ################################ # Shortest Paths via algorithm # ################################ @@ -99,12 +98,17 @@ s4 = shortest_paths(g, 1, BellmanFord()) s5 = shortest_paths(g, 1, w, DEsopoPape()) ``` """ -shortest_paths(g::AbstractGraph, s, alg::ShortestPathAlgorithm) = - shortest_paths(g, s, weights(g), alg) +function shortest_paths(g::AbstractGraph, s, alg::ShortestPathAlgorithm) + return shortest_paths(g, s, weights(g), alg) +end # If we don't specify an algorithm AND there are no dists, use BFS. -shortest_paths(g::AbstractGraph{T}, s::Integer) where {T<:Integer} = shortest_paths(g, s, BFS()) -shortest_paths(g::AbstractGraph{T}, ss::AbstractVector) where {T<:Integer} = shortest_paths(g, ss, BFS()) +function shortest_paths(g::AbstractGraph{T}, s::Integer) where {T<:Integer} + return shortest_paths(g, s, BFS()) +end +function shortest_paths(g::AbstractGraph{T}, ss::AbstractVector) where {T<:Integer} + return shortest_paths(g, ss, BFS()) +end # Full-formed methods. """ @@ -137,7 +141,7 @@ function paths(state::ShortestPathResult, vs::AbstractVector{<:Integer}) num_vs = length(vs) all_paths = Vector{Vector{T}}(undef, num_vs) - for i = 1:num_vs + for i in 1:num_vs all_paths[i] = Vector{T}() index = T(vs[i]) if parents[index] != 0 || parents[index] == index @@ -193,7 +197,9 @@ julia> has_negative_weight_cycle(g, d, SPFA()) false ``` """ -has_negative_weight_cycle(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) = has_negative_weight_cycle(g, distmx, BellmanFord()) +function has_negative_weight_cycle(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) + return has_negative_weight_cycle(g, distmx, BellmanFord()) +end has_negative_weight_cycle(g::AbstractSimpleGraph) = false export ShortestPathAlgorithm diff --git a/src/Experimental/ShortestPaths/astar.jl b/src/Experimental/ShortestPaths/astar.jl index 7da44fdd8..f12ad5a34 100644 --- a/src/Experimental/ShortestPaths/astar.jl +++ b/src/Experimental/ShortestPaths/astar.jl @@ -22,25 +22,27 @@ end AStar(T::Type=Float64) = AStar(n -> zero(T)) -struct AStarResult{T, U<:Integer} <: ShortestPathResult +struct AStarResult{T,U<:Integer} <: ShortestPathResult path::Vector{U} dist::T end -function reconstruct_path!(total_path, # a vector to be filled with the shortest path +function reconstruct_path!( + total_path, # a vector to be filled with the shortest path came_from, # a vector holding the parent of each node in the A* exploration end_idx, # the end vertex - g) # the graph - + g, +) # the graph curr_idx = end_idx while came_from[curr_idx] != curr_idx pushfirst!(total_path, came_from[curr_idx]) curr_idx = came_from[curr_idx] end - push!(total_path, end_idx) + return push!(total_path, end_idx) end -function a_star_impl!(g, # the graph +function a_star_impl!( + g, # the graph goal, # the end vertex open_set, # an initialized heap containing the active vertices closed_set, # an (initialized) color-map to indicate status of vertices @@ -48,8 +50,8 @@ function a_star_impl!(g, # the graph f_score, # a vector holding f scores for each node came_from, # a vector holding the parent of each node in the A* exploration distmx, - heuristic) - + heuristic, +) T = eltype(g) total_path = Vector{T}() @@ -93,13 +95,15 @@ function calc_dist(path, distmx) return dist end -function shortest_paths(g::AbstractGraph, s::Integer, t::Integer, distmx::AbstractMatrix, alg::AStar) +function shortest_paths( + g::AbstractGraph, s::Integer, t::Integer, distmx::AbstractMatrix, alg::AStar +) T = eltype(distmx) # if we do checkbounds here, we can use @inbounds in a_star_impl! checkbounds(distmx, Base.OneTo(nv(g)), Base.OneTo(nv(g))) - open_set = PriorityQueue{Integer, T}() + open_set = PriorityQueue{Integer,T}() enqueue!(open_set, s, 0) closed_set = zeros(Bool, nv(g)) @@ -113,13 +117,16 @@ function shortest_paths(g::AbstractGraph, s::Integer, t::Integer, distmx::Abstra came_from = -ones(Integer, nv(g)) came_from[s] = s - path = a_star_impl!(g, t, open_set, closed_set, g_score, f_score, came_from, distmx, alg.heuristic) + path = a_star_impl!( + g, t, open_set, closed_set, g_score, f_score, came_from, distmx, alg.heuristic + ) return AStarResult(path, calc_dist(path, distmx)) end -shortest_paths(g::AbstractGraph, s::Integer, t::Integer, alg::AStar) = shortest_paths(g, s, t, weights(g), alg) +function shortest_paths(g::AbstractGraph, s::Integer, t::Integer, alg::AStar) + return shortest_paths(g, s, t, weights(g), alg) +end paths(s::AStarResult) = [s.path] paths(s::AStarResult, v::Integer) = throw(ArgumentError("AStar produces at most one path.")) dists(s::AStarResult) = [[s.dist]] dists(s::AStarResult, v::Integer) = throw(ArgumentError("AStar produces at most one path.")) - diff --git a/src/Experimental/ShortestPaths/bellman-ford.jl b/src/Experimental/ShortestPaths/bellman-ford.jl index 22718c68c..975045108 100644 --- a/src/Experimental/ShortestPaths/bellman-ford.jl +++ b/src/Experimental/ShortestPaths/bellman-ford.jl @@ -19,7 +19,7 @@ No fields are specified or required. - all destinations """ struct BellmanFord <: ShortestPathAlgorithm end -struct BellmanFordResult{T, U<:Integer} <: ShortestPathResult +struct BellmanFordResult{T,U<:Integer} <: ShortestPathResult parents::Vector{U} dists::Vector{T} end @@ -28,9 +28,8 @@ function shortest_paths( graph::AbstractGraph{U}, sources::AbstractVector{<:Integer}, distmx::AbstractMatrix{T}, - ::BellmanFord - ) where {T, U<:Integer} - + ::BellmanFord, +) where {T,U<:Integer} nvg = nv(graph) active = falses(nvg) active[sources] .= true @@ -61,12 +60,17 @@ function shortest_paths( return BellmanFordResult(parents, dists) end -shortest_paths(g::AbstractGraph, v::Integer, distmx::AbstractMatrix, alg::BellmanFord) = - shortest_paths(g, [v], distmx, alg) +function shortest_paths( + g::AbstractGraph, v::Integer, distmx::AbstractMatrix, alg::BellmanFord +) + return shortest_paths(g, [v], distmx, alg) +end has_negative_weight_cycle(g::AbstractGraph, ::BellmanFord) = false -function has_negative_weight_cycle(g::AbstractGraph, distmx::AbstractMatrix, alg::BellmanFord) +function has_negative_weight_cycle( + g::AbstractGraph, distmx::AbstractMatrix, alg::BellmanFord +) try shortest_paths(g, vertices(g), distmx, alg) catch e diff --git a/src/Experimental/ShortestPaths/bfs.jl b/src/Experimental/ShortestPaths/bfs.jl index c25c648f2..cebb00cf5 100644 --- a/src/Experimental/ShortestPaths/bfs.jl +++ b/src/Experimental/ShortestPaths/bfs.jl @@ -1,6 +1,5 @@ using Graphs.Experimental.Traversals - """ struct BFS <: ShortestPathAlgorithm @@ -37,12 +36,12 @@ end s.dists[u] = 0 return true end -@inline function newvisitfn!(s::BFSSPState, u, v) - s.dists[v] = s.n_level - s.parents[v] = u - return true +@inline function newvisitfn!(s::BFSSPState, u, v) + s.dists[v] = s.n_level + s.parents[v] = u + return true end -@inline function postlevelfn!(s::BFSSPState{U}) where U +@inline function postlevelfn!(s::BFSSPState{U}) where {U} s.n_level += one(U) return true end @@ -53,11 +52,8 @@ struct BFSResult{U<:Integer} <: ShortestPathResult end function shortest_paths( - g::AbstractGraph{U}, - ss::AbstractVector{U}, - alg::BFS - ) where U <: Integer - + g::AbstractGraph{U}, ss::AbstractVector{U}, alg::BFS +) where {U<:Integer} n = nv(g) dists = fill(typemax(U), n) parents = zeros(U, n) @@ -66,8 +62,11 @@ function shortest_paths( return BFSResult(state.parents, state.dists) end - - - -shortest_paths(g::AbstractGraph{U}, ss::AbstractVector{<:Integer}, alg::BFS) where {U<:Integer} = shortest_paths(g, U.(ss), alg) -shortest_paths(g::AbstractGraph{U}, s::Integer, alg::BFS) where {U<:Integer} = shortest_paths(g, Vector{U}([s]), alg) +function shortest_paths( + g::AbstractGraph{U}, ss::AbstractVector{<:Integer}, alg::BFS +) where {U<:Integer} + return shortest_paths(g, U.(ss), alg) +end +function shortest_paths(g::AbstractGraph{U}, s::Integer, alg::BFS) where {U<:Integer} + return shortest_paths(g, Vector{U}([s]), alg) +end diff --git a/src/Experimental/ShortestPaths/desopo-pape.jl b/src/Experimental/ShortestPaths/desopo-pape.jl index 766b04536..5c7bfa900 100644 --- a/src/Experimental/ShortestPaths/desopo-pape.jl +++ b/src/Experimental/ShortestPaths/desopo-pape.jl @@ -11,12 +11,14 @@ No fields are specified or required. - all destinations """ struct DEsopoPape <: ShortestPathAlgorithm end -struct DEsopoPapeResult{T, U<:Integer} <: ShortestPathResult +struct DEsopoPapeResult{T,U<:Integer} <: ShortestPathResult parents::Vector{U} dists::Vector{T} end -function shortest_paths(g::AbstractGraph, src::Integer, distmx::AbstractMatrix, ::DEsopoPape) +function shortest_paths( + g::AbstractGraph, src::Integer, distmx::AbstractMatrix, ::DEsopoPape +) T = eltype(distmx) U = eltype(g) nvg = nv(g) @@ -27,17 +29,17 @@ function shortest_paths(g::AbstractGraph, src::Integer, distmx::AbstractMatrix, state = fill(Int8(2), nvg) q = U[src] @inbounds dists[src] = 0 - + @inbounds while !isempty(q) u = popfirst!(q) state[u] = 0 - + for v in outneighbors(g, u) alt = dists[u] + distmx[u, v] if (dists[v] > alt) dists[v] = alt parents[v] = u - + if state[v] == 2 state[v] = 1 push!(q, v) @@ -48,6 +50,6 @@ function shortest_paths(g::AbstractGraph, src::Integer, distmx::AbstractMatrix, end end end - - return DEsopoPapeResult{T, U}(parents, dists) + + return DEsopoPapeResult{T,U}(parents, dists) end diff --git a/src/Experimental/ShortestPaths/dijkstra.jl b/src/Experimental/ShortestPaths/dijkstra.jl index cc3140d72..9d9baea98 100644 --- a/src/Experimental/ShortestPaths/dijkstra.jl +++ b/src/Experimental/ShortestPaths/dijkstra.jl @@ -1,4 +1,4 @@ -struct DijkstraResult{T, U<:Integer} <: ShortestPathResult +struct DijkstraResult{T,U<:Integer} <: ShortestPathResult parents::Vector{U} dists::Vector{T} predecessors::Vector{Vector{U}} @@ -40,10 +40,11 @@ struct Dijkstra <: ShortestPathAlgorithm track_vertices::Bool end -Dijkstra(;all_paths=false, track_vertices=false) = Dijkstra(all_paths, track_vertices) +Dijkstra(; all_paths=false, track_vertices=false) = Dijkstra(all_paths, track_vertices) - -function shortest_paths(g::AbstractGraph, srcs::Vector{U}, distmx::AbstractMatrix{T}, alg::Dijkstra) where {T, U<:Integer} +function shortest_paths( + g::AbstractGraph, srcs::Vector{U}, distmx::AbstractMatrix{T}, alg::Dijkstra +) where {T,U<:Integer} nvg = nv(g) dists = fill(typemax(T), nvg) parents = zeros(U, nvg) @@ -118,10 +119,13 @@ function shortest_paths(g::AbstractGraph, srcs::Vector{U}, distmx::AbstractMatri empty!(preds[src]) end - return DijkstraResult{T, U}(parents, dists, preds, pathcounts, closest_vertices) + return DijkstraResult{T,U}(parents, dists, preds, pathcounts, closest_vertices) end -shortest_paths(g::AbstractGraph, s::Integer, distmx::AbstractMatrix, alg::Dijkstra) = shortest_paths(g, [s], distmx, alg) +function shortest_paths(g::AbstractGraph, s::Integer, distmx::AbstractMatrix, alg::Dijkstra) + return shortest_paths(g, [s], distmx, alg) +end # If we don't specify an algorithm, use dijkstra. -shortest_paths(g::AbstractGraph, s, distmx::AbstractMatrix) = shortest_paths(g, s, distmx, Dijkstra()) - +function shortest_paths(g::AbstractGraph, s, distmx::AbstractMatrix) + return shortest_paths(g, s, distmx, Dijkstra()) +end diff --git a/src/Experimental/ShortestPaths/floyd-warshall.jl b/src/Experimental/ShortestPaths/floyd-warshall.jl index ceac54b44..3e7208551 100644 --- a/src/Experimental/ShortestPaths/floyd-warshall.jl +++ b/src/Experimental/ShortestPaths/floyd-warshall.jl @@ -19,12 +19,14 @@ No additional configuration parameters are specified or required. Space complexity is on the order of ``\\mathcal{O}(|V|^2)``. """ struct FloydWarshall <: ShortestPathAlgorithm end -struct FloydWarshallResult{T, U<:Integer} <: ShortestPathResult +struct FloydWarshallResult{T,U<:Integer} <: ShortestPathResult dists::Matrix{T} parents::Matrix{U} end -function shortest_paths(g::AbstractGraph{U}, distmx::AbstractMatrix{T}, ::FloydWarshall) where {T, U<:Integer} +function shortest_paths( + g::AbstractGraph{U}, distmx::AbstractMatrix{T}, ::FloydWarshall +) where {T,U<:Integer} nvg = nv(g) # if we do checkbounds here, we can use @inbounds later checkbounds(distmx, Base.OneTo(nvg), Base.OneTo(nvg)) @@ -57,7 +59,7 @@ function shortest_paths(g::AbstractGraph{U}, distmx::AbstractMatrix{T}, ::FloydW d == typemax(T) && continue p = parents[pivot, v] for u in vertices(g) - ans = (fwdists[u, pivot] == typemax(T) ? typemax(T) : fwdists[u, pivot] + d) + ans = (fwdists[u, pivot] == typemax(T) ? typemax(T) : fwdists[u, pivot] + d) if fwdists[u, v] > ans fwdists[u, v] = ans parents[u, v] = p @@ -69,7 +71,7 @@ function shortest_paths(g::AbstractGraph{U}, distmx::AbstractMatrix{T}, ::FloydW return fws end -function paths(s::FloydWarshallResult{T, U}, v::Integer) where {T, U<:Integer} +function paths(s::FloydWarshallResult{T,U}, v::Integer) where {T,U<:Integer} pathinfo = s.parents[v, :] fwpaths = Vector{Vector{U}}() for i in 1:length(pathinfo) @@ -95,8 +97,9 @@ end shortest_paths(g::AbstractGraph, alg::FloydWarshall) = shortest_paths(g, weights(g), alg) # If we don't specify an algorithm AND there's no source, use Floyd-Warshall. -shortest_paths(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) = - shortest_paths(g, distmx, FloydWarshall()) +function shortest_paths(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) + return shortest_paths(g, distmx, FloydWarshall()) +end paths(s::FloydWarshallResult) = [paths(s, v) for v in 1:size(s.parents, 1)] paths(st::FloydWarshallResult, s::Integer, d::Integer) = paths(st, s)[d] diff --git a/src/Experimental/ShortestPaths/johnson.jl b/src/Experimental/ShortestPaths/johnson.jl index 7d6e2aa01..94c865927 100644 --- a/src/Experimental/ShortestPaths/johnson.jl +++ b/src/Experimental/ShortestPaths/johnson.jl @@ -19,29 +19,32 @@ Complexity: O(|V|*|E|) """ struct Johnson <: ShortestPathAlgorithm end -struct JohnsonResult{T, U<:Integer} <: ShortestPathResult +struct JohnsonResult{T,U<:Integer} <: ShortestPathResult parents::Matrix{U} dists::Matrix{T} end -function shortest_paths(g::AbstractGraph{U}, distmx::AbstractMatrix{T}, ::Johnson) where {T, U<:Integer} +function shortest_paths( + g::AbstractGraph{U}, distmx::AbstractMatrix{T}, ::Johnson +) where {T,U<:Integer} nvg = nv(g) type_distmx = typeof(distmx) - #Change when parallel implementation of Bellman Ford available - wt_transform = Graphs.Experimental.ShortestPaths.dists(shortest_paths(g, vertices(g), distmx, BellmanFord())) + # Change when parallel implementation of Bellman Ford available + wt_transform = Graphs.Experimental.ShortestPaths.dists( + shortest_paths(g, vertices(g), distmx, BellmanFord()) + ) - @compat if !ismutable(distmx) && type_distmx != Graphs.DefaultDistance - distmx = sparse(distmx) #Change reference, not value + @compat if !ismutable(distmx) && type_distmx != Graphs.DefaultDistance + distmx = sparse(distmx) # Change reference, not value end - #Weight transform not needed if all weights are positive. - if type_distmx != Graphs.DefaultDistance + # Weight transform not needed if all weights are positive. + if type_distmx != Graphs.DefaultDistance for e in edges(g) distmx[src(e), dst(e)] += wt_transform[src(e)] - wt_transform[dst(e)] end end - dists = Matrix{T}(undef, nvg, nvg) parents = Matrix{U}(undef, nvg, nvg) for v in vertices(g) @@ -52,7 +55,7 @@ function shortest_paths(g::AbstractGraph{U}, distmx::AbstractMatrix{T}, ::Johnso broadcast!(-, dists, dists, wt_transform) for v in vertices(g) - dists[:, v] .+= wt_transform[v] #Vertical traversal prefered + dists[:, v] .+= wt_transform[v] # Vertical traversal preferred end @compat if ismutable(distmx) @@ -66,7 +69,7 @@ end shortest_paths(g::AbstractGraph, alg::Johnson) = shortest_paths(g, weights(g), alg) -function paths(s::JohnsonResult{T, U}, v::Integer) where {T, U <: Integer} +function paths(s::JohnsonResult{T,U}, v::Integer) where {T,U<:Integer} pathinfo = s.parents[v, :] paths = Vector{Vector{U}}() for i in 1:length(pathinfo) diff --git a/src/Experimental/ShortestPaths/spfa.jl b/src/Experimental/ShortestPaths/spfa.jl index 7ef8ca50f..c4ee1fcc1 100644 --- a/src/Experimental/ShortestPaths/spfa.jl +++ b/src/Experimental/ShortestPaths/spfa.jl @@ -2,7 +2,7 @@ ################################################################### # -#The type that capsulates the state of Shortest Path Faster Algorithm +# The type that encapsulates the state of Shortest Path Faster Algorithm # ################################################################### @@ -21,17 +21,18 @@ No additional configuration parameters are specified or required. - all destinations """ struct SPFA <: ShortestPathAlgorithm end -struct SPFAResult{T, U<:Integer} <: ShortestPathResult +struct SPFAResult{T,U<:Integer} <: ShortestPathResult parents::Vector{U} dists::Vector{T} end -function shortest_paths(g::AbstractGraph{U}, source::Integer, distmx::AbstractMatrix{T}, alg::SPFA) where {T, U<:Integer} - - +function shortest_paths( + g::AbstractGraph{U}, source::Integer, distmx::AbstractMatrix{T}, alg::SPFA +) where {T,U<:Integer} nvg = nv(g) - (source in 1:nvg) || throw(DomainError(source, "source should be in between 1 and $nvg")) + (source in 1:nvg) || + throw(DomainError(source, "source should be in between 1 and $nvg")) dists = fill(typemax(T), nvg) parents = zeros(U, nvg) dists[source] = 0 @@ -39,7 +40,7 @@ function shortest_paths(g::AbstractGraph{U}, source::Integer, distmx::AbstractMa count = zeros(U, nvg) # Vector to store the count of number of times a vertex goes in the queue. queue = Vector{U}() # Vector used to implement queue - inqueue = falses(nvg,1) # BitArray to mark which vertices are in queue + inqueue = falses(nvg, 1) # BitArray to mark which vertices are in queue push!(queue, source) inqueue[source] = true @@ -48,20 +49,20 @@ function shortest_paths(g::AbstractGraph{U}, source::Integer, distmx::AbstractMa inqueue[v] = false @inbounds for v_neighbor in outneighbors(g, v) - d = distmx[v,v_neighbor] + d = distmx[v, v_neighbor] if dists[v] + d < dists[v_neighbor] # Relaxing edges dists[v_neighbor] = dists[v] + d parents[v_neighbor] = v if !inqueue[v_neighbor] - push!(queue,v_neighbor) + push!(queue, v_neighbor) inqueue[v_neighbor] = true # Mark the vertex as inside queue. - count[v_neighbor] = count[v_neighbor]+1 # Increment the number of times a vertex enters a queue. + count[v_neighbor] = count[v_neighbor] + 1 # Increment the number of times a vertex enters a queue. if count[v_neighbor] > nvg # This step is just to check negative edge cycle. If this step is not there, throw(NegativeCycleError()) # the algorithm will run infinitely in case of a negative weight cycle. - # If count[i]>nvg for any i belonging to [1,nvg], it means a negative edge - # cycle is present. + # If count[i]>nvg for any i belonging to [1,nvg], it means a negative edge + # cycle is present. end end end @@ -71,7 +72,9 @@ function shortest_paths(g::AbstractGraph{U}, source::Integer, distmx::AbstractMa return SPFAResult(parents, dists) end -shortest_paths(g::AbstractGraph, s::Integer, alg::SPFA) = shortest_paths(g, s, weights(g), alg) +function shortest_paths(g::AbstractGraph, s::Integer, alg::SPFA) + return shortest_paths(g, s, weights(g), alg) +end """ Function which returns true if there is any negative weight cycle in the graph. @@ -103,4 +106,3 @@ function has_negative_weight_cycle(g::AbstractGraph, distmx::AbstractMatrix, alg return false end has_negative_weight_cycle(g::AbstractGraph, ::SPFA) = false - diff --git a/src/Experimental/Traversals/Traversals.jl b/src/Experimental/Traversals/Traversals.jl index 195a7b762..726e37838 100644 --- a/src/Experimental/Traversals/Traversals.jl +++ b/src/Experimental/Traversals/Traversals.jl @@ -98,8 +98,12 @@ Traverse a graph `g` from source vertex `s` / vertices `ss` keeping track of `st traversal finished normally; `false` if one of the visit functions returned `false` (see ) """ -traverse_graph!(g::AbstractGraph, s::Integer, alg, state, neighborfn) = traverse_graph!(g, [s], alg, state, neighborfn) -traverse_graph!(g::AbstractGraph, s::Integer, alg, state) = traverse_graph!(g, [s], alg, state) +function traverse_graph!(g::AbstractGraph, s::Integer, alg, state, neighborfn) + return traverse_graph!(g, [s], alg, state, neighborfn) +end +function traverse_graph!(g::AbstractGraph, s::Integer, alg, state) + return traverse_graph!(g, [s], alg, state) +end struct VisitState{T<:Integer} <: AbstractTraversalState visited::Vector{T} @@ -122,11 +126,8 @@ Return a vector representing the vertices of `g` visited in order by [`Traversal starting at vertex `s` (vertices `ss`). """ function visited_vertices( - g::AbstractGraph{U}, - ss::AbstractVector, - alg::TraversalAlgorithm - ) where U<:Integer - + g::AbstractGraph{U}, ss::AbstractVector, alg::TraversalAlgorithm +) where {U<:Integer} v = Vector{U}() sizehint!(v, nv(g)) # actually just the largest connected component, but we'll use this. state = VisitState(v) @@ -135,8 +136,9 @@ function visited_vertices( return state.visited end -visited_vertices(g::AbstractGraph, s::Integer, alg::TraversalAlgorithm) = visited_vertices(g, [s], alg) - +function visited_vertices(g::AbstractGraph, s::Integer, alg::TraversalAlgorithm) + return visited_vertices(g, [s], alg) +end mutable struct ParentState{T<:Integer} <: AbstractTraversalState parents::Vector{T} @@ -160,7 +162,9 @@ implementations which are marginally faster in practice for smaller graphs, but the performance improvements using this implementation on large graphs can be significant. """ -function parents(g::AbstractGraph{T}, s::Integer, alg::TraversalAlgorithm, neighborfn=outneighbors) where T +function parents( + g::AbstractGraph{T}, s::Integer, alg::TraversalAlgorithm, neighborfn=outneighbors +) where {T} parents = zeros(T, nv(g)) state = ParentState(parents) @@ -168,7 +172,6 @@ function parents(g::AbstractGraph{T}, s::Integer, alg::TraversalAlgorithm, neigh return state.parents end - include("bfs.jl") include("dfs.jl") @@ -177,7 +180,7 @@ include("dfs.jl") Return a directed acyclic graph based on a [`parents`](@ref) vector `p`. """ -function tree(p::AbstractVector{T}) where T <: Integer +function tree(p::AbstractVector{T}) where {T<:Integer} n = T(length(p)) t = DiGraph{T}(n) for (v, u) in enumerate(p) @@ -194,7 +197,9 @@ end Return a directed acyclic graph based on traversal of the graph `g` starting with source vertex `s` using algorithm `alg` with neighbor function `neighborfn`. """ -function tree(g::AbstractGraph, s::Integer, alg::TraversalAlgorithm, neighborfn::Function=outneighbors) +function tree( + g::AbstractGraph, s::Integer, alg::TraversalAlgorithm, neighborfn::Function=outneighbors +) p = parents(g, s, alg, neighborfn) return tree(p) end diff --git a/src/Experimental/Traversals/bfs.jl b/src/Experimental/Traversals/bfs.jl index fdea62f90..40b663fc9 100644 --- a/src/Experimental/Traversals/bfs.jl +++ b/src/Experimental/Traversals/bfs.jl @@ -1,5 +1,5 @@ import Base.Sort, Base.Sort.Algorithm -import Base:sort! +import Base: sort! struct NOOPSortAlg <: Base.Sort.Algorithm end const NOOPSort = NOOPSortAlg() @@ -24,10 +24,8 @@ function traverse_graph!( ss::AbstractVector, alg::BFS, state::AbstractTraversalState, - neighborfn::Function=outneighbors - ) where U<:Integer - - + neighborfn::Function=outneighbors, +) where {U<:Integer} n = nv(g) visited = falses(n) cur_level = Vector{U}() @@ -56,7 +54,7 @@ function traverse_graph!( postlevelfn!(state) || return false empty!(cur_level) cur_level, next_level = next_level, cur_level - sort!(cur_level, alg=alg.sort_alg) + sort!(cur_level; alg=alg.sort_alg) end return true end @@ -66,15 +64,15 @@ mutable struct DistanceState{T<:Integer} <: AbstractTraversalState n_level::T end -@inline function initfn!(s::DistanceState{T}, u) where T +@inline function initfn!(s::DistanceState{T}, u) where {T} s.distances[u] = zero(T) return true end -@inline function newvisitfn!(s::DistanceState, u, v) +@inline function newvisitfn!(s::DistanceState, u, v) s.distances[v] = s.n_level return true end -@inline function postlevelfn!(s::DistanceState{T}) where T +@inline function postlevelfn!(s::DistanceState{T}) where {T} s.n_level += one(T) return true end @@ -86,7 +84,7 @@ end Return a vector filled with the geodesic distances of vertices in `g` from vertex `s` / unique vertices `ss` using BFS traversal algorithm `alg`. For vertices in disconnected components the default distance is `typemax(T)`. """ -function distances(g::AbstractGraph{T}, s, alg::BFS=BFS()) where T +function distances(g::AbstractGraph{T}, s, alg::BFS=BFS()) where {T} d = fill(typemax(T), nv(g)) state = DistanceState(d, one(T)) traverse_graph!(g, s, alg, state) diff --git a/src/Experimental/Traversals/dfs.jl b/src/Experimental/Traversals/dfs.jl index 64bec9486..705f39dd7 100644 --- a/src/Experimental/Traversals/dfs.jl +++ b/src/Experimental/Traversals/dfs.jl @@ -1,6 +1,8 @@ import Base.showerror struct CycleError <: Exception end -Base.showerror(io::IO, e::CycleError) = print(io, "Cycles are not allowed in this function.") +function Base.showerror(io::IO, e::CycleError) + return print(io, "Cycles are not allowed in this function.") +end struct DFS <: TraversalAlgorithm end @@ -9,9 +11,8 @@ function traverse_graph!( ss::AbstractVector, alg::DFS, state::AbstractTraversalState, - neighborfn::Function=outneighbors - ) where U<:Integer - + neighborfn::Function=outneighbors, +) where {U<:Integer} n = nv(g) visited = falses(n) S = Vector{U}() @@ -47,25 +48,25 @@ end mutable struct TopoSortState{T<:Integer} <: AbstractTraversalState vcolor::Vector{UInt8} - verts :: Vector{T} + verts::Vector{T} w::T end # 1 = visited # 2 = vcolor 2 # @inline initfn!(s::TopoSortState{T}, u) where T = s.vcolor[u] = one(T) -@inline function previsitfn!(s::TopoSortState{T}, u) where T +@inline function previsitfn!(s::TopoSortState{T}, u) where {T} s.w = 0 return true end -@inline function visitfn!(s::TopoSortState{T}, u, v) where T +@inline function visitfn!(s::TopoSortState{T}, u, v) where {T} return s.vcolor[v] != one(T) end -@inline function newvisitfn!(s::TopoSortState{T}, u, v) where T +@inline function newvisitfn!(s::TopoSortState{T}, u, v) where {T} s.w = v return true end -@inline function postvisitfn!(s::TopoSortState{T}, u) where T +@inline function postvisitfn!(s::TopoSortState{T}, u) where {T} if s.w != 0 s.vcolor[s.w] = one(T) else @@ -75,8 +76,7 @@ end return true end - -@traitfn function topological_sort(g::AG::IsDirected) where {T, AG<:AbstractGraph{T}} +@traitfn function topological_sort(g::AG::IsDirected) where {T,AG<:AbstractGraph{T}} vcolor = zeros(UInt8, nv(g)) verts = Vector{T}() state = TopoSortState(vcolor, verts, zero(T)) @@ -95,18 +95,18 @@ mutable struct CycleState{T<:Integer} <: AbstractTraversalState w::T end -@inline function previsitfn!(s::CycleState{T}, u) where T +@inline function previsitfn!(s::CycleState{T}, u) where {T} s.w = 0 return true end -@inline function visitfn!(s::CycleState{T}, u, v) where T +@inline function visitfn!(s::CycleState{T}, u, v) where {T} return s.vcolor[v] != one(T) end -@inline function newvisitfn!(s::CycleState{T}, u, v) where T +@inline function newvisitfn!(s::CycleState{T}, u, v) where {T} s.w = v return true end -@inline function postvisitfn!(s::CycleState{T}, u) where T +@inline function postvisitfn!(s::CycleState{T}, u) where {T} if s.w != 0 s.vcolor[s.w] = one(T) else @@ -115,7 +115,7 @@ end return true end -@traitfn function is_cyclic(g::AG::IsDirected) where {T, AG<:AbstractGraph{T}} +@traitfn function is_cyclic(g::AG::IsDirected) where {T,AG<:AbstractGraph{T}} vcolor = zeros(UInt8, nv(g)) state = CycleState(vcolor, zero(T)) @inbounds for v in vertices(g) diff --git a/src/Experimental/isomorphism.jl b/src/Experimental/isomorphism.jl index 14d8d7a75..970e5943d 100644 --- a/src/Experimental/isomorphism.jl +++ b/src/Experimental/isomorphism.jl @@ -10,7 +10,6 @@ An abstract type used for method dispatch on isomorphism functions. """ abstract type IsomorphismAlgorithm end - """ could_have_isomorph(g1, g2) @@ -81,12 +80,16 @@ false ### See also [`has_subgraphisomorph`](@ref), [`has_isomorph`](@ref), [`count_induced_subgraphisomorph`](@ref), [`all_induced_subgraphisomorph`](@ref) """ -function has_induced_subgraphisomorph(g1::AbstractGraph, g2::AbstractGraph, alg::IsomorphismAlgorithm=VF2(); - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Bool - - has_induced_subgraphisomorph(g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation) - +function has_induced_subgraphisomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::IsomorphismAlgorithm=VF2(); + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Bool + return has_induced_subgraphisomorph( + g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation + ) end """ @@ -120,12 +123,16 @@ false ### See also [`has_induced_subgraphisomorph`](@ref), [`has_isomorph`](@ref), [`count_subgraphisomorph`](@ref), [`all_subgraphisomorph`](@ref) """ -function has_subgraphisomorph(g1::AbstractGraph, g2::AbstractGraph, alg::IsomorphismAlgorithm=VF2(); - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Bool - has_subgraphisomorph(g1, g2, alg; - vertex_relation=vertex_relation, - edge_relation=edge_relation) +function has_subgraphisomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::IsomorphismAlgorithm=VF2(); + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Bool + return has_subgraphisomorph( + g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation + ) end """ @@ -159,12 +166,16 @@ false ### See also [`has_induced_subgraphisomorph`](@ref), [`has_subgraphisomorph`](@ref), [`count_subgraphisomorph`](@ref), [`all_subgraphisomorph`](@ref) """ -function has_isomorph(g1::AbstractGraph, g2::AbstractGraph, alg::IsomorphismAlgorithm=VF2(); - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Bool - has_isomorph(g1, g2, alg; - vertex_relation=vertex_relation, - edge_relation=edge_relation) +function has_isomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::IsomorphismAlgorithm=VF2(); + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Bool + return has_isomorph( + g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation + ) end """ @@ -196,12 +207,16 @@ julia> count_induced_subgraphisomorph(g1, g2, vertex_relation=color_rel) ### See also [`count_subgraphisomorph`](@ref), [`count_isomorph`](@ref), [`has_induced_subgraphisomorph`](@ref), [`all_induced_subgraphisomorph`](@ref) """ -function count_induced_subgraphisomorph(g1::AbstractGraph, g2::AbstractGraph, alg::IsomorphismAlgorithm=VF2(); - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Int - count_induced_subgraphisomorph(g1,g2, alg; - vertex_relation=vertex_relation, - edge_relation=edge_relation) +function count_induced_subgraphisomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::IsomorphismAlgorithm=VF2(); + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Int + return count_induced_subgraphisomorph( + g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation + ) end """ @@ -235,13 +250,20 @@ julia> count_subgraphisomorph(g1, g2, vertex_relation=color_rel) ### See also [`count_induced_subgraphisomorph`](@ref), [`count_isomorph`](@ref), [`has_subgraphisomorph`](@ref), [`all_subgraphisomorph`](@ref) """ -function count_subgraphisomorph(g1::AbstractGraph, g2::AbstractGraph, alg::IsomorphismAlgorithm=VF2(); - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Int - - count_subgraphisomorph(g1::AbstractGraph, g2::AbstractGraph, VF2(); - vertex_relation=vertex_relation, - edge_relation=edge_relation) +function count_subgraphisomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::IsomorphismAlgorithm=VF2(); + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Int + return count_subgraphisomorph( + g1::AbstractGraph, + g2::AbstractGraph, + VF2(); + vertex_relation=vertex_relation, + edge_relation=edge_relation, + ) end """ @@ -275,12 +297,16 @@ julia> count_isomorph(g1, g2, vertex_relation=color_rel) ### See also [`count_induced_subgraphisomorph`](@ref), [`count_subgraphisomorph`](@ref), [`has_isomorph`](@ref), [`all_isomorph`](@ref) """ -function count_isomorph(g1::AbstractGraph, g2::AbstractGraph, alg::IsomorphismAlgorithm=VF2(); - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Int - count_isomorph(g1, g2, alg; - vertex_relation=vertex_relation, - edge_relation=edge_relation) +function count_isomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::IsomorphismAlgorithm=VF2(); + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Int + return count_isomorph( + g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation + ) end """ @@ -320,12 +346,16 @@ julia> all_induced_subgraphisomorph(g1, g2, vertex_relation=color_rel) |> collec ### See also [`all_subgraphisomorph`](@ref), [`all_isomorph`](@ref), [`has_induced_subgraphisomorph`](@ref), [`count_induced_subgraphisomorph`](@ref) """ -function all_induced_subgraphisomorph(g1::AbstractGraph, g2::AbstractGraph, alg::IsomorphismAlgorithm=VF2(); - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}} - all_induced_subgraphisomorph(g1, g2, alg; - vertex_relation=vertex_relation, - edge_relation=edge_relation) +function all_induced_subgraphisomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::IsomorphismAlgorithm=VF2(); + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}} + return all_induced_subgraphisomorph( + g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation + ) end """ @@ -367,13 +397,16 @@ julia> all_subgraphisomorph(g1, g2, vertex_relation=color_rel) ### See also [`all_induced_subgraphisomorph`](@ref), [`all_isomorph`](@ref), [`has_subgraphisomorph`](@ref), [`count_subgraphisomorph`](@ref) """ -function all_subgraphisomorph(g1::AbstractGraph, g2::AbstractGraph, alg::IsomorphismAlgorithm=VF2(); - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Channel{Vector{Tuple{eltype(g1), eltype(g2)}}} - - all_subgraphisomorph(g1, g2, alg; - vertex_relation=vertex_relation, - edge_relation=edge_relation) +function all_subgraphisomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::IsomorphismAlgorithm=VF2(); + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}} + return all_subgraphisomorph( + g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation + ) end """ @@ -418,11 +451,14 @@ julia> all_subgraphisomorph(g1, g2, vertex_relation=color_rel) ### See also [`all_induced_subgraphisomorph`](@ref), [`all_subgraphisomorph`](@ref), [`has_isomorph`](@ref), [`count_isomorph`](@ref) """ -function all_isomorph(g1::AbstractGraph, g2::AbstractGraph, alg::IsomorphismAlgorithm=VF2(); - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}} - - all_isomorph(g1, g2, alg; - vertex_relation=vertex_relation, - edge_relation=edge_relation) +function all_isomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::IsomorphismAlgorithm=VF2(); + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}} + return all_isomorph( + g1, g2, alg; vertex_relation=vertex_relation, edge_relation=edge_relation + ) end diff --git a/src/Experimental/vf2.jl b/src/Experimental/vf2.jl index 79f6c597f..1614fb80a 100644 --- a/src/Experimental/vf2.jl +++ b/src/Experimental/vf2.jl @@ -10,7 +10,7 @@ struct VF2 <: IsomorphismAlgorithm end Structure that is internally used by vf2 """ -struct VF2State{G, T} +struct VF2State{G,T} g1::G g2::G core_1::Vector{T} @@ -20,7 +20,7 @@ struct VF2State{G, T} out_1::Vector{T} out_2::Vector{T} - function VF2State(g1::G, g2::G) where {G <: AbstractSimpleGraph{T}} where {T <: Integer} + function VF2State(g1::G, g2::G) where {G<:AbstractSimpleGraph{T}} where {T<:Integer} n1 = nv(g1) n2 = nv(g2) core_1 = zeros(T, n1) @@ -30,7 +30,7 @@ struct VF2State{G, T} out_1 = zeros(T, n1) out_2 = zeros(T, n2) - return new{G, T}(g1, g2, core_1, core_2, in_1, in_2, out_1, out_2) + return new{G,T}(g1, g2, core_1, core_2, in_1, in_2, out_1, out_2) end end @@ -57,17 +57,24 @@ If the algorithm should look for another isomorphism, then this function should Luigi P. Cordella, Pasquale Foggia, Carlo Sansone, Mario Vento “A (Sub)Graph Isomorphism Algorithm for Matching Large Graphs” """ -function vf2(callback::Function, g1::G, g2::G, problemtype::GraphMorphismProblem; - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing) where {G <: AbstractSimpleGraph} +function vf2( + callback::Function, + g1::G, + g2::G, + problemtype::GraphMorphismProblem; + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +) where {G<:AbstractSimpleGraph} if nv(g1) < nv(g2) || (problemtype == IsomorphismProblem() && nv(g1) != nv(g2)) - return + return nothing end start_state = VF2State(g1, g2) start_depth = 1 - vf2match!(start_state, start_depth, callback, problemtype, vertex_relation, edge_relation) - return + vf2match!( + start_state, start_depth, callback, problemtype, vertex_relation, edge_relation + ) + return nothing end """ @@ -75,9 +82,14 @@ end Check whether two vertices of G₁ and G₂ can be matched. Used by [`vf2match!`](@ref). """ -function vf2check_feasibility(u, v, state::VF2State, problemtype, - vertex_relation::Union{Nothing, Function}, - edge_relation::Union{Nothing, Function}) +function vf2check_feasibility( + u, + v, + state::VF2State, + problemtype, + vertex_relation::Union{Nothing,Function}, + edge_relation::Union{Nothing,Function}, +) @inline function vf2rule_pred(u, v, state::VF2State, problemtype) if problemtype != SubGraphIsomorphismProblem() @inbounds for u2 in inneighbors(state.g1, u) @@ -139,7 +151,6 @@ function vf2check_feasibility(u, v, state::VF2State, problemtype, end return true end - @inline function vf2rule_in(u, v, state::VF2State, problemtype) count1 = 0 @@ -171,9 +182,9 @@ function vf2check_feasibility(u, v, state::VF2State, problemtype, count2 += 1 end end - problemtype == IsomorphismProblem() && return count1 == count2 + problemtype == IsomorphismProblem() && return count1 == count2 - return count1 >= count2 + return count1 >= count2 end @inline function vf2rule_out(u, v, state::VF2State, problemtype) @@ -207,9 +218,9 @@ function vf2check_feasibility(u, v, state::VF2State, problemtype, count2 += 1 end end - problemtype == IsomorphismProblem() && return count1 == count2 + problemtype == IsomorphismProblem() && return count1 == count2 - return count1 >= count2 + return count1 >= count2 end @inline function vf2rule_new(u, v, state::VF2State, problemtype) @@ -243,8 +254,8 @@ function vf2check_feasibility(u, v, state::VF2State, problemtype, count2 += 1 end end - problemtype == IsomorphismProblem() && return count1 == count2 - + problemtype == IsomorphismProblem() && return count1 == count2 + return count1 >= count2 end @@ -258,15 +269,15 @@ function vf2check_feasibility(u, v, state::VF2State, problemtype, return u_selflooped == v_selflooped end - syntactic_feasability = vf2rule_pred(u, v, state, problemtype) && - vf2rule_succ(u, v, state, problemtype) && - vf2rule_in(u, v, state, problemtype) && - vf2rule_out(u, v, state, problemtype) && - vf2rule_new(u, v, state, problemtype) && - vf2rule_self_loops(u, v, state, problemtype) + syntactic_feasability = + vf2rule_pred(u, v, state, problemtype) && + vf2rule_succ(u, v, state, problemtype) && + vf2rule_in(u, v, state, problemtype) && + vf2rule_out(u, v, state, problemtype) && + vf2rule_new(u, v, state, problemtype) && + vf2rule_self_loops(u, v, state, problemtype) syntactic_feasability || return false - if vertex_relation != nothing vertex_relation(u, v) || return false end @@ -293,30 +304,30 @@ end Update state before recursing. Helper function for [`vf2match!`](@ref). """ function vf2update_state!(state::VF2State, u, v, depth) -@inbounds begin - state.core_1[u] = v - state.core_2[v] = u - for w in outneighbors(state.g1, u) - if state.out_1[w] == 0 - state.out_1[w] = depth - end - end - for w in inneighbors(state.g1, u) - if state.in_1[w] == 0 - state.in_1[w] = depth - end - end - for w in outneighbors(state.g2, v) - if state.out_2[w] == 0 - state.out_2[w] = depth - end - end - for w in inneighbors(state.g2, v) - if state.in_2[w] == 0 - state.in_2[w] = depth - end - end -end + @inbounds begin + state.core_1[u] = v + state.core_2[v] = u + for w in outneighbors(state.g1, u) + if state.out_1[w] == 0 + state.out_1[w] = depth + end + end + for w in inneighbors(state.g1, u) + if state.in_1[w] == 0 + state.in_1[w] = depth + end + end + for w in outneighbors(state.g2, v) + if state.out_2[w] == 0 + state.out_2[w] = depth + end + end + for w in inneighbors(state.g2, v) + if state.in_2[w] == 0 + state.in_2[w] = depth + end + end + end end """ @@ -325,39 +336,45 @@ end Reset state after returning from recursion. Helper function for [`vf2match!`](@ref). """ function vf2reset_state!(state::VF2State, u, v, depth) -@inbounds begin - state.core_1[u] = 0 - state.core_2[v] = 0 - for w in outneighbors(state.g1, u) - if state.out_1[w] == depth - state.out_1[w] = 0 + @inbounds begin + state.core_1[u] = 0 + state.core_2[v] = 0 + for w in outneighbors(state.g1, u) + if state.out_1[w] == depth + state.out_1[w] = 0 + end end - end - for w in inneighbors(state.g1, u) - if state.in_1[w] == depth - state.in_1[w] = 0 + for w in inneighbors(state.g1, u) + if state.in_1[w] == depth + state.in_1[w] = 0 + end end - end - for w in outneighbors(state.g2, v) - if state.out_2[w] == depth - state.out_2[w] = 0 + for w in outneighbors(state.g2, v) + if state.out_2[w] == depth + state.out_2[w] = 0 + end end - end - for w in inneighbors(state.g2, v) - if state.in_2[w] == depth - state.in_2[w] = 0 + for w in inneighbors(state.g2, v) + if state.in_2[w] == depth + state.in_2[w] = 0 + end end end end -end """ vf2match!(state, depth, callback, problemtype, vertex_relation, edge_relation) Perform isomorphic subgraph matching. Called by [`vf2`](@ref). """ -function vf2match!(state, depth, callback::Function, problemtype::GraphMorphismProblem, - vertex_relation, edge_relation) +function vf2match!( + state, + depth, + callback::Function, + problemtype::GraphMorphismProblem, + vertex_relation, + edge_relation, +) n1 = Int(nv(state.g1)) n2 = Int(nv(state.g2)) # if all vertices of G₂ are matched we call the callback function. If the @@ -370,20 +387,28 @@ function vf2match!(state, depth, callback::Function, problemtype::GraphMorphismP # by an edge going out of the set M(s) of already matched vertices found_pair = false v = 0 - @inbounds for j = 1:n2 + @inbounds for j in 1:n2 if state.out_2[j] != 0 && state.core_2[j] == 0 v = j break end end if v != 0 - @inbounds for u = 1:n1 + @inbounds for u in 1:n1 if state.out_1[u] != 0 && state.core_1[u] == 0 found_pair = true - if vf2check_feasibility(u, v, state, problemtype, vertex_relation, edge_relation) + if vf2check_feasibility( + u, v, state, problemtype, vertex_relation, edge_relation + ) vf2update_state!(state, u, v, depth) - keepgoing = vf2match!(state, depth + 1, callback, problemtype, - vertex_relation, edge_relation) + keepgoing = vf2match!( + state, + depth + 1, + callback, + problemtype, + vertex_relation, + edge_relation, + ) keepgoing || return false vf2reset_state!(state, u, v, depth) end @@ -391,23 +416,31 @@ function vf2match!(state, depth, callback::Function, problemtype::GraphMorphismP end end found_pair && return true - # If that is not the case we try if there is a pair of unmatched vertices u∈G₁ v∈G₂ that + # If that is not the case we try if there is a pair of unmatched vertices u∈G₁ v∈G₂ that # are connected by an edge coming in from the set M(s) of already matched vertices v = 0 - @inbounds for j = 1:n2 + @inbounds for j in 1:n2 if state.in_2[j] != 0 && state.core_2[j] == 0 v = j break end end if v != 0 - @inbounds for u = 1:n1 + @inbounds for u in 1:n1 if state.in_1[u] != 0 && state.core_1[u] == 0 found_pair = true - if vf2check_feasibility(u, v, state, problemtype, vertex_relation, edge_relation) + if vf2check_feasibility( + u, v, state, problemtype, vertex_relation, edge_relation + ) vf2update_state!(state, u, v, depth) - keepgoing = vf2match!(state, depth + 1, callback, problemtype, - vertex_relation, edge_relation) + keepgoing = vf2match!( + state, + depth + 1, + callback, + problemtype, + vertex_relation, + edge_relation, + ) keepgoing || return false vf2reset_state!(state, u, v, depth) end @@ -418,129 +451,227 @@ function vf2match!(state, depth, callback::Function, problemtype::GraphMorphismP # If this is also not the case, we try all pairs of vertices u∈G₁ v∈G₂ that are not # yet matched v = 0 - @inbounds for j = 1:n2 + @inbounds for j in 1:n2 if state.core_2[j] == 0 v = j break end end if v != 0 - @inbounds for u = 1:n1 + @inbounds for u in 1:n1 if state.core_1[u] == 0 - if vf2check_feasibility(u, v, state, problemtype, vertex_relation, edge_relation) + if vf2check_feasibility( + u, v, state, problemtype, vertex_relation, edge_relation + ) vf2update_state!(state, u, v, depth) - keepgoing = vf2match!(state, depth + 1, callback, problemtype, - vertex_relation, edge_relation) + keepgoing = vf2match!( + state, + depth + 1, + callback, + problemtype, + vertex_relation, + edge_relation, + ) keepgoing || return false vf2reset_state!(state, u, v, depth) end end - end + end end return true end - -function has_induced_subgraphisomorph(g1::AbstractGraph, g2::AbstractGraph, alg::VF2; - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Bool - result = false - callback(vmap) = (result = true; return false) - vf2(callback, g1, g2, InducedSubGraphIsomorphismProblem(); - vertex_relation=vertex_relation, edge_relation=edge_relation) - return result +function has_induced_subgraphisomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::VF2; + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Bool + result = false + callback(vmap) = (result = true; return false) + vf2( + callback, + g1, + g2, + InducedSubGraphIsomorphismProblem(); + vertex_relation=vertex_relation, + edge_relation=edge_relation, + ) + return result end -function has_subgraphisomorph(g1::AbstractGraph, g2::AbstractGraph, alg::VF2; - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing,)::Bool +function has_subgraphisomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::VF2; + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Bool result = false callback(vmap) = (result = true; return false) - vf2(callback, g1, g2, SubGraphIsomorphismProblem(); - vertex_relation=vertex_relation, edge_relation=edge_relation) + vf2( + callback, + g1, + g2, + SubGraphIsomorphismProblem(); + vertex_relation=vertex_relation, + edge_relation=edge_relation, + ) return result end -function has_isomorph(g1::AbstractGraph, g2::AbstractGraph, alg::VF2; - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Bool - +function has_isomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::VF2; + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Bool !could_have_isomorph(g1, g2) && return false result = false callback(vmap) = (result = true; return false) - vf2(callback, g1, g2, IsomorphismProblem(), + vf2( + callback, + g1, + g2, + IsomorphismProblem(); vertex_relation=vertex_relation, - edge_relation=edge_relation) + edge_relation=edge_relation, + ) return result end -function count_induced_subgraphisomorph(g1::AbstractGraph, g2::AbstractGraph, alg::VF2; - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Int +function count_induced_subgraphisomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::VF2; + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Int result = 0 callback(vmap) = (result += 1; return true) - vf2(callback, g1, g2, InducedSubGraphIsomorphismProblem(), + vf2( + callback, + g1, + g2, + InducedSubGraphIsomorphismProblem(); vertex_relation=vertex_relation, - edge_relation=edge_relation) + edge_relation=edge_relation, + ) return result end -function count_subgraphisomorph(g1::AbstractGraph, g2::AbstractGraph, alg::VF2; - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Int +function count_subgraphisomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::VF2; + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Int result = 0 callback(vmap) = (result += 1; return true) - vf2(callback, g1, g2, SubGraphIsomorphismProblem(), vertex_relation=vertex_relation, edge_relation=edge_relation) + vf2( + callback, + g1, + g2, + SubGraphIsomorphismProblem(); + vertex_relation=vertex_relation, + edge_relation=edge_relation, + ) return result end -function count_isomorph(g1::AbstractGraph, g2::AbstractGraph, alg::VF2; - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Int +function count_isomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::VF2; + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Int !could_have_isomorph(g1, g2) && return 0 result = 0 callback(vmap) = (result += 1; return true) - vf2(callback, g1, g2, IsomorphismProblem(), vertex_relation=vertex_relation, edge_relation=edge_relation) + vf2( + callback, + g1, + g2, + IsomorphismProblem(); + vertex_relation=vertex_relation, + edge_relation=edge_relation, + ) return result end -function all_induced_subgraphisomorph(g1::AbstractGraph, g2::AbstractGraph, alg::VF2; - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}} - make_callback(c) = vmap -> (put!(c, collect(zip(vmap,1:length(vmap)))), return true) - T = Vector{Tuple{eltype(g1), eltype(g2)}} - ch::Channel{T} = Channel(ctype=T) do c - vf2(make_callback(c), g1, g2, InducedSubGraphIsomorphismProblem(), - vertex_relation=vertex_relation, - edge_relation=edge_relation) +function all_induced_subgraphisomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::VF2; + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}} + make_callback(c) = vmap -> (put!(c, collect(zip(vmap, 1:length(vmap)))), return true) + T = Vector{Tuple{eltype(g1),eltype(g2)}} + return ch::Channel{T} = Channel(; ctype=T) do c + vf2( + make_callback(c), + g1, + g2, + InducedSubGraphIsomorphismProblem(); + vertex_relation=vertex_relation, + edge_relation=edge_relation, + ) end end -function all_subgraphisomorph(g1::AbstractGraph, g2::AbstractGraph, alg::VF2; - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Channel{Vector{Tuple{eltype(g1), eltype(g2)}}} - - make_callback(c) = vmap -> (put!(c, collect(zip(vmap,1:length(vmap)))), return true) - T = Vector{Tuple{eltype(g1), eltype(g2)}} - ch::Channel{T} = Channel(ctype=T) do c - vf2(make_callback(c), g1, g2, SubGraphIsomorphismProblem(), +function all_subgraphisomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::VF2; + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}} + make_callback(c) = vmap -> (put!(c, collect(zip(vmap, 1:length(vmap)))), return true) + T = Vector{Tuple{eltype(g1),eltype(g2)}} + ch::Channel{T} = Channel(; ctype=T) do c + vf2( + make_callback(c), + g1, + g2, + SubGraphIsomorphismProblem(); vertex_relation=vertex_relation, - edge_relation=edge_relation) + edge_relation=edge_relation, + ) end return ch end -function all_isomorph(g1::AbstractGraph, g2::AbstractGraph, alg::VF2; - vertex_relation::Union{Nothing, Function}=nothing, - edge_relation::Union{Nothing, Function}=nothing)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}} - T = Vector{Tuple{eltype(g1), eltype(g2)}} - !could_have_isomorph(g1, g2) && return Channel(_ -> return, ctype=T) - make_callback(c) = vmap -> (put!(c, collect(zip(vmap,1:length(vmap)))), return true) - ch::Channel{T} = Channel(ctype=T) do c - vf2(make_callback(c), g1, g2, IsomorphismProblem(), +#! format: off +# Turns off formatting from this point onwards + +function all_isomorph( + g1::AbstractGraph, + g2::AbstractGraph, + alg::VF2; + vertex_relation::Union{Nothing,Function}=nothing, + edge_relation::Union{Nothing,Function}=nothing, +)::Channel{Vector{Tuple{eltype(g1),eltype(g2)}}} + T = Vector{Tuple{eltype(g1),eltype(g2)}} + !could_have_isomorph(g1, g2) && return Channel(_ -> return, ctype = T) # TODO: fix problem with JuliaFormatter, right now the whole file is ignored + make_callback(c) = vmap -> (put!(c, collect(zip(vmap, 1:length(vmap)))), return true) + ch::Channel{T} = Channel(; ctype=T) do c + vf2( + make_callback(c), + g1, + g2, + IsomorphismProblem(); vertex_relation=vertex_relation, - edge_relation=edge_relation) + edge_relation=edge_relation, + ) end return ch end + +#! format: on +# Turns on formatting from this point onwards diff --git a/src/Graphs.jl b/src/Graphs.jl index bbddf395d..b754ccb6e 100644 --- a/src/Graphs.jl +++ b/src/Graphs.jl @@ -10,162 +10,409 @@ using Statistics: mean using Compat using Inflate: InflateGzipStream -using DataStructures: IntDisjointSets, PriorityQueue, dequeue!, dequeue_pair!, enqueue!, heappop!, heappush!, in_same_set, peek, union!, find_root! +using DataStructures: + IntDisjointSets, + PriorityQueue, + dequeue!, + dequeue_pair!, + enqueue!, + heappop!, + heappush!, + in_same_set, + peek, + union!, + find_root! using LinearAlgebra: I, Symmetric, diagm, eigen, eigvals, norm, rmul!, tril, triu import LinearAlgebra: Diagonal, issymmetric, mul! -using Random: AbstractRNG, GLOBAL_RNG, MersenneTwister, randperm, randsubseq!, seed!, shuffle, shuffle! +using Random: + AbstractRNG, + GLOBAL_RNG, + MersenneTwister, + randperm, + randsubseq!, + seed!, + shuffle, + shuffle! using SparseArrays: SparseMatrixCSC, nonzeros, nzrange, rowvals import SparseArrays: blockdiag, sparse -import Base: adjoint, write, ==, <, *, ≈, convert, isless, issubset, union, intersect, - reverse, reverse!, isassigned, getindex, setindex!, show, - print, copy, in, sum, size, eltype, length, ndims, transpose, - join, iterate, eltype, get, Pair, Tuple, zero +import Base: + adjoint, + write, + ==, + <, + *, + ≈, + convert, + isless, + issubset, + union, + intersect, + reverse, + reverse!, + isassigned, + getindex, + setindex!, + show, + print, + copy, + in, + sum, + size, + eltype, + length, + ndims, + transpose, + join, + iterate, + eltype, + get, + Pair, + Tuple, + zero export -# Interface -AbstractGraph, AbstractEdge, AbstractEdgeIter, -Edge, Graph, SimpleGraph, SimpleGraphFromIterator, DiGraph, SimpleDiGraphFromIterator, -SimpleDiGraph, vertices, edges, edgetype, nv, ne, src, dst, -is_directed, IsDirected, -has_vertex, has_edge, inneighbors, outneighbors, - -# core -is_ordered, add_vertices!, indegree, outdegree, degree, -Δout, Δin, δout, δin, Δ, δ, degree_histogram, -neighbors, all_neighbors, common_neighbors, -has_self_loops, num_self_loops, density, squash, weights, - -# simplegraphs -add_edge!, add_vertex!, add_vertices!, rem_edge!, rem_vertex!, rem_vertices!, - -# decomposition -core_number, k_core, k_shell, k_crust, k_corona, - -# distance -eccentricity, diameter, periphery, radius, center, - -# distance between graphs -spectral_distance, edit_distance, - -# edit path cost functions -MinkowskiCost, BoundedMinkowskiCost, - -# operators -complement, reverse, reverse!, blockdiag, union, intersect, -difference, symmetric_difference, -join, tensor_product, cartesian_product, crosspath, -induced_subgraph, egonet, merge_vertices!, merge_vertices, - -# bfs -gdistances, gdistances!, bfs_tree, bfs_parents, has_path, - -# bipartition -is_bipartite, bipartite_map, - -# dfs -is_cyclic, topological_sort_by_dfs, dfs_tree, dfs_parents, - -# random -randomwalk, self_avoiding_walk, non_backtracking_randomwalk, - -# diffusion -diffusion, diffusion_rate, - -# coloring -greedy_color, - -# connectivity -connected_components, strongly_connected_components, strongly_connected_components_kosaraju, weakly_connected_components, -is_connected, is_strongly_connected, is_weakly_connected, period, -condensation, attracting_components, neighborhood, neighborhood_dists, -isgraphical, - -# cycles -simplecycles_hawick_james, maxsimplecycles, simplecycles, simplecycles_iter, -simplecyclescount, simplecycleslength, karp_minimum_cycle_mean, cycle_basis, -simplecycles_limited_length, - -# incremental cycles -IncrementalCycleTracker, add_edge_checked!, topological_sort, - -# maximum_adjacency_visit -mincut, maximum_adjacency_visit, - -# a-star, dijkstra, bellman-ford, floyd-warshall, desopo-pape, spfa -a_star, dijkstra_shortest_paths, bellman_ford_shortest_paths, -spfa_shortest_paths,has_negative_edge_cycle_spfa,has_negative_edge_cycle, enumerate_paths, -johnson_shortest_paths, floyd_warshall_shortest_paths, transitiveclosure!, transitiveclosure, transitivereduction, -yen_k_shortest_paths, desopo_pape_shortest_paths, - -# centrality -betweenness_centrality, closeness_centrality, degree_centrality, -indegree_centrality, outdegree_centrality, katz_centrality, pagerank, -eigenvector_centrality, stress_centrality, radiality_centrality, - -# spectral -adjacency_matrix, laplacian_matrix, adjacency_spectrum, laplacian_spectrum, -non_backtracking_matrix, incidence_matrix, Nonbacktracking, -contract, - -# persistence -loadgraph, loadgraphs, savegraph, LGFormat, - -# randgraphs -erdos_renyi, expected_degree_graph, watts_strogatz, random_regular_graph, random_regular_digraph, -random_configuration_model, random_tournament_digraph, StochasticBlockModel, make_edgestream, -nearbipartiteSBM, blockcounts, blockfractions, stochastic_block_model, barabasi_albert, -barabasi_albert!, static_fitness_model, static_scale_free, kronecker, dorogovtsev_mendes, random_orientation_dag, - -#community -modularity, core_periphery_deg, -local_clustering,local_clustering_coefficient, global_clustering_coefficient, triangles, -label_propagation, maximal_cliques, clique_percolation, assortativity,rich_club, - -#generators -complete_graph, star_graph, path_graph, wheel_graph, cycle_graph, -complete_bipartite_graph, complete_multipartite_graph, turan_graph, -complete_digraph, star_digraph, path_digraph, grid, wheel_digraph, cycle_digraph, -binary_tree, double_binary_tree, roach_graph, clique_graph, ladder_graph, -circular_ladder_graph, barbell_graph, lollipop_graph, - -#generator deprecations -BullGraph, ChvatalGraph, CubicalGraph, DesarguesGraph, DiamondGraph, -DodecahedralGraph, FruchtGraph, HeawoodGraph, HouseGraph, HouseXGraph, -IcosahedralGraph, KarateGraph, KrackhardtKiteGraph, MoebiusKantorGraph, -OctahedralGraph, PappusGraph, PetersenGraph, SedgewickMazeGraph, TetrahedralGraph, -TruncatedCubeGraph, TruncatedTetrahedronGraph, TruncatedTetrahedronDiGraph, -TutteGraph, CompleteGraph, CompleteBipartiteGraph, CompleteMultipartiteGraph, -TuranGraph, CompleteDiGraph, StarGraph, StarDigraph, PathGraph, PathDiGraph, -CycleGraph, CycleDiGraph, WheelGraph, WheelDiGraph, Grid, BinaryTree, -Doublebinary_tree, RoachGraph, CliqueGraph, LadderGraph, Circularladder_graph, -BarbellGraph, LollipopGraph, - -#smallgraphs -smallgraph, - -# Euclidean graphs -euclidean_graph, - -#minimum_spanning_trees -boruvka_mst, kruskal_mst, prim_mst, - -#steinertree -steiner_tree, - -#biconnectivity and articulation points -articulation, biconnected_components, bridges, - -#graphcut -normalized_cut, karger_min_cut, karger_cut_cost, karger_cut_edges, - -#dominatingset -dominating_set, - -#independentset -independent_set, - -#vertexcover -vertex_cover + # Interface + AbstractGraph, + AbstractEdge, + AbstractEdgeIter, + Edge, + Graph, + SimpleGraph, + SimpleGraphFromIterator, + DiGraph, + SimpleDiGraphFromIterator, + SimpleDiGraph, + vertices, + edges, + edgetype, + nv, + ne, + src, + dst, + is_directed, + IsDirected, + has_vertex, + has_edge, + inneighbors, + outneighbors, + + # core + is_ordered, + add_vertices!, + indegree, + outdegree, + degree, + Δout, + Δin, + δout, + δin, + Δ, + δ, + degree_histogram, + neighbors, + all_neighbors, + common_neighbors, + has_self_loops, + num_self_loops, + density, + squash, + weights, + + # simplegraphs + add_edge!, + add_vertex!, + add_vertices!, + rem_edge!, + rem_vertex!, + rem_vertices!, + + # decomposition + core_number, + k_core, + k_shell, + k_crust, + k_corona, + + # distance + eccentricity, + diameter, + periphery, + radius, + center, + + # distance between graphs + spectral_distance, + edit_distance, + + # edit path cost functions + MinkowskiCost, + BoundedMinkowskiCost, + + # operators + complement, + reverse, + reverse!, + blockdiag, + union, + intersect, + difference, + symmetric_difference, + join, + tensor_product, + cartesian_product, + crosspath, + induced_subgraph, + egonet, + merge_vertices!, + merge_vertices, + + # bfs + gdistances, + gdistances!, + bfs_tree, + bfs_parents, + has_path, + + # bipartition + is_bipartite, + bipartite_map, + + # dfs + is_cyclic, + topological_sort_by_dfs, + dfs_tree, + dfs_parents, + + # random + randomwalk, + self_avoiding_walk, + non_backtracking_randomwalk, + + # diffusion + diffusion, + diffusion_rate, + + # coloring + greedy_color, + + # connectivity + connected_components, + strongly_connected_components, + strongly_connected_components_kosaraju, + weakly_connected_components, + is_connected, + is_strongly_connected, + is_weakly_connected, + period, + condensation, + attracting_components, + neighborhood, + neighborhood_dists, + isgraphical, + + # cycles + simplecycles_hawick_james, + maxsimplecycles, + simplecycles, + simplecycles_iter, + simplecyclescount, + simplecycleslength, + karp_minimum_cycle_mean, + cycle_basis, + simplecycles_limited_length, + + # incremental cycles + IncrementalCycleTracker, + add_edge_checked!, + topological_sort, + + # maximum_adjacency_visit + mincut, + maximum_adjacency_visit, + + # a-star, dijkstra, bellman-ford, floyd-warshall, desopo-pape, spfa + a_star, + dijkstra_shortest_paths, + bellman_ford_shortest_paths, + spfa_shortest_paths, + has_negative_edge_cycle_spfa, + has_negative_edge_cycle, + enumerate_paths, + johnson_shortest_paths, + floyd_warshall_shortest_paths, + transitiveclosure!, + transitiveclosure, + transitivereduction, + yen_k_shortest_paths, + desopo_pape_shortest_paths, + + # centrality + betweenness_centrality, + closeness_centrality, + degree_centrality, + indegree_centrality, + outdegree_centrality, + katz_centrality, + pagerank, + eigenvector_centrality, + stress_centrality, + radiality_centrality, + + # spectral + adjacency_matrix, + laplacian_matrix, + adjacency_spectrum, + laplacian_spectrum, + non_backtracking_matrix, + incidence_matrix, + Nonbacktracking, + contract, + + # persistence + loadgraph, + loadgraphs, + savegraph, + LGFormat, + + # randgraphs + erdos_renyi, + expected_degree_graph, + watts_strogatz, + random_regular_graph, + random_regular_digraph, + random_configuration_model, + random_tournament_digraph, + StochasticBlockModel, + make_edgestream, + nearbipartiteSBM, + blockcounts, + blockfractions, + stochastic_block_model, + barabasi_albert, + barabasi_albert!, + static_fitness_model, + static_scale_free, + kronecker, + dorogovtsev_mendes, + random_orientation_dag, + + # community + modularity, + core_periphery_deg, + local_clustering, + local_clustering_coefficient, + global_clustering_coefficient, + triangles, + label_propagation, + maximal_cliques, + clique_percolation, + assortativity, + rich_club, + + # generators + complete_graph, + star_graph, + path_graph, + wheel_graph, + cycle_graph, + complete_bipartite_graph, + complete_multipartite_graph, + turan_graph, + complete_digraph, + star_digraph, + path_digraph, + grid, + wheel_digraph, + cycle_digraph, + binary_tree, + double_binary_tree, + roach_graph, + clique_graph, + ladder_graph, + circular_ladder_graph, + barbell_graph, + lollipop_graph, + + # generator deprecations + BullGraph, + ChvatalGraph, + CubicalGraph, + DesarguesGraph, + DiamondGraph, + DodecahedralGraph, + FruchtGraph, + HeawoodGraph, + HouseGraph, + HouseXGraph, + IcosahedralGraph, + KarateGraph, + KrackhardtKiteGraph, + MoebiusKantorGraph, + OctahedralGraph, + PappusGraph, + PetersenGraph, + SedgewickMazeGraph, + TetrahedralGraph, + TruncatedCubeGraph, + TruncatedTetrahedronGraph, + TruncatedTetrahedronDiGraph, + TutteGraph, + CompleteGraph, + CompleteBipartiteGraph, + CompleteMultipartiteGraph, + TuranGraph, + CompleteDiGraph, + StarGraph, + StarDigraph, + PathGraph, + PathDiGraph, + CycleGraph, + CycleDiGraph, + WheelGraph, + WheelDiGraph, + Grid, + BinaryTree, + Doublebinary_tree, + RoachGraph, + CliqueGraph, + LadderGraph, + Circularladder_graph, + BarbellGraph, + LollipopGraph, + + # smallgraphs + smallgraph, + + # Euclidean graphs + euclidean_graph, + + # minimum_spanning_trees + boruvka_mst, + kruskal_mst, + prim_mst, + + # steinertree + steiner_tree, + + # biconnectivity and articulation points + articulation, + biconnected_components, + bridges, + + # graphcut + normalized_cut, + karger_min_cut, + karger_cut_cost, + karger_cut_edges, + + # dominatingset + dominating_set, + + # independentset + independent_set, + + # vertexcover + vertex_cover """ Graphs diff --git a/src/Parallel/centrality/betweenness.jl b/src/Parallel/centrality/betweenness.jl index 8b36df018..538bdd255 100644 --- a/src/Parallel/centrality/betweenness.jl +++ b/src/Parallel/centrality/betweenness.jl @@ -1,23 +1,51 @@ -betweenness_centrality(g::AbstractGraph, vs=vertices(g), distmx::AbstractMatrix=weights(g); normalize=true, endpoints=false, parallel=:distributed) = -parallel == :distributed ? distr_betweenness_centrality(g, vs, distmx; normalize=normalize, endpoints=endpoints) : -threaded_betweenness_centrality(g, vs, distmx; normalize=normalize, endpoints=endpoints) - function betweenness_centrality( - g::AbstractGraph, k::Integer, distmx::AbstractMatrix=weights(g); - normalize=true, endpoints=false, parallel=:distributed, - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + g::AbstractGraph, + vs=vertices(g), + distmx::AbstractMatrix=weights(g); + normalize=true, + endpoints=false, + parallel=:distributed, +) + return if parallel == :distributed + distr_betweenness_centrality( + g, vs, distmx; normalize=normalize, endpoints=endpoints + ) + else + threaded_betweenness_centrality( + g, vs, distmx; normalize=normalize, endpoints=endpoints + ) + end +end + +function betweenness_centrality( + g::AbstractGraph, + k::Integer, + distmx::AbstractMatrix=weights(g); + normalize=true, + endpoints=false, + parallel=:distributed, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) samples = sample(vertices(g), k; rng=rng, seed=seed) - parallel == :distributed ? distr_betweenness_centrality(g, samples, distmx; normalize=normalize, endpoints=endpoints) : - threaded_betweenness_centrality(g, samples, distmx; normalize=normalize, endpoints=endpoints) + return if parallel == :distributed + distr_betweenness_centrality( + g, samples, distmx; normalize=normalize, endpoints=endpoints + ) + else + threaded_betweenness_centrality( + g, samples, distmx; normalize=normalize, endpoints=endpoints + ) + end end -function distr_betweenness_centrality(g::AbstractGraph, +function distr_betweenness_centrality( + g::AbstractGraph, vs=vertices(g), distmx::AbstractMatrix=weights(g); normalize=true, - endpoints=false)::Vector{Float64} - + endpoints=false, +)::Vector{Float64} n_v = nv(g) k = length(vs) isdir = is_directed(g) @@ -27,7 +55,9 @@ function distr_betweenness_centrality(g::AbstractGraph, betweenness = @distributed (+) for s in vs temp_betweenness = zeros(n_v) if degree(g, s) > 0 # this might be 1? - state = Graphs.dijkstra_shortest_paths(g, s, distmx; allpaths=true, trackvertices=true) + state = Graphs.dijkstra_shortest_paths( + g, s, distmx; allpaths=true, trackvertices=true + ) if endpoints Graphs._accumulate_endpoints!(temp_betweenness, state, g, s) else @@ -43,49 +73,66 @@ function distr_betweenness_centrality(g::AbstractGraph, end function distr_betweenness_centrality( - g::AbstractGraph, k::Integer, distmx::AbstractMatrix=weights(g); - normalize=true, endpoints=false, rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) + g::AbstractGraph, + k::Integer, + distmx::AbstractMatrix=weights(g); + normalize=true, + endpoints=false, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) samples = sample(vertices(g), k; rng=rng, seed=seed) - distr_betweenness_centrality(g, samples, distmx; normalize=normalize, endpoints=endpoints) + return distr_betweenness_centrality( + g, samples, distmx; normalize=normalize, endpoints=endpoints + ) end -function threaded_betweenness_centrality(g::AbstractGraph, +function threaded_betweenness_centrality( + g::AbstractGraph, vs=vertices(g), distmx::AbstractMatrix=weights(g); normalize=true, - endpoints=false)::Vector{Float64} - + endpoints=false, +)::Vector{Float64} n_v = nv(g) k = length(vs) isdir = is_directed(g) local_betweenness = [zeros(n_v) for i in 1:nthreads()] - vs_active = findall((x)->degree(g, x) > 0, vs) # 0 might be 1? + vs_active = findall((x) -> degree(g, x) > 0, vs) # 0 might be 1? Base.Threads.@threads for s in vs_active - state = Graphs.dijkstra_shortest_paths(g, s, distmx; allpaths=true, trackvertices=true) + state = Graphs.dijkstra_shortest_paths( + g, s, distmx; allpaths=true, trackvertices=true + ) if endpoints - Graphs._accumulate_endpoints!(local_betweenness[Base.Threads.threadid()], state, g, s) + Graphs._accumulate_endpoints!( + local_betweenness[Base.Threads.threadid()], state, g, s + ) else - Graphs._accumulate_basic!(local_betweenness[Base.Threads.threadid()], state, g, s) + Graphs._accumulate_basic!( + local_betweenness[Base.Threads.threadid()], state, g, s + ) end end betweenness = reduce(+, local_betweenness) - Graphs._rescale!(betweenness, - n_v, - normalize, - isdir, - k) + Graphs._rescale!(betweenness, n_v, normalize, isdir, k) return betweenness end function threaded_betweenness_centrality( - g::AbstractGraph, k::Integer, distmx::AbstractMatrix=weights(g); - normalize=true, endpoints=false, rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + g::AbstractGraph, + k::Integer, + distmx::AbstractMatrix=weights(g); + normalize=true, + endpoints=false, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) samples = sample(vertices(g), k; rng=rng, seed=seed) - threaded_betweenness_centrality(g, samples, distmx; normalize=normalize, endpoints=endpoints) + return threaded_betweenness_centrality( + g, samples, distmx; normalize=normalize, endpoints=endpoints + ) end diff --git a/src/Parallel/centrality/closeness.jl b/src/Parallel/centrality/closeness.jl index 6fe27b062..920421211 100644 --- a/src/Parallel/centrality/closeness.jl +++ b/src/Parallel/centrality/closeness.jl @@ -1,11 +1,19 @@ -closeness_centrality(g::AbstractGraph, distmx::AbstractMatrix=weights(g); normalize=true, parallel=:distributed) = -parallel == :distributed ? distr_closeness_centrality(g, distmx; normalize=normalize) : -threaded_closeness_centrality(g, distmx; normalize=normalize) - -function distr_closeness_centrality(g::AbstractGraph, +function closeness_centrality( + g::AbstractGraph, distmx::AbstractMatrix=weights(g); - normalize=true)::Vector{Float64} + normalize=true, + parallel=:distributed, +) + return if parallel == :distributed + distr_closeness_centrality(g, distmx; normalize=normalize) + else + threaded_closeness_centrality(g, distmx; normalize=normalize) + end +end +function distr_closeness_centrality( + g::AbstractGraph, distmx::AbstractMatrix=weights(g); normalize=true +)::Vector{Float64} n_v = Int(nv(g)) closeness = SharedVector{Float64}(n_v) fill!(closeness, 0.0) @@ -32,10 +40,9 @@ function distr_closeness_centrality(g::AbstractGraph, return sdata(closeness) end -function threaded_closeness_centrality(g::AbstractGraph, - distmx::AbstractMatrix=weights(g); - normalize=true)::Vector{Float64} - +function threaded_closeness_centrality( + g::AbstractGraph, distmx::AbstractMatrix=weights(g); normalize=true +)::Vector{Float64} n_v = Int(nv(g)) closeness = zeros(Float64, n_v) diff --git a/src/Parallel/centrality/pagerank.jl b/src/Parallel/centrality/pagerank.jl index f3229d7a0..6f94b2450 100644 --- a/src/Parallel/centrality/pagerank.jl +++ b/src/Parallel/centrality/pagerank.jl @@ -1,20 +1,15 @@ -function pagerank( - g::AbstractGraph{U}, - α=0.85, - n=100::Integer, - ϵ=1.0e-6 - ) where U <: Integer - +function pagerank(g::AbstractGraph{U}, α=0.85, n=100::Integer, ϵ=1.0e-6) where {U<:Integer} + # indegree(g, v) is estimated run-time to iterate over inneighbors(g, v) partitions = Graphs.optimal_contiguous_partition(indegree(g), nthreads(), nv(g)) - α_div_outdegree = Vector{Float64}(undef,nv(g)) + α_div_outdegree = Vector{Float64}(undef, nv(g)) dangling_nodes = Vector{U}() @inbounds for v in vertices(g) if outdegree(g, v) == 0 push!(dangling_nodes, v) end - α_div_outdegree[v] = (α/outdegree(g, v)) + α_div_outdegree[v] = (α / outdegree(g, v)) end nvg = Int(nv(g)) @@ -50,5 +45,5 @@ function pagerank( return x end end - error("Pagerank did not converge after $n iterations.") # TODO 0.7: change to InexactError with appropriate msg. + return error("Pagerank did not converge after $n iterations.") # TODO 0.7: change to InexactError with appropriate msg. end diff --git a/src/Parallel/centrality/radiality.jl b/src/Parallel/centrality/radiality.jl index ba4a09b45..e10fbe81b 100644 --- a/src/Parallel/centrality/radiality.jl +++ b/src/Parallel/centrality/radiality.jl @@ -1,5 +1,10 @@ -radiality_centrality(g::AbstractGraph; parallel=:distributed) = -parallel == :distributed ? distr_radiality_centrality(g) : threaded_radiality_centrality(g) +function radiality_centrality(g::AbstractGraph; parallel=:distributed) + return if parallel == :distributed + distr_radiality_centrality(g) + else + threaded_radiality_centrality(g) + end +end function distr_radiality_centrality(g::AbstractGraph)::Vector{Float64} n_v = nv(g) @@ -8,7 +13,7 @@ function distr_radiality_centrality(g::AbstractGraph)::Vector{Float64} meandists = SharedVector{Float64}(Int(n_v)) maxdists = SharedVector{Float64}(Int(n_v)) - @sync @distributed for i = 1:n_v + @sync @distributed for i in 1:n_v d = Graphs.dijkstra_shortest_paths(g, vs[i]) maxdists[i] = maximum(d.dists) meandists[i] = sum(d.dists) / (n_v - 1) diff --git a/src/Parallel/centrality/stress.jl b/src/Parallel/centrality/stress.jl index 74879be62..fe7bcd929 100644 --- a/src/Parallel/centrality/stress.jl +++ b/src/Parallel/centrality/stress.jl @@ -1,18 +1,27 @@ -stress_centrality(g::AbstractGraph, vs=vertices(g); parallel=:distributed) = -parallel == :distributed ? distr_stress_centrality(g, vs) : threaded_stress_centrality(g, vs) +function stress_centrality(g::AbstractGraph, vs=vertices(g); parallel=:distributed) + return if parallel == :distributed + distr_stress_centrality(g, vs) + else + threaded_stress_centrality(g, vs) + end +end function stress_centrality( - g::AbstractGraph, k::Integer; - parallel=:distributed, rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + g::AbstractGraph, + k::Integer; + parallel=:distributed, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) samples = sample(vertices(g), k; rng=rng, seed=seed) - parallel == :distributed ? distr_stress_centrality(g, samples) : - threaded_stress_centrality(g, samples) + return if parallel == :distributed + distr_stress_centrality(g, samples) + else + threaded_stress_centrality(g, samples) + end end -function distr_stress_centrality(g::AbstractGraph, - vs=vertices(g))::Vector{Int64} - +function distr_stress_centrality(g::AbstractGraph, vs=vertices(g))::Vector{Int64} n_v = nv(g) k = length(vs) isdir = is_directed(g) @@ -29,9 +38,7 @@ function distr_stress_centrality(g::AbstractGraph, return stress end -function threaded_stress_centrality(g::AbstractGraph, - vs=vertices(g))::Vector{Int64} - +function threaded_stress_centrality(g::AbstractGraph, vs=vertices(g))::Vector{Int64} n_v = nv(g) k = length(vs) isdir = is_directed(g) @@ -42,7 +49,9 @@ function threaded_stress_centrality(g::AbstractGraph, Base.Threads.@threads for s in vs if degree(g, s) > 0 # this might be 1? state = Graphs.dijkstra_shortest_paths(g, s; allpaths=true, trackvertices=true) - Graphs._stress_accumulate_basic!(local_stress[Base.Threads.threadid()], state, g, s) + Graphs._stress_accumulate_basic!( + local_stress[Base.Threads.threadid()], state, g, s + ) end end return reduce(+, local_stress) diff --git a/src/Parallel/distance.jl b/src/Parallel/distance.jl index 044e4e76f..e84ec1da4 100644 --- a/src/Parallel/distance.jl +++ b/src/Parallel/distance.jl @@ -1,11 +1,11 @@ # used in shortest path calculations -function eccentricity(g::AbstractGraph, - vs=vertices(g), - distmx::AbstractMatrix{T}=weights(g)) where T <: Real +function eccentricity( + g::AbstractGraph, vs=vertices(g), distmx::AbstractMatrix{T}=weights(g) +) where {T<:Real} vlen = length(vs) eccs = SharedVector{T}(vlen) - @sync @distributed for i = 1:vlen + @sync @distributed for i in 1:vlen eccs[i] = maximum(Graphs.dijkstra_shortest_paths(g, vs[i], distmx).dists) end d = sdata(eccs) @@ -13,17 +13,22 @@ function eccentricity(g::AbstractGraph, return d end -eccentricity(g::AbstractGraph, distmx::AbstractMatrix) = - eccentricity(g, vertices(g), distmx) +function eccentricity(g::AbstractGraph, distmx::AbstractMatrix) + return eccentricity(g, vertices(g), distmx) +end -diameter(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) = - maximum(eccentricity(g, distmx)) +function diameter(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) + return maximum(eccentricity(g, distmx)) +end -periphery(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) = - Graphs.periphery(eccentricity(g, distmx)) +function periphery(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) + return Graphs.periphery(eccentricity(g, distmx)) +end -radius(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) = - minimum(eccentricity(g, distmx)) +function radius(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) + return minimum(eccentricity(g, distmx)) +end -center(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) = - Graphs.center(eccentricity(g, distmx)) +function center(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) + return Graphs.center(eccentricity(g, distmx)) +end diff --git a/src/Parallel/dominatingset/minimal_dom_set.jl b/src/Parallel/dominatingset/minimal_dom_set.jl index a4c3929fa..77789c737 100644 --- a/src/Parallel/dominatingset/minimal_dom_set.jl +++ b/src/Parallel/dominatingset/minimal_dom_set.jl @@ -1,23 +1,27 @@ """ dominating_set(g, reps, MinimalDominatingSet(); parallel=:threads, rng=nothing, seed=nothing) -Perform [`Graphs.dominating_set(g, MinimalDominatingSet())`](@ref) `reps` times in parallel +Perform [`Graphs.dominating_set(g, MinimalDominatingSet())`](@ref) `reps` times in parallel and return the solution with the fewest vertices. -### Optional Arguements +### Optional Arguments - `parallel=:threads`: If `parallel=:distributed` then the multiprocessor implementation is used. This implementation is more efficient if `reps` is large. - If `seed >= 0`, a random generator of each process/thread is seeded with this value. """ function dominating_set( - g::AbstractGraph{T}, reps::Integer, alg::MinimalDominatingSet; - parallel=:threads, rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Integer - Graphs.Parallel.generate_reduce( + g::AbstractGraph{T}, + reps::Integer, + alg::MinimalDominatingSet; + parallel=:threads, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Integer} + return Graphs.Parallel.generate_reduce( g, - (g::AbstractGraph{T})->Graphs.dominating_set(g, alg; rng=rng, seed=seed), - (x::Vector{T}, y::Vector{T})->length(x) Graphs.dominating_set(g, alg; rng=rng, seed=seed), + (x::Vector{T}, y::Vector{T}) -> length(x) < length(y), reps; - parallel=parallel + parallel=parallel, ) end diff --git a/src/Parallel/independentset/maximal_ind_set.jl b/src/Parallel/independentset/maximal_ind_set.jl index 1172005aa..fad46d5f2 100644 --- a/src/Parallel/independentset/maximal_ind_set.jl +++ b/src/Parallel/independentset/maximal_ind_set.jl @@ -1,22 +1,26 @@ """ independent_set(g, reps, MaximalIndependentSet(); parallel=:threads, rng=nothing, seed=nothing) -Perform [`Graphs.independent_set(g, MaximalIndependentSet())`](@ref) `reps` times in parallel +Perform [`Graphs.independent_set(g, MaximalIndependentSet())`](@ref) `reps` times in parallel and return the solution with the most vertices. -### Optional Arguements +### Optional Arguments - `parallel=:threads`: If `parallel=:distributed` then the multiprocessor implementation is used. This implementation is more efficient if `reps` is large. """ function independent_set( - g::AbstractGraph{T}, reps::Integer, alg::MaximalIndependentSet; - parallel=:threads, rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Integer - Graphs.Parallel.generate_reduce( + g::AbstractGraph{T}, + reps::Integer, + alg::MaximalIndependentSet; + parallel=:threads, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Integer} + return Graphs.Parallel.generate_reduce( g, - (g::AbstractGraph{T})->Graphs.independent_set(g, alg; rng=rng, seed=seed), - (x::Vector{T}, y::Vector{T})->length(x)>length(y), + (g::AbstractGraph{T}) -> Graphs.independent_set(g, alg; rng=rng, seed=seed), + (x::Vector{T}, y::Vector{T}) -> length(x) > length(y), reps; - parallel=parallel + parallel=parallel, ) end diff --git a/src/Parallel/shortestpaths/bellman-ford.jl b/src/Parallel/shortestpaths/bellman-ford.jl index b951d7845..c37ed5df3 100644 --- a/src/Parallel/shortestpaths/bellman-ford.jl +++ b/src/Parallel/shortestpaths/bellman-ford.jl @@ -1,9 +1,8 @@ function bellman_ford_shortest_paths( g::AbstractGraph{U}, sources::AbstractVector{<:Integer}, - distmx::AbstractMatrix{T}=weights(g) - ) where T<:Real where U<:Integer - + distmx::AbstractMatrix{T}=weights(g), +) where {T<:Real} where {U<:Integer} nvg = nv(g) active = Set{U}() sizehint!(active, nv(g)) @@ -24,27 +23,27 @@ function bellman_ford_shortest_paths( return BellmanFordState(parents, dists) end -#Helper function used due to performance bug in @threads. +# Helper function used due to performance bug in @threads. function _loop_body!( g::AbstractGraph{U}, distmx::AbstractMatrix{T}, dists::Vector{T}, parents::Vector{U}, - active::Set{U} - ) where T<:Real where U<:Integer - - + active::Set{U}, +) where {T<:Real} where {U<:Integer} prev_dists = deepcopy(dists) - + tmp_active = collect(active) @threads for v in tmp_active prev_dist_vertex = prev_dists[v] for u in inneighbors(g, v) - relax_dist = (prev_dists[u] == typemax(T) ? typemax(T) : prev_dists[u] + distmx[u,v]) - if prev_dist_vertex > relax_dist - prev_dist_vertex = relax_dist - parents[v] = u - end + relax_dist = ( + prev_dists[u] == typemax(T) ? typemax(T) : prev_dists[u] + distmx[u, v] + ) + if prev_dist_vertex > relax_dist + prev_dist_vertex = relax_dist + parents[v] = u + end end dists[v] = prev_dist_vertex end @@ -58,9 +57,8 @@ function _loop_body!( end function has_negative_edge_cycle( - g::AbstractGraph{U}, - distmx::AbstractMatrix{T} - ) where T<:Real where U<:Integer + g::AbstractGraph{U}, distmx::AbstractMatrix{T} +) where {T<:Real} where {U<:Integer} try Parallel.bellman_ford_shortest_paths(g, vertices(g), distmx) catch e @@ -68,5 +66,3 @@ function has_negative_edge_cycle( end return false end - - diff --git a/src/Parallel/shortestpaths/dijkstra.jl b/src/Parallel/shortestpaths/dijkstra.jl index 6d3f6cb5c..39f018b95 100644 --- a/src/Parallel/shortestpaths/dijkstra.jl +++ b/src/Parallel/shortestpaths/dijkstra.jl @@ -3,7 +3,7 @@ An [`AbstractPathState`](@ref) designed for Parallel.dijkstra_shortest_paths calculation. """ -struct MultipleDijkstraState{T <: Real,U <: Integer} <: AbstractPathState +struct MultipleDijkstraState{T<:Real,U<:Integer} <: AbstractPathState dists::Matrix{T} parents::Matrix{U} end @@ -16,15 +16,14 @@ Compute the shortest paths between all pairs of vertices in graph `g` by running an optional distance matrix `distmx`. Return a [`Parallel.MultipleDijkstraState`](@ref) with relevant traversal information. """ -function dijkstra_shortest_paths(g::AbstractGraph{U}, - sources=vertices(g), - distmx::AbstractMatrix{T}=weights(g)) where T <: Real where U - +function dijkstra_shortest_paths( + g::AbstractGraph{U}, sources=vertices(g), distmx::AbstractMatrix{T}=weights(g) +) where {T<:Real} where {U} n_v = nv(g) r_v = length(sources) # TODO: remove `Int` once julialang/#23029 / #23032 are resolved - dists = SharedMatrix{T}(Int(r_v), Int(n_v)) + dists = SharedMatrix{T}(Int(r_v), Int(n_v)) parents = SharedMatrix{U}(Int(r_v), Int(n_v)) @sync @distributed for i in 1:r_v diff --git a/src/Parallel/shortestpaths/floyd-warshall.jl b/src/Parallel/shortestpaths/floyd-warshall.jl index f333c8eb7..6136006e9 100644 --- a/src/Parallel/shortestpaths/floyd-warshall.jl +++ b/src/Parallel/shortestpaths/floyd-warshall.jl @@ -1,17 +1,20 @@ -#Helper function used due to performance bug in @threads. +# Helper function used due to performance bug in @threads. function _loopbody!( - pivot::U, - nvg::U, - dists::Matrix{T}, - parents::Matrix{U} - ) where T<:Real where U<:Integer + pivot::U, nvg::U, dists::Matrix{T}, parents::Matrix{U} +) where {T<:Real} where {U<:Integer} # Relax dists[u, v] = min(dists[u, v], dists[u, pivot]+dists[pivot, v]) for all u, v @inbounds @threads for v in one(U):nvg d = dists[pivot, v] if d != typemax(T) && v != pivot p = parents[pivot, v] @inbounds for u in one(U):nvg - ans = (dists[u, pivot] == typemax(T) || u == pivot ? typemax(T) : dists[u, pivot] + d) + ans = ( + if dists[u, pivot] == typemax(T) || u == pivot + typemax(T) + else + dists[u, pivot] + d + end + ) if dists[u, v] > ans dists[u, v] = ans parents[u, v] = p @@ -22,9 +25,8 @@ function _loopbody!( end function floyd_warshall_shortest_paths( - g::AbstractGraph{U}, - distmx::AbstractMatrix{T}=weights(g) -) where T<:Real where U<:Integer + g::AbstractGraph{U}, distmx::AbstractMatrix{T}=weights(g) +) where {T<:Real} where {U<:Integer} nvg = nv(g) dists = fill(typemax(T), (Int(nvg), Int(nvg))) parents = zeros(U, (Int(nvg), Int(nvg))) @@ -53,4 +55,3 @@ function floyd_warshall_shortest_paths( fws = FloydWarshallState(dists, parents) return fws end - diff --git a/src/Parallel/shortestpaths/johnson.jl b/src/Parallel/shortestpaths/johnson.jl index 0260040fb..f4fdff2ba 100644 --- a/src/Parallel/shortestpaths/johnson.jl +++ b/src/Parallel/shortestpaths/johnson.jl @@ -2,34 +2,32 @@ # Currently used to support the ismutable function that is not available in Julia < v1.7 using Compat -function johnson_shortest_paths(g::AbstractGraph{U}, -distmx::AbstractMatrix{T}=weights(g)) where T <: Real where U <: Integer - +function johnson_shortest_paths( + g::AbstractGraph{U}, distmx::AbstractMatrix{T}=weights(g) +) where {T<:Real} where {U<:Integer} nvg = nv(g) type_distmx = typeof(distmx) -#Change when parallel implementation of Bellman Ford available + # Change when parallel implementation of Bellman Ford available wt_transform = bellman_ford_shortest_paths(g, vertices(g), distmx).dists - @compat if !ismutable(distmx) && type_distmx != Graphs.DefaultDistance - distmx = sparse(distmx) #Change reference, not value + @compat if !ismutable(distmx) && type_distmx != Graphs.DefaultDistance + distmx = sparse(distmx) # Change reference, not value end -#Weight transform not needed if all weights are positive. - if type_distmx != Graphs.DefaultDistance + # Weight transform not needed if all weights are positive. + if type_distmx != Graphs.DefaultDistance for e in edges(g) distmx[src(e), dst(e)] += wt_transform[src(e)] - wt_transform[dst(e)] end end - dijk_state = Parallel.dijkstra_shortest_paths(g, vertices(g), distmx) dists = dijk_state.dists parents = dijk_state.parents - broadcast!(-, dists, dists, wt_transform) for v in vertices(g) - dists[:, v] .+= wt_transform[v] #Vertical traversal prefered + dists[:, v] .+= wt_transform[v] # Vertical traversal preferred end @compat if ismutable(distmx) diff --git a/src/Parallel/traversals/bfs.jl b/src/Parallel/traversals/bfs.jl index b8d5845c1..974f24e41 100644 --- a/src/Parallel/traversals/bfs.jl +++ b/src/Parallel/traversals/bfs.jl @@ -15,50 +15,50 @@ A thread safe queue implementation for using as the queue for BFS. """ struct ThreadQueue{T,N<:Integer} data::Vector{T} - head::Atomic{N} #Index of the head - tail::Atomic{N} #Index of the tail + head::Atomic{N} # Index of the head + tail::Atomic{N} # Index of the tail end -function ThreadQueue(T::Type, maxlength::N) where N <: Integer +function ThreadQueue(T::Type, maxlength::N) where {N<:Integer} q = ThreadQueue(Vector{T}(undef, maxlength), Atomic{N}(1), Atomic{N}(1)) return q end -function push!(q::ThreadQueue{T,N}, val::T) where T where N +function push!(q::ThreadQueue{T,N}, val::T) where {T} where {N} # TODO: check that head > tail offset = atomic_add!(q.tail, one(N)) q.data[offset] = val return offset end -function popfirst!(q::ThreadQueue{T,N}) where T where N +function popfirst!(q::ThreadQueue{T,N}) where {T} where {N} # TODO: check that head < tail offset = atomic_add!(q.head, one(N)) return q.data[offset] end -function isempty(q::ThreadQueue{T,N}) where T where N +function isempty(q::ThreadQueue{T,N}) where {T} where {N} return (q.head[] == q.tail[]) && q.head != one(N) end -function getindex(q::ThreadQueue{T}, iter) where T +function getindex(q::ThreadQueue{T}, iter) where {T} return q.data[iter] end # Traverses the vertices in the queue and adds newly found successors to the queue. function bfskernel( - next::ThreadQueue, # Thread safe queue to add vertices to - g::AbstractGraph, # The graph - parents::Array{Atomic{T}}, # Parents array - level::Array{T} # Vertices in the current frontier - ) where T <: Integer + next::ThreadQueue, # Thread safe queue to add vertices to + g::AbstractGraph, # The graph + parents::Array{Atomic{T}}, # Parents array + level::Array{T}, # Vertices in the current frontier +) where {T<:Integer} @threads for src in level vertexneighbors = neighbors(g, src) # Get the neighbors of the vertex for vertex in vertexneighbors # Atomically check and set parent value if not set yet. parent = atomic_cas!(parents[vertex], zero(T), src) if parent == 0 - push!(next, vertex) #Push onto queue if newly found + push!(next, vertex) # Push onto queue if newly found end end end @@ -76,11 +76,11 @@ environment variable to decide the number of threads to use. Refer `@threads` do for more details. """ function bfs_tree!( - next::ThreadQueue, # Thread safe queue to add vertices to - g::AbstractGraph, # The graph - source::T, # Source vertex - parents::Array{Atomic{T}} # Parents array - ) where T<:Integer + next::ThreadQueue, # Thread safe queue to add vertices to + g::AbstractGraph, # The graph + source::T, # Source vertex + parents::Array{Atomic{T}}, # Parents array +) where {T<:Integer} parents[source][] = source # Set source to source push!(next, source) # Add source to the queue while !isempty(next) @@ -91,14 +91,14 @@ function bfs_tree!( return parents end -function bfs_tree(g::AbstractGraph, source::T, nv::T) where T <: Integer +function bfs_tree(g::AbstractGraph, source::T, nv::T) where {T<:Integer} next = ThreadQueue(T, nv) # Initialize threadqueue - parents = [Atomic{T}(0) for i = 1:nv] # Create parents array + parents = [Atomic{T}(0) for i in 1:nv] # Create parents array Parallel.bfs_tree!(next, g, source, parents) - Graphs.tree([i[] for i in parents]) + return Graphs.tree([i[] for i in parents]) end -function bfs_tree(g::AbstractGraph, source::T) where T <: Integer +function bfs_tree(g::AbstractGraph, source::T) where {T<:Integer} nvg = nv(g) - Parallel.bfs_tree(g, source, nvg) + return Parallel.bfs_tree(g, source, nvg) end diff --git a/src/Parallel/traversals/greedy_color.jl b/src/Parallel/traversals/greedy_color.jl index a2229315f..6eb0c1e8e 100644 --- a/src/Parallel/traversals/greedy_color.jl +++ b/src/Parallel/traversals/greedy_color.jl @@ -1,4 +1,4 @@ -function random_greedy_color(g::AbstractGraph{T}, reps::Integer) where {T <: Integer} +function random_greedy_color(g::AbstractGraph{T}, reps::Integer) where {T<:Integer} best = @distributed (Graphs.best_color) for i in 1:reps seq = shuffle(vertices(g)) Graphs.perm_greedy_color(g, seq) @@ -7,5 +7,12 @@ function random_greedy_color(g::AbstractGraph{T}, reps::Integer) where {T <: Int return convert(Graphs.Coloring{T}, best) end -greedy_color(g::AbstractGraph{U}; sort_degree::Bool=false, reps::Integer=1) where {U <: Integer} = - sort_degree ? Graphs.degree_greedy_color(g) : Parallel.random_greedy_color(g, reps) +function greedy_color( + g::AbstractGraph{U}; sort_degree::Bool=false, reps::Integer=1 +) where {U<:Integer} + return if sort_degree + Graphs.degree_greedy_color(g) + else + Parallel.random_greedy_color(g, reps) + end +end diff --git a/src/Parallel/utils.jl b/src/Parallel/utils.jl index b34090da1..9a3be6f6b 100644 --- a/src/Parallel/utils.jl +++ b/src/Parallel/utils.jl @@ -7,8 +7,15 @@ Compute `gen_func(g)` `reps` times and return the instance `best` for which For example, `comp(x, y) = length(x) < length(y) ? x : y` then instance with the smallest length will be returned. """ -generate_reduce(g::AbstractGraph{T}, gen_func::Function, comp::Comp, reps::Integer; parallel=:threads) where {T<:Integer,Comp} = -parallel == :threads ? threaded_generate_reduce(g, gen_func, comp, reps) : distr_generate_reduce(g, gen_func, comp, reps) +function generate_reduce( + g::AbstractGraph{T}, gen_func::Function, comp::Comp, reps::Integer; parallel=:threads +) where {T<:Integer,Comp} + return if parallel == :threads + threaded_generate_reduce(g, gen_func, comp, reps) + else + distr_generate_reduce(g, gen_func, comp, reps) + end +end """ distr_generate_min_set(g, gen_func, comp, reps) @@ -16,13 +23,10 @@ parallel == :threads ? threaded_generate_reduce(g, gen_func, comp, reps) : distr Distributed implementation of [`generate_reduce`](@ref). """ function distr_generate_reduce( - g::AbstractGraph{T}, - gen_func::Function, - comp::Comp, - reps::Integer - ) where {T<:Integer,Comp} + g::AbstractGraph{T}, gen_func::Function, comp::Comp, reps::Integer +) where {T<:Integer,Comp} # Type assert required for type stability - min_set::Vector{T} = @distributed ((x, y)->comp(x, y) ? x : y) for _ in 1:reps + min_set::Vector{T} = @distributed ((x, y) -> comp(x, y) ? x : y) for _ in 1:reps gen_func(g) end return min_set @@ -34,11 +38,8 @@ end Multi-threaded implementation of [`generate_reduce`](@ref). """ function threaded_generate_reduce( - g::AbstractGraph{T}, - gen_func::Function, - comp::Comp, - reps::Integer - ) where {T<:Integer,Comp} + g::AbstractGraph{T}, gen_func::Function, comp::Comp, reps::Integer +) where {T<:Integer,Comp} n_t = Base.Threads.nthreads() is_undef = ones(Bool, n_t) min_set = [Vector{T}() for _ in 1:n_t] @@ -52,7 +53,7 @@ function threaded_generate_reduce( end min_ind = 0 - for i in filter((j)->!is_undef[j], 1:n_t) + for i in filter((j) -> !is_undef[j], 1:n_t) if min_ind == 0 || comp(min_set[i], min_set[min_ind]) min_ind = i end diff --git a/src/Parallel/vertexcover/random_vertex_cover.jl b/src/Parallel/vertexcover/random_vertex_cover.jl index a88548977..967ba716a 100644 --- a/src/Parallel/vertexcover/random_vertex_cover.jl +++ b/src/Parallel/vertexcover/random_vertex_cover.jl @@ -9,14 +9,18 @@ and return the solution with the fewest vertices. used. This implementation is more efficient if `reps` is large. """ function vertex_cover( - g::AbstractGraph{T}, reps::Integer, alg::RandomVertexCover; - parallel=:threads, rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Integer - Graphs.Parallel.generate_reduce( + g::AbstractGraph{T}, + reps::Integer, + alg::RandomVertexCover; + parallel=:threads, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Integer} + return Graphs.Parallel.generate_reduce( g, - (g::AbstractGraph{T})->Graphs.vertex_cover(g, alg; rng=rng, seed=seed), - (x::Vector{T}, y::Vector{T})->length(x) Graphs.vertex_cover(g, alg; rng=rng, seed=seed), + (x::Vector{T}, y::Vector{T}) -> length(x) < length(y), reps; - parallel=parallel + parallel=parallel, ) end diff --git a/src/SimpleGraphs/SimpleGraphs.jl b/src/SimpleGraphs/SimpleGraphs.jl index 358b36a2a..1a75a24d9 100644 --- a/src/SimpleGraphs/SimpleGraphs.jl +++ b/src/SimpleGraphs/SimpleGraphs.jl @@ -11,34 +11,97 @@ import Base: eltype, show, ==, Pair, Tuple, copy, length, issubset, reverse, zero, in, iterate import Graphs: - _NI, AbstractGraph, AbstractEdge, AbstractEdgeIter, - src, dst, edgetype, nv, ne, vertices, edges, is_directed, - has_vertex, has_edge, inneighbors, outneighbors, all_neighbors, - deepcopy_adjlist, indegree, outdegree, degree, has_self_loops, - num_self_loops, insorted, squash, rng_from_rng_or_seed - -export AbstractSimpleGraph, AbstractSimpleEdge, - SimpleEdge, SimpleGraph, SimpleGraphFromIterator, SimpleGraphEdge, - SimpleDiGraph, SimpleDiGraphFromIterator, SimpleDiGraphEdge, - add_vertex!, add_edge!, rem_vertex!, rem_vertices!, rem_edge!, squash, + _NI, + AbstractGraph, + AbstractEdge, + AbstractEdgeIter, + src, + dst, + edgetype, + nv, + ne, + vertices, + edges, + is_directed, + has_vertex, + has_edge, + inneighbors, + outneighbors, + all_neighbors, + deepcopy_adjlist, + indegree, + outdegree, + degree, + has_self_loops, + num_self_loops, + insorted, + squash, + rng_from_rng_or_seed + +export AbstractSimpleGraph, + AbstractSimpleEdge, + SimpleEdge, + SimpleGraph, + SimpleGraphFromIterator, + SimpleGraphEdge, + SimpleDiGraph, + SimpleDiGraphFromIterator, + SimpleDiGraphEdge, + add_vertex!, + add_edge!, + rem_vertex!, + rem_vertices!, + rem_edge!, + squash, # randgraphs - erdos_renyi, expected_degree_graph, watts_strogatz, random_regular_graph, - random_regular_digraph, random_configuration_model, random_tournament_digraph, - StochasticBlockModel, make_edgestream, nearbipartiteSBM, blockcounts, - blockfractions, stochastic_block_model, barabasi_albert, dorogovtsev_mendes, - barabasi_albert!, static_fitness_model, static_scale_free, kronecker, random_orientation_dag, - #generators - complete_graph, star_graph, path_graph, wheel_graph, cycle_graph, - complete_bipartite_graph, complete_multipartite_graph, turan_graph, complete_digraph, - star_digraph, path_digraph, grid, wheel_digraph, cycle_digraph, binary_tree, - double_binary_tree, roach_graph, clique_graph, barbell_graph, lollipop_graph, - ladder_graph, circular_ladder_graph, - #smallgraphs + erdos_renyi, + expected_degree_graph, + watts_strogatz, + random_regular_graph, + random_regular_digraph, + random_configuration_model, + random_tournament_digraph, + StochasticBlockModel, + make_edgestream, + nearbipartiteSBM, + blockcounts, + blockfractions, + stochastic_block_model, + barabasi_albert, + dorogovtsev_mendes, + barabasi_albert!, + static_fitness_model, + static_scale_free, + kronecker, + random_orientation_dag, + # generators + complete_graph, + star_graph, + path_graph, + wheel_graph, + cycle_graph, + complete_bipartite_graph, + complete_multipartite_graph, + turan_graph, + complete_digraph, + star_digraph, + path_digraph, + grid, + wheel_digraph, + cycle_digraph, + binary_tree, + double_binary_tree, + roach_graph, + clique_graph, + barbell_graph, + lollipop_graph, + ladder_graph, + circular_ladder_graph, + # smallgraphs smallgraph, # Euclidean graphs euclidean_graph - """ AbstractSimpleGraph @@ -50,12 +113,12 @@ An abstract type representing a simple graph structure. """ abstract type AbstractSimpleGraph{T<:Integer} <: AbstractGraph{T} end -function show(io::IO, ::MIME"text/plain", g::AbstractSimpleGraph{T}) where T +function show(io::IO, ::MIME"text/plain", g::AbstractSimpleGraph{T}) where {T} dir = is_directed(g) ? "directed" : "undirected" - print(io, "{$(nv(g)), $(ne(g))} $dir simple $T graph") + return print(io, "{$(nv(g)), $(ne(g))} $dir simple $T graph") end -nv(g::AbstractSimpleGraph{T}) where T = T(length(fadj(g))) +nv(g::AbstractSimpleGraph{T}) where {T} = T(length(fadj(g))) vertices(g::AbstractSimpleGraph) = Base.OneTo(nv(g)) """ @@ -72,14 +135,11 @@ function throw_if_invalid_eltype(T::Type{<:Integer}) end end - edges(g::AbstractSimpleGraph) = SimpleEdgeIter(g) - fadj(g::AbstractSimpleGraph) = g.fadjlist fadj(g::AbstractSimpleGraph, v::Integer) = g.fadjlist[v] - badj(x...) = _NI("badj") # handles single-argument edge constructors such as pairs and tuples @@ -93,7 +153,7 @@ add_edge!(g::AbstractSimpleGraph, x, y) = add_edge!(g, edgetype(g)(x, y)) inneighbors(g::AbstractSimpleGraph, v::Integer) = badj(g, v) outneighbors(g::AbstractSimpleGraph, v::Integer) = fadj(g, v) -function issubset(g::T, h::T) where T <: AbstractSimpleGraph +function issubset(g::T, h::T) where {T<:AbstractSimpleGraph} nv(g) <= nv(h) || return false for u in vertices(g) u_nbrs_g = neighbors(g, u) @@ -102,7 +162,7 @@ function issubset(g::T, h::T) where T <: AbstractSimpleGraph u_nbrs_h = neighbors(h, u) p = 1 len_u_nbrs_g > length(u_nbrs_h) && return false - (u_nbrs_g[1] < u_nbrs_h[1] || u_nbrs_g[end] > u_nbrs_h[end]) && return false + (u_nbrs_g[1] < u_nbrs_h[1] || u_nbrs_g[end] > u_nbrs_h[end]) && return false @inbounds for v in u_nbrs_h if v == u_nbrs_g[p] p == len_u_nbrs_g && break @@ -118,8 +178,8 @@ has_vertex(g::AbstractSimpleGraph, v::Integer) = v in vertices(g) ne(g::AbstractSimpleGraph) = g.ne -function rem_edge!(g::AbstractSimpleGraph{T}, u::Integer, v::Integer) where T - rem_edge!(g, edgetype(g)(T(u), T(v))) +function rem_edge!(g::AbstractSimpleGraph{T}, u::Integer, v::Integer) where {T} + return rem_edge!(g, edgetype(g)(T(u), T(v))) end """ diff --git a/src/SimpleGraphs/generators/euclideangraphs.jl b/src/SimpleGraphs/generators/euclideangraphs.jl index edd71ae60..d64200836 100644 --- a/src/SimpleGraphs/generators/euclideangraphs.jl +++ b/src/SimpleGraphs/generators/euclideangraphs.jl @@ -21,8 +21,12 @@ Dict{Graphs.SimpleGraphs.SimpleEdge{Int64},Float64} with 4 entries: ``` """ function euclidean_graph( - N::Int, d::Int; - L=1., rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing, kws... + N::Int, + d::Int; + L=1.0, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, + kws..., ) rng = rng_from_rng_or_seed(rng, seed) points = rmul!(rand(rng, d, N), L) @@ -57,18 +61,17 @@ julia> g {10, 45} undirected simple Int64 graph ``` """ -function euclidean_graph( - points::Matrix; - L=1., p=2., cutoff=-1., bc=:open -) +function euclidean_graph(points::Matrix; L=1.0, p=2.0, cutoff=-1.0, bc=:open) d, N = size(points) weights = Dict{SimpleEdge{Int},Float64}() - cutoff < 0. && (cutoff = typemax(Float64)) + cutoff < 0.0 && (cutoff = typemax(Float64)) if bc == :periodic - maximum(points) > L && throw(DomainError(maximum(points), "Some points are outside the box of size $L")) + maximum(points) > L && throw( + DomainError(maximum(points), "Some points are outside the box of size $L") + ) end - for i = 1:N - for j = (i + 1):N + for i in 1:N + for j in (i + 1):N if bc == :open Δ = points[:, i] - points[:, j] elseif bc == :periodic diff --git a/src/SimpleGraphs/generators/randgraphs.jl b/src/SimpleGraphs/generators/randgraphs.jl index fda82004a..52b749784 100644 --- a/src/SimpleGraphs/generators/randgraphs.jl +++ b/src/SimpleGraphs/generators/randgraphs.jl @@ -20,14 +20,16 @@ julia> SimpleGraph(5, 7) ``` """ function SimpleGraph{T}( - nv::Integer, ne::Integer; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Integer + nv::Integer, + ne::Integer; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Integer} tnv = T(nv) maxe = div(Int(nv) * (nv - 1), 2) @assert(ne <= maxe, "Maximum number of edges for this graph is $maxe") rng = rng_from_rng_or_seed(rng, seed) - ne > div((2 * maxe), 3) && return complement(SimpleGraph(tnv, maxe - ne, rng=rng)) + ne > div((2 * maxe), 3) && return complement(SimpleGraph(tnv, maxe - ne; rng=rng)) g = SimpleGraph(tnv) @@ -39,11 +41,14 @@ function SimpleGraph{T}( return g end -SimpleGraph( - nv::T, ne::Integer; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Integer = - SimpleGraph{T}(nv, ne; rng=rng, seed=seed) +function SimpleGraph( + nv::T, + ne::Integer; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Integer} + return SimpleGraph{T}(nv, ne; rng=rng, seed=seed) +end """ SimpleDiGraph{T}(nv, ne; rng=nothing, seed=nothing) @@ -63,9 +68,11 @@ julia> SimpleDiGraph(5, 7) ``` """ function SimpleDiGraph{T}( - nv::Integer, ne::Integer; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Integer + nv::Integer, + ne::Integer; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Integer} tnv = T(nv) maxe = Int(nv) * (nv - 1) @assert(ne <= maxe, "Maximum number of edges for this graph is $maxe") @@ -81,21 +88,28 @@ function SimpleDiGraph{T}( end function SimpleDiGraph( - nv::T, ne::Integer; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Integer - SimpleDiGraph{Int}(nv, ne; rng=rng, seed=seed) + nv::T, + ne::Integer; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Integer} + return SimpleDiGraph{Int}(nv, ne; rng=rng, seed=seed) end """ randbn(n, p; rng=nothing, seed=nothing) -Return a binomally-distribted random number with parameters `n` and `p` and optional `seed`. +Return a binomially-distributed random number with parameters `n` and `p` and optional `seed`. ### References - "Non-Uniform Random Variate Generation," Luc Devroye, p. 522. Retrieved via http://www.eirene.de/Devroye.pdf. - http://stackoverflow.com/questions/23561551/a-efficient-binomial-random-number-generator-code-in-java """ -function randbn(n::Integer, p::Real; rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing) +function randbn( + n::Integer, + p::Real; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) rng = rng_from_rng_or_seed(rng, seed) log_q = log(1.0 - p) x = 0 @@ -130,13 +144,20 @@ julia> erdos_renyi(10, 0.5, is_directed=true, seed=123) ``` """ function erdos_renyi( - n::Integer, p::Real; - is_directed=false, rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + n::Integer, + p::Real; + is_directed=false, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) p >= 1 && return is_directed ? complete_digraph(n) : complete_graph(n) m = is_directed ? n * (n - 1) : div(n * (n - 1), 2) ne = randbn(m, p; rng=rng, seed=seed) - return is_directed ? SimpleDiGraph(n, ne; rng=rng, seed=seed) : SimpleGraph(n, ne; rng=rng, seed=seed) + return if is_directed + SimpleDiGraph(n, ne; rng=rng, seed=seed) + else + SimpleGraph(n, ne; rng=rng, seed=seed) + end end """ @@ -160,10 +181,17 @@ julia> erdos_renyi(10, 30, is_directed=true, seed=123) ``` """ function erdos_renyi( - n::Integer, ne::Integer; - is_directed=false, rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + n::Integer, + ne::Integer; + is_directed=false, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) - return is_directed ? SimpleDiGraph(n, ne; rng=rng, seed=seed) : SimpleGraph(n, ne; rng=rng, seed=seed) + return if is_directed + SimpleDiGraph(n, ne; rng=rng, seed=seed) + else + SimpleGraph(n, ne; rng=rng, seed=seed) + end end """ @@ -203,25 +231,28 @@ julia> print(degree(g)) """ function expected_degree_graph( ω::Vector{T}; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Real + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Real} g = SimpleGraph(length(ω)) - expected_degree_graph!(g, ω; rng=rng, seed=seed) + return expected_degree_graph!(g, ω; rng=rng, seed=seed) end function expected_degree_graph!( - g::SimpleGraph, ω::Vector{T}; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Real + g::SimpleGraph, + ω::Vector{T}; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Real} n = length(ω) @assert all(zero(T) .<= ω .<= n - one(T)) "Elements of ω needs to be at least 0 and at most n-1" - π = sortperm(ω, rev=true) + π = sortperm(ω; rev=true) rng = rng_from_rng_or_seed(rng, seed) S = sum(ω) - for u = 1:(n - 1) + for u in 1:(n - 1) v = u + 1 p = min(ω[π[u]] * ω[π[v]] / S, one(T)) while v <= n && p > zero(p) @@ -241,16 +272,15 @@ function expected_degree_graph!( return g end - """ watts_strogatz(n, k, β) Return a [Watts-Strogatz](https://en.wikipedia.org/wiki/Watts_and_Strogatz_model) -small world random graph with `n` vertices, each with expected degree `k` +small world random graph with `n` vertices, each with expected degree `k` (or `k - 1` if `k` is odd). Edges are randomized per the model based on probability `β`. The algorithm proceeds as follows. First, a perfect 1-lattice is constructed, -where each vertex has exacly `div(k, 2)` neighbors on each side (i.e., `k` or +where each vertex has exactly `div(k, 2)` neighbors on each side (i.e., `k` or `k - 1` in total). Then the following steps are repeated for a hop length `i` of `1` through `div(k, 2)`. @@ -284,8 +314,12 @@ julia> watts_strogatz(Int8(10), 4, 0.8, is_directed=true, seed=123) - Small Worlds, Duncan J. watts. [https://en.wikipedia.org/wiki/Special:BookSources?isbn=978-0691005416](https://en.wikipedia.org/wiki/Special:BookSources?isbn=978-0691005416) """ function watts_strogatz( - n::Integer, k::Integer, β::Real; - is_directed=false, rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + n::Integer, + k::Integer, + β::Real; + is_directed=false, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) @assert k < n @@ -297,7 +331,6 @@ function watts_strogatz( g = is_directed ? SimpleDiGraph(n) : SimpleGraph(n) - # The ith next vertex, in clockwise order. # (Reduce to zero-based indexing, so the modulo works, by subtracting 1 # before and adding 1 after.) @@ -306,7 +339,7 @@ function watts_strogatz( # Phase 1: For each step size i, add an edge from each vertex s to the ith # next vertex, in clockwise order. - for i = 1:div(k, 2), s = 1:n + for i in 1:div(k, 2), s in 1:n add_edge!(g, s, target(s, i)) end @@ -315,7 +348,7 @@ function watts_strogatz( # and rewire it to any (valid) target, chosen uniformly at random. rng = rng_from_rng_or_seed(rng, seed) - for i = 1:div(k, 2), s = 1:n + for i in 1:div(k, 2), s in 1:n # We only rewire with a probability β, and we only worry about rewiring # if there is some vertex not connected to s; otherwise, the only valid @@ -334,13 +367,11 @@ function watts_strogatz( break # We found a valid target end end - end return g end - -function _suitable(edges::Set{SimpleEdge{T}}, potential_edges::Dict{T,T}) where T <: Integer +function _suitable(edges::Set{SimpleEdge{T}}, potential_edges::Dict{T,T}) where {T<:Integer} isempty(potential_edges) && return true list = keys(potential_edges) for s1 in list, s2 in list @@ -352,12 +383,12 @@ end _try_creation(n::Integer, k::Integer, rng::AbstractRNG) = _try_creation(n, fill(k, n), rng) -function _try_creation(n::T, k::Vector{T}, rng::AbstractRNG) where T <: Integer +function _try_creation(n::T, k::Vector{T}, rng::AbstractRNG) where {T<:Integer} edges = Set{SimpleEdge{T}}() m = 0 stubs = zeros(T, sum(k)) - for i = one(T):n - for j = one(T):k[i] + for i in one(T):n + for j in one(T):k[i] m += 1 stubs[m] = i end @@ -365,7 +396,7 @@ function _try_creation(n::T, k::Vector{T}, rng::AbstractRNG) where T <: Integer # stubs = vcat([fill(i, k[i]) for i = 1:n]...) # slower while !isempty(stubs) - potential_edges = Dict{T,T}() + potential_edges = Dict{T,T}() shuffle!(rng, stubs) for i in 1:2:length(stubs) s1, s2 = stubs[i:(i + 1)] @@ -416,8 +447,7 @@ julia> barabasi_albert(100, Int8(10), is_directed=true, complete=true, seed=123) {100, 990} directed simple Int8 graph ``` """ -barabasi_albert(n::Integer, k::Integer; keyargs...) = -barabasi_albert(n, k, k; keyargs...) +barabasi_albert(n::Integer, k::Integer; keyargs...) = barabasi_albert(n, k, k; keyargs...) """ barabasi_albert(n::Integer, n0::Integer, k::Integer) @@ -444,8 +474,13 @@ julia> barabasi_albert(100, Int8(10), 3, is_directed=true, seed=123) ``` """ function barabasi_albert( - n::Integer, n0::Integer, k::Integer; - is_directed::Bool=false, complete::Bool=false, rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + n::Integer, + n0::Integer, + k::Integer; + is_directed::Bool=false, + complete::Bool=false, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) if complete g = is_directed ? complete_digraph(n0) : complete_graph(n0) @@ -480,8 +515,11 @@ julia> g ``` """ function barabasi_albert!( - g::AbstractGraph, n::Integer, k::Integer; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + g::AbstractGraph, + n::Integer, + k::Integer; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) n0 = nv(g) 1 <= k <= n0 <= n || @@ -525,7 +563,7 @@ function barabasi_albert!( for source in (n0 + 1):n # choose k targets from the existing vertices - # pick uniformly from weightedVs (preferential attachement) + # pick uniformly from weightedVs (preferential attachment) i = 0 while i < k target = weightedVs[rand(rng, 1:offset)] @@ -547,7 +585,6 @@ function barabasi_albert!( return g end - """ static_fitness_model(m, fitness) @@ -580,9 +617,11 @@ julia> edges(g) |> collect ``` """ function static_fitness_model( - m::Integer, fitness::Vector{T}; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Real + m::Integer, + fitness::Vector{T}; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Real} m < 0 && throw(ArgumentError("number of edges must be positive")) n = length(fitness) m == 0 && return SimpleGraph(n) @@ -594,7 +633,8 @@ function static_fitness_model( end # avoid getting into an infinite loop when too many edges are requested max_no_of_edges = div(nvs * (nvs - 1), 2) - m > max_no_of_edges && throw(ArgumentError("too many edges requested ($m > $max_no_of_edges)")) + m > max_no_of_edges && + throw(ArgumentError("too many edges requested ($m > $max_no_of_edges)")) # calculate the cumulative fitness scores cum_fitness = cumsum(fitness) g = SimpleGraph(n) @@ -635,24 +675,30 @@ julia> edges(g) |> collect ``` """ function static_fitness_model( - m::Integer, fitness_out::Vector{T}, fitness_in::Vector{S}; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Real where S <: Real + m::Integer, + fitness_out::Vector{T}, + fitness_in::Vector{S}; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Real} where {S<:Real} m < 0 && throw(ArgumentError("number of edges must be positive")) n = length(fitness_out) - length(fitness_in) != n && throw(ArgumentError("fitness_in must have the same size as fitness_out")) + length(fitness_in) != n && + throw(ArgumentError("fitness_in must have the same size as fitness_out")) m == 0 && return SimpleDiGraph(n) # avoid getting into an infinite loop when too many edges are requested noutvs = ninvs = nvs = 0 - @inbounds for i = 1:n + @inbounds for i in 1:n # sanity check for the fitness - (fitness_out[i] < zero(T) || fitness_in[i] < zero(S)) && error("fitness scores must be non-negative") # TODO 0.7: change to DomainError? + (fitness_out[i] < zero(T) || fitness_in[i] < zero(S)) && + error("fitness scores must be non-negative") # TODO 0.7: change to DomainError? fitness_out[i] > zero(T) && (noutvs += 1) fitness_in[i] > zero(S) && (ninvs += 1) (fitness_out[i] > zero(T) && fitness_in[i] > zero(S)) && (nvs += 1) end max_no_of_edges = noutvs * ninvs - nvs - m > max_no_of_edges && throw(ArgumentError("too many edges requested ($m > $max_no_of_edges)")) + m > max_no_of_edges && + throw(ArgumentError("too many edges requested ($m > $max_no_of_edges)")) # calculate the cumulative fitness scores cum_fitness_out = cumsum(fitness_out) cum_fitness_in = cumsum(fitness_in) @@ -662,8 +708,13 @@ function static_fitness_model( end function _create_static_fitness_graph!( - g::AbstractGraph, m::Integer, cum_fitness_out::Vector{T}, cum_fitness_in::Vector{S}, rng::Union{Nothing, AbstractRNG}, seed::Union{Nothing, Integer} -) where T <: Real where S <: Real + g::AbstractGraph, + m::Integer, + cum_fitness_out::Vector{T}, + cum_fitness_in::Vector{S}, + rng::Union{Nothing,AbstractRNG}, + seed::Union{Nothing,Integer}, +) where {T<:Real} where {S<:Real} max_out = cum_fitness_out[end] max_in = cum_fitness_in[end] rng = rng_from_rng_or_seed(rng, seed) @@ -699,13 +750,17 @@ Time complexity is ``\\mathcal{O}(|V| + |E| log |E|)``. - Cho YS, Kim JS, Park J, Kahng B, Kim D: Percolation transitions in scale-free networks under the Achlioptas process. Phys Rev Lett 103:135702, 2009. """ function static_scale_free( - n::Integer, m::Integer, α::Real; - finite_size_correction::Bool=true, rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + n::Integer, + m::Integer, + α::Real; + finite_size_correction::Bool=true, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) n < 0 && throw(ArgumentError("number of vertices must be positive")) α < 2 && throw(ArgumentError("out-degree exponent must be >= 2")) fitness = _construct_fitness(n, α, finite_size_correction) - static_fitness_model(m, fitness; rng=rng, seed=seed) + return static_fitness_model(m, fitness; rng=rng, seed=seed) end """ @@ -730,8 +785,13 @@ Time complexity is ``\\mathcal{O}(|V| + |E| log |E|)``. - Cho YS, Kim JS, Park J, Kahng B, Kim D: Percolation transitions in scale-free networks under the Achlioptas process. Phys Rev Lett 103:135702, 2009. """ function static_scale_free( - n::Integer, m::Integer, α_out::Real, α_in::Float64; - finite_size_correction::Bool=true, rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + n::Integer, + m::Integer, + α_out::Real, + α_in::Float64; + finite_size_correction::Bool=true, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) n < 0 && throw(ArgumentError("number of vertices must be positive")) α_out < 2 && throw(ArgumentError("out-degree exponent must be >= 2")) @@ -741,7 +801,7 @@ function static_scale_free( fitness_in = _construct_fitness(n, α_in, finite_size_correction) # eliminate correlation shuffle!(fitness_in) - static_fitness_model(m, fitness_out, fitness_in; rng=rng, seed=seed) + return static_fitness_model(m, fitness_out, fitness_in; rng=rng, seed=seed) end function _construct_fitness(n::Integer, α::Real, finite_size_correction::Bool) @@ -753,7 +813,7 @@ function _construct_fitness(n::Integer, α::Real, finite_size_correction::Bool) j += n^(1 + 1 / 2 * α) * (10 * sqrt(2) * (1 + α))^(-1 / α) - 1 end j = max(j, n) - @inbounds for i = 1:n + @inbounds for i in 1:n fitness[i] = j^α j -= 1 end @@ -779,8 +839,10 @@ Allocates an array of `nk` `Int`s, and . For ``k > \\frac{n}{2}``, generates a g ``n-k-1`` and returns its complement. """ function random_regular_graph( - n::Integer, k::Integer; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + n::Integer, + k::Integer; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) !iseven(n * k) && throw(ArgumentError("n * k must be even")) !(0 <= k < n) && throw(ArgumentError("the 0 <= k < n inequality must be satisfied")) @@ -824,13 +886,17 @@ Time complexity is approximately ``\\mathcal{O}(n \\bar{k}^2)``. Allocates an array of ``n \\bar{k}`` `Int`s. """ function random_configuration_model( - n::Integer, k::Array{T}; - check_graphical::Bool=false, rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Integer + n::Integer, + k::Array{T}; + check_graphical::Bool=false, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Integer} n != length(k) && throw(ArgumentError("a degree sequence of length n must be provided")) m = sum(k) !iseven(m) && throw(ArgumentError("sum(k) must be even")) - !all(0 .<= k .< n) && throw(ArgumentError("the 0 <= k[i] < n inequality must be satisfied")) + !all(0 .<= k .< n) && + throw(ArgumentError("the 0 <= k[i] < n inequality must be satisfied")) if check_graphical isgraphical(k) || throw(ArgumentError("degree sequence must be graphical")) end @@ -855,7 +921,7 @@ Create a random directed [regular graph](https://en.wikipedia.org/wiki/Regular_g with `n` vertices, each with degree `k`. ### Optional Arguments -- `dir=:out`: the direction of the edges for degree parameter. +- `dir=:out`: the direction of the edges for the degree parameter. - `rng=nothing`: set the Random Number Generator. - `seed=nothing`: set the RNG seed. @@ -864,10 +930,13 @@ Allocates an ``n × n`` sparse matrix of boolean as an adjacency matrix and uses that to generate the directed graph. """ function random_regular_digraph( - n::Integer, k::Integer; - dir::Symbol=:out, rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + n::Integer, + k::Integer; + dir::Symbol=:out, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) - #TODO remove the function sample from StatsBase for one allowing the use + # TODO remove the function sample from StatsBase for one allowing the use # of a local rng !(0 <= k < n) && throw(ArgumentError("the 0 <= k < n inequality must be satisfied")) @@ -886,7 +955,7 @@ function random_regular_digraph( for r in 1:n l = ((r - 1) * k + 1):(r * k) I[l] .= r - J[l] = sample!(rng, cs, k, exclude=r) + J[l] = sample!(rng, cs, k; exclude=r) end m = dir == :out ? sparse(I, J, V, n, n) : sparse(I, J, V, n, n)' @@ -913,11 +982,15 @@ julia> random_tournament_digraph(Int8(10), seed=123) {10, 45} directed simple Int8 graph ``` """ -function random_tournament_digraph(n::Integer; rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing) +function random_tournament_digraph( + n::Integer; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) rng = rng_from_rng_or_seed(rng, seed) g = SimpleDiGraph(n) - for i = 1:n, j = i + 1:n + for i in 1:n, j in (i + 1):n rand(rng, Bool) ? add_edge!(g, SimpleEdge(i, j)) : add_edge!(g, SimpleEdge(j, i)) end @@ -930,7 +1003,7 @@ end Return a Graph generated according to the Stochastic Block Model (SBM). `c[a,b]` : Mean number of neighbors of a vertex in block `a` belonging to block `b`. - Only the upper triangular part is considered, since the lower traingular is + Only the upper triangular part is considered, since the lower triangular is determined by ``c[b,a] = c[a,b] * \\frac{n[a]}{n[b]}``. `n[a]` : Number of vertices in block `a` @@ -942,10 +1015,13 @@ For a dynamic version of the SBM see the [`StochasticBlockModel`](@ref) type and related functions. """ function stochastic_block_model( - c::Matrix{T}, n::Vector{U}; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Real where U <: Integer - size(c, 1) == size(c, 2) == length(n) || throw(ArgumentError("matrix-vector size mismatch")) + c::Matrix{T}, + n::Vector{U}; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Real} where {U<:Integer} + size(c, 1) == size(c, 2) == length(n) || + throw(ArgumentError("matrix-vector size mismatch")) # init dsfmt generator with a fixed seed rng = rng_from_rng_or_seed(rng, seed) @@ -953,12 +1029,14 @@ function stochastic_block_model( K = length(n) nedg = zeros(Int, K, K) g = SimpleGraph(N) - cum = [sum(n[1:a]) for a = 0:K] - for a = 1:K + cum = [sum(n[1:a]) for a in 0:K] + for a in 1:K ra = (cum[a] + 1):cum[a + 1] - for b = a:K - ((a == b) && !(c[a, b] <= n[b] - 1)) || ((a != b) && !(c[a, b] <= n[b])) && - error("Mean degree cannot be greater than available neighbors in the block.") # TODO 0.7: turn into some other error? + for b in a:K + ((a == b) && !(c[a, b] <= n[b] - 1)) || + ((a != b) && !(c[a, b] <= n[b])) && error( + "Mean degree cannot be greater than available neighbors in the block.", + ) # TODO 0.7: turn into some other error? m = a == b ? div(n[a] * (n[a] - 1), 2) : n[a] * n[b] p = a == b ? n[a] * c[a, b] / (2m) : n[a] * c[a, b] / m @@ -986,12 +1064,15 @@ Return a Graph generated according to the Stochastic Block Model (SBM), sampling from an SBM with ``c_{a,a}=cint``, and ``c_{a,b}=cext``. """ function stochastic_block_model( - cint::T, cext::T, n::Vector{U}; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Real where U <: Integer + cint::T, + cext::T, + n::Vector{U}; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Real} where {U<:Integer} K = length(n) - c = [ifelse(a == b, cint, cext) for a = 1:K, b = 1:K] - stochastic_block_model(c, n; rng=rng, seed=seed) + c = [ifelse(a == b, cint, cext) for a in 1:K, b in 1:K] + return stochastic_block_model(c, n; rng=rng, seed=seed) end """ @@ -1001,7 +1082,7 @@ A type capturing the parameters of the SBM. Each vertex is assigned to a block and the probability of edge `(i,j)` depends only on the block labels of vertex `i` and vertex `j`. -The assignement is stored in nodemap and the block affinities a `k` by `k` +The assignment is stored in nodemap and the block affinities a `k` by `k` matrix is stored in affinities. `affinities[k,l]` is the probability of an edge between any vertex in @@ -1011,15 +1092,17 @@ block `k` and any vertex in block `l`. Graphs are generated by taking random ``i,j ∈ V`` and flipping a coin with probability `affinities[nodemap[i],nodemap[j]]`. """ -mutable struct StochasticBlockModel{T <: Integer,P <: Real} +mutable struct StochasticBlockModel{T<:Integer,P<:Real} n::T nodemap::Array{T} affinities::Matrix{P} end -==(sbm::StochasticBlockModel, other::StochasticBlockModel) = - (sbm.n == other.n) && (sbm.nodemap == other.nodemap) && (sbm.affinities == other.affinities) - +function ==(sbm::StochasticBlockModel, other::StochasticBlockModel) + return (sbm.n == other.n) && + (sbm.nodemap == other.nodemap) && + (sbm.affinities == other.affinities) +end # A constructor for StochasticBlockModel that uses the sizes of the blocks # and the affinity matrix. This construction implies that consecutive @@ -1037,7 +1120,6 @@ function StochasticBlockModel(sizes::AbstractVector, affinities::AbstractMatrix) return StochasticBlockModel(csum[end], nodemap, affinities) end - ### TODO: This documentation needs work. sbromberger 20170326 """ sbmaffinity(internalp, externalp, sizes) @@ -1045,31 +1127,34 @@ end Produce the sbm affinity matrix with internal probabilities `internalp` and external probabilities `externalp`. """ -function sbmaffinity(internalp::Vector{T}, externalp::Real, sizes::Vector{U}) where T <: Real where U <: Integer +function sbmaffinity( + internalp::Vector{T}, externalp::Real, sizes::Vector{U} +) where {T<:Real} where {U<:Integer} numblocks = length(sizes) - numblocks == length(internalp) || throw(ArgumentError("Inconsistent input dimensions: internalp, sizes")) + numblocks == length(internalp) || + throw(ArgumentError("Inconsistent input dimensions: internalp, sizes")) B = diagm(0 => internalp) + externalp * (ones(numblocks, numblocks) - I) return B end -function StochasticBlockModel(internalp::Real, - externalp::Real, - size::Integer, - numblocks::Integer) +function StochasticBlockModel( + internalp::Real, externalp::Real, size::Integer, numblocks::Integer +) sizes = fill(size, numblocks) B = sbmaffinity(fill(internalp, numblocks), externalp, sizes) - StochasticBlockModel(sizes, B) + return StochasticBlockModel(sizes, B) end -function StochasticBlockModel(internalp::Vector{T}, externalp::Real, sizes::Vector{U}) where {T <: Real, U <: Integer} +function StochasticBlockModel( + internalp::Vector{T}, externalp::Real, sizes::Vector{U} +) where {T<:Real,U<:Integer} B = sbmaffinity(internalp, externalp, sizes) return StochasticBlockModel(sizes, B) end - const biclique = ones(2, 2) - Matrix{Float64}(I, 2, 2) -#TODO: this documentation needs work. sbromberger 20170326 +# TODO: this documentation needs work. sbromberger 20170326 """ nearbipartiteaffinity(sizes, between, intra) @@ -1081,17 +1166,24 @@ This is a specific type of SBM with ``\\frac{k}{2} blocks each with two halves. Each half is connected as a random bipartite graph with probability `intra` The blocks are connected with probability `between`. """ -function nearbipartiteaffinity(sizes::AbstractVector{T}, between::Real, intra::Real) where {T <: Integer} +function nearbipartiteaffinity( + sizes::AbstractVector{T}, between::Real, intra::Real +) where {T<:Integer} numblocks = div(length(sizes), 2) - return kron(between * Matrix{Float64}(I, numblocks, numblocks), biclique) + Matrix{Float64}(I, 2 * numblocks, 2 * numblocks) * intra + return kron(between * Matrix{Float64}(I, numblocks, numblocks), biclique) + + Matrix{Float64}(I, 2 * numblocks, 2 * numblocks) * intra end -#Return a generator for edges from a stochastic block model near-bipartite graph. -nearbipartiteaffinity(sizes::Vector{T}, between::Real, inter::Real, noise::Real) where {T <: Integer} = - nearbipartiteaffinity(sizes, between, inter) .+ noise +# Return a generator for edges from a stochastic block model near-bipartite graph. +function nearbipartiteaffinity( + sizes::Vector{T}, between::Real, inter::Real, noise::Real +) where {T<:Integer} + return nearbipartiteaffinity(sizes, between, inter) .+ noise +end -nearbipartiteSBM(sizes, between, inter, noise) = - StochasticBlockModel(sizes, nearbipartiteaffinity(sizes, between, inter, noise)) +function nearbipartiteSBM(sizes, between, inter, noise) + return StochasticBlockModel(sizes, nearbipartiteaffinity(sizes, between, inter, noise)) +end """ random_pair(rng, n) @@ -1107,7 +1199,6 @@ function random_pair(rng::AbstractRNG, n::Integer) return f end - """ make_edgestream(sbm; rng=nothing, seed=nothing) @@ -1116,10 +1207,11 @@ Pass to `Graph(nvg, neg, edgestream)` to get a Graph object based on `sbm`. """ function make_edgestream( sbm::StochasticBlockModel; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) rng = rng_from_rng_or_seed(rng, seed) - pairs = Channel(random_pair(rng, sbm.n), ctype=SimpleEdge, csize=32) + pairs = Channel(random_pair(rng, sbm.n); ctype=SimpleEdge, csize=32) edges(ch) = begin for e in pairs i, j = Tuple(e) @@ -1130,7 +1222,7 @@ function make_edgestream( end end end - return Channel(edges, ctype=SimpleEdge, csize=32) + return Channel(edges; ctype=SimpleEdge, csize=32) end """ @@ -1159,27 +1251,29 @@ The graph is sampled according to the stochastic block model `smb`. The element type is the type of `nv`. """ function SimpleGraph( - nvg::Integer, neg::Integer, sbm::StochasticBlockModel; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + nvg::Integer, + neg::Integer, + sbm::StochasticBlockModel; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) return SimpleGraph(nvg, neg, make_edgestream(sbm; rng=rng, seed=seed)) end -#TODO: this documentation needs work. sbromberger 20170326 +# TODO: this documentation needs work. sbromberger 20170326 """ blockcounts(sbm, A) Count the number of edges that go between each block. """ function blockcounts(sbm::StochasticBlockModel, A::AbstractMatrix) - I = collect(1:sbm.n) - J = [sbm.nodemap[i] for i in 1:sbm.n] - V = ones(sbm.n) + I = collect(1:(sbm.n)) + J = [sbm.nodemap[i] for i in 1:(sbm.n)] + V = ones(sbm.n) Q = sparse(I, J, V) return (Q'A) * Q end - function blockcounts(sbm::StochasticBlockModel, g::AbstractGraph) return blockcounts(sbm, adjacency_matrix(g)) end @@ -1201,8 +1295,13 @@ References - http://www.graph500.org/specifications#alg:generator """ function kronecker( - SCALE, edgefactor, A=0.57, B=0.19, C=0.19; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + SCALE, + edgefactor, + A=0.57, + B=0.19, + C=0.19; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) N = 2^SCALE M = edgefactor * N @@ -1212,7 +1311,7 @@ function kronecker( a_norm = A / (A + B) rng = rng_from_rng_or_seed(rng, seed) - for ib = 1:SCALE + for ib in 1:SCALE ii_bit = rand(rng, M) .> (ab) # bitarray jj_bit = rand(rng, M) .> (c_norm .* (ii_bit) + a_norm .* .!(ii_bit)) ij .+= 2^(ib - 1) .* (hcat(ii_bit, jj_bit)) @@ -1238,7 +1337,7 @@ Generate a random `n` vertex graph by the Dorogovtsev-Mendes method (with `n \\g The Dorogovtsev-Mendes process begins with a triangle graph and inserts `n-3` additional vertices. Each time a vertex is added, a random edge is selected and the new vertex is connected to the two -endpoints of the chosen edge. This creates graphs with a many triangles and a high local clustering coefficient. +endpoints of the chosen edge. This creates graphs with many triangles and a high local clustering coefficient. It is often useful to track the evolution of the graph as vertices are added, you can access the graph from the `t`th stage of this algorithm by accessing the first `t` vertices with `g[1:t]`. @@ -1258,14 +1357,15 @@ julia> dorogovtsev_mendes(11, seed=123) """ function dorogovtsev_mendes( n::Integer; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) n < 3 && throw(DomainError("n=$n must be at least 3")) rng = rng_from_rng_or_seed(rng, seed) g = cycle_graph(3) - for iteration in 1:(n-3) - chosenedge = rand(rng, 1:(2*ne(g))) # undirected so each edge is listed twice in adjlist + for iteration in 1:(n - 3) + chosenedge = rand(rng, 1:(2 * ne(g))) # undirected so each edge is listed twice in adjlist u, v = -1, -1 for i in 1:nv(g) edgelist = outneighbors(g, i) @@ -1306,8 +1406,9 @@ julia> random_orientation_dag(star_graph(Int8(10)), 123) """ function random_orientation_dag( g::SimpleGraph{T}; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where {T <: Integer} + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Integer} nvg = length(g.fadjlist) rng = rng_from_rng_or_seed(rng, seed) order = randperm(rng, nvg) diff --git a/src/SimpleGraphs/generators/smallgraphs.jl b/src/SimpleGraphs/generators/smallgraphs.jl index 1a42dbf07..0706d5d81 100644 --- a/src/SimpleGraphs/generators/smallgraphs.jl +++ b/src/SimpleGraphs/generators/smallgraphs.jl @@ -36,29 +36,29 @@ Create a small graph of type `s`. Admissible values for `s` are: """ function smallgraph(s::Symbol) graphmap = Dict( - :bull => bull_graph, - :chvatal => chvatal_graph, - :cubical => cubical_graph, - :desargues => desargues_graph, - :diamond => diamond_graph, - :dodecahedral => dodecahedral_graph, - :frucht => frucht_graph, - :heawood => heawood_graph, - :house => house_graph, - :housex => house_x_graph, - :icosahedral => icosahedral_graph, - :karate => karate_graph, - :krackhardtkite => krackhardt_kite_graph, - :moebiuskantor => moebius_kantor_graph, - :octahedral => octahedral_graph, - :pappus => pappus_graph, - :petersen => petersen_graph, - :sedgewickmaze => sedgewick_maze_graph, - :tetrahedral => tetrahedral_graph, - :truncatedcube => truncated_cube_graph, - :truncatedtetrahedron => truncated_tetrahedron_graph, - :truncatedtetrahedron_dir => truncated_tetrahedron_digraph, - :tutte => tutte_graph + :bull => bull_graph, + :chvatal => chvatal_graph, + :cubical => cubical_graph, + :desargues => desargues_graph, + :diamond => diamond_graph, + :dodecahedral => dodecahedral_graph, + :frucht => frucht_graph, + :heawood => heawood_graph, + :house => house_graph, + :housex => house_x_graph, + :icosahedral => icosahedral_graph, + :karate => karate_graph, + :krackhardtkite => krackhardt_kite_graph, + :moebiuskantor => moebius_kantor_graph, + :octahedral => octahedral_graph, + :pappus => pappus_graph, + :petersen => petersen_graph, + :sedgewickmaze => sedgewick_maze_graph, + :tetrahedral => tetrahedral_graph, + :truncatedcube => truncated_cube_graph, + :truncatedtetrahedron => truncated_tetrahedron_graph, + :truncatedtetrahedron_dir => truncated_tetrahedron_digraph, + :tutte => tutte_graph, ) if (s in keys(graphmap)) @@ -76,135 +76,192 @@ function smallgraph(s::AbstractString) return smallgraph(Symbol(ls)) end -diamond_graph() = SimpleGraph( SimpleEdge.([(1, 2), (1, 3), (2, 3), (2, 4), (3, 4)]) ) +diamond_graph() = SimpleGraph(SimpleEdge.([(1, 2), (1, 3), (2, 3), (2, 4), (3, 4)])) bull_graph() = SimpleGraph(SimpleEdge.([(1, 2), (1, 3), (2, 3), (2, 4), (3, 5)])) - function chvatal_graph() - e = SimpleEdge.([ - (1, 2), (1, 5), (1, 7), (1, 10), - (2, 3), (2, 6), (2, 8), - (3, 4), (3, 7), (3, 9), - (4, 5), (4, 8), (4, 10), - (5, 6), (5, 9), - (6, 11), (6, 12), - (7, 11), (7, 12), - (8, 9), (8, 12), - (9, 11), - (10, 11), (10, 12) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 5), + (1, 7), + (1, 10), + (2, 3), + (2, 6), + (2, 8), + (3, 4), + (3, 7), + (3, 9), + (4, 5), + (4, 8), + (4, 10), + (5, 6), + (5, 9), + (6, 11), + (6, 12), + (7, 11), + (7, 12), + (8, 9), + (8, 12), + (9, 11), + (10, 11), + (10, 12), + ]) return SimpleGraph(e) end function cubical_graph() - e = SimpleEdge.([ - (1, 2), (1, 4), (1, 5), - (2, 3), (2, 8), - (3, 4), (3, 7), - (4, 6), (5, 6), (5, 8), - (6, 7), - (7, 8) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 4), + (1, 5), + (2, 3), + (2, 8), + (3, 4), + (3, 7), + (4, 6), + (5, 6), + (5, 8), + (6, 7), + (7, 8), + ]) return SimpleGraph(e) end - function desargues_graph() - e = SimpleEdge.([ - (1, 2), (1, 6), (1, 20), - (2, 3), (2, 17), - (3, 4), (3, 12), - (4, 5), (4, 15), - (5, 6), (5, 10), - (6, 7), - (7, 8), (7, 16), - (8, 9), (8, 19), - (9, 10), (9, 14), - (10, 11), - (11, 12), (11, 20), - (12, 13), - (13, 14), (13, 18), - (14, 15), - (15, 16), - (16, 17), - (17, 18), - (18, 19), - (19, 20) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 6), + (1, 20), + (2, 3), + (2, 17), + (3, 4), + (3, 12), + (4, 5), + (4, 15), + (5, 6), + (5, 10), + (6, 7), + (7, 8), + (7, 16), + (8, 9), + (8, 19), + (9, 10), + (9, 14), + (10, 11), + (11, 12), + (11, 20), + (12, 13), + (13, 14), + (13, 18), + (14, 15), + (15, 16), + (16, 17), + (17, 18), + (18, 19), + (19, 20), + ]) return SimpleGraph(e) end - function dodecahedral_graph() - e = SimpleEdge.([ - (1, 2), (1, 11), (1, 20), - (2, 3), (2, 9), - (3, 4), (3, 7), - (4, 5), (4, 20), - (5, 6), (5, 18), - (6, 7), (6, 16), - (7, 8), - (8, 9), (8, 15), - (9, 10), - (10, 11), (10, 14), - (11, 12), - (12, 13), (12, 19), - (13, 14), (13, 17), - (14, 15), - (15, 16), - (16, 17), - (17, 18), - (18, 19), - (19, 20) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 11), + (1, 20), + (2, 3), + (2, 9), + (3, 4), + (3, 7), + (4, 5), + (4, 20), + (5, 6), + (5, 18), + (6, 7), + (6, 16), + (7, 8), + (8, 9), + (8, 15), + (9, 10), + (10, 11), + (10, 14), + (11, 12), + (12, 13), + (12, 19), + (13, 14), + (13, 17), + (14, 15), + (15, 16), + (16, 17), + (17, 18), + (18, 19), + (19, 20), + ]) return SimpleGraph(e) end - function frucht_graph() - e = SimpleEdge.([ - (1, 2), (1, 7), (1, 8), - (2, 3), (2, 8), - (3, 4), (3, 9), - (4, 5), (4, 10), - (5, 6), (5, 10), - (6, 7), (6, 11), - (7, 11), - (8, 12), - (9, 10), (9, 12), - (11, 12) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 7), + (1, 8), + (2, 3), + (2, 8), + (3, 4), + (3, 9), + (4, 5), + (4, 10), + (5, 6), + (5, 10), + (6, 7), + (6, 11), + (7, 11), + (8, 12), + (9, 10), + (9, 12), + (11, 12), + ]) return SimpleGraph(e) end - function heawood_graph() - e = SimpleEdge.([ - (1, 2), (1, 6), (1, 14), - (2, 3), (2, 11), - (3, 4), (3, 8), - (4, 5), (4, 13), - (5, 6), (5, 10), - (6, 7), - (7, 8), (7, 12), - (8, 9), - (9, 10), (9, 14), - (10, 11), - (11, 12), - (12, 13), - (13, 14) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 6), + (1, 14), + (2, 3), + (2, 11), + (3, 4), + (3, 8), + (4, 5), + (4, 13), + (5, 6), + (5, 10), + (6, 7), + (7, 8), + (7, 12), + (8, 9), + (9, 10), + (9, 14), + (10, 11), + (11, 12), + (12, 13), + (13, 14), + ]) return SimpleGraph(e) end - function house_graph() e = SimpleEdge.([(1, 2), (1, 3), (2, 4), (3, 4), (3, 5), (4, 5)]) return SimpleGraph(e) end - function house_x_graph() g = house_graph() add_edge!(g, SimpleEdge(1, 4)) @@ -212,253 +269,436 @@ function house_x_graph() return g end - function icosahedral_graph() - e = SimpleEdge.([ - (1, 2), (1, 6), (1, 8), (1, 9), (1, 12), - (2, 3), (2, 6), (2, 7), (2, 9), - (3, 4), (3, 7), (3, 9), (3, 10), - (4, 5), (4, 7), (4, 10), (4, 11), - (5, 6), (5, 7), (5, 11), (5, 12), - (6, 7), (6, 12), - (8, 9), (8, 10), (8, 11), (8, 12), - (9, 10), - (10, 11), (11, 12) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 6), + (1, 8), + (1, 9), + (1, 12), + (2, 3), + (2, 6), + (2, 7), + (2, 9), + (3, 4), + (3, 7), + (3, 9), + (3, 10), + (4, 5), + (4, 7), + (4, 10), + (4, 11), + (5, 6), + (5, 7), + (5, 11), + (5, 12), + (6, 7), + (6, 12), + (8, 9), + (8, 10), + (8, 11), + (8, 12), + (9, 10), + (10, 11), + (11, 12), + ]) return SimpleGraph(e) end function karate_graph() - e = SimpleEdge.([ - (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), - (1, 8), (1, 9), (1, 11), (1, 12), (1, 13), (1, 14), - (1, 18), (1, 20), (1, 22), (1, 32), (2, 3), (2, 4), - (2, 8), (2, 14), (2, 18), (2, 20), (2, 22), (2, 31), - (3, 4), (3, 8), (3, 9), (3, 10), (3, 14), (3, 28), - (3, 29), (3, 33), (4, 8), (4, 13) , (4, 14), (5, 7), - (5, 11), (6, 7), (6, 11), (6, 17) , (7, 17), (9, 31), - (9, 33), (9, 34), (10, 34), (14, 34), (15, 33), (15, 34), - (16, 33), (16, 34), (19, 33), (19, 34), (20, 34), (21, 33), - (21, 34), (23, 33), (23, 34), (24, 26), (24, 28), (24, 30), - (24, 33), (24, 34), (25, 26), (25, 28), (25, 32), (26, 32), - (27, 30), (27, 34), (28, 34), (29, 32), (29, 34), (30, 33), - (30, 34), (31, 33), (31, 34), (32, 33), (32, 34), (33, 34), - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 3), + (1, 4), + (1, 5), + (1, 6), + (1, 7), + (1, 8), + (1, 9), + (1, 11), + (1, 12), + (1, 13), + (1, 14), + (1, 18), + (1, 20), + (1, 22), + (1, 32), + (2, 3), + (2, 4), + (2, 8), + (2, 14), + (2, 18), + (2, 20), + (2, 22), + (2, 31), + (3, 4), + (3, 8), + (3, 9), + (3, 10), + (3, 14), + (3, 28), + (3, 29), + (3, 33), + (4, 8), + (4, 13), + (4, 14), + (5, 7), + (5, 11), + (6, 7), + (6, 11), + (6, 17), + (7, 17), + (9, 31), + (9, 33), + (9, 34), + (10, 34), + (14, 34), + (15, 33), + (15, 34), + (16, 33), + (16, 34), + (19, 33), + (19, 34), + (20, 34), + (21, 33), + (21, 34), + (23, 33), + (23, 34), + (24, 26), + (24, 28), + (24, 30), + (24, 33), + (24, 34), + (25, 26), + (25, 28), + (25, 32), + (26, 32), + (27, 30), + (27, 34), + (28, 34), + (29, 32), + (29, 34), + (30, 33), + (30, 34), + (31, 33), + (31, 34), + (32, 33), + (32, 34), + (33, 34), + ]) return SimpleGraph(e) end function krackhardt_kite_graph() - e = SimpleEdge.([ - (1, 2), (1, 3), (1, 4), (1, 6), - (2, 4), (2, 5), (2, 7), - (3, 4), (3, 6), - (4, 5), (4, 6), (4, 7), - (5, 7), - (6, 7), (6, 8), - (7, 8), - (8, 9), - (9, 10) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 3), + (1, 4), + (1, 6), + (2, 4), + (2, 5), + (2, 7), + (3, 4), + (3, 6), + (4, 5), + (4, 6), + (4, 7), + (5, 7), + (6, 7), + (6, 8), + (7, 8), + (8, 9), + (9, 10), + ]) return SimpleGraph(e) end - function moebius_kantor_graph() - e = SimpleEdge.([ - (1, 2), (1, 6), (1, 16), - (2, 3), (2, 13), - (3, 4), (3, 8), - (4, 5), (4, 15), - (5, 6), (5, 10), - (6, 7), - (7, 8), (7, 12), - (8, 9), - (9, 10), (9, 14), - (10, 11), - (11, 12), (11, 16), - (12, 13), - (13, 14), - (14, 15), - (15, 16) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 6), + (1, 16), + (2, 3), + (2, 13), + (3, 4), + (3, 8), + (4, 5), + (4, 15), + (5, 6), + (5, 10), + (6, 7), + (7, 8), + (7, 12), + (8, 9), + (9, 10), + (9, 14), + (10, 11), + (11, 12), + (11, 16), + (12, 13), + (13, 14), + (14, 15), + (15, 16), + ]) return SimpleGraph(e) end - function octahedral_graph() - e = SimpleEdge.([ - (1, 2), (1, 3), (1, 4), (1, 5), - (2, 3), (2, 4), (2, 6), - (3, 5), (3, 6), - (4, 5), (4, 6), - (5, 6) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 3), + (1, 4), + (1, 5), + (2, 3), + (2, 4), + (2, 6), + (3, 5), + (3, 6), + (4, 5), + (4, 6), + (5, 6), + ]) return SimpleGraph(e) end - function pappus_graph() - e = SimpleEdge.([ - (1, 2), (1, 6), (1, 18), - (2, 3), (2, 9), - (3, 4), (3, 14), - (4, 5), (4, 11), - (5, 6), (5, 16), - (6, 7), - (7, 8), (7, 12), - (8, 9), (8, 15), - (9, 10), - (10, 11), (10, 17), - (11, 12), - (12, 13), - (13, 14), (13, 18), - (14, 15), - (15, 16), - (16, 17), - (17, 18) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 6), + (1, 18), + (2, 3), + (2, 9), + (3, 4), + (3, 14), + (4, 5), + (4, 11), + (5, 6), + (5, 16), + (6, 7), + (7, 8), + (7, 12), + (8, 9), + (8, 15), + (9, 10), + (10, 11), + (10, 17), + (11, 12), + (12, 13), + (13, 14), + (13, 18), + (14, 15), + (15, 16), + (16, 17), + (17, 18), + ]) return SimpleGraph(e) end - function petersen_graph() - e = SimpleEdge.([ - (1, 2), (1, 5), (1, 6), - (2, 3), (2, 7), - (3, 4), (3, 8), - (4, 5), (4, 9), - (5, 10), - (6, 8), (6, 9), - (7, 9), (7, 10), - (8, 10) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 5), + (1, 6), + (2, 3), + (2, 7), + (3, 4), + (3, 8), + (4, 5), + (4, 9), + (5, 10), + (6, 8), + (6, 9), + (7, 9), + (7, 10), + (8, 10), + ]) return SimpleGraph(e) end function sedgewick_maze_graph() - e = SimpleEdge.([ - (1, 3), - (1, 6), (1, 8), - (2, 8), - (3, 7), - (4, 5), (4, 6), - (5, 6), (5, 7), (5, 8) - ]) + e = + SimpleEdge.([ + (1, 3), (1, 6), (1, 8), (2, 8), (3, 7), (4, 5), (4, 6), (5, 6), (5, 7), (5, 8) + ]) return SimpleGraph(e) end - -tetrahedral_graph() = -SimpleGraph(SimpleEdge.([(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)])) - +function tetrahedral_graph() + return SimpleGraph(SimpleEdge.([(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)])) +end function truncated_cube_graph() - e = SimpleEdge.([ - (1, 2), (1, 3), (1, 5), - (2, 12), (2, 15), - (3, 4), (3, 5), - (4, 7), (4, 9), - (5, 6), - (6, 17), (6, 19), - (7, 8), (7, 9), - (8, 11), (8, 13), - (9, 10), - (10, 18), (10, 21), - (11, 12), (11, 13), - (12, 15), (13, 14), - (14, 22), (14, 23), - (15, 16), - (16, 20), (16, 24), - (17, 18), (17, 19), - (18, 21), - (19, 20), - (20, 24), - (21, 22), - (22, 23), - (23, 24) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 3), + (1, 5), + (2, 12), + (2, 15), + (3, 4), + (3, 5), + (4, 7), + (4, 9), + (5, 6), + (6, 17), + (6, 19), + (7, 8), + (7, 9), + (8, 11), + (8, 13), + (9, 10), + (10, 18), + (10, 21), + (11, 12), + (11, 13), + (12, 15), + (13, 14), + (14, 22), + (14, 23), + (15, 16), + (16, 20), + (16, 24), + (17, 18), + (17, 19), + (18, 21), + (19, 20), + (20, 24), + (21, 22), + (22, 23), + (23, 24), + ]) return SimpleGraph(e) end - function truncated_tetrahedron_graph() - e = SimpleEdge.([ - (1, 2), (1, 3), (1, 10), - (2, 3), (2, 7), - (3, 4), - (4, 5), (4, 12), - (5, 6), (5, 12), - (6, 7), (6, 8), - (7, 8), - (8, 9), - (9, 10), (9, 11), - (10, 11), - (11, 12) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 3), + (1, 10), + (2, 3), + (2, 7), + (3, 4), + (4, 5), + (4, 12), + (5, 6), + (5, 12), + (6, 7), + (6, 8), + (7, 8), + (8, 9), + (9, 10), + (9, 11), + (10, 11), + (11, 12), + ]) return SimpleGraph(e) end - function truncated_tetrahedron_digraph() - e = SimpleEdge.([ - (1, 2), (1, 3), (1, 10), - (2, 3), (2, 7), - (3, 4), - (4, 5), (4, 12), - (5, 6), (5, 12), - (6, 7), (6, 8), - (7, 8), - (8, 9), - (9, 10), (9, 11), - (10, 11), - (11, 12) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 3), + (1, 10), + (2, 3), + (2, 7), + (3, 4), + (4, 5), + (4, 12), + (5, 6), + (5, 12), + (6, 7), + (6, 8), + (7, 8), + (8, 9), + (9, 10), + (9, 11), + (10, 11), + (11, 12), + ]) return SimpleDiGraph(e) end - function tutte_graph() - e = SimpleEdge.([ - (1, 2), (1, 3), (1, 4), - (2, 5), (2, 27), - (3, 11), (3, 12), - (4, 19), (4, 20), - (5, 6), (5, 34), - (6, 7), (6, 30), - (7, 8), (7, 28), - (8, 9), (8, 15), - (9, 10), (9, 39), - (10, 11), (10, 38), - (11, 40), - (12, 13), (12, 40), - (13, 14), (13, 36), - (14, 15), (14, 16), - (15, 35), - (16, 17), (16, 23), - (17, 18), (17, 45), - (18, 19), (18, 44), - (19, 46), - (20, 21), (20, 46), - (21, 22), (21, 42), - (22, 23), (22, 24), - (23, 41), - (24, 25), (24, 28), - (25, 26), (25, 33), - (26, 27), (26, 32), - (27, 34), - (28, 29), - (29, 30), (29, 33), - (30, 31), - (31, 32), (31, 34), - (32, 33), - (35, 36), (35, 39), - (36, 37), - (37, 38), (37, 40), - (38, 39), - (41, 42), (41, 45), - (42, 43), - (43, 44), (43, 46), - (44, 45) - ]) + e = + SimpleEdge.([ + (1, 2), + (1, 3), + (1, 4), + (2, 5), + (2, 27), + (3, 11), + (3, 12), + (4, 19), + (4, 20), + (5, 6), + (5, 34), + (6, 7), + (6, 30), + (7, 8), + (7, 28), + (8, 9), + (8, 15), + (9, 10), + (9, 39), + (10, 11), + (10, 38), + (11, 40), + (12, 13), + (12, 40), + (13, 14), + (13, 36), + (14, 15), + (14, 16), + (15, 35), + (16, 17), + (16, 23), + (17, 18), + (17, 45), + (18, 19), + (18, 44), + (19, 46), + (20, 21), + (20, 46), + (21, 22), + (21, 42), + (22, 23), + (22, 24), + (23, 41), + (24, 25), + (24, 28), + (25, 26), + (25, 33), + (26, 27), + (26, 32), + (27, 34), + (28, 29), + (29, 30), + (29, 33), + (30, 31), + (31, 32), + (31, 34), + (32, 33), + (35, 36), + (35, 39), + (36, 37), + (37, 38), + (37, 40), + (38, 39), + (41, 42), + (41, 45), + (42, 43), + (43, 44), + (43, 46), + (44, 45), + ]) return SimpleGraph(e) end diff --git a/src/SimpleGraphs/generators/staticgraphs.jl b/src/SimpleGraphs/generators/staticgraphs.jl index 4b2b4c906..64e004877 100644 --- a/src/SimpleGraphs/generators/staticgraphs.jl +++ b/src/SimpleGraphs/generators/staticgraphs.jl @@ -16,20 +16,19 @@ julia> complete_graph(Int8(6)) {6, 15} undirected simple Int8 graph ``` """ -function complete_graph(n::T) where {T <: Integer} +function complete_graph(n::T) where {T<:Integer} n <= 0 && return SimpleGraph{T}(0) ne = Int(n * (n - 1) ÷ 2) fadjlist = Vector{Vector{T}}(undef, n) - @inbounds @simd for u = 1:n - listu = Vector{T}(undef, n-1) - listu[1:(u-1)] = 1:(u - 1) - listu[u:(n-1)] = (u + 1):n + @inbounds @simd for u in 1:n + listu = Vector{T}(undef, n - 1) + listu[1:(u - 1)] = 1:(u - 1) + listu[u:(n - 1)] = (u + 1):n fadjlist[u] = listu end return SimpleGraph(ne, fadjlist) end - """ complete_bipartite_graph(n1, n2) @@ -45,7 +44,7 @@ julia> complete_bipartite_graph(Int8(3), Int8(4)) {7, 12} undirected simple Int8 graph ``` """ -function complete_bipartite_graph(n1::T, n2::T) where {T <: Integer} +function complete_bipartite_graph(n1::T, n2::T) where {T<:Integer} (n1 < 0 || n2 < 0) && return SimpleGraph{T}(0) Tw = widen(T) nw = Tw(n1) + Tw(n2) @@ -84,7 +83,7 @@ julia> complete_multipartite_graph(Int8[5,5,5]) {15, 75} undirected simple Int8 graph ``` """ -function complete_multipartite_graph(partitions::AbstractVector{T}) where {T <: Integer} +function complete_multipartite_graph(partitions::AbstractVector{T}) where {T<:Integer} any(x -> x < 0, partitions) && return SimpleGraph{T}(0) length(partitions) == 1 && return SimpleGraph{T}(partitions[1]) length(partitions) == 2 && return complete_bipartite_graph(partitions[1], partitions[2]) @@ -93,20 +92,20 @@ function complete_multipartite_graph(partitions::AbstractVector{T}) where {T <: ne = 0 for p in partitions # type stability fails if we use sum and a generator here - ne += p*(Int(n)-p) # overflow if we don't convert to Int + ne += p * (Int(n) - p) # overflow if we don't convert to Int end ne = div(ne, 2) fadjlist = Vector{Vector{T}}(undef, n) cur = 1 for p in partitions - currange = cur:(cur+p-1) # all vertices in the current partition - lowerrange = 1:(cur-1) # all vertices lower than the current partition - upperrange = (cur+p):n # all vertices higher than the current partition + currange = cur:(cur + p - 1) # all vertices in the current partition + lowerrange = 1:(cur - 1) # all vertices lower than the current partition + upperrange = (cur + p):n # all vertices higher than the current partition @inbounds @simd for u in currange fadjlist[u] = Vector{T}(undef, length(lowerrange) + length(upperrange)) fadjlist[u][1:length(lowerrange)] = lowerrange - fadjlist[u][(length(lowerrange)+1):end] = upperrange + fadjlist[u][(length(lowerrange) + 1):end] = upperrange end cur += p end @@ -130,15 +129,16 @@ julia> turan_graph(Int8(7), 2) ``` """ function turan_graph(n::Integer, r::Integer) - !(1 <= r <= n) && throw(DomainError("n=$n and r=$r are invalid, must satisfy 1 <= r <= n")) + !(1 <= r <= n) && + throw(DomainError("n=$n and r=$r are invalid, must satisfy 1 <= r <= n")) T = typeof(n) partitions = Vector{T}(undef, r) - c = cld(n,r) - f = fld(n,r) - @inbounds @simd for i in 1:(n%r) + c = cld(n, r) + f = fld(n, r) + @inbounds @simd for i in 1:(n % r) partitions[i] = c end - @inbounds @simd for i in ((n%r)+1):r + @inbounds @simd for i in ((n % r) + 1):r partitions[i] = f end return complete_multipartite_graph(partitions) @@ -159,16 +159,16 @@ julia> complete_digraph(Int8(6)) {6, 30} directed simple Int8 graph ``` """ -function complete_digraph(n::T) where {T <: Integer} +function complete_digraph(n::T) where {T<:Integer} n <= 0 && return SimpleDiGraph{T}(0) ne = Int(n * (n - 1)) fadjlist = Vector{Vector{T}}(undef, n) badjlist = Vector{Vector{T}}(undef, n) - @inbounds @simd for u = 1:n - listu = Vector{T}(undef, n-1) - listu[1:(u-1)] = 1:(u - 1) - listu[u:(n-1)] = (u + 1):n + @inbounds @simd for u in 1:n + listu = Vector{T}(undef, n - 1) + listu[1:(u - 1)] = 1:(u - 1) + listu[u:(n - 1)] = (u + 1):n fadjlist[u] = listu badjlist[u] = deepcopy(listu) end @@ -190,13 +190,13 @@ julia> star_graph(Int8(10)) {10, 9} undirected simple Int8 graph ``` """ -function star_graph(n::T) where {T <: Integer} +function star_graph(n::T) where {T<:Integer} n <= 0 && return SimpleGraph{T}(0) ne = Int(n - 1) fadjlist = Vector{Vector{T}}(undef, n) @inbounds fadjlist[1] = Vector{T}(2:n) - @inbounds @simd for u = 2:n + @inbounds @simd for u in 2:n fadjlist[u] = T[1] end return SimpleGraph(ne, fadjlist) @@ -217,7 +217,7 @@ julia> star_digraph(Int8(10)) {10, 9} directed simple Int8 graph ``` """ -function star_digraph(n::T) where {T <: Integer} +function star_digraph(n::T) where {T<:Integer} n <= 0 && return SimpleDiGraph{T}(0) ne = Int(n - 1) @@ -225,7 +225,7 @@ function star_digraph(n::T) where {T <: Integer} badjlist = Vector{Vector{T}}(undef, n) @inbounds fadjlist[1] = Vector{T}(2:n) @inbounds badjlist[1] = T[] - @inbounds @simd for u = 2:n + @inbounds @simd for u in 2:n fadjlist[u] = T[] badjlist[u] = T[1] end @@ -247,7 +247,7 @@ julia> path_graph(Int8(10)) {10, 9} undirected simple Int8 graph ``` """ -function path_graph(n::T) where {T <: Integer} +function path_graph(n::T) where {T<:Integer} n <= 1 && return SimpleGraph(n) ne = Int(n - 1) @@ -255,7 +255,7 @@ function path_graph(n::T) where {T <: Integer} @inbounds fadjlist[1] = T[2] @inbounds fadjlist[n] = T[n - 1] - @inbounds @simd for u = 2:(n-1) + @inbounds @simd for u in 2:(n - 1) fadjlist[u] = T[u - 1, u + 1] end return SimpleGraph(ne, fadjlist) @@ -276,7 +276,7 @@ julia> path_digraph(Int8(10)) {10, 9} directed simple Int8 graph ``` """ -function path_digraph(n::T) where {T <: Integer} +function path_digraph(n::T) where {T<:Integer} n <= 1 && return SimpleDiGraph(n) ne = Int(n - 1) @@ -288,7 +288,7 @@ function path_digraph(n::T) where {T <: Integer} @inbounds fadjlist[n] = T[] @inbounds badjlist[n] = T[n - 1] - @inbounds @simd for u = 2:(n-1) + @inbounds @simd for u in 2:(n - 1) fadjlist[u] = T[u + 1] badjlist[u] = T[u - 1] end @@ -310,17 +310,17 @@ julia> cycle_graph(Int8(5)) {5, 5} undirected simple Int8 graph ``` """ -function cycle_graph(n::T) where {T <: Integer} +function cycle_graph(n::T) where {T<:Integer} n <= 1 && return SimpleGraph(n) n == 2 && return SimpleGraph(Edge{T}.([(1, 2)])) ne = Int(n) fadjlist = Vector{Vector{T}}(undef, n) @inbounds fadjlist[1] = T[2, n] - @inbounds fadjlist[n] = T[1, n-1] + @inbounds fadjlist[n] = T[1, n - 1] - @inbounds @simd for u = 2:(n-1) - fadjlist[u] = T[u-1, u+1] + @inbounds @simd for u in 2:(n - 1) + fadjlist[u] = T[u - 1, u + 1] end return SimpleGraph(ne, fadjlist) end @@ -340,7 +340,7 @@ julia> cycle_digraph(Int8(5)) {5, 5} directed simple Int8 graph ``` """ -function cycle_digraph(n::T) where {T <: Integer} +function cycle_digraph(n::T) where {T<:Integer} n <= 1 && return SimpleDiGraph(n) n == 2 && return SimpleDiGraph(Edge{T}.([(1, 2), (2, 1)])) @@ -350,16 +350,15 @@ function cycle_digraph(n::T) where {T <: Integer} @inbounds fadjlist[1] = T[2] @inbounds badjlist[1] = T[n] @inbounds fadjlist[n] = T[1] - @inbounds badjlist[n] = T[n-1] + @inbounds badjlist[n] = T[n - 1] - @inbounds @simd for u = 2:(n-1) + @inbounds @simd for u in 2:(n - 1) fadjlist[u] = T[u + 1] badjlist[u] = T[u + -1] end return SimpleDiGraph(ne, fadjlist, badjlist) end - """ wheel_graph(n) @@ -375,7 +374,7 @@ julia> wheel_graph(Int8(6)) {6, 10} undirected simple Int8 graph ``` """ -function wheel_graph(n::T) where {T <: Integer} +function wheel_graph(n::T) where {T<:Integer} n <= 1 && return SimpleGraph(n) n <= 3 && return cycle_graph(n) @@ -385,7 +384,7 @@ function wheel_graph(n::T) where {T <: Integer} @inbounds fadjlist[2] = T[1, 3, n] @inbounds fadjlist[n] = T[1, 2, n - 1] - @inbounds @simd for u = 3:(n-1) + @inbounds @simd for u in 3:(n - 1) fadjlist[u] = T[1, u - 1, u + 1] end return SimpleGraph(ne, fadjlist) @@ -406,9 +405,9 @@ julia> wheel_digraph(Int8(6)) {6, 10} directed simple Int8 graph ``` """ -function wheel_digraph(n::T) where {T <: Integer} +function wheel_digraph(n::T) where {T<:Integer} n <= 2 && return path_digraph(n) - n == 3 && return SimpleDiGraph(Edge{T}.([(1,2),(1,3),(2,3),(3,2)])) + n == 3 && return SimpleDiGraph(Edge{T}.([(1, 2), (1, 3), (2, 3), (3, 2)])) ne = Int(2 * (n - 1)) fadjlist = Vector{Vector{T}}(undef, n) @@ -420,7 +419,7 @@ function wheel_digraph(n::T) where {T <: Integer} @inbounds fadjlist[n] = T[2] @inbounds badjlist[n] = T[1, n - 1] - @inbounds @simd for u = 3:(n-1) + @inbounds @simd for u in 3:(n - 1) fadjlist[u] = T[u + 1] badjlist[u] = T[1, u - 1] end @@ -449,7 +448,7 @@ julia> grid((2,3)) {6, 7} undirected simple Int64 graph ``` """ -function grid(dims::AbstractVector{T}; periodic=false) where {T <: Integer} +function grid(dims::AbstractVector{T}; periodic=false) where {T<:Integer} # checks if T is large enough for product(dims) Tw = widen(T) n = one(T) @@ -489,10 +488,10 @@ julia> binary_tree(Int8(5)) {31, 30} undirected simple Int8 graph ``` """ -function binary_tree(k::T) where {T <: Integer} +function binary_tree(k::T) where {T<:Integer} k <= 0 && return SimpleGraph(0) k == 1 && return SimpleGraph(1) - if Graphs.isbounded(k) && BigInt(2) ^ k - 1 > typemax(k) + if Graphs.isbounded(k) && BigInt(2)^k - 1 > typemax(k) throw(DomainError(k, "2^k - 1 not representable by type $T")) end n = T(2^k - 1) @@ -537,7 +536,6 @@ function double_binary_tree(k::Integer) return g end - """ roach_graph(k) @@ -563,7 +561,6 @@ function roach_graph(k::Integer) return roach end - """ clique_graph(k, n) @@ -578,18 +575,18 @@ julia> clique_graph(Int8(10), Int8(4)) {40, 184} undirected simple Int8 graph ``` """ -function clique_graph(k::T, n::T) where {T <: Integer} +function clique_graph(k::T, n::T) where {T<:Integer} Tw = widen(T) knw = Tw(k) * Tw(n) kn = T(knw) # checks if T is large enough for k * n g = SimpleGraph(kn) - for c = 1:n - for i = ((c - 1) * k + 1):(c * k - 1), j = (i + 1):(c * k) + for c in 1:n + for i in ((c - 1) * k + 1):(c * k - 1), j in (i + 1):(c * k) add_edge!(g, i, j) end end - for i = 1:(n - 1) + for i in 1:(n - 1) add_edge!(g, (i - 1) * k + 1, i * k + 1) end add_edge!(g, 1, (n - 1) * k + 1) @@ -614,23 +611,23 @@ julia> ladder_graph(Int8(4)) {8, 10} undirected simple Int8 graph ``` """ -function ladder_graph(n::T) where {T <: Integer} +function ladder_graph(n::T) where {T<:Integer} n <= 0 && return SimpleGraph{T}(0) n == 1 && return path_graph(T(2)) Tw = widen(T) - temp = T(Tw(n)+Tw(n)) # test to check if T is large enough + temp = T(Tw(n) + Tw(n)) # test to check if T is large enough - fadjlist = Vector{Vector{T}}(undef, 2*n) - @inbounds @simd for i in 2:(n-1) - fadjlist[i] = T[i-1, i+1, i+n] - fadjlist[n+i] = T[i, n+i-1, n+i+1] + fadjlist = Vector{Vector{T}}(undef, 2 * n) + @inbounds @simd for i in 2:(n - 1) + fadjlist[i] = T[i - 1, i + 1, i + n] + fadjlist[n + i] = T[i, n + i - 1, n + i + 1] end - fadjlist[1] = T[2, n+1] - fadjlist[n+1] = T[1, n+2] - fadjlist[n] = T[n-1, 2*n] - fadjlist[2*n] = T[n, 2*n-1] + fadjlist[1] = T[2, n + 1] + fadjlist[n + 1] = T[1, n + 2] + fadjlist[n] = T[n - 1, 2 * n] + fadjlist[2 * n] = T[n, 2 * n - 1] - return SimpleGraph(3*n-2, fadjlist) + return SimpleGraph(3 * n - 2, fadjlist) end """ @@ -657,7 +654,7 @@ function circular_ladder_graph(n::Integer) n < 3 && throw(DomainError("n=$n must be at least 3")) g = ladder_graph(n) add_edge!(g, 1, n) - add_edge!(g, n+1, 2*n) + add_edge!(g, n + 1, 2 * n) return g end @@ -681,30 +678,30 @@ julia> barbell_graph(Int8(5), Int8(5)) {10, 21} undirected simple Int8 graph ``` """ -function barbell_graph(n1::T, n2::T) where {T <: Integer} +function barbell_graph(n1::T, n2::T) where {T<:Integer} (n1 < 1 || n2 < 1) && throw(DomainError("n1=$n1 and n2=$n2 must be at least 1")) n = Base.Checked.checked_add(n1, n2) # check for overflow fadjlist = Vector{Vector{T}}(undef, n) - ne = Int(n1)*(n1-1)÷2 + Int(n2)*(n2-1)÷2 + ne = Int(n1) * (n1 - 1) ÷ 2 + Int(n2) * (n2 - 1) ÷ 2 - @inbounds @simd for u = 1:n1 - listu = Vector{T}(undef, n1-1) - listu[1:(u-1)] = 1:(u-1) - listu[u:(n1-1)] = (u+1):n1 + @inbounds @simd for u in 1:n1 + listu = Vector{T}(undef, n1 - 1) + listu[1:(u - 1)] = 1:(u - 1) + listu[u:(n1 - 1)] = (u + 1):n1 fadjlist[u] = listu end - @inbounds for u = 1:n2 - listu = Vector{T}(undef, n2-1) - listu[1:(u-1)] = (n1+1):(n1+(u-1)) - listu[u:(n2-1)] = (n1+u+1):(n1+n2) - fadjlist[n1+u] = listu + @inbounds for u in 1:n2 + listu = Vector{T}(undef, n2 - 1) + listu[1:(u - 1)] = (n1 + 1):(n1 + (u - 1)) + listu[u:(n2 - 1)] = (n1 + u + 1):(n1 + n2) + fadjlist[n1 + u] = listu end g = SimpleGraph(ne, fadjlist) - add_edge!(g, n1, n1+1) + add_edge!(g, n1, n1 + 1) return g end @@ -728,38 +725,38 @@ julia> lollipop_graph(Int8(3), Int8(4)) {7, 7} undirected simple Int8 graph ``` """ -function lollipop_graph(n1::T, n2::T) where {T <: Integer} +function lollipop_graph(n1::T, n2::T) where {T<:Integer} (n1 < 1 || n2 < 1) && throw(DomainError("n1=$n1 and n2=$n2 must be at least 1")) if n1 == 1 - return path_graph(T(n2+1)) + return path_graph(T(n2 + 1)) elseif n1 > 1 && n2 == 1 g = complete_graph(n1) add_vertex!(g) - add_edge!(g, n1, n1+1) + add_edge!(g, n1, n1 + 1) return g end n = Base.Checked.checked_add(n1, n2) # check for overflow fadjlist = Vector{Vector{T}}(undef, n) - ne = Int(Int(n1)*(n1-1)÷2 + n2-1) + ne = Int(Int(n1) * (n1 - 1) ÷ 2 + n2 - 1) - @inbounds @simd for u = 1:n1 - listu = Vector{T}(undef, n1-1) - listu[1:(u-1)] = 1:(u-1) - listu[u:(n1-1)] = (u+1):n1 + @inbounds @simd for u in 1:n1 + listu = Vector{T}(undef, n1 - 1) + listu[1:(u - 1)] = 1:(u - 1) + listu[u:(n1 - 1)] = (u + 1):n1 fadjlist[u] = listu end - @inbounds fadjlist[n1+1] = T[n1+2] - @inbounds fadjlist[n1+n2] = T[n1+n2-1] + @inbounds fadjlist[n1 + 1] = T[n1 + 2] + @inbounds fadjlist[n1 + n2] = T[n1 + n2 - 1] - @inbounds @simd for u = (n1+2):(n1+n2-1) - fadjlist[u] = T[u-1, u+1] + @inbounds @simd for u in (n1 + 2):(n1 + n2 - 1) + fadjlist[u] = T[u - 1, u + 1] end g = SimpleGraph(ne, fadjlist) - add_edge!(g, n1, n1+1) + add_edge!(g, n1, n1 + 1) return g end diff --git a/src/SimpleGraphs/simpledigraph.jl b/src/SimpleGraphs/simpledigraph.jl index eec1ae701..4f57acf40 100644 --- a/src/SimpleGraphs/simpledigraph.jl +++ b/src/SimpleGraphs/simpledigraph.jl @@ -5,33 +5,26 @@ const SimpleDiGraphEdge = SimpleEdge A type representing a directed graph. """ -mutable struct SimpleDiGraph{T <: Integer} <: AbstractSimpleGraph{T} +mutable struct SimpleDiGraph{T<:Integer} <: AbstractSimpleGraph{T} ne::Int fadjlist::Vector{Vector{T}} # [src]: (dst, dst, dst) badjlist::Vector{Vector{T}} # [dst]: (src, src, src) function SimpleDiGraph{T}( - ne::Int, - fadjlist::Vector{Vector{T}}, - badjlist::Vector{Vector{T}} - ) where T - + ne::Int, fadjlist::Vector{Vector{T}}, badjlist::Vector{Vector{T}} + ) where {T} throw_if_invalid_eltype(T) return new(ne, fadjlist, badjlist) end end function SimpleDiGraph( - ne::Int, - fadjlist::Vector{Vector{T}}, - badjlist::Vector{Vector{T}} -) where T - + ne::Int, fadjlist::Vector{Vector{T}}, badjlist::Vector{Vector{T}} +) where {T} return SimpleDiGraph{T}(ne, fadjlist, badjlist) end - -eltype(x::SimpleDiGraph{T}) where T = T +eltype(x::SimpleDiGraph{T}) where {T} = T # DiGraph{UInt8}(6), DiGraph{Int16}(7), DiGraph{Int8}() """ @@ -46,14 +39,14 @@ julia> SimpleDiGraph(UInt8(10)) {10, 0} directed simple UInt8 graph ``` """ -function SimpleDiGraph{T}(n::Integer=0) where T <: Integer - fadjlist = [Vector{T}() for _ = one(T):n] - badjlist = [Vector{T}() for _ = one(T):n] +function SimpleDiGraph{T}(n::Integer=0) where {T<:Integer} + fadjlist = [Vector{T}() for _ in one(T):n] + badjlist = [Vector{T}() for _ in one(T):n] return SimpleDiGraph(0, fadjlist, badjlist) end # SimpleDiGraph(6), SimpleDiGraph(0x5) -SimpleDiGraph(n::T) where T <: Integer = SimpleDiGraph{T}(n) +SimpleDiGraph(n::T) where {T<:Integer} = SimpleDiGraph{T}(n) # SimpleDiGraph() SimpleDiGraph() = SimpleDiGraph{Int}() @@ -70,8 +63,7 @@ julia> SimpleDiGraph(UInt8) {0, 0} directed simple UInt8 graph ``` """ -SimpleDiGraph(::Type{T}) where T <: Integer = SimpleDiGraph{T}(zero(T)) - +SimpleDiGraph(::Type{T}) where {T<:Integer} = SimpleDiGraph{T}(zero(T)) # SimpleDiGraph(adjmx) """ @@ -95,14 +87,15 @@ julia> SimpleDiGraph{Int16}(A2) SimpleDiGraph(adjmx::AbstractMatrix) = SimpleDiGraph{Int}(adjmx) # sparse adjacency matrix constructor: SimpleDiGraph(adjmx) -function SimpleDiGraph{T}(adjmx::SparseMatrixCSC{U}) where T <: Integer where U <: Real +function SimpleDiGraph{T}(adjmx::SparseMatrixCSC{U}) where {T<:Integer} where {U<:Real} dima, dimb = size(adjmx) - isequal(dima, dimb) || throw(ArgumentError("Adjacency / distance matrices must be square")) + isequal(dima, dimb) || + throw(ArgumentError("Adjacency / distance matrices must be square")) g = SimpleDiGraph(T(dima)) maxc = length(adjmx.colptr) - @inbounds for c = 1:(maxc - 1) - for rind = adjmx.colptr[c]:(adjmx.colptr[c + 1] - 1) + @inbounds for c in 1:(maxc - 1) + for rind in adjmx.colptr[c]:(adjmx.colptr[c + 1] - 1) isnz = (adjmx.nzval[rind] != zero(U)) if isnz r = adjmx.rowval[rind] @@ -114,13 +107,14 @@ function SimpleDiGraph{T}(adjmx::SparseMatrixCSC{U}) where T <: Integer where U end # dense adjacency matrix constructor: DiGraph{UInt8}(adjmx) -function SimpleDiGraph{T}(adjmx::AbstractMatrix{U}) where T <: Integer where U <: Real +function SimpleDiGraph{T}(adjmx::AbstractMatrix{U}) where {T<:Integer} where {U<:Real} dima, dimb = size(adjmx) - isequal(dima, dimb) || throw(ArgumentError("Adjacency / distance matrices must be square")) + isequal(dima, dimb) || + throw(ArgumentError("Adjacency / distance matrices must be square")) g = SimpleDiGraph(T(dima)) @inbounds for i in findall(adjmx .!= zero(U)) - add_edge!(g, i[1], i[2]) + add_edge!(g, i[1], i[2]) end return g end @@ -140,7 +134,7 @@ julia> SimpleDiGraph{UInt8}(g) {5, 20} directed simple UInt8 graph ``` """ -function SimpleDiGraph{T}(g::SimpleDiGraph) where T <: Integer +function SimpleDiGraph{T}(g::SimpleDiGraph) where {T<:Integer} h_fadj = [Vector{T}(x) for x in fadj(g)] h_badj = [Vector{T}(x) for x in badj(g)] return SimpleDiGraph(ne(g), h_fadj, h_badj) @@ -170,9 +164,9 @@ function SimpleDiGraph(g::AbstractSimpleGraph) return h end - -@inbounds function cleanupedges!(fadjlist::Vector{Vector{T}}, - badjlist::Vector{Vector{T}}) where T <: Integer +@inbounds function cleanupedges!( + fadjlist::Vector{Vector{T}}, badjlist::Vector{Vector{T}} +) where {T<:Integer} neg = 0 for v in 1:length(fadjlist) if !issorted(fadjlist[v]) @@ -210,42 +204,46 @@ julia> SimpleDiGraph(el) {5, 3} directed simple Int64 graph ``` """ -function SimpleDiGraph(edge_list::Vector{SimpleDiGraphEdge{T}}) where T <: Integer +function SimpleDiGraph(edge_list::Vector{SimpleDiGraphEdge{T}}) where {T<:Integer} nvg = zero(T) @inbounds( - for e in edge_list - nvg = max(nvg, src(e), dst(e)) - end) - + for e in edge_list + nvg = max(nvg, src(e), dst(e)) + end + ) + list_sizes_out = ones(Int, nvg) list_sizes_in = ones(Int, nvg) degs_out = zeros(Int, nvg) degs_in = zeros(Int, nvg) @inbounds( - for e in edge_list - s, d = src(e), dst(e) - (s >= 1 && d >= 1) || continue - degs_out[s] += 1 - degs_in[d] += 1 - end) - + for e in edge_list + s, d = src(e), dst(e) + (s >= 1 && d >= 1) || continue + degs_out[s] += 1 + degs_in[d] += 1 + end + ) + fadjlist = Vector{Vector{T}}(undef, nvg) badjlist = Vector{Vector{T}}(undef, nvg) @inbounds( - for v in 1:nvg - fadjlist[v] = Vector{T}(undef, degs_out[v]) - badjlist[v] = Vector{T}(undef, degs_in[v]) - end) + for v in 1:nvg + fadjlist[v] = Vector{T}(undef, degs_out[v]) + badjlist[v] = Vector{T}(undef, degs_in[v]) + end + ) @inbounds( - for e in edge_list - s, d = src(e), dst(e) - (s >= 1 && d >= 1) || continue - fadjlist[s][list_sizes_out[s]] = d - list_sizes_out[s] += 1 - badjlist[d][list_sizes_in[d]] = s - list_sizes_in[d] += 1 - end) + for e in edge_list + s, d = src(e), dst(e) + (s >= 1 && d >= 1) || continue + fadjlist[s][list_sizes_out[s]] = d + list_sizes_out[s] += 1 + badjlist[d][list_sizes_in[d]] = s + list_sizes_in[d] += 1 + end + ) neg = cleanupedges!(fadjlist, badjlist) g = SimpleDiGraph{T}() @@ -256,23 +254,22 @@ function SimpleDiGraph(edge_list::Vector{SimpleDiGraphEdge{T}}) where T <: Integ return g end - -@inbounds function add_to_lists!(fadjlist::Vector{Vector{T}}, - badjlist::Vector{Vector{T}}, s::T, d::T) where T <: Integer +@inbounds function add_to_lists!( + fadjlist::Vector{Vector{T}}, badjlist::Vector{Vector{T}}, s::T, d::T +) where {T<:Integer} nvg = length(fadjlist) nvg_new = max(nvg, s, d) - for v = (nvg + 1):nvg_new + for v in (nvg + 1):nvg_new push!(fadjlist, Vector{T}()) push!(badjlist, Vector{T}()) end push!(fadjlist[s], d) - push!(badjlist[d], s) + return push!(badjlist[d], s) end # Try to get the eltype from the first element function _SimpleDiGraphFromIterator(iter)::SimpleDiGraph - next = iterate(iter) if (next === nothing) return SimpleDiGraph(0) @@ -280,14 +277,14 @@ function _SimpleDiGraphFromIterator(iter)::SimpleDiGraph e = first(next) E = typeof(e) - if !(E <: SimpleGraphEdge{<: Integer}) + if !(E <: SimpleGraphEdge{<:Integer}) throw(DomainError(iter, "Edges must be of type SimpleEdge{T <: Integer}")) end T = eltype(e) g = SimpleDiGraph{T}() - fadjlist = Vector{Vector{T}}() - badjlist = Vector{Vector{T}}() + fadjlist = Vector{Vector{T}}() + badjlist = Vector{Vector{T}}() while next != nothing (e, state) = next @@ -303,7 +300,7 @@ function _SimpleDiGraphFromIterator(iter)::SimpleDiGraph next = iterate(iter, state) end - neg = cleanupedges!(fadjlist, badjlist) + neg = cleanupedges!(fadjlist, badjlist) g.fadjlist = fadjlist g.badjlist = badjlist g.ne = neg @@ -311,21 +308,20 @@ function _SimpleDiGraphFromIterator(iter)::SimpleDiGraph return g end -function _SimpleDiGraphFromIterator(iter, ::Type{T}) where {T <: Integer} - +function _SimpleDiGraphFromIterator(iter, ::Type{T}) where {T<:Integer} g = SimpleDiGraph{T}() - fadjlist = Vector{Vector{T}}() - badjlist = Vector{Vector{T}}() + fadjlist = Vector{Vector{T}}() + badjlist = Vector{Vector{T}}() @inbounds( - for e in iter - s, d = src(e), dst(e) - (s >= 1 && d >= 1) || continue - add_to_lists!(fadjlist, badjlist, s, d) - - end) + for e in iter + s, d = src(e), dst(e) + (s >= 1 && d >= 1) || continue + add_to_lists!(fadjlist, badjlist, s, d) + end + ) - neg = cleanupedges!(fadjlist, badjlist) + neg = cleanupedges!(fadjlist, badjlist) g.fadjlist = fadjlist g.badjlist = badjlist g.ne = neg @@ -359,10 +355,9 @@ julia> collect(edges(h)) ``` """ function SimpleDiGraphFromIterator(iter)::SimpleDiGraph - if Base.IteratorEltype(iter) == Base.HasEltype() E = eltype(iter) - if (E <: SimpleGraphEdge{<: Integer} && isconcretetype(E)) + if (E <: SimpleGraphEdge{<:Integer} && isconcretetype(E)) T = eltype(E) if isconcretetype(T) return _SimpleDiGraphFromIterator(iter, T) @@ -373,28 +368,27 @@ function SimpleDiGraphFromIterator(iter)::SimpleDiGraph return _SimpleDiGraphFromIterator(iter) end - - -edgetype(::SimpleDiGraph{T}) where T <: Integer = SimpleGraphEdge{T} - +edgetype(::SimpleDiGraph{T}) where {T<:Integer} = SimpleGraphEdge{T} badj(g::SimpleDiGraph) = g.badjlist badj(g::SimpleDiGraph, v::Integer) = badj(g)[v] +function copy(g::SimpleDiGraph{T}) where {T<:Integer} + return SimpleDiGraph{T}( + g.ne, deepcopy_adjlist(g.fadjlist), deepcopy_adjlist(g.badjlist) + ) +end -copy(g::SimpleDiGraph{T}) where T <: Integer = -SimpleDiGraph{T}(g.ne, deepcopy_adjlist(g.fadjlist), deepcopy_adjlist(g.badjlist)) - - -==(g::SimpleDiGraph, h::SimpleDiGraph) = -vertices(g) == vertices(h) && -ne(g) == ne(h) && -fadj(g) == fadj(h) && -badj(g) == badj(h) +function ==(g::SimpleDiGraph, h::SimpleDiGraph) + return vertices(g) == vertices(h) && + ne(g) == ne(h) && + fadj(g) == fadj(h) && + badj(g) == badj(h) +end is_directed(::Type{<:SimpleDiGraph}) = true -function has_edge(g::SimpleDiGraph{T}, s, d) where T +function has_edge(g::SimpleDiGraph{T}, s, d) where {T} verts = vertices(g) (s in verts && d in verts) || return false # edge out of bounds @inbounds list = g.fadjlist[s] @@ -406,12 +400,12 @@ function has_edge(g::SimpleDiGraph{T}, s, d) where T return insorted(d, list) end -function has_edge(g::SimpleDiGraph{T}, e::SimpleDiGraphEdge{T}) where T +function has_edge(g::SimpleDiGraph{T}, e::SimpleDiGraphEdge{T}) where {T} s, d = T.(Tuple(e)) return has_edge(g, s, d) end -function add_edge!(g::SimpleDiGraph{T}, e::SimpleDiGraphEdge{T}) where T +function add_edge!(g::SimpleDiGraph{T}, e::SimpleDiGraphEdge{T}) where {T} s, d = T.(Tuple(e)) verts = vertices(g) (s in verts && d in verts) || return false # edge out of bounds @@ -428,25 +422,24 @@ function add_edge!(g::SimpleDiGraph{T}, e::SimpleDiGraphEdge{T}) where T return true # edge successfully added end - -function rem_edge!(g::SimpleDiGraph{T}, e::SimpleDiGraphEdge{T}) where T +function rem_edge!(g::SimpleDiGraph{T}, e::SimpleDiGraphEdge{T}) where {T} s, d = T.(Tuple(e)) verts = vertices(g) (s in verts && d in verts) || return false # edge out of bounds - @inbounds list = g.fadjlist[s] + @inbounds list = g.fadjlist[s] index = searchsortedfirst(list, d) @inbounds (index <= length(list) && list[index] == d) || return false # edge not in graph deleteat!(list, index) g.ne -= 1 - @inbounds list = g.badjlist[d] + @inbounds list = g.badjlist[d] index = searchsortedfirst(list, s) deleteat!(list, index) return true # edge successfully removed end -function add_vertex!(g::SimpleDiGraph{T}) where T +function add_vertex!(g::SimpleDiGraph{T}) where {T} (nv(g) + one(T) <= nv(g)) && return false # test for overflow push!(g.badjlist, Vector{T}()) push!(g.fadjlist, Vector{T}()) @@ -454,10 +447,9 @@ function add_vertex!(g::SimpleDiGraph{T}) where T return true end -function rem_vertices!(g::SimpleDiGraph{T}, - vs::AbstractVector{<: Integer}; - keep_order::Bool=false - ) where {T <: Integer} +function rem_vertices!( + g::SimpleDiGraph{T}, vs::AbstractVector{<:Integer}; keep_order::Bool=false +) where {T<:Integer} # check the implementation in simplegraph.jl for more comments n = nv(g) @@ -467,7 +459,7 @@ function rem_vertices!(g::SimpleDiGraph{T}, remove = sort(vs) unique!(remove) (1 <= remove[1] && remove[end] <= n) || - throw(ArgumentError("Vertices to be removed must be in the range 1:nv(g).")) + throw(ArgumentError("Vertices to be removed must be in the range 1:nv(g).")) # Create a vmap that maps vertices to their new position # vertices that get removed are mapped to 0 @@ -493,9 +485,9 @@ function rem_vertices!(g::SimpleDiGraph{T}, u > v && break if i <= length(remove) && u == remove[i] while v == remove[j] && v > u - vmap[v] = 0 - v -= one(T) - j -= 1 + vmap[v] = 0 + v -= one(T) + j -= 1 end # v > remove[j] || u == v vmap[v] = u @@ -567,7 +559,7 @@ function rem_vertices!(g::SimpleDiGraph{T}, return reverse_vmap end -function all_neighbors(g::SimpleDiGraph{T}, u::Integer) where T +function all_neighbors(g::SimpleDiGraph{T}, u::Integer) where {T} i, j = 1, 1 in_nbrs, out_nbrs = inneighbors(g, u), outneighbors(g, u) in_len, out_len = length(in_nbrs), length(out_nbrs) @@ -580,7 +572,7 @@ function all_neighbors(g::SimpleDiGraph{T}, u::Integer) where T elseif in_nbrs[i] > out_nbrs[j] union_nbrs[indx] = out_nbrs[j] j += 1 - else + else union_nbrs[indx] = out_nbrs[j] i += 1 j += 1 @@ -597,6 +589,6 @@ function all_neighbors(g::SimpleDiGraph{T}, u::Integer) where T j += 1 indx += 1 end - resize!(union_nbrs, indx-1) + resize!(union_nbrs, indx - 1) return union_nbrs end diff --git a/src/SimpleGraphs/simpleedge.jl b/src/SimpleGraphs/simpleedge.jl index 5c41d14ee..504982832 100644 --- a/src/SimpleGraphs/simpleedge.jl +++ b/src/SimpleGraphs/simpleedge.jl @@ -10,10 +10,10 @@ end SimpleEdge(t::Tuple) = SimpleEdge(t[1], t[2]) SimpleEdge(p::Pair) = SimpleEdge(p.first, p.second) -SimpleEdge{T}(p::Pair) where T<:Integer = SimpleEdge(T(p.first), T(p.second)) -SimpleEdge{T}(t::Tuple) where T<:Integer = SimpleEdge(T(t[1]), T(t[2])) +SimpleEdge{T}(p::Pair) where {T<:Integer} = SimpleEdge(T(p.first), T(p.second)) +SimpleEdge{T}(t::Tuple) where {T<:Integer} = SimpleEdge(T(t[1]), T(t[2])) -eltype(::Type{<:ET}) where ET<:AbstractSimpleEdge{T} where T = T +eltype(::Type{<:ET}) where {ET<:AbstractSimpleEdge{T}} where {T} = T # Accessors src(e::AbstractSimpleEdge) = e.src @@ -26,9 +26,11 @@ show(io::IO, e::AbstractSimpleEdge) = print(io, "Edge $(e.src) => $(e.dst)") Pair(e::AbstractSimpleEdge) = Pair(src(e), dst(e)) Tuple(e::AbstractSimpleEdge) = (src(e), dst(e)) -SimpleEdge{T}(e::AbstractSimpleEdge) where T <: Integer = SimpleEdge{T}(T(e.src), T(e.dst)) +SimpleEdge{T}(e::AbstractSimpleEdge) where {T<:Integer} = SimpleEdge{T}(T(e.src), T(e.dst)) # Convenience functions -reverse(e::T) where T<:AbstractSimpleEdge = T(dst(e), src(e)) -==(e1::AbstractSimpleEdge, e2::AbstractSimpleEdge) = (src(e1) == src(e2) && dst(e1) == dst(e2)) +reverse(e::T) where {T<:AbstractSimpleEdge} = T(dst(e), src(e)) +function ==(e1::AbstractSimpleEdge, e2::AbstractSimpleEdge) + return (src(e1) == src(e2) && dst(e1) == dst(e2)) +end hash(e::AbstractSimpleEdge, h::UInt) = hash(src(e), hash(dst(e), h)) diff --git a/src/SimpleGraphs/simpleedgeiter.jl b/src/SimpleGraphs/simpleedgeiter.jl index e5ff5b686..867990ceb 100644 --- a/src/SimpleGraphs/simpleedgeiter.jl +++ b/src/SimpleGraphs/simpleedgeiter.jl @@ -28,7 +28,9 @@ end eltype(::Type{SimpleEdgeIter{SimpleGraph{T}}}) where {T} = SimpleGraphEdge{T} eltype(::Type{SimpleEdgeIter{SimpleDiGraph{T}}}) where {T} = SimpleDiGraphEdge{T} -@traitfn @inline function iterate(eit::SimpleEdgeIter{G}, state=(one(eltype(eit.g)), 1) ) where {G <: AbstractSimpleGraph; !IsDirected{G}} +@traitfn @inline function iterate( + eit::SimpleEdgeIter{G}, state=(one(eltype(eit.g)), 1) +) where {G <: AbstractSimpleGraph; !IsDirected{G}} g = eit.g fadjlist = fadj(g) T = eltype(g) @@ -54,7 +56,9 @@ eltype(::Type{SimpleEdgeIter{SimpleDiGraph{T}}}) where {T} = SimpleDiGraphEdge{T return e, state end -@traitfn @inline function iterate(eit::SimpleEdgeIter{G}, state=(one(eltype(eit.g)), 1) ) where {G <: AbstractSimpleGraph; IsDirected{G}} +@traitfn @inline function iterate( + eit::SimpleEdgeIter{G}, state=(one(eltype(eit.g)), 1) +) where {G <: AbstractSimpleGraph; IsDirected{G}} g = eit.g fadjlist = fadj(g) T = eltype(g) @@ -96,7 +100,7 @@ end ==(e1::SimpleEdgeIter, e2::Set{SimpleEdge}) = _isequal(e1, e2) ==(e1::Set{SimpleEdge}, e2::SimpleEdgeIter) = _isequal(e2, e1) -function ==(e1::SimpleEdgeIter, e2::SimpleEdgeIter) +function ==(e1::SimpleEdgeIter, e2::SimpleEdgeIter) g = e1.g h = e2.g ne(g) == ne(h) || return false @@ -105,13 +109,13 @@ function ==(e1::SimpleEdgeIter, e2::SimpleEdgeIter) fadj(g, i) == fadj(h, i) || return false end nv(g) == nv(h) && return true - for i in m+1:nv(g) + for i in (m + 1):nv(g) isempty(fadj(g, i)) || return false end - for i in m+1:nv(h) + for i in (m + 1):nv(h) isempty(fadj(h, i)) || return false end - return true + return true end in(e, es::SimpleEdgeIter) = has_edge(es.g, e) diff --git a/src/SimpleGraphs/simplegraph.jl b/src/SimpleGraphs/simplegraph.jl index 1c244fc6c..10ab9023f 100644 --- a/src/SimpleGraphs/simplegraph.jl +++ b/src/SimpleGraphs/simplegraph.jl @@ -5,25 +5,21 @@ const SimpleGraphEdge = SimpleEdge A type representing an undirected graph. """ -mutable struct SimpleGraph{T <: Integer} <: AbstractSimpleGraph{T} +mutable struct SimpleGraph{T<:Integer} <: AbstractSimpleGraph{T} ne::Int fadjlist::Vector{Vector{T}} # [src]: (dst, dst, dst) - function SimpleGraph{T}(ne::Int, fadjlist::Vector{Vector{T}}) where T + function SimpleGraph{T}(ne::Int, fadjlist::Vector{Vector{T}}) where {T} throw_if_invalid_eltype(T) return new{T}(ne, fadjlist) end end -function SimpleGraph( - ne, - fadjlist::Vector{Vector{T}} -) where T - +function SimpleGraph(ne, fadjlist::Vector{Vector{T}}) where {T} return SimpleGraph{T}(ne, fadjlist) end -eltype(x::SimpleGraph{T}) where T = T +eltype(x::SimpleGraph{T}) where {T} = T # Graph{UInt8}(6), Graph{Int16}(7), Graph{UInt8}() """ @@ -38,13 +34,13 @@ julia> SimpleGraph(UInt8(10)) {10, 0} undirected simple UInt8 graph ``` """ -function SimpleGraph{T}(n::Integer=0) where T <: Integer - fadjlist = [Vector{T}() for _ = one(T):n] +function SimpleGraph{T}(n::Integer=0) where {T<:Integer} + fadjlist = [Vector{T}() for _ in one(T):n] return SimpleGraph{T}(0, fadjlist) end # SimpleGraph(6), SimpleGraph(0x5) -SimpleGraph(n::T) where T <: Integer = SimpleGraph{T}(n) +SimpleGraph(n::T) where {T<:Integer} = SimpleGraph{T}(n) # SimpleGraph() SimpleGraph() = SimpleGraph{Int}() @@ -61,7 +57,7 @@ julia> SimpleGraph(UInt8) {0, 0} undirected simple UInt8 graph ``` """ -SimpleGraph(::Type{T}) where T <: Integer = SimpleGraph{T}(zero(T)) +SimpleGraph(::Type{T}) where {T<:Integer} = SimpleGraph{T}(zero(T)) # SimpleGraph(adjmx) """ @@ -85,10 +81,12 @@ julia> SimpleGraph{Int16}(A2) SimpleGraph(adjmx::AbstractMatrix) = SimpleGraph{Int}(adjmx) # Graph{UInt8}(adjmx) -function SimpleGraph{T}(adjmx::AbstractMatrix) where T <: Integer +function SimpleGraph{T}(adjmx::AbstractMatrix) where {T<:Integer} dima, dimb = size(adjmx) - isequal(dima, dimb) || throw(ArgumentError("Adjacency / distance matrices must be square")) - issymmetric(adjmx) || throw(ArgumentError("Adjacency / distance matrices must be symmetric")) + isequal(dima, dimb) || + throw(ArgumentError("Adjacency / distance matrices must be square")) + issymmetric(adjmx) || + throw(ArgumentError("Adjacency / distance matrices must be symmetric")) g = SimpleGraph(T(dima)) @inbounds for i in findall(triu(adjmx) .!= 0) @@ -115,13 +113,11 @@ julia> SimpleGraph{UInt8}(g) SimpleGraph(g::SimpleGraph) = copy(g) # converts Graph{Int} to Graph{Int32} -function SimpleGraph{T}(g::SimpleGraph) where T <: Integer +function SimpleGraph{T}(g::SimpleGraph) where {T<:Integer} h_fadj = [Vector{T}(x) for x in fadj(g)] return SimpleGraph(ne(g), h_fadj) end - - # SimpleGraph(digraph) """ SimpleGraph(g::SimpleDiGraph) @@ -155,12 +151,12 @@ function SimpleGraph(g::SimpleDiGraph) end end end - iseven(edgect) || throw(AssertionError("invalid edgect in graph creation - please file bug report")) + iseven(edgect) || + throw(AssertionError("invalid edgect in graph creation - please file bug report")) return SimpleGraph(edgect ÷ 2, newfadj) end - -@inbounds function cleanupedges!(fadjlist::Vector{Vector{T}}) where T <: Integer +@inbounds function cleanupedges!(fadjlist::Vector{Vector{T}}) where {T<:Integer} neg = 0 for v in 1:length(fadjlist) if !issorted(fadjlist[v]) @@ -201,42 +197,46 @@ julia> SimpleGraph(el) {5, 2} undirected simple Int64 graph ``` """ -function SimpleGraph(edge_list::Vector{SimpleGraphEdge{T}}) where T <: Integer +function SimpleGraph(edge_list::Vector{SimpleGraphEdge{T}}) where {T<:Integer} nvg = zero(T) @inbounds( - for e in edge_list - nvg = max(nvg, src(e), dst(e)) - end) + for e in edge_list + nvg = max(nvg, src(e), dst(e)) + end + ) list_sizes = ones(Int, nvg) degs = zeros(Int, nvg) @inbounds( - for e in edge_list - s, d = src(e), dst(e) - (s >= 1 && d >= 1) || continue - degs[s] += 1 - if s != d - degs[d] += 1 + for e in edge_list + s, d = src(e), dst(e) + (s >= 1 && d >= 1) || continue + degs[s] += 1 + if s != d + degs[d] += 1 + end end - end) + ) fadjlist = Vector{Vector{T}}(undef, nvg) @inbounds( - for v in 1:nvg - fadjlist[v] = Vector{T}(undef, degs[v]) - end) + for v in 1:nvg + fadjlist[v] = Vector{T}(undef, degs[v]) + end + ) @inbounds( - for e in edge_list - s, d = src(e), dst(e) - (s >= 1 && d >= 1) || continue - fadjlist[s][list_sizes[s]] = d - list_sizes[s] += 1 - if s != d - fadjlist[d][list_sizes[d]] = s - list_sizes[d] += 1 + for e in edge_list + s, d = src(e), dst(e) + (s >= 1 && d >= 1) || continue + fadjlist[s][list_sizes[s]] = d + list_sizes[s] += 1 + if s != d + fadjlist[d][list_sizes[d]] = s + list_sizes[d] += 1 + end end - end) + ) neg = cleanupedges!(fadjlist) g = SimpleGraph{T}() @@ -246,11 +246,12 @@ function SimpleGraph(edge_list::Vector{SimpleGraphEdge{T}}) where T <: Integer return g end - -@inbounds function add_to_fadjlist!(fadjlist::Vector{Vector{T}}, s::T, d::T) where T <: Integer +@inbounds function add_to_fadjlist!( + fadjlist::Vector{Vector{T}}, s::T, d::T +) where {T<:Integer} nvg = length(fadjlist) nvg_new = max(nvg, s, d) - for v = (nvg + 1):nvg_new + for v in (nvg + 1):nvg_new push!(fadjlist, Vector{T}()) end @@ -260,10 +261,8 @@ end end end - # Try to get the eltype from the first element function _SimpleGraphFromIterator(iter)::SimpleGraph - next = iterate(iter) if (next === nothing) return SimpleGraph(0) @@ -271,7 +270,7 @@ function _SimpleGraphFromIterator(iter)::SimpleGraph e = first(next) E = typeof(e) - if !(E <: SimpleGraphEdge{<: Integer}) + if !(E <: SimpleGraphEdge{<:Integer}) throw(DomainError(iter, "Edges must be of type SimpleEdge{T <: Integer}")) end @@ -293,27 +292,26 @@ function _SimpleGraphFromIterator(iter)::SimpleGraph next = iterate(iter, state) end - neg = cleanupedges!(fadjlist) + neg = cleanupedges!(fadjlist) g.fadjlist = fadjlist g.ne = neg return g end - -function _SimpleGraphFromIterator(iter, ::Type{T}) where {T <: Integer} - +function _SimpleGraphFromIterator(iter, ::Type{T}) where {T<:Integer} g = SimpleGraph{T}() fadjlist = Vector{Vector{T}}() @inbounds( - for e in iter - s, d = src(e), dst(e) - (s >= 1 && d >= 1) || continue - add_to_fadjlist!(fadjlist, s, d) - end) + for e in iter + s, d = src(e), dst(e) + (s >= 1 && d >= 1) || continue + add_to_fadjlist!(fadjlist, s, d) + end + ) - neg = cleanupedges!(fadjlist) + neg = cleanupedges!(fadjlist) g.fadjlist = fadjlist g.ne = neg @@ -345,10 +343,9 @@ julia> collect(edges(h)) ``` """ function SimpleGraphFromIterator(iter)::SimpleGraph - if Base.IteratorEltype(iter) == Base.HasEltype() E = eltype(iter) - if (E <: SimpleGraphEdge{<: Integer} && isconcretetype(E)) + if (E <: SimpleGraphEdge{<:Integer} && isconcretetype(E)) T = eltype(E) if isconcretetype(T) return _SimpleGraphFromIterator(iter, T) @@ -359,8 +356,7 @@ function SimpleGraphFromIterator(iter)::SimpleGraph return _SimpleGraphFromIterator(iter) end - -edgetype(::SimpleGraph{T}) where T <: Integer = SimpleGraphEdge{T} +edgetype(::SimpleGraph{T}) where {T<:Integer} = SimpleGraphEdge{T} """ badj(g::SimpleGraph[, v::Integer]) @@ -368,7 +364,7 @@ edgetype(::SimpleGraph{T}) where T <: Integer = SimpleGraphEdge{T} Return the backwards adjacency list of a graph. If `v` is specified, return only the adjacency list for that vertex. -###Implementation Notes +### Implementation Notes Returns a reference to the current graph's internal structures, not a copy. Do not modify result. If the graph is modified, the behavior is undefined: the array behind this reference may be modified too, but this is not guaranteed. @@ -376,7 +372,6 @@ the array behind this reference may be modified too, but this is not guaranteed. badj(g::SimpleGraph) = fadj(g) badj(g::SimpleGraph, v::Integer) = fadj(g, v) - """ adj(g[, v]) @@ -391,13 +386,11 @@ the array behind this reference may be modified too, but this is not guaranteed. adj(g::SimpleGraph) = fadj(g) adj(g::SimpleGraph, v::Integer) = fadj(g, v) -copy(g::SimpleGraph) = SimpleGraph(g.ne, deepcopy_adjlist(g.fadjlist)) - -==(g::SimpleGraph, h::SimpleGraph) = -vertices(g) == vertices(h) && -ne(g) == ne(h) && -fadj(g) == fadj(h) +copy(g::SimpleGraph) = SimpleGraph(g.ne, deepcopy_adjlist(g.fadjlist)) +function ==(g::SimpleGraph, h::SimpleGraph) + return vertices(g) == vertices(h) && ne(g) == ne(h) && fadj(g) == fadj(h) +end """ is_directed(g) @@ -406,7 +399,7 @@ Return `true` if `g` is a directed graph. """ is_directed(::Type{<:SimpleGraph}) = false -function has_edge(g::SimpleGraph{T}, s, d) where T +function has_edge(g::SimpleGraph{T}, s, d) where {T} verts = vertices(g) (s in verts && d in verts) || return false # edge out of bounds @inbounds list_s = g.fadjlist[s] @@ -418,7 +411,7 @@ function has_edge(g::SimpleGraph{T}, s, d) where T return insorted(d, list_s) end -function has_edge(g::SimpleGraph{T}, e::SimpleGraphEdge{T}) where T +function has_edge(g::SimpleGraph{T}, e::SimpleGraphEdge{T}) where {T} s, d = T.(Tuple(e)) return has_edge(g, s, d) end @@ -442,7 +435,7 @@ julia> add_edge!(g, 2, 3) false ``` """ -function add_edge!(g::SimpleGraph{T}, e::SimpleGraphEdge{T}) where T +function add_edge!(g::SimpleGraph{T}, e::SimpleGraphEdge{T}) where {T} s, d = T.(Tuple(e)) verts = vertices(g) (s in verts && d in verts) || return false # edge out of bounds @@ -485,7 +478,7 @@ julia> rem_edge!(g, 1, 2) false ``` """ -function rem_edge!(g::SimpleGraph{T}, e::SimpleGraphEdge{T}) where T +function rem_edge!(g::SimpleGraph{T}, e::SimpleGraphEdge{T}) where {T} s, d = T.(Tuple(e)) verts = vertices(g) (s in verts && d in verts) || return false # edge out of bounds @@ -523,7 +516,7 @@ julia> add_vertex!(g) false ``` """ -function add_vertex!(g::SimpleGraph{T}) where T +function add_vertex!(g::SimpleGraph{T}) where {T} (nv(g) + one(T) <= nv(g)) && return false # test for overflow push!(g.fadjlist, Vector{T}()) return true @@ -560,10 +553,9 @@ julia> g {3, 3} undirected simple Int64 graph ``` """ -function rem_vertices!(g::SimpleGraph{T}, - vs::AbstractVector{<:Integer}; - keep_order::Bool=false - ) where {T <: Integer} +function rem_vertices!( + g::SimpleGraph{T}, vs::AbstractVector{<:Integer}; keep_order::Bool=false +) where {T<:Integer} # TODO There might be some room for performance improvements. # At the moment, we check for all edges if they stay in the graph. # If some vertices keep their position, this might be unnecessary. @@ -575,7 +567,7 @@ function rem_vertices!(g::SimpleGraph{T}, remove = sort(vs) unique!(remove) (1 <= remove[1] && remove[end] <= n) || - throw(ArgumentError("Vertices to be removed must be in the range 1:nv(g).")) + throw(ArgumentError("Vertices to be removed must be in the range 1:nv(g).")) # Create a vmap that maps vertices to their new position # vertices that get removed are mapped to 0 @@ -602,9 +594,9 @@ function rem_vertices!(g::SimpleGraph{T}, u > v && break if i <= length(remove) && u == remove[i] while v == remove[j] && v > u - vmap[v] = 0 - v -= one(T) - j -= 1 + vmap[v] = 0 + v -= one(T) + j -= 1 end # v > remove[j] || u == v vmap[v] = u diff --git a/src/SimpleGraphs/specializations.jl b/src/SimpleGraphs/specializations.jl index ca8e8d4b3..fd7a4dfd8 100644 --- a/src/SimpleGraphs/specializations.jl +++ b/src/SimpleGraphs/specializations.jl @@ -6,24 +6,22 @@ Specialised version of `Graphs.squash` for `SimpleGraph` and `SimpleDiGraph`. If `alwayscopy` is `true`, the resulting graph will always be a copy, otherwise it can also be the original graph. """ -function Graphs.squash(g::Union{SimpleGraph, SimpleDiGraph}; alwayscopy::Bool=true) - +function Graphs.squash(g::Union{SimpleGraph,SimpleDiGraph}; alwayscopy::Bool=true) G = is_directed(g) ? SimpleDiGraph : SimpleGraph T = eltype(g) - (!alwayscopy && T <: Union{Int8, UInt8}) && return g - nv(g) < typemax(Int8) && return G{Int8}(g) - nv(g) < typemax(UInt8) && return G{UInt8}(g) - (!alwayscopy && T <: Union{Int16, UInt16}) && return g - nv(g) < typemax(Int16) && return G{Int16}(g) - nv(g) < typemax(UInt16) && return G{UInt16}(g) - (!alwayscopy && T <: Union{Int32, UInt32}) && return g - nv(g) < typemax(Int32) && return G{Int32}(g) - nv(g) < typemax(UInt32) && return G{UInt32}(g) - (!alwayscopy && T <: Union{Int64, UInt64}) && return g - nv(g) < typemax(Int64) && return G{Int64}(g) - nv(g) < typemax(UInt64) && return G{UInt64}(g) + (!alwayscopy && T <: Union{Int8,UInt8}) && return g + nv(g) < typemax(Int8) && return G{Int8}(g) + nv(g) < typemax(UInt8) && return G{UInt8}(g) + (!alwayscopy && T <: Union{Int16,UInt16}) && return g + nv(g) < typemax(Int16) && return G{Int16}(g) + nv(g) < typemax(UInt16) && return G{UInt16}(g) + (!alwayscopy && T <: Union{Int32,UInt32}) && return g + nv(g) < typemax(Int32) && return G{Int32}(g) + nv(g) < typemax(UInt32) && return G{UInt32}(g) + (!alwayscopy && T <: Union{Int64,UInt64}) && return g + nv(g) < typemax(Int64) && return G{Int64}(g) + nv(g) < typemax(UInt64) && return G{UInt64}(g) return alwayscopy ? copy(g) : g end - diff --git a/src/biconnectivity/articulation.jl b/src/biconnectivity/articulation.jl index 0405836d0..46f931212 100644 --- a/src/biconnectivity/articulation.jl +++ b/src/biconnectivity/articulation.jl @@ -20,8 +20,8 @@ julia> articulation(path_graph(5)) ``` """ function articulation end -@traitfn function articulation(g::AG::(!IsDirected)) where {T, AG<:AbstractGraph{T}} - s = Vector{Tuple{T, T, T}}() +@traitfn function articulation(g::AG::(!IsDirected)) where {T,AG<:AbstractGraph{T}} + s = Vector{Tuple{T,T,T}}() is_articulation_pt = falses(nv(g)) low = zeros(T, nv(g)) pre = zeros(T, nv(g)) @@ -37,7 +37,7 @@ function articulation end while !isempty(s) || first_time first_time = false - if wi < 1 + if wi < 1 pre[v] = cnt cnt += 1 low[v] = pre[v] @@ -71,14 +71,14 @@ function articulation end end wi < 1 && continue end - + if children > 1 is_articulation_pt[u] = true end end - + articulation_points = Vector{T}() - + for u in findall(is_articulation_pt) push!(articulation_points, T(u)) end diff --git a/src/biconnectivity/biconnect.jl b/src/biconnectivity/biconnect.jl index a0f8fb217..a99da35a0 100644 --- a/src/biconnectivity/biconnect.jl +++ b/src/biconnectivity/biconnect.jl @@ -3,7 +3,7 @@ A state type for depth-first search that finds the biconnected components. """ -mutable struct Biconnections{E <: AbstractEdge} +mutable struct Biconnections{E<:AbstractEdge} low::Vector{Int} depth::Vector{Int} stack::Vector{E} @@ -17,7 +17,6 @@ end return Biconnections(zeros(Int, n), zeros(Int, n), Vector{E}(), Vector{Vector{E}}(), 0) end - """ visit!(g, state, u, v) diff --git a/src/biconnectivity/bridge.jl b/src/biconnectivity/bridge.jl index 7dbcd5b27..f5c98fe5e 100644 --- a/src/biconnectivity/bridge.jl +++ b/src/biconnectivity/bridge.jl @@ -24,26 +24,26 @@ julia> bridges(path_graph(5)) ``` """ function bridges end -@traitfn function bridges(g::AG::(!IsDirected)) where {T, AG<:AbstractGraph{T}} - s = Vector{Tuple{T, T, T}}() - low = zeros(T, nv(g)) #keeps track of the earliest accesible time of a vertex in DFS-stack, effect of having back-edges is considered here - pre = zeros(T, nv(g)) #checks the entry time of a vertex in the DFS-stack, pre[u] = 0 if a vertex isn't visited; non-zero, otherwise - bridges = Edge{T}[] #keeps record of the bridge-edges - +@traitfn function bridges(g::AG::(!IsDirected)) where {T,AG<:AbstractGraph{T}} + s = Vector{Tuple{T,T,T}}() + low = zeros(T, nv(g)) # keeps track of the earliest accessible time of a vertex in DFS-stack, effect of having back-edges is considered here + pre = zeros(T, nv(g)) # checks the entry time of a vertex in the DFS-stack, pre[u] = 0 if a vertex isn't visited; non-zero, otherwise + bridges = Edge{T}[] # keeps record of the bridge-edges + # We iterate over all vertices, and if they have already been visited (pre != 0), we don't start a DFS from that vertex. # The purpose is to create a DFS forest. @inbounds for u in vertices(g) pre[u] != 0 && continue - v = u #currently visiting vertex - wi::T = zero(T) #index of children of v - w::T = zero(T) #children of v + v = u # currently visiting vertex + wi::T = zero(T) # index of children of v + w::T = zero(T) # children of v cnt::T = one(T) # keeps record of the time first_time = true - - #start of DFS + + # start of DFS while !isempty(s) || first_time first_time = false - if wi < 1 #initialisation for vertex v + if wi < 1 # initialisation for vertex v pre[v] = cnt cnt += 1 low[v] = pre[v] @@ -60,16 +60,16 @@ function bridges end end wi += 1 end - - # here, we're iterating of all the childen of vertex v, if unvisited, we start a DFS from that child, else we update the low[v] as the edge is a back-edge. + + # here, we're iterating of all the children of vertex v, if unvisited, we start a DFS from that child, else we update the low[v] as the edge is a back-edge. while wi <= length(v_neighbors) w = v_neighbors[wi] # If this is true , this indicates the vertex is still unvisited, then we push this on the stack. # Pushing onto the stack is analogous to visiting the vertex and starting DFS from that vertex. if pre[w] == 0 push!(s, (wi, u, v)) # the stack states are (index of child, currently visiting vertex, parent vertex of the child) - #updates the value for stimulating DFS from top of the stack - wi = 0 + # updates the value for stimulating DFS from top of the stack + wi = 0 u = v v = w break @@ -80,8 +80,7 @@ function bridges end end wi < 1 && continue end - end - + return bridges end diff --git a/src/centrality/betweenness.jl b/src/centrality/betweenness.jl index 8a9941590..8d212db2c 100644 --- a/src/centrality/betweenness.jl +++ b/src/centrality/betweenness.jl @@ -1,7 +1,6 @@ # Betweenness centrality measures # TODO - weighted, separate unweighted, edge betweenness - """ betweenness_centrality(g[, vs]) betweenness_centrality(g, k) @@ -43,12 +42,13 @@ julia> betweenness_centrality(path_graph(4)) 0.0 ``` """ -function betweenness_centrality(g::AbstractGraph, +function betweenness_centrality( + g::AbstractGraph, vs=vertices(g), distmx::AbstractMatrix=weights(g); normalize=true, - endpoints=false) - + endpoints=false, +) n_v = nv(g) k = length(vs) isdir = is_directed(g) @@ -65,27 +65,32 @@ function betweenness_centrality(g::AbstractGraph, end end - _rescale!(betweenness, - n_v, - normalize, - isdir, - k) + _rescale!(betweenness, n_v, normalize, isdir, k) return betweenness end function betweenness_centrality( - g::AbstractGraph, k::Integer, distmx::AbstractMatrix=weights(g); - normalize=true, endpoints=false, rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + g::AbstractGraph, + k::Integer, + distmx::AbstractMatrix=weights(g); + normalize=true, + endpoints=false, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) - betweenness_centrality(g, sample(vertices(g), k, rng=rng, seed=seed), distmx; normalize=normalize, endpoints=endpoints) + return betweenness_centrality( + g, + sample(vertices(g), k; rng=rng, seed=seed), + distmx; + normalize=normalize, + endpoints=endpoints, + ) end -function _accumulate_basic!(betweenness::Vector{Float64}, - state::DijkstraState, - g::AbstractGraph, - si::Integer) - +function _accumulate_basic!( + betweenness::Vector{Float64}, state::DijkstraState, g::AbstractGraph, si::Integer +) n_v = length(state.parents) # this is the ttl number of vertices δ = zeros(n_v) σ = state.pathcounts @@ -94,7 +99,7 @@ function _accumulate_basic!(betweenness::Vector{Float64}, # make sure the source index has no parents. P[si] = [] # we need to order the source vertices by decreasing distance for this to work. - S = reverse(state.closest_vertices) #Replaced sortperm with this + S = reverse(state.closest_vertices) # Replaced sortperm with this for w in S coeff = (1.0 + δ[w]) / σ[w] for v in P[w] @@ -109,11 +114,9 @@ function _accumulate_basic!(betweenness::Vector{Float64}, return nothing end -function _accumulate_endpoints!(betweenness::Vector{Float64}, - state::DijkstraState, - g::AbstractGraph, - si::Integer) - +function _accumulate_endpoints!( + betweenness::Vector{Float64}, state::DijkstraState, g::AbstractGraph, si::Integer +) n_v = nv(g) # this is the ttl number of vertices δ = zeros(n_v) σ = state.pathcounts @@ -136,7 +139,9 @@ function _accumulate_endpoints!(betweenness::Vector{Float64}, return nothing end -function _rescale!(betweenness::Vector{Float64}, n::Integer, normalize::Bool, directed::Bool, k::Integer) +function _rescale!( + betweenness::Vector{Float64}, n::Integer, normalize::Bool, directed::Bool, k::Integer +) if normalize if n <= 2 do_scale = false @@ -157,7 +162,6 @@ function _rescale!(betweenness::Vector{Float64}, n::Integer, normalize::Bool, di scale = scale * n / k end betweenness .*= scale - end return nothing end diff --git a/src/centrality/closeness.jl b/src/centrality/closeness.jl index b4f50edc4..7608a2471 100644 --- a/src/centrality/closeness.jl +++ b/src/centrality/closeness.jl @@ -29,10 +29,9 @@ julia> closeness_centrality(path_graph(4)) 0.5 ``` """ -function closeness_centrality(g::AbstractGraph, - distmx::AbstractMatrix=weights(g); - normalize=true) - +function closeness_centrality( + g::AbstractGraph, distmx::AbstractMatrix=weights(g); normalize=true +) n_v = nv(g) closeness = zeros(n_v) diff --git a/src/centrality/eigenvector.jl b/src/centrality/eigenvector.jl index 041524591..3639ca633 100644 --- a/src/centrality/eigenvector.jl +++ b/src/centrality/eigenvector.jl @@ -24,4 +24,6 @@ eigenvector of the adjacency matrix \$\\mathbf{A}\$. - Mark E. J. Newman: Networks: An Introduction. Oxford University Press, USA, 2010, pp. 169. """ -eigenvector_centrality(g::AbstractGraph) = abs.(vec(eigs(adjacency_matrix(g), which=LM(), nev=1)[2]))::Vector{Float64} +function eigenvector_centrality(g::AbstractGraph) + return abs.(vec(eigs(adjacency_matrix(g); which=LM(), nev=1)[2]))::Vector{Float64} +end diff --git a/src/centrality/katz.jl b/src/centrality/katz.jl index 059c019e9..33586ecff 100644 --- a/src/centrality/katz.jl +++ b/src/centrality/katz.jl @@ -34,6 +34,6 @@ function katz_centrality(g::AbstractGraph, α::Real=0.3) spI = sparse(one(Float64) * I, nvg, nvg) A = adjacency_matrix(g, Bool; dir=:in) v = (spI - α * A) \ v - v /= norm(v) + v /= norm(v) return v end diff --git a/src/centrality/pagerank.jl b/src/centrality/pagerank.jl index b34d43b5f..0693e8f70 100644 --- a/src/centrality/pagerank.jl +++ b/src/centrality/pagerank.jl @@ -12,19 +12,14 @@ graph `g` parameterized by damping factor `α`, number of iterations centrality calculated for each node in `g`, or an error if convergence is not reached within `n` iterations. """ -function pagerank( - g::AbstractGraph{U}, - α=0.85, - n::Integer=100, - ϵ=1.0e-6 - ) where U <: Integer - α_div_outdegree = Vector{Float64}(undef,nv(g)) +function pagerank(g::AbstractGraph{U}, α=0.85, n::Integer=100, ϵ=1.0e-6) where {U<:Integer} + α_div_outdegree = Vector{Float64}(undef, nv(g)) dangling_nodes = Vector{U}() for v in vertices(g) if outdegree(g, v) == 0 push!(dangling_nodes, v) end - α_div_outdegree[v] = (α/outdegree(g, v)) + α_div_outdegree[v] = (α / outdegree(g, v)) end N = Int(nv(g)) # solution vector and temporary vector @@ -40,7 +35,7 @@ function pagerank( xlast[v] = (1 - α + α * dangling_sum) * (1.0 / N) end # flow from edges - + for v in vertices(g) for u in inneighbors(g, v) xlast[v] += (x[u] * α_div_outdegree[u]) @@ -56,5 +51,5 @@ function pagerank( return x end end - error("Pagerank did not converge after $n iterations.") # TODO 0.7: change to InexactError with appropriate msg. + return error("Pagerank did not converge after $n iterations.") # TODO 0.7: change to InexactError with appropriate msg. end diff --git a/src/centrality/stress.jl b/src/centrality/stress.jl index 4490e603b..3411a9955 100644 --- a/src/centrality/stress.jl +++ b/src/centrality/stress.jl @@ -46,17 +46,17 @@ function stress_centrality(g::AbstractGraph, vs=vertices(g)) end function stress_centrality( - g::AbstractGraph, k::Integer; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing + g::AbstractGraph, + k::Integer; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, ) - stress_centrality(g, sample(vertices(g), k, rng=rng, seed=seed)) + return stress_centrality(g, sample(vertices(g), k; rng=rng, seed=seed)) end -function _stress_accumulate_basic!(stress::Vector{<:Integer}, - state::DijkstraState, - g::AbstractGraph, - si::Integer) - +function _stress_accumulate_basic!( + stress::Vector{<:Integer}, state::DijkstraState, g::AbstractGraph, si::Integer +) n_v = length(state.parents) # this is the ttl number of vertices δ = zeros(Int, n_v) P = state.predecessors @@ -65,11 +65,11 @@ function _stress_accumulate_basic!(stress::Vector{<:Integer}, # make sure the source index has no parents. P[si] = [] # we need to order the source vertices by decreasing distance for this to work. - S = reverse(state.closest_vertices) #Replaced sortperm with this + S = reverse(state.closest_vertices) # Replaced sortperm with this for w in S # w is the farthest vertex from si for v in P[w] # get the predecessors of w if v > 0 - δ[v] += δ[w] + 1 # increment sp of pred + δ[v] += δ[w] + 1 # increment sp of pred end end δ[w] *= length(P[w]) # adjust the # of sps of vertex diff --git a/src/community/assortativity.jl b/src/community/assortativity.jl index 57efd159e..1eccd8044 100644 --- a/src/community/assortativity.jl +++ b/src/community/assortativity.jl @@ -18,16 +18,16 @@ julia> assortativity(star_graph(4)) -1.0 ``` """ -function assortativity(g::AbstractGraph{T}) where T +function assortativity(g::AbstractGraph{T}) where {T} P = promote_type(Int64, T) # at least Int64 to reduce risk of overflow - nue = ne(g) + nue = ne(g) sjk = sj = sk = sjs = sks = zero(P) for d in edges(g) j = P(outdegree(g, src(d)) - 1) k = P(indegree(g, dst(d)) - 1) - sjk += j*k - sj += j - sk += k + sjk += j * k + sj += j + sk += k sjs += j^2 sks += k^2 end @@ -40,7 +40,7 @@ see equation (21) in M. E. J. Newman: Mixing patterns in networks, Phys. Rev. E http://arxiv.org/abs/cond-mat/0209450 =# @traitfn function assortativity_coefficient(g::::IsDirected, sjk, sj, sk, sjs, sks, nue) - return (sjk - sj*sk/nue) / sqrt((sjs - sj^2/nue)*(sks - sk^2/nue)) + return (sjk - sj * sk / nue) / sqrt((sjs - sj^2 / nue) * (sks - sk^2 / nue)) end #= @@ -49,5 +49,6 @@ see equation (4) in M. E. J. Newman: Assortative mixing in networks, Phys. Rev. http://arxiv.org/abs/cond-mat/0205405/ =# @traitfn function assortativity_coefficient(g::::(!IsDirected), sjk, sj, sk, sjs, sks, nue) - return (sjk/nue - ((sj + sk)/(2*nue))^2) / ((sjs + sks)/(2*nue) - ((sj + sk)/(2*nue))^2) + return (sjk / nue - ((sj + sk) / (2 * nue))^2) / + ((sjs + sks) / (2 * nue) - ((sj + sk) / (2 * nue))^2) end diff --git a/src/community/clique_percolation.jl b/src/community/clique_percolation.jl index 804314a36..f0b1f0765 100644 --- a/src/community/clique_percolation.jl +++ b/src/community/clique_percolation.jl @@ -1,8 +1,8 @@ """ clique_percolation(g, k=3) -Community detection using the clique percolation algorithm. Communities are potentionally overlapping. -Return a vector of vectors `c` such that `c[i]` is the set of vertices in community `i`. +Community detection using the clique percolation algorithm. Communities are potentially overlapping. +Return a vector of vectors `c` such that `c[i]` is the set of vertices in community `i`. The parameter `k` defines the size of the clique to use in percolation. ### References @@ -27,25 +27,25 @@ julia> clique_percolation(clique_graph(3, 2), k=4) """ function clique_percolation end -@traitfn function clique_percolation(g::::(!IsDirected); k = 3) - kcliques = filter(x->length(x)>=k, maximal_cliques(g)) - nc = length(kcliques) - # graph with nodes represent k-cliques - h = Graph(nc) - # vector for counting common nodes between two cliques efficiently - x = falses(nv(g)) - for i = 1:nc - x[kcliques[i]] .= true - for j = i+1:nc - sum(x[kcliques[j]]) >= k-1 && add_edge!(h, i, j) +@traitfn function clique_percolation(g::::(!IsDirected); k=3) + kcliques = filter(x -> length(x) >= k, maximal_cliques(g)) + nc = length(kcliques) + # graph with nodes represent k-cliques + h = Graph(nc) + # vector for counting common nodes between two cliques efficiently + x = falses(nv(g)) + for i in 1:nc + x[kcliques[i]] .= true + for j in (i + 1):nc + sum(x[kcliques[j]]) >= k - 1 && add_edge!(h, i, j) + end + # reset status + x[kcliques[i]] .= false end - # reset status - x[kcliques[i]] .= false - end - components = connected_components(h) - communities = [BitSet() for i=1:length(components)] - for (i,component) in enumerate(components) - push!(communities[i], vcat(kcliques[component]...)...) - end - return communities + components = connected_components(h) + communities = [BitSet() for i in 1:length(components)] + for (i, component) in enumerate(components) + push!(communities[i], vcat(kcliques[component]...)...) + end + return communities end diff --git a/src/community/cliques.jl b/src/community/cliques.jl index 26ce15ffc..3c4e90be7 100644 --- a/src/community/cliques.jl +++ b/src/community/cliques.jl @@ -24,7 +24,7 @@ julia> maximal_cliques(g) """ function maximal_cliques end # see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax -@traitfn function maximal_cliques(g::AG::(!IsDirected)) where {T, AG<:AbstractGraph{T}} +@traitfn function maximal_cliques(g::AG::(!IsDirected)) where {T,AG<:AbstractGraph{T}} # Cache nbrs and find first pivot (highest degree) maxconn = -1 # uncomment this when https://github.com/JuliaLang/julia/issues/23618 is fixed diff --git a/src/community/clustering.jl b/src/community/clustering.jl index 361b83652..98ee9bdca 100644 --- a/src/community/clustering.jl +++ b/src/community/clustering.jl @@ -27,12 +27,12 @@ julia> local_clustering_coefficient(g, [1, 2, 3]) """ function local_clustering_coefficient(g::AbstractGraph, v::Integer) ntriang, nalltriang = local_clustering(g, v) - return nalltriang == 0 ? 0. : ntriang * 1.0 / nalltriang + return nalltriang == 0 ? 0.0 : ntriang * 1.0 / nalltriang end -function local_clustering_coefficient(g::AbstractGraph, vs = vertices(g)) +function local_clustering_coefficient(g::AbstractGraph, vs=vertices(g)) ntriang, nalltriang = local_clustering(g, vs) - return map(p -> p[2] == 0 ? 0. : p[1] * 1.0 / p[2], zip(ntriang, nalltriang)) + return map(p -> p[2] == 0 ? 0.0 : p[1] * 1.0 / p[2], zip(ntriang, nalltriang)) end function local_clustering!(storage::AbstractVector{Bool}, g::AbstractGraph, v::Integer) @@ -53,11 +53,13 @@ function local_clustering!(storage::AbstractVector{Bool}, g::AbstractGraph, v::I return is_directed(g) ? (tcount, k * (k - 1)) : (div(tcount, 2), div(k * (k - 1), 2)) end -function local_clustering!(storage::AbstractVector{Bool}, - ntriang::AbstractVector{Int}, - nalltriang::AbstractVector{Int}, - g::AbstractGraph, - vs) +function local_clustering!( + storage::AbstractVector{Bool}, + ntriang::AbstractVector{Int}, + nalltriang::AbstractVector{Int}, + g::AbstractGraph, + vs, +) i = 0 for (i, v) in enumerate(vs) ntriang[i], nalltriang[i] = local_clustering!(storage, g, v) @@ -82,14 +84,13 @@ function local_clustering(g::AbstractGraph, v::Integer) return local_clustering!(storage, g, v) end -function local_clustering(g::AbstractGraph, vs = vertices(g)) +function local_clustering(g::AbstractGraph, vs=vertices(g)) storage = zeros(Bool, nv(g)) ntriang = zeros(Int, length(vs)) nalltriang = zeros(Int, length(vs)) return local_clustering!(storage, ntriang, nalltriang, g, vs) end - """ triangles(g[, v]) triangles(g, vs) @@ -120,8 +121,7 @@ julia> triangles(g) ``` """ triangles(g::AbstractGraph, v::Integer) = local_clustering(g, v)[1] -triangles(g::AbstractGraph, vs = vertices(g)) = local_clustering(g, vs)[1] - +triangles(g::AbstractGraph, vs=vertices(g)) = local_clustering(g, vs)[1] """ global_clustering_coefficient(g) @@ -154,6 +154,6 @@ function global_clustering_coefficient(g::AbstractGraph) k = degree(g, v) ntriangles += k * (k - 1) end - ntriangles == 0 && return 1. + ntriangles == 0 && return 1.0 return c / ntriangles end diff --git a/src/community/core-periphery.jl b/src/community/core-periphery.jl index 8a749a3a4..b33a2daee 100644 --- a/src/community/core-periphery.jl +++ b/src/community/core-periphery.jl @@ -29,11 +29,11 @@ julia> core_periphery_deg(path_graph(3)) function core_periphery_deg end @traitfn function core_periphery_deg(g::::(!IsDirected)) degs = degree(g) - p = sortperm(degs, rev=true) - s = sum(degs) / 2. + p = sortperm(degs; rev=true) + s = sum(degs) / 2.0 sbest = +Inf kbest = 0 - for k = 1:nv(g) - 1 + for k in 1:(nv(g) - 1) s = s + k - 1 - degree(g, p[k]) if s < sbest sbest = s @@ -42,5 +42,5 @@ function core_periphery_deg end end c = fill(2, nv(g)) c[p[1:kbest]] .= 1 - c + return c end diff --git a/src/community/label_propagation.jl b/src/community/label_propagation.jl index 5f1c1c284..8fb3b877d 100644 --- a/src/community/label_propagation.jl +++ b/src/community/label_propagation.jl @@ -10,9 +10,11 @@ the second is the convergence history for each node. Will return after - [Raghavan et al.](http://arxiv.org/abs/0709.2938) """ function label_propagation( - g::AbstractGraph{T}, maxiter=1000; - rng::Union{Nothing, AbstractRNG} = nothing, seed::Union{Nothing, Integer} = nothing -) where T + g::AbstractGraph{T}, + maxiter=1000; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T} rng = rng_from_rng_or_seed(rng, seed) n = nv(g) @@ -34,7 +36,7 @@ function label_propagation( random_order[j] = node end range_shuffle!(rng, 1:num_active, random_order) - @inbounds for j = 1:num_active + @inbounds for j in 1:num_active u = random_order[j] old_comm = label[u] label[u] = vote!(rng, g, label, c, u) @@ -49,7 +51,7 @@ function label_propagation( end fill!(c.neigh_cnt, 0) renumber_labels!(label, c.neigh_cnt) - label, convergence_hist + return label, convergence_hist end """ @@ -69,8 +71,9 @@ end Fast shuffle Array `a` in UnitRange `r`. """ function range_shuffle!(rng::AbstractRNG, r::UnitRange, a::AbstractVector) - (r.start > 0 && r.stop <= length(a)) || throw(DomainError(r, "range indices are out of bounds")) - @inbounds for i = length(r):-1:2 + (r.start > 0 && r.stop <= length(a)) || + throw(DomainError(r, "range indices are out of bounds")) + @inbounds for i in length(r):-1:2 j = rand(rng, 1:i) ii = i + r.start - 1 jj = j + r.start - 1 @@ -84,7 +87,7 @@ end Return the label with greatest frequency. """ function vote!(rng::AbstractRNG, g::AbstractGraph, m::Vector, c::NeighComm, u::Integer) - @inbounds for i = 1:c.neigh_last - 1 + @inbounds for i in 1:(c.neigh_last - 1) c.neigh_cnt[c.neigh_pos[i]] = -1 end c.neigh_last = 1 @@ -105,7 +108,7 @@ function vote!(rng::AbstractRNG, g::AbstractGraph, m::Vector, c::NeighComm, u::I end end # ties breaking randomly - range_shuffle!(rng, 1:c.neigh_last - 1, c.neigh_pos) + range_shuffle!(rng, 1:(c.neigh_last - 1), c.neigh_pos) result_lbl = zero(eltype(c.neigh_pos)) for lbl in c.neigh_pos @@ -118,11 +121,14 @@ function vote!(rng::AbstractRNG, g::AbstractGraph, m::Vector, c::NeighComm, u::I return result_lbl end -function renumber_labels!(membership::Vector{T}, label_counters::Vector{Int}) where {T <: Integer} +function renumber_labels!( + membership::Vector{T}, label_counters::Vector{Int} +) where {T<:Integer} N = length(membership) - (maximum(membership) > N || minimum(membership) < 1) && throw(ArgumentError("Labels must between 1 and |V|")) # TODO 0.7: change to DomainError? + (maximum(membership) > N || minimum(membership) < 1) && + throw(ArgumentError("Labels must between 1 and |V|")) # TODO 0.7: change to DomainError? j = one(T) - @inbounds for i = 1:length(membership) + @inbounds for i in 1:length(membership) k::T = membership[i] if k >= 1 if label_counters[k] == 0 diff --git a/src/community/modularity.jl b/src/community/modularity.jl index fe1b0c470..4d7d23d70 100644 --- a/src/community/modularity.jl +++ b/src/community/modularity.jl @@ -73,15 +73,14 @@ julia> modularity(barbell, [1, 1, 1, 2, 2, 2]) """ function modularity( g::AbstractGraph, - c::AbstractVector{<:Integer}; - distmx::AbstractArray{<:Number}=weights(g), - γ=1.0 - ) - + c::AbstractVector{<:Integer}; + distmx::AbstractArray{<:Number}=weights(g), + γ=1.0, +) m = sum([distmx[src(e), dst(e)] for e in edges(g)]) m = is_directed(g) ? m : 2 * m - m == 0 && return 0. + m == 0 && return 0.0 nc = maximum(c) kin = zeros(Float32, nc) kout = zeros(Float32, nc) @@ -91,14 +90,14 @@ function modularity( c1 = c[u] c2 = c[v] if c1 == c2 - Q += distmx[u,v] + Q += distmx[u, v] end - kout[c1] += distmx[u,v] - kin[c2] += distmx[u,v] + kout[c1] += distmx[u, v] + kin[c2] += distmx[u, v] end - end + end Q = Q * m - @inbounds for i = 1:nc + @inbounds for i in 1:nc Q -= γ * kin[i] * kout[i] end return Q / m^2 diff --git a/src/community/rich_club.jl b/src/community/rich_club.jl index bde82b37f..bb4d6921d 100644 --- a/src/community/rich_club.jl +++ b/src/community/rich_club.jl @@ -11,17 +11,17 @@ julia> rich_club(g, 1) 0.4 ``` """ -function rich_club(g::AbstractGraph{T}, k::Int) where T +function rich_club(g::AbstractGraph{T}, k::Int) where {T} E = zero(T) for e in edges(g) - if (outdegree(g, src(e)) >= k) && (indegree(g, dst(e)) >= k ) - E +=1 + if (outdegree(g, src(e)) >= k) && (indegree(g, dst(e)) >= k) + E += 1 end end N = count(degree(g) .>= k) if is_directed(g) - return E / (N*(N-1)) + return E / (N * (N - 1)) else - return 2*E / (N*(N-1)) + return 2 * E / (N * (N - 1)) end end diff --git a/src/connectivity.jl b/src/connectivity.jl index 81295c8be..174c3d72b 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -10,7 +10,7 @@ to each vertex. The component value is the smallest vertex ID in the component. ### Performance This algorithm is linear in the number of edges of the graph. """ -function connected_components!(label::AbstractVector, g::AbstractGraph{T}) where T +function connected_components!(label::AbstractVector, g::AbstractGraph{T}) where {T} for u in vertices(g) label[u] != zero(T) && continue label[u] = u @@ -29,7 +29,6 @@ function connected_components!(label::AbstractVector, g::AbstractGraph{T}) where return label end - """ components_dict(labels) @@ -37,7 +36,7 @@ Convert an array of labels to a map of component id to vertices, and return a map with each key corresponding to a given component id and each value containing the vertices associated with that component. """ -function components_dict(labels::Vector{T}) where T <: Integer +function components_dict(labels::Vector{T}) where {T<:Integer} d = Dict{T,Vector{T}}() for (v, l) in enumerate(labels) vec = get(d, l, Vector{T}()) @@ -53,7 +52,7 @@ end Given a vector of component labels, return a vector of vectors representing the vertices associated with a given component id. """ -function components(labels::Vector{T}) where T <: Integer +function components(labels::Vector{T}) where {T<:Integer} d = Dict{T,T}() c = Vector{Vector{T}}() i = one(T) @@ -95,7 +94,7 @@ julia> connected_components(g) [4, 5] ``` """ -function connected_components(g::AbstractGraph{T}) where T +function connected_components(g::AbstractGraph{T}) where {T} label = zeros(T, nv(g)) connected_components!(label, g) c, d = components(label) @@ -176,7 +175,6 @@ true """ is_weakly_connected(g) = is_connected(g) - """ strongly_connected_components(g) @@ -216,13 +214,14 @@ julia> strongly_connected_components(g) """ function strongly_connected_components end # see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax -@traitfn function strongly_connected_components(g::AG::IsDirected) where {T<:Integer, AG <: AbstractGraph{T}} +@traitfn function strongly_connected_components( + g::AG::IsDirected +) where {T<:Integer,AG<:AbstractGraph{T}} zero_t = zero(T) one_t = one(T) nvg = nv(g) count = one_t - index = zeros(T, nvg) # first time in which vertex is discovered stack = Vector{T}() # stores vertices which have been discovered and not yet assigned to any component onstack = zeros(Bool, nvg) # false if a vertex is waiting in the stack to receive a component assignment @@ -230,7 +229,6 @@ function strongly_connected_components end parents = zeros(T, nvg) # parent of every vertex in dfs components = Vector{Vector{T}}() # maintains a list of scc (order is not guaranteed in API) - dfs_stack = Vector{T}() @inbounds for s in vertices(g) @@ -246,14 +244,14 @@ function strongly_connected_components end push!(dfs_stack, s) while !isempty(dfs_stack) - v = dfs_stack[end] #end is the most recently added item + v = dfs_stack[end] # end is the most recently added item u = zero_t @inbounds for v_neighbor in outneighbors(g, v) if index[v_neighbor] == zero_t # unvisited neighbor found u = v_neighbor break - #GOTO A push u onto DFS stack and continue DFS + # GOTO A push u onto DFS stack and continue DFS elseif onstack[v_neighbor] # we have already seen n, but can update the lowlink of v # which has the effect of possibly keeping v on the stack until n is ready to pop. @@ -266,13 +264,15 @@ function strongly_connected_components end # we have fully explored the DFS tree from v. # time to start popping. popped = pop!(dfs_stack) - lowlink[parents[popped]] = min(lowlink[parents[popped]], lowlink[popped]) + lowlink[parents[popped]] = min( + lowlink[parents[popped]], lowlink[popped] + ) if index[v] == lowlink[v] # found a cycle in a completed dfs tree. component = Vector{T}() - while !isempty(stack) #break when popped == v + while !isempty(stack) # break when popped == v # drain stack until we see v. # everything on the stack until we see v is in the SCC rooted at v. popped = pop!(stack) @@ -289,7 +289,7 @@ function strongly_connected_components end push!(components, component) end - else #LABEL A + else # LABEL A # add unvisited neighbor to dfs index[u] = count lowlink[u] = count @@ -308,7 +308,6 @@ function strongly_connected_components end return components end - """ strongly_connected_components_kosaraju(g) @@ -372,94 +371,92 @@ julia> strongly_connected_components_kosaraju(g) ``` """ function strongly_connected_components_kosaraju end -@traitfn function strongly_connected_components_kosaraju(g::AG::IsDirected) where {T<:Integer, AG <: AbstractGraph{T}} - - nvg = nv(g) - - components = Vector{Vector{T}}() # Maintains a list of strongly connected components +@traitfn function strongly_connected_components_kosaraju( + g::AG::IsDirected +) where {T<:Integer,AG<:AbstractGraph{T}} + nvg = nv(g) - order = Vector{T}() # Vector which will store the order in which vertices are visited - sizehint!(order, nvg) + components = Vector{Vector{T}}() # Maintains a list of strongly connected components - color = zeros(UInt8, nvg) # Vector used as for marking the colors during dfs + order = Vector{T}() # Vector which will store the order in which vertices are visited + sizehint!(order, nvg) - dfs_stack = Vector{T}() # Stack used for dfs + color = zeros(UInt8, nvg) # Vector used as for marking the colors during dfs - # dfs1 - @inbounds for v in vertices(g) + dfs_stack = Vector{T}() # Stack used for dfs - color[v] != 0 && continue - color[v] = 1 + # dfs1 + @inbounds for v in vertices(g) + color[v] != 0 && continue + color[v] = 1 - # Start dfs from v - push!(dfs_stack, v) # Push v to the stack + # Start dfs from v + push!(dfs_stack, v) # Push v to the stack - while !isempty(dfs_stack) - u = dfs_stack[end] - w = zero(T) + while !isempty(dfs_stack) + u = dfs_stack[end] + w = zero(T) - for u_neighbor in outneighbors(g, u) - if color[u_neighbor] == 0 - w = u_neighbor - break - end - end + for u_neighbor in outneighbors(g, u) + if color[u_neighbor] == 0 + w = u_neighbor + break + end + end - if w != 0 - push!(dfs_stack, w) - color[w] = 1 - else - push!(order, u) #Push back in vector to store the order in which the traversal finishes(Reverse Topological Sort) - color[u] = 2 - pop!(dfs_stack) - end - end - end + if w != 0 + push!(dfs_stack, w) + color[w] = 1 + else + push!(order, u) # Push back in vector to store the order in which the traversal finishes(Reverse Topological Sort) + color[u] = 2 + pop!(dfs_stack) + end + end + end - @inbounds for i in vertices(g) + @inbounds for i in vertices(g) color[i] = 0 # Marking all the vertices from 1 to n as unvisited for dfs2 - end - - # dfs2 - @inbounds for i in 1:nvg + end - v = order[end-i+1] # Reading the order vector in the decreasing order of finish time - color[v] != 0 && continue - color[v] = 1 + # dfs2 + @inbounds for i in 1:nvg + v = order[end - i + 1] # Reading the order vector in the decreasing order of finish time + color[v] != 0 && continue + color[v] = 1 - component=Vector{T}() # Vector used to store the vertices of one component temporarily + component = Vector{T}() # Vector used to store the vertices of one component temporarily - # Start dfs from v - push!(dfs_stack, v) # Push v to the stack + # Start dfs from v + push!(dfs_stack, v) # Push v to the stack - while !isempty(dfs_stack) - u = dfs_stack[end] - w = zero(T) + while !isempty(dfs_stack) + u = dfs_stack[end] + w = zero(T) - for u_neighbor in inneighbors(g, u) - if color[u_neighbor] == 0 - w = u_neighbor - break - end - end + for u_neighbor in inneighbors(g, u) + if color[u_neighbor] == 0 + w = u_neighbor + break + end + end - if w != 0 - push!(dfs_stack, w) - color[w] = 1 - else - color[u] = 2 - push!(component, u) # Push u to the vector component - pop!(dfs_stack) - end - end + if w != 0 + push!(dfs_stack, w) + color[w] = 1 + else + color[u] = 2 + push!(component, u) # Push u to the vector component + pop!(dfs_stack) + end + end - push!(components, component) - end + push!(components, component) + end - return components + return components end - """ is_strongly_connected(g) @@ -474,7 +471,9 @@ true ``` """ function is_strongly_connected end -@traitfn is_strongly_connected(g::::IsDirected) = length(strongly_connected_components(g)) == 1 +@traitfn function is_strongly_connected(g::::IsDirected) + return length(strongly_connected_components(g)) == 1 +end """ period(g) @@ -492,14 +491,14 @@ julia> period(g) """ function period end # see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax -@traitfn function period(g::AG::IsDirected) where {T, AG <: AbstractGraph{T}} +@traitfn function period(g::AG::IsDirected) where {T,AG<:AbstractGraph{T}} !is_strongly_connected(g) && throw(ArgumentError("Graph must be strongly connected")) # First check if there's a self loop has_self_loops(g) && return 1 - g_bfs_tree = bfs_tree(g, 1) - levels = gdistances(g_bfs_tree, 1) + g_bfs_tree = bfs_tree(g, 1) + levels = gdistances(g_bfs_tree, 1) edge_values = Vector{T}() divisor = 0 @@ -535,7 +534,7 @@ Edge 2 => 1 ``` """ function condensation end -@traitfn function condensation(g::::IsDirected, scc::Vector{Vector{T}}) where T <: Integer +@traitfn function condensation(g::::IsDirected, scc::Vector{Vector{T}}) where {T<:Integer} h = DiGraph{T}(length(scc)) component = Vector{T}(undef, nv(g)) @@ -580,8 +579,8 @@ julia> attracting_components(g) """ function attracting_components end # see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax -@traitfn function attracting_components(g::AG::IsDirected) where {T, AG <: AbstractGraph{T}} - scc = strongly_connected_components(g) +@traitfn function attracting_components(g::AG::IsDirected) where {T,AG<:AbstractGraph{T}} + scc = strongly_connected_components(g) cond = condensation(g, scc) attracting = Vector{T}() @@ -630,8 +629,11 @@ julia> neighborhood(g, 1, 3, [0 1 0 0 0; 0 0 1 0 0; 1 0 0 0.25 0; 0 0 0 0 0.25; 5 ``` """ -neighborhood(g::AbstractGraph{T}, v::Integer, d, distmx::AbstractMatrix{U}=weights(g); dir=:out) where T <: Integer where U <: Real = - first.(neighborhood_dists(g, v, d, distmx; dir=dir)) +function neighborhood( + g::AbstractGraph{T}, v::Integer, d, distmx::AbstractMatrix{U}=weights(g); dir=:out +) where {T<:Integer} where {U<:Real} + return first.(neighborhood_dists(g, v, d, distmx; dir=dir)) +end """ neighborhood_dists(g, v, d, distmx=weights(g)) @@ -676,22 +678,35 @@ julia> neighborhood_dists(g, 4, 3, dir=:in) (1, 3) ``` """ -neighborhood_dists(g::AbstractGraph{T}, v::Integer, d, distmx::AbstractMatrix{U}=weights(g); dir=:out) where T <: Integer where U <: Real = - (dir == :out) ? _neighborhood(g, v, d, distmx, outneighbors) : _neighborhood(g, v, d, distmx, inneighbors) - +function neighborhood_dists( + g::AbstractGraph{T}, v::Integer, d, distmx::AbstractMatrix{U}=weights(g); dir=:out +) where {T<:Integer} where {U<:Real} + return if (dir == :out) + _neighborhood(g, v, d, distmx, outneighbors) + else + _neighborhood(g, v, d, distmx, inneighbors) + end +end -function _neighborhood(g::AbstractGraph{T}, v::Integer, d::Real, distmx::AbstractMatrix{U}, neighborfn::Function) where T <: Integer where U <: Real +function _neighborhood( + g::AbstractGraph{T}, + v::Integer, + d::Real, + distmx::AbstractMatrix{U}, + neighborfn::Function, +) where {T<:Integer} where {U<:Real} Q = Vector{Tuple{T,U}}() d < zero(U) && return Q - push!(Q, (v,zero(U),) ) - seen = fill(false,nv(g)); seen[v] = true #Bool Vector benchmarks faster than BitArray - for (src,currdist) in Q + push!(Q, (v, zero(U))) + seen = fill(false, nv(g)) + seen[v] = true # Bool Vector benchmarks faster than BitArray + for (src, currdist) in Q currdist >= d && continue - for dst in neighborfn(g,src) + for dst in neighborfn(g, src) if !seen[dst] - seen[dst]=true - if currdist+distmx[src,dst] <= d - push!(Q, (dst , currdist+distmx[src,dst],)) + seen[dst] = true + if currdist + distmx[src, dst] <= d + push!(Q, (dst, currdist + distmx[src, dst])) end end end @@ -717,15 +732,15 @@ for each integer r <= n-1 """ function isgraphical(degs::Vector{<:Integer}) iseven(sum(degs)) || return false - sorted_degs = sort(degs, rev = true) + sorted_degs = sort(degs; rev=true) n = length(sorted_degs) cur_sum = zero(UInt64) mindeg = Vector{UInt64}(undef, n) - @inbounds for i = 1:n + @inbounds for i in 1:n mindeg[i] = min(i, sorted_degs[i]) end cum_min = sum(mindeg) - @inbounds for r = 1:(n - 1) + @inbounds for r in 1:(n - 1) cur_sum += sorted_degs[r] cum_min -= mindeg[r] cond = cur_sum <= (r * (r - 1) + cum_min) diff --git a/src/core.jl b/src/core.jl index caaaeddfc..a1c9ee9fc 100644 --- a/src/core.jl +++ b/src/core.jl @@ -42,7 +42,7 @@ julia> add_vertices!(g, 2) 2 ``` """ -add_vertices!(g::AbstractGraph, n::Integer) = sum([add_vertex!(g) for i = 1:n]) +add_vertices!(g::AbstractGraph, n::Integer) = sum([add_vertex!(g) for i in 1:n]) """ indegree(g[, v]) @@ -68,7 +68,7 @@ julia> indegree(g) ``` """ indegree(g::AbstractGraph, v::Integer) = length(inneighbors(g, v)) -indegree(g::AbstractGraph, v::AbstractVector = vertices(g)) = [indegree(g, x) for x in v] +indegree(g::AbstractGraph, v::AbstractVector=vertices(g)) = [indegree(g, x) for x in v] """ outdegree(g[, v]) @@ -94,7 +94,7 @@ julia> outdegree(g) ``` """ outdegree(g::AbstractGraph, v::Integer) = length(outneighbors(g, v)) -outdegree(g::AbstractGraph, v::AbstractVector = vertices(g)) = [outdegree(g, x) for x in v] +outdegree(g::AbstractGraph, v::AbstractVector=vertices(g)) = [outdegree(g, x) for x in v] """ degree(g[, v]) @@ -125,7 +125,7 @@ function degree end @traitfn degree(g::::IsDirected, v::Integer) = indegree(g, v) + outdegree(g, v) @traitfn degree(g::::(!IsDirected), v::Integer) = indegree(g, v) -degree(g::AbstractGraph, v::AbstractVector = vertices(g)) = [degree(g, x) for x in v] +degree(g::AbstractGraph, v::AbstractVector=vertices(g)) = [degree(g, x) for x in v] """ Δout(g) @@ -168,7 +168,6 @@ Return the minimum [`degree`](@ref) of vertices in `g`. """ δ(g) = noallocextreme(degree, (<), typemax(Int), g) - """ noallocextreme(f, comparison, initial, g) @@ -194,7 +193,7 @@ represented by the key. Degree function (for example, [`indegree`](@ref) or [`outdegree`](@ref)) may be specified by overriding `degfn`. """ -function degree_histogram(g::AbstractGraph{T}, degfn=degree) where T +function degree_histogram(g::AbstractGraph{T}, degfn=degree) where {T} hist = Dict{T,Int}() for v in vertices(g) # minimize allocations by for d in degfn(g, v) # iterating over vertices @@ -204,7 +203,6 @@ function degree_histogram(g::AbstractGraph{T}, degfn=degree) where T return hist end - """ neighbors(g, v) @@ -278,11 +276,10 @@ julia> all_neighbors(g, 3) ``` """ function all_neighbors end -@traitfn all_neighbors(g::::IsDirected, v::Integer) = - union(outneighbors(g, v), inneighbors(g, v)) -@traitfn all_neighbors(g::::(!IsDirected), v::Integer) = - neighbors(g, v) - +@traitfn function all_neighbors(g::::IsDirected, v::Integer) + return union(outneighbors(g, v), inneighbors(g, v)) +end +@traitfn all_neighbors(g::::(!IsDirected), v::Integer) = neighbors(g, v) """ common_neighbors(g, u, v) @@ -320,8 +317,9 @@ julia> common_neighbors(g, 1, 4) 3 ``` """ -common_neighbors(g::AbstractGraph, u::Integer, v::Integer) = - intersect(neighbors(g, u), neighbors(g, v)) +function common_neighbors(g::AbstractGraph, u::Integer, v::Integer) + return intersect(neighbors(g, u), neighbors(g, v)) +end """ has_self_loops(g) @@ -345,7 +343,9 @@ julia> has_self_loops(g) true ``` """ -has_self_loops(g::AbstractGraph) = nv(g) == 0 ? false : any(v -> has_edge(g, v, v), vertices(g)) +function has_self_loops(g::AbstractGraph) + return nv(g) == 0 ? false : any(v -> has_edge(g, v, v), vertices(g)) +end """ num_self_loops(g) @@ -380,11 +380,8 @@ number of possible edges (``|V|×(|V|-1)`` for directed graphs and ``\\frac{|V|×(|V|-1)}{2}`` for undirected graphs). """ function density end -@traitfn density(g::::IsDirected) = -ne(g) / (nv(g) * (nv(g) - 1)) -@traitfn density(g::::(!IsDirected)) = -(2 * ne(g)) / (nv(g) * (nv(g) - 1)) - +@traitfn density(g::::IsDirected) = ne(g) / (nv(g) * (nv(g) - 1)) +@traitfn density(g::::(!IsDirected)) = (2 * ne(g)) / (nv(g) * (nv(g) - 1)) """ squash(g) @@ -404,7 +401,6 @@ function squash(g::AbstractGraph) Base.depwarn(deprecation_msg, :squash) end - gtype = is_directed(g) ? DiGraph : Graph validtypes = [UInt8, UInt16, UInt32, UInt64, Int64] nvg = nv(g) diff --git a/src/cycles/basis.jl b/src/cycles/basis.jl index 40e4e1df2..cadf486fa 100644 --- a/src/cycles/basis.jl +++ b/src/cycles/basis.jl @@ -44,30 +44,30 @@ function cycle_basis(g::AbstractSimpleGraph, root=nothing) while !isempty(stack) z = pop!(stack) zused = used[z] - for nbr in neighbors(g,z) + for nbr in neighbors(g, z) if !in(nbr, keys_used) pred[nbr] = z push!(keys_pred, nbr) - push!(stack,nbr) + push!(stack, nbr) used[nbr] = [z] push!(keys_used, nbr) elseif nbr == z push!(cycles, [z]) elseif !in(nbr, zused) pn = used[nbr] - cycle = [nbr,z] + cycle = [nbr, z] p = pred[z] while !in(p, pn) push!(cycle, p) p = pred[p] end - push!(cycle,p) - push!(cycles,cycle) + push!(cycle, p) + push!(cycles, cycle) push!(used[nbr], z) end end - end - setdiff!(gnodes,keys_pred) + end + setdiff!(gnodes, keys_pred) isempty(gnodes) && break r = pop!(gnodes) end diff --git a/src/cycles/hawick-james.jl b/src/cycles/hawick-james.jl index 9f1f112f0..5ae35c5c9 100644 --- a/src/cycles/hawick-james.jl +++ b/src/cycles/hawick-james.jl @@ -9,7 +9,9 @@ of Hawick & James. """ function simplecycles_hawick_james end # see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax -@traitfn function simplecycles_hawick_james(g::AG::IsDirected) where {T, AG<:AbstractGraph{T}} +@traitfn function simplecycles_hawick_james( + g::AG::IsDirected +) where {T,AG<:AbstractGraph{T}} nvg = nv(g) B = Vector{T}[Vector{T}() for i in vertices(g)] blocked = zeros(Bool, nvg) @@ -43,7 +45,15 @@ resetblocked!(blocked) = fill!(blocked, false) Find circuits in `g` recursively starting from v1. """ function circuit_recursive! end -@traitfn function circuit_recursive!(g::::IsDirected, v1::T, v2::T, blocked::AbstractVector, B::Vector{Vector{T}}, stack::Vector{T}, cycles::Vector{Vector{T}}) where T<:Integer +@traitfn function circuit_recursive!( + g::::IsDirected, + v1::T, + v2::T, + blocked::AbstractVector, + B::Vector{Vector{T}}, + stack::Vector{T}, + cycles::Vector{Vector{T}}, +) where {T<:Integer} f = false push!(stack, v2) blocked[v2] = true @@ -77,7 +87,7 @@ end Unblock the value `v` from the `blocked` list and remove from `B`. """ -function unblock!(v::T, blocked::AbstractVector, B::Vector{Vector{T}}) where T +function unblock!(v::T, blocked::AbstractVector, B::Vector{Vector{T}}) where {T} blocked[v] = false wPos = 1 Bv = B[v] diff --git a/src/cycles/incremental.jl b/src/cycles/incremental.jl index a027cd2d0..749f3713c 100644 --- a/src/cycles/incremental.jl +++ b/src/cycles/incremental.jl @@ -12,7 +12,9 @@ for a usage example. """ abstract type IncrementalCycleTracker{I} <: AbstractGraph{I} end -function (::Type{IncrementalCycleTracker})(s::AbstractGraph{I}; dir::Union{Symbol,Nothing}=nothing) where {I} +function (::Type{IncrementalCycleTracker})( + s::AbstractGraph{I}; dir::Union{Symbol,Nothing}=nothing +) where {I} # TODO: Once we have more algorithms, the poly-algorithm decision goes here. # For now, we only have Algorithm N. return DenseGraphICT_BFGT_N{something(dir, :out)}(s) @@ -75,12 +77,14 @@ Edge 2 => 3 """ function add_edge_checked! end -to_edges(v::Integer, w::Integer) = (v=>w,) +to_edges(v::Integer, w::Integer) = (v => w,) to_edges(v::Integer, ws) = zip(repeated(v), ws) to_edges(vs, w::Integer) = zip(vs, repeated(w)) -add_edge_checked!(ict::IncrementalCycleTracker, vs, ws) = add_edge_checked!(ict, vs, ws) do g - foreach(((v, w),)->add_edge!(g, v, w), to_edges(vs, ws)) +function add_edge_checked!(ict::IncrementalCycleTracker, vs, ws) + add_edge_checked!(ict, vs, ws) do g + foreach(((v, w),) -> add_edge!(g, v, w), to_edges(vs, ws)) + end end # Utilities @@ -92,9 +96,8 @@ is set by calling `commit!`. """ struct TransactionalVector{T} <: AbstractVector{T} v::Vector{T} - log::Vector{Pair{Int, T}} - TransactionalVector(v::Vector{T}) where {T} = - new{T}(v, Vector{Pair{Int, T}}()) + log::Vector{Pair{Int,T}} + TransactionalVector(v::Vector{T}) where {T} = new{T}(v, Vector{Pair{Int,T}}()) end function commit!(v::TransactionalVector) @@ -112,7 +115,7 @@ end function Base.setindex!(vec::TransactionalVector, val, idx) oldval = vec.v[idx] vec.v[idx] = val - push!(vec.log, idx=>oldval) + push!(vec.log, idx => oldval) return nothing end Base.getindex(vec::TransactionalVector, idx) = vec.v[idx] @@ -171,23 +174,23 @@ for dense graphs from BFGT15 (Section 3). $bibliography """ -struct DenseGraphICT_BFGT_N{InOut, I, G<:AbstractGraph{I}} <: IncrementalCycleTracker{I} +struct DenseGraphICT_BFGT_N{InOut,I,G<:AbstractGraph{I}} <: IncrementalCycleTracker{I} graph::G levels::TransactionalVector{Int} - function DenseGraphICT_BFGT_N{InOut}(g::G) where {InOut, I, G<:AbstractGraph{I}} + function DenseGraphICT_BFGT_N{InOut}(g::G) where {InOut,I,G<:AbstractGraph{I}} if ne(g) == 0 # Common case fast path, no edges, all level start at 0. levels = fill(0, nv(g)) else levels = weak_topological_levels(g) end - new{InOut, I, G}(g, TransactionalVector(levels)) + return new{InOut,I,G}(g, TransactionalVector(levels)) end end function Base.show(io::IO, ict::DenseGraphICT_BFGT_N) print(io, "BFGT_N cycle tracker on ") - show(io, ict.graph) + return show(io, ict.graph) end function topological_sort(ict::DenseGraphICT_BFGT_N{InOut}) where {InOut} @@ -200,10 +203,12 @@ end # Even when both `v` and `w` are integer, we know that `v` would come first, so # we prefer to check for `v` as the cycle vertex in this case. -add_edge_checked!(f!, ict::DenseGraphICT_BFGT_N{:out}, v::Integer, ws) = - _check_cycle_add!(f!, ict, to_edges(v, ws), v) -add_edge_checked!(f!, ict::DenseGraphICT_BFGT_N{:in}, vs, w::Integer) = - _check_cycle_add!(f!, ict, to_edges(vs, w), w) +function add_edge_checked!(f!, ict::DenseGraphICT_BFGT_N{:out}, v::Integer, ws) + return _check_cycle_add!(f!, ict, to_edges(v, ws), v) +end +function add_edge_checked!(f!, ict::DenseGraphICT_BFGT_N{:in}, vs, w::Integer) + return _check_cycle_add!(f!, ict, to_edges(vs, w), w) +end ### [BFGT15] Algorithm N # @@ -224,7 +229,7 @@ add_edge_checked!(f!, ict::DenseGraphICT_BFGT_N{:in}, vs, w::Integer) = # 3. We add some early out checks to detect we're about to do redundant work. function _check_cycle_add!(f!, ict::DenseGraphICT_BFGT_N{InOut}, edges, v) where {InOut} g = ict.graph - worklist = Pair{Int, Int}[] + worklist = Pair{Int,Int}[] # TODO: In the case where there's a single target vertex, we could saturate # the level first before we assign it to the tracked vector to save some # log space. @@ -235,7 +240,7 @@ function _check_cycle_add!(f!, ict::DenseGraphICT_BFGT_N{InOut}, edges, v) where end v == w && return false ict.levels[w] = ict.levels[v] + 1 - push!(worklist, v=>w) + push!(worklist, v => w) end while !isempty(worklist) (x, y) = popfirst!(worklist) @@ -257,7 +262,7 @@ function _check_cycle_add!(f!, ict::DenseGraphICT_BFGT_N{InOut}, edges, v) where end if ylevel >= ict.levels[z] ict.levels[z] = ylevel + 1 - push!(worklist, y=>z) + push!(worklist, y => z) end end end diff --git a/src/cycles/johnson.jl b/src/cycles/johnson.jl index 997522769..58495104e 100644 --- a/src/cycles/johnson.jl +++ b/src/cycles/johnson.jl @@ -1,4 +1,4 @@ -abstract type Visitor{T <: Integer} end +abstract type Visitor{T<:Integer} end """ ncycles_n_i(n::Integer, i::Integer) @@ -6,8 +6,9 @@ abstract type Visitor{T <: Integer} end Compute the theoretical maximum number of cycles of size `i` in a directed graph of `n` vertices. """ -ncycles_n_i(n::Integer, i::Integer) = - binomial(big(n), big(n - i + 1)) * factorial(big(n - i)) +function ncycles_n_i(n::Integer, i::Integer) + return binomial(big(n), big(n - i + 1)) * factorial(big(n - i)) +end """ maxsimplecycles(n::Integer) @@ -20,7 +21,6 @@ assuming there are no self-loops. """ maxsimplecycles(n::Integer) = sum(x -> ncycles_n_i(n, x), 1:(n - 1)) - """ maxsimplecycles(dg::::IsDirected, byscc::Bool = true) @@ -71,7 +71,7 @@ vertices to unblock if the key vertex is unblocked. `JohnsonVisitor` may also be constructed directly from the directed graph. """ -struct JohnsonVisitor{T <: Integer} <: Visitor{T} +struct JohnsonVisitor{T<:Integer} <: Visitor{T} stack::Vector{T} blocked::BitVector blockedmap::Vector{Set{T}} @@ -93,7 +93,7 @@ Unblock the vertices recursively. not and `B` is the map that tells if the unblocking of one vertex should unblock other vertices. """ -function unblock!(v::T, blocked::BitVector, B::Vector{Set{T}}) where T <: Integer +function unblock!(v::T, blocked::BitVector, B::Vector{Set{T}}) where {T<:Integer} blocked[v] = false for w in B[v] delete!(B[v], w) @@ -125,13 +125,19 @@ using a DFS algorithm. ### Implementation Notes Implements Johnson's CIRCUIT function. This is a recursive version. Modifies the vector of cycles, when needed. - + ### References - [Johnson](http://epubs.siam.org/doi/abs/10.1137/0204007) """ function circuit end -@traitfn function circuit(v::T, dg::::IsDirected, vis::JohnsonVisitor{T}, -allcycles::Vector{Vector{T}}, vmap::Vector{T}, startnode::T=v) where T <: Integer +@traitfn function circuit( + v::T, + dg::::IsDirected, + vis::JohnsonVisitor{T}, + allcycles::Vector{Vector{T}}, + vmap::Vector{T}, + startnode::T=v, +) where {T<:Integer} done = false push!(vis.stack, v) vis.blocked[v] = true @@ -140,7 +146,7 @@ allcycles::Vector{Vector{T}}, vmap::Vector{T}, startnode::T=v) where T <: Intege push!(allcycles, vmap[vis.stack]) done = true elseif !vis.blocked[w] - circuit(w, dg, vis, allcycles, vmap, startnode) && (done = true) #This is different from done = circuit(...). It keeps the previous value of done in the for loop + circuit(w, dg, vis, allcycles, vmap, startnode) && (done = true) # This is different from done = circuit(...). It keeps the previous value of done in the for loop end end if done @@ -156,7 +162,6 @@ allcycles::Vector{Vector{T}}, vmap::Vector{T}, startnode::T=v) where T <: Intege return done end - """ simplecycles(dg::::IsDirected) @@ -198,7 +203,6 @@ function simplecycles end return cycles end - ########################################################## #### Iterative version, using Tasks, of the previous algorithms. """ @@ -211,7 +215,7 @@ Return `true` if a circuit has been found in the current exploration. # Arguments * v: the vertex considered in this iteration of the DFS * dg: the digraph from which cycles are computed -* visitor: Informations needed for the cycle computation, contains: +* visitor: information needed for the cycle computation, contains: * stack: the stack of parent vertices * blocked: tells whether a vertex has already been explored or not * blockedmap: mapping of the blocking / unblocking consequences @@ -228,12 +232,14 @@ Produces a cycle when needed. Can be used only inside a `Channel`. - [Johnson](http://epubs.siam.org/doi/abs/10.1137/0204007) """ function circuit_iter end -@traitfn function circuit_iter(v::T, - dg::::IsDirected, - vis::JohnsonVisitor{T}, - vmap::Vector{T}, - cycle::Channel{Vector{T}}, - startnode::T=v) where T <: Integer +@traitfn function circuit_iter( + v::T, + dg::::IsDirected, + vis::JohnsonVisitor{T}, + vmap::Vector{T}, + cycle::Channel{Vector{T}}, + startnode::T=v, +) where {T<:Integer} done = false push!(vis.stack, v) vis.blocked[v] = true @@ -242,7 +248,7 @@ function circuit_iter end put!(cycle, vmap[vis.stack]) done = true elseif !vis.blocked[w] - circuit_iter(w, dg, vis, vmap, cycle, startnode) && (done = true) #This is different from done = circuit(...). It keeps the previous value of done in the for loop + circuit_iter(w, dg, vis, vmap, cycle, startnode) && (done = true) # This is different from done = circuit(...). It keeps the previous value of done in the for loop end end if done @@ -258,7 +264,6 @@ function circuit_iter end return done end - """ itercycles(dg::::IsDirected, cycle::Channel) @@ -272,7 +277,9 @@ after a given number of cycles. - [Johnson](http://epubs.siam.org/doi/abs/10.1137/0204007) """ function itercycles end -@traitfn function itercycles(dg::AG::IsDirected, cycle::Channel{Vector{T}}) where {T, AG <: AbstractGraph{T}} +@traitfn function itercycles( + dg::AG::IsDirected, cycle::Channel{Vector{T}} +) where {T,AG<:AbstractGraph{T}} sccs = strongly_connected_components(dg) for scc in sccs while length(scc) >= 1 @@ -306,9 +313,13 @@ julia> simplecyclescount(complete_digraph(6)) ``` """ function simplecyclescount end -@traitfn function simplecyclescount(dg::AG::IsDirected, ceiling=10^6) where {T, AG <: AbstractGraph{T}} +@traitfn function simplecyclescount( + dg::AG::IsDirected, ceiling=10^6 +) where {T,AG<:AbstractGraph{T}} len = 0 - for cycle in Iterators.take(Channel(c -> itercycles(dg, c), ctype=Vector{T})::Channel{Vector{T}}, ceiling) + for cycle in Iterators.take( + Channel(c -> itercycles(dg, c); ctype=Vector{T})::Channel{Vector{T}}, ceiling + ) len += 1 end return len @@ -333,8 +344,13 @@ To get an idea of the possible number of cycles, use function - [Johnson](http://epubs.siam.org/doi/abs/10.1137/0204007) """ function simplecycles_iter end -@traitfn simplecycles_iter(dg::AG::IsDirected, ceiling=10^6) where {T, AG <: AbstractGraph{T}} = - return collect(Iterators.take(Channel(c -> itercycles(dg, c), ctype=Vector{T}), ceiling))::Vector{Vector{T}} +@traitfn function simplecycles_iter( + dg::AG::IsDirected, ceiling=10^6 +) where {T,AG<:AbstractGraph{T}} + return collect( + Iterators.take(Channel(c -> itercycles(dg, c); ctype=Vector{T}), ceiling) + )::Vector{Vector{T}} +end """ simplecycleslength(dg::DiGraph, ceiling = 10^6) @@ -362,10 +378,14 @@ julia> simplecycleslength(wheel_digraph(16)) ``` """ function simplecycleslength end -@traitfn function simplecycleslength(dg::AG::IsDirected, ceiling=10^6) where {T, AG <: AbstractGraph{T}} +@traitfn function simplecycleslength( + dg::AG::IsDirected, ceiling=10^6 +) where {T,AG<:AbstractGraph{T}} ncycles = 0 cyclelength = zeros(Int, nv(dg)) - for cycle in Iterators.take(Channel(c -> itercycles(dg, c), ctype=Vector{T})::Channel{Vector{T}}, ceiling) + for cycle in Iterators.take( + Channel(c -> itercycles(dg, c); ctype=Vector{T})::Channel{Vector{T}}, ceiling + ) cyclelength[length(cycle)] += 1 ncycles += 1 end diff --git a/src/cycles/karp.jl b/src/cycles/karp.jl index 8c1e78e61..992e3f44e 100644 --- a/src/cycles/karp.jl +++ b/src/cycles/karp.jl @@ -2,24 +2,21 @@ # A characterization of the minimum cycle mean in a digraph # Discrete Mathematics, 1978, 23, 309 - 311 function _karp_minimum_cycle_mean( - g::AbstractGraph, - distmx::AbstractMatrix{T}, - component::Vector{U} - ) where T <: Real where U <: Integer - - v2j = Dict{U, Int}() + g::AbstractGraph, distmx::AbstractMatrix{T}, component::Vector{U} +) where {T<:Real} where {U<:Integer} + v2j = Dict{U,Int}() for (j, v) in enumerate(component) v2j[v] = j end n = length(component) - F = fill(Inf, n+1, n) - F[1, 1] = 0. - for i in 2:n+1 + F = fill(Inf, n + 1, n) + F[1, 1] = 0.0 + for i in 2:(n + 1) for (j, v) in enumerate(component) for u in inneighbors(g, v) k = get(v2j, u, 0) if !iszero(k) - F[i, j] = min(F[i, j], F[i-1, k] + distmx[u, v]) + F[i, j] = min(F[i, j], F[i - 1, k] + distmx[u, v]) end end end @@ -45,20 +42,20 @@ function _karp_minimum_cycle_mean( jbest = 0 for j in 1:n - λ = maximum(map(i -> (F[n+1, j] - F[i, j]) / (n+1 - i), 1:n)) - if λ < λmin || (isfinite(λ) && λ ≈ λmin && F[n+1, j] < F[n+1, jbest]) + λ = maximum(map(i -> (F[n + 1, j] - F[i, j]) / (n + 1 - i), 1:n)) + if λ < λmin || (isfinite(λ) && λ ≈ λmin && F[n + 1, j] < F[n + 1, jbest]) λmin = λ jbest = j end end - + iszero(jbest) && return U[], Inf # Backward walk from jbest - walk = zeros(Int, n+1) - walk[n+1] = jbest + walk = zeros(Int, n + 1) + walk[n + 1] = jbest for i in n:-1:1 - v = component[walk[i+1]] + v = component[walk[i + 1]] dmin = Inf for u in inneighbors(g, v) j = get(v2j, u, 0) @@ -75,11 +72,11 @@ function _karp_minimum_cycle_mean( # Extract cycle in the walk invmap = zeros(Int, n) I = 1:0 - for i in n+1:-1:1 + for i in (n + 1):-1:1 if iszero(invmap[walk[i]]) invmap[walk[i]] = i else - I = i+1:invmap[walk[i]] + I = (i + 1):invmap[walk[i]] break end end @@ -96,9 +93,8 @@ Return minimum cycle mean of the directed graph `g` with optional edge weights c """ function karp_minimum_cycle_mean end @traitfn function karp_minimum_cycle_mean( - g::::IsDirected, - distmx::AbstractMatrix = weights(g) - ) + g::::IsDirected, distmx::AbstractMatrix=weights(g) +) cycle = Int[] λmin = Inf for component in strongly_connected_components(g) @@ -108,5 +104,5 @@ function karp_minimum_cycle_mean end λmin = λ end end - cycle, λmin + return cycle, λmin end diff --git a/src/cycles/limited_length.jl b/src/cycles/limited_length.jl index 8df62a397..2904d8200 100644 --- a/src/cycles/limited_length.jl +++ b/src/cycles/limited_length.jl @@ -11,8 +11,9 @@ finding short cycles. If you want to find cycles of any length in a directed graph, [`simplecycles`](@ref) or [`simplecycles_iter`](@ref) may be more efficient. """ -function simplecycles_limited_length(graph::AbstractGraph{T}, n::Int, - ceiling = 10^6) where {T} +function simplecycles_limited_length( + graph::AbstractGraph{T}, n::Int, ceiling=10^6 +) where {T} cycles = Vector{Vector{T}}() n < 1 && return cycles cycle = Vector{T}(undef, n) @@ -25,13 +26,11 @@ function simplecycles_limited_length(graph::AbstractGraph{T}, n::Int, end function simplecycles_limited_length!(graph, n, ceiling, cycles, cycle, i) - length(cycles) >= ceiling && return + length(cycles) >= ceiling && return nothing for v in outneighbors(graph, cycle[i]) if v == cycle[1] push!(cycles, cycle[1:i]) - elseif (i < n - && v > cycle[1] - && !repeated_vertex(v, cycle, 2, i)) + elseif (i < n && v > cycle[1] && !repeated_vertex(v, cycle, 2, i)) cycle[i + 1] = v simplecycles_limited_length!(graph, n, ceiling, cycles, cycle, i + 1) end @@ -42,7 +41,7 @@ end # views are completely allocation free this can be expected to be # faster. function repeated_vertex(v, cycle, n1, n2) - for k = n1:n2 + for k in n1:n2 cycle[k] == v && return true end return false diff --git a/src/degeneracy.jl b/src/degeneracy.jl index e29d398f7..306636665 100644 --- a/src/degeneracy.jl +++ b/src/degeneracy.jl @@ -35,48 +35,48 @@ julia> core_number(g) 0 ``` """ -function core_number(g::AbstractGraph{T}) where T +function core_number(g::AbstractGraph{T}) where {T} has_self_loops(g) && throw(ArgumentError("graph must not have self-loops")) n = nv(g) deg = T.(degree(g)) # this will contain core number for each vertex of graph maxdeg = maximum(deg) # maximum degree of a vertex in graph - bin = zeros(T, maxdeg+1) # used for bin-sort and storing starting positions of bins + bin = zeros(T, maxdeg + 1) # used for bin-sort and storing starting positions of bins vert = zeros(T, n) # contains the set of vertices, sorted by their degrees pos = zeros(T, n) # contains positions of vertices in array vert # count number of vertices will be in each bin - for v = 1:n - bin[deg[v]+1] += one(T) + for v in 1:n + bin[deg[v] + 1] += one(T) end # from bin sizes determine starting positions of bins in array vert start = one(T) - for d = zero(T):maxdeg - num = bin[d+1] - bin[d+1] = start + for d in zero(T):maxdeg + num = bin[d + 1] + bin[d + 1] = start start += num end # sort the vertices in increasing order of their degrees and store in array vert for v in vertices(g) - pos[v] = bin[deg[v]+1] + pos[v] = bin[deg[v] + 1] vert[pos[v]] = v - bin[deg[v]+1] += one(T) + bin[deg[v] + 1] += one(T) end # recover starting positions of the bins - for d = maxdeg:-1:one(T) - bin[d+1] = bin[d] + for d in maxdeg:-1:one(T) + bin[d + 1] = bin[d] end bin[1] = one(T) # cores decomposition - for i = 1:n + for i in 1:n v = vert[i] # for each neighbor u of vertex v with higher degree we have to decrease its degree and move it for one bin to the left for u in outneighbors(g, v) if deg[u] > deg[v] du = deg[u] pu = pos[u] - pw = bin[du+1] + pw = bin[du + 1] w = vert[pw] if u != w pos[u] = pw @@ -84,28 +84,28 @@ function core_number(g::AbstractGraph{T}) where T pos[w] = pu vert[pw] = u end - bin[du+1] += one(T) + bin[du + 1] += one(T) deg[u] -= one(T) end end if is_directed(g) - for u in inneighbors(g, v) - if deg[u] > deg[v] - du = deg[u] - pu = pos[u] - pw = bin[du+1] - w = vert[pw] - if u != w - pos[u] = pw - vert[pu] = w - pos[w] = pu - vert[pw] = u - end - bin[du+1] += one(T) - deg[u] -= one(T) - end - end - end + for u in inneighbors(g, v) + if deg[u] > deg[v] + du = deg[u] + pu = pos[u] + pw = bin[du + 1] + w = vert[pw] + if u != w + pos[u] = pw + vert[pu] = w + pos[w] = pu + vert[pw] = u + end + bin[du + 1] += one(T) + deg[u] -= one(T) + end + end + end end return deg @@ -155,7 +155,7 @@ julia> k_core(g, 2) """ function k_core(g::AbstractGraph, k=-1; corenum=core_number(g)) if (k == -1) - # type assertion for inference (performance of captured variables julia#15726) + # type assertion for inference (performance of captured variables julia#15726) k::Int = maximum(corenum) # max core end @@ -211,7 +211,7 @@ julia> k_shell(g, 2) """ function k_shell(g::AbstractGraph, k=-1; corenum=core_number(g)) if k == -1 - # type assertion for inference (performance of captured variables julia#15726) + # type assertion for inference (performance of captured variables julia#15726) k::Int = maximum(corenum) end return findall(x -> x == k, corenum) @@ -269,7 +269,7 @@ julia> k_crust(g, 2) """ function k_crust(g, k=-1; corenum=core_number(g)) if k == -1 - # type assertion for inference (performance of captured variables julia#15726) + # type assertion for inference (performance of captured variables julia#15726) k::Int = maximum(corenum) - 1 end return findall(x -> x <= k, corenum) @@ -328,5 +328,5 @@ function k_corona(g::AbstractGraph, k; corenum=core_number(g)) kcoreg = g[kcore] kcoredeg = degree(kcoreg) - return kcore[findall(x-> x == k, kcoredeg)] + return kcore[findall(x -> x == k, kcoredeg)] end diff --git a/src/digraph/transitivity.jl b/src/digraph/transitivity.jl index 0a44c4a7b..34af55bf0 100644 --- a/src/digraph/transitivity.jl +++ b/src/digraph/transitivity.jl @@ -19,8 +19,8 @@ function transitiveclosure! end x = selflooped ? 0 : 1 for comp in scc - for j in 1:(length(comp)-x) - for k in (j+x):length(comp) + for j in 1:(length(comp) - x) + for k in (j + x):length(comp) add_edge!(g, comp[j], comp[k]) add_edge!(g, comp[k], comp[j]) end @@ -102,7 +102,7 @@ julia> collect(edges(transitiveclosure(barbell))) Edge 6 => 5 ``` """ -function transitiveclosure(g::DiGraph, selflooped = false) +function transitiveclosure(g::DiGraph, selflooped=false) copyg = copy(g) return transitiveclosure!(copyg, selflooped) end @@ -164,56 +164,58 @@ function transitivereduction end stack = Vector{eltype(cg)}(undef, nv(cg)) resultg = SimpleDiGraph{eltype(g)}(nv(g)) -# Calculate the transitive reduction of the acyclic condensation graph. + # Calculate the transitive reduction of the acyclic condensation graph. @inbounds( - for u in vertices(cg) - fill!(reachable, false) # vertices reachable from u on a path of length >= 2 - fill!(visited, false) - stacksize = 0 - for v in outneighbors(cg,u) - @simd for w in outneighbors(cg, v) - if !visited[w] - visited[w] = true - stacksize += 1 - stack[stacksize] = w + for u in vertices(cg) + fill!(reachable, false) # vertices reachable from u on a path of length >= 2 + fill!(visited, false) + stacksize = 0 + for v in outneighbors(cg, u) + @simd for w in outneighbors(cg, v) + if !visited[w] + visited[w] = true + stacksize += 1 + stack[stacksize] = w + end end end - end - while stacksize > 0 - v = stack[stacksize] - stacksize -= 1 - reachable[v] = true - @simd for w in outneighbors(cg, v) - if !visited[w] - visited[w] = true - stacksize += 1 - stack[stacksize] = w + while stacksize > 0 + v = stack[stacksize] + stacksize -= 1 + reachable[v] = true + @simd for w in outneighbors(cg, v) + if !visited[w] + visited[w] = true + stacksize += 1 + stack[stacksize] = w + end end end - end -# Add the edges from the condensation graph to the resulting graph. - @simd for v in outneighbors(cg,u) - if !reachable[v] - add_edge!(resultg, scc[u][1], scc[v][1]) + # Add the edges from the condensation graph to the resulting graph. + @simd for v in outneighbors(cg, u) + if !reachable[v] + add_edge!(resultg, scc[u][1], scc[v][1]) + end end end - end) + ) -# Replace each strongly connected component with a directed cycle. + # Replace each strongly connected component with a directed cycle. @inbounds( - for component in scc - nvc = length(component) - if nvc == 1 - if selflooped && has_edge(g, component[1], component[1]) - add_edge!(resultg, component[1], component[1]) + for component in scc + nvc = length(component) + if nvc == 1 + if selflooped && has_edge(g, component[1], component[1]) + add_edge!(resultg, component[1], component[1]) + end + continue end - continue - end - for i in 1:(nvc-1) - add_edge!(resultg, component[i], component[i+1]) + for i in 1:(nvc - 1) + add_edge!(resultg, component[i], component[i + 1]) + end + add_edge!(resultg, component[nvc], component[1]) end - add_edge!(resultg, component[nvc], component[1]) - end) + ) return resultg end diff --git a/src/distance.jl b/src/distance.jl index 7179929b8..aa7aee50e 100644 --- a/src/distance.jl +++ b/src/distance.jl @@ -12,7 +12,9 @@ end DefaultDistance(nv::Integer) = DefaultDistance(Int(nv)) -show(io::IO, x::DefaultDistance) = print(io, "$(x.nv) × $(x.nv) default distance matrix (value = 1)") +function show(io::IO, x::DefaultDistance) + return print(io, "$(x.nv) × $(x.nv) default distance matrix (value = 1)") +end show(io::IO, z::MIME"text/plain", x::DefaultDistance) = show(io, x) getindex(::DefaultDistance, s::Integer, d::Integer) = 1 @@ -67,22 +69,22 @@ julia> eccentricity(g, [1; 2], [0 2 0; 0.5 0 0.5; 0 2 0]) 0.5 ``` """ -function eccentricity(g::AbstractGraph, - v::Integer, - distmx::AbstractMatrix{T}=weights(g)) where T <: Real +function eccentricity( + g::AbstractGraph, v::Integer, distmx::AbstractMatrix{T}=weights(g) +) where {T<:Real} e = maximum(dijkstra_shortest_paths(g, v, distmx).dists) e == typemax(T) && @warn("Infinite path length detected for vertex $v") return e end -eccentricity(g::AbstractGraph, - vs=vertices(g), - distmx::AbstractMatrix=weights(g)) = [eccentricity(g, v, distmx) for v in vs] - +function eccentricity(g::AbstractGraph, vs=vertices(g), distmx::AbstractMatrix=weights(g)) + return [eccentricity(g, v, distmx) for v in vs] +end -eccentricity(g::AbstractGraph, distmx::AbstractMatrix) = - eccentricity(g, vertices(g), distmx) +function eccentricity(g::AbstractGraph, distmx::AbstractMatrix) + return eccentricity(g, vertices(g), distmx) +end """ diameter(eccentricities) @@ -103,8 +105,9 @@ julia> diameter(path_graph(5)) ``` """ diameter(eccentricities::Vector) = maximum(eccentricities) -diameter(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) = - maximum(eccentricity(g, distmx)) +function diameter(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) + return maximum(eccentricity(g, distmx)) +end """ periphery(eccentricities) @@ -137,8 +140,9 @@ function periphery(eccentricities::Vector) return filter(x -> eccentricities[x] == diam, 1:length(eccentricities)) end -periphery(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) = - periphery(eccentricity(g, distmx)) +function periphery(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) + return periphery(eccentricity(g, distmx)) +end """ radius(eccentricities) @@ -159,8 +163,9 @@ julia> radius(path_graph(5)) ``` """ radius(eccentricities::Vector) = minimum(eccentricities) -radius(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) = - minimum(eccentricity(g, distmx)) +function radius(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) + return minimum(eccentricity(g, distmx)) +end """ center(eccentricities) @@ -188,5 +193,6 @@ function center(eccentricities::Vector) return filter(x -> eccentricities[x] == rad, 1:length(eccentricities)) end -center(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) = - center(eccentricity(g, distmx)) +function center(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) + return center(eccentricity(g, distmx)) +end diff --git a/src/dominatingset/degree_dom_set.jl b/src/dominatingset/degree_dom_set.jl index 94210b7de..3e3da690b 100644 --- a/src/dominatingset/degree_dom_set.jl +++ b/src/dominatingset/degree_dom_set.jl @@ -14,16 +14,15 @@ function update_dominated!( degree_queue::PriorityQueue, v::Integer, dominated::BitArray{1}, - in_dom_set::BitArray{1} - ) where T <: Integer - + in_dom_set::BitArray{1}, +) where {T<:Integer} @inbounds if !dominated[v] dominated[v] = true - if !in_dom_set[v] + if !in_dom_set[v] degree_queue[v] -= 1 end @inbounds @simd for u in neighbors(g, v) - if !in_dom_set[u] + if !in_dom_set[u] degree_queue[u] -= ifelse(in_dom_set[u], 0, 1) end end @@ -46,12 +45,8 @@ Runtime: ``\\mathcal{O}((|V|+|E|)*log(|V|))`` Memory: ``\\mathcal{O}(|V|)`` Approximation Factor: `ln(maximum(degree(g)))+2` """ -function dominating_set( - g::AbstractGraph{T}, - alg::DegreeDominatingSet - ) where T <: Integer - - nvg = nv(g) +function dominating_set(g::AbstractGraph{T}, alg::DegreeDominatingSet) where {T<:Integer} + nvg = nv(g) in_dom_set = falses(nvg) dominated = falses(nvg) degree_queue = PriorityQueue(Base.Order.Reverse, enumerate(degree(g) .+ 1)) @@ -67,6 +62,6 @@ function dominating_set( update_dominated!(g, degree_queue, u, dominated, in_dom_set) end end - + return Graphs.findall!(in_dom_set, Vector{T}(undef, length_ds)) end diff --git a/src/dominatingset/minimal_dom_set.jl b/src/dominatingset/minimal_dom_set.jl index 22d33c886..a91837de0 100644 --- a/src/dominatingset/minimal_dom_set.jl +++ b/src/dominatingset/minimal_dom_set.jl @@ -5,13 +5,13 @@ struct MinimalDominatingSet end """ dominating_set(g, MinimalDominatingSet(); rng=nothing, seed=nothing) -Find a set of vertices that consitute a dominating set (all vertices in `g` are either adjacent to a vertex -in the set or is a vertex in the set) and it is not possible to delete a vertex from the set +Find a set of vertices that constitutes a dominating set (all vertices in `g` are either adjacent to a vertex +in the set or is a vertex in the set) and it is not possible to delete a vertex from the set without sacrificing the dominating property. ### Implementation Notes Initially, every vertex is in the dominating set. -In some random order, we check if the removal of a vertex from the set will destroy the +In some random order, we check if the removal of a vertex from the set will destroy the dominating property. If no, the vertex is removed from the dominating set. ### Performance @@ -21,15 +21,16 @@ Memory: ``\\mathcal{O}(|V|)`` ### Optional Arguments - `rng=nothing`: set the Random Number Generator. - If `seed >= 0`, a random generator is seeded with this value. -""" +""" function dominating_set( g::AbstractGraph{T}, alg::MinimalDominatingSet; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Integer + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Integer} rng = rng_from_rng_or_seed(rng, seed) - nvg = nv(g) - in_dom_set = trues(nvg) + nvg = nv(g) + in_dom_set = trues(nvg) length_ds = Int(nvg) dom_degree = degree(g) @inbounds @simd for v in vertices(g) @@ -37,8 +38,8 @@ function dominating_set( end for v in randperm(rng, nvg) - (dom_degree[v] == 0) && continue #It is not adjacent to any dominating vertex - #Check if any vertex is depending on v to be dominated + (dom_degree[v] == 0) && continue # It is not adjacent to any dominating vertex + # Check if any vertex is depending on v to be dominated dependent = findfirst(u -> !in_dom_set[u] && dom_degree[u] <= 1, neighbors(g, v)) (dependent != nothing) && continue @@ -46,6 +47,6 @@ function dominating_set( length_ds -= 1 dom_degree[neighbors(g, v)] .-= 1 end - + return Graphs.findall!(in_dom_set, Vector{T}(undef, length_ds)) end diff --git a/src/editdist.jl b/src/editdist.jl index ee67e7cb7..9c1f27b44 100644 --- a/src/editdist.jl +++ b/src/editdist.jl @@ -50,16 +50,19 @@ julia> edit_distance(g1, g2) (3.5, Tuple[(1, 2), (2, 1), (3, 0), (4, 3), (5, 0)]) ``` """ -function edit_distance(G₁::AbstractGraph, G₂::AbstractGraph; - insert_cost::Function=v -> 1.0, - delete_cost::Function=u -> 1.0, - subst_cost::Function=(u, v) -> 0.5, - heuristic::Function=DefaultEditHeuristic) - - # A* search heuristic +function edit_distance( + G₁::AbstractGraph, + G₂::AbstractGraph; + insert_cost::Function=v -> 1.0, + delete_cost::Function=u -> 1.0, + subst_cost::Function=(u, v) -> 0.5, + heuristic::Function=DefaultEditHeuristic, +) + + # A* search heuristic h(λ) = heuristic(λ, G₁, G₂) - # initialize open set + # initialize open set OPEN = PriorityQueue{Vector{Tuple},Float64}() for v in 1:nv(G₂) enqueue!(OPEN, [(1, v)], subst_cost(1, v) + h([(1, v)])) @@ -95,7 +98,8 @@ function edit_distance(G₁::AbstractGraph, G₂::AbstractGraph; end function is_complete_path(λ, G₁, G₂) - us = Set(); vs = Set() + us = Set() + vs = Set() for (u, v) in λ push!(us, u) push!(vs, v) @@ -113,7 +117,6 @@ function DefaultEditHeuristic(λ, G₁::AbstractGraph, G₂::AbstractGraph) return nv(G₂) - length(vs) end - #------------------------- # Edit path cost functions #------------------------- @@ -129,7 +132,7 @@ vertex v ∈ G₂. `p=1`: the p value for p-norm calculation. """ function MinkowskiCost(μ₁::AbstractVector, μ₂::AbstractVector; p::Real=1) - (u, v) -> norm(μ₁[u] - μ₂[v], p) + return (u, v) -> norm(μ₁[u] - μ₂[v], p) end """ @@ -142,5 +145,5 @@ Return value similar to [`MinkowskiCost`](@ref), but ensure costs smaller than 2 `τ=1`: value specifying half of the upper limit of the Minkowski cost. """ function BoundedMinkowskiCost(μ₁::AbstractVector, μ₂::AbstractVector; p::Real=1, τ::Real=1) - (u, v) -> 1 / (1 / (2τ) + exp(-norm(μ₁[u] - μ₂[v], p))) + return (u, v) -> 1 / (1 / (2τ) + exp(-norm(μ₁[u] - μ₂[v], p))) end diff --git a/src/graphcut/karger_min_cut.jl b/src/graphcut/karger_min_cut.jl index fcb1dbbcf..1fbd5af7d 100644 --- a/src/graphcut/karger_min_cut.jl +++ b/src/graphcut/karger_min_cut.jl @@ -18,8 +18,7 @@ If |V| < 2 then `cut[v] = 0` for all `v`. Runtime: O(|E|) Memory: O(|E|) """ -function karger_min_cut(g::AbstractGraph{T}) where T <: Integer - +function karger_min_cut(g::AbstractGraph{T}) where {T<:Integer} nvg = nv(g) nvg < 2 && return zeros(Int, nvg) nvg == 2 && return [1, 2] @@ -46,8 +45,9 @@ end Find the number of crossing edges in a cut of graph `g` where the cut is represented by the integer array, `cut`. """ -karger_cut_cost(g::AbstractGraph{T}, cut::Vector{<:Integer}) where T <: Integer = -count((e::Edge{T})->cut[src(e)] != cut[dst(e)], edges(g)) +function karger_cut_cost(g::AbstractGraph{T}, cut::Vector{<:Integer}) where {T<:Integer} + return count((e::Edge{T}) -> cut[src(e)] != cut[dst(e)], edges(g)) +end """ karger_cut_edges(g, cut) @@ -55,5 +55,6 @@ count((e::Edge{T})->cut[src(e)] != cut[dst(e)], edges(g)) Find the crossing edges in a cut of graph `g` where the cut is represented by the integer array, `cut`. """ -karger_cut_edges(g::AbstractGraph{T}, cut::Vector{<:Integer}) where T <: Integer = -[e for e in edges(g) if cut[src(e)] != cut[dst(e)]] +function karger_cut_edges(g::AbstractGraph{T}, cut::Vector{<:Integer}) where {T<:Integer} + return [e for e in edges(g) if cut[src(e)] != cut[dst(e)]] +end diff --git a/src/graphcut/normalized_cut.jl b/src/graphcut/normalized_cut.jl index 31bbb2fec..f5f48c2ff 100644 --- a/src/graphcut/normalized_cut.jl +++ b/src/graphcut/normalized_cut.jl @@ -1,5 +1,5 @@ using ArnoldiMethod -#computes normalized cut cost for partition `cut` +# computes normalized cut cost for partition `cut` function _normalized_cut_cost(cut, W::AbstractMatrix, D) cut_cost = zero(eltype(W)) for j in axes(W, 2) @@ -18,7 +18,7 @@ function _normalized_cut_cost(cut, W::SparseMatrixCSC, D) rows = rowvals(W) vals = nonzeros(W) n = size(W, 2) - for i = 1:n + for i in 1:n for j in nzrange(W, i) row = rows[j] if cut[i] != cut[row] @@ -90,10 +90,13 @@ function _partition_weightmx(cut, W::SparseMatrixCSC) rows = rowvals(W) vals = nonzeros(W) - I1 = Vector{Int}(); I2 = Vector{Int}() - J1 = Vector{Int}(); J2 = Vector{Int}() - V1 = Vector{Float64}(); V2 = Vector{Float64}() - for i = 1:nv + I1 = Vector{Int}() + I2 = Vector{Int}() + J1 = Vector{Int}() + J2 = Vector{Int}() + V1 = Vector{Float64}() + V2 = Vector{Float64}() + for i in 1:nv for j in nzrange(W, i) row = rows[j] if cut[i] == cut[row] == false @@ -114,26 +117,26 @@ end function _recursive_normalized_cut(W, thres=thres, num_cuts=num_cuts) m, n = size(W) - D = Diagonal(vec(sum(W, dims=2))) + D = Diagonal(vec(sum(W; dims=2))) m == 1 && return [1] - #get eigenvector corresponding to second smallest eigenvalue + # get eigenvector corresponding to second smallest eigenvalue # v = eigs(D-W, D, nev=2, which=SR())[2][:,2] # At least some versions of ARPACK have a bug, this is a workaround invDroot = sqrt.(inv(D)) # equal to Cholesky factorization for diagonal D if n > 12 - λ, Q = eigs(invDroot' * (D - W) * invDroot, nev=12, which=SR()) - ret = real(Q[:,2]) + λ, Q = eigs(invDroot' * (D - W) * invDroot; nev=12, which=SR()) + ret = real(Q[:, 2]) else - ret = eigen(Matrix(invDroot' * (D - W) * invDroot)).vectors[:,2] + ret = eigen(Matrix(invDroot' * (D - W) * invDroot)).vectors[:, 2] end v = invDroot * ret - #perform n-cuts with different partitions of v and find best one + # perform n-cuts with different partitions of v and find best one min_cost = Inf best_thres = -1 - for t in range(minimum(v), stop=maximum(v), length=num_cuts) + for t in range(minimum(v); stop=maximum(v), length=num_cuts) cut = v .> t cost = _normalized_cut_cost(cut, W, D) if cost < min_cost @@ -143,7 +146,7 @@ function _recursive_normalized_cut(W, thres=thres, num_cuts=num_cuts) end if min_cost < thres - #split graph, compute normalized_cut for each subgraph recursively and merge indices. + # split graph, compute normalized_cut for each subgraph recursively and merge indices. cut = v .> best_thres W1, W2, vmap1, vmap2 = _partition_weightmx(cut, W) labels1 = _recursive_normalized_cut(W1, thres, num_cuts) @@ -181,11 +184,11 @@ It is important to identify a good threshold for your application. A bisection s ### References "Normalized Cuts and Image Segmentation" - Jianbo Shi and Jitendra Malik """ -function normalized_cut(g::AbstractGraph, +function normalized_cut( + g::AbstractGraph, thres::Real, W::AbstractMatrix{T}=adjacency_matrix(g), - num_cuts::Int=10) where T <: Real - + num_cuts::Int=10, +) where {T<:Real} return _recursive_normalized_cut(W, thres, num_cuts) end - diff --git a/src/independentset/degree_ind_set.jl b/src/independentset/degree_ind_set.jl index 78cc01331..7ca403cf0 100644 --- a/src/independentset/degree_ind_set.jl +++ b/src/independentset/degree_ind_set.jl @@ -18,14 +18,10 @@ adjacent to the fewest valid vertices in the independent set until all vertices Runtime: O((|V|+|E|)*log(|V|)) Memory: O(|V|) """ -function independent_set( - g::AbstractGraph{T}, - alg::DegreeIndependentSet - ) where T <: Integer - - nvg = nv(g) +function independent_set(g::AbstractGraph{T}, alg::DegreeIndependentSet) where {T<:Integer} + nvg = nv(g) ind_set = Vector{T}() - sizehint!(ind_set, nvg) + sizehint!(ind_set, nvg) deleted = falses(nvg) degree_queue = PriorityQueue(enumerate(degree(g))) @@ -39,7 +35,7 @@ function independent_set( deleted[u] && continue deleted[u] = true @inbounds @simd for w in neighbors(g, u) - if !deleted[w] + if !deleted[w] degree_queue[w] -= 1 end end diff --git a/src/independentset/maximal_ind_set.jl b/src/independentset/maximal_ind_set.jl index 0c152a4bf..410f9629d 100644 --- a/src/independentset/maximal_ind_set.jl +++ b/src/independentset/maximal_ind_set.jl @@ -24,13 +24,13 @@ Approximation Factor: maximum(degree(g))+1 function independent_set( g::AbstractGraph{T}, alg::MaximalIndependentSet; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing - ) where T <: Integer - + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Integer} rng = rng_from_rng_or_seed(rng, seed) nvg = nv(g) ind_set = Vector{T}() - sizehint!(ind_set, nvg) + sizehint!(ind_set, nvg) deleted = falses(nvg) for v in randperm(rng, nvg) diff --git a/src/interface.jl b/src/interface.jl index 43520f355..05464848c 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -11,7 +11,9 @@ struct NotImplementedError{M} <: Exception NotImplementedError(m::M) where {M} = new{M}(m) end -Base.showerror(io::IO, ie::NotImplementedError) = print(io, "method $(ie.m) not implemented.") +function Base.showerror(io::IO, ie::NotImplementedError) + return print(io, "method $(ie.m) not implemented.") +end _NI(m) = throw(NotImplementedError(m)) @@ -36,10 +38,8 @@ An abstract type representing a graph. """ abstract type AbstractGraph{T} end - @traitdef IsDirected{G<:AbstractGraph} -@traitimpl IsDirected{G} <- is_directed(G) - +@traitimpl IsDirected{G} < -is_directed(G) # # Interface for AbstractEdges @@ -107,7 +107,6 @@ reverse(e::AbstractEdge) = _NI("reverse") ==(e1::AbstractEdge, e2::AbstractEdge) = _NI("==") - # # Interface for AbstractGraphs # @@ -227,7 +226,7 @@ true ``` """ is_directed(::G) where {G} = is_directed(G) -is_directed(::Type{T}) where T = _NI("is_directed") +is_directed(::Type{T}) where {T} = _NI("is_directed") """ has_vertex(g, v) @@ -338,4 +337,4 @@ julia> zero(g) """ zero(::Type{<:AbstractGraph}) = _NI("zero") -zero(g::G) where {G<: AbstractGraph} = zero(G) +zero(g::G) where {G<:AbstractGraph} = zero(G) diff --git a/src/linalg/LinAlg.jl b/src/linalg/LinAlg.jl index d240d0ff5..fb5b75e51 100644 --- a/src/linalg/LinAlg.jl +++ b/src/linalg/LinAlg.jl @@ -10,7 +10,6 @@ import LinearAlgebra: Diagonal, diag, issymmetric, mul! using ..Graphs - import Base: convert, size, eltype, ndims, ==, *, length export convert, @@ -35,12 +34,10 @@ export convert, prescalefactor, postscalefactor, perron, - non_backtracking_matrix, Nonbacktracking, contract!, contract, - adjacency_matrix, laplacian_matrix, incidence_matrix, @@ -51,14 +48,14 @@ export convert, eigs function eigs(A; kwargs...) - schr =partialschur(A; kwargs...) + schr = partialschur(A; kwargs...) vals, vectors = partialeigen(schr[1]) reved = (kwargs[:which] == LR() || kwargs[:which] == LM()) k::Int = get(kwargs, :nev, length(vals)) k = min(k, length(vals)) perm = collect(1:k) - if vals[1] isa(Real) - perm = sortperm(vals, rev=reved) + if vals[1] isa (Real) + perm = sortperm(vals; rev=reved) perm = perm[1:k] end λ = vals[perm] @@ -70,6 +67,4 @@ include("./graphmatrices.jl") include("./spectral.jl") include("./nonbacktracking.jl") - end - diff --git a/src/linalg/graphmatrices.jl b/src/linalg/graphmatrices.jl index 43c2ddc9a..5aaeb302f 100644 --- a/src/linalg/graphmatrices.jl +++ b/src/linalg/graphmatrices.jl @@ -3,11 +3,10 @@ const SparseMatrix{T} = SparseMatrixCSC{T,Int} """ GraphMatrix{T} -An abstract type to allow opertions on any type of graph matrix +An abstract type to allow operations on any type of graph matrix """ abstract type GraphMatrix{T} end - """ Adjacency{T} @@ -34,12 +33,11 @@ struct CombinatorialAdjacency{T,S,V} <: Adjacency{T} D::V end -function CombinatorialAdjacency(A::SparseMatrix{T}) where T - D = vec(sum(A, dims=1)) +function CombinatorialAdjacency(A::SparseMatrix{T}) where {T} + D = vec(sum(A; dims=1)) return CombinatorialAdjacency{T,SparseMatrix{T},typeof(D)}(A, D) end - """ NormalizedAdjacency{T} @@ -52,7 +50,7 @@ struct NormalizedAdjacency{T} <: Adjacency{T} scalefactor::Vector{T} end function NormalizedAdjacency(adjmat::CombinatorialAdjacency) - sf = adjmat.D.^(-1 / 2) + sf = adjmat.D .^ (-1 / 2) return NormalizedAdjacency(adjmat, sf) end @@ -64,10 +62,9 @@ A transition matrix for the random walk. struct StochasticAdjacency{T} <: Adjacency{T} A::CombinatorialAdjacency{T} scalefactor::Vector{T} - end function StochasticAdjacency(adjmat::CombinatorialAdjacency) - sf = adjmat.D.^(-1) + sf = adjmat.D .^ (-1) return StochasticAdjacency(adjmat, sf) end @@ -81,7 +78,7 @@ struct AveragingAdjacency{T} <: Adjacency{T} scalefactor::Vector{T} end function AveragingAdjacency(adjmat::CombinatorialAdjacency) - sf = adjmat.D.^(-1) + sf = adjmat.D .^ (-1) return AveragingAdjacency(adjmat, sf) end @@ -113,7 +110,6 @@ Broadcast.broadcasted(::typeof(*), ::Noop, x) = x Diagonal(::Noop) = Noop() - ==(g::GraphMatrix, h::GraphMatrix) = typeof(g) == typeof(h) && (g.A == h.A) postscalefactor(::Adjacency) = Noop() @@ -128,7 +124,6 @@ prescalefactor(adjmat::NormalizedAdjacency) = adjmat.scalefactor prescalefactor(adjmat::StochasticAdjacency) = adjmat.scalefactor - struct CombinatorialLaplacian{T} <: Laplacian{T} A::CombinatorialAdjacency{T} end @@ -188,20 +183,17 @@ degrees(mat::GraphMatrix) = degrees(adjacency(mat)) adjacency(lapl::Laplacian) = lapl.A adjacency(lapl::GraphMatrix) = lapl.A - - convert(::Type{CombinatorialAdjacency}, adjmat::Adjacency) = adjmat.A convert(::Type{CombinatorialAdjacency}, adjmat::CombinatorialAdjacency) = adjmat - -function sparse(lapl::M) where M <: Laplacian +function sparse(lapl::M) where {M<:Laplacian} adjmat = adjacency(lapl) A = sparse(adjmat) L = sparse(Diagonal(diag(lapl))) - A return L end -function SparseMatrix(lapl::M) where M <: GraphMatrix +function SparseMatrix(lapl::M) where {M<:GraphMatrix} return sparse(lapl) end @@ -210,9 +202,7 @@ function sparse(adjmat::Adjacency) return Diagonal(prescalefactor(adjmat)) * (A * Diagonal(postscalefactor(adjmat))) end - - -function convert(::Type{SparseMatrix{T}}, lapl::Laplacian{T}) where T +function convert(::Type{SparseMatrix{T}}, lapl::Laplacian{T}) where {T} adjmat = adjacency(lapl) A = convert(SparseMatrix{T}, adjmat) L = sparse(Diagonal(diag(lapl))) - A @@ -224,17 +214,17 @@ diag(lapl::Laplacian) = ones(size(lapl)[2]) *(x::AbstractArray, ::Noop) = x *(::Noop, x) = x -*(adjmat::Adjacency{T}, x::AbstractVector{T}) where T <: Number = - postscalefactor(adjmat) .* (adjmat.A * (prescalefactor(adjmat) .* x)) - -*(adjmat::CombinatorialAdjacency{T}, x::AbstractVector{T}) where T <: Number = - adjmat.A * x +function *(adjmat::Adjacency{T}, x::AbstractVector{T}) where {T<:Number} + return postscalefactor(adjmat) .* (adjmat.A * (prescalefactor(adjmat) .* x)) +end -*(lapl::Laplacian{T}, x::AbstractVector{T}) where T <: Number = - (diag(lapl) .* x) - (adjacency(lapl) * x) +*(adjmat::CombinatorialAdjacency{T}, x::AbstractVector{T}) where {T<:Number} = adjmat.A * x +function *(lapl::Laplacian{T}, x::AbstractVector{T}) where {T<:Number} + return (diag(lapl) .* x) - (adjacency(lapl) * x) +end -function *(adjmat::PunchedAdjacency{T}, x::AbstractVector{T}) where T <: Number +function *(adjmat::PunchedAdjacency{T}, x::AbstractVector{T}) where {T<:Number} y = adjmat.A * x return y - dot(adjmat.perron, y) * adjmat.perron end @@ -275,7 +265,6 @@ function mul!(Y, lapl::Laplacian, B) return Y end - """ symmetrize(A::SparseMatrix, which=:or) @@ -312,17 +301,15 @@ Use `:sum` for weighted graphs. ### Implementation Notes Only works on [`Adjacency`](@ref) because the normalizations don't commute with symmetrization. """ -symmetrize(adjmat::CombinatorialAdjacency, which=:or) = - CombinatorialAdjacency(symmetrize(adjmat.A, which)) - +function symmetrize(adjmat::CombinatorialAdjacency, which=:or) + return CombinatorialAdjacency(symmetrize(adjmat.A, which)) +end # per #564 # @deprecate mul!(Y, A::Noop, B) None @deprecate convert(::Type{Adjacency}, lapl::Laplacian) None @deprecate convert(::Type{SparseMatrix}, adjmat::GraphMatrix) sparse(adjmat) - - """ LinAlg diff --git a/src/linalg/nonbacktracking.jl b/src/linalg/nonbacktracking.jl index 1a26e4ad9..58cf051e8 100644 --- a/src/linalg/nonbacktracking.jl +++ b/src/linalg/nonbacktracking.jl @@ -61,7 +61,7 @@ Additionally the `contract!(vertexspace, nbt, edgespace)` method takes vectors represented in the domain of ``B`` and represents them in the domain of the adjacency matrix of `g`. """ -struct Nonbacktracking{G <: AbstractGraph} +struct Nonbacktracking{G<:AbstractGraph} g::G edgeidmap::Dict{Edge,Int} m::Int @@ -88,7 +88,7 @@ size(nbt::Nonbacktracking, i::Number) = size(nbt)[i] eltype(nbt::Nonbacktracking) = Float64 issymmetric(nbt::Nonbacktracking) = false -function *(nbt::Nonbacktracking, x::Vector{T}) where T <: Number +function *(nbt::Nonbacktracking, x::Vector{T}) where {T<:Number} length(x) == nbt.m || error("dimension mismatch") y = zeros(T, length(x)) for (e, u) in nbt.edgeidmap @@ -131,7 +131,7 @@ sparse(nbt::Nonbacktracking) = sparse(coo_sparse(nbt)..., nbt.m, nbt.m) function *(nbt::Nonbacktracking, x::AbstractMatrix) y = zero(x) - for i in 1:nbt.m + for i in 1:(nbt.m) y[:, i] = nbt * x[:, i] end return y @@ -144,13 +144,13 @@ The mutating version of `contract(nbt, edgespace)`. Modifies `vertexspace`. """ function contract!(vertexspace::Vector, nbt::Nonbacktracking, edgespace::Vector) T = eltype(nbt.g) - for i = one(T):nv(nbt.g), j in neighbors(nbt.g, i) + for i in one(T):nv(nbt.g), j in neighbors(nbt.g, i) u = nbt.edgeidmap[i > j ? Edge(j, i) : Edge(i, j)] vertexspace[i] += edgespace[u] end end -#TODO: documentation needs work. sbromberger 20170326 +# TODO: documentation needs work. sbromberger 20170326 """ contract(nbt, edgespace) diff --git a/src/linalg/spectral.jl b/src/linalg/spectral.jl index a4a8f5417..4f6f3605e 100644 --- a/src/linalg/spectral.jl +++ b/src/linalg/spectral.jl @@ -36,15 +36,17 @@ function adjacency_matrix(g::AbstractGraph, T::DataType=Int; dir::Symbol=:out) end end -@generated function _find_correct_type(g::AbstractGraph{T}) where T +@generated function _find_correct_type(g::AbstractGraph{T}) where {T} TT = widen(T) if typemax(TT) >= typemax(Int64) TT = Int64 end return :($TT) - end +end -function _adjacency_matrix(g::AbstractGraph, T::DataType, neighborfn::Function, nzmult::Int=1) +function _adjacency_matrix( + g::AbstractGraph, T::DataType, neighborfn::Function, nzmult::Int=1 +) n_v = nv(g) nz = ne(g) * (is_directed(g) ? 1 : 2) * nzmult TT = _find_correct_type(g) @@ -87,13 +89,15 @@ for a graph `g`, indexed by `[u, v]` vertices. `T` defaults to `Int` for both gr For undirected graphs, `dir` defaults to `:out`; for directed graphs, `dir` defaults to `:both`. """ -function laplacian_matrix(g::AbstractGraph{U}, T::DataType=Int; dir::Symbol=:unspec) where U +function laplacian_matrix( + g::AbstractGraph{U}, T::DataType=Int; dir::Symbol=:unspec +) where {U} if dir == :unspec dir = is_directed(g) ? :both : :out end A = adjacency_matrix(g, T; dir=dir) s = sum(A; dims=2) - D = convert(SparseMatrixCSC{T, U}, spdiagm(0 => s[:])) + D = convert(SparseMatrixCSC{T,U}, spdiagm(0 => s[:])) return D - A end @@ -114,7 +118,9 @@ Converts the matrix to dense with ``nv^2`` memory usage. Use `eigvals(Matrix(laplacian_matrix(g, args...)))` to compute some of the eigenvalues/eigenvectors. """ -laplacian_spectrum(g::AbstractGraph, T::DataType=Int; dir::Symbol=:unspec) = eigvals(Matrix(laplacian_matrix(g, T; dir=dir))) +function laplacian_spectrum(g::AbstractGraph, T::DataType=Int; dir::Symbol=:unspec) + return eigvals(Matrix(laplacian_matrix(g, T; dir=dir))) +end """ adjacency_spectrum(g[, T=Int; dir=:unspec]) @@ -135,7 +141,7 @@ eigenvalues/eigenvectors. """ function adjacency_spectrum(g::AbstractGraph, T::DataType=Int; dir::Symbol=:unspec) if dir == :unspec - dir = is_directed(g) ? :both : :out + dir = is_directed(g) ? :both : :out end return eigvals(Matrix(adjacency_matrix(g, T; dir=dir))) end @@ -160,8 +166,7 @@ function incidence_matrix(g::AbstractGraph, T::DataType=Int; oriented=false) I = vcat(src.(edges(g)), dst.(edges(g))) J = vcat(1:n_e, 1:n_e) V = vcat( - (isdir || oriented) ? -fill(one(T), n_e) : fill(one(T), n_e), - fill(one(T), n_e), + (isdir || oriented) ? -fill(one(T), n_e) : fill(one(T), n_e), fill(one(T), n_e) ) return sparse(I, J, V, nv(g), ne(g)) end @@ -171,7 +176,7 @@ end Compute the spectral distance between undirected n-vertex graphs `G₁` and `G₂` using the top `k` greatest eigenvalues. -If `k` is ommitted, uses full spectrum. +If `k` is omitted, uses full spectrum. ### References - JOVANOVIC, I.; STANIC, Z., 2014. Spectral Distances of Graphs Based on their Different Matrix Representations @@ -179,18 +184,29 @@ If `k` is ommitted, uses full spectrum. function spectral_distance end # can't use Traitor syntax here (https://github.com/mauro3/SimpleTraits.jl/issues/36) -@traitfn function spectral_distance(G₁::G, G₂::G, k::Integer) where {G <: AbstractGraph; !IsDirected{G}} +@traitfn function spectral_distance( + G₁::G, G₂::G, k::Integer +) where {G <: AbstractGraph; !IsDirected{G}} A₁ = adjacency_matrix(G₁) A₂ = adjacency_matrix(G₂) - λ₁ = k < nv(G₁) - 1 ? eigs(A₁, nev=k, which=LR())[1] : eigvals(Matrix(A₁))[end:-1:(end - (k - 1))] - λ₂ = k < nv(G₂) - 1 ? eigs(A₂, nev=k, which=LR())[1] : eigvals(Matrix(A₂))[end:-1:(end - (k - 1))] + λ₁ = if k < nv(G₁) - 1 + eigs(A₁; nev=k, which=LR())[1] + else + eigvals(Matrix(A₁))[end:-1:(end - (k - 1))] + end + λ₂ = if k < nv(G₂) - 1 + eigs(A₂; nev=k, which=LR())[1] + else + eigvals(Matrix(A₂))[end:-1:(end - (k - 1))] + end return sum(abs, (λ₁ - λ₂)) end # can't use Traitor syntax here (https://github.com/mauro3/SimpleTraits.jl/issues/36) @traitfn function spectral_distance(G₁::G, G₂::G) where {G <: AbstractGraph; !IsDirected{G}} - nv(G₁) == nv(G₂) || throw(ArgumentError("Spectral distance not defined for |G₁| != |G₂|")) + nv(G₁) == nv(G₂) || + throw(ArgumentError("Spectral distance not defined for |G₁| != |G₂|")) return spectral_distance(G₁, G₂, nv(G₁)) end diff --git a/src/operators.jl b/src/operators.jl index daa597b66..5ee4c952b 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -31,8 +31,8 @@ Edge 5 => 3 function complement(g::Graph) gnv = nv(g) h = SimpleGraph(gnv) - for i = 1:gnv - for j = (i + 1):gnv + for i in 1:gnv + for j in (i + 1):gnv if !has_edge(g, i, j) add_edge!(h, i, j) end @@ -75,7 +75,7 @@ Edge 5 => 4 ``` """ function reverse end -@traitfn function reverse(g::G::IsDirected) where G<:AbstractSimpleGraph +@traitfn function reverse(g::G::IsDirected) where {G<:AbstractSimpleGraph} gnv = nv(g) gne = ne(g) h = SimpleDiGraph(gnv) @@ -92,7 +92,7 @@ In-place reverse of a directed graph (modifies the original graph). See [`reverse`](@ref) for a non-modifying version. """ function reverse! end -@traitfn function reverse!(g::G::IsDirected) where G<:AbstractSimpleGraph +@traitfn function reverse!(g::G::IsDirected) where {G<:AbstractSimpleGraph} g.fadjlist, g.badjlist = g.badjlist, g.fadjlist return g end @@ -128,7 +128,7 @@ Edge 7 => 8 Edge 8 => 6 ``` """ -function blockdiag(g::T, h::T) where T <: AbstractGraph +function blockdiag(g::T, h::T) where {T<:AbstractGraph} gnv = nv(g) r = T(gnv + nv(h)) for e in edges(g) @@ -161,7 +161,7 @@ Edge 2 => 3 Edge 3 => 1 ``` """ -function intersect(g::T, h::T) where T <: AbstractGraph +function intersect(g::T, h::T) where {T<:AbstractGraph} gnv = nv(g) hnv = nv(h) @@ -193,7 +193,7 @@ Edge 4 => 5 Edge 5 => 4 ``` """ -function difference(g::T, h::T) where T <: AbstractGraph +function difference(g::T, h::T) where {T<:AbstractGraph} gnv = nv(g) hnv = nv(h) @@ -236,7 +236,7 @@ julia> collect(edges(f)) Edge 2 => 3 ``` """ -function symmetric_difference(g::T, h::T) where T <: AbstractGraph +function symmetric_difference(g::T, h::T) where {T<:AbstractGraph} gnv = nv(g) hnv = nv(h) @@ -287,7 +287,7 @@ julia> collect(edges(f)) Edge 4 => 5 ``` """ -function union(g::T, h::T) where T <: AbstractSimpleGraph +function union(g::T, h::T) where {T<:AbstractSimpleGraph} gnv = nv(g) hnv = nv(h) @@ -305,7 +305,6 @@ function union(g::T, h::T) where T <: AbstractSimpleGraph return r end - """ join(g, h) @@ -336,17 +335,16 @@ julia> collect(edges(g)) Edge 4 => 5 ``` """ -function join(g::T, h::T) where T <: AbstractGraph +function join(g::T, h::T) where {T<:AbstractGraph} r = blockdiag(g, h) for i in vertices(g) - for j = (nv(g) + 1):(nv(g) + nv(h)) + for j in (nv(g) + 1):(nv(g) + nv(h)) add_edge!(r, i, j) end end return r end - """ crosspath(len::Integer, g::Graph) @@ -382,7 +380,9 @@ julia> collect(edges(g)) """ function crosspath end # see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax -@traitfn function crosspath(len::Integer, g::AG::(!IsDirected)) where {T, AG <: AbstractGraph{T}} +@traitfn function crosspath( + len::Integer, g::AG::(!IsDirected) +) where {T,AG<:AbstractGraph{T}} p = path_graph(len) h = Graph{T}(p) return cartesian_product(h, g) @@ -392,7 +392,7 @@ end # """Provides multiplication of a graph `g` by a vector `v` such that spectral # graph functions in [GraphMatrices.jl](https://github.com/jpfairbanks/GraphMatrices.jl) can utilize Graphs natively. # """ -function *(g::Graph, v::Vector{T}) where T <: Real +function *(g::Graph, v::Vector{T}) where {T<:Real} length(v) == nv(g) || throw(ArgumentError("Vector size must equal number of vertices")) y = zeros(T, nv(g)) for e in edges(g) @@ -404,7 +404,7 @@ function *(g::Graph, v::Vector{T}) where T <: Real return y end -function *(g::DiGraph, v::Vector{T}) where T <: Real +function *(g::DiGraph, v::Vector{T}) where {T<:Real} length(v) == nv(g) || throw(ArgumentError("Vector size must equal number of vertices")) y = zeros(T, nv(g)) for e in edges(g) @@ -447,7 +447,6 @@ function sum(g::AbstractGraph, dim::Int) throw(ArgumentError("dimension must be <= 2")) end - size(g::AbstractGraph) = (nv(g), nv(g)) """ size(g, i) @@ -531,12 +530,12 @@ julia> collect(edges(g)) Edge 8 => 9 ``` """ -function cartesian_product(g::G, h::G) where G <: AbstractGraph +function cartesian_product(g::G, h::G) where {G<:AbstractGraph} z = G(nv(g) * nv(h)) id(i, j) = (i - 1) * nv(h) + j for e in edges(g) i1, i2 = Tuple(e) - for j = 1:nv(h) + for j in 1:nv(h) add_edge!(z, id(i1, j), id(i2, j)) end end @@ -579,7 +578,7 @@ julia> collect(edges(g)) Edge 3 => 8 ``` """ -function tensor_product(g::G, h::G) where G <: AbstractGraph +function tensor_product(g::G, h::G) where {G<:AbstractGraph} z = G(nv(g) * nv(h)) id(i, j) = (i - 1) * nv(h) + j undirected = !is_directed(g) @@ -596,7 +595,6 @@ function tensor_product(g::G, h::G) where G <: AbstractGraph return z end - ## subgraphs ### """ @@ -636,7 +634,9 @@ julia> sg, vmap = induced_subgraph(g, elist) julia> @assert sg == g[elist] ``` """ -function induced_subgraph(g::T, vlist::AbstractVector{U}) where T <: AbstractGraph where U <: Integer +function induced_subgraph( + g::T, vlist::AbstractVector{U} +) where {T<:AbstractGraph} where {U<:Integer} allunique(vlist) || throw(ArgumentError("Vertices in subgraph list must be unique")) h = T(length(vlist)) newvid = Dict{U,U}() @@ -664,7 +664,9 @@ function induced_subgraph(g::AbstractGraph, vlist::AbstractVector{Bool}) return induced_subgraph(g, findall(vlist)) end -function induced_subgraph(g::AG, elist::AbstractVector{U}) where AG <: AbstractGraph{T} where T where U <: AbstractEdge +function induced_subgraph( + g::AG, elist::AbstractVector{U} +) where {AG<:AbstractGraph{T}} where {T} where {U<:AbstractEdge} h = zero(g) newvid = Dict{T,T}() vmap = Vector{T}() @@ -683,7 +685,6 @@ function induced_subgraph(g::AG, elist::AbstractVector{U}) where AG <: AbstractG return h, vmap end - """ g[iter] @@ -692,7 +693,6 @@ Equivalent to [`induced_subgraph`](@ref)`(g, iter)[1]`. """ getindex(g::AbstractGraph, iter) = induced_subgraph(g, iter)[1] - """ egonet(g, v, d, distmx=weights(g)) @@ -704,10 +704,15 @@ This is equivalent to [`induced_subgraph`](@ref)`(g, neighborhood(g, v, d, dir=d - `dir=:out`: if `g` is directed, this argument specifies the edge direction with respect to `v` (i.e. `:in` or `:out`). """ -egonet(g::AbstractGraph{T}, v::Integer, d::Integer, distmx::AbstractMatrix{U}=weights(g); dir=:out) where T <: Integer where U <: Real = - g[neighborhood(g, v, d, distmx, dir=dir)] - - +function egonet( + g::AbstractGraph{T}, + v::Integer, + d::Integer, + distmx::AbstractMatrix{U}=weights(g); + dir=:out, +) where {T<:Integer} where {U<:Real} + return g[neighborhood(g, v, d, distmx; dir=dir)] +end """ compute_shifts(n::Int, x::AbstractArray) @@ -754,17 +759,19 @@ function merge_vertices(g::AbstractSimpleGraph, vs) nvnew = nv(g) - length(vs) nvnew <= nv(g) || return g - merged_vertex > 0 || throw(ArgumentError("invalid vertex ID: $merged_vertex in list of vertices to be merged")) + merged_vertex > 0 || throw( + ArgumentError("invalid vertex ID: $merged_vertex in list of vertices to be merged"), + ) vs[end] <= nv(g) || throw(ArgumentError("vertex $(vs[end]) not found in graph")) # TODO 0.7: change to DomainError? new_vertex_ids = collect(vertices(g)) .- compute_shifts(nv(g), vs) new_vertex_ids[vs] .= merged_vertex - #if v in vs then labels[v] == v0 else labels[v] == v + # if v in vs then labels[v] == v0 else labels[v] == v newg = SimpleGraph(nvnew) for e in edges(g) u, w = src(e), dst(e) - if new_vertex_ids[u] != new_vertex_ids[w] #not a new self loop + if new_vertex_ids[u] != new_vertex_ids[w] # not a new self loop add_edge!(newg, new_vertex_ids[u], new_vertex_ids[w]) end end @@ -811,11 +818,13 @@ julia> collect(edges(g)) Edge 3 => 4 ``` """ -function merge_vertices!(g::Graph{T}, vs::Vector{U} where U <: Integer) where T +function merge_vertices!(g::Graph{T}, vs::Vector{U} where {U<:Integer}) where {T} vs = unique!(sort(vs)) (merged_vertex, vm) = extrema(vs) - merged_vertex > 0 || throw(ArgumentError("invalid vertex ID: $merged_vertex in list of vertices to be merged")) + merged_vertex > 0 || throw( + ArgumentError("invalid vertex ID: $merged_vertex in list of vertices to be merged"), + ) vm <= nv(g) || throw(ArgumentError("vertex $vm not found in graph")) # TODO 0.7: change to DomainError? new_vertex_ids = collect(vertices(g)) .- compute_shifts(nv(g), vs[2:end]) @@ -830,7 +839,7 @@ function merge_vertices!(g::Graph{T}, vs::Vector{U} where U <: Integer) where T end g.fadjlist[new_vertex_ids[i]] = sort!(collect(nbrs_to_rewire)) - # Collect connections to new merged vertex + # Collect connections to new merged vertex else nbrs_to_merge = Set{T}() for j in vs, e in outneighbors(g, j) @@ -842,9 +851,8 @@ function merge_vertices!(g::Graph{T}, vs::Vector{U} where U <: Integer) where T end end - # Drop excess vertices - g.fadjlist = g.fadjlist[firstindex(g.fadjlist):(end - length(vs)+1)] + g.fadjlist = g.fadjlist[firstindex(g.fadjlist):(end - length(vs) + 1)] # Correct edge counts g.ne = sum(degree(g, i) for i in vertices(g)) / 2 diff --git a/src/persistence/common.jl b/src/persistence/common.jl index fb35a559f..3bd7fd673 100644 --- a/src/persistence/common.jl +++ b/src/persistence/common.jl @@ -19,7 +19,6 @@ loadgraph(fn::AbstractString) = loadgraph(fn, "graph", LGFormat()) loadgraph(fn::AbstractString, gname::AbstractString) = loadgraph(fn, gname, LGFormat()) loadgraph(fn::AbstractString, format::AbstractGraphFormat) = loadgraph(fn, "graph", format) - """ loadgraphs(file, format=LGFormat()) @@ -57,7 +56,6 @@ function auto_decompress(io::IO) return io end - """ savegraph(file, g, gname="graph", format=LGFormat) @@ -67,14 +65,23 @@ Return the number of graphs written. ### Implementation Notes The default graph name assigned to `gname` may change in the future. """ -function savegraph(fn::AbstractString, g::AbstractGraph, gname::AbstractString, - format::AbstractGraphFormat; compress=nothing - ) +function savegraph( + fn::AbstractString, + g::AbstractGraph, + gname::AbstractString, + format::AbstractGraphFormat; + compress=nothing, +) if compress !== nothing if !compress - @info("Note: the `compress` keyword is no longer supported in Graphs. Saving uncompressed.") + @info( + "Note: the `compress` keyword is no longer supported in Graphs. Saving uncompressed." + ) else - Base.depwarn("Saving compressed graphs is no longer supported in Graphs. Use `LGCompressedFormat()` from the `GraphIO.jl` package instead. Saving uncompressed.", :savegraph) + Base.depwarn( + "Saving compressed graphs is no longer supported in Graphs. Use `LGCompressedFormat()` from the `GraphIO.jl` package instead. Saving uncompressed.", + :savegraph, + ) end end io = open(fn, "w") @@ -88,11 +95,16 @@ function savegraph(fn::AbstractString, g::AbstractGraph, gname::AbstractString, end # without graph name -savegraph(fn::AbstractString, g::AbstractGraph, format::AbstractGraphFormat; compress=nothing) = - savegraph(fn, g, "graph", format, compress=compress) +function savegraph( + fn::AbstractString, g::AbstractGraph, format::AbstractGraphFormat; compress=nothing +) + return savegraph(fn, g, "graph", format; compress=compress) +end # without format - default to LGFormat() -savegraph(fn::AbstractString, g::AbstractSimpleGraph; compress=nothing) = savegraph(fn, g, "graph", LGFormat(), compress=compress) +function savegraph(fn::AbstractString, g::AbstractSimpleGraph; compress=nothing) + return savegraph(fn, g, "graph", LGFormat(); compress=compress) +end """ savegraph(file, g, d, format=LGFormat) @@ -103,12 +115,15 @@ Return the number of graphs written. ### Implementation Notes Will only work if the file format supports multiple graph types. """ -function savegraph(fn::AbstractString, d::Dict{T,U}, - format::AbstractGraphFormat; compress=nothing) where T <: AbstractString where U <: AbstractGraph - compress === nothing || - Base.depwarn("Saving compressed graphs is no longer supported in Graphs. Use `LGCompressedFormat()` from the `GraphIO.jl` package instead. Saving uncompressed.", :savegraph) - io = open(fn, "w") - +function savegraph( + fn::AbstractString, d::Dict{T,U}, format::AbstractGraphFormat; compress=nothing +) where {T<:AbstractString} where {U<:AbstractGraph} + compress === nothing || Base.depwarn( + "Saving compressed graphs is no longer supported in Graphs. Use `LGCompressedFormat()` from the `GraphIO.jl` package instead. Saving uncompressed.", + :savegraph, + ) + io = open(fn, "w") + try return savegraph(io, d, format) catch diff --git a/src/persistence/lg.jl b/src/persistence/lg.jl index 8bc587e81..f45e70790 100644 --- a/src/persistence/lg.jl +++ b/src/persistence/lg.jl @@ -12,7 +12,6 @@ # header followed by a list of (comma-delimited) edges - src,dst. # Multiple graphs may be present in one file. - struct LGFormat <: AbstractGraphFormat end struct LGHeader @@ -26,11 +25,12 @@ struct LGHeader end function show(io::IO, h::LGHeader) isdir = h.is_directed ? "d" : "u" - print(io, "$(h.nv),$(h.ne),$isdir,$(h.name),$(h.ver),$(h.dtype),$(h.code)") + return print(io, "$(h.nv),$(h.ne),$isdir,$(h.name),$(h.ver),$(h.dtype),$(h.code)") end -LGHeader(nv::Int, ne::Int, is_directed::Bool, name::AbstractString) = - LGHeader(nv, ne, is_directed, name, 1, Int64, "simplegraph") +function LGHeader(nv::Int, ne::Int, is_directed::Bool, name::AbstractString) + return LGHeader(nv, ne, is_directed, name, 1, Int64, "simplegraph") +end function _lg_read_one_graph(f::IO, header::LGHeader) T = header.dtype @@ -39,7 +39,7 @@ function _lg_read_one_graph(f::IO, header::LGHeader) else g = Graph{T}(header.nv) end - for i = 1:header.ne + for i in 1:(header.ne) line = chomp(readline(f)) if length(line) > 0 src_s, dst_s = split(line, r"\s*,\s*") @@ -56,10 +56,10 @@ function _lg_skip_one_graph(f::IO, n_e::Integer) readline(f) end end - + function _parse_header(s::AbstractString) addl_info = false - nvstr, nestr, dirundir, graphname = split(s, r"s*,s*", limit=4) + nvstr, nestr, dirundir, graphname = split(s, r"s*,s*"; limit=4) if occursin(",", graphname) # version number and type graphname, _ver, _dtype, graphcode = split(graphname, r"s*,s*") ver = parse(Int, _ver) @@ -117,7 +117,7 @@ end Write a graph `g` with name `gname` in a proprietary format to the IO stream designated by `io`. Return 1 (number of graphs written). """ -function savelg(io::IO, g::AbstractGraph{T}, gname::String) where T +function savelg(io::IO, g::AbstractGraph{T}, gname::String) where {T} header = LGHeader(nv(g), ne(g), is_directed(g), gname, 2, T, "simplegraph") # write header line line = string(header) @@ -143,7 +143,6 @@ function savelg_mult(io::IO, graphs::Dict) return ng end - loadgraph(io::IO, gname::String, ::LGFormat) = loadlg(io, gname) loadgraphs(io::IO, ::LGFormat) = loadlg_mult(io) savegraph(io::IO, g::AbstractGraph, gname::String, ::LGFormat) = savelg(io, g, gname) diff --git a/src/shortestpaths/astar.jl b/src/shortestpaths/astar.jl index b81148f41..fac91105d 100644 --- a/src/shortestpaths/astar.jl +++ b/src/shortestpaths/astar.jl @@ -3,11 +3,13 @@ # A* shortest-path algorithm -function reconstruct_path!(total_path, # a vector to be filled with the shortest path +function reconstruct_path!( + total_path, # a vector to be filled with the shortest path came_from, # a vector holding the parent of each node in the A* exploration end_idx, # the end vertex g, # the graph - edgetype_to_return::Type{E}=edgetype(g)) where {E<:AbstractEdge} + edgetype_to_return::Type{E}=edgetype(g), +) where {E<:AbstractEdge} curr_idx = end_idx while came_from[curr_idx] != curr_idx pushfirst!(total_path, edgetype_to_return(came_from[curr_idx], curr_idx)) @@ -15,7 +17,8 @@ function reconstruct_path!(total_path, # a vector to be filled with the shortest end end -function a_star_impl!(g, # the graph +function a_star_impl!( + g, # the graph goal, # the end vertex open_set, # an initialized heap containing the active vertices closed_set, # an (initialized) color-map to indicate status of vertices @@ -23,7 +26,8 @@ function a_star_impl!(g, # the graph came_from, # a vector holding the parent of each node in the A* exploration distmx, heuristic, - edgetype_to_return::Type{E}) where {E<:AbstractEdge} + edgetype_to_return::Type{E}, +) where {E<:AbstractEdge} total_path = Vector{edgetype_to_return}() @inbounds while !isempty(open_set) @@ -65,16 +69,18 @@ Compute a shortest path using the [A* search algorithm](http://en.wikipedia.org/ - `heuristic::Function`: an optional function mapping each vertex to a lower estimate of the remaining distance from `v` to `t`. It is set to `v -> 0` by default (which corresponds to Dijkstra's algorithm) - `edgetype_to_return::Type{E}`: the eltype `E<:AbstractEdge` of the vector of edges returned. It is set to `edgetype(g)` by default. Note that the two-argument constructor `E(u, v)` must be defined, even for weighted edges: if it isn't, consider using `E = Graphs.SimpleEdge`. """ -function a_star(g::AbstractGraph{U}, # the g +function a_star( + g::AbstractGraph{U}, # the g s::Integer, # the start vertex t::Integer, # the end vertex distmx::AbstractMatrix{T}=weights(g), heuristic::Function=n -> zero(T), - edgetype_to_return::Type{E}=edgetype(g)) where {T, U, E<:AbstractEdge} + edgetype_to_return::Type{E}=edgetype(g), +) where {T,U,E<:AbstractEdge} # if we do checkbounds here, we can use @inbounds in a_star_impl! checkbounds(distmx, Base.OneTo(nv(g)), Base.OneTo(nv(g))) - open_set = PriorityQueue{U, T}() + open_set = PriorityQueue{U,T}() enqueue!(open_set, s, 0) closed_set = zeros(Bool, nv(g)) @@ -85,7 +91,7 @@ function a_star(g::AbstractGraph{U}, # the g came_from = fill(-one(s), nv(g)) came_from[s] = s - a_star_impl!( + return a_star_impl!( g, t, open_set, @@ -94,6 +100,6 @@ function a_star(g::AbstractGraph{U}, # the g came_from, distmx, heuristic, - edgetype_to_return + edgetype_to_return, ) end diff --git a/src/shortestpaths/bellman-ford.jl b/src/shortestpaths/bellman-ford.jl index 43c582ff9..4b388147c 100644 --- a/src/shortestpaths/bellman-ford.jl +++ b/src/shortestpaths/bellman-ford.jl @@ -5,7 +5,7 @@ ################################################################### # -# The type that capsulates the state of Bellman Ford algorithm +# The type that encapsulates the state of Bellman Ford algorithm # ################################################################### using Base.Threads @@ -18,7 +18,7 @@ struct NegativeCycleError <: Exception end An `AbstractPathState` designed for Bellman-Ford shortest-paths calculations. """ -struct BellmanFordState{T<:Real, U<:Integer} <: AbstractPathState +struct BellmanFordState{T<:Real,U<:Integer} <: AbstractPathState parents::Vector{U} dists::Vector{T} end @@ -34,9 +34,8 @@ Return a [`Graphs.BellmanFordState`](@ref) with relevant traversal information. function bellman_ford_shortest_paths( graph::AbstractGraph{U}, sources::AbstractVector{<:Integer}, - distmx::AbstractMatrix{T}=weights(graph) - ) where T<:Real where U<:Integer - + distmx::AbstractMatrix{T}=weights(graph), +) where {T<:Real} where {U<:Integer} nvg = nv(graph) active = falses(nvg) active[sources] .= true @@ -69,18 +68,17 @@ function bellman_ford_shortest_paths( return BellmanFordState(parents, dists) end -bellman_ford_shortest_paths( - graph::AbstractGraph{U}, - v::Integer, - distmx::AbstractMatrix{T} = weights(graph); - ) where T<:Real where U<:Integer = bellman_ford_shortest_paths(graph, [v], distmx) +function bellman_ford_shortest_paths( + graph::AbstractGraph{U}, v::Integer, distmx::AbstractMatrix{T}=weights(graph); +) where {T<:Real} where {U<:Integer} + return bellman_ford_shortest_paths(graph, [v], distmx) +end has_negative_edge_cycle(g::AbstractGraph) = false function has_negative_edge_cycle( - g::AbstractGraph{U}, - distmx::AbstractMatrix{T} - ) where T<:Real where U<:Integer + g::AbstractGraph{U}, distmx::AbstractMatrix{T} +) where {T<:Real} where {U<:Integer} try bellman_ford_shortest_paths(g, vertices(g), distmx) catch e @@ -116,7 +114,7 @@ function enumerate_paths(state::AbstractPathState, vs::AbstractVector{<:Integer} num_vs = length(vs) all_paths = Vector{Vector{T}}(undef, num_vs) - for i = 1:num_vs + for i in 1:num_vs all_paths[i] = Vector{T}() index = T(vs[i]) if parents[index] != 0 || parents[index] == index @@ -132,5 +130,6 @@ function enumerate_paths(state::AbstractPathState, vs::AbstractVector{<:Integer} end enumerate_paths(state::AbstractPathState, v::Integer) = enumerate_paths(state, [v])[1] -enumerate_paths(state::AbstractPathState) = enumerate_paths(state, [1:length(state.parents);]) - +function enumerate_paths(state::AbstractPathState) + return enumerate_paths(state, [1:length(state.parents);]) +end diff --git a/src/shortestpaths/desopo-pape.jl b/src/shortestpaths/desopo-pape.jl index 18d5db75b..856725515 100644 --- a/src/shortestpaths/desopo-pape.jl +++ b/src/shortestpaths/desopo-pape.jl @@ -3,7 +3,7 @@ An [`AbstractPathState`](@ref) designed for D`Esopo-Pape shortest-path calculations. """ -struct DEsopoPapeState{T <:Real, U <: Integer} <: AbstractPathState +struct DEsopoPapeState{T<:Real,U<:Integer} <: AbstractPathState parents::Vector{U} dists::Vector{T} end @@ -40,9 +40,9 @@ julia> ds.dists 3 ``` """ -function desopo_pape_shortest_paths(g::AbstractGraph, - src::Integer, - distmx::AbstractMatrix{T} = weights(g)) where T <: Real +function desopo_pape_shortest_paths( + g::AbstractGraph, src::Integer, distmx::AbstractMatrix{T}=weights(g) +) where {T<:Real} U = eltype(g) nvg = nv(g) (src in 1:nvg) || throw(DomainError(src, "src should be in between 1 and $nvg")) @@ -52,17 +52,17 @@ function desopo_pape_shortest_paths(g::AbstractGraph, state = fill(Int8(2), nvg) q = U[src] @inbounds dists[src] = 0 - + @inbounds while !isempty(q) u = popfirst!(q) state[u] = 0 - + for v in outneighbors(g, u) alt = dists[u] + distmx[u, v] if (dists[v] > alt) dists[v] = alt parents[v] = u - + if state[v] == 2 state[v] = 1 push!(q, v) @@ -73,6 +73,6 @@ function desopo_pape_shortest_paths(g::AbstractGraph, end end end - - return DEsopoPapeState{T, U}(parents, dists) + + return DEsopoPapeState{T,U}(parents, dists) end diff --git a/src/shortestpaths/dijkstra.jl b/src/shortestpaths/dijkstra.jl index f5f72cb83..ba750e797 100644 --- a/src/shortestpaths/dijkstra.jl +++ b/src/shortestpaths/dijkstra.jl @@ -3,7 +3,7 @@ An [`AbstractPathState`](@ref) designed for Dijkstra shortest-paths calculations. """ -struct DijkstraState{T <: Real,U <: Integer} <: AbstractPathState +struct DijkstraState{T<:Real,U<:Integer} <: AbstractPathState parents::Vector{U} dists::Vector{T} predecessors::Vector{Vector{U}} @@ -67,13 +67,13 @@ julia> ds.dists 3 ``` """ -function dijkstra_shortest_paths(g::AbstractGraph, +function dijkstra_shortest_paths( + g::AbstractGraph, srcs::Vector{U}, distmx::AbstractMatrix{T}=weights(g); allpaths=false, - trackvertices=false - ) where T <: Real where U <: Integer - + trackvertices=false, +) where {T<:Real} where {U<:Integer} nvg = nv(g) dists = fill(typemax(T), nvg) parents = zeros(U, nvg) @@ -151,5 +151,14 @@ function dijkstra_shortest_paths(g::AbstractGraph, return DijkstraState{T,U}(parents, dists, preds, pathcounts, closest_vertices) end -dijkstra_shortest_paths(g::AbstractGraph, src::Integer, distmx::AbstractMatrix=weights(g); allpaths=false, trackvertices=false) = -dijkstra_shortest_paths(g, [src;], distmx; allpaths=allpaths, trackvertices=trackvertices) +function dijkstra_shortest_paths( + g::AbstractGraph, + src::Integer, + distmx::AbstractMatrix=weights(g); + allpaths=false, + trackvertices=false, +) + return dijkstra_shortest_paths( + g, [src;], distmx; allpaths=allpaths, trackvertices=trackvertices + ) +end diff --git a/src/shortestpaths/floyd-warshall.jl b/src/shortestpaths/floyd-warshall.jl index 39ffe6a02..782d10368 100644 --- a/src/shortestpaths/floyd-warshall.jl +++ b/src/shortestpaths/floyd-warshall.jl @@ -22,9 +22,8 @@ traversal information. Space complexity is on the order of ``\\mathcal{O}(|V|^2)``. """ function floyd_warshall_shortest_paths( - g::AbstractGraph{U}, - distmx::AbstractMatrix{T}=weights(g) -) where T<:Real where U<:Integer + g::AbstractGraph{U}, distmx::AbstractMatrix{T}=weights(g) +) where {T<:Real} where {U<:Integer} nvg = nv(g) # if we do checkbounds here, we can use @inbounds later checkbounds(distmx, Base.OneTo(nvg), Base.OneTo(nvg)) @@ -57,7 +56,7 @@ function floyd_warshall_shortest_paths( d == typemax(T) && continue p = parents[pivot, v] for u in vertices(g) - ans = (dists[u, pivot] == typemax(T) ? typemax(T) : dists[u, pivot] + d) + ans = (dists[u, pivot] == typemax(T) ? typemax(T) : dists[u, pivot] + d) if dists[u, v] > ans dists[u, v] = ans parents[u, v] = p @@ -69,7 +68,9 @@ function floyd_warshall_shortest_paths( return fws end -function enumerate_paths(s::FloydWarshallState{T,U}, v::Integer) where T where U<:Integer +function enumerate_paths( + s::FloydWarshallState{T,U}, v::Integer +) where {T} where {U<:Integer} pathinfo = s.parents[v, :] paths = Vector{Vector{U}}() for i in 1:length(pathinfo) @@ -92,5 +93,7 @@ function enumerate_paths(s::FloydWarshallState{T,U}, v::Integer) where T where U return paths end -enumerate_paths(s::FloydWarshallState) = [enumerate_paths(s, v) for v in 1:size(s.parents, 1)] +function enumerate_paths(s::FloydWarshallState) + return [enumerate_paths(s, v) for v in 1:size(s.parents, 1)] +end enumerate_paths(st::FloydWarshallState, s::Integer, d::Integer) = enumerate_paths(st, s)[d] diff --git a/src/shortestpaths/johnson.jl b/src/shortestpaths/johnson.jl index 9662430ae..22296dfc7 100644 --- a/src/shortestpaths/johnson.jl +++ b/src/shortestpaths/johnson.jl @@ -4,7 +4,7 @@ An [`AbstractPathState`](@ref) designed for Johnson shortest-paths calculations. """ -struct JohnsonState{T <: Real,U <: Integer} <: AbstractPathState +struct JohnsonState{T<:Real,U<:Integer} <: AbstractPathState dists::Matrix{T} parents::Matrix{U} end @@ -22,26 +22,25 @@ traversal information. ### Performance Complexity: O(|V|*|E|) """ -function johnson_shortest_paths(g::AbstractGraph{U}, - distmx::AbstractMatrix{T}=weights(g)) where T <: Real where U <: Integer - +function johnson_shortest_paths( + g::AbstractGraph{U}, distmx::AbstractMatrix{T}=weights(g) +) where {T<:Real} where {U<:Integer} nvg = nv(g) type_distmx = typeof(distmx) - #Change when parallel implementation of Bellman Ford available + # Change when parallel implementation of Bellman Ford available wt_transform = bellman_ford_shortest_paths(g, vertices(g), distmx).dists - @compat if !ismutable(distmx) && type_distmx != Graphs.DefaultDistance - distmx = sparse(distmx) #Change reference, not value + @compat if !ismutable(distmx) && type_distmx != Graphs.DefaultDistance + distmx = sparse(distmx) # Change reference, not value end - #Weight transform not needed if all weights are positive. - if type_distmx != Graphs.DefaultDistance + # Weight transform not needed if all weights are positive. + if type_distmx != Graphs.DefaultDistance for e in edges(g) distmx[src(e), dst(e)] += wt_transform[src(e)] - wt_transform[dst(e)] end end - dists = Matrix{T}(undef, nvg, nvg) parents = Matrix{U}(undef, nvg, nvg) for v in vertices(g) @@ -52,7 +51,7 @@ function johnson_shortest_paths(g::AbstractGraph{U}, broadcast!(-, dists, dists, wt_transform) for v in vertices(g) - dists[:, v] .+= wt_transform[v] #Vertical traversal prefered + dists[:, v] .+= wt_transform[v] # Vertical traversal preferred end @compat if ismutable(distmx) @@ -64,7 +63,9 @@ function johnson_shortest_paths(g::AbstractGraph{U}, return JohnsonState(dists, parents) end -function enumerate_paths(s::JohnsonState{T,U}, v::Integer) where T <: Real where U <: Integer +function enumerate_paths( + s::JohnsonState{T,U}, v::Integer +) where {T<:Real} where {U<:Integer} pathinfo = s.parents[v, :] paths = Vector{Vector{U}}() for i in 1:length(pathinfo) diff --git a/src/shortestpaths/spfa.jl b/src/shortestpaths/spfa.jl index b15a2ceac..e1eeb424d 100644 --- a/src/shortestpaths/spfa.jl +++ b/src/shortestpaths/spfa.jl @@ -2,13 +2,12 @@ ################################################################### # -#The type that capsulates the state of Shortest Path Faster Algorithm +# The type that encapsulates the state of Shortest Path Faster Algorithm # ################################################################### using Base.Threads - """ spfa_shortest_paths(g, s, distmx=weights(g)) @@ -42,22 +41,19 @@ julia> spfa_shortest_paths(gx, 1, d) """ function spfa_shortest_paths( - graph::AbstractGraph{U}, - source::Integer, - distmx::AbstractMatrix{T}=weights(graph) - ) where T<:Real where U<:Integer - - + graph::AbstractGraph{U}, source::Integer, distmx::AbstractMatrix{T}=weights(graph) +) where {T<:Real} where {U<:Integer} nvg = nv(graph) - (source in 1:nvg) || throw(DomainError(source, "source should be in between 1 and $nvg")) - dists = fill(typemax(T),nvg) + (source in 1:nvg) || + throw(DomainError(source, "source should be in between 1 and $nvg")) + dists = fill(typemax(T), nvg) dists[source] = 0 count = zeros(U, nvg) # Vector to store the count of number of times a vertex goes in the queue. queue = Vector{U}() # Vector used to implement queue - inqueue = falses(nvg,1) # BitArray to mark which vertices are in queue + inqueue = falses(nvg, 1) # BitArray to mark which vertices are in queue push!(queue, source) inqueue[source] = true @@ -65,20 +61,20 @@ function spfa_shortest_paths( v = popfirst!(queue) inqueue[v] = false - @inbounds for v_neighbor in outneighbors(graph,v) - d = distmx[v,v_neighbor] + @inbounds for v_neighbor in outneighbors(graph, v) + d = distmx[v, v_neighbor] if dists[v] + d < dists[v_neighbor] # Relaxing edges dists[v_neighbor] = dists[v] + d if !inqueue[v_neighbor] - push!(queue,v_neighbor) + push!(queue, v_neighbor) inqueue[v_neighbor] = true # Mark the vertex as inside queue. - count[v_neighbor] = count[v_neighbor]+1 # Increment the number of times a vertex enters a queue. + count[v_neighbor] = count[v_neighbor] + 1 # Increment the number of times a vertex enters a queue. if count[v_neighbor] > nvg # This step is just to check negative edge cycle. If this step is not there, throw(NegativeCycleError()) # the algorithm will run infinitely in case of a negative weight cycle. - # If count[i]>nvg for any i belonging to [1,nvg], it means a negative edge - # cycle is present. + # If count[i]>nvg for any i belonging to [1,nvg], it means a negative edge + # cycle is present. end end end @@ -112,10 +108,8 @@ false """ function has_negative_edge_cycle_spfa( - g::AbstractGraph{U}, - distmx::AbstractMatrix{T} - ) where T<:Real where U<:Integer - + g::AbstractGraph{U}, distmx::AbstractMatrix{T} +) where {T<:Real} where {U<:Integer} try spfa_shortest_paths(g, 1, distmx) catch e diff --git a/src/shortestpaths/yen.jl b/src/shortestpaths/yen.jl index 1d70819c9..a3861ad11 100644 --- a/src/shortestpaths/yen.jl +++ b/src/shortestpaths/yen.jl @@ -3,12 +3,11 @@ Designed for yen k-shortest-paths calculations. """ -struct YenState{T,U <: Integer} <: AbstractPathState +struct YenState{T,U<:Integer} <: AbstractPathState dists::Vector{T} paths::Vector{Vector{U}} end - """ yen_k_shortest_paths(g, source, target, distmx=weights(g), K=1; maxdist=Inf); @@ -16,13 +15,14 @@ Perform [Yen's algorithm](http://en.wikipedia.org/wiki/Yen%27s_algorithm) on a graph, computing k-shortest distances between `source` and `target` other vertices. Return a [`YenState`](@ref) that contains distances and paths. """ -function yen_k_shortest_paths(g::AbstractGraph, +function yen_k_shortest_paths( + g::AbstractGraph, source::U, target::U, distmx::AbstractMatrix{T}=weights(g), K::Int=1; - maxdist=Inf) where T <: Real where U <: Integer - + maxdist=Inf, +) where {T<:Real} where {U<:Integer} source == target && return YenState{T,U}([U(0)], [[source]]) dj = dijkstra_shortest_paths(g, source, distmx) @@ -35,8 +35,8 @@ function yen_k_shortest_paths(g::AbstractGraph, B = PriorityQueue() gcopy = deepcopy(g) - for k = 1:(K - 1) - for j = 1:length(A[k]) + for k in 1:(K - 1) + for j in 1:length(A[k]) # Spur node is retrieved from the previous k-shortest path, k − 1 spurnode = A[k][j] # The sequence of nodes from the source to the spur node of the previous k-shortest path @@ -57,8 +57,8 @@ function yen_k_shortest_paths(g::AbstractGraph, end # Remove node of root path and calculate dist of it - distrootpath = 0. - for n = 1:(length(rootpath) - 1) + distrootpath = 0.0 + for n in 1:(length(rootpath) - 1) u = rootpath[n] nei = copy(neighbors(gcopy, u)) for v in nei @@ -66,7 +66,7 @@ function yen_k_shortest_paths(g::AbstractGraph, push!(edgesremoved, (u, v)) end - # Evaluate distante of root path + # Evaluate distance of root path v = rootpath[n + 1] distrootpath += distmx[u, v] end @@ -77,7 +77,7 @@ function yen_k_shortest_paths(g::AbstractGraph, if !isempty(spurpath) # Entire path is made up of the root path and spur path pathtotal = [rootpath[1:(end - 1)]; spurpath] - distpath = distrootpath + djspur.dists[target] + distpath = distrootpath + djspur.dists[target] # Add the potential k-shortest path to the heap if !haskey(B, pathtotal) enqueue!(B, pathtotal, distpath) @@ -100,4 +100,3 @@ function yen_k_shortest_paths(g::AbstractGraph, return YenState{T,U}(dists, A) end - diff --git a/src/spanningtrees/boruvka.jl b/src/spanningtrees/boruvka.jl index bd7d42609..0fa39e6d9 100644 --- a/src/spanningtrees/boruvka.jl +++ b/src/spanningtrees/boruvka.jl @@ -6,16 +6,15 @@ optimum (minimum, by default) spanning tree of a connected, undirected graph `g` with optional matrix `distmx` that provides distinct edge weights, and `weights` is the sum of all the edges in the solution by using [Boruvka's algorithm](https://en.wikipedia.org/wiki/Bor%C5%AFvka%27s_algorithm). -The algorithm requires that all edges have different weights to correctly generate a minimun/maximum spanning tree +The algorithm requires that all edges have different weights to correctly generate a minimum/maximum spanning tree ### Optional Arguments - `minimize=true`: if set to `false`, calculate the maximum spanning tree. """ -function boruvka_mst end - -@traitfn function boruvka_mst(g::AG::(!IsDirected), - distmx::AbstractMatrix{T} = weights(g); - minimize = true) where {T<:Real, U, AG<:AbstractGraph{U}} +function boruvka_mst end +@traitfn function boruvka_mst( + g::AG::(!IsDirected), distmx::AbstractMatrix{T}=weights(g); minimize=true +) where {T<:Real,U,AG<:AbstractGraph{U}} djset = IntDisjointSets(nv(g)) # maximizing Z is the same as minimizing -Z # mode will indicate the need for the -1 multiplication @@ -23,9 +22,9 @@ function boruvka_mst end mst = Vector{edgetype(g)}() sizehint!(mst, nv(g) - 1) weight = zero(T) - + while true - cheapest = Vector{Union{edgetype(g), Nothing}}(nothing, nv(g)) + cheapest = Vector{Union{edgetype(g),Nothing}}(nothing, nv(g)) # find cheapest edge that connects two components found_edge = false for edge in edges(g) @@ -34,21 +33,23 @@ function boruvka_mst end if set1 != set2 found_edge = true e1 = cheapest[set1] - if e1 === nothing || distmx[src(e1), dst(e1)] * mode > distmx[src(edge), dst(edge)] * mode + if e1 === nothing || + distmx[src(e1), dst(e1)] * mode > distmx[src(edge), dst(edge)] * mode cheapest[set1] = edge end e2 = cheapest[set2] - if e2===nothing || distmx[src(e2), dst(e2)] * mode > distmx[src(edge), dst(edge)] * mode + if e2 === nothing || + distmx[src(e2), dst(e2)] * mode > distmx[src(edge), dst(edge)] * mode cheapest[set2] = edge end end end - #no more edges between two components + # no more edges between two components !found_edge && break # add cheapest edges to the tree for v in vertices(g) if cheapest[v] !== nothing - edge = cheapest[v] + edge = cheapest[v] if !in_same_set(djset, src(edge), dst(edge)) weight += distmx[src(edge), dst(edge)] union!(djset, src(edge), dst(edge)) diff --git a/src/spanningtrees/kruskal.jl b/src/spanningtrees/kruskal.jl index 12111fa59..52cb936ae 100644 --- a/src/spanningtrees/kruskal.jl +++ b/src/spanningtrees/kruskal.jl @@ -9,9 +9,9 @@ undirected graph `g` with optional distance matrix `distmx` using [Kruskal's alg """ function kruskal_mst end # see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax -@traitfn function kruskal_mst(g::AG::(!IsDirected), - distmx::AbstractMatrix{T}=weights(g); minimize=true) where {T <: Real, U, AG <: AbstractGraph{U}} - +@traitfn function kruskal_mst( + g::AG::(!IsDirected), distmx::AbstractMatrix{T}=weights(g); minimize=true +) where {T<:Real,U,AG<:AbstractGraph{U}} connected_vs = IntDisjointSets(nv(g)) mst = Vector{edgetype(g)}() @@ -34,4 +34,3 @@ function kruskal_mst end return mst end - diff --git a/src/spanningtrees/prim.jl b/src/spanningtrees/prim.jl index 38d4f7777..39a855405 100644 --- a/src/spanningtrees/prim.jl +++ b/src/spanningtrees/prim.jl @@ -6,14 +6,14 @@ distance matrix `distmx` using [Prim's algorithm](https://en.wikipedia.org/wiki/ Return a vector of edges. """ function prim_mst end -@traitfn function prim_mst(g::AG::(!IsDirected), - distmx::AbstractMatrix{T}=weights(g)) where {T <: Real, U, AG <: AbstractGraph{U}} - +@traitfn function prim_mst( + g::AG::(!IsDirected), distmx::AbstractMatrix{T}=weights(g) +) where {T<:Real,U,AG<:AbstractGraph{U}} nvg = nv(g) - pq = PriorityQueue{U, T}() + pq = PriorityQueue{U,T}() finished = zeros(Bool, nvg) - wt = fill(typemax(T), nvg) #Faster access time + wt = fill(typemax(T), nvg) # Faster access time parents = zeros(U, nv(g)) pq[1] = typemin(T) @@ -25,9 +25,9 @@ function prim_mst end for u in neighbors(g, v) finished[u] && continue - + if wt[u] > distmx[u, v] - wt[u] = distmx[u, v] + wt[u] = distmx[u, v] pq[u] = wt[u] parents[u] = v end @@ -36,4 +36,3 @@ function prim_mst end return [Edge{U}(parents[v], v) for v in vertices(g) if parents[v] != 0] end - diff --git a/src/steinertree/steiner_tree.jl b/src/steinertree/steiner_tree.jl index 028ddef65..6f7713cd1 100644 --- a/src/steinertree/steiner_tree.jl +++ b/src/steinertree/steiner_tree.jl @@ -4,10 +4,8 @@ Remove edges of `g` so that all non-isolated leaves of `g` are in the set `term_vert` """ function filter_non_term_leaves!( - g::AbstractGraph{T}, - term_vert::Vector{<:Integer} - ) where T<:Integer - + g::AbstractGraph{T}, term_vert::Vector{<:Integer} +) where {T<:Integer} is_term = falses(nv(g)) is_term[term_vert] .= true leaves = [v for v in vertices(g) if degree(g, v) == 1 && !is_term[v]] @@ -43,11 +41,8 @@ Approximation Factor: 2-2/t function steiner_tree end @traitfn function steiner_tree( - g::AG::(!IsDirected), - term_vert::Vector{<:Integer}, - distmx::AbstractMatrix{U} = weights(g) - ) where {U<:Real, T, AG<:AbstractGraph{T}} - + g::AG::(!IsDirected), term_vert::Vector{<:Integer}, distmx::AbstractMatrix{U}=weights(g) +) where {U<:Real,T,AG<:AbstractGraph{T}} nvg = nv(g) term_to_actual = T.(term_vert) unique!(term_to_actual) @@ -74,15 +69,15 @@ function steiner_tree end s = term_to_actual[i] t = term_to_actual[dst(e)] while s != t - t_next = parents[t, i] - push!(expanded_mst, Edge(min(t_next, t), max(t_next, t))) + t_next = parents[t, i] + push!(expanded_mst, Edge(min(t_next, t), max(t_next, t))) t = t_next end end # Compute the MST of the expanded graph - mst_mst_mc = kruskal_mst(SimpleGraph(expanded_mst), distmx) - + mst_mst_mc = kruskal_mst(SimpleGraph(expanded_mst), distmx) + # Remove non-terminal leaves return filter_non_term_leaves!(SimpleGraph(mst_mst_mc), term_to_actual) end diff --git a/src/traversals/bfs.jl b/src/traversals/bfs.jl index a672f5cf9..8e2430dae 100644 --- a/src/traversals/bfs.jl +++ b/src/traversals/bfs.jl @@ -7,11 +7,11 @@ Convert a parents array into a directed graph. """ -function tree(parents::AbstractVector{T}) where T <: Integer +function tree(parents::AbstractVector{T}) where {T<:Integer} n = T(length(parents)) t = DiGraph{T}(n) for (v, u) in enumerate(parents) - if u > zero(T) && u != v + if u > zero(T) && u != v add_edge!(t, u, v) end end @@ -32,10 +32,15 @@ implementations which are marginally faster in practice for smaller graphs, but the performance improvements using this implementation on large graphs can be significant. """ -bfs_parents(g::AbstractGraph, s::Integer; dir = :out) = - (dir == :out) ? _bfs_parents(g, s, outneighbors) : _bfs_parents(g, s, inneighbors) +function bfs_parents(g::AbstractGraph, s::Integer; dir=:out) + return if (dir == :out) + _bfs_parents(g, s, outneighbors) + else + _bfs_parents(g, s, inneighbors) + end +end -function _bfs_parents(g::AbstractGraph{T}, source, neighborfn::Function) where T +function _bfs_parents(g::AbstractGraph{T}, source, neighborfn::Function) where {T} n = nv(g) visited = falses(n) parents = zeros(T, nv(g)) @@ -50,7 +55,7 @@ function _bfs_parents(g::AbstractGraph{T}, source, neighborfn::Function) where T end while !isempty(cur_level) @inbounds for v in cur_level - @inbounds @simd for i in neighborfn(g, v) + @inbounds @simd for i in neighborfn(g, v) if !visited[i] push!(next_level, i) parents[i] = v @@ -73,7 +78,7 @@ and return a directed acyclic graph of vertices in the order they were discovere If `dir` is specified, use the corresponding edge direction (`:in` and `:out` are acceptable values). """ -bfs_tree(g::AbstractGraph, s::Integer; dir = :out) = tree(bfs_parents(g, s; dir = dir)) +bfs_tree(g::AbstractGraph, s::Integer; dir=:out) = tree(bfs_parents(g, s; dir=dir)) """ gdistances!(g, source, dists; sort_alg=QuickSort) @@ -92,7 +97,7 @@ the best of the algorithms built into Julia Base. However, passing a `RadixSort` [SortingAlgorithms.jl](https://github.com/JuliaCollections/SortingAlgorithms.jl)) will provide significant performance improvements on larger graphs. """ -function gdistances!(g::AbstractGraph{T}, source, vert_level; sort_alg = QuickSort) where T +function gdistances!(g::AbstractGraph{T}, source, vert_level; sort_alg=QuickSort) where {T} n = nv(g) visited = falses(n) n_level = one(T) @@ -118,7 +123,7 @@ function gdistances!(g::AbstractGraph{T}, source, vert_level; sort_alg = QuickSo n_level += one(T) empty!(cur_level) cur_level, next_level = next_level, cur_level - sort!(cur_level, alg = sort_alg) + sort!(cur_level; alg=sort_alg) end return vert_level end @@ -138,7 +143,9 @@ the best of the algorithms built into Julia Base. However, passing a `RadixSort` [SortingAlgorithms.jl](https://github.com/JuliaCollections/SortingAlgorithms.jl)) will provide significant performance improvements on larger graphs. """ -gdistances(g::AbstractGraph{T}, source; sort_alg = Base.Sort.QuickSort) where T = gdistances!(g, source, fill(typemax(T), nv(g)); sort_alg = sort_alg) +function gdistances(g::AbstractGraph{T}, source; sort_alg=Base.Sort.QuickSort) where {T} + return gdistances!(g, source, fill(typemax(T), nv(g)); sort_alg=sort_alg) +end """ has_path(g::AbstractGraph, u, v; exclude_vertices=Vector()) @@ -147,8 +154,12 @@ Return `true` if there is a path from `u` to `v` in `g` (while avoiding vertices `exclude_vertices`) or `u == v`. Return false if there is no such path or if `u` or `v` is in `excluded_vertices`. """ -function has_path(g::AbstractGraph{T}, u::Integer, v::Integer; - exclude_vertices::AbstractVector = Vector{T}()) where T +function has_path( + g::AbstractGraph{T}, + u::Integer, + v::Integer; + exclude_vertices::AbstractVector=Vector{T}(), +) where {T} seen = zeros(Bool, nv(g)) for ve in exclude_vertices # mark excluded vertices as seen seen[ve] = true diff --git a/src/traversals/bipartition.jl b/src/traversals/bipartition.jl index c785aac05..d4f34473a 100644 --- a/src/traversals/bipartition.jl +++ b/src/traversals/bipartition.jl @@ -34,7 +34,7 @@ julia> bipartite_map(g) 0x01 ``` """ -function bipartite_map(g::AbstractGraph{T}) where T +function bipartite_map(g::AbstractGraph{T}) where {T} nvg = nv(g) if !is_directed(g) ccs = filter(x -> length(x) >= 2, connected_components(g)) @@ -61,7 +61,7 @@ function bipartite_map(g::AbstractGraph{T}) where T end end end - return UInt8.(colors).+(one(UInt8)) + return UInt8.(colors) .+ (one(UInt8)) end """ diff --git a/src/traversals/dfs.jl b/src/traversals/dfs.jl index 1308872aa..9de8501cb 100644 --- a/src/traversals/dfs.jl +++ b/src/traversals/dfs.jl @@ -11,12 +11,12 @@ The algorithm uses a DFS. Self-loops are counted as cycles. """ function is_cyclic end @enum Vertex_state unvisited visited -@traitfn function is_cyclic(g::AG::(!IsDirected)) where {T, AG<:AbstractGraph{T}} - visited = falses(nv(g)) +@traitfn function is_cyclic(g::AG::(!IsDirected)) where {T,AG<:AbstractGraph{T}} + visited = falses(nv(g)) for v in vertices(g) visited[v] && continue visited[v] = true - S = [(v,v)] + S = [(v, v)] while !isempty(S) parent, w = pop!(S) for u in neighbors(g, w) @@ -31,7 +31,7 @@ function is_cyclic end return false end # see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax -@traitfn function is_cyclic(g::AG::IsDirected) where {T, AG<:AbstractGraph{T}} +@traitfn function is_cyclic(g::AG::IsDirected) where {T,AG<:AbstractGraph{T}} vcolor = zeros(UInt8, nv(g)) for v in vertices(g) vcolor[v] != 0 && continue @@ -69,7 +69,7 @@ graph `g` as a vector of vertices in topological order. """ function topological_sort_by_dfs end # see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax -@traitfn function topological_sort_by_dfs(g::AG::IsDirected) where {T, AG<:AbstractGraph{T}} +@traitfn function topological_sort_by_dfs(g::AG::IsDirected) where {T,AG<:AbstractGraph{T}} vcolor = zeros(UInt8, nv(g)) verts = Vector{T}() for v in vertices(g) @@ -118,10 +118,15 @@ use the corresponding edge direction (`:in` and `:out` are acceptable values). ### Implementation Notes This version of DFS is iterative. """ -dfs_parents(g::AbstractGraph, s::Integer; dir=:out) = -(dir == :out) ? _dfs_parents(g, s, outneighbors) : _dfs_parents(g, s, inneighbors) +function dfs_parents(g::AbstractGraph, s::Integer; dir=:out) + return if (dir == :out) + _dfs_parents(g, s, outneighbors) + else + _dfs_parents(g, s, inneighbors) + end +end -function _dfs_parents(g::AbstractGraph{T}, s::Integer, neighborfn::Function) where T +function _dfs_parents(g::AbstractGraph{T}, s::Integer, neighborfn::Function) where {T} parents = zeros(T, nv(g)) seen = zeros(Bool, nv(g)) diff --git a/src/traversals/diffusion.jl b/src/traversals/diffusion.jl index a71686de1..288422451 100644 --- a/src/traversals/diffusion.jl +++ b/src/traversals/diffusion.jl @@ -17,15 +17,16 @@ from a vertex ``i`` to each of the `outneighbors` of ``i`` to ``\\frac{p}{outdegreee(g, i)}``. - `rng=nothing`: A random generator to sample from. """ -function diffusion(g::AbstractGraph{T}, - p::Real, - n::Integer; - watch::AbstractVector=Vector{Int}(), - normalize::Bool=false, - rng::Union{Nothing, AbstractRNG} = nothing, - seed::Union{Nothing, Integer} = nothing, - initial_infections::AbstractVector=Graphs.sample(vertices(g), 1, rng=rng, seed=seed), - ) where T +function diffusion( + g::AbstractGraph{T}, + p::Real, + n::Integer; + watch::AbstractVector=Vector{Int}(), + normalize::Bool=false, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, + initial_infections::AbstractVector=Graphs.sample(vertices(g), 1; rng=rng, seed=seed), +) where {T} # Initialize rng = rng_from_rng_or_seed(rng, seed) @@ -88,14 +89,27 @@ diffusion as a vector representing the cumulative number of vertices infected at each simulation step, restricted to vertices included in `watch`, if specified. """ -diffusion_rate(x::Vector{Vector{T}}) where T <: Integer = cumsum(length.(x)) -diffusion_rate(g::AbstractGraph, p::Real, n::Integer; +diffusion_rate(x::Vector{Vector{T}}) where {T<:Integer} = cumsum(length.(x)) +function diffusion_rate( + g::AbstractGraph, + p::Real, + n::Integer; watch::AbstractVector=Vector{Int}(), normalize::Bool=false, - rng::Union{Nothing, AbstractRNG} = nothing, - seed::Union{Nothing, Integer} = nothing, - initial_infections::AbstractVector=Graphs.sample(vertices(g), 1, rng=rng, seed=seed), - ) = diffusion_rate(diffusion(g, p, n, + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, + initial_infections::AbstractVector=Graphs.sample(vertices(g), 1; rng=rng, seed=seed), +) + return diffusion_rate( + diffusion( + g, + p, + n; initial_infections=initial_infections, - watch=watch, normalize=normalize, rng=rng, seed=seed)) - + watch=watch, + normalize=normalize, + rng=rng, + seed=seed, + ), + ) +end diff --git a/src/traversals/greedy_color.jl b/src/traversals/greedy_color.jl index e44f8d836..4aa7e2d7b 100644 --- a/src/traversals/greedy_color.jl +++ b/src/traversals/greedy_color.jl @@ -3,7 +3,7 @@ Store the number of colors used and mapping from vertex to color """ -struct Coloring{T <: Integer} +struct Coloring{T<:Integer} num_colors::T colors::Vector{T} end @@ -16,13 +16,13 @@ best_color(c1::Coloring, c2::Coloring) = c1.num_colors < c2.num_colors ? c1 : c2 Color graph `g` according to an order specified by `seq` using a greedy heuristic. `seq[i] = v` implies that vertex v is the ``i^{th}`` vertex to be colored. """ -function perm_greedy_color(g::AbstractGraph, seq::Vector{T}) where {T <: Integer} +function perm_greedy_color(g::AbstractGraph, seq::Vector{T}) where {T<:Integer} nvg::T = nv(g) cols = Vector{T}(undef, nvg) seen = zeros(Bool, nvg + 1) has_self_loops(g) && throw(ArgumentError("graph must not have self loops")) - + for v in seq colors_used = zeros(Bool, nvg) for w in neighbors(g, v) @@ -34,7 +34,7 @@ function perm_greedy_color(g::AbstractGraph, seq::Vector{T}) where {T <: Integer for i in one(T):nvg if colors_used[i] == false cols[v] = i - break; + break end end @@ -49,20 +49,18 @@ end Color graph `g` iteratively in the descending order of the degree of the vertices. """ -function degree_greedy_color(g::AbstractGraph{T}) where {T <: Integer} - seq = convert(Vector{T}, sortperm(degree(g), rev=true)) +function degree_greedy_color(g::AbstractGraph{T}) where {T<:Integer} + seq = convert(Vector{T}, sortperm(degree(g); rev=true)) return perm_greedy_color(g, seq) end - """ random_greedy_color(g, reps) Color the graph `g` iteratively in a random order using a greedy heuristic and choose the best coloring out of `reps` such random colorings. """ -function random_greedy_color(g::AbstractGraph{T}, reps::Integer) where {T <: Integer} - +function random_greedy_color(g::AbstractGraph{T}, reps::Integer) where {T<:Integer} seq = shuffle(vertices(g)) best = perm_greedy_color(g, seq) @@ -87,5 +85,8 @@ If `sort_degree` is true then the permutation is chosen in reverse sorted order If `sort_degree` is false then `reps` colorings are obtained based on random permutations and the one using least colors is chosen. """ -greedy_color(g::AbstractGraph{U}; sort_degree::Bool=false, reps::Integer=1) where {U <: Integer} = - sort_degree ? degree_greedy_color(g) : random_greedy_color(g, reps) +function greedy_color( + g::AbstractGraph{U}; sort_degree::Bool=false, reps::Integer=1 +) where {U<:Integer} + return sort_degree ? degree_greedy_color(g) : random_greedy_color(g, reps) +end diff --git a/src/traversals/maxadjvisit.jl b/src/traversals/maxadjvisit.jl index 450edd277..980f31734 100644 --- a/src/traversals/maxadjvisit.jl +++ b/src/traversals/maxadjvisit.jl @@ -7,7 +7,6 @@ # ################################################# - """ mincut(g, distmx=weights(g)) @@ -16,9 +15,7 @@ values that determines the partition in `g` (1 or 2) and `bestcut` is the weight of the cut that makes this partition. An optional `distmx` matrix may be specified; if omitted, edge distances are assumed to be 1. """ -function mincut(g::AbstractGraph, - distmx::AbstractMatrix{T}=weights(g)) where T <: Real - +function mincut(g::AbstractGraph, distmx::AbstractMatrix{T}=weights(g)) where {T<:Real} U = eltype(g) colormap = zeros(UInt8, nv(g)) ## 0 if unseen, 1 if processing and 2 if seen and closed parities = falses(nv(g)) @@ -36,7 +33,7 @@ function mincut(g::AbstractGraph, # in which case we'll return immediately. (haskey(pq, one(U)) && nv(g) > one(U)) || return (Vector{Int8}([1]), cutweight) - #Give the starting vertex high priority + # Give the starting vertex high priority pq[one(U)] = one(T) while !isempty(pq) @@ -66,10 +63,9 @@ function mincut(g::AbstractGraph, end end end - return(convert(Vector{Int8}, parities) .+ one(Int8), bestweight) + return (convert(Vector{Int8}, parities) .+ one(Int8), bestweight) end - """ maximum_adjacency_visit(g[, distmx][, log][, io][, s]) maximum_adjacency_visit(g[, s]) @@ -80,12 +76,13 @@ specified; if omitted, edge distances are assumed to be 1. If `log` (default `false`) is `true`, visitor events will be printed to `io`, which defaults to `STDOUT`; otherwise, no event information will be displayed. """ -function maximum_adjacency_visit(g::AbstractGraph{U}, +function maximum_adjacency_visit( + g::AbstractGraph{U}, distmx::AbstractMatrix{T}, log::Bool=false, io::IO=stdout, - s::U=one(U)) where {U, T <: Real} - + s::U=one(U), +) where {U,T<:Real} pq = PriorityQueue{U,T}(Base.Order.Reverse) vertices_order = Vector{U}() has_key = ones(Bool, nv(g)) @@ -98,11 +95,10 @@ function maximum_adjacency_visit(g::AbstractGraph{U}, pq[v] = zero(T) end - # Give start vertex maximum priority pq[s] = one(T) - #start traversing the graph + # start traversing the graph while !isempty(pq) u = dequeue!(pq) has_key[u] = false @@ -120,9 +116,6 @@ function maximum_adjacency_visit(g::AbstractGraph{U}, return vertices_order end -maximum_adjacency_visit(g::AbstractGraph{U}, s::U=one(U)) where {U} = maximum_adjacency_visit(g, - weights(g), - false, - stdout, - s) - +function maximum_adjacency_visit(g::AbstractGraph{U}, s::U=one(U)) where {U} + return maximum_adjacency_visit(g, weights(g), false, stdout, s) +end diff --git a/src/traversals/randomwalks.jl b/src/traversals/randomwalks.jl index bd9859b43..15a1a0c25 100644 --- a/src/traversals/randomwalks.jl +++ b/src/traversals/randomwalks.jl @@ -5,9 +5,12 @@ Perform a random walk on graph `g` starting at vertex `s` and continuing for a maximum of `niter` steps. Return a vector of vertices visited in order. """ function randomwalk( - g::AG, s::Integer, niter::Integer; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where AG <: AbstractGraph{T} where T + g::AG, + s::Integer, + niter::Integer; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {AG<:AbstractGraph{T}} where {T} s in vertices(g) || throw(BoundsError()) rng = rng_from_rng_or_seed(rng, seed) visited = Vector{T}() @@ -34,9 +37,12 @@ vector of vertices visited in order. function non_backtracking_randomwalk end # see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax @traitfn function non_backtracking_randomwalk( - g::AG::(!IsDirected), s::Integer, niter::Integer; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where {T, AG<:AbstractGraph{T}} + g::AG::(!IsDirected), + s::Integer, + niter::Integer; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T,AG<:AbstractGraph{T}} s in vertices(g) || throw(BoundsError()) rng = rng_from_rng_or_seed(rng, seed) visited = Vector{T}() @@ -70,9 +76,12 @@ end # see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax @traitfn function non_backtracking_randomwalk( - g::AG::IsDirected, s::Integer, niter::Integer; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where {T, AG<:AbstractGraph{T}} + g::AG::IsDirected, + s::Integer, + niter::Integer; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T,AG<:AbstractGraph{T}} s in vertices(g) || throw(BoundsError()) rng = rng_from_rng_or_seed(rng, seed) visited = Vector{T}() @@ -109,9 +118,12 @@ on graph `g` starting at vertex `s` and continuing for a maximum of `niter` step Return a vector of vertices visited in order. """ function self_avoiding_walk( - g::AG, s::Integer, niter::Integer; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where AG <: AbstractGraph{T} where T + g::AG, + s::Integer, + niter::Integer; + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {AG<:AbstractGraph{T}} where {T} s in vertices(g) || throw(BoundsError()) rng = rng_from_rng_or_seed(rng, seed) visited = Vector{T}() diff --git a/src/utils.jl b/src/utils.jl index e1f4e2e3e..770d0d54d 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -24,13 +24,18 @@ function sample!(rng::AbstractRNG, a::AbstractVector, k::Integer; exclude=()) i += 1 end end - res + return res end -sample!( - a::AbstractVector, k::Integer; - exclude=(), rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) = sample!(rng_from_rng_or_seed(rng, seed), a, k; exclude=exclude) +function sample!( + a::AbstractVector, + k::Integer; + exclude=(), + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) + return sample!(rng_from_rng_or_seed(rng, seed), a, k; exclude=exclude) +end """ sample(a, k; exclude=(), rng=nothing, seed=nothing) @@ -45,10 +50,15 @@ Sample `k` element from AbstractVector `a` without repetition and eventually exc ### Implementation Notes Unlike [`sample!`](@ref), does not produce side effects. """ -sample( - a::AbstractVector, k::Integer; - exclude=(), rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) = sample!(rng_from_rng_or_seed(rng, seed), collect(a), k; exclude=exclude) +function sample( + a::AbstractVector, + k::Integer; + exclude=(), + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) + return sample!(rng_from_rng_or_seed(rng, seed), collect(a), k; exclude=exclude) +end getRNG(seed::Integer=-1) = seed >= 0 ? MersenneTwister(seed) : GLOBAL_RNG @@ -63,9 +73,9 @@ this function helps with the transition by taking `rng` and `seed` as an argumen always returning a random number generator. At least one of these arguments must be `nothing`. """ -function rng_from_rng_or_seed(rng::Union{Nothing, AbstractRNG}, seed::Union{Nothing, Integer}) - - !(isnothing(seed) || isnothing(rng)) && throw(ArgumentError("Cannot specify both, seed and rng")) +function rng_from_rng_or_seed(rng::Union{Nothing,AbstractRNG}, seed::Union{Nothing,Integer}) + !(isnothing(seed) || isnothing(rng)) && + throw(ArgumentError("Cannot specify both, seed and rng")) # TODO at some point we might emit a deprecation warning if a seed is specified !isnothing(seed) && return getRNG(seed) isnothing(rng) && return GLOBAL_RNG @@ -88,11 +98,11 @@ end """ findall!(A, B) -Set the `B[1:|I|]` to `I` where `I` is the set of indices `A[I]` returns true. +Set the `B[1:|I|]` to `I` where `I` is the set of indices `A[I]` returns true. Assumes `length(B) >= |I|`. """ -function findall!(A::Union{BitArray{1}, Vector{Bool}}, B::Vector{T}) where T<:Integer +function findall!(A::Union{BitArray{1},Vector{Bool}}, B::Vector{T}) where {T<:Integer} len = 0 @inbounds for (i, a) in enumerate(A) if a @@ -107,21 +117,17 @@ end unweighted_contiguous_partition(num_items, required_partitions) Partition `1:num_items` into `required_partitions` number of partitions such that the -difference in length of the largest and smallest partition is atmost 1. +difference in length of the largest and smallest partition is at most 1. ### Performance Time: O(required_partitions) """ -function unweighted_contiguous_partition( - num_items::Integer, - required_partitions::Integer - ) - +function unweighted_contiguous_partition(num_items::Integer, required_partitions::Integer) left = 1 part = Vector{UnitRange}(undef, required_partitions) for i in 1:required_partitions - len = fld(num_items+i-1, required_partitions) - part[i] = left:(left+len-1) + len = fld(num_items + i - 1, required_partitions) + part[i] = left:(left + len - 1) left += len end return part @@ -130,7 +136,7 @@ end """ greedy_contiguous_partition(weight, required_partitions, num_items=length(weight)) -Partition `1:num_items` into atmost `required_partitions` number of contiguous partitions with +Partition `1:num_items` into at most `required_partitions` number of contiguous partitions with the objective of minimising the largest partition. The size of a partition is equal to the num of the weight of its elements. `weight[i] > 0`. @@ -140,44 +146,40 @@ Time: O(num_items+required_partitions) Requires only one iteration over `weight` but may not output the optimal partition. ### Implementation Notes -`Balance(wt, left, right, n_items, n_part) = +`Balance(wt, left, right, n_items, n_part) = max(sum(wt[left:right])*(n_part-1), sum(wt[right+1:n_items]))`. Find `right` that minimises `Balance(weight, 1, right, num_items, required_partitions)`. Set the first partition as `1:right`. Repeat on indices `right+1:num_items` and one less partition. """ function greedy_contiguous_partition( - weight::Vector{<:Integer}, - required_partitions::Integer, - num_items::U=length(weight) - ) where U <: Integer - + weight::Vector{<:Integer}, required_partitions::Integer, num_items::U=length(weight) +) where {U<:Integer} suffix_sum = cumsum(reverse(weight)) - reverse!(suffix_sum) - push!(suffix_sum, 0) #Eg. [2, 3, 1] => [6, 4, 1, 0] + reverse!(suffix_sum) + push!(suffix_sum, 0) # Eg. [2, 3, 1] => [6, 4, 1, 0] partitions = Vector{UnitRange{U}}() sizehint!(partitions, required_partitions) left = one(U) - for partitions_remain in reverse(1:(required_partitions-1)) - - left >= num_items && break + for partitions_remain in reverse(1:(required_partitions - 1)) + left >= num_items && break - partition_size = weight[left]*partitions_remain #At least one item in each partition + partition_size = weight[left] * partitions_remain # At least one item in each partition right = left - #Find right: sum(wt[left:right])*partitions_remain and sum(wt[(right+1):num_items]) is balanced - while right+one(U) < num_items && partition_size < suffix_sum[right+one(U)] + # Find right: sum(wt[left:right])*partitions_remain and sum(wt[(right+1):num_items]) is balanced + while right + one(U) < num_items && partition_size < suffix_sum[right + one(U)] right += one(U) - partition_size += weight[right]*partitions_remain + partition_size += weight[right] * partitions_remain end - #max( sum(wt[left:right]), sum(wt[(right+1):num_items]) ) = partition_size - #max( sum(wt[left:(right-1)]), sum(wt[right:num_items]) ) = suffix_sum[right] + # max( sum(wt[left:right]), sum(wt[(right+1):num_items]) ) = partition_size + # max( sum(wt[left:(right-1)]), sum(wt[right:num_items]) ) = suffix_sum[right] if left != right && partition_size > suffix_sum[right] right -= one(U) end - + push!(partitions, left:right) left = right + one(U) end @@ -189,7 +191,7 @@ end """ optimal_contiguous_partition(weight, required_partitions, num_items=length(weight)) -Partition `1:num_items` into atmost `required_partitions` number of contiguous partitions such +Partition `1:num_items` into at most `required_partitions` number of contiguous partitions such that the largest partition is minimised. The size of a partition is equal to the sum of the weight of its elements. `weight[i] > 0`. @@ -198,22 +200,19 @@ The size of a partition is equal to the sum of the weight of its elements. Time: O(num_items*log(sum(weight))) ### Implementation Notes -Binary Search for the partitioning over `[fld(sum(weight)-1, required_partitions), sum(weight)]`. +Binary Search for the partitioning over `[fld(sum(weight)-1, required_partitions), sum(weight)]`. """ function optimal_contiguous_partition( - weight::Vector{<:Integer}, - required_partitions::Integer, - num_items::U=length(weight) - ) where U <: Integer - + weight::Vector{<:Integer}, required_partitions::Integer, num_items::U=length(weight) +) where {U<:Integer} item_it = Iterators.take(weight, num_items) up_bound = sum(item_it) # Smallest known possible value - low_bound = fld(up_bound-1, required_partitions) # Largest known impossible value + low_bound = fld(up_bound - 1, required_partitions) # Largest known impossible value # Find optimal balance - while up_bound > low_bound+1 - search_for = fld(up_bound+low_bound, 2) + while up_bound > low_bound + 1 + search_for = fld(up_bound + low_bound, 2) sum_part = 0 remain_part = required_partitions @@ -246,7 +245,7 @@ function optimal_contiguous_partition( for (i, w) in enumerate(item_it) sum_part += w if sum_part > best_balance - push!(partitions, left:(i-1)) + push!(partitions, left:(i - 1)) sum_part = w left = i end @@ -269,7 +268,7 @@ isbounded(n::BigInt) = false Returns true if `typemax(T)` of a type `T <: Integer` exists. """ -isbounded(::Type{T}) where {T <: Integer} = isconcretetype(T) +isbounded(::Type{T}) where {T<:Integer} = isconcretetype(T) isbounded(::Type{BigInt}) = false """ diff --git a/src/vertexcover/degree_vertex_cover.jl b/src/vertexcover/degree_vertex_cover.jl index 505eb93f0..528eabd45 100644 --- a/src/vertexcover/degree_vertex_cover.jl +++ b/src/vertexcover/degree_vertex_cover.jl @@ -30,12 +30,8 @@ julia> vertex_cover(cycle_graph(3), DegreeVertexCover()) 3 ``` """ -function vertex_cover( - g::AbstractGraph{T}, - alg::DegreeVertexCover - ) where T <: Integer - - nvg = nv(g) +function vertex_cover(g::AbstractGraph{T}, alg::DegreeVertexCover) where {T<:Integer} + nvg = nv(g) in_cover = falses(nvg) length_cover = 0 degree_queue = PriorityQueue(Base.Order.Reverse, enumerate(degree(g))) @@ -46,11 +42,10 @@ function vertex_cover( length_cover += 1 @inbounds @simd for u in neighbors(g, v) - if !in_cover[u] + if !in_cover[u] degree_queue[u] -= 1 end end end return Graphs.findall!(in_cover, Vector{T}(undef, length_cover)) end - diff --git a/src/vertexcover/random_vertex_cover.jl b/src/vertexcover/random_vertex_cover.jl index 97e9dcfe7..7a84cfa67 100644 --- a/src/vertexcover/random_vertex_cover.jl +++ b/src/vertexcover/random_vertex_cover.jl @@ -24,11 +24,11 @@ Approximation Factor: 2 function vertex_cover( g::AbstractGraph{T}, alg::RandomVertexCover; - rng::Union{Nothing, AbstractRNG}=nothing, seed::Union{Nothing, Integer}=nothing -) where T <: Integer - + rng::Union{Nothing,AbstractRNG}=nothing, + seed::Union{Nothing,Integer}=nothing, +) where {T<:Integer} (ne(g) > 0) || return Vector{T}() #Shuffle raises error - nvg = nv(g) + nvg = nv(g) in_cover = falses(nvg) length_cover = 0 @@ -44,4 +44,3 @@ function vertex_cover( return Graphs.findall!(in_cover, Vector{T}(undef, length_cover)) end - diff --git a/test/biconnectivity/biconnect.jl b/test/biconnectivity/biconnect.jl index d0184f8cb..93e31a42d 100644 --- a/test/biconnectivity/biconnect.jl +++ b/test/biconnectivity/biconnect.jl @@ -15,11 +15,13 @@ add_edge!(gint, 9, 10) add_edge!(gint, 11, 12) - a = [[Edge(3, 5), Edge(4, 5), Edge(2, 4), Edge(3, 4), Edge(2, 3)], - [Edge(9, 10)], - [Edge(6, 9), Edge(8, 9), Edge(6, 8)], - [Edge(1, 7), Edge(6, 7), Edge(2, 6), Edge(1, 2)], - [Edge(11, 12)]] + a = [ + [Edge(3, 5), Edge(4, 5), Edge(2, 4), Edge(3, 4), Edge(2, 3)], + [Edge(9, 10)], + [Edge(6, 9), Edge(8, 9), Edge(6, 8)], + [Edge(1, 7), Edge(6, 7), Edge(2, 6), Edge(1, 2)], + [Edge(11, 12)], + ] for g in testgraphs(gint) bcc = @inferred(biconnected_components(g)) @@ -42,7 +44,11 @@ gint = blockdiag(g, h) add_edge!(gint, 4, 5) - a = [[Edge(5, 8), Edge(7, 8), Edge(6, 7), Edge(5, 6)], [Edge(4, 5)], [Edge(1, 4), Edge(3, 4), Edge(2, 3), Edge(1, 2)]] + a = [ + [Edge(5, 8), Edge(7, 8), Edge(6, 7), Edge(5, 6)], + [Edge(4, 5)], + [Edge(1, 4), Edge(3, 4), Edge(2, 3), Edge(1, 2)], + ] for g in testgraphs(gint) bcc = @inferred(biconnected_components(g)) @@ -52,7 +58,7 @@ # Non regression test for #13 g = complete_graph(4) - a = [[Edge(2,4), Edge(1,4), Edge(3,4), Edge(1,3), Edge(2,3), Edge(1,2)]] + a = [[Edge(2, 4), Edge(1, 4), Edge(3, 4), Edge(1, 3), Edge(2, 3), Edge(1, 2)]] bcc = @inferred(biconnected_components(g)) @test bcc == a end diff --git a/test/biconnectivity/bridge.jl b/test/biconnectivity/bridge.jl index 05013b5f2..7526b5fe6 100644 --- a/test/biconnectivity/bridge.jl +++ b/test/biconnectivity/bridge.jl @@ -22,12 +22,7 @@ for g in testgraphs(gint) brd = @inferred(bridges(g)) - ans = [ - Edge(1, 2), - Edge(8, 9), - Edge(7, 8), - Edge(11, 12), - ] + ans = [Edge(1, 2), Edge(8, 9), Edge(7, 8), Edge(11, 12)] @test brd == ans end for level in 1:6 @@ -42,11 +37,9 @@ hint = blockdiag(wheel_graph(5), wheel_graph(5)) add_edge!(hint, 5, 6) for h in (hint, Graph{UInt8}(hint), Graph{Int16}(hint)) - @test @inferred(bridges(h)) == [ - Edge(5, 6), - ] + @test @inferred(bridges(h)) == [Edge(5, 6)] end - dir = SimpleDiGraph(10, 10, rng=rng) + dir = SimpleDiGraph(10, 10; rng=rng) @test_throws MethodError bridges(dir) end diff --git a/test/centrality/betweenness.jl b/test/centrality/betweenness.jl index d6f59eda6..e6d199bf6 100644 --- a/test/centrality/betweenness.jl +++ b/test/centrality/betweenness.jl @@ -2,7 +2,9 @@ rng = StableRNG(1) # self loops s2 = SimpleDiGraph(3) - add_edge!(s2, 1, 2); add_edge!(s2, 2, 3); add_edge!(s2, 3, 3) + add_edge!(s2, 1, 2) + add_edge!(s2, 2, 3) + add_edge!(s2, 3, 3) s1 = SimpleGraph(s2) g3 = path_graph(5) @@ -10,17 +12,15 @@ c = vec(readdlm(joinpath(testdir, "testdata", "graph-50-500-bc.txt"), ',')) for g in testdigraphs(gint) - z = @inferred(betweenness_centrality(g)) - @test map(Float32, z) == map(Float32, c) + z = @inferred(betweenness_centrality(g)) + @test map(Float32, z) == map(Float32, c) - y = @inferred(betweenness_centrality(g, endpoints=true, normalize=false)) + y = @inferred(betweenness_centrality(g, endpoints=true, normalize=false)) @test round.(y[1:3], digits=4) == round.([122.10760591498584, 159.0072453120582, 176.39547945994505], digits=4) - - - x = @inferred(betweenness_centrality(g, 3, rng=rng)) - x2 = @inferred(betweenness_centrality(g, collect(1:20))) + x = @inferred(betweenness_centrality(g, 3, rng=rng)) + x2 = @inferred(betweenness_centrality(g, collect(1:20))) @test length(x) == 50 @test length(x2) == 50 @@ -28,17 +28,16 @@ @test @inferred(betweenness_centrality(s1)) == [0, 1, 0] @test @inferred(betweenness_centrality(s2)) == [0, 0.5, 0] - + g = SimpleGraph(2) add_edge!(g, 1, 2) - z = @inferred(betweenness_centrality(g; normalize=true)) + z = @inferred(betweenness_centrality(g; normalize=true)) @test z[1] == z[2] == 0.0 - z2 = @inferred(betweenness_centrality(g, vertices(g))) - z3 = @inferred(betweenness_centrality(g, [vertices(g);])) + z2 = @inferred(betweenness_centrality(g, vertices(g))) + z3 = @inferred(betweenness_centrality(g, [vertices(g);])) @test z == z2 == z3 - - z = @inferred(betweenness_centrality(g3; normalize=false)) + z = @inferred(betweenness_centrality(g3; normalize=false)) @test z[1] == z[5] == 0.0 # Weighted Graph tests @@ -50,30 +49,57 @@ add_edge!(g, 5, 6) add_edge!(g, 5, 4) - distmx = [ 0.0 2.0 0.0 0.0 0.0 0.0; - 2.0 0.0 4.2 0.0 1.2 0.0; - 0.0 4.2 0.0 5.5 0.0 0.0; - 0.0 0.0 5.5 0.0 0.9 0.0; - 0.0 1.2 0.0 0.9 0.0 0.6; - 0.0 0.0 0.0 0.0 0.6 0.0;] + distmx = [ + 0.0 2.0 0.0 0.0 0.0 0.0 + 2.0 0.0 4.2 0.0 1.2 0.0 + 0.0 4.2 0.0 5.5 0.0 0.0 + 0.0 0.0 5.5 0.0 0.9 0.0 + 0.0 1.2 0.0 0.9 0.0 0.6 + 0.0 0.0 0.0 0.0 0.6 0.0 + ] - @test isapprox(betweenness_centrality(g, vertices(g), distmx; normalize=false), [0.0,6.0,0.0,0.0,6.0,0.0]) - @test isapprox(betweenness_centrality(g, vertices(g), distmx; normalize=false, endpoints=true), [5.0,11.0,5.0,5.0,11.0,5.0]) - @test isapprox(betweenness_centrality(g, vertices(g), distmx; normalize=true), [0.0, 0.6000000000000001, 0.0, 0.0, 0.6000000000000001, 0.0]) - @test isapprox(betweenness_centrality(g, vertices(g), distmx; normalize=true, endpoints=true), [0.5,1.1,0.5,0.5,1.1,0.5]) + @test isapprox( + betweenness_centrality(g, vertices(g), distmx; normalize=false), + [0.0, 6.0, 0.0, 0.0, 6.0, 0.0], + ) + @test isapprox( + betweenness_centrality(g, vertices(g), distmx; normalize=false, endpoints=true), + [5.0, 11.0, 5.0, 5.0, 11.0, 5.0], + ) + @test isapprox( + betweenness_centrality(g, vertices(g), distmx; normalize=true), + [0.0, 0.6000000000000001, 0.0, 0.0, 0.6000000000000001, 0.0], + ) + @test isapprox( + betweenness_centrality(g, vertices(g), distmx; normalize=true, endpoints=true), + [0.5, 1.1, 0.5, 0.5, 1.1, 0.5], + ) adjmx2 = [0 1 0; 1 0 1; 1 1 0] # digraph a2 = SimpleDiGraph(adjmx2) for g in testdigraphs(a2) distmx2 = [Inf 2.0 Inf; 3.2 Inf 4.2; 5.5 6.1 Inf] - c2 = [0.24390243902439027,0.27027027027027023,0.1724137931034483] - @test isapprox(betweenness_centrality(g, vertices(g), distmx2; normalize=false), [0.0,1.0,0.0]) - @test isapprox(betweenness_centrality(g, vertices(g), distmx2; normalize=false, endpoints=true), [4.0,5.0,4.0]) - @test isapprox(betweenness_centrality(g, vertices(g), distmx2; normalize=true), [0.0,0.5,0.0]) - @test isapprox(betweenness_centrality(g, vertices(g), distmx2; normalize=true, endpoints=true), [2.0,2.5,2.0]) + c2 = [0.24390243902439027, 0.27027027027027023, 0.1724137931034483] + @test isapprox( + betweenness_centrality(g, vertices(g), distmx2; normalize=false), + [0.0, 1.0, 0.0], + ) + @test isapprox( + betweenness_centrality( + g, vertices(g), distmx2; normalize=false, endpoints=true + ), + [4.0, 5.0, 4.0], + ) + @test isapprox( + betweenness_centrality(g, vertices(g), distmx2; normalize=true), [0.0, 0.5, 0.0] + ) + @test isapprox( + betweenness_centrality(g, vertices(g), distmx2; normalize=true, endpoints=true), + [2.0, 2.5, 2.0], + ) end # test #1405 / #1406 g = grid([50, 50]) - z = betweenness_centrality(g, normalize=false) - @test maximum(z) < nv(g) * (nv(g)-1) + z = betweenness_centrality(g; normalize=false) + @test maximum(z) < nv(g) * (nv(g) - 1) end diff --git a/test/centrality/closeness.jl b/test/centrality/closeness.jl index dc7c79372..620676bee 100644 --- a/test/centrality/closeness.jl +++ b/test/centrality/closeness.jl @@ -1,6 +1,9 @@ @testset "Closeness" begin g5 = SimpleDiGraph(4) - add_edge!(g5, 1, 2); add_edge!(g5, 2, 3); add_edge!(g5, 1, 3); add_edge!(g5, 3, 4) + add_edge!(g5, 1, 2) + add_edge!(g5, 2, 3) + add_edge!(g5, 1, 3) + add_edge!(g5, 3, 4) for g in testdigraphs(g5) y = @inferred(closeness_centrality(g; normalize=false)) @@ -27,7 +30,7 @@ @test z[1] == z[2] == 0.25 @test z[3] == z[4] == z[5] == 0.0 end - + adjmx1 = [0 1 0; 1 0 1; 0 1 0] # graph a1 = SimpleGraph(adjmx1) for g in testgraphs(a1) diff --git a/test/centrality/degree.jl b/test/centrality/degree.jl index 6144bc282..beab2f2c4 100644 --- a/test/centrality/degree.jl +++ b/test/centrality/degree.jl @@ -1,9 +1,13 @@ @testset "Degree" begin g5 = SimpleDiGraph(4) - add_edge!(g5, 1, 2); add_edge!(g5, 2, 3); add_edge!(g5, 1, 3); add_edge!(g5, 3, 4) + add_edge!(g5, 1, 2) + add_edge!(g5, 2, 3) + add_edge!(g5, 1, 3) + add_edge!(g5, 3, 4) for g in testdigraphs(g5) - @test @inferred(degree_centrality(g)) == [0.6666666666666666, 0.6666666666666666, 1.0, 0.3333333333333333] - @test @inferred(indegree_centrality(g, normalize=false)) == [0.0, 1.0, 2.0, 1.0] - @test @inferred(outdegree_centrality(g; normalize=false)) == [2.0, 1.0, 1.0, 0.0] + @test @inferred(degree_centrality(g)) == + [0.6666666666666666, 0.6666666666666666, 1.0, 0.3333333333333333] + @test @inferred(indegree_centrality(g, normalize=false)) == [0.0, 1.0, 2.0, 1.0] + @test @inferred(outdegree_centrality(g; normalize=false)) == [2.0, 1.0, 1.0, 0.0] end end diff --git a/test/centrality/eigenvector.jl b/test/centrality/eigenvector.jl index 33c852562..de75cc8df 100644 --- a/test/centrality/eigenvector.jl +++ b/test/centrality/eigenvector.jl @@ -4,10 +4,17 @@ for g in testgraphs(g1) y = @inferred(eigenvector_centrality(g)) - @test round.(y, digits=3) == round.([ - 0.3577513877490464, 0.3577513877490464, 0.5298987782873977, - 0.5298987782873977, 0.4271328349194304 - ], digits=3) + @test round.(y, digits=3) == + round.( + [ + 0.3577513877490464, + 0.3577513877490464, + 0.5298987782873977, + 0.5298987782873977, + 0.4271328349194304, + ], + digits=3, + ) end for g in testdigraphs(g2) y = @inferred(eigenvector_centrality(g)) diff --git a/test/centrality/katz.jl b/test/centrality/katz.jl index 99b38d249..c559c0051 100644 --- a/test/centrality/katz.jl +++ b/test/centrality/katz.jl @@ -1,6 +1,9 @@ @testset "Katz" begin g5 = SimpleDiGraph(4) - add_edge!(g5, 1, 2); add_edge!(g5, 2, 3); add_edge!(g5, 1, 3); add_edge!(g5, 3, 4) + add_edge!(g5, 1, 2) + add_edge!(g5, 2, 3) + add_edge!(g5, 1, 3) + add_edge!(g5, 3, 4) for g in testdigraphs(g5) z = @inferred(katz_centrality(g, 0.4)) @test round.(z, digits=2) == [0.32, 0.44, 0.62, 0.56] diff --git a/test/centrality/pagerank.jl b/test/centrality/pagerank.jl index 7c8e31214..f0134e9bc 100644 --- a/test/centrality/pagerank.jl +++ b/test/centrality/pagerank.jl @@ -5,19 +5,24 @@ M = Matrix{Float64}(adjacency_matrix(g)) M = M' M[:, danglingnodes] .= sum(danglingnodes) ./ nv(g) - M = M * Diagonal(1 ./ sum(M, dims=1)[:]) - @assert all(1.01 .>= sum(M, dims=1) .>= 0.999) - # v = inv(I-β*M) * ((1-β)/nv(g) * ones(nv(g), 1)) + M = M * Diagonal(1 ./ sum(M; dims=1)[:]) + @assert all(1.01 .>= sum(M; dims=1) .>= 0.999) + # v = inv(I-β*M) * ((1-β)/nv(g) * ones(nv(g), 1)) v = inv(I - α * M) * ((1 - α) / nv(g) * ones(nv(g), 1)) return v end g5 = SimpleDiGraph(4) - add_edge!(g5, 1, 2); add_edge!(g5, 2, 3); add_edge!(g5, 1, 3); add_edge!(g5, 3, 4) + add_edge!(g5, 1, 2) + add_edge!(g5, 2, 3) + add_edge!(g5, 1, 3) + add_edge!(g5, 3, 4) g6 = SimpleGraph(4) - add_edge!(g6, 1, 2); add_edge!(g6, 2, 3); add_edge!(g6, 1, 3); add_edge!(g6, 3, 4) + add_edge!(g6, 1, 2) + add_edge!(g6, 2, 3) + add_edge!(g6, 1, 3) + add_edge!(g6, 3, 4) for α in [0.75, 0.85] - for g in testdigraphs(g5) @test pagerank(g)[3] ≈ 0.318 atol = 0.001 @test length(@inferred(pagerank(g))) == nv(g) diff --git a/test/centrality/radiality.jl b/test/centrality/radiality.jl index 9caadd684..d8b2ec571 100644 --- a/test/centrality/radiality.jl +++ b/test/centrality/radiality.jl @@ -3,7 +3,7 @@ c = vec(readdlm(joinpath(testdir, "testdata", "graph-50-500-rc.txt"), ',')) for g in testdigraphs(gint) - z = @inferred(radiality_centrality(g)) + z = @inferred(radiality_centrality(g)) @test z == c end @@ -11,7 +11,7 @@ add_vertex!(g1) add_edge!(g1, 4, 5) for g in testgraphs(g1) - z = @inferred(radiality_centrality(g)) - @test z ≈ [5 // 6, 3 // 4, 5 // 6, 11 // 12, 2 // 3] + z = @inferred(radiality_centrality(g)) + @test z ≈ [5//6, 3//4, 5//6, 11//12, 2//3] end end diff --git a/test/centrality/stress.jl b/test/centrality/stress.jl index f9d9e1d43..db94a8f35 100644 --- a/test/centrality/stress.jl +++ b/test/centrality/stress.jl @@ -4,11 +4,11 @@ c = vec(readdlm(joinpath(testdir, "testdata", "graph-50-500-sc.txt"), ',')) for g in testdigraphs(gint) - z = @inferred(stress_centrality(g)) + z = @inferred(stress_centrality(g)) @test z == c - x = @inferred(stress_centrality(g, 3, rng=rng)) - x2 = @inferred(stress_centrality(g, collect(1:20))) + x = @inferred(stress_centrality(g, 3, rng=rng)) + x2 = @inferred(stress_centrality(g, collect(1:20))) @test length(x) == 50 @test length(x2) == 50 end @@ -17,7 +17,7 @@ add_vertex!(g1) add_edge!(g1, 4, 5) for g in testgraphs(g1) - z = @inferred(stress_centrality(g)) + z = @inferred(stress_centrality(g)) @test z == [4, 2, 4, 10, 0] end end diff --git a/test/community/assortativity.jl b/test/community/assortativity.jl index 92c50b20a..d6318348d 100644 --- a/test/community/assortativity.jl +++ b/test/community/assortativity.jl @@ -3,21 +3,25 @@ using Statistics @testset "Assortativity" begin # Test definition of assortativity as Pearson correlation coefficient # between excess of degrees - @testset "Small graphs" for n = 5:10 - @test @inferred assortativity(wheel_graph(n)) ≈ -1/3 + @testset "Small graphs" for n in 5:10 + @test @inferred assortativity(wheel_graph(n)) ≈ -1 / 3 end - @testset "Directed ($seed)" for seed in [1, 2, 3], (n, ne) in [(14, 18), (10, 22), (7, 16)] + @testset "Directed ($seed)" for seed in [1, 2, 3], + (n, ne) in [(14, 18), (10, 22), (7, 16)] + g = erdos_renyi(n, ne; is_directed=true, rng=StableRNG(seed)) assort = assortativity(g) - x = [outdegree(g, src(d))-1 for d in edges(g)] - y = [indegree(g, dst(d))-1 for d in edges(g)] + x = [outdegree(g, src(d)) - 1 for d in edges(g)] + y = [indegree(g, dst(d)) - 1 for d in edges(g)] @test @inferred assort ≈ cor(x, y) end - @testset "Undirected ($seed)" for seed in [1, 2, 3], (n, ne) in [(14, 18), (10, 22), (7, 16)] + @testset "Undirected ($seed)" for seed in [1, 2, 3], + (n, ne) in [(14, 18), (10, 22), (7, 16)] + g = erdos_renyi(n, ne; is_directed=false, rng=StableRNG(seed)) assort = assortativity(g) - x = [outdegree(g, src(d))-1 for d in edges(g)] - y = [indegree(g, dst(d))-1 for d in edges(g)] - @test @inferred assort ≈ cor([x;y], [y;x]) + x = [outdegree(g, src(d)) - 1 for d in edges(g)] + y = [indegree(g, dst(d)) - 1 for d in edges(g)] + @test @inferred assort ≈ cor([x; y], [y; x]) end end diff --git a/test/community/clique_percolation.jl b/test/community/clique_percolation.jl index f37c3f8ef..c9aa484dc 100644 --- a/test/community/clique_percolation.jl +++ b/test/community/clique_percolation.jl @@ -1,11 +1,11 @@ @testset "Clique percolation" begin function setofsets(array_of_arrays) - Set(map(BitSet, array_of_arrays)) + return Set(map(BitSet, array_of_arrays)) end function test_cliques(graph, expected) # Make test results insensitive to ordering - Set(clique_percolation(graph)) == setofsets(expected) + return Set(clique_percolation(graph)) == setofsets(expected) end g = Graph(5) diff --git a/test/community/cliques.jl b/test/community/cliques.jl index a66b79c5c..ba062212d 100644 --- a/test/community/cliques.jl +++ b/test/community/cliques.jl @@ -7,22 +7,22 @@ @testset "Cliques" begin function setofsets(array_of_arrays) - Set(map(Set, array_of_arrays)) + return Set(map(Set, array_of_arrays)) end function test_cliques(graph, expected) # Make test results insensitive to ordering - setofsets(@inferred(maximal_cliques(graph))) == setofsets(expected) + return setofsets(@inferred(maximal_cliques(graph))) == setofsets(expected) end gx = SimpleGraph(3) add_edge!(gx, 1, 2) for g in testgraphs(gx) - @test test_cliques(g, Array[[1, 2], [3]]) + @test test_cliques(g, Array[[1, 2], [3]]) end add_edge!(gx, 2, 3) for g in testgraphs(gx) - @test test_cliques(g, Array[[1, 2], [2, 3]]) + @test test_cliques(g, Array[[1, 2], [2, 3]]) end # Test for "pivotdonenbrs not defined" bug h = SimpleGraph(6) @@ -36,7 +36,7 @@ add_edge!(h, 5, 6) for g in testgraphs(h) - @test !isempty(@inferred(maximal_cliques(g))) + @test !isempty(@inferred(maximal_cliques(g))) end # test for extra cliques bug @@ -50,6 +50,6 @@ add_edge!(h, 4, 7) add_edge!(h, 5, 7) for g in testgraphs(h) - @test test_cliques(h, Array[[7, 4, 5], [2, 6], [3, 5], [3, 6], [3, 1]]) + @test test_cliques(h, Array[[7, 4, 5], [2, 6], [3, 5], [3, 6], [3, 1]]) end end diff --git a/test/community/core-periphery.jl b/test/community/core-periphery.jl index 7772881d1..57347d30a 100644 --- a/test/community/core-periphery.jl +++ b/test/community/core-periphery.jl @@ -4,7 +4,7 @@ c = core_periphery_deg(g) @test @inferred(degree(g, 1)) == 9 @test c[1] == 1 - for i = 2:10 + for i in 2:10 @test c[i] == 2 end end @@ -16,10 +16,10 @@ c = @inferred(core_periphery_deg(g)) @test c[1] == 1 @test c[11] == 1 - for i = 2:10 + for i in 2:10 @test c[i] == 2 end - for i = 12:20 + for i in 12:20 @test c[i] == 2 end end diff --git a/test/community/label_propagation.jl b/test/community/label_propagation.jl index 013938e41..5551a0fac 100644 --- a/test/community/label_propagation.jl +++ b/test/community/label_propagation.jl @@ -4,15 +4,15 @@ g10 = complete_graph(n) for g in testgraphs(g10) z = copy(g) - for k = 2:5 + for k in 2:5 z = blockdiag(z, g) add_edge!(z, (k - 1) * n, k * n) c, ch = @inferred(label_propagation(z; rng=rng)) a = collect(n:n:(k * n)) - a = Int[div(i - 1, n) + 1 for i = 1:(k * n)] - # check the number of communities + a = Int[div(i - 1, n) + 1 for i in 1:(k * n)] + # check the number of communities @test length(unique(a)) == length(unique(c)) - # check the partition + # check the partition @test a == c end end diff --git a/test/community/modularity.jl b/test/community/modularity.jl index 1b05564a1..78a417155 100644 --- a/test/community/modularity.jl +++ b/test/community/modularity.jl @@ -1,17 +1,17 @@ @testset "Modularity" begin - + # 1. undirected test cases n = 10 m = n * (n - 1) / 2 c = ones(Int, n) gint = complete_graph(n) for g in testgraphs(gint) - @test @inferred(modularity(g, c)) == 0 + @test @inferred(modularity(g, c)) == 0 end gint = SimpleGraph(n) for g in testgraphs(gint) - @test @inferred(modularity(g, c)) == 0 + @test @inferred(modularity(g, c)) == 0 end barbell = blockdiag(complete_graph(3), complete_graph(3)) @@ -58,12 +58,14 @@ barbell = blockdiag(triangle, triangle) add_edge!(barbell, 1, 4) # this edge has a weight of 5 c = [1, 1, 1, 2, 2, 2] - d = [[0 1 1 5 0 0] - [1 0 1 0 0 0] - [1 1 0 0 0 0] - [5 0 0 0 1 1] - [0 0 0 1 0 1] - [0 0 0 1 1 0]] + d = [ + [0 1 1 5 0 0] + [1 0 1 0 0 0] + [1 1 0 0 0 0] + [5 0 0 0 1 1] + [0 0 0 1 0 1] + [0 0 0 1 1 0] + ] for g in testgraphs(barbell) Q = @inferred(modularity(g, c, distmx=d)) @test isapprox(Q, 0.045454545454545456, atol=1e-3) @@ -78,12 +80,14 @@ barbell = blockdiag(triangle, triangle) add_edge!(barbell, 1, 4) # this edge has a weight of 5 c = [1, 1, 1, 2, 2, 2] - d = [[0 1 0 5 0 0] - [0 0 1 0 0 0] - [1 0 0 0 0 0] - [0 0 0 0 1 0] - [0 0 0 0 0 1] - [0 0 0 1 0 0]] + d = [ + [0 1 0 5 0 0] + [0 0 1 0 0 0] + [1 0 0 0 0 0] + [0 0 0 0 1 0] + [0 0 0 0 0 1] + [0 0 0 1 0 0] + ] for g in testdigraphs(barbell) Q = @inferred(modularity(g, c, distmx=d)) @test isapprox(Q, 0.1487603305785124, atol=1e-3) diff --git a/test/community/rich_club.jl b/test/community/rich_club.jl index 4f5d5509b..65a5e421c 100644 --- a/test/community/rich_club.jl +++ b/test/community/rich_club.jl @@ -5,14 +5,18 @@ @test @inferred rich_club(star_graph(_n), 1) ≈ 2 / _n @test @inferred rich_club(DiGraph(star_graph(_n)), 1) ≈ 2 / _n end - @testset "Directed ($seed)" for seed in [1, 2, 3], (n, ne) in [(14, 18), (10, 22), (7, 16)] + @testset "Directed ($seed)" for seed in [1, 2, 3], + (n, ne) in [(14, 18), (10, 22), (7, 16)] + g = erdos_renyi(n, ne; is_directed=true, rng=StableRNG(seed)) _r = rich_club(g, 1) - @test @inferred rich_club(g, 1) > 0. + @test @inferred rich_club(g, 1) > 0.0 end - @testset "Undirected ($seed)" for seed in [1, 2, 3], (n, ne) in [(14, 18), (10, 22), (7, 16)] + @testset "Undirected ($seed)" for seed in [1, 2, 3], + (n, ne) in [(14, 18), (10, 22), (7, 16)] + g = erdos_renyi(n, ne; is_directed=false, rng=StableRNG(seed)) _r = rich_club(g, 1) - @test @inferred rich_club(g, 1) > 0. + @test @inferred rich_club(g, 1) > 0.0 end end diff --git a/test/connectivity.jl b/test/connectivity.jl index 5d1d0395a..ae6adebf3 100644 --- a/test/connectivity.jl +++ b/test/connectivity.jl @@ -7,7 +7,6 @@ add_edge!(gx, 8, 9) add_edge!(gx, 10, 9) - for g in testgraphs(gx) @test @inferred(!is_connected(g)) cc = @inferred(connected_components(g)) @@ -25,7 +24,6 @@ @test @inferred(is_connected(g)) end - g10 = SimpleDiGraph(4) add_edge!(g10, 1, 3) add_edge!(g10, 2, 4) @@ -39,26 +37,35 @@ g10 = SimpleDiGraph(20) for g in testdigraphs(g10) - for m = 1:50 + for m in 1:50 i = rand(1:10) j = rand(11:20) if rand() < 0.5 i, j = j, i end - if !has_edge(g, i, j) - add_edge!(g, i, j) - @test @inferred(is_bipartite(g)) - end + if !has_edge(g, i, j) + add_edge!(g, i, j) + @test @inferred(is_bipartite(g)) + end end end # graph from https://en.wikipedia.org/wiki/Strongly_connected_component h = SimpleDiGraph(8) - add_edge!(h, 1, 2); add_edge!(h, 2, 3); add_edge!(h, 2, 5); - add_edge!(h, 2, 6); add_edge!(h, 3, 4); add_edge!(h, 3, 7); - add_edge!(h, 4, 3); add_edge!(h, 4, 8); add_edge!(h, 5, 1); - add_edge!(h, 5, 6); add_edge!(h, 6, 7); add_edge!(h, 7, 6); - add_edge!(h, 8, 4); add_edge!(h, 8, 7) + add_edge!(h, 1, 2) + add_edge!(h, 2, 3) + add_edge!(h, 2, 5) + add_edge!(h, 2, 6) + add_edge!(h, 3, 4) + add_edge!(h, 3, 7) + add_edge!(h, 4, 3) + add_edge!(h, 4, 8) + add_edge!(h, 5, 1) + add_edge!(h, 5, 6) + add_edge!(h, 6, 7) + add_edge!(h, 7, 6) + add_edge!(h, 8, 4) + add_edge!(h, 8, 7) for g in testdigraphs(h) @test @inferred(is_weakly_connected(g)) scc = @inferred(strongly_connected_components(g)) @@ -70,114 +77,143 @@ @test length(wcc) == 1 && length(wcc[1]) == nv(g) end - function scc_ok(graph) - #Check that all SCC really are strongly connected - scc = @inferred(strongly_connected_components(graph)) - scc_as_subgraphs = map(i -> graph[i], scc) - return all(is_strongly_connected, scc_as_subgraphs) + # Check that all SCC really are strongly connected + scc = @inferred(strongly_connected_components(graph)) + scc_as_subgraphs = map(i -> graph[i], scc) + return all(is_strongly_connected, scc_as_subgraphs) end - + function scc_k_ok(graph) - #Check that all SCC really are strongly connected - scc_k = @inferred(strongly_connected_components_kosaraju(graph)) - scc_k_as_subgraphs = map(i -> graph[i], scc_k) - return all(is_strongly_connected, scc_k_as_subgraphs) + # Check that all SCC really are strongly connected + scc_k = @inferred(strongly_connected_components_kosaraju(graph)) + scc_k_as_subgraphs = map(i -> graph[i], scc_k) + return all(is_strongly_connected, scc_k_as_subgraphs) end # the two graphs below are isomorphic (exchange 2 <--> 4) - h = SimpleDiGraph(4); add_edge!(h, 1, 4); add_edge!(h, 4, 2); add_edge!(h, 2, 3); add_edge!(h, 1, 3); + h = SimpleDiGraph(4) + add_edge!(h, 1, 4) + add_edge!(h, 4, 2) + add_edge!(h, 2, 3) + add_edge!(h, 1, 3) for g in testdigraphs(h) - @test scc_ok(g) - @test scc_k_ok(g) - + @test scc_ok(g) + @test scc_k_ok(g) end - h2 = SimpleDiGraph(4); add_edge!(h2, 1, 2); add_edge!(h2, 2, 4); add_edge!(h2, 4, 3); add_edge!(h2, 1, 3); + h2 = SimpleDiGraph(4) + add_edge!(h2, 1, 2) + add_edge!(h2, 2, 4) + add_edge!(h2, 4, 3) + add_edge!(h2, 1, 3) for g in testdigraphs(h2) - @test scc_ok(g) - @test scc_k_ok(g) + @test scc_ok(g) + @test scc_k_ok(g) end - - #Test case for empty graph + # Test case for empty graph h = SimpleDiGraph(0) for g in testdigraphs(h) - scc = @inferred(strongly_connected_components(g)) - scc_k = @inferred(strongly_connected_components_kosaraju(g)) - @test length(scc) == 0 - @test length(scc_k) == 0 - end - + scc = @inferred(strongly_connected_components(g)) + scc_k = @inferred(strongly_connected_components_kosaraju(g)) + @test length(scc) == 0 + @test length(scc_k) == 0 + end - #Test case for graph with one vertex + # Test case for graph with one vertex h = SimpleDiGraph(1) for g in testdigraphs(h) - scc = @inferred(strongly_connected_components(g)) - scc_k = @inferred(strongly_connected_components_kosaraju(g)) - @test length(scc) == 1 && scc[1] == [1] - @test length(scc_k) == 1 && scc[1] == [1] - end - + scc = @inferred(strongly_connected_components(g)) + scc_k = @inferred(strongly_connected_components_kosaraju(g)) + @test length(scc) == 1 && scc[1] == [1] + @test length(scc_k) == 1 && scc[1] == [1] + end - #Test case for graph with self loops - h = SimpleDiGraph(3); - add_edge!(h, 1, 1); add_edge!(h, 2, 2); add_edge!(h, 3, 3); - add_edge!(h, 1, 2); add_edge!(h, 2, 3); add_edge!(h, 2, 1); + # Test case for graph with self loops + h = SimpleDiGraph(3) + add_edge!(h, 1, 1) + add_edge!(h, 2, 2) + add_edge!(h, 3, 3) + add_edge!(h, 1, 2) + add_edge!(h, 2, 3) + add_edge!(h, 2, 1) for g in testdigraphs(h) - scc = @inferred(strongly_connected_components(g)) - scc_k = @inferred(strongly_connected_components_kosaraju(g)) - @test length(scc) == 2 - @test sort(scc[1]) == [3] - @test sort(scc[2]) == [1,2] - - @test length(scc_k) == 2 - @test sort(scc_k[1]) == [1,2] - @test sort(scc_k[2]) == [3] + scc = @inferred(strongly_connected_components(g)) + scc_k = @inferred(strongly_connected_components_kosaraju(g)) + @test length(scc) == 2 + @test sort(scc[1]) == [3] + @test sort(scc[2]) == [1, 2] + + @test length(scc_k) == 2 + @test sort(scc_k[1]) == [1, 2] + @test sort(scc_k[2]) == [3] end - - + h = SimpleDiGraph(6) - add_edge!(h, 1, 3); add_edge!(h, 3, 4); add_edge!(h, 4, 2); add_edge!(h, 2, 1) - add_edge!(h, 3, 5); add_edge!(h, 5, 6); add_edge!(h, 6, 4) + add_edge!(h, 1, 3) + add_edge!(h, 3, 4) + add_edge!(h, 4, 2) + add_edge!(h, 2, 1) + add_edge!(h, 3, 5) + add_edge!(h, 5, 6) + add_edge!(h, 6, 4) for g in testdigraphs(h) - scc = @inferred(strongly_connected_components(g)) - scc_k = @inferred(strongly_connected_components_kosaraju(g)) - @test length(scc) == 1 && sort(scc[1]) == [1:6;] - @test length(scc_k) == 1 && sort(scc_k[1]) == [1:6;] + scc = @inferred(strongly_connected_components(g)) + scc_k = @inferred(strongly_connected_components_kosaraju(g)) + @test length(scc) == 1 && sort(scc[1]) == [1:6;] + @test length(scc_k) == 1 && sort(scc_k[1]) == [1:6;] end # tests from Graphs.jl h = SimpleDiGraph(4) - add_edge!(h, 1, 2); add_edge!(h, 2, 3); add_edge!(h, 3, 1); add_edge!(h, 4, 1) + add_edge!(h, 1, 2) + add_edge!(h, 2, 3) + add_edge!(h, 3, 1) + add_edge!(h, 4, 1) for g in testdigraphs(h) - scc = @inferred(strongly_connected_components(g)) - scc_k = @inferred(strongly_connected_components_kosaraju(g)) - @test length(scc) == 2 && sort(scc[1]) == [1:3;] && sort(scc[2]) == [4] - @test length(scc_k) == 2 && sort(scc_k[2]) == [1:3;] && sort(scc_k[1]) == [4] + scc = @inferred(strongly_connected_components(g)) + scc_k = @inferred(strongly_connected_components_kosaraju(g)) + @test length(scc) == 2 && sort(scc[1]) == [1:3;] && sort(scc[2]) == [4] + @test length(scc_k) == 2 && sort(scc_k[2]) == [1:3;] && sort(scc_k[1]) == [4] end h = SimpleDiGraph(12) - add_edge!(h, 1, 2); add_edge!(h, 2, 3); add_edge!(h, 2, 4); add_edge!(h, 2, 5); - add_edge!(h, 3, 6); add_edge!(h, 4, 5); add_edge!(h, 4, 7); add_edge!(h, 5, 2); - add_edge!(h, 5, 6); add_edge!(h, 5, 7); add_edge!(h, 6, 3); add_edge!(h, 6, 8); - add_edge!(h, 7, 8); add_edge!(h, 7, 10); add_edge!(h, 8, 7); add_edge!(h, 9, 7); - add_edge!(h, 10, 9); add_edge!(h, 10, 11); add_edge!(h, 11, 12); add_edge!(h, 12, 10) + add_edge!(h, 1, 2) + add_edge!(h, 2, 3) + add_edge!(h, 2, 4) + add_edge!(h, 2, 5) + add_edge!(h, 3, 6) + add_edge!(h, 4, 5) + add_edge!(h, 4, 7) + add_edge!(h, 5, 2) + add_edge!(h, 5, 6) + add_edge!(h, 5, 7) + add_edge!(h, 6, 3) + add_edge!(h, 6, 8) + add_edge!(h, 7, 8) + add_edge!(h, 7, 10) + add_edge!(h, 8, 7) + add_edge!(h, 9, 7) + add_edge!(h, 10, 9) + add_edge!(h, 10, 11) + add_edge!(h, 11, 12) + add_edge!(h, 12, 10) for g in testdigraphs(h) - scc = @inferred(strongly_connected_components(g)) - scc_k = @inferred(strongly_connected_components_kosaraju(g)) - @test length(scc) == 4 - @test sort(scc[1]) == [7, 8, 9, 10, 11, 12] - @test sort(scc[2]) == [3, 6] - @test sort(scc[3]) == [2, 4, 5] - @test scc[4] == [1] - - @test length(scc_k) == 4 - @test sort(scc_k[1]) == [1] - @test sort(scc_k[2]) == [2, 4 ,5] - @test sort(scc_k[3]) == [3, 6] - @test sort(scc_k[4]) == [7, 8, 9, 10, 11, 12] + scc = @inferred(strongly_connected_components(g)) + scc_k = @inferred(strongly_connected_components_kosaraju(g)) + @test length(scc) == 4 + @test sort(scc[1]) == [7, 8, 9, 10, 11, 12] + @test sort(scc[2]) == [3, 6] + @test sort(scc[3]) == [2, 4, 5] + @test scc[4] == [1] + + @test length(scc_k) == 4 + @test sort(scc_k[1]) == [1] + @test sort(scc_k[2]) == [2, 4, 5] + @test sort(scc_k[3]) == [3, 6] + @test sort(scc_k[4]) == [7, 8, 9, 10, 11, 12] end # Test examples with self-loops from @@ -185,7 +221,9 @@ # figure 1 example fig1 = spzeros(5, 5) - fig1[[3, 4, 9, 10, 11, 13, 18, 19, 22, 24]] = [.5, .4, .1, 1., 1., .2, .3, .2, 1., .3] + fig1[[3, 4, 9, 10, 11, 13, 18, 19, 22, 24]] = [ + 0.5, 0.4, 0.1, 1.0, 1.0, 0.2, 0.3, 0.2, 1.0, 0.3 + ] fig1 = SimpleDiGraph(fig1) scc_fig1 = Vector[[2, 5], [1, 3, 4]] @@ -196,19 +234,22 @@ # figure 3 example fig3 = spzeros(8, 8) - fig3[[1, 7, 9, 13, 14, 15, 18, 20, 23, 27, 28, 31, 33, 34, 37, 45, 46, 49, 57, 63, 64]] .= 1 + fig3[[ + 1, 7, 9, 13, 14, 15, 18, 20, 23, 27, 28, 31, 33, 34, 37, 45, 46, 49, 57, 63, 64 + ]] .= 1 fig3 = SimpleDiGraph(fig3) scc_fig3 = Vector[[3, 4], [2, 5, 6], [8], [1, 7]] - fig3_cond = SimpleDiGraph(4); - add_edge!(fig3_cond, 4, 3); add_edge!(fig3_cond, 2, 1) - add_edge!(fig3_cond, 4, 1); add_edge!(fig3_cond, 4, 2) - + fig3_cond = SimpleDiGraph(4) + add_edge!(fig3_cond, 4, 3) + add_edge!(fig3_cond, 2, 1) + add_edge!(fig3_cond, 4, 1) + add_edge!(fig3_cond, 4, 2) # construct a n-number edge ring graph (period = n) n = 10 n_ring = cycle_digraph(n) - n_ring_shortcut = copy(n_ring); add_edge!(n_ring_shortcut, 1, 4) - + n_ring_shortcut = copy(n_ring) + add_edge!(n_ring_shortcut, 1, 4) # figure 8 example fig8 = spzeros(6, 6) @@ -227,16 +268,16 @@ @test @inferred(attracting_components(fig3)) == Vector[[3, 4], [8]] g10dists = ones(10, 10) - g10dists[1,2] = 10.0 + g10dists[1, 2] = 10.0 g10 = star_graph(10) for g in testgraphs(g10) - @test @inferred(neighborhood_dists(g, 1, 0)) == [(1, 0)] - @test length(@inferred(neighborhood(g, 1, 1))) == 10 - @test length(@inferred(neighborhood(g, 1, 1, g10dists))) == 9 - @test length(@inferred(neighborhood(g, 2, 1))) == 2 - @test length(@inferred(neighborhood(g, 1, 2))) == 10 - @test length(@inferred(neighborhood(g, 2, 2))) == 10 - @test length(@inferred(neighborhood(g, 2, -1))) == 0 + @test @inferred(neighborhood_dists(g, 1, 0)) == [(1, 0)] + @test length(@inferred(neighborhood(g, 1, 1))) == 10 + @test length(@inferred(neighborhood(g, 1, 1, g10dists))) == 9 + @test length(@inferred(neighborhood(g, 2, 1))) == 2 + @test length(@inferred(neighborhood(g, 1, 2))) == 10 + @test length(@inferred(neighborhood(g, 2, 2))) == 10 + @test length(@inferred(neighborhood(g, 2, -1))) == 0 end g10 = star_digraph(10) for g in testdigraphs(g10) diff --git a/test/core.jl b/test/core.jl index 3ffafffcd..3660dec7d 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5,21 +5,26 @@ @test @inferred(!is_ordered(reverse(e3))) @testset "add_vertices!" begin - gx = SimpleGraph(10); gdx = SimpleDiGraph(10) - @testset "$g" for g in testgraphs(gx, gdx) + gx = SimpleGraph(10) + gdx = SimpleDiGraph(10) + @testset "$g" for g in testgraphs(gx, gdx) gc = copy(g) @test add_vertices!(gc, 5) == 5 @test @inferred(nv(gc)) == 15 end end - g5w = wheel_graph(5); g5wd = wheel_digraph(5) + g5w = wheel_graph(5) + g5wd = wheel_digraph(5) @testset "degree functions" begin @testset "$g" for g in testgraphs(g5w) @test @inferred(indegree(g, 1)) == @inferred(outdegree(g, 1)) == 4 @test degree(g, 1) == 4 # explicit codecov - @test @inferred(indegree(g)) == @inferred(outdegree(g)) == @inferred(degree(g)) == [4, 3, 3, 3, 3] - + @test @inferred(indegree(g)) == + @inferred(outdegree(g)) == + @inferred(degree(g)) == + [4, 3, 3, 3, 3] + @test @inferred(Δout(g)) == @inferred(Δin(g)) == @inferred(Δ(g)) == 4 @test @inferred(δout(g)) == @inferred(δin(g)) == @inferred(δ(g)) == 3 z1 = @inferred(degree_histogram(g)) @@ -54,7 +59,6 @@ end end - @testset "neighbor functions" begin @testset "$g" for g in testgraphs(g5w) @test @inferred(neighbors(g, 2)) == [1, 3, 5] @@ -79,7 +83,6 @@ @test @inferred(num_self_loops(gsl)) == 2 end end - @testset "density" begin @testset "$g" for g in testgraphs(g5w) @@ -89,6 +92,5 @@ @testset "$g" for g in testdigraphs(g5wd) @test @inferred(density(g)) == 0.4 end - end end diff --git a/test/cycles/basis.jl b/test/cycles/basis.jl index e9e43a040..7d17e3f8c 100644 --- a/test/cycles/basis.jl +++ b/test/cycles/basis.jl @@ -1,6 +1,5 @@ @testset "Cycle Basis" begin - - function evaluate(x,y) + function evaluate(x, y) x_sorted = sort(sort.(x)) y_sorted = sort(sort.(y)) @test x_sorted == y_sorted @@ -15,45 +14,39 @@ end # Only one self-edge - elist = [(1,1)] + elist = [(1, 1)] ex = Graph(SimpleEdge.(elist)) expected_cyclebasis = Array{Int64,1}[[1]] @testset "one self-edge" for g in testgraphs(ex) ex_cyclebasis = cycle_basis(g) - evaluate(ex_cyclebasis,expected_cyclebasis) + evaluate(ex_cyclebasis, expected_cyclebasis) end # Graph with one cycle - elist = [(1,2),(2,3),(3,4),(4,1),(1,5)] + elist = [(1, 2), (2, 3), (3, 4), (4, 1), (1, 5)] ex = Graph(SimpleEdge.(elist)) - expected_cyclebasis = Array{Int64,1}[ - [1,2,3,4] ] + expected_cyclebasis = Array{Int64,1}[[1, 2, 3, 4]] @testset "one cycle" for g in testgraphs(ex) ex_cyclebasis = cycle_basis(g) - evaluate(ex_cyclebasis,expected_cyclebasis) - end + evaluate(ex_cyclebasis, expected_cyclebasis) + end # Graph with 2 of 3 cycles forming a basis - elist = [(1,2),(1,3),(2,3),(2,4),(3,4)] + elist = [(1, 2), (1, 3), (2, 3), (2, 4), (3, 4)] ex = Graph(SimpleEdge.(elist)) - expected_cyclebasis = Array{Int64,1}[ - [2,3,4], - [2,1,3] ] + expected_cyclebasis = Array{Int64,1}[[2, 3, 4], [2, 1, 3]] @testset "2 of 3 cycles w/ basis" for g in testgraphs(ex) ex_cyclebasis = cycle_basis(g) - evaluate(ex_cyclebasis,expected_cyclebasis) + evaluate(ex_cyclebasis, expected_cyclebasis) end # Testing root argument - elist = [(1,2),(1,3),(2,3),(2,4),(3,4),(1,5),(5,6),(6,4)] + elist = [(1, 2), (1, 3), (2, 3), (2, 4), (3, 4), (1, 5), (5, 6), (6, 4)] ex = Graph(SimpleEdge.(elist)) - expected_cyclebasis = Array{Int64,1}[ - [2, 4, 3], - [1, 5, 6, 4, 3], - [1, 2, 3] ] + expected_cyclebasis = Array{Int64,1}[[2, 4, 3], [1, 5, 6, 4, 3], [1, 2, 3]] @testset "root argument" for g in testgraphs(ex) - ex_cyclebasis = @inferred cycle_basis(g,3) - evaluate(ex_cyclebasis,expected_cyclebasis) + ex_cyclebasis = @inferred cycle_basis(g, 3) + evaluate(ex_cyclebasis, expected_cyclebasis) end @testset "two isolated cycles" begin diff --git a/test/cycles/hawick-james.jl b/test/cycles/hawick-james.jl index 7ca460d34..636298f18 100644 --- a/test/cycles/hawick-james.jl +++ b/test/cycles/hawick-james.jl @@ -10,10 +10,7 @@ add_edge!(ex1, 5, 2) @testset "subset" for g in testgraphs(ex1) - expected_circuits = Vector{Int}[ - [2, 3, 4, 5], - [2, 3, 5] - ] + expected_circuits = Vector{Int}[[2, 3, 4, 5], [2, 3, 5]] ex1_circuits = simplecycles_hawick_james(g) @test issubset(expected_circuits, ex1_circuits) @@ -46,8 +43,22 @@ # Almost fully connected DiGraph ex4 = SimpleDiGraph(9) - for (src, dest) in [(1, 2), (1, 5), (1, 7), (1, 8), (2, 9), (3, 4), (3, 6), - (4, 5), (4, 7), (5, 6), (6, 7), (6, 8), (7, 9), (8, 9)] + for (src, dest) in [ + (1, 2), + (1, 5), + (1, 7), + (1, 8), + (2, 9), + (3, 4), + (3, 6), + (4, 5), + (4, 7), + (5, 6), + (6, 7), + (6, 8), + (7, 9), + (8, 9), + ] add_edge!(ex4, src, dest) add_edge!(ex4, dest, src) end @@ -57,8 +68,10 @@ end # These test cases cover a bug that occurred in a previous version - @testset "bugfix (unknown issue; PR#1007) ($seed)" for seed in [1, 2, 3], (n, k) in [(14, 18), (10, 22), (7, 16)] - g = erdos_renyi(n, k, is_directed=true, rng=StableRNG(seed)) + @testset "bugfix (unknown issue; PR#1007) ($seed)" for seed in [1, 2, 3], + (n, k) in [(14, 18), (10, 22), (7, 16)] + + g = erdos_renyi(n, k; is_directed=true, rng=StableRNG(seed)) cycles1 = simplecycles(g) cycles2 = simplecycles_hawick_james(g) foreach(sort!, cycles1) diff --git a/test/cycles/incremental.jl b/test/cycles/incremental.jl index 8ca28afca..c20bc815b 100644 --- a/test/cycles/incremental.jl +++ b/test/cycles/incremental.jl @@ -17,11 +17,11 @@ @test !add_edge_checked!(ict, (2, 3), 1) @test !add_edge_checked!(ict, (1, 3), 2) else - @test !add_edge_checked!(ict, 3, (1,2)) - @test !add_edge_checked!(ict, 2, (1,3)) + @test !add_edge_checked!(ict, 3, (1, 2)) + @test !add_edge_checked!(ict, 2, (1, 3)) end @test length(edges(G)) == 2 + length(edges(Gtemplate)) - @test filter(in((1,2,3)), topological_sort(ict)) == [1, 2, 3] + @test filter(in((1, 2, 3)), topological_sort(ict)) == [1, 2, 3] end end diff --git a/test/cycles/johnson.jl b/test/cycles/johnson.jl index ab10da6e1..aff6666ab 100644 --- a/test/cycles/johnson.jl +++ b/test/cycles/johnson.jl @@ -2,9 +2,9 @@ rng = StableRNG(1) completedg = complete_digraph(4) pathdg = path_digraph(5) - triangle = random_regular_graph(3, 2, rng=rng) - quadrangle = random_regular_graph(4, 2, rng=rng) - pentagon = random_regular_graph(5, 2, rng=rng) + triangle = random_regular_graph(3, 2; rng=rng) + quadrangle = random_regular_graph(4, 2; rng=rng) + pentagon = random_regular_graph(5, 2; rng=rng) @testset "path digraph" for g in testgraphs(pathdg) @test maxsimplecycles(g) == 0 @@ -50,10 +50,10 @@ end selfloopg = DiGraph([ - 0 1 0 0; - 0 0 1 0; - 1 0 1 0; - 0 0 0 1; + 0 1 0 0 + 0 0 1 0 + 1 0 1 0 + 0 0 0 1 ]) @testset "self loops" for g in testgraphs(selfloopg) diff --git a/test/cycles/karp.jl b/test/cycles/karp.jl index 5f46a3d38..a45324f27 100644 --- a/test/cycles/karp.jl +++ b/test/cycles/karp.jl @@ -1,48 +1,52 @@ @testset "Karp Minimum Cycle Mean" begin - w = [0. 0. 0. 0. 0. 0. 1. 0. 0. 0.0 0. 0. - 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.0 0. 0. - 0. 0. 0. 0. 0. 0. 0. 0. 1.1 0.0 0. 0. - 0. 1.2 0. 0. 0. 0. 1. 0. 0. 0.0 0. 0. - 0. 1. 0. 0. 0. 0. 0. 0. 1.3 0.0 0. 0. - 0. 1.2 0. 0. 0. 0. 1. 0. 0. 0.0 0. 0. - 0. 1. 0. 0. 0. 0. 0. 0. 1.3 0.0 0. 0. - 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.0 0. 0. - 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.0 0.4 0. - 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.0 0. 0. - 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.0 0. 0. - 0. 0. 0. 0. 0. 0. 0. 0. 1.2 0.0 0. 0.] + w = [ + 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 + 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 + 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.1 0.0 0.0 0.0 + 0.0 1.2 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 + 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 1.3 0.0 0.0 0.0 + 0.0 1.2 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 + 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 1.3 0.0 0.0 0.0 + 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 + 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.4 0.0 + 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 + 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 + 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.2 0.0 0.0 0.0 + ] - w2 = [Inf Inf Inf Inf Inf Inf -0.09 Inf Inf Inf Inf Inf - Inf Inf Inf Inf Inf Inf -0.06 Inf Inf Inf Inf Inf - Inf Inf Inf Inf Inf Inf Inf Inf -0.01 Inf Inf Inf - Inf -0.19 Inf Inf Inf Inf -0.09 Inf Inf Inf Inf Inf - Inf -0.02 Inf Inf Inf Inf Inf Inf -0.03 Inf Inf Inf - Inf -0.19 Inf Inf Inf Inf -0.09 Inf Inf Inf Inf Inf - Inf -0.02 Inf Inf Inf Inf Inf Inf -0.03 Inf Inf Inf - Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf - Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf 0.39 Inf - Inf Inf Inf Inf Inf Inf -0.09 Inf Inf Inf Inf Inf - Inf Inf Inf Inf Inf Inf -0.06 Inf Inf Inf Inf Inf - Inf Inf Inf Inf Inf Inf Inf Inf -0.01 Inf Inf Inf] + w2 = [ + Inf Inf Inf Inf Inf Inf -0.09 Inf Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf -0.06 Inf Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf -0.01 Inf Inf Inf + Inf -0.19 Inf Inf Inf Inf -0.09 Inf Inf Inf Inf Inf + Inf -0.02 Inf Inf Inf Inf Inf Inf -0.03 Inf Inf Inf + Inf -0.19 Inf Inf Inf Inf -0.09 Inf Inf Inf Inf Inf + Inf -0.02 Inf Inf Inf Inf Inf Inf -0.03 Inf Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf Inf Inf 0.39 Inf + Inf Inf Inf Inf Inf Inf -0.09 Inf Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf -0.06 Inf Inf Inf Inf Inf + Inf Inf Inf Inf Inf Inf Inf Inf -0.01 Inf Inf Inf + ] - g1 = SimpleDiGraph(12) - add_edge!(g1, 1, 7) - add_edge!(g1, 2, 7) - add_edge!(g1, 3, 9) - add_edge!(g1, 4, 2) - add_edge!(g1, 4, 7) - add_edge!(g1, 5, 2) - add_edge!(g1, 5, 9) - add_edge!(g1, 6, 2) - add_edge!(g1, 6, 7) - add_edge!(g1, 7, 2) - add_edge!(g1, 7, 9) - add_edge!(g1, 9, 11) - add_edge!(g1, 10, 7) - add_edge!(g1, 11, 7) - add_edge!(g1, 12, 9) + g1 = SimpleDiGraph(12) + add_edge!(g1, 1, 7) + add_edge!(g1, 2, 7) + add_edge!(g1, 3, 9) + add_edge!(g1, 4, 2) + add_edge!(g1, 4, 7) + add_edge!(g1, 5, 2) + add_edge!(g1, 5, 9) + add_edge!(g1, 6, 2) + add_edge!(g1, 6, 7) + add_edge!(g1, 7, 2) + add_edge!(g1, 7, 9) + add_edge!(g1, 9, 11) + add_edge!(g1, 10, 7) + add_edge!(g1, 11, 7) + add_edge!(g1, 12, 9) - @testset "simple digraphs" for g in testgraphs(g1) + @testset "simple digraphs" for g in testgraphs(g1) c, λ = karp_minimum_cycle_mean(g, w) @test c == [9, 11, 7] @test λ == 0.9 @@ -50,7 +54,7 @@ c2, λ2 = karp_minimum_cycle_mean(g, w2) @test λ2 == -0.04 @test c2 == [2, 7] - end + end # Tricky test case # Backward walk from 3 is good: @@ -67,18 +71,20 @@ add_edge!(tricky, 4, 3) add_edge!(tricky, 2, 3) add_edge!(tricky, 2, 2) - distmx = [Inf 0. 1. Inf - Inf 1. 0. Inf - Inf Inf Inf 0. - 1. Inf 0. Inf] + distmx = [ + Inf 0.0 1.0 Inf + Inf 1.0 0.0 Inf + Inf Inf Inf 0.0 + 1.0 Inf 0.0 Inf + ] @testset "tricky case" for g in testgraphs(tricky) c, λ = karp_minimum_cycle_mean(g, distmx) - @test λ == 0. + @test λ == 0.0 @test sort(c) == [3, 4] c, λ = karp_minimum_cycle_mean(g, distmx .- 2) - @test λ == -2. + @test λ == -2.0 @test sort(c) == [3, 4] end @@ -86,12 +92,14 @@ multi = SimpleDiGraph(3) add_edge!(multi, 1, 1) add_edge!(multi, 2, 2) - distmx = [0. Inf - Inf -1] + distmx = [ + 0.0 Inf + Inf -1 + ] @testset "multiple SCCs" for g in testgraphs(multi) c, λ = karp_minimum_cycle_mean(g, distmx) - @test λ == -1. + @test λ == -1.0 @test c == [2] end end diff --git a/test/cycles/limited_length.jl b/test/cycles/limited_length.jl index af647521b..f9c529033 100644 --- a/test/cycles/limited_length.jl +++ b/test/cycles/limited_length.jl @@ -31,10 +31,10 @@ @testset "self loops" begin selfloopg = DiGraph([ - 0 1 0 0; - 0 0 1 0; - 1 0 1 0; - 0 0 0 1; + 0 1 0 0 + 0 0 1 0 + 1 0 1 0 + 0 0 0 1 ]) cycles = simplecycles_limited_length(selfloopg, nv(selfloopg)) @test [3] in cycles @@ -47,7 +47,7 @@ octag = smallgraph(:octahedral) octadg = DiGraph(octag) octalengths, _ = simplecycleslength(octadg) - for k = 1:6 + for k in 1:6 @test sum(octalengths[1:k]) == length(simplecycles_limited_length(octag, k)) @test sum(octalengths[1:k]) == length(simplecycles_limited_length(octadg, k)) end diff --git a/test/degeneracy.jl b/test/degeneracy.jl index 7defec0da..8b3bd70f2 100644 --- a/test/degeneracy.jl +++ b/test/degeneracy.jl @@ -4,27 +4,27 @@ @testset "$g" for g in testgraphs(d) corenum = @inferred(core_number(g)) @test corenum == [3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 0] - + @testset "k-core" begin - @test @inferred(k_core(g)) == k_core(g, corenum=corenum) == [1:8;] - @test @inferred(k_core(g, 2)) == k_core(g, 2, corenum=corenum) == [1:16;] + @test @inferred(k_core(g)) == k_core(g; corenum=corenum) == [1:8;] + @test @inferred(k_core(g, 2)) == k_core(g, 2; corenum=corenum) == [1:16;] @test length(k_core(g, 4)) == 0 end - + @testset "k-shell" begin - @test @inferred(k_shell(g)) == k_shell(g, corenum=corenum) == [1:8;] - @test @inferred(k_shell(g, 2)) == k_shell(g, 2, corenum=corenum) == [9:16;] + @test @inferred(k_shell(g)) == k_shell(g; corenum=corenum) == [1:8;] + @test @inferred(k_shell(g, 2)) == k_shell(g, 2; corenum=corenum) == [9:16;] @test length(k_shell(g, 4)) == 0 end @testset "k-crust" begin - @test @inferred(k_crust(g)) == k_crust(g, corenum=corenum) == [9:21;] - @test @inferred(k_crust(g, 2)) == k_crust(g, 2, corenum=corenum) == [9:21;] + @test @inferred(k_crust(g)) == k_crust(g; corenum=corenum) == [9:21;] + @test @inferred(k_crust(g, 2)) == k_crust(g, 2; corenum=corenum) == [9:21;] @test @inferred(k_crust(g, 4, corenum=corenum)) == [1:21;] end @testset "k-corona" begin - @test @inferred(k_corona(g, 1)) == k_corona(g, 1, corenum=corenum) == [17:20;] + @test @inferred(k_corona(g, 1)) == k_corona(g, 1; corenum=corenum) == [17:20;] @test @inferred(k_corona(g, 2)) == [10, 12, 13, 14, 15, 16] end diff --git a/test/deprecations.jl b/test/deprecations.jl index b3fa98ce3..9785cacb6 100644 --- a/test/deprecations.jl +++ b/test/deprecations.jl @@ -1,17 +1,49 @@ @testset "Generator deprecations" begin - types_0 = [BullGraph, ChvatalGraph, CubicalGraph, DesarguesGraph, - DiamondGraph, DodecahedralGraph, FruchtGraph, HeawoodGraph, - HouseGraph, HouseXGraph, IcosahedralGraph, KarateGraph, KrackhardtKiteGraph, - MoebiusKantorGraph, OctahedralGraph, PappusGraph, PetersenGraph, - SedgewickMazeGraph, TetrahedralGraph, TruncatedCubeGraph, - TruncatedTetrahedronGraph, TruncatedTetrahedronDiGraph, TutteGraph] - types_1param = [CompleteGraph, CompleteDiGraph, - StarGraph, StarDigraph, PathGraph, - PathDiGraph, CycleGraph, CycleDiGraph, WheelGraph, WheelDiGraph, - BinaryTree, Doublebinary_tree, RoachGraph, - LadderGraph, Circularladder_graph] - types_2params = [CompleteBipartiteGraph, LollipopGraph, BarbellGraph, - TuranGraph, CliqueGraph] + types_0 = [ + BullGraph, + ChvatalGraph, + CubicalGraph, + DesarguesGraph, + DiamondGraph, + DodecahedralGraph, + FruchtGraph, + HeawoodGraph, + HouseGraph, + HouseXGraph, + IcosahedralGraph, + KarateGraph, + KrackhardtKiteGraph, + MoebiusKantorGraph, + OctahedralGraph, + PappusGraph, + PetersenGraph, + SedgewickMazeGraph, + TetrahedralGraph, + TruncatedCubeGraph, + TruncatedTetrahedronGraph, + TruncatedTetrahedronDiGraph, + TutteGraph, + ] + types_1param = [ + CompleteGraph, + CompleteDiGraph, + StarGraph, + StarDigraph, + PathGraph, + PathDiGraph, + CycleGraph, + CycleDiGraph, + WheelGraph, + WheelDiGraph, + BinaryTree, + Doublebinary_tree, + RoachGraph, + LadderGraph, + Circularladder_graph, + ] + types_2params = [ + CompleteBipartiteGraph, LollipopGraph, BarbellGraph, TuranGraph, CliqueGraph + ] for G in types_0 @test_deprecated G() end diff --git a/test/digraph/transitivity.jl b/test/digraph/transitivity.jl index 07c0fb039..5ddabeb29 100644 --- a/test/digraph/transitivity.jl +++ b/test/digraph/transitivity.jl @@ -37,7 +37,7 @@ @test g == @inferred(transitivereduction(g)) end end - + # transitive reduction of a path is a path again @testset "pathgraph reduction" begin pathgraph = path_digraph(10) @@ -49,7 +49,7 @@ @test g == @inferred(transitivereduction(gclosure)) end end - + # Transitive reduction of a complete graph should be s simple cycle @testset "completegraph reduction" begin completegraph = complete_digraph(9) @@ -62,30 +62,30 @@ # transitive reduction a graph with no edges is the same graph again @testset "zero-edge reduction" begin - noedgegraph = SimpleDiGraph(7) + noedgegraph = SimpleDiGraph(7) @testset "$g" for g in testgraphs(noedgegraph) @test g == @inferred(transitivereduction(g)) end end - + # transitve reduction should maintain a selfloop only when selflooped==true @testset "selfloops reduction" begin selfloopgraph = SimpleDiGraph(1) add_edge!(selfloopgraph, 1, 1) @testset "$g" for g in testgraphs(selfloopgraph) - @test g == @inferred(transitivereduction(g; selflooped=true)); - @test g != @inferred(transitivereduction(g; selflooped=false)); + @test g == @inferred(transitivereduction(g; selflooped=true)) + @test g != @inferred(transitivereduction(g; selflooped=false)) end - # transitive should not maintain selfloops for strongly connected components - # of size > 1 + # transitive should not maintain selfloops for strongly connected components + # of size > 1 selfloopgraph2 = SimpleDiGraph(2) add_edge!(selfloopgraph2, 1, 1) add_edge!(selfloopgraph2, 1, 2) add_edge!(selfloopgraph2, 2, 1) add_edge!(selfloopgraph2, 2, 2) @testset "$g" for g in testgraphs(selfloopgraph2) - @test g != @inferred(transitivereduction(g; selflooped=true)); + @test g != @inferred(transitivereduction(g; selflooped=true)) end end @@ -102,7 +102,7 @@ add_edge!(barbellgraph, i, j) add_edge!(barbellgraph, j, i) end - add_edge!(barbellgraph, 1, 5); + add_edge!(barbellgraph, 1, 5) @testset "$g" for g in testgraphs(barbellgraph) greduced = @inferred(transitivereduction(g)) scc = strongly_connected_components(greduced) diff --git a/test/distance.jl b/test/distance.jl index f0448d56f..a970d1f9f 100644 --- a/test/distance.jl +++ b/test/distance.jl @@ -51,16 +51,20 @@ @test d[1, 1] == getindex(d, 1, 1) == 1 @test d[1:2, 1:2] == Graphs.DefaultDistance(2) @test d == transpose(d) == adjoint(d) - @test sprint(show, d) == - stringmime("text/plain", d) == + @test sprint(show, d) == + stringmime("text/plain", d) == "$(d.nv) × $(d.nv) default distance matrix (value = 1)" end @testset "warnings and errors" begin - # ensures that eccentricity only throws an error if there is more than one component + # ensures that eccentricity only throws an error if there is more than one component g1 = SimpleGraph(2) - @test_logs (:warn, "Infinite path length detected for vertex 1") match_mode=:any eccentricity(g1) - @test_logs (:warn, "Infinite path length detected for vertex 2") match_mode=:any eccentricity(g1) + @test_logs (:warn, "Infinite path length detected for vertex 1") match_mode = :any eccentricity( + g1 + ) + @test_logs (:warn, "Infinite path length detected for vertex 2") match_mode = :any eccentricity( + g1 + ) g2 = path_graph(2) @test_logs eccentricity(g2) end diff --git a/test/dominatingset/degree_dom_set.jl b/test/dominatingset/degree_dom_set.jl index e7b75f3cf..94e7b53cb 100644 --- a/test/dominatingset/degree_dom_set.jl +++ b/test/dominatingset/degree_dom_set.jl @@ -1,5 +1,4 @@ @testset "Degree Dominating Set" begin - g0 = SimpleGraph(0) for g in testgraphs(g0) d = @inferred(dominating_set(g, DegreeDominatingSet())) @@ -9,39 +8,39 @@ g1 = SimpleGraph(1) for g in testgraphs(g1) d = @inferred(dominating_set(g, DegreeDominatingSet())) - @test (d == [1,]) + @test (d == [1]) end add_edge!(g1, 1, 1) for g in testgraphs(g1) d = @inferred(dominating_set(g, DegreeDominatingSet())) - @test (d == [1,]) + @test (d == [1]) end - + g3 = star_graph(5) for g in testgraphs(g1) d = @inferred(dominating_set(g, DegreeDominatingSet())) - @test (d == [1,]) + @test (d == [1]) end - + g4 = complete_graph(5) for g in testgraphs(g4) d = @inferred(dominating_set(g, DegreeDominatingSet())) - @test length(d)== 1 + @test length(d) == 1 end - #path_graph(5) with additional edge 2-5 + # path_graph(5) with additional edge 2-5 g5 = path_graph(5) add_edge!(g5, 2, 5) for g in testgraphs(g5) d = @inferred(dominating_set(g, DegreeDominatingSet())) - @test (length(d)== 2 && minimum(d) == 2) + @test (length(d) == 2 && minimum(d) == 2) end add_edge!(g5, 2, 2) add_edge!(g5, 3, 3) for g in testgraphs(g5) d = @inferred(dominating_set(g, DegreeDominatingSet())) - @test (length(d)== 2 && minimum(d) == 2) + @test (length(d) == 2 && minimum(d) == 2) end end diff --git a/test/dominatingset/minimal_dom_set.jl b/test/dominatingset/minimal_dom_set.jl index 34c05cded..81dd58a0e 100644 --- a/test/dominatingset/minimal_dom_set.jl +++ b/test/dominatingset/minimal_dom_set.jl @@ -1,5 +1,4 @@ @testset "Minimal Dominating Set" begin - g0 = SimpleGraph(0) for g in testgraphs(g0) d = @inferred(dominating_set(g, MinimalDominatingSet(); rng=StableRNG(3))) @@ -9,25 +8,25 @@ g1 = SimpleGraph(1) for g in testgraphs(g1) d = @inferred(dominating_set(g, MinimalDominatingSet(); rng=StableRNG(3))) - @test (d == [1,]) + @test (d == [1]) end add_edge!(g1, 1, 1) for g in testgraphs(g1) d = @inferred(dominating_set(g, MinimalDominatingSet(); rng=StableRNG(3))) - @test (d == [1,]) + @test (d == [1]) end g3 = star_graph(5) for g in testgraphs(g3) d = @inferred(dominating_set(g, MinimalDominatingSet(); rng=StableRNG(3))) - @test (length(d)== 1 || (length(d)== 4 && minimum(d) > 1 )) + @test (length(d) == 1 || (length(d) == 4 && minimum(d) > 1)) end - + g4 = complete_graph(5) for g in testgraphs(g4) d = @inferred(dominating_set(g, MinimalDominatingSet(); rng=StableRNG(3))) - @test length(d)== 1 #Exactly one vertex + @test length(d) == 1 # Exactly one vertex end g5 = path_graph(4) diff --git a/test/edit_distance.jl b/test/edit_distance.jl index 0575e511c..7b5484629 100644 --- a/test/edit_distance.jl +++ b/test/edit_distance.jl @@ -1,23 +1,35 @@ @testset "Edit distance" begin rng = StableRNG(1) - gtri = random_regular_graph(3, 2, rng=rng) - gquad = random_regular_graph(4, 2, rng=rng) - gpent = random_regular_graph(5, 2, rng=rng) + gtri = random_regular_graph(3, 2; rng=rng) + gquad = random_regular_graph(4, 2; rng=rng) + gpent = random_regular_graph(5, 2; rng=rng) - @testset "edit_distance $triangle, $quadrangle, $pentagon" for triangle in testgraphs(gtri), quadrangle in testgraphs(gquad), pentagon in testgraphs(gpent) - d, λ = @inferred(edit_distance(triangle, quadrangle, subst_cost=MinkowskiCost(1:3, 1:4))) + @testset "edit_distance $triangle, $quadrangle, $pentagon" for triangle in + testgraphs(gtri), + quadrangle in testgraphs(gquad), + pentagon in testgraphs(gpent) + + d, λ = @inferred( + edit_distance(triangle, quadrangle, subst_cost=MinkowskiCost(1:3, 1:4)) + ) @test d == 1.0 @test λ == Tuple[(1, 1), (2, 2), (3, 3), (0, 4)] - d, λ = @inferred(edit_distance(quadrangle, triangle, subst_cost=MinkowskiCost(1:4, 1:3))) + d, λ = @inferred( + edit_distance(quadrangle, triangle, subst_cost=MinkowskiCost(1:4, 1:3)) + ) @test d == 1.0 @test λ == Tuple[(1, 1), (2, 2), (3, 3), (4, 0)] - d, λ = @inferred(edit_distance(triangle, pentagon, subst_cost=MinkowskiCost(1:3, 1:5))) + d, λ = @inferred( + edit_distance(triangle, pentagon, subst_cost=MinkowskiCost(1:3, 1:5)) + ) @test d == 2.0 @test λ == Tuple[(1, 1), (2, 2), (3, 3), (0, 4), (0, 5)] - d, λ = @inferred(edit_distance(pentagon, triangle, subst_cost=MinkowskiCost(1:5, 1:3))) + d, λ = @inferred( + edit_distance(pentagon, triangle, subst_cost=MinkowskiCost(1:5, 1:3)) + ) @test d == 2.0 @test λ == Tuple[(1, 1), (2, 2), (3, 3), (4, 0), (5, 0)] end @@ -25,9 +37,9 @@ @testset "Minkowski cost / bounded Minkowski" begin cost = @inferred(MinkowskiCost(1:3, 1:3)) bcost = @inferred(BoundedMinkowskiCost(1:3, 1:3)) - for i = 1:3 - @test cost(i, i) == 0. - @test bcost(i, i) == 2 / 3 + for i in 1:3 + @test cost(i, i) == 0.0 + @test bcost(i, i) == 2 / 3 end end diff --git a/test/experimental/isomorphism.jl b/test/experimental/isomorphism.jl index d6c5166a7..658ca60d5 100644 --- a/test/experimental/isomorphism.jl +++ b/test/experimental/isomorphism.jl @@ -1,6 +1,5 @@ using Random: randperm - # helper function that permutates the vertices of a graph function shuffle_vertices(g::AbstractGraph, σ) G = typeof(g) @@ -14,26 +13,83 @@ end # all (up to isomorphism) 3-regular, connected graphs on 8 vertices # list taken from https://en.wikipedia.org/wiki/Table_of_simple_cubic_graphs#4_vertices -cubic_graphs = [SimpleGraph(Edge.([(1,2), (1,3), (1,8), (2,3), (2,4), (3,4), - (4,5), (5,6), (5,7), (6,7), (6,8), (7,8)])), - SimpleGraph(Edge.([(1,2), (1,3), (1,8), (2,3), (2,5), (3,4), - (4,5), (4,7), (5,6), (6,7), (6,8), (7,8)])), - SimpleGraph(Edge.([(1,2), (1,3), (1,8), (2,3), (2,6), (3,4), - (4,5), (4,7), (5,6), (5,8), (6,7), (7,8)])), - grid([2,2,2]), - SimpleGraph(Edge.([(1,2), (1,5), (1,8), (2,3), (2,6), (3,4), - (3,7), (4,5), (4,8), (5,6), (6,7), (7,8)])) - ] +cubic_graphs = [ + SimpleGraph( + Edge.([ + (1, 2), + (1, 3), + (1, 8), + (2, 3), + (2, 4), + (3, 4), + (4, 5), + (5, 6), + (5, 7), + (6, 7), + (6, 8), + (7, 8), + ]), + ), + SimpleGraph( + Edge.([ + (1, 2), + (1, 3), + (1, 8), + (2, 3), + (2, 5), + (3, 4), + (4, 5), + (4, 7), + (5, 6), + (6, 7), + (6, 8), + (7, 8), + ]), + ), + SimpleGraph( + Edge.([ + (1, 2), + (1, 3), + (1, 8), + (2, 3), + (2, 6), + (3, 4), + (4, 5), + (4, 7), + (5, 6), + (5, 8), + (6, 7), + (7, 8), + ]), + ), + grid([2, 2, 2]), + SimpleGraph( + Edge.([ + (1, 2), + (1, 5), + (1, 8), + (2, 3), + (2, 6), + (3, 4), + (3, 7), + (4, 5), + (4, 8), + (5, 6), + (6, 7), + (7, 8), + ]), + ), +] # vertex permutated versions of cubic_graphs cubic_graphs_perm = [] rng = StableRNG(1) -for i = 1:length(cubic_graphs) +for i in 1:length(cubic_graphs) push!(cubic_graphs_perm, shuffle_vertices(cubic_graphs[i], randperm(rng, 8))) end @testset "Isomorphism" begin - @test has_isomorph(grid([2,3]), grid([3,2])) + @test has_isomorph(grid([2, 3]), grid([3, 2])) # the cubic graphs should only be isomorph to themself # the same holds for subgraph isomorphism and induced subgraph isomorphism @@ -48,18 +104,26 @@ end for i in 1:length(cubic_graphs), j in 1:length(cubic_graphs_perm) @test (i == j) == has_isomorph(cubic_graphs[i], cubic_graphs_perm[j]) @test (i == j) == has_subgraphisomorph(cubic_graphs[i], cubic_graphs_perm[j]) - @test (i == j) == has_induced_subgraphisomorph(cubic_graphs[i], cubic_graphs_perm[j]) + @test (i == j) == + has_induced_subgraphisomorph(cubic_graphs[i], cubic_graphs_perm[j]) end # count_isomorph, count_subgraphisomorph and count_induced_subgraphisomorph are commutative for i in 1:length(cubic_graphs) g1 = cubic_graphs[i] g2 = cubic_graphs_perm[i] - @test count_isomorph(g1, g1) == count_isomorph(g1, g2) == count_isomorph(g2, g1) == count_isomorph(g2,g2) - @test count_subgraphisomorph(g1, g1) == count_subgraphisomorph(g1, g2) == - count_subgraphisomorph(g2, g1) == count_subgraphisomorph(g2,g2) - @test count_induced_subgraphisomorph(g1, g1) == count_subgraphisomorph(g1, g2) == - count_subgraphisomorph(g2, g1) == count_subgraphisomorph(g2,g2) + @test count_isomorph(g1, g1) == + count_isomorph(g1, g2) == + count_isomorph(g2, g1) == + count_isomorph(g2, g2) + @test count_subgraphisomorph(g1, g1) == + count_subgraphisomorph(g1, g2) == + count_subgraphisomorph(g2, g1) == + count_subgraphisomorph(g2, g2) + @test count_induced_subgraphisomorph(g1, g1) == + count_subgraphisomorph(g1, g2) == + count_subgraphisomorph(g2, g1) == + count_subgraphisomorph(g2, g2) end for i in 1:length(cubic_graphs) @@ -80,39 +144,65 @@ end # TODO better tests for vertex_relation and edge_relation vrel(u, v) = false erel(e1, e2) = false - @test has_isomorph(complete_graph(4), complete_graph(4), vertex_relation=vrel) == false - @test has_isomorph(complete_graph(4), complete_graph(4), edge_relation=erel) == false - @test has_subgraphisomorph(complete_graph(4), complete_graph(3), vertex_relation=vrel) == false - @test has_subgraphisomorph(complete_graph(4), complete_graph(3), edge_relation=erel) == false - @test has_induced_subgraphisomorph(complete_graph(4), complete_graph(3), vertex_relation=vrel) == false - @test has_induced_subgraphisomorph(complete_graph(4), complete_graph(3), edge_relation=erel) == false - - @test count_isomorph(complete_graph(4), complete_graph(4), vertex_relation=vrel) == 0 - @test count_isomorph(complete_graph(4), complete_graph(4), edge_relation=erel) == 0 - @test count_subgraphisomorph(complete_graph(4), complete_graph(3), vertex_relation=vrel) == 0 - @test count_subgraphisomorph(complete_graph(4), complete_graph(3), edge_relation=erel) == 0 - @test count_induced_subgraphisomorph(complete_graph(4), complete_graph(3), vertex_relation=vrel) == 0 - @test count_induced_subgraphisomorph(complete_graph(4), complete_graph(3), edge_relation=erel) == 0 - - @test isempty(all_isomorph(complete_graph(4), complete_graph(4), vertex_relation=vrel)) - @test isempty(all_isomorph(complete_graph(4), complete_graph(4), edge_relation=erel)) - @test isempty(all_subgraphisomorph(complete_graph(4), complete_graph(3), vertex_relation=vrel)) - @test isempty(all_subgraphisomorph(complete_graph(4), complete_graph(3), edge_relation=erel)) - @test isempty(all_induced_subgraphisomorph(complete_graph(4), complete_graph(3), vertex_relation=vrel)) - @test isempty(all_induced_subgraphisomorph(complete_graph(4), complete_graph(3), edge_relation=erel)) + @test has_isomorph(complete_graph(4), complete_graph(4); vertex_relation=vrel) == false + @test has_isomorph(complete_graph(4), complete_graph(4); edge_relation=erel) == false + @test has_subgraphisomorph( + complete_graph(4), complete_graph(3); vertex_relation=vrel + ) == false + @test has_subgraphisomorph(complete_graph(4), complete_graph(3); edge_relation=erel) == + false + @test has_induced_subgraphisomorph( + complete_graph(4), complete_graph(3); vertex_relation=vrel + ) == false + @test has_induced_subgraphisomorph( + complete_graph(4), complete_graph(3); edge_relation=erel + ) == false + + @test count_isomorph(complete_graph(4), complete_graph(4); vertex_relation=vrel) == 0 + @test count_isomorph(complete_graph(4), complete_graph(4); edge_relation=erel) == 0 + @test count_subgraphisomorph( + complete_graph(4), complete_graph(3); vertex_relation=vrel + ) == 0 + @test count_subgraphisomorph( + complete_graph(4), complete_graph(3); edge_relation=erel + ) == 0 + @test count_induced_subgraphisomorph( + complete_graph(4), complete_graph(3); vertex_relation=vrel + ) == 0 + @test count_induced_subgraphisomorph( + complete_graph(4), complete_graph(3); edge_relation=erel + ) == 0 + + @test isempty(all_isomorph(complete_graph(4), complete_graph(4); vertex_relation=vrel)) + @test isempty(all_isomorph(complete_graph(4), complete_graph(4); edge_relation=erel)) + @test isempty( + all_subgraphisomorph(complete_graph(4), complete_graph(3); vertex_relation=vrel) + ) + @test isempty( + all_subgraphisomorph(complete_graph(4), complete_graph(3); edge_relation=erel) + ) + @test isempty( + all_induced_subgraphisomorph( + complete_graph(4), complete_graph(3); vertex_relation=vrel + ), + ) + @test isempty( + all_induced_subgraphisomorph( + complete_graph(4), complete_graph(3); edge_relation=erel + ), + ) # some test for early returns if there is no isomorphism @test count_isomorph(complete_graph(4), cycle_graph(4)) == 0 @test isempty(all_isomorph(complete_graph(4), cycle_graph(4))) @test count_subgraphisomorph(complete_graph(3), complete_graph(4)) == 0 - # this tests triggers the shortcut in the vf2 algorithm if the first graph is smaller than the second one @test has_isomorph(complete_graph(3), complete_graph(4)) == false # this test is mainly to cover a certain branch of vf2 # TODO cover directed graphs better - gd = SimpleDiGraph(Edge.([(2,1)])) + gd = SimpleDiGraph(Edge.([(2, 1)])) @test has_isomorph(gd, gd, VF2()) == true # Test for correct handling of self-loops diff --git a/test/experimental/parallel/runtests.jl b/test/experimental/parallel/runtests.jl index d2065df09..f655eea30 100644 --- a/test/experimental/parallel/runtests.jl +++ b/test/experimental/parallel/runtests.jl @@ -1,9 +1,9 @@ -#using Graphs -#using Graphs.Parallel -#using Base.Threads: @threads, Atomic +# using Graphs +# using Graphs.Parallel +# using Base.Threads: @threads, Atomic tests = [ - # "traversals/gdistances", # TODO currently disabled as the code in gdistances seems to be broken +# "traversals/gdistances", # TODO currently disabled as the code in gdistances seems to be broken ] @testset "Graphs.Experimental.Parallel" begin diff --git a/test/experimental/parallel/traversals/gdistances.jl b/test/experimental/parallel/traversals/gdistances.jl index 315af214d..ae8dc429b 100644 --- a/test/experimental/parallel/traversals/gdistances.jl +++ b/test/experimental/parallel/traversals/gdistances.jl @@ -1,19 +1,21 @@ @testset "Experimental.Parallel.BFS" begin - - g5 = SimpleDiGraph(4) - add_edge!(g5, 1, 2); add_edge!(g5, 2, 3); add_edge!(g5, 1, 3); add_edge!(g5, 3, 4) - + add_edge!(g5, 1, 2) + add_edge!(g5, 2, 3) + add_edge!(g5, 1, 3) + add_edge!(g5, 3, 4) + for g in testdigraphs(g5) - @test @inferred(Experimental.Parallel.gdistances(g, 1)) == gdistances(g, 1) - @test @inferred(Experimental.Parallel.gdistances(g, [1, 3])) == gdistances(g, [1, 3]) + @test @inferred(Experimental.Parallel.gdistances(g, 1)) == gdistances(g, 1) + @test @inferred(Experimental.Parallel.gdistances(g, [1, 3])) == + gdistances(g, [1, 3]) end g6 = smallgraph(:house) for g in testgraphs(g6) - @test @inferred(Experimental.Parallel.gdistances(g, 2)) == gdistances(g, 2) - @test @inferred(Experimental.Parallel.gdistances(g, [1, 2])) == gdistances(g, [1, 2]) + @test @inferred(Experimental.Parallel.gdistances(g, 2)) == gdistances(g, 2) + @test @inferred(Experimental.Parallel.gdistances(g, [1, 2])) == + gdistances(g, [1, 2]) end - end diff --git a/test/experimental/shortestpaths.jl b/test/experimental/shortestpaths.jl index a0427594a..e10a8e7b9 100644 --- a/test/experimental/shortestpaths.jl +++ b/test/experimental/shortestpaths.jl @@ -4,10 +4,9 @@ using SparseArrays using Graphs.Experimental.Traversals: NOOPSort import Base.== function ==(a::ShortestPaths.AStarResult, b::ShortestPaths.AStarResult) - return a.path == b.path && a.dist == b.dist + return a.path == b.path && a.dist == b.dist end @testset "Shortest Paths" begin - g1 = path_graph(5) g2 = path_digraph(5) s1 = shortest_paths(g1, 1) # bfs @@ -24,11 +23,14 @@ end g4 = path_digraph(5) d1 = float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) - d2 = sparse(float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) + d2 = sparse( + float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) + ) for g in testgraphs(g3), dg in testdigraphs(g4) y = @inferred(shortest_paths(g, 1, 4, d1, AStar())) - @test y == @inferred(shortest_paths(dg, 1, 4, d1, AStar())) == - @inferred(shortest_paths(g, 1, 4, d2, AStar())) + @test y == + @inferred(shortest_paths(dg, 1, 4, d1, AStar())) == + @inferred(shortest_paths(g, 1, 4, d2, AStar())) @test paths(y) == [y.path] @test_throws ArgumentError paths(y, 2) @test dists(y) == [[y.dist]] @@ -41,18 +43,20 @@ end end # test for #1258 - + g = complete_graph(4) w = float([1 1 1 4; 1 1 1 1; 1 1 1 1; 4 1 1 1]) @test length(first(paths(shortest_paths(g, 1, 4, w, AStar())))) == 3 end @testset "BellmanFord" begin - g4 = path_digraph(5) + g4 = path_digraph(5) - d1 = float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) - d2 = sparse(float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) - for g in testdigraphs(g4) + d1 = float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) + d2 = sparse( + float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) + ) + for g in testdigraphs(g4) y = @inferred(shortest_paths(g, 2, d1, BellmanFord())) z = @inferred(shortest_paths(g, 2, d2, BellmanFord())) @test y.dists == z.dists == [Inf, 0, 6, 17, 33] @@ -61,7 +65,6 @@ end @test @inferred(!has_negative_weight_cycle(g, BellmanFord())) @test @inferred(!has_negative_weight_cycle(g, d1, BellmanFord())) - y = @inferred(shortest_paths(g, 2, d1, BellmanFord())) z = @inferred(shortest_paths(g, 2, d2, BellmanFord())) @test dists(y) == dists(z) == [Inf, 0, 6, 17, 33] @@ -95,7 +98,7 @@ end end @testset "BFS" begin - g1= path_graph(5) + g1 = path_graph(5) g2 = path_digraph(5) add_edge!(g1, 2, 5) add_edge!(g2, 2, 5) @@ -125,14 +128,16 @@ end @testset "DEsopoPape" begin g4 = path_digraph(5) d1 = float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) - d2 = sparse(float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) + d2 = sparse( + float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) + ) @testset "generic tests: $g" for g in testdigraphs(g4) y = @inferred(shortest_paths(g, 2, d1, DEsopoPape())) z = @inferred(shortest_paths(g, 2, d2, DEsopoPape())) @test y.parents == z.parents == [0, 0, 2, 3, 4] @test y.dists == z.dists == [Inf, 0, 6, 17, 33] end - + gx = path_graph(5) add_edge!(gx, 2, 4) d = ones(Int, 5, 5) @@ -143,7 +148,7 @@ end @test z.parents == [0, 1, 4, 2, 4] end - m = [0 2 2 0 0; 2 0 0 0 3; 2 0 0 1 2;0 0 1 0 1;0 3 2 1 0] + m = [0 2 2 0 0; 2 0 0 0 3; 2 0 0 1 2; 0 0 1 0 1; 0 3 2 1 0] G = SimpleGraph(5) add_edge!(G, 1, 2) add_edge!(G, 1, 3) @@ -193,11 +198,11 @@ end end @testset "random simple graphs" begin - for seed = 1:5 + for seed in 1:5 rng = StableRNG(seed) - nvg = Int(ceil(250*rand(rng))) - neg = Int(floor((nvg*(nvg-1)/2)*rand(rng))) - g = SimpleGraph(nvg, neg; rng = rng) + nvg = Int(ceil(250 * rand(rng))) + neg = Int(floor((nvg * (nvg - 1) / 2) * rand(rng))) + g = SimpleGraph(nvg, neg; rng=rng) z = shortest_paths(g, 1, DEsopoPape()) y = shortest_paths(g, 1, Dijkstra()) @test isapprox(z.dists, y.dists) @@ -205,11 +210,11 @@ end end @testset "random simple digraphs" begin - for seed = 1:5 + for seed in 1:5 rng = StableRNG(seed) - nvg = Int(ceil(250*rand(rng))) - neg = Int(floor((nvg*(nvg-1)/2)*rand(rng))) - g = SimpleDiGraph(nvg, neg; rng = rng) + nvg = Int(ceil(250 * rand(rng))) + neg = Int(floor((nvg * (nvg - 1) / 2) * rand(rng))) + g = SimpleDiGraph(nvg, neg; rng=rng) z = shortest_paths(g, 1, DEsopoPape()) y = shortest_paths(g, 1, Dijkstra()) @test isapprox(z.dists, y.dists) @@ -259,19 +264,35 @@ end end @testset "smallgraphs: $s" for s in [ - :bull, :chvatal, :cubical, :desargues, - :diamond, :dodecahedral, :frucht, :heawood, - :house, :housex, :icosahedral, :krackhardtkite, :moebiuskantor, - :octahedral, :pappus, :petersen, :sedgewickmaze, :tutte, - :tetrahedral, :truncatedcube, :truncatedtetrahedron, - :truncatedtetrahedron_dir - ] + :bull, + :chvatal, + :cubical, + :desargues, + :diamond, + :dodecahedral, + :frucht, + :heawood, + :house, + :housex, + :icosahedral, + :krackhardtkite, + :moebiuskantor, + :octahedral, + :pappus, + :petersen, + :sedgewickmaze, + :tutte, + :tetrahedral, + :truncatedcube, + :truncatedtetrahedron, + :truncatedtetrahedron_dir, + ] G = smallgraph(s) z = shortest_paths(G, 1, DEsopoPape()) y = shortest_paths(G, 1, Dijkstra()) @test isapprox(z.dists, y.dists) end - + @testset "errors" begin g = Graph() @test_throws DomainError shortest_paths(g, 1, DEsopoPape()) @@ -283,7 +304,9 @@ end @testset "Dijkstra" begin g4 = path_digraph(5) d1 = float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) - d2 = sparse(float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) + d2 = sparse( + float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) + ) for g in testdigraphs(g4) y = @inferred(shortest_paths(g, 2, d1, Dijkstra())) @@ -292,8 +315,8 @@ end @test y.parents == z.parents == [0, 0, 2, 3, 4] @test y.dists == z.dists == [Inf, 0, 6, 17, 33] - y = @inferred(shortest_paths(g, 2, d1, Dijkstra(all_paths=true))) - z = @inferred(shortest_paths(g, 2, d2, Dijkstra(all_paths=true))) + y = @inferred(shortest_paths(g, 2, d1, Dijkstra(; all_paths=true))) + z = @inferred(shortest_paths(g, 2, d2, Dijkstra(; all_paths=true))) @test z.predecessors[3] == y.predecessors[3] == [2] @test @inferred(paths(z)) == paths(y) @@ -315,7 +338,13 @@ end # small function to reconstruct the shortest path; I copied it from somewhere, # can't find the original source to give the credits # @Beatzekatze on github - spath(target, dijkstraStruct, source) = target == source ? target : [spath(dijkstraStruct.parents[target], dijkstraStruct, source) target] + function spath(target, dijkstraStruct, source) + return if target == source + target + else + [spath(dijkstraStruct.parents[target], dijkstraStruct, source) target] + end + end spaths(ds, targets, source) = [spath(i, ds, source) for i in targets] G = Graph(4) @@ -324,29 +353,27 @@ end add_edge!(G, 1, 4) add_edge!(G, 3, 4) add_edge!(G, 2, 2) - w = [0. 3. 0. 1.; - 3. 0. 2. 0.; - 0. 2. 0. 3.; - 1. 0. 3. 0.] + w = [ + 0.0 3.0 0.0 1.0 + 3.0 0.0 2.0 0.0 + 0.0 2.0 0.0 3.0 + 1.0 0.0 3.0 0.0 + ] for g in testgraphs(G) - ds = @inferred(shortest_paths(g, 2, w, Dijkstra()) ) - # this loop reconstructs the shortest path for vertices 1, 3 and 4 - @test spaths(ds, [1, 3, 4], 2) == Array[[2 1], - [2 3], - [2 1 4]] + ds = @inferred(shortest_paths(g, 2, w, Dijkstra())) + # this loop reconstructs the shortest path for vertices 1, 3 and 4 + @test spaths(ds, [1, 3, 4], 2) == Array[[2 1], [2 3], [2 1 4]] - # here a selflink at source is introduced; it should not change the shortest paths + # here a selflink at source is introduced; it should not change the shortest paths w[2, 2] = 10.0 ds = @inferred(shortest_paths(g, 2, w, Dijkstra())) - # this loop reconstructs the shortest path for vertices 1, 3 and 4 - @test spaths(ds, [1, 3, 4], 2) == Array[[2 1], - [2 3], - [2 1 4]] + # this loop reconstructs the shortest path for vertices 1, 3 and 4 + @test spaths(ds, [1, 3, 4], 2) == Array[[2 1], [2 3], [2 1 4]] end #615 - m = [0 2 2 0 0; 2 0 0 0 3; 2 0 0 1 2;0 0 1 0 1;0 3 2 1 0] + m = [0 2 2 0 0; 2 0 0 0 3; 2 0 0 1 2; 0 0 1 0 1; 0 3 2 1 0] G = SimpleGraph(5) add_edge!(G, 1, 2) add_edge!(G, 1, 3) @@ -355,14 +382,16 @@ end add_edge!(G, 3, 4) add_edge!(G, 4, 5) for g in testgraphs(G) - ds = @inferred(shortest_paths(g, 1, m, Dijkstra(all_paths=true))) - @test ds.pathcounts == [1, 1, 1, 1, 2] + ds = @inferred(shortest_paths(g, 1, m, Dijkstra(; all_paths=true))) + @test ds.pathcounts == [1, 1, 1, 1, 2] @test ds.predecessors == [[], [1], [1], [3], [3, 4]] @test ds.predecessors == [[], [1], [1], [3], [3, 4]] - dm = @inferred(shortest_paths(g, 1, Dijkstra(all_paths=true, track_vertices=true))) - @test dm.pathcounts == [1, 1, 1, 1, 2] - @test dm.predecessors == [[], [1], [1], [3], [2, 3]] + dm = @inferred( + shortest_paths(g, 1, Dijkstra(; all_paths=true, track_vertices=true)) + ) + @test dm.pathcounts == [1, 1, 1, 1, 2] + @test dm.predecessors == [[], [1], [1], [3], [2, 3]] @test dm.closest_vertices == [1, 2, 3, 5, 4] end @@ -371,7 +400,9 @@ end add_edge!(G, 1, 3) add_edge!(G, 4, 5) for g in testgraphs(G) - dm = @inferred(shortest_paths(g, 1, Dijkstra(all_paths=true, track_vertices=true))) + dm = @inferred( + shortest_paths(g, 1, Dijkstra(; all_paths=true, track_vertices=true)) + ) @test dm.closest_vertices == [1, 2, 3, 4, 5] end end @@ -394,14 +425,14 @@ end @test length(paths(z, 4, 3)) == 0 @test length(paths(z, 4, 1)) == 0 @test length(paths(z, 2, 3)) == 2 - end + end g5 = DiGraph([1 1 1 0 1; 0 1 0 1 1; 0 1 1 0 0; 1 0 1 1 0; 0 0 0 1 1]) d = [0 3 8 0 -4; 0 0 0 1 7; 0 4 0 0 0; 2 0 -5 0 0; 0 0 0 6 0] for g in testdigraphs(g5) z = @inferred(shortest_paths(g, d, FloydWarshall())) @test z.dists == [0 1 -3 2 -4; 3 0 -4 1 -1; 7 4 0 5 3; 2 -1 -5 0 -2; 8 5 1 6 0] - end + end @testset "paths infinite loop bug" begin g = SimpleGraph(2) @@ -420,10 +451,10 @@ end end @testset "default for no source, no alg" begin g = path_graph(4) - w = zeros(4,4) + w = zeros(4, 4) for i in 1:3 - w[i, i+1] = 1.0 - w[i+1, i] = 1.0 + w[i, i + 1] = 1.0 + w[i + 1, i] = 1.0 end @test shortest_paths(g) isa ShortestPaths.FloydWarshallResult @test shortest_paths(g, w) isa ShortestPaths.FloydWarshallResult @@ -448,14 +479,14 @@ end @test length(paths(z, 4, 3)) == 0 @test length(paths(z, 4, 1)) == 0 @test length(paths(z, 2, 3)) == 2 - end + end g5 = DiGraph([1 1 1 0 1; 0 1 0 1 1; 0 1 1 0 0; 1 0 1 1 0; 0 0 0 1 1]) d = [0 3 8 0 -4; 0 0 0 1 7; 0 4 0 0 0; 2 0 -5 0 0; 0 0 0 6 0] for g in testdigraphs(g5) z = @inferred(shortest_paths(g, d, Johnson())) @test z.dists == [0 1 -3 2 -4; 3 0 -4 1 -1; 7 4 0 5 3; 2 -1 -5 0 -2; 8 5 1 6 0] - end + end end @testset "SPFA" begin @@ -505,7 +536,7 @@ end add_edge!(G, 4, 5) m = [0 10 2 0 15; 10 9 0 1 0; 2 0 1 0 0; 0 1 0 0 2; 15 0 0 2 0] for g in testgraphs(G) - z = @inferred(shortest_paths(g, 1 , m, SPFA())) + z = @inferred(shortest_paths(g, 1, m, SPFA())) y = @inferred(dijkstra_shortest_paths(g, 1, m)) @test isapprox(z.dists, y.dists) end @@ -534,11 +565,11 @@ end @testset "Random Graphs" begin @testset "Simple graphs" begin - for seed = 1:5 + for seed in 1:5 rng = StableRNG(seed) - nvg = Int(ceil(250*rand(rng))) - neg = Int(floor((nvg*(nvg-1)/2)*rand(rng))) - g = SimpleGraph(nvg, neg; rng = rng) + nvg = Int(ceil(250 * rand(rng))) + neg = Int(floor((nvg * (nvg - 1) / 2) * rand(rng))) + g = SimpleGraph(nvg, neg; rng=rng) z = shortest_paths(g, 1, SPFA()) y = dijkstra_shortest_paths(g, 1) @test isapprox(z.dists, y.dists) @@ -546,11 +577,11 @@ end end @testset "Simple DiGraphs" begin - for seed = 1:5 + for seed in 1:5 rng = StableRNG(seed) - nvg = Int(ceil(250*rand(rng))) - neg = Int(floor((nvg*(nvg-1)/2)*rand(rng))) - g = SimpleDiGraph(nvg, neg; rng = rng) + nvg = Int(ceil(250 * rand(rng))) + neg = Int(floor((nvg * (nvg - 1) / 2) * rand(rng))) + g = SimpleDiGraph(nvg, neg; rng=rng) z = shortest_paths(g, 1, SPFA()) y = dijkstra_shortest_paths(g, 1) @test isapprox(z.dists, y.dists) @@ -600,11 +631,30 @@ end @test isapprox(z.dists, y.dists) @testset "Small Graphs" begin - for s in [:bull, :chvatal, :cubical, :desargues, - :diamond, :dodecahedral, :frucht, :heawood, - :house, :housex, :icosahedral, :krackhardtkite, :moebiuskantor, - :octahedral, :pappus, :petersen, :sedgewickmaze, :tutte, - :tetrahedral, :truncatedcube, :truncatedtetrahedron, :truncatedtetrahedron_dir] + for s in [ + :bull, + :chvatal, + :cubical, + :desargues, + :diamond, + :dodecahedral, + :frucht, + :heawood, + :house, + :housex, + :icosahedral, + :krackhardtkite, + :moebiuskantor, + :octahedral, + :pappus, + :petersen, + :sedgewickmaze, + :tutte, + :tetrahedral, + :truncatedcube, + :truncatedtetrahedron, + :truncatedtetrahedron_dir, + ] G = smallgraph(s) z = shortest_paths(G, 1, SPFA()) y = dijkstra_shortest_paths(G, 1) @@ -617,7 +667,9 @@ end g4 = path_digraph(5) d1 = float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) - d2 = sparse(float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) + d2 = sparse( + float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) + ) for g in testdigraphs(g4) y = @inferred(shortest_paths(g, 2, d1, SPFA())) z = @inferred(shortest_paths(g, 2, d2, SPFA())) @@ -625,7 +677,6 @@ end @test @inferred(!has_negative_weight_cycle(g, SPFA())) @test @inferred(!has_negative_weight_cycle(g, d1, SPFA())) - y = @inferred(shortest_paths(g, 2, d1, SPFA())) z = @inferred(shortest_paths(g, 2, d2, SPFA())) @test y.dists == z.dists == [Inf, 0, 6, 17, 33] @@ -635,17 +686,20 @@ end end end - @testset "Negative Cycle" begin # Negative Cycle 1 gx = complete_graph(3) for g in testgraphs(gx) d = [1 -3 1; -3 1 1; 1 1 1] - @test_throws ShortestPaths.NegativeCycleError shortest_paths(g, 1, d, SPFA()) + @test_throws ShortestPaths.NegativeCycleError shortest_paths( + g, 1, d, SPFA() + ) @test has_negative_weight_cycle(g, d, SPFA()) d = [1 -1 1; -1 1 1; 1 1 1] - @test_throws ShortestPaths.NegativeCycleError shortest_paths(g, 1, d, SPFA()) + @test_throws ShortestPaths.NegativeCycleError shortest_paths( + g, 1, d, SPFA() + ) @test has_negative_weight_cycle(g, d, SPFA()) end @@ -653,10 +707,11 @@ end gx = complete_graph(4) d = [1 -1 1 1; 1 1 1 -1; 1 1 1 1; 1 1 1 1] for g in testgraphs(gx) - @test_throws ShortestPaths.NegativeCycleError shortest_paths(g, 1, d, SPFA()) + @test_throws ShortestPaths.NegativeCycleError shortest_paths( + g, 1, d, SPFA() + ) @test has_negative_weight_cycle(g, d, SPFA()) end end - end end diff --git a/test/experimental/traversals.jl b/test/experimental/traversals.jl index d35e31b60..900066ebb 100644 --- a/test/experimental/traversals.jl +++ b/test/experimental/traversals.jl @@ -11,12 +11,13 @@ struct DummyTraversalState <: LET.AbstractTraversalState end @test LET.postvisitfn!(d, 1) == true @test LET.postlevelfn!(d) == true - x = [3,1,2] + x = [3, 1, 2] @test sort!(x, 3, 1, LET.NOOPSort, Base.Sort.ForwardOrdering()) == x @testset "BFS" begin - g1= smallgraph(:house) - dg1 = path_digraph(6); add_edge!(dg1, 2, 6) + g1 = smallgraph(:house) + dg1 = path_digraph(6) + add_edge!(dg1, 2, 6) b1 = LET.BFS() @test b1 == LET.BFS(LET.NOOPSort) @@ -33,7 +34,7 @@ struct DummyTraversalState <: LET.AbstractTraversalState end p2 = @inferred LET.parents(g, 2, b2) @test p1 == p2 == [2, 0, 1, 2, 4] d1 = @inferred LET.distances(g, 1, b1) - d2 = @inferred LET.distances(g, 1 ,b2) + d2 = @inferred LET.distances(g, 1, b2) d3 = @inferred LET.distances(g, [1], b1) @test d1 == d2 == d3 == [0, 1, 1, 2, 2] t1 = @inferred LET.tree(g, 2, b1) @@ -41,7 +42,6 @@ struct DummyTraversalState <: LET.AbstractTraversalState end t3 = @inferred LET.tree(p1) t4 = @inferred LET.tree(p2) @test t1 == t2 == t3 == t4 - end end @testset "directed" begin @@ -50,7 +50,9 @@ struct DummyTraversalState <: LET.AbstractTraversalState end v2 = @inferred LET.visited_vertices(dg, 1, b2) v3 = @inferred LET.visited_vertices(dg, [1], b1) @test v1 == v2 == v3 == [1, 2, 3, 6, 4, 5] - @test (@inferred LET.parents(dg, 2, b1)) == LET.parents(dg, 2, b2) == [0, 0, 2, 3, 4, 2] + @test (@inferred LET.parents(dg, 2, b1)) == + LET.parents(dg, 2, b2) == + [0, 0, 2, 3, 4, 2] p1 = @inferred LET.parents(dg, 2, b1) p2 = @inferred LET.parents(dg, 2, b2) p3 = @inferred LET.parents(dg, 6, b1, inneighbors) @@ -122,4 +124,3 @@ struct DummyTraversalState <: LET.AbstractTraversalState end end end end - diff --git a/test/graphcut/karger_min_cut.jl b/test/graphcut/karger_min_cut.jl index 79a38c3cd..bcd176638 100644 --- a/test/graphcut/karger_min_cut.jl +++ b/test/graphcut/karger_min_cut.jl @@ -1,31 +1,30 @@ @testset "Karger Minimum Cut" begin - gx = path_graph(5) #Assumes cut[1] = 1 for g in testgraphs(gx) cut = @inferred(karger_min_cut(g)) - @test findfirst(isequal(2), cut) == findlast(isequal(1), cut)+1 + @test findfirst(isequal(2), cut) == findlast(isequal(1), cut) + 1 @test karger_cut_cost(g, cut) == 1 - @test karger_cut_edges(g, cut) == [Edge(findlast(isequal(1), cut), findfirst(isequal(2), cut))] + @test karger_cut_edges(g, cut) == + [Edge(findlast(isequal(1), cut), findfirst(isequal(2), cut))] end add_vertex!(gx) for g in testgraphs(gx) cut = @inferred(karger_min_cut(g)) - @test cut == [1, 1, 1, 1, 1, 2] + @test cut == [1, 1, 1, 1, 1, 2] @test karger_cut_cost(g, cut) == 0 @test karger_cut_edges(g, cut) == Vector{Edge}() - end gx = star_graph(5) for g in testgraphs(gx) cut = @inferred(karger_min_cut(g)) - @test count(isequal(2), cut) == 1 + @test count(isequal(2), cut) == 1 @test karger_cut_cost(g, cut) == 1 - @test karger_cut_edges(g, cut) == [Edge(1, findfirst(isequal(2), cut))] + @test karger_cut_edges(g, cut) == [Edge(1, findfirst(isequal(2), cut))] end gx = SimpleGraph(1) @@ -33,5 +32,4 @@ cut = @inferred(karger_min_cut(g)) @test cut == zeros(Int, 1) end - end diff --git a/test/graphcut/normalized_cut.jl b/test/graphcut/normalized_cut.jl index 6573a5758..a343678b9 100644 --- a/test/graphcut/normalized_cut.jl +++ b/test/graphcut/normalized_cut.jl @@ -1,5 +1,4 @@ @testset "Normalized Cut" begin - gx = SimpleGraph(6) add_edge!(gx, 1, 2) add_edge!(gx, 2, 3) @@ -29,14 +28,14 @@ w[5, 6] = 1.0 for g in testgraphs(gx) - labels = @inferred(normalized_cut(g, 1, w)) - @test labels == [1, 1, 1, 2, 2, 2] || labels == [2, 2, 2, 1, 1, 1] + labels = @inferred(normalized_cut(g, 1, w)) + @test labels == [1, 1, 1, 2, 2, 2] || labels == [2, 2, 2, 1, 1, 1] end w = SparseMatrixCSC(w) for g in testgraphs(gx) - labels = @inferred(normalized_cut(g, 1, w)) - @test labels == [1, 1, 1, 2, 2, 2] || labels == [2, 2, 2, 1, 1, 1] + labels = @inferred(normalized_cut(g, 1, w)) + @test labels == [1, 1, 1, 2, 2, 2] || labels == [2, 2, 2, 1, 1, 1] end gx = SimpleGraph(4) @@ -49,34 +48,34 @@ w[4, 3] = 1.0 w[3, 4] = 1.0 for g in testgraphs(gx) - labels = @inferred(normalized_cut(g, 0.1, w)) - @test labels == [1, 1, 2, 2] || labels == [2, 2, 1, 1] + labels = @inferred(normalized_cut(g, 0.1, w)) + @test labels == [1, 1, 2, 2] || labels == [2, 2, 1, 1] end w = SparseMatrixCSC(w) for g in testgraphs(gx) - labels = @inferred(normalized_cut(g, 0.1, w)) - @test labels == [1, 1, 2, 2] || labels == [2, 2, 1, 1] + labels = @inferred(normalized_cut(g, 0.1, w)) + @test labels == [1, 1, 2, 2] || labels == [2, 2, 1, 1] end w = ones(12, 12) for g in testgraphs(gx) - labels = @inferred(normalized_cut(g, 0.1, w)) - @test labels == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + labels = @inferred(normalized_cut(g, 0.1, w)) + @test labels == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] end w = ones(12, 12) for g in testgraphs(gx) - labels = @inferred(normalized_cut(g, 0.1, w)) - @test labels == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + labels = @inferred(normalized_cut(g, 0.1, w)) + @test labels == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] end g = path_graph(50) function contiguous(labels::Vector{Int})::Bool changes = 0 - for i in 1:length(labels)-1 - if labels[i] != labels[i+1] + for i in 1:(length(labels) - 1) + if labels[i] != labels[i + 1] changes += 1 end end @@ -88,12 +87,12 @@ for t in [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9] labels = @inferred(normalized_cut(g, t)) @test contiguous(labels) == true - num_subgraphs[convert(Int, 10*t)] = size(unique(labels), 1) + num_subgraphs[convert(Int, 10 * t)] = size(unique(labels), 1) end @test issorted(num_subgraphs) == true - - @test any(length(unique(normalized_cut(g,t))) == 4 for t in [0.125, 0.15, 0.16, 0.175, 0.20]) - + @test any( + length(unique(normalized_cut(g, t))) == 4 for t in [0.125, 0.15, 0.16, 0.175, 0.20] + ) end diff --git a/test/independentset/degree_ind_set.jl b/test/independentset/degree_ind_set.jl index a89b73144..bff6666c8 100644 --- a/test/independentset/degree_ind_set.jl +++ b/test/independentset/degree_ind_set.jl @@ -1,5 +1,4 @@ @testset "Degree Independent Set" begin - g0 = SimpleGraph(0) for g in testgraphs(g0) c = @inferred(independent_set(g, DegreeIndependentSet())) @@ -9,7 +8,7 @@ g1 = SimpleGraph(1) for g in testgraphs(g1) c = @inferred(independent_set(g, DegreeIndependentSet())) - @test (c == [1,]) + @test (c == [1]) end add_edge!(g1, 1, 1) @@ -17,20 +16,20 @@ c = @inferred(independent_set(g, DegreeIndependentSet())) @test isempty(c) end - + g3 = star_graph(5) for g in testgraphs(g3) c = @inferred(independent_set(g, DegreeIndependentSet())) @test sort(c) == [2, 3, 4, 5] end - + g4 = complete_graph(5) for g in testgraphs(g4) c = @inferred(independent_set(g, DegreeIndependentSet())) - @test length(c)== 1 #Exactly one vertex + @test length(c) == 1 # Exactly one vertex end - #path_graph(5) with additional edge 2-5 + # path_graph(5) with additional edge 2-5 g5 = path_graph(5) add_edge!(g5, 2, 5) for g in testgraphs(g5) diff --git a/test/independentset/maximal_ind_set.jl b/test/independentset/maximal_ind_set.jl index 7a0e16f27..34d8319ed 100644 --- a/test/independentset/maximal_ind_set.jl +++ b/test/independentset/maximal_ind_set.jl @@ -1,5 +1,4 @@ @testset "Maximal Independent Set" begin - g0 = SimpleGraph(0) for g in testgraphs(g0) z = @inferred(independent_set(g, MaximalIndependentSet(); rng=StableRNG(3))) @@ -9,7 +8,7 @@ g1 = SimpleGraph(1) for g in testgraphs(g1) z = @inferred(independent_set(g, MaximalIndependentSet(); rng=StableRNG(3))) - @test (z == [1,]) + @test (z == [1]) end add_edge!(g1, 1, 1) @@ -17,17 +16,17 @@ z = @inferred(independent_set(g, MaximalIndependentSet(); rng=StableRNG(3))) isempty(z) end - + g3 = star_graph(5) for g in testgraphs(g3) z = @inferred(independent_set(g, MaximalIndependentSet(); rng=StableRNG(3))) - @test (length(z)== 1 || length(z)== 4) + @test (length(z) == 1 || length(z) == 4) end - + g4 = complete_graph(5) for g in testgraphs(g4) z = @inferred(independent_set(g, MaximalIndependentSet(); rng=StableRNG(3))) - @test length(z)== 1 #Exactly one vertex + @test length(z) == 1 # Exactly one vertex end g5 = path_graph(5) @@ -42,6 +41,6 @@ for g in testgraphs(g5) z = @inferred(independent_set(g, MaximalIndependentSet(); rng=StableRNG(3))) sort!(z) - @test (z == [4,] || z == [5,] || z == [1, 5] || z == [1, 4]) + @test (z == [4] || z == [5] || z == [1, 5] || z == [1, 4]) end end diff --git a/test/interface.jl b/test/interface.jl index 62792589f..6219ea16b 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -16,23 +16,16 @@ mutable struct DummyEdge <: AbstractEdge{Int} end for edgefun2edges in [==] @test_throws Graphs.NotImplementedError edgefun2edges(dummyedge, dummyedge) - end + end - for graphfunbasic in [ - nv, ne, vertices, edges, is_directed, - edgetype, eltype - ] + for graphfunbasic in [nv, ne, vertices, edges, is_directed, edgetype, eltype] @test_throws Graphs.NotImplementedError graphfunbasic(dummygraph) end - for graphfun1int in [ - has_vertex, inneighbors, outneighbors - ] + for graphfun1int in [has_vertex, inneighbors, outneighbors] @test_throws Graphs.NotImplementedError graphfun1int(dummygraph, 1) end - for graphfunedge in [ - has_edge, - ] + for graphfunedge in [has_edge] @test_throws Graphs.NotImplementedError graphfunedge(dummygraph, dummyedge) @test_throws Graphs.NotImplementedError graphfunedge(dummygraph, 1, 2) end @@ -43,5 +36,4 @@ mutable struct DummyEdge <: AbstractEdge{Int} end io = IOBuffer() Base.showerror(io, impl_error) @test String(take!(io)) == "method $edges not implemented." - end # testset diff --git a/test/linalg/graphmatrices.jl b/test/linalg/graphmatrices.jl index 219691804..cad436a93 100644 --- a/test/linalg/graphmatrices.jl +++ b/test/linalg/graphmatrices.jl @@ -22,7 +22,7 @@ using ArnoldiMethod function test_adjacency(mat) adjmat, stochmat, adjhat, avgmat = constructors(mat) - @test adjmat.D == vec(sum(mat, dims=1)) + @test adjmat.D == vec(sum(mat; dims=1)) @test adjmat.A == mat @test isa(sparse(mat), SparseMatrixCSC) @test isa(sparse(stochmat), SparseMatrixCSC) @@ -39,7 +39,7 @@ using ArnoldiMethod function test_laplacian(mat) lapl = CombinatorialLaplacian(CombinatorialAdjacency(mat)) @test typeof(lapl) <: Laplacian - #constructors that work. + # constructors that work. @test adjacency(lapl).A == mat adj = adjacency(lapl) @test typeof(StochasticAdjacency(adj)) <: StochasticAdjacency @@ -50,7 +50,7 @@ using ArnoldiMethod # converttest(SparseMatrix{Float64}, lapl) adjmat, stochmat, adjhat, avgmat = constructors(mat) - @test typeof(adjacency(lapl)) <: CombinatorialAdjacency + @test typeof(adjacency(lapl)) <: CombinatorialAdjacency stochlapl = StochasticLaplacian(StochasticAdjacency(adjmat)) @test typeof(adjacency(stochlapl)) <: StochasticAdjacency averaginglapl = AveragingLaplacian(AveragingAdjacency(adjmat)) @@ -60,7 +60,7 @@ using ArnoldiMethod @test typeof(adjacency(normalizedlapl)) <: NormalizedAdjacency @test !(typeof(adjacency(normalizedlapl)) <: CombinatorialAdjacency) - #constructors that fail. + # constructors that fail. @test_throws MethodError CombinatorialAdjacency(lapl) @test_throws MethodError StochasticLaplacian(lapl) @test_throws MethodError NormalizedLaplacian(lapl) @@ -69,7 +69,7 @@ using ArnoldiMethod L = sparse(lapl) - @test sum(abs, (sum(L, dims=1))) == 0 + @test sum(abs, (sum(L; dims=1))) == 0 end function test_accessors(mat, n) @@ -95,21 +95,21 @@ using ArnoldiMethod @test sum(abs, (adjmat * onevec)) > 0.0 @test sum(abs, ((stochmat * onevec) / sum(onevec))) ≈ 1.0 @test sum(abs, (lapl * onevec)) == 0 - g(a) = sum(abs, (sum(sparse(a), dims=1))) + g(a) = sum(abs, (sum(sparse(a); dims=1))) @test g(lapl) == 0 @test g(NormalizedLaplacian(adjhat)) > 1e-13 @test g(StochasticLaplacian(stochmat)) > 1e-13 - @test eigs(adjmat, which=LR())[1][1] > 1.0 - @test eigs(stochmat, which=LR())[1][1] ≈ 1.0 - @test eigs(avgmat, which=LR())[1][1] ≈ 1.0 - @test eigs(lapl, which=LR())[1][1] > 2.0 - @test eigs(sparse(lapl), which=SR())[1][2] > 0.0 - @test eigs(sparse(lapl), which=SR())[1][1] < 1e-7 - + @test eigs(adjmat; which=LR())[1][1] > 1.0 + @test eigs(stochmat; which=LR())[1][1] ≈ 1.0 + @test eigs(avgmat; which=LR())[1][1] ≈ 1.0 + @test eigs(lapl; which=LR())[1][1] > 2.0 + @test eigs(sparse(lapl); which=SR())[1][2] > 0.0 + @test eigs(sparse(lapl); which=SR())[1][1] < 1e-7 + lhat = NormalizedLaplacian(adjhat) - @test eigs(lhat, which=LR())[1][1] < 2.0 + 1e-9 + @test eigs(lhat; which=LR())[1][1] < 2.0 + 1e-9 end function test_other(mat, n) @@ -165,12 +165,12 @@ using ArnoldiMethod function test_punchedmatrix(mat, n) adjmat = CombinatorialAdjacency(mat) - ahatp = PunchedAdjacency(adjmat) + ahatp = PunchedAdjacency(adjmat) y = ahatp * perron(ahatp) @test dot(y, ahatp.perron) ≈ 0.0 atol = 1.0e-8 @test sum(abs, y) ≈ 0.0 atol = 1.0e-8 - eval, evecs = eigs(ahatp, which=LM()) - @test eval[1] - (1 + 1.0e-8) <= 0 + eval, evecs = eigs(ahatp; which=LM()) + @test eval[1] - (1 + 1.0e-8) <= 0 @test dot(perron(ahatp), evecs[:, 1]) ≈ 0.0 atol = 1e-8 ahat = ahatp.A @test isa(ahat, NormalizedAdjacency) @@ -179,7 +179,6 @@ using ArnoldiMethod @test norm(z) ≈ 0.0 atol = 1e-8 end - n = 10 mat = Float64.(sprand(rng, Bool, n, n, 0.3)) @@ -194,11 +193,9 @@ using ArnoldiMethod test_symmetry(mat, n) test_punchedmatrix(mat, n) - - """Computes the stationary distribution of a random walk""" function stationarydistribution(R::StochasticAdjacency; kwargs...) - er = eigs(R, nev=1, which=LR(); kwargs...) + er = eigs(R; nev=1, which=LR(), kwargs...) l1 = er[1][1] abs(l1 - 1) < 1e-8 || error("failed to compute stationary distribution") # TODO 0.7: should we change the error type to InexactError? p = real(er[2][:, 1]) @@ -212,7 +209,7 @@ using ArnoldiMethod function stationarydistribution(A::CombinatorialAdjacency; kwargs...) R = StochasticAdjacency(A) - stationarydistribution(R; kwargs...) + return stationarydistribution(R; kwargs...) end # Random walk demo @@ -226,18 +223,17 @@ using ArnoldiMethod @testset "Noop" begin @testset "Noop broadcasted with * is identity" begin - testobjects = [1, "string", 'c', cycle_graph(3), [1,2,3], sin, Noop()] + testobjects = [1, "string", 'c', cycle_graph(3), [1, 2, 3], sin, Noop()] for object in testobjects @test Noop() .* object === object end end @testset "Noop * is identity" begin - testobjects = [1, "string", 'c', cycle_graph(3), [1,2,3], sin, Noop()] + testobjects = [1, "string", 'c', cycle_graph(3), [1, 2, 3], sin, Noop()] for object in testobjects @test Noop() * object === object end end end - end diff --git a/test/linalg/runtests.jl b/test/linalg/runtests.jl index c7a346bac..697e41354 100644 --- a/test/linalg/runtests.jl +++ b/test/linalg/runtests.jl @@ -4,11 +4,7 @@ using SparseArrays using LinearAlgebra const linalgtestdir = dirname(@__FILE__) -tests = [ - "graphmatrices", - "spectral" -] - +tests = ["graphmatrices", "spectral"] @testset "Graphs.LinAlg" begin for t in tests diff --git a/test/linalg/spectral.jl b/test/linalg/spectral.jl index 1478008c3..0addfcdcd 100644 --- a/test/linalg/spectral.jl +++ b/test/linalg/spectral.jl @@ -2,7 +2,7 @@ import Base: Matrix import Base: size using ArnoldiMethod -#using Graphs.LinAlg: eigs +# using Graphs.LinAlg: eigs # just so that we can assert equality of matrices Matrix(nbt::Nonbacktracking) = Matrix(sparse(nbt)) @testset "Spectral" begin @@ -20,8 +20,10 @@ Matrix(nbt::Nonbacktracking) = Matrix(sparse(nbt)) @test adjacency_spectrum(g)[1] ≈ -1.732050807568878 end - - add_edge!(g5, 1, 2); add_edge!(g5, 2, 3); add_edge!(g5, 1, 3); add_edge!(g5, 3, 4) + add_edge!(g5, 1, 2) + add_edge!(g5, 2, 3) + add_edge!(g5, 1, 3) + add_edge!(g5, 3, 4) for g in testdigraphs(g5) @test adjacency_matrix(g, Bool) == adjacency_matrix(g, Bool; dir=:out) @test laplacian_spectrum(g)[3] == laplacian_spectrum(g; dir=:both)[3] == 3.0 @@ -38,15 +40,15 @@ Matrix(nbt::Nonbacktracking) = Matrix(sparse(nbt)) g = copy(g5) add_edge!(g, 1, 1) @test adjacency_matrix(g)[1, 1] == 1 - @test indegree(g) == sum(adjacency_matrix(g), dims=1)[1, :] - @test outdegree(g) == sum(adjacency_matrix(g), dims=2)[:, 1] + @test indegree(g) == sum(adjacency_matrix(g); dims=1)[1, :] + @test outdegree(g) == sum(adjacency_matrix(g); dims=2)[:, 1] g10 = complete_graph(10) for g in testgraphs(g10) B, em = non_backtracking_matrix(g) @test length(em) == 2 * ne(g) @test size(B) == (2 * ne(g), 2 * ne(g)) - for i = 1:10 + for i in 1:10 @test sum(B[:, i]) == 8 @test sum(B[i, :]) == 8 end @@ -56,8 +58,8 @@ Matrix(nbt::Nonbacktracking) = Matrix(sparse(nbt)) z = zeros(Float64, nv(g)) n10 = Nonbacktracking(g) @test size(n10) == (2 * ne(g), 2 * ne(g)) - @test size(n10,1) == n10.m - @test size(n10,2) == n10.m + @test size(n10, 1) == n10.m + @test size(n10, 2) == n10.m @test eltype(n10) == Float64 @test !issymmetric(n10) @@ -74,25 +76,25 @@ Matrix(nbt::Nonbacktracking) = Matrix(sparse(nbt)) for g in testgraphs(g3) @test adjacency_matrix(g) == - adjacency_matrix(g, dir=:out) == - adjacency_matrix(g, dir=:in) == - adjacency_matrix(g, dir=:both) + adjacency_matrix(g; dir=:out) == + adjacency_matrix(g; dir=:in) == + adjacency_matrix(g; dir=:both) @test_throws ErrorException adjacency_matrix(g; dir=:purple) end - #that call signature works + # that call signature works for g in testdigraphs(g5) - inmat = adjacency_matrix(g, Int; dir=:in) - outmat = adjacency_matrix(g, Int; dir=:out) + inmat = adjacency_matrix(g, Int; dir=:in) + outmat = adjacency_matrix(g, Int; dir=:out) bothmat = adjacency_matrix(g, Int; dir=:both) - #relations that should be true + # relations that should be true @test inmat' == outmat @test all((bothmat - outmat) .>= 0) - @test all((bothmat - inmat) .>= 0) + @test all((bothmat - inmat) .>= 0) - #check properties of the undirected laplacian carry over. + # check properties of the undirected laplacian carry over. for dir in [:in, :out, :both] T = eltype(g) amat = adjacency_matrix(g, Float64; dir=dir) @@ -103,7 +105,6 @@ Matrix(nbt::Nonbacktracking) = Matrix(sparse(nbt)) end end - for g in testdigraphs(g4) # testing incidence_matrix, first directed graph @test size(incidence_matrix(g)) == (5, 4) @@ -113,7 +114,7 @@ Matrix(nbt::Nonbacktracking) = Matrix(sparse(nbt)) A = incidence_matrix(g) B = incidence_matrix(reverse(g)) - @test all((A+B) .== 0) + @test all((A + B) .== 0) end for g in testgraphs(g3) @@ -132,7 +133,7 @@ Matrix(nbt::Nonbacktracking) = Matrix(sparse(nbt)) # Check size of incidence matrix with isolated vertices g6 = complete_graph(4) - for k = 1:3 + for k in 1:3 add_vertex!(g6) end for g in testgraphs(g6) @@ -141,7 +142,8 @@ Matrix(nbt::Nonbacktracking) = Matrix(sparse(nbt)) # TESTS FOR Nonbacktracking operator. - n = 10; k = 5 + n = 10 + k = 5 pg = complete_graph(n) # ϕ1 = nonbacktrack_embedding(pg, k)' for g in testgraphs(pg) @@ -149,7 +151,7 @@ Matrix(nbt::Nonbacktracking) = Matrix(sparse(nbt)) B, emap = non_backtracking_matrix(g) Bs = sparse(nbt) @test sparse(B) == Bs - @test eigs(nbt, which=LR(), nev=1)[1] ≈ eigs(B, which=LR(), nev=1)[1] atol = 1e-5 + @test eigs(nbt; which=LR(), nev=1)[1] ≈ eigs(B; which=LR(), nev=1)[1] atol = 1e-5 # check that matvec works x = ones(Float64, nbt.m) @@ -157,11 +159,11 @@ Matrix(nbt::Nonbacktracking) = Matrix(sparse(nbt)) z = B * x @test norm(y - z) < 1e-8 - #check that matmat works and Matrix(nbt) == B + # check that matmat works and Matrix(nbt) == B @test norm(nbt * Matrix{Float64}(I, nbt.m, nbt.m) - B) < 1e-8 - #check that matmat works and Matrix(nbt) == B + # check that matmat works and Matrix(nbt) == B @test norm(nbt * Matrix{Float64}(I, nbt.m, nbt.m) - B) < 1e-8 @test size(y) == size(x) @@ -169,7 +171,7 @@ Matrix(nbt::Nonbacktracking) = Matrix(sparse(nbt)) B₁ = Nonbacktracking(g10) @test Matrix(B₁) == Matrix(B) - @test B₁ * ones(size(B₁)[2]) == B * ones(size(B)[2]) + @test B₁ * ones(size(B₁)[2]) == B * ones(size(B)[2]) @test size(B₁) == size(B) @test !issymmetric(B₁) @test eltype(B₁) == Float64 @@ -177,8 +179,8 @@ Matrix(nbt::Nonbacktracking) = Matrix(sparse(nbt)) # END tests for Nonbacktracking # spectral distance checks - for n = 3:10 - polygon = random_regular_graph(n, 2, rng=rng) + for n in 3:10 + polygon = random_regular_graph(n, 2; rng=rng) for g in testgraphs(polygon) @test spectral_distance(g, g) ≈ 0 atol = 1e-8 @test spectral_distance(g, g, 1) ≈ 0 atol = 1e-8 diff --git a/test/operators.jl b/test/operators.jl index 891816753..afbc714ce 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -107,8 +107,8 @@ @test nv(h2) == 5 h3 = star_graph(5) - h3merged = merge_vertices(h3, [1,2]) - @test neighbors(h3merged, 1) == [2,3,4] + h3merged = merge_vertices(h3, [1, 2]) + @test neighbors(h3merged, 1) == [2, 3, 4] @test neighbors(h3merged, 2) == [1] @test neighbors(h3merged, 3) == [1] @test neighbors(h3merged, 4) == [1] @@ -178,7 +178,7 @@ px = path_graph(10) @testset "Matrix operations: $p" for p in testgraphs(px) x = @inferred(p * ones(10)) - @test x[1] == 1.0 && all(x[2:(end - 1)] .== 2.0) && x[end] == 1.0 + @test x[1] == 1.0 && all(x[2:(end - 1)] .== 2.0) && x[end] == 1.0 @test size(p) == (10, 10) @test size(p, 1) == size(p, 2) == 10 @test size(p, 3) == 1 @@ -191,16 +191,20 @@ end gx = SimpleDiGraph(4) - add_edge!(gx, 1, 2); add_edge!(gx, 2, 3); add_edge!(gx, 1, 3); add_edge!(gx, 3, 4) + add_edge!(gx, 1, 2) + add_edge!(gx, 2, 3) + add_edge!(gx, 1, 3) + add_edge!(gx, 3, 4) @testset "Matrix operations: $g" for g in testdigraphs(gx) @test @inferred(g * ones(nv(g))) == [2.0, 1.0, 1.0, 0.0] - @test sum(g, 1) == [0, 1, 2, 1] - @test sum(g, 2) == [2, 1, 1, 0] + @test sum(g, 1) == [0, 1, 2, 1] + @test sum(g, 2) == [2, 1, 1, 0] @test sum(g) == 4 @test @inferred(!issymmetric(g)) end - nx = 20; ny = 21 + nx = 20 + ny = 21 @testset "Cartesian Product / Crosspath: $g" for g in testlargegraphs(path_graph(ny)) T = eltype(g) hp = path_graph(nx) @@ -260,7 +264,7 @@ @test typeof(h) == typeof(g) end - gx = SimpleDiGraph(100, 200, rng=rng) + gx = SimpleDiGraph(100, 200; rng=rng) @testset "Subgraphs: $g" for g in testdigraphs(gx) h = @inferred(g[5:26]) @test nv(h) == 22 @@ -279,7 +283,6 @@ @test h2 == h @test vm == findall(r2) @test h2 == g[r2] - end g10 = complete_graph(10) @@ -299,8 +302,11 @@ @test vm[4] == 8 elist = [ - SimpleEdge(1, 2), SimpleEdge(2, 3), SimpleEdge(3, 4), - SimpleEdge(4, 5), SimpleEdge(5, 1) + SimpleEdge(1, 2), + SimpleEdge(2, 3), + SimpleEdge(3, 4), + SimpleEdge(4, 5), + SimpleEdge(5, 1), ] sg, vm = @inferred(induced_subgraph(g, elist)) @test sg == cycle_graph(5) diff --git a/test/parallel/centrality/betweenness.jl b/test/parallel/centrality/betweenness.jl index 4f2d3fb8c..70ab5063b 100644 --- a/test/parallel/centrality/betweenness.jl +++ b/test/parallel/centrality/betweenness.jl @@ -1,55 +1,68 @@ @testset "Parallel.Betweenness" begin rng = StableRNG(1) s2 = SimpleDiGraph(3) - add_edge!(s2, 1, 2); add_edge!(s2, 2, 3); add_edge!(s2, 3, 3) + add_edge!(s2, 1, 2) + add_edge!(s2, 2, 3) + add_edge!(s2, 3, 3) s1 = SimpleGraph(s2) g3 = path_graph(5) gint = loadgraph(joinpath(testdir, "testdata", "graph-50-500.jgz"), "graph-50-500") for g in testdigraphs(gint) - z = @inferred(Graphs.betweenness_centrality(g)) + z = @inferred(Graphs.betweenness_centrality(g)) zt = @inferred(Parallel.betweenness_centrality(g; parallel=:threads)) @test all(isapprox.(z, zt)) zd = @inferred(Parallel.betweenness_centrality(g; parallel=:distributed)) @test all(isapprox.(z, zd)) - y = Graphs.betweenness_centrality(g, endpoints=true, normalize=false) - yt = Parallel.betweenness_centrality(g, endpoints=true, normalize=false, parallel=:threads) + y = Graphs.betweenness_centrality(g; endpoints=true, normalize=false) + yt = Parallel.betweenness_centrality( + g; endpoints=true, normalize=false, parallel=:threads + ) @test all(isapprox(y, yt)) - yd = Parallel.betweenness_centrality(g, endpoints=true, normalize=false, parallel=:distributed) + yd = Parallel.betweenness_centrality( + g; endpoints=true, normalize=false, parallel=:distributed + ) @test all(isapprox(y, yd)) xt = @inferred(Parallel.betweenness_centrality(g, 3; parallel=:threads, rng=rng)) @test length(xt) == 50 - xd = @inferred(Parallel.betweenness_centrality(g, 3; parallel=:distributed, rng=rng)) + xd = @inferred( + Parallel.betweenness_centrality(g, 3; parallel=:distributed, rng=rng) + ) @test length(xd) == 50 - xt2 = @inferred(Parallel.betweenness_centrality(g, collect(1:20); parallel=:threads)) + xt2 = @inferred( + Parallel.betweenness_centrality(g, collect(1:20); parallel=:threads) + ) @test length(xt2) == 50 - xd2 = @inferred(Parallel.betweenness_centrality(g, collect(1:20); parallel=:distributed)) + xd2 = @inferred( + Parallel.betweenness_centrality(g, collect(1:20); parallel=:distributed) + ) @test length(xd2) == 50 end @test @inferred(Parallel.betweenness_centrality(s1; parallel=:threads)) == [0, 1, 0] @test @inferred(Parallel.betweenness_centrality(s1; parallel=:distributed)) == [0, 1, 0] @test @inferred(Parallel.betweenness_centrality(s2; parallel=:threads)) == [0, 0.5, 0] - @test @inferred(Parallel.betweenness_centrality(s2; parallel=:distributed)) == [0, 0.5, 0] + @test @inferred(Parallel.betweenness_centrality(s2; parallel=:distributed)) == + [0, 0.5, 0] g = SimpleGraph(2) add_edge!(g, 1, 2) - z = Graphs.betweenness_centrality(g; normalize=true) + z = Graphs.betweenness_centrality(g; normalize=true) @test z[1] == z[2] == 0.0 zt = Parallel.betweenness_centrality(g; normalize=true, parallel=:threads) @test all(isapprox(z, zt)) zd = Parallel.betweenness_centrality(g; normalize=true, parallel=:distributed) @test all(isapprox(z, zd)) - z2 = Graphs.betweenness_centrality(g, vertices(g)) + z2 = Graphs.betweenness_centrality(g, vertices(g)) zt2 = Parallel.betweenness_centrality(g, vertices(g); parallel=:threads) @test all(isapprox(z2, zt2)) zd2 = Parallel.betweenness_centrality(g, vertices(g); parallel=:distributed) @test all(isapprox(z2, zd2)) - z3 = Graphs.betweenness_centrality(g, [vertices(g);]) + z3 = Graphs.betweenness_centrality(g, [vertices(g);]) zt3 = Parallel.betweenness_centrality(g, [vertices(g);]; parallel=:threads) @test all(isapprox(z3, zt3)) zd3 = Parallel.betweenness_centrality(g, [vertices(g);]; parallel=:distributed) @@ -57,7 +70,7 @@ @test z == z2 == z3 - z = Graphs.betweenness_centrality(g3; normalize=false) + z = Graphs.betweenness_centrality(g3; normalize=false) zt = Parallel.betweenness_centrality(g3; normalize=false, parallel=:threads) @test all(isapprox(z, zt)) zd = Parallel.betweenness_centrality(g3; normalize=false, parallel=:distributed) diff --git a/test/parallel/centrality/closeness.jl b/test/parallel/centrality/closeness.jl index 3d5516dc6..770c63dcf 100644 --- a/test/parallel/centrality/closeness.jl +++ b/test/parallel/centrality/closeness.jl @@ -1,16 +1,21 @@ @testset "Parallel.Closeness" begin g5 = SimpleDiGraph(4) - add_edge!(g5, 1, 2); add_edge!(g5, 2, 3); add_edge!(g5, 1, 3); add_edge!(g5, 3, 4) + add_edge!(g5, 1, 2) + add_edge!(g5, 2, 3) + add_edge!(g5, 1, 3) + add_edge!(g5, 3, 4) for g in testdigraphs(g5) - dy = @inferred(Parallel.closeness_centrality(g; normalize=false, parallel=:distributed)) + dy = @inferred( + Parallel.closeness_centrality(g; normalize=false, parallel=:distributed) + ) @test dy ≈ [0.75, 0.6666666666666666, 1.0, 0.0] ty = @inferred(Parallel.closeness_centrality(g; normalize=false, parallel=:threads)) @test ty ≈ [0.75, 0.6666666666666666, 1.0, 0.0] dz = @inferred(Parallel.closeness_centrality(g; parallel=:distributed)) @test dz ≈ [0.75, 0.4444444444444444, 0.3333333333333333, 0.0] - tz = @inferred(Parallel.closeness_centrality(g; parallel=:threads)) + tz = @inferred(Parallel.closeness_centrality(g; parallel=:threads)) @test tz ≈ [0.75, 0.4444444444444444, 0.3333333333333333, 0.0] end @@ -19,12 +24,18 @@ for g in testdigraphs(a2) distmx2 = [Inf 2.0 Inf; 3.2 Inf 4.2; 5.5 6.1 Inf] c2 = [0.24390243902439027, 0.27027027027027023, 0.1724137931034483] - - dy = @inferred(Parallel.closeness_centrality(g, distmx2; normalize=false, parallel=:distributed)) + + dy = @inferred( + Parallel.closeness_centrality( + g, distmx2; normalize=false, parallel=:distributed + ) + ) @test isapprox(dy, c2) - ty = @inferred(Parallel.closeness_centrality(g, distmx2; normalize=false, parallel=:threads)) + ty = @inferred( + Parallel.closeness_centrality(g, distmx2; normalize=false, parallel=:threads) + ) @test isapprox(ty, c2) - + dz = @inferred(Parallel.closeness_centrality(g, distmx2; parallel=:distributed)) @test isapprox(dz, c2) tz = @inferred(Parallel.closeness_centrality(g, distmx2; parallel=:threads)) @@ -48,12 +59,18 @@ for g in testgraphs(a1) distmx1 = [Inf 2.0 Inf; 2.0 Inf 4.2; Inf 4.2 Inf] c1 = [0.24390243902439027, 0.3225806451612903, 0.1923076923076923] - dy = @inferred(Parallel.closeness_centrality(g, distmx1; normalize=false, parallel=:distributed)) + dy = @inferred( + Parallel.closeness_centrality( + g, distmx1; normalize=false, parallel=:distributed + ) + ) dz = @inferred(Parallel.closeness_centrality(g, distmx1; parallel=:distributed)) @test isapprox(dy, c1) @test isapprox(dz, c1) - ty = @inferred(Parallel.closeness_centrality(g, distmx1; normalize=false, parallel=:threads)) + ty = @inferred( + Parallel.closeness_centrality(g, distmx1; normalize=false, parallel=:threads) + ) tz = @inferred(Parallel.closeness_centrality(g, distmx1; parallel=:threads)) @test isapprox(ty, c1) @test isapprox(tz, c1) diff --git a/test/parallel/centrality/pagerank.jl b/test/parallel/centrality/pagerank.jl index c694ce088..0c1564498 100644 --- a/test/parallel/centrality/pagerank.jl +++ b/test/parallel/centrality/pagerank.jl @@ -5,19 +5,24 @@ M = Matrix{Float64}(adjacency_matrix(g)) M = M' M[:, danglingnodes] .= sum(danglingnodes) ./ nv(g) - M = M * Diagonal(1 ./ sum(M, dims=1)[:]) - @assert all(1.01 .>= sum(M, dims=1) .>= 0.999) - # v = inv(I-β*M) * ((1-β)/nv(g) * ones(nv(g), 1)) + M = M * Diagonal(1 ./ sum(M; dims=1)[:]) + @assert all(1.01 .>= sum(M; dims=1) .>= 0.999) + # v = inv(I-β*M) * ((1-β)/nv(g) * ones(nv(g), 1)) v = inv(I - α * M) * ((1 - α) / nv(g) * ones(nv(g), 1)) return v end g5 = SimpleDiGraph(4) - add_edge!(g5, 1, 2); add_edge!(g5, 2, 3); add_edge!(g5, 1, 3); add_edge!(g5, 3, 4) + add_edge!(g5, 1, 2) + add_edge!(g5, 2, 3) + add_edge!(g5, 1, 3) + add_edge!(g5, 3, 4) g6 = SimpleGraph(4) - add_edge!(g6, 1, 2); add_edge!(g6, 2, 3); add_edge!(g6, 1, 3); add_edge!(g6, 3, 4) + add_edge!(g6, 1, 2) + add_edge!(g6, 2, 3) + add_edge!(g6, 1, 3) + add_edge!(g6, 3, 4) for α in [0.75, 0.85] - for g in testdigraphs(g5) @test Parallel.pagerank(g)[3] ≈ 0.318 atol = 0.001 @test length(@inferred(Parallel.pagerank(g))) == nv(g) diff --git a/test/parallel/centrality/radiality.jl b/test/parallel/centrality/radiality.jl index 8f3cb4b57..14f09b22a 100644 --- a/test/parallel/centrality/radiality.jl +++ b/test/parallel/centrality/radiality.jl @@ -13,8 +13,8 @@ add_edge!(g1, 4, 5) for g in testgraphs(g1) zd = @inferred(Parallel.radiality_centrality(g; parallel=:distributed)) - @test zd ≈ [5 // 6, 3 // 4, 5 // 6, 11 // 12, 2 // 3] + @test zd ≈ [5//6, 3//4, 5//6, 11//12, 2//3] zt = @inferred(Parallel.radiality_centrality(g; parallel=:threads)) - @test zt ≈ [5 // 6, 3 // 4, 5 // 6, 11 // 12, 2 // 3] + @test zt ≈ [5//6, 3//4, 5//6, 11//12, 2//3] end end diff --git a/test/parallel/centrality/stress.jl b/test/parallel/centrality/stress.jl index abfed85b4..abc2c67a6 100644 --- a/test/parallel/centrality/stress.jl +++ b/test/parallel/centrality/stress.jl @@ -3,18 +3,17 @@ gint = loadgraph(joinpath(testdir, "testdata", "graph-50-500.jgz"), "graph-50-500") c = vec(readdlm(joinpath(testdir, "testdata", "graph-50-500-sc.txt"), ',')) for g in testdigraphs(gint) - - z = Graphs.stress_centrality(g) + z = Graphs.stress_centrality(g) zd = @inferred(Parallel.stress_centrality(g; parallel=:distributed)) @test z == zd == c zt = @inferred(Parallel.stress_centrality(g; parallel=:threads)) - @test z == zt == c + @test z == zt == c xd = Parallel.stress_centrality(g, 3; parallel=:distributed, rng=rng) @test length(xd) == 50 xt = Parallel.stress_centrality(g, 3; parallel=:threads, rng=rng) @test length(xt) == 50 - + xd2 = Parallel.stress_centrality(g, collect(1:20); parallel=:distributed) @test length(xd2) == 50 xt2 = Parallel.stress_centrality(g, collect(1:20); parallel=:threads) diff --git a/test/parallel/distance.jl b/test/parallel/distance.jl index 08595d4cb..16db54e06 100644 --- a/test/parallel/distance.jl +++ b/test/parallel/distance.jl @@ -11,8 +11,12 @@ z = @inferred(Graphs.eccentricity(g, distmx1)) y = @inferred(Parallel.eccentricity(g, distmx1)) @test isapprox(y, z) - @test @inferred(Graphs.diameter(y)) == @inferred(Parallel.diameter(g, distmx1)) == 6.2 - @test @inferred(Graphs.periphery(y)) == @inferred(Parallel.periphery(g, distmx1)) == [1, 3] + @test @inferred(Graphs.diameter(y)) == + @inferred(Parallel.diameter(g, distmx1)) == + 6.2 + @test @inferred(Graphs.periphery(y)) == + @inferred(Parallel.periphery(g, distmx1)) == + [1, 3] @test @inferred(Graphs.radius(y)) == @inferred(Parallel.radius(g, distmx1)) == 4.2 @test @inferred(Graphs.center(y)) == @inferred(Parallel.center(g, distmx1)) == [2] end @@ -21,9 +25,13 @@ z = @inferred(Graphs.eccentricity(g, distmx2)) y = @inferred(Parallel.eccentricity(g, distmx2)) @test isapprox(y, z) - @test @inferred(Graphs.diameter(y)) == @inferred(Parallel.diameter(g, distmx2)) == 6.2 - @test @inferred(Graphs.periphery(y)) == @inferred(Parallel.periphery(g, distmx2)) == [1] + @test @inferred(Graphs.diameter(y)) == + @inferred(Parallel.diameter(g, distmx2)) == + 6.2 + @test @inferred(Graphs.periphery(y)) == + @inferred(Parallel.periphery(g, distmx2)) == + [1] @test @inferred(Graphs.radius(y)) == @inferred(Parallel.radius(g, distmx2)) == 4.2 @test @inferred(Graphs.center(y)) == @inferred(Parallel.center(g, distmx2)) == [2] - end + end end diff --git a/test/parallel/dominatingset/minimal_dom_set.jl b/test/parallel/dominatingset/minimal_dom_set.jl index 5b4b63cf6..3affa04ac 100644 --- a/test/parallel/dominatingset/minimal_dom_set.jl +++ b/test/parallel/dominatingset/minimal_dom_set.jl @@ -2,7 +2,11 @@ g0 = SimpleGraph(0) for g in testgraphs(g0) for parallel in [:threads, :distributed] - d = @inferred(Parallel.dominating_set(g, 4, MinimalDominatingSet(); parallel=parallel, rng=StableRNG(0))) + d = @inferred( + Parallel.dominating_set( + g, 4, MinimalDominatingSet(); parallel=parallel, rng=StableRNG(0) + ) + ) @test isempty(d) end end @@ -10,37 +14,59 @@ g1 = SimpleGraph(1) for g in testgraphs(g1) for parallel in [:threads, :distributed] - d = @inferred(Parallel.dominating_set(g, 4, MinimalDominatingSet(); parallel=parallel, rng=StableRNG(0))) - @test (d == [1,]) + d = @inferred( + Parallel.dominating_set( + g, 4, MinimalDominatingSet(); parallel=parallel, rng=StableRNG(0) + ) + ) + @test (d == [1]) add_edge!(g, 1, 1) - d = @inferred(Parallel.dominating_set(g, 4, MinimalDominatingSet(); parallel=parallel, rng=StableRNG(0))) - @test (d == [1,]) + d = @inferred( + Parallel.dominating_set( + g, 4, MinimalDominatingSet(); parallel=parallel, rng=StableRNG(0) + ) + ) + @test (d == [1]) end end g3 = star_graph(5) for parallel in [:threads, :distributed] for g in testgraphs(g3) - d = @inferred(Parallel.dominating_set(g, 4, MinimalDominatingSet(); parallel=parallel, rng=StableRNG(0))) - @test (length(d)== 1 || (length(d)== 4 && minimum(d) > 1 )) + d = @inferred( + Parallel.dominating_set( + g, 4, MinimalDominatingSet(); parallel=parallel, rng=StableRNG(0) + ) + ) + @test (length(d) == 1 || (length(d) == 4 && minimum(d) > 1)) end end - + g4 = complete_graph(5) for parallel in [:threads, :distributed] for g in testgraphs(g4) - d = @inferred(Parallel.dominating_set(g, 4, MinimalDominatingSet(); parallel=parallel, rng=StableRNG(0))) - @test length(d)== 1 #Exactly one vertex + d = @inferred( + Parallel.dominating_set( + g, 4, MinimalDominatingSet(); parallel=parallel, rng=StableRNG(0) + ) + ) + @test length(d) == 1 # Exactly one vertex end end g5 = path_graph(4) for parallel in [:threads, :distributed] for g in testgraphs(g5) - d = @inferred(Parallel.dominating_set(g, 4, MinimalDominatingSet(); parallel=parallel, rng=StableRNG(0))) + d = @inferred( + Parallel.dominating_set( + g, 4, MinimalDominatingSet(); parallel=parallel, rng=StableRNG(0) + ) + ) sort!(d) - @test (d == [1, 2, 4] || d == [1, 3] || d == [1, 4] || d == [2, 3] || d == [2, 4]) + @test ( + d == [1, 2, 4] || d == [1, 3] || d == [1, 4] || d == [2, 3] || d == [2, 4] + ) end end @@ -48,9 +74,15 @@ add_edge!(g5, 3, 3) for parallel in [:threads, :distributed] for g in testgraphs(g5) - d = @inferred(Parallel.dominating_set(g, 4, MinimalDominatingSet(); parallel=parallel, rng=StableRNG(0))) + d = @inferred( + Parallel.dominating_set( + g, 4, MinimalDominatingSet(); parallel=parallel, rng=StableRNG(0) + ) + ) sort!(d) - @test (d == [1, 2, 4] || d == [1, 3] || d == [1, 4] || d == [2, 3] || d == [2, 4]) + @test ( + d == [1, 2, 4] || d == [1, 3] || d == [1, 4] || d == [2, 3] || d == [2, 4] + ) end end end diff --git a/test/parallel/independentset/maximal_ind_set.jl b/test/parallel/independentset/maximal_ind_set.jl index 172494393..2832def4e 100644 --- a/test/parallel/independentset/maximal_ind_set.jl +++ b/test/parallel/independentset/maximal_ind_set.jl @@ -1,11 +1,14 @@ @testset "Maximal Independent Set" begin - rng = StableRNG(1) g0 = SimpleGraph(0) - for parallel in [:threads, :distributed] + for parallel in [:threads, :distributed] for g in testgraphs(g0) - z = @inferred(Parallel.independent_set(g, 4, MaximalIndependentSet(); parallel=parallel, rng=rng)) + z = @inferred( + Parallel.independent_set( + g, 4, MaximalIndependentSet(); parallel=parallel, rng=rng + ) + ) @test isempty(z) end end @@ -13,39 +16,59 @@ g1 = SimpleGraph(1) for parallel in [:threads, :distributed] for g in testgraphs(g1) - z = @inferred(Parallel.independent_set(g, 4, MaximalIndependentSet(); parallel=parallel, rng=rng)) - @test (z == [1,]) + z = @inferred( + Parallel.independent_set( + g, 4, MaximalIndependentSet(); parallel=parallel, rng=rng + ) + ) + @test (z == [1]) end end add_edge!(g1, 1, 1) for parallel in [:threads, :distributed] for g in testgraphs(g1) - z = @inferred(Parallel.independent_set(g, 4, MaximalIndependentSet(); parallel=parallel, rng=rng)) + z = @inferred( + Parallel.independent_set( + g, 4, MaximalIndependentSet(); parallel=parallel, rng=rng + ) + ) isempty(z) end end - + g3 = star_graph(5) for parallel in [:threads, :distributed] for g in testgraphs(g3) - z = @inferred(Parallel.independent_set(g, 4, MaximalIndependentSet(); parallel=parallel, rng=rng)) - @test (length(z)== 1 || length(z)== 4) + z = @inferred( + Parallel.independent_set( + g, 4, MaximalIndependentSet(); parallel=parallel, rng=rng + ) + ) + @test (length(z) == 1 || length(z) == 4) end end - + g4 = complete_graph(5) for parallel in [:threads, :distributed] for g in testgraphs(g4) - z = @inferred(Parallel.independent_set(g, 4, MaximalIndependentSet(); parallel=parallel, rng=rng)) - @test length(z)== 1 #Exactly one vertex + z = @inferred( + Parallel.independent_set( + g, 4, MaximalIndependentSet(); parallel=parallel, rng=rng + ) + ) + @test length(z) == 1 # Exactly one vertex end end g5 = path_graph(5) for parallel in [:threads, :distributed] for g in testgraphs(g5) - z = @inferred(Parallel.independent_set(g, 4, MaximalIndependentSet(); parallel=parallel, rng=rng)) + z = @inferred( + Parallel.independent_set( + g, 4, MaximalIndependentSet(); parallel=parallel, rng=rng + ) + ) sort!(z) @test (z == [2, 4] || z == [2, 5] || z == [1, 3, 5] || z == [1, 4]) end @@ -55,9 +78,13 @@ add_edge!(g5, 3, 3) for parallel in [:threads, :distributed] for g in testgraphs(g5) - z = @inferred(Parallel.independent_set(g, 4, MaximalIndependentSet(); parallel=parallel, rng=rng)) + z = @inferred( + Parallel.independent_set( + g, 4, MaximalIndependentSet(); parallel=parallel, rng=rng + ) + ) sort!(z) - @test (z == [4,] || z == [5,] || z == [1, 5] || z == [1, 4]) + @test (z == [4] || z == [5] || z == [1, 5] || z == [1, 4]) end end end diff --git a/test/parallel/runtests.jl b/test/parallel/runtests.jl index 925c4aaaa..76805ac5b 100644 --- a/test/parallel/runtests.jl +++ b/test/parallel/runtests.jl @@ -19,7 +19,7 @@ tests = [ "dominatingset/minimal_dom_set", "independentset/maximal_ind_set", "vertexcover/random_vertex_cover", - "utils" + "utils", ] @testset "Graphs.Parallel" begin diff --git a/test/parallel/shortestpaths/bellman-ford.jl b/test/parallel/shortestpaths/bellman-ford.jl index 2a61f6e4d..74ca9264b 100644 --- a/test/parallel/shortestpaths/bellman-ford.jl +++ b/test/parallel/shortestpaths/bellman-ford.jl @@ -11,7 +11,6 @@ @test @inferred(enumerate_paths(z))[4] == enumerate_paths(z, 4) == [2, 3, 4] @test @inferred(!Parallel.has_negative_edge_cycle(g, d1)) - y = @inferred(Parallel.bellman_ford_shortest_paths(g, [2], d1)) z = @inferred(Parallel.bellman_ford_shortest_paths(g, [2], d2)) @test y.dists == z.dists == [Inf, 0, 6, 17, 33] @@ -25,11 +24,15 @@ gx = complete_graph(3) for g in testgraphs(gx) d = [1 -3 1; -3 1 1; 1 1 1] - @test_throws Graphs.NegativeCycleError Parallel.bellman_ford_shortest_paths(g, [1], d) + @test_throws Graphs.NegativeCycleError Parallel.bellman_ford_shortest_paths( + g, [1], d + ) @test Parallel.has_negative_edge_cycle(g, d) d = [1 -1 1; -1 1 1; 1 1 1] - @test_throws Graphs.NegativeCycleError Parallel.bellman_ford_shortest_paths(g, [1], d) + @test_throws Graphs.NegativeCycleError Parallel.bellman_ford_shortest_paths( + g, [1], d + ) @test Parallel.has_negative_edge_cycle(g, d) end @@ -37,7 +40,9 @@ gx = complete_graph(4) d = [1 -1 1 1; 1 1 1 -1; 1 1 1 1; 1 1 1 1] for g in testgraphs(gx) - @test_throws Graphs.NegativeCycleError Parallel.bellman_ford_shortest_paths(g, [1], d) + @test_throws Graphs.NegativeCycleError Parallel.bellman_ford_shortest_paths( + g, [1], d + ) @test Parallel.has_negative_edge_cycle(g, d) end end diff --git a/test/parallel/shortestpaths/dijkstra.jl b/test/parallel/shortestpaths/dijkstra.jl index ccac98b97..34f948232 100644 --- a/test/parallel/shortestpaths/dijkstra.jl +++ b/test/parallel/shortestpaths/dijkstra.jl @@ -2,17 +2,17 @@ g4 = path_digraph(5) d1 = float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) d2 = sparse(float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) - #Testing multisource On undirected Graph + # Testing multisource On undirected Graph g3 = path_graph(5) d = [0 1 2 3 4; 1 0 1 0 1; 2 1 0 11 12; 3 0 11 0 5; 4 1 19 5 0] for g in testgraphs(g3) - z = floyd_warshall_shortest_paths(g, d) + z = floyd_warshall_shortest_paths(g, d) zp = @inferred(Parallel.dijkstra_shortest_paths(g, collect(1:5), d)) @test all(isapprox(z.dists, zp.dists)) for i in 1:5 - state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true); + state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) for j in 1:5 if zp.parents[i, j] != 0 @test zp.parents[i, j] in state.predecessors[j] @@ -20,12 +20,12 @@ end end - z = floyd_warshall_shortest_paths(g) + z = floyd_warshall_shortest_paths(g) zp = @inferred(Parallel.dijkstra_shortest_paths(g)) @test all(isapprox(z.dists, zp.dists)) for i in 1:5 - state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true); + state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) for j in 1:5 if zp.parents[i, j] != 0 @test zp.parents[i, j] in state.predecessors[j] @@ -33,12 +33,12 @@ end end - z = floyd_warshall_shortest_paths(g) + z = floyd_warshall_shortest_paths(g) zp = @inferred(Parallel.dijkstra_shortest_paths(g, [1, 2])) @test all(isapprox(z.dists[1:2, :], zp.dists)) for i in 1:2 - state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true); + state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) for j in 1:5 if zp.parents[i, j] != 0 @test zp.parents[i, j] in state.predecessors[j] @@ -47,18 +47,17 @@ end end - - #Testing multisource On directed Graph + # Testing multisource On directed Graph g3 = path_digraph(5) d = float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) for g in testdigraphs(g3) - z = floyd_warshall_shortest_paths(g, d) + z = floyd_warshall_shortest_paths(g, d) zp = @inferred(Parallel.dijkstra_shortest_paths(g, collect(1:5), d)) @test all(isapprox(z.dists, zp.dists)) for i in 1:5 - state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true); + state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) for j in 1:5 if z.parents[i, j] != 0 @test zp.parents[i, j] in state.predecessors[j] @@ -66,12 +65,12 @@ end end - z = floyd_warshall_shortest_paths(g) + z = floyd_warshall_shortest_paths(g) zp = @inferred(Parallel.dijkstra_shortest_paths(g)) @test all(isapprox(z.dists, zp.dists)) for i in 1:5 - state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true); + state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) for j in 1:5 if zp.parents[i, j] != 0 @test zp.parents[i, j] in state.predecessors[j] @@ -79,12 +78,12 @@ end end - z = floyd_warshall_shortest_paths(g) + z = floyd_warshall_shortest_paths(g) zp = @inferred(Parallel.dijkstra_shortest_paths(g, [1, 2])) @test all(isapprox(z.dists[1:2, :], zp.dists)) for i in 1:2 - state = Graphs.dijkstra_shortest_paths(g, i;allpaths=true); + state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) for j in 1:5 if zp.parents[i, j] != 0 @test zp.parents[i, j] in state.predecessors[j] diff --git a/test/parallel/shortestpaths/floyd-warshall.jl b/test/parallel/shortestpaths/floyd-warshall.jl index 688d3faf8..64788def6 100644 --- a/test/parallel/shortestpaths/floyd-warshall.jl +++ b/test/parallel/shortestpaths/floyd-warshall.jl @@ -7,7 +7,10 @@ @test z.parents[3, :][:] == [2, 3, 0, 3, 4] @test @inferred(enumerate_paths(z))[2][2] == [] - @test @inferred(enumerate_paths(z))[2][4] == enumerate_paths(z, 2)[4] == enumerate_paths(z, 2, 4) == [2, 3, 4] + @test @inferred(enumerate_paths(z))[2][4] == + enumerate_paths(z, 2)[4] == + enumerate_paths(z, 2, 4) == + [2, 3, 4] end g4 = path_digraph(4) d = ones(4, 4) @@ -16,12 +19,12 @@ @test length(enumerate_paths(z, 4, 3)) == 0 @test length(enumerate_paths(z, 4, 1)) == 0 @test length(enumerate_paths(z, 2, 3)) == 2 - end + end g5 = DiGraph([1 1 1 0 1; 0 1 0 1 1; 0 1 1 0 0; 1 0 1 1 0; 0 0 0 1 1]) d = [0 3 8 0 -4; 0 0 0 1 7; 0 4 0 0 0; 2 0 -5 0 0; 0 0 0 6 0] for g in testdigraphs(g5) z = @inferred(Parallel.floyd_warshall_shortest_paths(g, d)) @test z.dists == [0 1 -3 2 -4; 3 0 -4 1 -1; 7 4 0 5 3; 2 -1 -5 0 -2; 8 5 1 6 0] - end + end end diff --git a/test/parallel/shortestpaths/johnson.jl b/test/parallel/shortestpaths/johnson.jl index 05a870ac2..2619da629 100644 --- a/test/parallel/shortestpaths/johnson.jl +++ b/test/parallel/shortestpaths/johnson.jl @@ -6,7 +6,10 @@ @test z.dists[3, :][:] == [7, 6, 0, 11, 27] @test z.parents[3, :][:] == [2, 3, 0, 3, 4] @test @inferred(enumerate_paths(z))[2][2] == [] - @test @inferred(enumerate_paths(z))[2][4] == enumerate_paths(z, 2)[4] == enumerate_paths(z, 2, 4) == [2, 3, 4] + @test @inferred(enumerate_paths(z))[2][4] == + enumerate_paths(z, 2)[4] == + enumerate_paths(z, 2, 4) == + [2, 3, 4] end g4 = path_digraph(4) @@ -15,12 +18,12 @@ @test length(enumerate_paths(z, 4, 3)) == 0 @test length(enumerate_paths(z, 4, 1)) == 0 @test length(enumerate_paths(z, 2, 3)) == 2 - end + end g5 = DiGraph([1 1 1 0 1; 0 1 0 1 1; 0 1 1 0 0; 1 0 1 1 0; 0 0 0 1 1]) d = [0 3 8 0 -4; 0 0 0 1 7; 0 4 0 0 0; 2 0 -5 0 0; 0 0 0 6 0] for g in testdigraphs(g5) z = @inferred(Parallel.johnson_shortest_paths(g, d)) @test z.dists == [0 1 -3 2 -4; 3 0 -4 1 -1; 7 4 0 5 3; 2 -1 -5 0 -2; 8 5 1 6 0] - end + end end diff --git a/test/parallel/traversals/bfs.jl b/test/parallel/traversals/bfs.jl index 1fac2be26..6507cb1ed 100644 --- a/test/parallel/traversals/bfs.jl +++ b/test/parallel/traversals/bfs.jl @@ -4,7 +4,7 @@ @test isempty(next) == true push!(next, 1) @test next[1][] == 1 - @threads for i = 2:5 + @threads for i in 2:5 push!(next, i) end @test Set([i[] for i in next[1:5]]) == Set([1, 2, 3, 4, 5]) @@ -13,21 +13,24 @@ end g5 = SimpleDiGraph(4) - add_edge!(g5, 1, 2); add_edge!(g5, 2, 3); add_edge!(g5, 1, 3); add_edge!(g5, 3, 4) + add_edge!(g5, 1, 2) + add_edge!(g5, 2, 3) + add_edge!(g5, 1, 3) + add_edge!(g5, 3, 4) g6 = smallgraph(:house) for g in testdigraphs(g5) - T = eltype(g) - z = @inferred(Parallel.bfs_tree(g, T(1))) - next = Parallel.ThreadQueue(T, nv(g)) # Initialize threadqueue - parents = [Atomic{T}(0) for i = 1:nv(g)] # Create parents array - Parallel.bfs_tree!(next, g, T(1), parents) - t = [i[] for i in parents] - @test t == [T(1), T(1), T(1), T(3)] - @test nv(z) == T(4) && ne(z) == T(3) && !has_edge(z, 2, 3) + T = eltype(g) + z = @inferred(Parallel.bfs_tree(g, T(1))) + next = Parallel.ThreadQueue(T, nv(g)) # Initialize threadqueue + parents = [Atomic{T}(0) for i in 1:nv(g)] # Create parents array + Parallel.bfs_tree!(next, g, T(1), parents) + t = [i[] for i in parents] + @test t == [T(1), T(1), T(1), T(3)] + @test nv(z) == T(4) && ne(z) == T(3) && !has_edge(z, 2, 3) end - function istree(parents::Vector{Atomic{T}}, maxdepth, n::T) where T<:Integer + function istree(parents::Vector{Atomic{T}}, maxdepth, n::T) where {T<:Integer} flag = true for i in one(T):n s = i @@ -47,7 +50,7 @@ n = nv(g) T = eltype(g) next = Parallel.ThreadQueue(eltype(g), nv(g)) # Initialize threadqueue - parents = [Atomic{T}(0) for i = 1:nv(g)] # Create parents array + parents = [Atomic{T}(0) for i in 1:nv(g)] # Create parents array @test length(next.data) == n @inferred(Parallel.bfs_tree!(next, g, T(1), parents)) @test istree(parents, n, n) @@ -57,5 +60,4 @@ @test typeof(t) <: AbstractGraph @test ne(t) < nv(t) end - end diff --git a/test/parallel/traversals/greedy_color.jl b/test/parallel/traversals/greedy_color.jl index a491b6624..65a43a514 100644 --- a/test/parallel/traversals/greedy_color.jl +++ b/test/parallel/traversals/greedy_color.jl @@ -6,7 +6,7 @@ @test C.num_colors == 2 end end - + g4 = path_graph(20) g5 = complete_graph(20) @@ -14,8 +14,8 @@ for g in testgraphs(graph) for op_sort in (true, false) C = @inferred(Parallel.greedy_color(g, reps=5, sort_degree=op_sort)) - - @test C.num_colors <= maximum(degree(g))+1 + + @test C.num_colors <= maximum(degree(g)) + 1 correct = true for e in edges(g) C.colors[src(e)] == C.colors[dst(e)] && (correct = false) @@ -25,4 +25,3 @@ end end end - diff --git a/test/parallel/utils.jl b/test/parallel/utils.jl index 8d92d9d27..a72ff9bf5 100644 --- a/test/parallel/utils.jl +++ b/test/parallel/utils.jl @@ -1,6 +1,5 @@ @testset "Parallel.Generate Reduce" begin - - function make_vec(g::AbstractGraph{T}) where T<:Integer + function make_vec(g::AbstractGraph{T}) where {T<:Integer} return Vector{T}(undef, nv(g)) end @@ -12,8 +11,9 @@ for parallel in [:distributed, :threads] for g in testgraphs(g1) - - s = @inferred(Graphs.Parallel.generate_reduce(g, make_vec, comp_vec, 5; parallel=parallel)) + s = @inferred( + Graphs.Parallel.generate_reduce(g, make_vec, comp_vec, 5; parallel=parallel) + ) @test length(s) == 5 end end diff --git a/test/parallel/vertexcover/random_vertex_cover.jl b/test/parallel/vertexcover/random_vertex_cover.jl index 1b88c8db0..a42faa504 100644 --- a/test/parallel/vertexcover/random_vertex_cover.jl +++ b/test/parallel/vertexcover/random_vertex_cover.jl @@ -4,7 +4,9 @@ g0 = SimpleGraph(0) for parallel in [:threads, :distributed] for g in testgraphs(g0) - c = @inferred(Parallel.vertex_cover(g, 4, RandomVertexCover(); parallel=parallel, rng=rng)) + c = @inferred( + Parallel.vertex_cover(g, 4, RandomVertexCover(); parallel=parallel, rng=rng) + ) @test isempty(c) end end @@ -12,7 +14,9 @@ g1 = SimpleGraph(1) for parallel in [:threads, :distributed] for g in testgraphs(g1) - c = @inferred(Parallel.vertex_cover(g, 4, RandomVertexCover(); parallel=parallel, rng=rng)) + c = @inferred( + Parallel.vertex_cover(g, 4, RandomVertexCover(); parallel=parallel, rng=rng) + ) @test isempty(c) end end @@ -20,31 +24,39 @@ add_edge!(g1, 1, 1) for parallel in [:threads, :distributed] for g in testgraphs(g1) - c = @inferred(Parallel.vertex_cover(g, 4, RandomVertexCover(); parallel=parallel, rng=rng)) - @test c == [1,] + c = @inferred( + Parallel.vertex_cover(g, 4, RandomVertexCover(); parallel=parallel, rng=rng) + ) + @test c == [1] end end g3 = star_graph(5) for parallel in [:threads, :distributed] for g in testgraphs(g3) - c = @inferred(Parallel.vertex_cover(g, 4, RandomVertexCover(); parallel=parallel, rng=rng)) - @test (length(c)== 2 && (c[1] == 1 || c[2] == 1)) + c = @inferred( + Parallel.vertex_cover(g, 4, RandomVertexCover(); parallel=parallel, rng=rng) + ) + @test (length(c) == 2 && (c[1] == 1 || c[2] == 1)) end end - + g4 = complete_graph(5) for parallel in [:threads, :distributed] for g in testgraphs(g4) - c = @inferred(Parallel.vertex_cover(g, 4, RandomVertexCover(); parallel=parallel, rng=rng)) - @test length(c)== 4 #All except one vertex + c = @inferred( + Parallel.vertex_cover(g, 4, RandomVertexCover(); parallel=parallel, rng=rng) + ) + @test length(c) == 4 #All except one vertex end end g5 = path_graph(5) for parallel in [:threads, :distributed] for g in testgraphs(g5) - c = @inferred(Parallel.vertex_cover(g, 4, RandomVertexCover(); parallel=parallel, rng=rng)) + c = @inferred( + Parallel.vertex_cover(g, 4, RandomVertexCover(); parallel=parallel, rng=rng) + ) sort!(c) @test (c == [1, 2, 3, 4] || c == [1, 2, 4, 5] || c == [2, 3, 4, 5]) end @@ -54,9 +66,16 @@ add_edge!(g5, 3, 3) for parallel in [:threads, :distributed] for g in testgraphs(g5) - c = @inferred(Parallel.vertex_cover(g, 4, RandomVertexCover(); parallel=parallel, rng=rng)) + c = @inferred( + Parallel.vertex_cover(g, 4, RandomVertexCover(); parallel=parallel, rng=rng) + ) sort!(c) - @test (c == [1, 2, 3, 4] || c == [1, 2, 3, 4, 5] || c == [2, 3, 4] || c == [2, 3, 4, 5]) + @test ( + c == [1, 2, 3, 4] || + c == [1, 2, 3, 4, 5] || + c == [2, 3, 4] || + c == [2, 3, 4, 5] + ) end end end diff --git a/test/persistence/persistence.jl b/test/persistence/persistence.jl index a6058d858..1729bd2d3 100644 --- a/test/persistence/persistence.jl +++ b/test/persistence/persistence.jl @@ -15,10 +15,16 @@ # test :lg @testset "LGFormat save single graph" begin @test savegraph(f, p1) == 1 - @test_deprecated r"Saving compressed graphs is no longer supported" savegraph(f, p1; compress=true) + @test_deprecated r"Saving compressed graphs is no longer supported" savegraph( + f, p1; compress=true + ) @test savegraph(f, p1) == 1 - @test_deprecated r"Saving compressed graphs is no longer supported" savegraph(f, p1, LGFormat(); compress=true) - @test_logs (:info,r"Note: the `compress` keyword is no longer supported in Graphs") savegraph(f, p1; compress=false) + @test_deprecated r"Saving compressed graphs is no longer supported" savegraph( + f, p1, LGFormat(); compress=true + ) + @test_logs (:info, r"Note: the `compress` keyword is no longer supported in Graphs") savegraph( + f, p1; compress=false + ) @test savegraph(f, p1, LGFormat()) == 1 @test savegraph(f, p2) == 1 end @@ -35,9 +41,13 @@ (f, fio) = mktemp() @test length(sprint(savegraph, p1, LGFormat())) == 421 @test length(sprint(savegraph, p2, LGFormat())) == 70 - gs = loadgraph(joinpath(testdir, "testdata", "tutte-pathdigraph.jgz"), "pathdigraph") + gs = loadgraph( + joinpath(testdir, "testdata", "tutte-pathdigraph.jgz"), "pathdigraph" + ) @test gs == p2 - @test_throws ArgumentError loadgraph(joinpath(testdir, "testdata", "tutte-pathdigraph.jgz"), "badname") + @test_throws ArgumentError loadgraph( + joinpath(testdir, "testdata", "tutte-pathdigraph.jgz"), "badname" + ) end @testset "LGFormat save multiple graphs" begin diff --git a/test/runtests.jl b/test/runtests.jl index c851b52f7..5e24af649 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,9 @@ +using Aqua +using Documenter using Graphs using Graphs.SimpleGraphs using Graphs.Experimental +using JuliaFormatter using Test using SparseArrays using LinearAlgebra @@ -13,13 +16,37 @@ using StableRNGs const testdir = dirname(@__FILE__) -testgraphs(g) = is_directed(g) ? [g, DiGraph{UInt8}(g), DiGraph{Int16}(g)] : [g, Graph{UInt8}(g), Graph{Int16}(g)] +@testset verbose = true "Code quality (Aqua.jl)" begin + Aqua.test_all(Graphs; ambiguities=false) +end + +@testset verbose = true "Code formatting (JuliaFormatter.jl)" begin + @test format(Graphs; verbose=false, overwrite=false, ignore=["vf2.jl"]) # TODO: remove ignore kwarg once the file is formatted correctly +end + +@testset verbose = true "Doctests (Documenter.jl)" begin + # doctest(Graphs) # TODO: uncomment it when the errors it throws are fixed +end + +function testgraphs(g) + return if is_directed(g) + [g, DiGraph{UInt8}(g), DiGraph{Int16}(g)] + else + [g, Graph{UInt8}(g), Graph{Int16}(g)] + end +end testgraphs(gs...) = vcat((testgraphs(g) for g in gs)...) testdigraphs = testgraphs # some operations will create a large graph from two smaller graphs. We # might error out on very small eltypes. -testlargegraphs(g) = is_directed(g) ? [g, DiGraph{UInt16}(g), DiGraph{Int32}(g)] : [g, Graph{UInt16}(g), Graph{Int32}(g)] +function testlargegraphs(g) + return if is_directed(g) + [g, DiGraph{UInt16}(g), DiGraph{Int32}(g)] + else + [g, Graph{UInt16}(g), Graph{Int32}(g)] + end +end testlargegraphs(gs...) = vcat((testlargegraphs(g) for g in gs)...) tests = [ @@ -89,7 +116,7 @@ tests = [ "independentset/maximal_ind_set", "vertexcover/degree_vertex_cover", "vertexcover/random_vertex_cover", - "experimental/experimental" + "experimental/experimental", ] @testset verbose = true "Graphs" begin diff --git a/test/shortestpaths/astar.jl b/test/shortestpaths/astar.jl index 510f45f7e..591635e44 100644 --- a/test/shortestpaths/astar.jl +++ b/test/shortestpaths/astar.jl @@ -5,10 +5,10 @@ d1 = float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) d2 = sparse(float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) for g in testgraphs(g3), dg in testdigraphs(g4) - @test @inferred(a_star(g, 1, 4, d1)) == - @inferred(a_star(dg, 1, 4, d1)) == - @inferred(a_star(g, 1, 4, d2)) - @test isempty(@inferred(a_star(dg, 4, 1))) + @test @inferred(a_star(g, 1, 4, d1)) == + @inferred(a_star(dg, 1, 4, d1)) == + @inferred(a_star(g, 1, 4, d2)) + @test isempty(@inferred(a_star(dg, 4, 1))) end # test for #1258 diff --git a/test/shortestpaths/bellman-ford.jl b/test/shortestpaths/bellman-ford.jl index 6acd48c2a..2d4384752 100644 --- a/test/shortestpaths/bellman-ford.jl +++ b/test/shortestpaths/bellman-ford.jl @@ -1,5 +1,4 @@ @testset "Bellman Ford" begin - g4 = path_digraph(5) d1 = float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) @@ -13,7 +12,6 @@ @test @inferred(!has_negative_edge_cycle(g)) @test @inferred(!has_negative_edge_cycle(g, d1)) - y = @inferred(bellman_ford_shortest_paths(g, 2, d1)) z = @inferred(bellman_ford_shortest_paths(g, 2, d2)) @test y.dists == z.dists == [Inf, 0, 6, 17, 33] diff --git a/test/shortestpaths/desopo-pape.jl b/test/shortestpaths/desopo-pape.jl index adfa12f6b..037e7b6ff 100644 --- a/test/shortestpaths/desopo-pape.jl +++ b/test/shortestpaths/desopo-pape.jl @@ -1,5 +1,4 @@ @testset "D'Esopo-Pape" begin - g4 = path_digraph(5) d1 = float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) d2 = sparse(float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) @@ -9,7 +8,7 @@ @test y.parents == z.parents == [0, 0, 2, 3, 4] @test y.dists == z.dists == [Inf, 0, 6, 17, 33] end - + gx = path_graph(5) add_edge!(gx, 2, 4) d = ones(Int, 5, 5) @@ -20,7 +19,7 @@ @test z.parents == [0, 1, 4, 2, 4] end - m = [0 2 2 0 0; 2 0 0 0 3; 2 0 0 1 2;0 0 1 0 1;0 3 2 1 0] + m = [0 2 2 0 0; 2 0 0 0 3; 2 0 0 1 2; 0 0 1 0 1; 0 3 2 1 0] G = SimpleGraph(5) add_edge!(G, 1, 2) add_edge!(G, 1, 3) @@ -45,7 +44,7 @@ add_edge!(G, 4, 5) m = [0 10 2 0 15; 10 9 0 1 0; 2 0 1 0 0; 0 1 0 0 2; 15 0 0 2 0] @testset "self loops: $g" for g in testgraphs(G) - z = @inferred(desopo_pape_shortest_paths(g, 1 , m)) + z = @inferred(desopo_pape_shortest_paths(g, 1, m)) y = @inferred(dijkstra_shortest_paths(g, 1, m)) @test isapprox(z.dists, y.dists) end @@ -70,10 +69,10 @@ end @testset "random simple graphs" begin - for seed = 1:5 + for seed in 1:5 rng = StableRNG(seed) - nvg = Int(ceil(250*rand(rng))) - neg = Int(floor((nvg*(nvg-1)/2)*rand(rng))) + nvg = Int(ceil(250 * rand(rng))) + neg = Int(floor((nvg * (nvg - 1) / 2) * rand(rng))) g = SimpleGraph(nvg, neg; rng=rng) z = desopo_pape_shortest_paths(g, 1) y = dijkstra_shortest_paths(g, 1) @@ -82,10 +81,10 @@ end @testset "random simple digraphs" begin - for seed = 1:5 + for seed in 1:5 rng = StableRNG(seed) - nvg = Int(ceil(250*rand(rng))) - neg = Int(floor((nvg*(nvg-1)/2)*rand(rng))) + nvg = Int(ceil(250 * rand(rng))) + neg = Int(floor((nvg * (nvg - 1) / 2) * rand(rng))) g = SimpleDiGraph(nvg, neg; rng=rng) z = desopo_pape_shortest_paths(g, 1) y = dijkstra_shortest_paths(g, 1) @@ -136,19 +135,35 @@ end @testset "smallgraphs: $s" for s in [ - :bull, :chvatal, :cubical, :desargues, - :diamond, :dodecahedral, :frucht, :heawood, - :house, :housex, :icosahedral, :krackhardtkite, :moebiuskantor, - :octahedral, :pappus, :petersen, :sedgewickmaze, :tutte, - :tetrahedral, :truncatedcube, :truncatedtetrahedron, - :truncatedtetrahedron_dir - ] + :bull, + :chvatal, + :cubical, + :desargues, + :diamond, + :dodecahedral, + :frucht, + :heawood, + :house, + :housex, + :icosahedral, + :krackhardtkite, + :moebiuskantor, + :octahedral, + :pappus, + :petersen, + :sedgewickmaze, + :tutte, + :tetrahedral, + :truncatedcube, + :truncatedtetrahedron, + :truncatedtetrahedron_dir, + ] G = smallgraph(s) z = desopo_pape_shortest_paths(G, 1) y = dijkstra_shortest_paths(G, 1) @test isapprox(z.dists, y.dists) end - + @testset "errors" begin g = Graph() @test_throws DomainError desopo_pape_shortest_paths(g, 1) diff --git a/test/shortestpaths/dijkstra.jl b/test/shortestpaths/dijkstra.jl index 05ddc32cf..2dc878c6e 100644 --- a/test/shortestpaths/dijkstra.jl +++ b/test/shortestpaths/dijkstra.jl @@ -16,11 +16,12 @@ @test @inferred(enumerate_paths(z)) == enumerate_paths(y) @test @inferred(enumerate_paths(z))[4] == - enumerate_paths(z, 4) == - # test that we can pass a range into enumerate_paths - previously this caused - # infinite recursion - see #1552 - enumerate_paths(z, 3:4)[2] == - enumerate_paths(y, 4) == [2, 3, 4] + enumerate_paths(z, 4) == + # test that we can pass a range into enumerate_paths - previously this caused + # infinite recursion - see #1552 + enumerate_paths(z, 3:4)[2] == + enumerate_paths(y, 4) == + [2, 3, 4] end gx = path_graph(5) @@ -35,7 +36,13 @@ # small function to reconstruct the shortest path; I copied it from somewhere, can't find the original source to give the credits # @Beatzekatze on github - spath(target, dijkstraStruct, source) = target == source ? target : [spath(dijkstraStruct.parents[target], dijkstraStruct, source) target] + function spath(target, dijkstraStruct, source) + return if target == source + target + else + [spath(dijkstraStruct.parents[target], dijkstraStruct, source) target] + end + end spaths(ds, targets, source) = [spath(i, ds, source) for i in targets] G = Graphs.Graph() @@ -45,30 +52,28 @@ add_edge!(G, 1, 4) add_edge!(G, 3, 4) add_edge!(G, 2, 2) - w = [0. 3. 0. 1.; - 3. 0. 2. 0.; - 0. 2. 0. 3.; - 1. 0. 3. 0.] + w = [ + 0.0 3.0 0.0 1.0 + 3.0 0.0 2.0 0.0 + 0.0 2.0 0.0 3.0 + 1.0 0.0 3.0 0.0 + ] for g in testgraphs(G) ds = @inferred(dijkstra_shortest_paths(g, 2, w)) - # this loop reconstructs the shortest path for vertices 1, 3 and 4 - @test spaths(ds, [1, 3, 4], 2) == Array[[2 1], - [2 3], - [2 1 4]] + # this loop reconstructs the shortest path for vertices 1, 3 and 4 + @test spaths(ds, [1, 3, 4], 2) == Array[[2 1], [2 3], [2 1 4]] - # here a selflink at source is introduced; it should not change the shortest paths + # here a selflink at source is introduced; it should not change the shortest paths w[2, 2] = 10.0 ds = @inferred(dijkstra_shortest_paths(g, 2, w)) shortest_paths = [] - # this loop reconstructs the shortest path for vertices 1, 3 and 4 - @test spaths(ds, [1, 3, 4], 2) == Array[[2 1], - [2 3], - [2 1 4]] + # this loop reconstructs the shortest path for vertices 1, 3 and 4 + @test spaths(ds, [1, 3, 4], 2) == Array[[2 1], [2 3], [2 1 4]] end #615 - m = [0 2 2 0 0; 2 0 0 0 3; 2 0 0 1 2;0 0 1 0 1;0 3 2 1 0] + m = [0 2 2 0 0; 2 0 0 0 3; 2 0 0 1 2; 0 0 1 0 1; 0 3 2 1 0] G = SimpleGraph(5) add_edge!(G, 1, 2) add_edge!(G, 1, 3) @@ -78,13 +83,13 @@ add_edge!(G, 4, 5) for g in testgraphs(G) ds = @inferred(dijkstra_shortest_paths(g, 1, m; allpaths=true)) - @test ds.pathcounts == [1.0, 1.0, 1.0, 1.0, 2.0] + @test ds.pathcounts == [1.0, 1.0, 1.0, 1.0, 2.0] @test ds.predecessors == [[], [1], [1], [3], [3, 4]] @test ds.predecessors == [[], [1], [1], [3], [3, 4]] dm = @inferred(dijkstra_shortest_paths(g, 1; allpaths=true, trackvertices=true)) - @test dm.pathcounts == [1.0, 1.0, 1.0, 1.0, 2.0] - @test dm.predecessors == [[], [1], [1], [3], [2, 3]] + @test dm.pathcounts == [1.0, 1.0, 1.0, 1.0, 2.0] + @test dm.predecessors == [[], [1], [1], [3], [2, 3]] @test dm.closest_vertices == [1, 2, 3, 5, 4] end diff --git a/test/shortestpaths/floyd-warshall.jl b/test/shortestpaths/floyd-warshall.jl index 981430787..0eb0f2992 100644 --- a/test/shortestpaths/floyd-warshall.jl +++ b/test/shortestpaths/floyd-warshall.jl @@ -7,7 +7,10 @@ @test z.parents[3, :][:] == [2, 3, 0, 3, 4] @test @inferred(enumerate_paths(z))[2][2] == [] - @test @inferred(enumerate_paths(z))[2][4] == enumerate_paths(z, 2)[4] == enumerate_paths(z, 2, 4) == [2, 3, 4] + @test @inferred(enumerate_paths(z))[2][4] == + enumerate_paths(z, 2)[4] == + enumerate_paths(z, 2, 4) == + [2, 3, 4] end g4 = path_digraph(4) d = ones(4, 4) @@ -16,14 +19,14 @@ @test length(enumerate_paths(z, 4, 3)) == 0 @test length(enumerate_paths(z, 4, 1)) == 0 @test length(enumerate_paths(z, 2, 3)) == 2 - end + end g5 = DiGraph([1 1 1 0 1; 0 1 0 1 1; 0 1 1 0 0; 1 0 1 1 0; 0 0 0 1 1]) d = [0 3 8 0 -4; 0 0 0 1 7; 0 4 0 0 0; 2 0 -5 0 0; 0 0 0 6 0] for g in testdigraphs(g5) z = @inferred(floyd_warshall_shortest_paths(g, d)) @test z.dists == [0 1 -3 2 -4; 3 0 -4 1 -1; 7 4 0 5 3; 2 -1 -5 0 -2; 8 5 1 6 0] - end + end @testset "enumerate_paths infinite loop bug" begin g = SimpleGraph(2) diff --git a/test/shortestpaths/johnson.jl b/test/shortestpaths/johnson.jl index 0830a6b57..20bb04f2d 100644 --- a/test/shortestpaths/johnson.jl +++ b/test/shortestpaths/johnson.jl @@ -7,7 +7,10 @@ @test z.parents[3, :][:] == [2, 3, 0, 3, 4] @test @inferred(enumerate_paths(z))[2][2] == [] - @test @inferred(enumerate_paths(z))[2][4] == enumerate_paths(z, 2)[4] == enumerate_paths(z, 2, 4) == [2, 3, 4] + @test @inferred(enumerate_paths(z))[2][4] == + enumerate_paths(z, 2)[4] == + enumerate_paths(z, 2, 4) == + [2, 3, 4] end g4 = path_digraph(4) @@ -16,12 +19,12 @@ @test length(enumerate_paths(z, 4, 3)) == 0 @test length(enumerate_paths(z, 4, 1)) == 0 @test length(enumerate_paths(z, 2, 3)) == 2 - end + end g5 = DiGraph([1 1 1 0 1; 0 1 0 1 1; 0 1 1 0 0; 1 0 1 1 0; 0 0 0 1 1]) d = [0 3 8 0 -4; 0 0 0 1 7; 0 4 0 0 0; 2 0 -5 0 0; 0 0 0 6 0] for g in testdigraphs(g5) z = @inferred(johnson_shortest_paths(g, d)) @test z.dists == [0 1 -3 2 -4; 3 0 -4 1 -1; 7 4 0 5 3; 2 -1 -5 0 -2; 8 5 1 6 0] - end + end end diff --git a/test/shortestpaths/spfa.jl b/test/shortestpaths/spfa.jl index 8ea5a35e3..f0265baad 100644 --- a/test/shortestpaths/spfa.jl +++ b/test/shortestpaths/spfa.jl @@ -1,5 +1,4 @@ @testset "Shortest_Path_Faster_Algorithm" begin - @testset "Generic tests for graphs" begin g4 = path_digraph(5) d1 = float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) @@ -20,7 +19,7 @@ end end - m = [0 2 2 0 0; 2 0 0 0 3; 2 0 0 1 2;0 0 1 0 1;0 3 2 1 0] + m = [0 2 2 0 0; 2 0 0 0 3; 2 0 0 1 2; 0 0 1 0 1; 0 3 2 1 0] G = SimpleGraph(5) add_edge!(G, 1, 2) add_edge!(G, 1, 3) @@ -46,7 +45,7 @@ add_edge!(G, 4, 5) m = [0 10 2 0 15; 10 9 0 1 0; 2 0 1 0 0; 0 1 0 0 2; 15 0 0 2 0] for g in testgraphs(G) - z = @inferred(spfa_shortest_paths(g, 1 , m)) + z = @inferred(spfa_shortest_paths(g, 1, m)) y = @inferred(dijkstra_shortest_paths(g, 1, m)) @test isapprox(z, y.dists) end @@ -75,11 +74,11 @@ @testset "Random Graphs" begin @testset "Simple graphs" begin - for seed = 1:5 + for seed in 1:5 rng = StableRNG(seed) - nvg = Int(ceil(250*rand(rng))) - neg = Int(floor((nvg*(nvg-1)/2)*rand(rng))) - g = SimpleGraph(nvg, neg; rng = rng) + nvg = Int(ceil(250 * rand(rng))) + neg = Int(floor((nvg * (nvg - 1) / 2) * rand(rng))) + g = SimpleGraph(nvg, neg; rng=rng) z = spfa_shortest_paths(g, 1) y = dijkstra_shortest_paths(g, 1) @test isapprox(z, y.dists) @@ -87,11 +86,11 @@ end @testset "Simple DiGraphs" begin - for seed = 1:5 + for seed in 1:5 rng = StableRNG(seed) - nvg = Int(ceil(250*rand(rng))) - neg = Int(floor((nvg*(nvg-1)/2)*rand(rng))) - g = SimpleDiGraph(nvg, neg; rng = rng) + nvg = Int(ceil(250 * rand(rng))) + neg = Int(floor((nvg * (nvg - 1) / 2) * rand(rng))) + g = SimpleDiGraph(nvg, neg; rng=rng) z = spfa_shortest_paths(g, 1) y = dijkstra_shortest_paths(g, 1) @test isapprox(z, y.dists) @@ -141,11 +140,30 @@ @test isapprox(z, y.dists) @testset "Small Graphs" begin - for s in [:bull, :chvatal, :cubical, :desargues, - :diamond, :dodecahedral, :frucht, :heawood, - :house, :housex, :icosahedral, :krackhardtkite, :moebiuskantor, - :octahedral, :pappus, :petersen, :sedgewickmaze, :tutte, - :tetrahedral, :truncatedcube, :truncatedtetrahedron, :truncatedtetrahedron_dir] + for s in [ + :bull, + :chvatal, + :cubical, + :desargues, + :diamond, + :dodecahedral, + :frucht, + :heawood, + :house, + :housex, + :icosahedral, + :krackhardtkite, + :moebiuskantor, + :octahedral, + :pappus, + :petersen, + :sedgewickmaze, + :tutte, + :tetrahedral, + :truncatedcube, + :truncatedtetrahedron, + :truncatedtetrahedron_dir, + ] G = smallgraph(s) z = spfa_shortest_paths(G, 1) y = dijkstra_shortest_paths(G, 1) @@ -158,7 +176,9 @@ g4 = path_digraph(5) d1 = float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) - d2 = sparse(float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0])) + d2 = sparse( + float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) + ) for g in testdigraphs(g4) y = @inferred(spfa_shortest_paths(g, 2, d1)) z = @inferred(spfa_shortest_paths(g, 2, d2)) @@ -166,7 +186,6 @@ @test @inferred(!has_negative_edge_cycle_spfa(g)) @test @inferred(!has_negative_edge_cycle_spfa(g, d1)) - y = @inferred(spfa_shortest_paths(g, 2, d1)) z = @inferred(spfa_shortest_paths(g, 2, d2)) @test y == z == [Inf, 0, 6, 17, 33] @@ -176,7 +195,6 @@ end end - @testset "Negative Cycle" begin # Negative Cycle 1 gx = complete_graph(3) @@ -198,5 +216,4 @@ @test has_negative_edge_cycle_spfa(g, d) end end - end diff --git a/test/shortestpaths/yen.jl b/test/shortestpaths/yen.jl index 98fc84ce3..2c608b3ba 100644 --- a/test/shortestpaths/yen.jl +++ b/test/shortestpaths/yen.jl @@ -5,9 +5,9 @@ for g in testdigraphs(g4) x = @inferred(yen_k_shortest_paths(g, 5, 5)) - @test length(x.dists) == length(x.paths) == 1 - @test x.dists[1] == 0 - @test x.paths[1] == [5] + @test length(x.dists) == length(x.paths) == 1 + @test x.dists[1] == 0 + @test x.paths[1] == [5] y = @inferred(yen_k_shortest_paths(g, 2, 5, d1, 1)) z = @inferred(yen_k_shortest_paths(g, 2, 5, d2, 1)) @@ -20,7 +20,7 @@ # Remove edge to test empty paths rem_edge!(g, 1, 2) x = @inferred(yen_k_shortest_paths(g, 1, 5)) - @test length(x.dists) == length(x.paths) == 0 + @test length(x.dists) == length(x.paths) == 0 end gx = path_graph(5) @@ -35,7 +35,6 @@ @test z.paths[1] == [1, 2, 4, 5] end - G = Graphs.Graph() add_vertices!(G, 4) add_edge!(G, 2, 1) @@ -43,10 +42,12 @@ add_edge!(G, 1, 4) add_edge!(G, 3, 4) add_edge!(G, 2, 2) - w = [0. 3. 0. 1.; - 3. 0. 2. 0.; - 0. 2. 0. 3.; - 1. 0. 3. 0.] + w = [ + 0.0 3.0 0.0 1.0 + 3.0 0.0 2.0 0.0 + 0.0 2.0 0.0 3.0 + 1.0 0.0 3.0 0.0 + ] for g in testgraphs(G) ds = @inferred(yen_k_shortest_paths(g, 2, 4, w)) @@ -69,8 +70,8 @@ # Test with new short link add_edge!(g, 2, 4) - w[2, 4] = 1. - w[4, 2] = 1. + w[2, 4] = 1.0 + w[4, 2] = 1.0 ds = @inferred(yen_k_shortest_paths(g, 2, 4, w, 2)) @test ds.paths == [[2, 4], [2, 1, 4]] ds = @inferred(yen_k_shortest_paths(g, 2, 4, w, 3)) @@ -89,16 +90,18 @@ add_edge!(G, 4, 5) add_edge!(G, 4, 6) add_edge!(G, 5, 6) - w = [0. 3. 2. 0. 0. 0.; - 0. 0. 0. 4. 0. 0.; - 0. 1. 0. 2. 3. 0.; - 0. 0. 0. 0. 2. 1.; - 0. 0. 0. 0. 0. 2.; - 0. 0. 0. 0. 0. 0.;] + w = [ + 0.0 3.0 2.0 0.0 0.0 0.0 + 0.0 0.0 0.0 4.0 0.0 0.0 + 0.0 1.0 0.0 2.0 3.0 0.0 + 0.0 0.0 0.0 0.0 2.0 1.0 + 0.0 0.0 0.0 0.0 0.0 2.0 + 0.0 0.0 0.0 0.0 0.0 0.0 + ] for g in testdigraphs(G) ds = @inferred(yen_k_shortest_paths(g, 1, 6, w, 3)) - @test ds.dists == [5., 7., 8.] + @test ds.dists == [5.0, 7.0, 8.0] @test ds.paths[1] == [1, 3, 4, 6] @test ds.paths[2] == [1, 3, 5, 6] end diff --git a/test/simplegraphs/generators/binomial.jl b/test/simplegraphs/generators/binomial.jl index 8b6e0738f..9ce52b87c 100644 --- a/test/simplegraphs/generators/binomial.jl +++ b/test/simplegraphs/generators/binomial.jl @@ -5,19 +5,21 @@ using Distributions using Graphs using StatsBase using Test -import Random +using Random: Random import Base: - import Graphs: randbn import StatsBase: SummaryStats function -(s::SummaryStats, t::SummaryStats) - return SummaryStats(s.mean - t.mean, + return SummaryStats( + s.mean - t.mean, s.min - t.min, s.q25 - t.q25, s.median - t.median, s.q75 - t.q75, - s.max - t.max) + s.max - t.max, + ) end function binomial_test(n, p, s) drand = rand(Binomial(n, p), s) @@ -29,18 +31,18 @@ function binomial_test(n, p, s) lσ = std(lrand) summarydiff = ds - ls - @test abs(summarydiff.mean) / ds.mean < .10 - @test abs(summarydiff.median) / ds.median < .10 - @test abs(summarydiff.q25) / ds.q25 < .10 - @test abs(summarydiff.q75) / ds.q75 < .10 + @test abs(summarydiff.mean) / ds.mean < 0.10 + @test abs(summarydiff.median) / ds.median < 0.10 + @test abs(summarydiff.q25) / ds.q25 < 0.10 + @test abs(summarydiff.q75) / ds.q75 < 0.10 - @test abs(dσ - lσ) / dσ < .10 + @test abs(dσ - lσ) / dσ < 0.10 end n = 10000 p = 0.3 s = 100000 -@testset "($n, $p, $s)" for (n, p, s) in [(100, 0.3, 1000), (1000, 0.8, 1000), (10000, 0.25, 1000)] +@testset "($n, $p, $s)" for (n, p, s) in + [(100, 0.3, 1000), (1000, 0.8, 1000), (10000, 0.25, 1000)] binomial_test(n, p, s) end - diff --git a/test/simplegraphs/generators/euclideangraphs.jl b/test/simplegraphs/generators/euclideangraphs.jl index 181fb581e..47207ec34 100644 --- a/test/simplegraphs/generators/euclideangraphs.jl +++ b/test/simplegraphs/generators/euclideangraphs.jl @@ -9,15 +9,15 @@ @test maximum(x -> x[2], weights) <= sqrt(d) @test minimum(x -> x[2], weights) >= 0 @test maximum(points) <= 1 - @test minimum(points) >= 0. + @test minimum(points) >= 0.0 g, weights, points = @inferred(euclidean_graph(N, d, bc=:periodic, rng=rng)) @test maximum(x -> x[2], weights) <= sqrt(d / 2) - @test minimum(x -> x[2], weights) >= 0. + @test minimum(x -> x[2], weights) >= 0.0 @test maximum(points) <= 1 - @test minimum(points) >= 0. + @test minimum(points) >= 0.0 - @test_throws DomainError euclidean_graph(points, L=0.01, bc=:periodic) + @test_throws DomainError euclidean_graph(points, L=0.01, bc=:periodic) @test_throws ArgumentError euclidean_graph(points, bc=:badbc) # In our algorithm we ensure, that the resulting graph has the same @@ -29,7 +29,7 @@ point2 = [0.0, 1.0, 0.0] point3 = [0.0, 0.0, 100.0] matrix = hcat(point1, point2, point3) - g, _ = euclidean_graph(matrix, cutoff=5.0) + g, _ = euclidean_graph(matrix; cutoff=5.0) @test has_edge(g, 1, 2) && ne(g) == 1 @test nv(g) == 3 end diff --git a/test/simplegraphs/generators/randgraphs.jl b/test/simplegraphs/generators/randgraphs.jl index 6ba2a5103..9853aadb6 100644 --- a/test/simplegraphs/generators/randgraphs.jl +++ b/test/simplegraphs/generators/randgraphs.jl @@ -2,8 +2,8 @@ rng = StableRNG(1) @testset "(Int, Int)" begin - r1 = SimpleGraph(10, 20, rng=rng) - r2 = SimpleDiGraph(5, 10, rng=rng) + r1 = SimpleGraph(10, 20; rng=rng) + r2 = SimpleDiGraph(5, 10; rng=rng) @test nv(r1) == 10 @test ne(r1) == 20 @test nv(r2) == 5 @@ -11,174 +11,179 @@ @test eltype(r1) == Int @test eltype(r2) == Int - @test SimpleGraph(10, 20, rng=StableRNG(3)) == SimpleGraph(10, 20, rng=StableRNG(3)) - @test SimpleGraph(10, 40, rng=StableRNG(3)) == SimpleGraph(10, 40, rng=StableRNG(3)) - @test SimpleDiGraph(10, 20, rng=StableRNG(3)) == SimpleDiGraph(10, 20, rng=StableRNG(3)) - @test SimpleDiGraph(10, 80, rng=StableRNG(3)) == SimpleDiGraph(10, 80, rng=StableRNG(3)) - @test SimpleGraph(10, 20, rng=StableRNG(3)) == erdos_renyi(10, 20, rng=rng=StableRNG(3)) - @test ne(Graph(10, 40, rng=StableRNG(3))) == 40 - @test ne(DiGraph(10, 80, rng=StableRNG(3))) == 80 + @test SimpleGraph(10, 20; rng=StableRNG(3)) == SimpleGraph(10, 20; rng=StableRNG(3)) + @test SimpleGraph(10, 40; rng=StableRNG(3)) == SimpleGraph(10, 40; rng=StableRNG(3)) + @test SimpleDiGraph(10, 20; rng=StableRNG(3)) == + SimpleDiGraph(10, 20; rng=StableRNG(3)) + @test SimpleDiGraph(10, 80; rng=StableRNG(3)) == + SimpleDiGraph(10, 80; rng=StableRNG(3)) + @test SimpleGraph(10, 20; rng=StableRNG(3)) == + erdos_renyi(10, 20; rng=rng = StableRNG(3)) + @test ne(Graph(10, 40; rng=StableRNG(3))) == 40 + @test ne(DiGraph(10, 80; rng=StableRNG(3))) == 80 end @testset "(UInt8, Mixed) eltype" begin - @test eltype(Graph(0x5, 0x2, rng=rng)) == eltype(Graph(0x5, 2, rng=rng)) == UInt8 + @test eltype(Graph(0x5, 0x2; rng=rng)) == eltype(Graph(0x5, 2; rng=rng)) == UInt8 end - @testset "(Graph{$T}(Int, Int) eltype"for T in [UInt8, Int8, UInt16, Int16, UInt32, Int32, UInt, Int] - @test eltype(Graph{T}(5, 2, rng=rng)) == T - @test eltype(DiGraph{T}(5, 2, rng=rng)) == T - @test eltype(Graph{T}(5, 8, rng=rng)) == T - @test eltype(DiGraph{T}(5, 8, rng=rng)) == T + @testset "(Graph{$T}(Int, Int) eltype" for T in [ + UInt8, Int8, UInt16, Int16, UInt32, Int32, UInt, Int + ] + @test eltype(Graph{T}(5, 2; rng=rng)) == T + @test eltype(DiGraph{T}(5, 2; rng=rng)) == T + @test eltype(Graph{T}(5, 8; rng=rng)) == T + @test eltype(DiGraph{T}(5, 8; rng=rng)) == T end @testset "Erdös-Renyí" begin - er = erdos_renyi(10, 0.5, rng=rng) + er = erdos_renyi(10, 0.5; rng=rng) @test nv(er) == 10 @test is_directed(er) == false - er = erdos_renyi(10, 0.5, is_directed=true, rng=rng) + er = erdos_renyi(10, 0.5; is_directed=true, rng=rng) @test nv(er) == 10 @test is_directed(er) == true - er = erdos_renyi(10, 0.5, rng=StableRNG(17)) + er = erdos_renyi(10, 0.5; rng=StableRNG(17)) @test nv(er) == 10 @test is_directed(er) == false - @test erdos_renyi(5, 1.0, rng=rng) == complete_graph(5) - @test erdos_renyi(5, 1.0, is_directed=true, rng=rng) == complete_digraph(5) - @test erdos_renyi(5, 2.1, rng=rng) == complete_graph(5) - @test erdos_renyi(5, 2.1, is_directed=true, rng=rng) == complete_digraph(5) + @test erdos_renyi(5, 1.0; rng=rng) == complete_graph(5) + @test erdos_renyi(5, 1.0; is_directed=true, rng=rng) == complete_digraph(5) + @test erdos_renyi(5, 2.1; rng=rng) == complete_graph(5) + @test erdos_renyi(5, 2.1; is_directed=true, rng=rng) == complete_digraph(5) # issue #173 - er = erdos_renyi(4, 6, seed=1) + er = erdos_renyi(4, 6; seed=1) @test nv(er) == 4 @test ne(er) == 6 end @testset "expected degree" begin - cl = expected_degree_graph(zeros(10), rng=StableRNG(17)) + cl = expected_degree_graph(zeros(10); rng=StableRNG(17)) @test nv(cl) == 10 @test ne(cl) == 0 @test is_directed(cl) == false - cl = expected_degree_graph([3, 2, 1, 2], rng=StableRNG(17)) + cl = expected_degree_graph([3, 2, 1, 2]; rng=StableRNG(17)) @test nv(cl) == 4 @test is_directed(cl) == false - cl = expected_degree_graph(fill(99, 100), rng=StableRNG(17)) + cl = expected_degree_graph(fill(99, 100); rng=StableRNG(17)) @test nv(cl) == 100 @test all(degree(cl) .> 90) end @testset "Watts-Strogatz" begin - ws = watts_strogatz(10, 4, 0.2, rng=rng) + ws = watts_strogatz(10, 4, 0.2; rng=rng) @test nv(ws) == 10 @test ne(ws) == 20 @test is_directed(ws) == false - - ws = watts_strogatz(10, 4, 0.2, is_directed=true, rng=rng) + + ws = watts_strogatz(10, 4, 0.2; is_directed=true, rng=rng) @test nv(ws) == 10 @test ne(ws) == 20 @test is_directed(ws) == true end - + @testset "Barabasi-Albert" begin - ba = barabasi_albert(10, 2, rng=rng) + ba = barabasi_albert(10, 2; rng=rng) @test nv(ba) == 10 @test ne(ba) == 16 @test is_directed(ba) == false - - ba = barabasi_albert(10, 2, 2, rng=rng) + + ba = barabasi_albert(10, 2, 2; rng=rng) @test nv(ba) == 10 @test ne(ba) == 16 @test is_directed(ba) == false - - ba = barabasi_albert(10, 4, 2, rng=rng) + + ba = barabasi_albert(10, 4, 2; rng=rng) @test nv(ba) == 10 @test ne(ba) == 12 @test is_directed(ba) == false - - ba = barabasi_albert(10, 2, complete=true, rng=rng) + + ba = barabasi_albert(10, 2; complete=true, rng=rng) @test nv(ba) == 10 @test ne(ba) == 17 @test is_directed(ba) == false - - ba = barabasi_albert(10, 2, 2, complete=true, rng=rng) + + ba = barabasi_albert(10, 2, 2; complete=true, rng=rng) @test nv(ba) == 10 @test ne(ba) == 17 @test is_directed(ba) == false - - ba = barabasi_albert(10, 4, 2, complete=true, rng=rng) + + ba = barabasi_albert(10, 4, 2; complete=true, rng=rng) @test nv(ba) == 10 @test ne(ba) == 18 @test is_directed(ba) == false - - ba = barabasi_albert(10, 2, is_directed=true, rng=rng) + + ba = barabasi_albert(10, 2; is_directed=true, rng=rng) @test nv(ba) == 10 @test ne(ba) == 16 @test is_directed(ba) == true - - ba = barabasi_albert(10, 2, 2, is_directed=true, rng=rng) + + ba = barabasi_albert(10, 2, 2; is_directed=true, rng=rng) @test nv(ba) == 10 @test ne(ba) == 16 @test is_directed(ba) == true - - ba = barabasi_albert(10, 4, 2, is_directed=true, rng=rng) + + ba = barabasi_albert(10, 4, 2; is_directed=true, rng=rng) @test nv(ba) == 10 @test ne(ba) == 12 @test is_directed(ba) == true - - ba = barabasi_albert(10, 2, is_directed=true, complete=true, rng=rng) + + ba = barabasi_albert(10, 2; is_directed=true, complete=true, rng=rng) @test nv(ba) == 10 @test ne(ba) == 18 @test is_directed(ba) == true - - ba = barabasi_albert(10, 2, 2, is_directed=true, complete=true, rng=rng) + + ba = barabasi_albert(10, 2, 2; is_directed=true, complete=true, rng=rng) @test nv(ba) == 10 @test ne(ba) == 18 @test is_directed(ba) == true - - ba = barabasi_albert(10, 4, 2, is_directed=true, complete=true, rng=rng) + + ba = barabasi_albert(10, 4, 2; is_directed=true, complete=true, rng=rng) @test nv(ba) == 10 @test ne(ba) == 24 @test is_directed(ba) == true end @testset "static fitness" begin - fm = static_fitness_model(20, rand(10), rng=rng) + fm = static_fitness_model(20, rand(10); rng=rng) @test nv(fm) == 10 @test ne(fm) == 20 @test is_directed(fm) == false - - fm = static_fitness_model(20, rand(10), rand(10), rng=rng) + + fm = static_fitness_model(20, rand(10), rand(10); rng=rng) @test nv(fm) == 10 @test ne(fm) == 20 @test is_directed(fm) == true end - + @testset "static scale-free" begin - sf = static_scale_free(10, 20, 2.0, rng=rng) + sf = static_scale_free(10, 20, 2.0; rng=rng) @test nv(sf) == 10 @test ne(sf) == 20 @test is_directed(sf) == false - sf = static_scale_free(10, 20, 2.0, 2.0, rng=rng) + sf = static_scale_free(10, 20, 2.0, 2.0; rng=rng) @test nv(sf) == 10 @test ne(sf) == 20 @test is_directed(sf) == true end @testset "random regular" begin - rr = random_regular_graph(5, 0, rng=rng) + rr = random_regular_graph(5, 0; rng=rng) @test nv(rr) == 5 @test ne(rr) == 0 @test is_directed(rr) == false - rd = random_regular_digraph(10, 0, rng=rng) + rd = random_regular_digraph(10, 0; rng=rng) @test nv(rd) == 10 @test ne(rd) == 0 @test is_directed(rd) - rr = random_regular_graph(10, 8, rng=StableRNG(4)) + rr = random_regular_graph(10, 8; rng=StableRNG(4)) @test nv(rr) == 10 @test ne(rr) == 40 @test is_directed(rr) == false @@ -186,66 +191,67 @@ @test degree(rr, v) == 8 end - rr = random_regular_graph(1000, 50, rng=rng) + rr = random_regular_graph(1000, 50; rng=rng) @test nv(rr) == 1000 @test ne(rr) == 25000 @test is_directed(rr) == false for v in vertices(rr) @test degree(rr, v) == 50 end - rd = random_regular_digraph(1000, 4, rng=rng) + rd = random_regular_digraph(1000, 4; rng=rng) @test nv(rd) == 1000 @test ne(rd) == 4000 @test is_directed(rd) outdegree_rd = @inferred(outdegree(rd)) @test all(outdegree_rd .== outdegree_rd[1]) - rd = random_regular_digraph(1000, 4, dir=:in, rng=rng) + rd = random_regular_digraph(1000, 4; dir=:in, rng=rng) @test nv(rd) == 1000 @test ne(rd) == 4000 @test is_directed(rd) indegree_rd = @inferred(indegree(rd)) @test all(indegree_rd .== indegree_rd[1]) - rd = random_regular_digraph(10, 8, dir=:out, rng=StableRNG(4)) + rd = random_regular_digraph(10, 8; dir=:out, rng=StableRNG(4)) @test nv(rd) == 10 @test ne(rd) == 80 @test is_directed(rd) end @testset "random configuration model" begin - rr = random_configuration_model(10, repeat([2,4], 5), rng=StableRNG(3)) + rr = random_configuration_model(10, repeat([2, 4], 5); rng=StableRNG(3)) @test nv(rr) == 10 @test ne(rr) == 15 @test is_directed(rr) == false - num2 = 0; num4 = 0 + num2 = 0 + num4 = 0 for v in vertices(rr) d = degree(rr, v) - @test d == 2 || d == 4 + @test d == 2 || d == 4 d == 2 ? num2 += 1 : num4 += 1 end @test num4 == 5 @test num2 == 5 - rr = random_configuration_model(1000, zeros(Int, 1000), rng=rng) + rr = random_configuration_model(1000, zeros(Int, 1000); rng=rng) @test nv(rr) == 1000 @test ne(rr) == 0 @test is_directed(rr) == false - rr = random_configuration_model(3, [2,2,2], check_graphical=true, rng=rng) + rr = random_configuration_model(3, [2, 2, 2]; check_graphical=true, rng=rng) @test nv(rr) == 3 @test ne(rr) == 3 @test is_directed(rr) == false end @testset "random tournament" begin - rt = random_tournament_digraph(10, rng=rng) + rt = random_tournament_digraph(10; rng=rng) @test nv(rt) == 10 @test ne(rt) == 45 @test is_directed(rt) @test all(degree(rt) .== 9) Edges = edges(rt) - for i = 1:10, j = 1:10 + for i in 1:10, j in 1:10 if i != j edge = Edge(i, j) @test xor(edge ∈ Edges, reverse(edge) ∈ Edges) @@ -254,10 +260,10 @@ end @testset "SBM" begin - g = stochastic_block_model(2., 3., [100,100], rng=rng) - @test 4.0 < mean(degree(g)) < 6.0 - g = stochastic_block_model(3., 4., [100,100,100], rng=rng) - @test 10.0 < mean(degree(g)) < 12.0 + g = stochastic_block_model(2.0, 3.0, [100, 100]; rng=rng) + @test 4.0 < mean(degree(g)) < 6.0 + g = stochastic_block_model(3.0, 4.0, [100, 100, 100]; rng=rng) + @test 10.0 < mean(degree(g)) < 12.0 function generate_nbp_sbm(numedges, sizes) density = 1 @@ -265,7 +271,7 @@ intra = density * -0.005 noise = density * 0.00501 sbm = nearbipartiteSBM(sizes, between, intra, noise) - edgestream = make_edgestream(sbm, rng=rng) + edgestream = make_edgestream(sbm; rng=rng) g = SimpleGraph(sum(sizes), numedges, edgestream) return sbm, g end @@ -299,7 +305,7 @@ numedges *= div(sum(sizes), 2) sbm = StochasticBlockModel(internalp, externalp, sizes) - g = SimpleGraph(sum(sizes), numedges, sbm, rng=rng) + g = SimpleGraph(sum(sizes), numedges, sbm; rng=rng) @test ne(g) >= 0.9numedges @test ne(g) <= numedges @test nv(g) == sum(sizes) @@ -311,16 +317,15 @@ # check that average degree is not too high # factor of two is cushion for random process - @test mean(degree(g)) <= 4 // 2 * numedges / sum(sizes) + @test mean(degree(g)) <= 4//2 * numedges / sum(sizes) # check that the internal degrees are higher than the external degrees # 5//4 is cushion for random process. - @test all(sum(bc - diagm(0 => diag(bc)), dims=1) .<= 5 // 4 .* diag(bc)) - + @test all(sum(bc - diagm(0 => diag(bc)); dims=1) .<= 5//4 .* diag(bc)) sbm2 = StochasticBlockModel(0.5 * ones(4), 0.3, 10 * ones(Int, 4)) - sbm = StochasticBlockModel(0.5, 0.3, 10, 4) + sbm = StochasticBlockModel(0.5, 0.3, 10, 4) @test sbm == sbm2 - sbm.affinities[1,1] = 0 + sbm.affinities[1, 1] = 0 @test sbm != sbm2 end @@ -333,10 +338,10 @@ @testset "Dorogovtsev-Mendes" begin g = @inferred(dorogovtsev_mendes(10, rng=rng)) @test nv(g) == 10 && ne(g) == 17 - g = dorogovtsev_mendes(11, rng=rng) + g = dorogovtsev_mendes(11; rng=rng) @test nv(g) == 11 && ne(g) == 19 @test δ(g) == 2 - g = dorogovtsev_mendes(3, rng=rng) + g = dorogovtsev_mendes(3; rng=rng) @test nv(g) == 3 && ne(g) == 3 # testing domain errors @test_throws DomainError dorogovtsev_mendes(2, rng=rng) @@ -344,18 +349,18 @@ end @testset "random orientation DAG" begin - # testing if returned graph is acyclic and valid SimpleGraph - rog = random_orientation_dag(SimpleGraph(5, 10, rng=rng), rng=rng) + # testing if returned graph is acyclic and valid SimpleGraph + rog = random_orientation_dag(SimpleGraph(5, 10; rng=rng); rng=rng) @test isvalid_simplegraph(rog) @test !is_cyclic(rog) # testing if returned graph is acyclic and valid ComplexGraph - rog2 = random_orientation_dag(complete_graph(5), rng=rng) + rog2 = random_orientation_dag(complete_graph(5); rng=rng) @test isvalid_simplegraph(rog2) @test !is_cyclic(rog2) # testing with abstract RNG - rog3 = random_orientation_dag(SimpleGraph(10,15, rng=rng), rng=rng) + rog3 = random_orientation_dag(SimpleGraph(10, 15; rng=rng); rng=rng) @test isvalid_simplegraph(rog3) @test !is_cyclic(rog3) end diff --git a/test/simplegraphs/generators/smallgraphs.jl b/test/simplegraphs/generators/smallgraphs.jl index 41a4b2149..06db7c939 100644 --- a/test/simplegraphs/generators/smallgraphs.jl +++ b/test/simplegraphs/generators/smallgraphs.jl @@ -8,28 +8,28 @@ end smallgraphs = [ - (:diamond , 4, 5, false), - (:bull , 5, 5, false), - (:chvatal , 12, 24, false), - (:cubical , 8, 12, false), - (:desargues , 20, 30, false), - (:dodecahedral , 20, 30, false), - (:frucht , 12, 18, false), - (:heawood , 14, 21, false), - (:house , 5,6, false), - (:housex , 5, 8, false), - (:icosahedral , 12, 30, false), - (:krackhardtkite , 10, 18, false), - (:moebiuskantor , 16, 24, false), - (:octahedral , 6, 12, false), - (:pappus , 18, 27, false), - (:petersen , 10, 15, false), - (:sedgewickmaze , 8, 10, false), - (:tetrahedral , 4, 6, false), - (:truncatedcube , 24, 36, false), - (:truncatedtetrahedron , 12, 18, false), - (:truncatedtetrahedron_dir , 12, 18, true), - (:tutte , 46, 69, false) + (:diamond, 4, 5, false), + (:bull, 5, 5, false), + (:chvatal, 12, 24, false), + (:cubical, 8, 12, false), + (:desargues, 20, 30, false), + (:dodecahedral, 20, 30, false), + (:frucht, 12, 18, false), + (:heawood, 14, 21, false), + (:house, 5, 6, false), + (:housex, 5, 8, false), + (:icosahedral, 12, 30, false), + (:krackhardtkite, 10, 18, false), + (:moebiuskantor, 16, 24, false), + (:octahedral, 6, 12, false), + (:pappus, 18, 27, false), + (:petersen, 10, 15, false), + (:sedgewickmaze, 8, 10, false), + (:tetrahedral, 4, 6, false), + (:truncatedcube, 24, 36, false), + (:truncatedtetrahedron, 12, 18, false), + (:truncatedtetrahedron_dir, 12, 18, true), + (:tutte, 46, 69, false), ] @testset "$(s[1])" for s in smallgraphs @@ -42,8 +42,42 @@ @testset "karate" begin g = smallgraph(:karate) - degree_sequence = sort([16, 9, 10, 6, 3, 4, 4, 4, 5, 2, 3, 1, 2, 5, 2, 2, 2, - 2, 2, 3, 2, 2, 2, 5, 3, 3, 2, 4, 3, 4, 4, 6, 12, 17]) + degree_sequence = sort([ + 16, + 9, + 10, + 6, + 3, + 4, + 4, + 4, + 5, + 2, + 3, + 1, + 2, + 5, + 2, + 2, + 2, + 2, + 2, + 3, + 2, + 2, + 2, + 5, + 3, + 3, + 2, + 4, + 3, + 4, + 4, + 6, + 12, + 17, + ]) @test nv(g) == 34 && ne(g) == 78 && sort(degree(g)) == degree_sequence end @testset "nonexistent graph" begin diff --git a/test/simplegraphs/generators/staticgraphs.jl b/test/simplegraphs/generators/staticgraphs.jl index eba73f043..17107adce 100644 --- a/test/simplegraphs/generators/staticgraphs.jl +++ b/test/simplegraphs/generators/staticgraphs.jl @@ -31,17 +31,16 @@ @test eltype(g) == Int8 end - @testset "Bipartite Graphs" begin g = @inferred(complete_bipartite_graph(5, 8)) @test nv(g) == 13 && ne(g) == 40 @test isvalid_simplegraph(g) # tests for extreme values - g = complete_bipartite_graph(0,0) + g = complete_bipartite_graph(0, 0) @test nv(g) == 0 && ne(g) == 0 - g = complete_bipartite_graph(5,0) + g = complete_bipartite_graph(5, 0) @test nv(g) == 5 && ne(g) == 0 - g = complete_bipartite_graph(0,5) + g = complete_bipartite_graph(0, 5) @test nv(g) == 5 && ne(g) == 0 g = @inferred complete_bipartite_graph(Int8(100), Int8(27)) @test nv(g) == 127 && ne(g) == 100 * 27 @@ -53,38 +52,38 @@ end function iscompletemultipartite(g, partitions) - sum(partitions) != nv(g) && return false - n = nv(g) - - edges = 0 - for p in partitions - edges += p*(Int(n)-p) - end - edges = div(edges, 2) - - edges != ne(g) && return false - - cur = 1 - for p in partitions - currange = cur:(cur+p-1) - lowerrange = 1:(cur-1) - upperrange = (cur+p):n - for u in currange - for v in currange # check that no vertices are connected to vertices in the same partition - has_edge(g, u, v) && return false - end - - for v in lowerrange # check all lower partition vertices - !has_edge(g, u, v) && return false - end - - for v in upperrange # check all higher partition vertices - !has_edge(g, u, v) && return false - end + sum(partitions) != nv(g) && return false + n = nv(g) + + edges = 0 + for p in partitions + edges += p * (Int(n) - p) end - cur += p - end - return true + edges = div(edges, 2) + + edges != ne(g) && return false + + cur = 1 + for p in partitions + currange = cur:(cur + p - 1) + lowerrange = 1:(cur - 1) + upperrange = (cur + p):n + for u in currange + for v in currange # check that no vertices are connected to vertices in the same partition + has_edge(g, u, v) && return false + end + + for v in lowerrange # check all lower partition vertices + !has_edge(g, u, v) && return false + end + + for v in upperrange # check all higher partition vertices + !has_edge(g, u, v) && return false + end + end + cur += p + end + return true end @testset "Multipartite Graphs" begin @@ -119,16 +118,16 @@ end function retrievepartitions(n, r) - partitions = partitions = Vector{Int}(undef, r) - c = cld(n,r) - f = fld(n,r) - for i in 1:(n%r) - partitions[i] = c - end - for i in ((n%r)+1):r - partitions[i] = f - end - return partitions + partitions = partitions = Vector{Int}(undef, r) + c = cld(n, r) + f = fld(n, r) + for i in 1:(n % r) + partitions[i] = c + end + for i in ((n % r) + 1):r + partitions[i] = f + end + return partitions end @testset "Turan Graphs" begin @@ -144,17 +143,17 @@ @test nv(g) == 35 && ne(g) == 576 @test iscompletemultipartite(g, retrievepartitions(35, 17)) # tests for extreme values - g = turan_graph(15,15) + g = turan_graph(15, 15) @test nv(g) == 15 && ne(g) == 105 @test iscompletemultipartite(g, retrievepartitions(15, 15)) g = turan_graph(10, 1) @test nv(g) == 10 && ne(g) == 0 @test iscompletemultipartite(g, retrievepartitions(10, 1)) - @test_throws DomainError turan_graph(3,0) - @test_throws DomainError turan_graph(0,4) - @test_throws DomainError turan_graph(3,4) - @test_throws DomainError turan_graph(-1,5) - @test_throws DomainError turan_graph(3,-6) + @test_throws DomainError turan_graph(3, 0) + @test_throws DomainError turan_graph(0, 4) + @test_throws DomainError turan_graph(3, 4) + @test_throws DomainError turan_graph(-1, 5) + @test_throws DomainError turan_graph(3, -6) end @testset "Star Graphs" begin @@ -188,7 +187,6 @@ g = @inferred star_digraph(Int8(127)) @test nv(g) == 127 && ne(g) == 127 - 1 @test eltype(g) == Int8 - end @testset "Path DiGraphs" begin @@ -203,7 +201,6 @@ g = @inferred path_digraph(Int8(127)) @test nv(g) == 127 && ne(g) == 126 @test eltype(g) == Int8 - end @testset "Path Graphs" begin @@ -224,7 +221,7 @@ g = @inferred(cycle_digraph(5)) @test nv(g) == 5 && ne(g) == 5 @test isvalid_simplegraph(g) - # tests for extreme values + # tests for extreme values g = cycle_digraph(0) @test nv(g) == 0 && ne(g) == 0 g = cycle_digraph(1) @@ -240,7 +237,7 @@ g = @inferred(cycle_graph(5)) @test nv(g) == 5 && ne(g) == 5 @test isvalid_simplegraph(g) - # tests for extreme values + # tests for extreme values g = cycle_graph(0) @test nv(g) == 0 && ne(g) == 0 g = cycle_graph(1) @@ -256,7 +253,7 @@ g = @inferred(wheel_digraph(5)) @test nv(g) == 5 && ne(g) == 8 @test isvalid_simplegraph(g) - # tests for extreme values + # tests for extreme values g = wheel_digraph(0) @test nv(g) == 0 && ne(g) == 0 g = wheel_digraph(1) @@ -274,7 +271,7 @@ g = @inferred(wheel_graph(5)) @test nv(g) == 5 && ne(g) == 8 @test isvalid_simplegraph(g) - # tests for extreme values + # tests for extreme values g = wheel_graph(0) @test nv(g) == 0 && ne(g) == 0 g = wheel_graph(1) @@ -352,8 +349,62 @@ # [9] # [10] # [10]] - I = [1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 5, 6, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 12, 13, 14] - J = [3, 2, 8, 4, 1, 5, 1, 6, 7, 2, 2, 3, 3, 10, 9, 1, 11, 8, 12, 8, 13, 14, 9, 9, 10, 10] + I = [ + 1, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 4, + 5, + 6, + 7, + 8, + 8, + 8, + 9, + 9, + 9, + 10, + 10, + 10, + 11, + 12, + 13, + 14, + ] + J = [ + 3, + 2, + 8, + 4, + 1, + 5, + 1, + 6, + 7, + 2, + 2, + 3, + 3, + 10, + 9, + 1, + 11, + 8, + 12, + 8, + 13, + 14, + 9, + 9, + 10, + 10, + ] V = ones(Int, length(I)) Adj = sparse(I, J, V) @test Adj == sparse(g) @@ -374,8 +425,62 @@ # [8, 9, 12] # [9, 12] # [10, 11] - I = [1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12] - J = [3, 4, 1, 5, 2, 6, 3, 7, 4, 8, 9, 8, 5, 10, 7, 6, 11, 10, 7, 8, 9, 12, 9, 12, 10, 11] + I = [ + 1, + 2, + 3, + 3, + 4, + 4, + 5, + 5, + 6, + 6, + 7, + 7, + 7, + 8, + 8, + 8, + 9, + 9, + 9, + 10, + 10, + 10, + 11, + 11, + 12, + 12, + ] + J = [ + 3, + 4, + 1, + 5, + 2, + 6, + 3, + 7, + 4, + 8, + 9, + 8, + 5, + 10, + 7, + 6, + 11, + 10, + 7, + 8, + 9, + 12, + 9, + 12, + 10, + 11, + ] V = ones(Int, length(I)) Adj = sparse(I, J, V) @test Adj == sparse(rg3) @@ -383,16 +488,22 @@ end function isladdergraph(g) - n = nv(g)÷2 - !(degree(g, 1) == degree(g, n) == degree(g, n+1) == degree(g, 2*n) == 2) && return false - !(has_edge(g, 1, n+1) && has_edge(g, n, 2*n)) && return false - !(has_edge(g, 1, 2) && has_edge(g, n, n-1) && has_edge(g, n+1, n+2) && has_edge(g, 2*n, 2*n-1) ) && return false - for i in 2:(n-1) - !(degree(g, i) == 3 && degree(g, n+i) == 3) && return false - !(has_edge(g, i, i%n +1) && has_edge(g, i, i+n)) && return false - !(has_edge(g, n+i,n+(i%n +1)) && has_edge(g, n+i, i)) && return false - end - return true + n = nv(g) ÷ 2 + !(degree(g, 1) == degree(g, n) == degree(g, n + 1) == degree(g, 2 * n) == 2) && + return false + !(has_edge(g, 1, n + 1) && has_edge(g, n, 2 * n)) && return false + !( + has_edge(g, 1, 2) && + has_edge(g, n, n - 1) && + has_edge(g, n + 1, n + 2) && + has_edge(g, 2 * n, 2 * n - 1) + ) && return false + for i in 2:(n - 1) + !(degree(g, i) == 3 && degree(g, n + i) == 3) && return false + !(has_edge(g, i, i % n + 1) && has_edge(g, i, i + n)) && return false + !(has_edge(g, n + i, n + (i % n + 1)) && has_edge(g, n + i, i)) && return false + end + return true end @testset "Ladder Graphs" begin @@ -416,13 +527,13 @@ end function iscircularladdergraph(g) - n = nv(g)÷2 - for i in 1:n - !(degree(g, i) == 3 && degree(g, n+i) == 3) && return false - !(has_edge(g, i, i%n +1) && has_edge(g, i, i+n)) && return false - !(has_edge(g, n+i,n+(i%n +1)) && has_edge(g, n+i, i)) && return false - end - return true + n = nv(g) ÷ 2 + for i in 1:n + !(degree(g, i) == 3 && degree(g, n + i) == 3) && return false + !(has_edge(g, i, i % n + 1) && has_edge(g, i, i + n)) && return false + !(has_edge(g, n + i, n + (i % n + 1)) && has_edge(g, n + i, i)) && return false + end + return true end @testset "Circular Ladder Graphs" begin @@ -449,22 +560,22 @@ # checking that the nodes are organized correctly # see the docstring implementation notes for lollipop_graph function isbarbellgraph(g, n1, n2) - nv(g) != n1+n2 && return false - ne(g) != n1*(n1-1)÷2+n2*(n2-1)÷2 +1 && return false - for i in 1:n1 - for j in (i+1):n1 - !has_edge(g, i, j) && return false + nv(g) != n1 + n2 && return false + ne(g) != n1 * (n1 - 1) ÷ 2 + n2 * (n2 - 1) ÷ 2 + 1 && return false + for i in 1:n1 + for j in (i + 1):n1 + !has_edge(g, i, j) && return false + end end - end - for i in n1 .+ 1:n2 - for j in (i+1):(n1+n2) - !has_edge(g, i, j) && return false + for i in (n1 .+ 1):n2 + for j in (i + 1):(n1 + n2) + !has_edge(g, i, j) && return false + end end - end - !has_edge(g, n1, n1+1) && return false - return true + !has_edge(g, n1, n1 + 1) && return false + return true end @testset "Barbell Graphs" begin @@ -495,21 +606,21 @@ # checking that the nodes are organized correctly # see the docstring implementation notes for lollipop_graph function islollipopgraph(g, n1, n2) - nv(g) != n1+n2 && return false - ne(g) != n1*(n1-1)÷2+n2 && return false - for i in 1:n1 - for j in (i+1):n1 - !has_edge(g, i, j) && return false + nv(g) != n1 + n2 && return false + ne(g) != n1 * (n1 - 1) ÷ 2 + n2 && return false + for i in 1:n1 + for j in (i + 1):n1 + !has_edge(g, i, j) && return false + end end - end - for i in n1 .+ 1:(n2-1) - !has_edge(g, i, i+1) && return false - end + for i in (n1 .+ 1):(n2 - 1) + !has_edge(g, i, i + 1) && return false + end - !has_edge(g, n1, n1+1) && return false + !has_edge(g, n1, n1 + 1) && return false - return true + return true end @testset "Lollipop Graphs" begin @@ -537,4 +648,3 @@ @test_throws DomainError lollipop_graph(-1, -1) end end - diff --git a/test/simplegraphs/runtests.jl b/test/simplegraphs/runtests.jl index 93fd3c794..576110f14 100644 --- a/test/simplegraphs/runtests.jl +++ b/test/simplegraphs/runtests.jl @@ -11,7 +11,7 @@ Graphs.edgetype(g::DummySimpleGraph) = DummySimpleEdge has_edge(::DummySimpleGraph, ::DummySimpleEdge) = true # function to check if the invariants for SimpleGraph and SimpleDiGraph holds -function isvalid_simplegraph(g::SimpleGraph{T}) where {T <: Integer} +function isvalid_simplegraph(g::SimpleGraph{T}) where {T<:Integer} nf = length(g.fadjlist) n = T(nf) # checks it the adjacency lists are sorted, free of duplicates and in the correct range @@ -42,7 +42,7 @@ function isvalid_simplegraph(g::SimpleGraph{T}) where {T <: Integer} return true end -function isvalid_simplegraph(g::SimpleDiGraph{T}) where {T <: Integer} +function isvalid_simplegraph(g::SimpleDiGraph{T}) where {T<:Integer} nf = length(g.fadjlist) nb = length(g.badjlist) nf == nb || return false @@ -88,7 +88,7 @@ const simple_tests = [ "generators/randgraphs", "generators/staticgraphs", "generators/smallgraphs", - "generators/euclideangraphs" + "generators/euclideangraphs", ] @testset "Graphs.SimpleGraphs" begin diff --git a/test/simplegraphs/simpleedge.jl b/test/simplegraphs/simpleedge.jl index 1e4f7b934..4fe93b593 100644 --- a/test/simplegraphs/simpleedge.jl +++ b/test/simplegraphs/simpleedge.jl @@ -25,7 +25,9 @@ @test SimpleEdge(t1) == SimpleEdge{UInt8}(t1) == SimpleEdge{Int16}(t1) @test SimpleEdge{Int64}(ep1) == e - @test hash(SimpleEdge(t1)) == hash(SimpleEdge{UInt8}(t1)) == hash(SimpleEdge{UInt16}(t1)) + @test hash(SimpleEdge(t1)) == + hash(SimpleEdge{UInt8}(t1)) == + hash(SimpleEdge{UInt16}(t1)) @test hash(SimpleEdge(1, 2)) != hash(SimpleEdge(2, 1)) @test Pair(e) == p diff --git a/test/simplegraphs/simpleedgeiter.jl b/test/simplegraphs/simpleedgeiter.jl index bb9cc8a70..7852a2967 100644 --- a/test/simplegraphs/simpleedgeiter.jl +++ b/test/simplegraphs/simpleedgeiter.jl @@ -33,9 +33,12 @@ # # codecov for eltype(::Type{SimpleEdgeIter{SimpleDiGraph{T}}}) where {T} = SimpleDiGraphEdge{T} gd = SimpleDiGraph{UInt8}(10, 20; rng=rng) - @test @inferred(eltype(edges(gd))) == eltype(typeof(edges(gd))) == edgetype(gd) == SimpleDiGraphEdge{UInt8} + @test @inferred(eltype(edges(gd))) == + eltype(typeof(edges(gd))) == + edgetype(gd) == + SimpleDiGraphEdge{UInt8} end - + ga = SimpleGraph(10) add_edge!(ga, 3, 2) add_edge!(ga, 3, 10) @@ -74,10 +77,8 @@ @test collect(eit) == [Edge(2, 3), Edge(3, 10), Edge(5, 10)] eit = @inferred(edges(dga)) - @test collect(eit) == [ - SimpleEdge(3, 2), SimpleEdge(3, 10), - SimpleEdge(5, 10), SimpleEdge(10, 3) - ] + @test collect(eit) == + [SimpleEdge(3, 2), SimpleEdge(3, 10), SimpleEdge(5, 10), SimpleEdge(10, 3)] end @testset "graph modifications" begin diff --git a/test/simplegraphs/simplegraphs.jl b/test/simplegraphs/simplegraphs.jl index 4242d0e18..6d13e62ef 100644 --- a/test/simplegraphs/simplegraphs.jl +++ b/test/simplegraphs/simplegraphs.jl @@ -1,4 +1,4 @@ -import Random +using Random: Random @testset "SimpleGraphs" begin rng = StableRNG(1) @@ -21,7 +21,6 @@ import Random @test @inferred(is_directed(SimpleDiGraph)) @test @inferred(is_directed(SimpleDiGraph{Int})) - for gbig in [SimpleGraph(0xff), SimpleDiGraph(0xff)] @test @inferred(!add_vertex!(gbig)) # overflow @test @inferred(add_vertices!(gbig, 10) == 0) @@ -67,7 +66,10 @@ import Random gc = copy(g) @test add_edge!(gc, 4, 1) && gc == cycle_graph(4) - @test @inferred(inneighbors(g, 2)) == @inferred(outneighbors(g, 2)) == @inferred(neighbors(g, 2)) == [1, 3] + @test @inferred(inneighbors(g, 2)) == + @inferred(outneighbors(g, 2)) == + @inferred(neighbors(g, 2)) == + [1, 3] @test @inferred(add_vertex!(gc)) # out of order, but we want it for issubset @test @inferred(g ⊆ gc) @test @inferred(has_vertex(gc, 5)) @@ -132,7 +134,8 @@ import Random @test @inferred(inneighbors(g, 2)) == [1] @test @inferred(outneighbors(g, 2)) == @inferred(neighbors(g, 2)) == [3] - @test @inferred Set(all_neighbors(g, 2)) == Set(union(inneighbors(g, 2), outneighbors(g, 2))) + @test @inferred Set(all_neighbors(g, 2)) == + Set(union(inneighbors(g, 2), outneighbors(g, 2))) @test @inferred(add_vertex!(gc)) # out of order, but we want it for issubset @test @inferred(g ⊆ gc) @test @inferred(has_vertex(gc, 5)) @@ -181,7 +184,6 @@ import Random @test g != h end - gdx = complete_digraph(4) for g in testdigraphs(gdx) h = DiGraph(g) @@ -202,7 +204,6 @@ import Random @test nv(g) == 2 && ne(g) == 2 && has_edge(g, 1, 1) @testset "Cannot create graphs for noncrete integer type $T" for T in [Signed, Integer] - @test_throws DomainError SimpleGraph{T}() @test_throws DomainError SimpleGraph{T}(one(T)) @@ -216,7 +217,9 @@ import Random add_edge!(g_undir, 200, 1) # ensure that the result uses all vertices add_edge!(g_undir, 2, 2) # add a self-loop - @testset "SimpleGraphFromIterator for edgetype $(edgetype(g))" for g in testgraphs(g_undir) + @testset "SimpleGraphFromIterator for edgetype $(edgetype(g))" for g in testgraphs( + g_undir + ) # We create an edge list, shuffle it and reverse half of its edges # using this edge list should result in the same graph @@ -255,11 +258,14 @@ import Random add_edge!(g_dir, 200, 1) add_edge!(g_dir, 2, 2) - @testset "SimpleGraphFromIterator for edgetype $(edgetype(g))" for g in testdigraphs(g_dir) + @testset "SimpleGraphFromIterator for edgetype $(edgetype(g))" for g in + testdigraphs( + g_dir + ) # We create an edge list and shuffle it edge_list = [e for e in edges(g)] shuffle!(rng, edge_list) - + edge_iter = (e for e in edge_list) edge_set = Set(edge_list) edge_set_any = Set{Any}(edge_list) @@ -292,25 +298,31 @@ import Random end @testset "SimpleGraphDiFromIterator for empty iterator" begin @test SimpleDiGraphFromIterator(empty_iter) == SimpleDiGraph(0) - @test edgetype(SimpleDiGraphFromIterator(empty_iter)) == edgetype(SimpleDiGraph(0)) + @test edgetype(SimpleDiGraphFromIterator(empty_iter)) == + edgetype(SimpleDiGraph(0)) end @testset "SimpleGraphFromIterator for wrong edge types" begin - @test_throws DomainError SimpleGraphFromIterator( (i for i in 1:2) ) + @test_throws DomainError SimpleGraphFromIterator((i for i in 1:2)) end @testset "SimpleDiGraphFromIterator for wrong edge types" begin - @test_throws DomainError SimpleDiGraphFromIterator( (SimpleDiGraphEdge(1,2), "a string") ) + @test_throws DomainError SimpleDiGraphFromIterator(( + SimpleDiGraphEdge(1, 2), "a string" + )) end # check if multiple edges && multiple self-loops result in the # correct number of edges & vertices # edges using integers < 1 should be ignored g_undir = SimpleGraph(0) - @testset "SimpleGraphFromIterator with self-loops and multiple edges, edgetype $(edgetype(g))" for g in testgraphs(SimpleGraph(0)) - + @testset "SimpleGraphFromIterator with self-loops and multiple edges, edgetype $(edgetype(g))" for g in + testgraphs( + SimpleGraph(0) + ) E = edgetype(g) - edge_list = E.([(4, 4),(1, 2),(4, 4),(1, 2),(4, 4),(2, 1),(0, 1),(1, 0),(0, 0)]) + edge_list = + E.([(4, 4), (1, 2), (4, 4), (1, 2), (4, 4), (2, 1), (0, 1), (1, 0), (0, 0)]) edge_iter = (e for e in edge_list) edge_set = Set(edge_list) edge_set_any = Set{Any}(edge_list) @@ -326,7 +338,7 @@ import Random @test nv(g3) == 4 @test nv(g4) == 4 @test nv(g5) == 4 - + @test ne(g1) == 2 @test ne(g2) == 2 @test ne(g3) == 2 @@ -334,10 +346,13 @@ import Random @test ne(g5) == 2 end - @testset "SimpleDiGraphFromIterator with self-loops and multiple edges, edgetype $(edgetype(g))" for g in testdigraphs(SimpleDiGraph(0)) - + @testset "SimpleDiGraphFromIterator with self-loops and multiple edges, edgetype $(edgetype(g))" for g in + testdigraphs( + SimpleDiGraph(0) + ) E = edgetype(g) - edge_list = E.([(4, 4),(1, 2),(4, 4),(1, 2),(4, 4),(2, 1),(0, 1),(1, 0),(0, 0)]) + edge_list = + E.([(4, 4), (1, 2), (4, 4), (1, 2), (4, 4), (2, 1), (0, 1), (1, 0), (0, 0)]) edge_iter = (e for e in edge_list) edge_set = Set(edge_list) edge_set_any = Set{Any}(edge_list) @@ -353,7 +368,7 @@ import Random @test nv(g3) == 4 @test nv(g4) == 4 @test nv(g5) == 4 - + @test ne(g1) == 3 @test ne(g2) == 3 @test ne(g3) == 3 @@ -363,27 +378,31 @@ import Random # test for iterators where the type of the elements can only be determined at runtime g_undir = SimpleGraph(0) - @testset "SimpleGraphFromIterator with edgelist of eltype Any" for g in testgraphs(g_undir) + @testset "SimpleGraphFromIterator with edgelist of eltype Any" for g in testgraphs( + g_undir + ) T = edgetype(g) - edge_list_good = Any[ T.(1, 2), T.(3, 4) ] - edge_list_bad = Any[ T.(1, 2), Int64(1) ] + edge_list_good = Any[T.(1, 2), T.(3, 4)] + edge_list_bad = Any[T.(1, 2), Int64(1)] g1 = SimpleGraphFromIterator(edge_list_good) @test edgetype(g1) == T @test_throws DomainError SimpleGraphFromIterator(edge_list_bad) end g_dir = SimpleDiGraph(0) - @testset "SimpleGraphDiFromIterator with edgelist of eltype Any" for g in testdigraphs(g_dir) + @testset "SimpleGraphDiFromIterator with edgelist of eltype Any" for g in + testdigraphs( + g_dir + ) T = edgetype(g) - edge_list_good = Any[ T.(1, 2), T.(3, 4) ] - edge_list_bad = Any[ T.(1, 2), Int64(1) ] + edge_list_good = Any[T.(1, 2), T.(3, 4)] + edge_list_bad = Any[T.(1, 2), Int64(1)] g1 = SimpleDiGraphFromIterator(edge_list_good) @test edgetype(g1) == T @test_throws DomainError SimpleDiGraphFromIterator(edge_list_bad) end - @testset "SimpleGraphFromIterator with edgelist of eltype Any" begin # If there are edges of multiple types, the construction should fail edge_list_1 = Any[Edge{Int8}(1, 2), Edge{Int16}(3, 4)] @@ -392,7 +411,6 @@ import Random @test_throws DomainError SimpleGraphFromIterator(edge_list_2) end - @testset "SimpleDiGraphFromIterator with edgelist of eltype Any" begin edge_list_1 = Any[Edge{Int8}(1, 2), Edge{Int16}(3, 4)] edge_list_2 = Any[Edge{Int16}(1, 2), Edge{Int8}(3, 4)] @@ -415,7 +433,7 @@ import Random @test isvalid_simplegraph(g5) g4 = copy(g) - vmap = rem_vertices!(g4, T[3], keep_order=true) + vmap = rem_vertices!(g4, T[3]; keep_order=true) @test g4 == (is_directed(g) ? complete_digraph(T(4)) : complete_graph(T(4))) @test vmap == [1, 2, 4, 5] @test isvalid_simplegraph(g4) @@ -423,23 +441,23 @@ import Random g4 = copy(g) add_edge!(g4, 1, 1) # some self_loops add_edge!(g4, 2, 2) - vmap = rem_vertices!(g4, T[1, 1, 1], keep_order=false) + vmap = rem_vertices!(g4, T[1, 1, 1]; keep_order=false) @test ne(g4) == (is_directed(g) ? 13 : 7) @test sort(vmap) == [2, 3, 4, 5] @test isvalid_simplegraph(g4) g2 = copy(g) - vmap = rem_vertices!(g2, T[2, 1, 4], keep_order=false) + vmap = rem_vertices!(g2, T[2, 1, 4]; keep_order=false) @test g2 == (is_directed(g) ? complete_digraph(T(2)) : complete_graph(T(2))) @test sort(vmap) == [3, 5] @test isvalid_simplegraph(g2) g0 = copy(g) - vmap = rem_vertices!(g0, T[1, 3, 2, 3, 5, 4], keep_order=false) + vmap = rem_vertices!(g0, T[1, 3, 2, 3, 5, 4]; keep_order=false) @test g0 == (is_directed(g) ? SimpleDiGraph(T(0)) : SimpleGraph(T(0))) @test isempty(vmap) @test isvalid_simplegraph(g0) - vmap = rem_vertices!(g0, T[], keep_order=false) + vmap = rem_vertices!(g0, T[]; keep_order=false) @test g0 == (is_directed(g) ? SimpleDiGraph(T(0)) : SimpleGraph(T(0))) @test isempty(vmap) @test isvalid_simplegraph(g0) @@ -450,9 +468,9 @@ import Random @test_throws ArgumentError rem_vertices!(g5, T[3, 0], keep_order=false) end - g_undir = erdos_renyi(10, 0.5, rng=rng) - g_dir = erdos_renyi(10, 0.5, is_directed=true, rng=rng) - for u = 1:2:10 + g_undir = erdos_renyi(10, 0.5; rng=rng) + g_dir = erdos_renyi(10, 0.5; is_directed=true, rng=rng) + for u in 1:2:10 add_edge!(g_undir, u, u) add_edge!(g_dir, u, u) end @@ -463,8 +481,8 @@ import Random gt = copy(g) gf = copy(g) a_converted = convert(Vector{T}, a) - vmap_t = rem_vertices!(gt, a_converted, keep_order=true) - vmap_f = rem_vertices!(gf, a_converted, keep_order=false) + vmap_t = rem_vertices!(gt, a_converted; keep_order=true) + vmap_f = rem_vertices!(gf, a_converted; keep_order=false) @test issorted(vmap_t) @test allunique(vmap_t) @test allunique(vmap_f) diff --git a/test/simplegraphs/specializations.jl b/test/simplegraphs/specializations.jl index b068b6898..763635a64 100644 --- a/test/simplegraphs/specializations.jl +++ b/test/simplegraphs/specializations.jl @@ -1,17 +1,34 @@ @testset "squash" begin - @testset "$g_in, kwargs=$kwargs" for kwargs in [(), (alwayscopy=false,), (alwayscopy=true,)], (g_in, g_expected) in [ - (SimpleGraph{Int64}(), SimpleGraph{Int8}()), - (SimpleDiGraph{UInt8}(), kwargs == (alwayscopy=false,) ? SimpleDiGraph{UInt8}() : SimpleDiGraph{Int8}()) , - (path_graph(Int16(126)), path_graph(Int8(126))), - (path_digraph(Int16(127)), path_digraph(UInt8(127))), - (path_graph(Int16(254)), path_graph(UInt8(254))), - (path_digraph(Int16(255)), path_digraph(Int16(255))), - (path_graph(UInt16(255)), kwargs == (alwayscopy=false,) ? path_graph(UInt16(255)) : path_graph(Int16(255))), - (star_graph(Int16(32766)), star_graph(Int16(32766))), - (star_digraph(Int32(32767)), star_digraph(UInt16(32767))), - (cycle_graph(Int128(123)), cycle_graph(Int8(123))), - ] + @testset "$g_in, kwargs=$kwargs" for kwargs in + [(), (alwayscopy=false,), (alwayscopy=true,)], + (g_in, g_expected) in [ + (SimpleGraph{Int64}(), SimpleGraph{Int8}()), + ( + SimpleDiGraph{UInt8}(), + if kwargs == (alwayscopy=false,) + SimpleDiGraph{UInt8}() + else + SimpleDiGraph{Int8}() + end, + ), + (path_graph(Int16(126)), path_graph(Int8(126))), + (path_digraph(Int16(127)), path_digraph(UInt8(127))), + (path_graph(Int16(254)), path_graph(UInt8(254))), + (path_digraph(Int16(255)), path_digraph(Int16(255))), + ( + path_graph(UInt16(255)), + if kwargs == (alwayscopy=false,) + path_graph(UInt16(255)) + else + path_graph(Int16(255)) + end, + ), + (star_graph(Int16(32766)), star_graph(Int16(32766))), + (star_digraph(Int32(32767)), star_digraph(UInt16(32767))), + (cycle_graph(Int128(123)), cycle_graph(Int8(123))), + ] + g_actual = squash(g_in; kwargs...) @test typeof(g_actual) === typeof(g_expected) @test g_actual == g_expected @@ -20,4 +37,3 @@ end end end - diff --git a/test/spanningtrees/boruvka.jl b/test/spanningtrees/boruvka.jl index d9da00db5..d91d6173f 100644 --- a/test/spanningtrees/boruvka.jl +++ b/test/spanningtrees/boruvka.jl @@ -1,117 +1,130 @@ @testset "Boruvka" begin - -g4 = complete_graph(4) - -distmx = [ - 0 1 5 6 - 1 0 4 10 - 5 4 0 3 - 6 10 3 0 -] - -vec_mst = Vector{Edge}([Edge(1, 2), Edge(3, 4), Edge(2, 3)]) -cost_mst = sum(distmx[src(e),dst(e)] for e in vec_mst) - -max_vec_mst = Vector{Edge}([Edge(1, 4), Edge(2, 4), Edge(1, 3)]) -cost_max_vec_mst = sum(distmx[src(e),dst(e)] for e in max_vec_mst) - -for g in testgraphs(g4) -# Testing Boruvka's algorithm - res1 = boruvka_mst(g,distmx) - g1t = SimpleGraph(res1.mst) - @test res1.weight == cost_mst - # acyclic graphs have n - c edges - @test nv(g1t) - length(connected_components(g1t)) == ne(g1t) - @test nv(g1t) == nv(g) - - res2 = boruvka_mst(g,distmx,minimize=false) - g2t = SimpleGraph(res2.mst) - @test res2.weight == cost_max_vec_mst - @test nv(g2t) - length(connected_components(g2t)) == ne(g2t) - @test nv(g2t) == nv(g) - -end -#second test -distmx_sec = [ - 0 0 0.26 0 0.38 0 0.58 0.16 - 0 0 0.36 0.29 0 0.32 0 0.19 - 0.26 0.36 0 0.17 0 0 0.4 0.34 - 0 0.29 0.17 0 0 0 0.52 0 - 0.38 0 0 0 0 0.35 0.93 0.37 - 0 0.32 0 0 0.35 0 0 0.28 - 0.58 0 0.4 0.52 0.93 0 0 0 - 0.16 0.19 0.34 0 0.37 0.28 0 0 -] - -gx = SimpleGraph(distmx_sec) - -vec2 = Vector{Edge}([Edge(1, 8), Edge(2, 8), Edge(3, 4), Edge(5, 6), Edge(6, 8), Edge(3, 7), Edge(1, 3)]) -weight_vec2 = sum(distmx_sec[src(e),dst(e)] for e in vec2) - -max_vec2 = Vector{Edge}([Edge(1, 7), Edge(2, 3), Edge(3, 7), Edge(4, 7), Edge(5, 7), Edge(5, 6), Edge(5, 8)]) -weight_max_vec2 = sum(distmx_sec[src(e),dst(e)] for e in max_vec2) - -for g in testgraphs(gx) - - res3 = boruvka_mst(g, distmx_sec) - g3t = SimpleGraph(res3.mst) - @test res3.weight == weight_vec2 - @test nv(g3t) - length(connected_components(g3t)) == ne(g3t) - @test nv(g3t) == nv(gx) - - res4 = boruvka_mst(g, distmx_sec,minimize = false) - g4t = SimpleGraph(res4.mst) - @test res4.weight == weight_max_vec2 - @test nv(g4t) - length(connected_components(g4t)) == ne(g4t) - @test nv(g4t) == nv(gx) - -end - -#third test with two components - -distmx_third = [ - 0 0 0.26 0 0.38 0 0.58 0.16 0 0 0 0 - 0 0 0.36 0.29 0 0.32 0 0.19 0 0 0 0 - 0.26 0.36 0 0.17 0 0 0.4 0.34 0 0 0 0 - 0 0.29 0.17 0 0 0 0.52 0 0 0 0 0 - 0.38 0 0 0 0 0.35 0.93 0.37 0 0 0 0 - 0 0.32 0 0 0.35 0 0 0.28 0 0 0 0 - 0.58 0 0.4 0.52 0.93 0 0 0 0 0 0 0 - 0.16 0.19 0.34 0 0.37 0.28 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 1 5 6 - 0 0 0 0 0 0 0 0 1 0 4 10 - 0 0 0 0 0 0 0 0 5 4 0 3 - 0 0 0 0 0 0 0 0 6 10 3 0 -] - -gd = SimpleGraph(distmx_third) - -vec3 = Vector{Edge}([Edge(1, 8), Edge(2, 8), Edge(3, 4), - Edge(5, 6), Edge(6, 8), Edge(3, 7), - Edge(9, 10), Edge(11,12), - Edge(1, 3), Edge(10,11)]) -weight_vec3 = sum(distmx_third[src(e),dst(e)] for e in vec3) - -max_vec3 = Vector{Edge}([Edge(1, 7), Edge(2, 3), Edge(3, 7), - Edge(4, 7), Edge(5, 7), Edge(5, 6), - Edge(5, 8), Edge(9,12), Edge(10,12), Edge(9,11)]) -weight_max_vec3 = sum(distmx_third[src(e),dst(e)] for e in max_vec3) - - -for g in testgraphs(gd) - - res5 = boruvka_mst(g, distmx_third) - g5t = SimpleGraph(res5.mst) - @test res5.weight == weight_vec3 - @test nv(g5t) - length(connected_components(g5t)) == ne(g5t) - @test nv(g5t) == nv(gd) - - res6 = boruvka_mst(g, distmx_third,minimize = false) - g6t = SimpleGraph(res6.mst) - @test res6.weight == weight_max_vec3 - @test nv(g6t) - length(connected_components(g6t)) == ne(g6t) - @test nv(g6t) == nv(gd) - -end - + g4 = complete_graph(4) + + distmx = [ + 0 1 5 6 + 1 0 4 10 + 5 4 0 3 + 6 10 3 0 + ] + + vec_mst = Vector{Edge}([Edge(1, 2), Edge(3, 4), Edge(2, 3)]) + cost_mst = sum(distmx[src(e), dst(e)] for e in vec_mst) + + max_vec_mst = Vector{Edge}([Edge(1, 4), Edge(2, 4), Edge(1, 3)]) + cost_max_vec_mst = sum(distmx[src(e), dst(e)] for e in max_vec_mst) + + for g in testgraphs(g4) + # Testing Boruvka's algorithm + res1 = boruvka_mst(g, distmx) + g1t = SimpleGraph(res1.mst) + @test res1.weight == cost_mst + # acyclic graphs have n - c edges + @test nv(g1t) - length(connected_components(g1t)) == ne(g1t) + @test nv(g1t) == nv(g) + + res2 = boruvka_mst(g, distmx; minimize=false) + g2t = SimpleGraph(res2.mst) + @test res2.weight == cost_max_vec_mst + @test nv(g2t) - length(connected_components(g2t)) == ne(g2t) + @test nv(g2t) == nv(g) + end + # second test + distmx_sec = [ + 0 0 0.26 0 0.38 0 0.58 0.16 + 0 0 0.36 0.29 0 0.32 0 0.19 + 0.26 0.36 0 0.17 0 0 0.4 0.34 + 0 0.29 0.17 0 0 0 0.52 0 + 0.38 0 0 0 0 0.35 0.93 0.37 + 0 0.32 0 0 0.35 0 0 0.28 + 0.58 0 0.4 0.52 0.93 0 0 0 + 0.16 0.19 0.34 0 0.37 0.28 0 0 + ] + + gx = SimpleGraph(distmx_sec) + + vec2 = Vector{Edge}([ + Edge(1, 8), Edge(2, 8), Edge(3, 4), Edge(5, 6), Edge(6, 8), Edge(3, 7), Edge(1, 3) + ]) + weight_vec2 = sum(distmx_sec[src(e), dst(e)] for e in vec2) + + max_vec2 = Vector{Edge}([ + Edge(1, 7), Edge(2, 3), Edge(3, 7), Edge(4, 7), Edge(5, 7), Edge(5, 6), Edge(5, 8) + ]) + weight_max_vec2 = sum(distmx_sec[src(e), dst(e)] for e in max_vec2) + + for g in testgraphs(gx) + res3 = boruvka_mst(g, distmx_sec) + g3t = SimpleGraph(res3.mst) + @test res3.weight == weight_vec2 + @test nv(g3t) - length(connected_components(g3t)) == ne(g3t) + @test nv(g3t) == nv(gx) + + res4 = boruvka_mst(g, distmx_sec; minimize=false) + g4t = SimpleGraph(res4.mst) + @test res4.weight == weight_max_vec2 + @test nv(g4t) - length(connected_components(g4t)) == ne(g4t) + @test nv(g4t) == nv(gx) + end + + # third test with two components + + distmx_third = [ + 0 0 0.26 0 0.38 0 0.58 0.16 0 0 0 0 + 0 0 0.36 0.29 0 0.32 0 0.19 0 0 0 0 + 0.26 0.36 0 0.17 0 0 0.4 0.34 0 0 0 0 + 0 0.29 0.17 0 0 0 0.52 0 0 0 0 0 + 0.38 0 0 0 0 0.35 0.93 0.37 0 0 0 0 + 0 0.32 0 0 0.35 0 0 0.28 0 0 0 0 + 0.58 0 0.4 0.52 0.93 0 0 0 0 0 0 0 + 0.16 0.19 0.34 0 0.37 0.28 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 1 5 6 + 0 0 0 0 0 0 0 0 1 0 4 10 + 0 0 0 0 0 0 0 0 5 4 0 3 + 0 0 0 0 0 0 0 0 6 10 3 0 + ] + + gd = SimpleGraph(distmx_third) + + vec3 = Vector{Edge}([ + Edge(1, 8), + Edge(2, 8), + Edge(3, 4), + Edge(5, 6), + Edge(6, 8), + Edge(3, 7), + Edge(9, 10), + Edge(11, 12), + Edge(1, 3), + Edge(10, 11), + ]) + weight_vec3 = sum(distmx_third[src(e), dst(e)] for e in vec3) + + max_vec3 = Vector{Edge}([ + Edge(1, 7), + Edge(2, 3), + Edge(3, 7), + Edge(4, 7), + Edge(5, 7), + Edge(5, 6), + Edge(5, 8), + Edge(9, 12), + Edge(10, 12), + Edge(9, 11), + ]) + weight_max_vec3 = sum(distmx_third[src(e), dst(e)] for e in max_vec3) + + for g in testgraphs(gd) + res5 = boruvka_mst(g, distmx_third) + g5t = SimpleGraph(res5.mst) + @test res5.weight == weight_vec3 + @test nv(g5t) - length(connected_components(g5t)) == ne(g5t) + @test nv(g5t) == nv(gd) + + res6 = boruvka_mst(g, distmx_third; minimize=false) + g6t = SimpleGraph(res6.mst) + @test res6.weight == weight_max_vec3 + @test nv(g6t) - length(connected_components(g6t)) == ne(g6t) + @test nv(g6t) == nv(gd) + end end diff --git a/test/spanningtrees/kruskal.jl b/test/spanningtrees/kruskal.jl index 64b293cc6..6109a9dc6 100644 --- a/test/spanningtrees/kruskal.jl +++ b/test/spanningtrees/kruskal.jl @@ -2,38 +2,42 @@ g4 = complete_graph(4) distmx = [ - 0 1 5 6 - 1 0 4 10 - 5 4 0 3 - 6 10 3 0 + 0 1 5 6 + 1 0 4 10 + 5 4 0 3 + 6 10 3 0 ] vec_mst = Vector{Edge}([Edge(1, 2), Edge(3, 4), Edge(2, 3)]) max_vec_mst = Vector{Edge}([Edge(2, 4), Edge(1, 4), Edge(1, 3)]) for g in testgraphs(g4) - # Testing Kruskal's algorithm + # Testing Kruskal's algorithm mst = @inferred(kruskal_mst(g, distmx)) @test mst == vec_mst - @test @inferred(kruskal_mst(g, distmx, minimize=false)) == max_vec_mst + @test @inferred(kruskal_mst(g, distmx, minimize=false)) == max_vec_mst end - #second test + # second test distmx_sec = [ - 0 0 0.26 0 0.38 0 0.58 0.16 - 0 0 0.36 0.29 0 0.32 0 0.19 - 0.26 0.36 0 0.17 0 0 0.4 0.34 - 0 0.29 0.17 0 0 0 0.52 0 - 0.38 0 0 0 0 0.35 0.93 0.37 - 0 0.32 0 0 0.35 0 0 0.28 - 0.58 0 0.4 0.52 0.93 0 0 0 - 0.16 0.19 0.34 0 0.37 0.28 0 0 + 0 0 0.26 0 0.38 0 0.58 0.16 + 0 0 0.36 0.29 0 0.32 0 0.19 + 0.26 0.36 0 0.17 0 0 0.4 0.34 + 0 0.29 0.17 0 0 0 0.52 0 + 0.38 0 0 0 0 0.35 0.93 0.37 + 0 0.32 0 0 0.35 0 0 0.28 + 0.58 0 0.4 0.52 0.93 0 0 0 + 0.16 0.19 0.34 0 0.37 0.28 0 0 ] gx = SimpleGraph(distmx_sec) - vec2 = Vector{Edge}([Edge(1, 8), Edge(3, 4), Edge(2, 8), Edge(1, 3), Edge(6, 8), Edge(5, 6), Edge(3, 7)]) - max_vec2 = Vector{Edge}([Edge(5, 7), Edge(1, 7), Edge(4, 7), Edge(3, 7), Edge(5, 8), Edge(2, 3), Edge(5, 6)]) + vec2 = Vector{Edge}([ + Edge(1, 8), Edge(3, 4), Edge(2, 8), Edge(1, 3), Edge(6, 8), Edge(5, 6), Edge(3, 7) + ]) + max_vec2 = Vector{Edge}([ + Edge(5, 7), Edge(1, 7), Edge(4, 7), Edge(3, 7), Edge(5, 8), Edge(2, 3), Edge(5, 6) + ]) for g in testgraphs(gx) mst2 = @inferred(kruskal_mst(g, distmx_sec)) @test mst2 == vec2 - @test @inferred(kruskal_mst(g, distmx_sec, minimize=false)) == max_vec2 + @test @inferred(kruskal_mst(g, distmx_sec, minimize=false)) == max_vec2 end end diff --git a/test/spanningtrees/prim.jl b/test/spanningtrees/prim.jl index b8dd573cb..7ee038777 100644 --- a/test/spanningtrees/prim.jl +++ b/test/spanningtrees/prim.jl @@ -2,10 +2,10 @@ g4 = complete_graph(4) distmx = [ - 0 1 5 6 - 1 0 4 10 - 5 4 0 3 - 6 10 3 0 + 0 1 5 6 + 1 0 4 10 + 5 4 0 3 + 6 10 3 0 ] vec_mst = Vector{Edge}([Edge(1, 2), Edge(2, 3), Edge(3, 4)]) @@ -15,19 +15,21 @@ @test mst == vec_mst end - #second test + # second test distmx_sec = [ - 0 0 0.26 0 0.38 0 0.58 0.16 - 0 0 0.36 0.29 0 0.32 0 0.19 - 0.26 0.36 0 0.17 0 0 0.4 0.34 - 0 0.29 0.17 0 0 0 0.52 0 - 0.38 0 0 0 0 0.35 0.93 0.37 - 0 0.32 0 0 0.35 0 0 0.28 - 0.58 0 0.4 0.52 0.93 0 0 0 - 0.16 0.19 0.34 0 0.37 0.28 0 0 + 0 0 0.26 0 0.38 0 0.58 0.16 + 0 0 0.36 0.29 0 0.32 0 0.19 + 0.26 0.36 0 0.17 0 0 0.4 0.34 + 0 0.29 0.17 0 0 0 0.52 0 + 0.38 0 0 0 0 0.35 0.93 0.37 + 0 0.32 0 0 0.35 0 0 0.28 + 0.58 0 0.4 0.52 0.93 0 0 0 + 0.16 0.19 0.34 0 0.37 0.28 0 0 ] - vec2 = Vector{Edge}([Edge(8, 2), Edge(1, 3), Edge(3, 4), Edge(6, 5), Edge(8, 6), Edge(3, 7), Edge(1, 8)]) + vec2 = Vector{Edge}([ + Edge(8, 2), Edge(1, 3), Edge(3, 4), Edge(6, 5), Edge(8, 6), Edge(3, 7), Edge(1, 8) + ]) gx = SimpleGraph(distmx_sec) for g in testgraphs(gx) mst2 = @inferred(prim_mst(g, distmx_sec)) diff --git a/test/steinertree/steiner_tree.jl b/test/steinertree/steiner_tree.jl index 437d96476..c5ee13b90 100644 --- a/test/steinertree/steiner_tree.jl +++ b/test/steinertree/steiner_tree.jl @@ -1,6 +1,7 @@ @testset "Steiner Tree" begin - - function sum_weight(g::AbstractGraph{<:Integer}, distmx::AbstractMatrix{<:Integer} = weights(g)) + function sum_weight( + g::AbstractGraph{<:Integer}, distmx::AbstractMatrix{<:Integer}=weights(g) + ) sum_wt = zero(eltype(g)) for e in edges(g) sum_wt += distmx[src(e), dst(e)] @@ -8,9 +9,9 @@ return sum_wt end - approx_factor(t::Integer) = 2-2/t + approx_factor(t::Integer) = 2 - 2 / t - g3 = star_graph(5) + g3 = star_graph(5) for g in testgraphs(g3) g_st = @inferred(steiner_tree(g, [2, 5])) @test ne(g_st) == 2 # [Edge(2, 1), Edge(1, 5)] @@ -20,7 +21,7 @@ @test ne(g_copy) == 2 # [Edge(2, 1), Edge(1, 5)] end - g4 = path_graph(11) + g4 = path_graph(11) for g in testgraphs(g4) g_st = @inferred(steiner_tree(g, [4, 8])) @test ne(g_st) == 4 @@ -30,33 +31,31 @@ @test ne(g_copy) == 4 end - g5 = grid([5, 5]) for g in testgraphs(g5) g_st = @inferred(steiner_tree(g, [3, 11, 15, 23])) - @test sum_weight(g_st) == 8 + @test sum_weight(g_st) == 8 end - d = [0 2 3 4 5 - 2 0 60 80 1 - 3 60 0 120 150 - 4 80 120 0 200 - 5 1 150 200 0] + d = [ + 0 2 3 4 5 + 2 0 60 80 1 + 3 60 0 120 150 + 4 80 120 0 200 + 5 1 150 200 0 + ] - g6 = complete_graph(5) + g6 = complete_graph(5) for g in testgraphs(g6) g_st = @inferred(steiner_tree(g, [2, 4, 5], d)) - @test sum_weight(g_st, d) <= approx_factor(3)*(1+2+4) + @test sum_weight(g_st, d) <= approx_factor(3) * (1 + 2 + 4) end d[2, 5] = d[5, 2] = 100 for g in testgraphs(g6) g_st = @inferred(steiner_tree(g, [2, 4, 5], d)) - @test sum_weight(g_st, d) <= approx_factor(3)*(2+4+5) + @test sum_weight(g_st, d) <= approx_factor(3) * (2 + 4 + 5) end - - - end diff --git a/test/traversals/bfs.jl b/test/traversals/bfs.jl index e86e24e47..7582bfb88 100644 --- a/test/traversals/bfs.jl +++ b/test/traversals/bfs.jl @@ -1,8 +1,10 @@ import Graphs: tree @testset "BFS" begin - g5 = SimpleDiGraph(4) - add_edge!(g5, 1, 2); add_edge!(g5, 2, 3); add_edge!(g5, 1, 3); add_edge!(g5, 3, 4) + add_edge!(g5, 1, 2) + add_edge!(g5, 2, 3) + add_edge!(g5, 1, 3) + add_edge!(g5, 3, 4) g6 = smallgraph(:house) @testset "bfs_tree and bfs_parents" begin @@ -28,7 +30,9 @@ import Graphs: tree @testset "gdistances" begin for g in testgraphs(g6) - @test @inferred(gdistances(g, 2)) == @inferred(gdistances(g, 2; sort_alg = MergeSort)) == [1, 0, 2, 1, 2] + @test @inferred(gdistances(g, 2)) == + @inferred(gdistances(g, 2; sort_alg=MergeSort)) == + [1, 0, 2, 1, 2] @test @inferred(gdistances(g, [1, 2])) == [0, 0, 1, 1, 2] @test @inferred(gdistances(g, [])) == fill(typemax(eltype(g)), 5) end @@ -36,8 +40,10 @@ import Graphs: tree @testset "is_bipartite" begin gx = SimpleGraph(5) - add_edge!(gx, 1, 2); add_edge!(gx, 1, 4) - add_edge!(gx, 2, 3); add_edge!(gx, 2, 5) + add_edge!(gx, 1, 2) + add_edge!(gx, 1, 4) + add_edge!(gx, 2, 3) + add_edge!(gx, 2, 5) add_edge!(gx, 3, 4) for g in testgraphs(gx) @@ -51,7 +57,7 @@ import Graphs: tree for (i, j) in [(1, 2), (2, 3), (2, 4), (4, 5), (3, 5)] add_edge!(gx, i, j) end - for g in testgraphs(gx) + for g in testgraphs(gx) @test has_path(g, 1, 5) @test has_path(g, 1, 2) @test has_path(g, 1, 5; exclude_vertices=[3]) @@ -61,19 +67,19 @@ import Graphs: tree @test has_path(g, 5, 1; exclude_vertices=[3]) @test has_path(g, 5, 1; exclude_vertices=[4]) @test !has_path(g, 5, 1; exclude_vertices=[3, 4]) - + # Edge cases @test !has_path(g, 1, 6) - @test !has_path(g, 6, 1) + @test !has_path(g, 6, 1) @test has_path(g, 1, 1) # inseparable @test !has_path(g, 1, 2; exclude_vertices=[2]) @test !has_path(g, 1, 2; exclude_vertices=[1]) - end + end end - + # import Graphs: TreeBFSVisitorVector, bfs_tree!, tree - function istree(parents::Vector{T}, maxdepth, n::T) where T<:Integer + function istree(parents::Vector{T}, maxdepth, n::T) where {T<:Integer} flag = true for i in one(T):n s = i @@ -88,5 +94,4 @@ import Graphs: tree end return flag end - end diff --git a/test/traversals/bipartition.jl b/test/traversals/bipartition.jl index 864e26cbc..182dc6a1a 100644 --- a/test/traversals/bipartition.jl +++ b/test/traversals/bipartition.jl @@ -5,8 +5,10 @@ end gx = SimpleGraph(5) - add_edge!(gx, 1, 2); add_edge!(gx, 1, 4) - add_edge!(gx, 2, 3); add_edge!(gx, 2, 5) + add_edge!(gx, 1, 2) + add_edge!(gx, 1, 4) + add_edge!(gx, 2, 3) + add_edge!(gx, 2, 5) add_edge!(gx, 3, 4) for g in testgraphs(gx) @@ -24,7 +26,8 @@ @test @inferred(bipartite_map(g)) == Vector{T}([ones(T, 10); 2 * ones(T, 10)]) h = blockdiag(g, g) - @test @inferred(bipartite_map(h)) == Vector{T}([ones(T, 10); 2 * ones(T, 10); ones(T, 10); 2 * ones(T, 10)]) + @test @inferred(bipartite_map(h)) == + Vector{T}([ones(T, 10); 2 * ones(T, 10); ones(T, 10); 2 * ones(T, 10)]) end g2 = complete_graph(2) diff --git a/test/traversals/dfs.jl b/test/traversals/dfs.jl index ffe74823c..457e078e2 100644 --- a/test/traversals/dfs.jl +++ b/test/traversals/dfs.jl @@ -1,61 +1,73 @@ @testset "DFS" begin - gnodes_directed=SimpleDiGraph(4) - gnodes_undirected=SimpleGraph(4) - gloop_directed=SimpleDiGraph(1) - add_edge!(gloop_directed, 1, 1); - gloop_undirected=SimpleGraph(1) - add_edge!(gloop_undirected, 1, 1); - g5 = SimpleDiGraph(4) - add_edge!(g5, 1, 2); add_edge!(g5, 2, 3); add_edge!(g5, 1, 3); add_edge!(g5, 3, 4) - gx = cycle_digraph(3) - gcyclic = SimpleGraph([0 1 1 0 0; 1 0 1 0 0; 1 1 0 0 0; 0 0 0 0 1; 0 0 0 1 0]) - gtree = SimpleGraph([0 1 1 1 0 0 0; 1 0 0 0 0 0 0; 1 0 0 0 0 0 0; 1 0 0 0 1 1 1; 0 0 0 1 0 0 0; 0 0 0 1 0 0 0; 0 0 0 1 0 0 0]) - @testset "dfs_tree" begin - for g in testdigraphs(g5) - z = @inferred(dfs_tree(g, 1)) - @test ne(z) == 3 && nv(z) == 4 - @test !has_edge(z, 1, 3) - @test !is_cyclic(g) + gnodes_directed = SimpleDiGraph(4) + gnodes_undirected = SimpleGraph(4) + gloop_directed = SimpleDiGraph(1) + add_edge!(gloop_directed, 1, 1) + gloop_undirected = SimpleGraph(1) + add_edge!(gloop_undirected, 1, 1) + g5 = SimpleDiGraph(4) + add_edge!(g5, 1, 2) + add_edge!(g5, 2, 3) + add_edge!(g5, 1, 3) + add_edge!(g5, 3, 4) + gx = cycle_digraph(3) + gcyclic = SimpleGraph([0 1 1 0 0; 1 0 1 0 0; 1 1 0 0 0; 0 0 0 0 1; 0 0 0 1 0]) + gtree = SimpleGraph( + [ + 0 1 1 1 0 0 0 + 1 0 0 0 0 0 0 + 1 0 0 0 0 0 0 + 1 0 0 0 1 1 1 + 0 0 0 1 0 0 0 + 0 0 0 1 0 0 0 + 0 0 0 1 0 0 0 + ], + ) + @testset "dfs_tree" begin + for g in testdigraphs(g5) + z = @inferred(dfs_tree(g, 1)) + @test ne(z) == 3 && nv(z) == 4 + @test !has_edge(z, 1, 3) + @test !is_cyclic(g) + end end - end - @testset "topological_sort_by_dfs" begin - for g in testdigraphs(g5) - @test @inferred(topological_sort_by_dfs(g)) == [1, 2, 3, 4] - end + @testset "topological_sort_by_dfs" begin + for g in testdigraphs(g5) + @test @inferred(topological_sort_by_dfs(g)) == [1, 2, 3, 4] + end - for g in testdigraphs(gx) - @test @inferred(is_cyclic(g)) - @test_throws ErrorException topological_sort_by_dfs(g) + for g in testdigraphs(gx) + @test @inferred(is_cyclic(g)) + @test_throws ErrorException topological_sort_by_dfs(g) + end end - end - @testset "is_cyclic" begin - for g in testgraphs(path_graph(2)) - @test !@inferred(is_cyclic(g)) - @test !@inferred(is_cyclic(zero(g))) - end - for g in testgraphs(gcyclic) - @test @inferred(is_cyclic(g)) - end - for g in testgraphs(gtree) - @test !@inferred(is_cyclic(g)) - end - for g in testgraphs(g5) - @test !@inferred(is_cyclic(g)) - end - for g in testgraphs(gnodes_directed) - @test !@inferred(is_cyclic(g)) + @testset "is_cyclic" begin + for g in testgraphs(path_graph(2)) + @test !@inferred(is_cyclic(g)) + @test !@inferred(is_cyclic(zero(g))) + end + for g in testgraphs(gcyclic) + @test @inferred(is_cyclic(g)) + end + for g in testgraphs(gtree) + @test !@inferred(is_cyclic(g)) + end + for g in testgraphs(g5) + @test !@inferred(is_cyclic(g)) + end + for g in testgraphs(gnodes_directed) + @test !@inferred(is_cyclic(g)) + end + for g in testgraphs(gnodes_undirected) + @test !@inferred(is_cyclic(g)) + end + for g in testgraphs(gloop_directed) + @test @inferred(is_cyclic(g)) + end + for g in testgraphs(gloop_undirected) + @test @inferred(is_cyclic(g)) + end end - for g in testgraphs(gnodes_undirected) - @test !@inferred(is_cyclic(g)) - end - for g in testgraphs(gloop_directed) - @test @inferred(is_cyclic(g)) - end - for g in testgraphs(gloop_undirected) - @test @inferred(is_cyclic(g)) - end - end - end diff --git a/test/traversals/diffusion.jl b/test/traversals/diffusion.jl index fcc1d310f..6efcc702d 100644 --- a/test/traversals/diffusion.jl +++ b/test/traversals/diffusion.jl @@ -1,168 +1,141 @@ @testset "Diffusion Simulation" begin -rng = StableRNG(1) + rng = StableRNG(1) -gx = complete_graph(5) + gx = complete_graph(5) -for g in testgraphs(gx) # this makes graphs of different eltypes - # Most basic - @test @inferred(diffusion_rate(g, 1.0, 4; rng=rng)) == [1, 5, 5, 5] -end - -for i in 1:5 - add_vertex!(gx) -end - -for g in testgraphs(gx) # this makes graphs of different eltypes - - ###### - # Check on fully connected, prob = 1 - ###### + for g in testgraphs(gx) # this makes graphs of different eltypes + # Most basic + @test @inferred(diffusion_rate(g, 1.0, 4; rng=rng)) == [1, 5, 5, 5] + end + for i in 1:5 + add_vertex!(gx) + end - # Add disconnected for more dynamics + for g in testgraphs(gx) # this makes graphs of different eltypes - # Basic test. Watch connected vertices - @test @inferred(diffusion_rate(g, 1.0, 4, - watch=collect(1:5), - initial_infections=[2], - rng=rng - )) == [1, 5, 5, 5] + ###### + # Check on fully connected, prob = 1 + ###### - # Watching unconnected vertices - @test @inferred(diffusion_rate(g, 1.0, 4, - watch=collect(6:10), - initial_infections=[2], - rng=rng - )) == [0, 0, 0, 0] + # Add disconnected for more dynamics - # Watch subset - @test @inferred(diffusion_rate(g, 1.0, 4, - watch=collect(1:2), - initial_infections=[2], - rng=rng - )) == [1, 2, 2, 2] + # Basic test. Watch connected vertices + @test @inferred( + diffusion_rate(g, 1.0, 4, watch=collect(1:5), initial_infections=[2], rng=rng) + ) == [1, 5, 5, 5] - @test @inferred(diffusion_rate(g, 1.0, 4, - watch=collect(1:5), - initial_infections=[10], - rng=rng - )) == [0, 0, 0, 0] + # Watching unconnected vertices + @test @inferred( + diffusion_rate(g, 1.0, 4, watch=collect(6:10), initial_infections=[2], rng=rng) + ) == [0, 0, 0, 0] -end + # Watch subset + @test @inferred( + diffusion_rate(g, 1.0, 4, watch=collect(1:2), initial_infections=[2], rng=rng) + ) == [1, 2, 2, 2] -###### -# Check along path graph -###### + @test @inferred( + diffusion_rate(g, 1.0, 4, watch=collect(1:5), initial_infections=[10], rng=rng) + ) == [0, 0, 0, 0] + end -gx = path_graph(5) + ###### + # Check along path graph + ###### -for g in testgraphs(gx) # this makes graphs of different eltypes + gx = path_graph(5) - @test @inferred(diffusion_rate(g, 1.0, 4, - watch=collect(1:5), - initial_infections=[1], - rng=rng - )) == [1, 2, 3, 4] + for g in testgraphs(gx) # this makes graphs of different eltypes + @test @inferred( + diffusion_rate(g, 1.0, 4, watch=collect(1:5), initial_infections=[1], rng=rng) + ) == [1, 2, 3, 4] - @test @inferred(diffusion_rate(g, 1.0, 4, - watch=collect(1:5), - initial_infections=[3], - rng=rng - )) == [1, 3, 5, 5] -end + @test @inferred( + diffusion_rate(g, 1.0, 4, watch=collect(1:5), initial_infections=[3], rng=rng) + ) == [1, 3, 5, 5] + end -gx = path_graph(30) -for g in testgraphs(gx) - # Check normalize - @test @inferred(diffusion_rate(g, - 1.0, - 6, - initial_infections=[15], - normalize=false, - rng=rng - )) == [1, 3, 5, 7, 9, 11] - - - @test @inferred(diffusion_rate(g, 2.0, 6, - initial_infections=[15], - normalize=true, - rng=rng - )) == [1, 3, 5, 7, 9, 11] - - # Test probability accurate - # In a Path network, - # number of nodes infected (minus 1) - # is equal to number of successes of a - # Burnoulli process. - # So if p = 0.2, in 5 steps (seed + - # 4 trials) expected value is 0.8, - # with standard deviation 0.8. - - means = Dict(0.2 => 0.8, 0.4 => 1.6) - stds = Dict(0.2 => 0.8, 0.4 => 0.98) - runs = 20 - for p in [0.2, 0.4] - final_value = 0.0 - - for i in 1:20 - result = @inferred(diffusion_rate(g, p, 5, - initial_infections=[1], rng=rng)) - final_value += result[5] + gx = path_graph(30) + for g in testgraphs(gx) + # Check normalize + @test @inferred( + diffusion_rate(g, 1.0, 6, initial_infections=[15], normalize=false, rng=rng) + ) == [1, 3, 5, 7, 9, 11] + + @test @inferred( + diffusion_rate(g, 2.0, 6, initial_infections=[15], normalize=true, rng=rng) + ) == [1, 3, 5, 7, 9, 11] + + # Test probability accurate + # In a Path network, + # number of nodes infected (minus 1) + # is equal to number of successes of a + # Burnoulli process. + # So if p = 0.2, in 5 steps (seed + + # 4 trials) expected value is 0.8, + # with standard deviation 0.8. + + means = Dict(0.2 => 0.8, 0.4 => 1.6) + stds = Dict(0.2 => 0.8, 0.4 => 0.98) + runs = 20 + for p in [0.2, 0.4] + final_value = 0.0 + + for i in 1:20 + result = @inferred(diffusion_rate(g, p, 5, initial_infections=[1], rng=rng)) + final_value += result[5] + end + + # Pretty loose bounds so don't get lots of failed tests. + # Just want some safeguard. + # Note 5 steps = 4 Bernoullis + initial infection. + # False rate less than 1 in 1000 + # Subtract 1 for initial infection + avg = final_value / runs - 1 + @test avg < means[p] + stds[p] / sqrt(runs) * 3.5 + @test avg > means[p] - stds[p] / sqrt(runs) * 3.5 end - - # Pretty loose bounds so don't get lots of failed tests. - # Just want some safeguard. - # Note 5 steps = 4 Bernoullis + initial infection. - # False rate less than 1 in 1000 - # Subtract 1 for initial infection - avg = final_value / runs - 1 - @test avg < means[p] + stds[p] / sqrt(runs) * 3.5 - @test avg > means[p] - stds[p] / sqrt(runs) * 3.5 end -end + gx = path_digraph(10) -gx = path_digraph(10) + for g in testdigraphs(gx) -for g in testdigraphs(gx) + ###### + # Check on digraphs + ###### - ###### - # Check on digraphs - ###### - - @test @inferred(diffusion_rate(g, 1.0, 9, - initial_infections=[1], rng=rng - )) == collect(1:9) + @test @inferred(diffusion_rate(g, 1.0, 9, initial_infections=[1], rng=rng)) == + collect(1:9) - @test @inferred(diffusion_rate(g, 1.0, 9, - initial_infections=[10], rng=rng - )) == ones(Int, 9) + @test @inferred(diffusion_rate(g, 1.0, 9, initial_infections=[10], rng=rng)) == + ones(Int, 9) - # Check probabilities. - # See note in analogous tests above for undirected tests. - runs = 20 - means = Dict(0.2 => 2, 0.4 => 4) - stds = Dict(0.2 => 1.2649110640673518, - 0.4 => 1.5491933384829668) + # Check probabilities. + # See note in analogous tests above for undirected tests. + runs = 20 + means = Dict(0.2 => 2, 0.4 => 4) + stds = Dict(0.2 => 1.2649110640673518, 0.4 => 1.5491933384829668) - for p in [0.2, 0.4] - final_value = 0.0 + for p in [0.2, 0.4] + final_value = 0.0 - for i in 1:20 - result = @inferred(diffusion_rate(g, p, 11, - initial_infections=[1], rng=rng)) - final_value += result[11] + for i in 1:20 + result = @inferred( + diffusion_rate(g, p, 11, initial_infections=[1], rng=rng) + ) + final_value += result[11] + end + # Pretty loose bounds so don't get lots of failed tests. + # Just want some safeguard. + # False rate less than 1 in 1000 + # Subtract 1 for initial infection + avg = final_value / runs - 1 + @test avg < means[p] + stds[p] / sqrt(runs) * 3.5 + @test avg > means[p] - stds[p] / sqrt(runs) * 3.5 end - - # Pretty loose bounds so don't get lots of failed tests. - # Just want some safeguard. - # False rate less than 1 in 1000 - # Subtract 1 for initial infection - avg = final_value / runs - 1 - @test avg < means[p] + stds[p] / sqrt(runs) * 3.5 - @test avg > means[p] - stds[p] / sqrt(runs) * 3.5 end end -end diff --git a/test/traversals/greedy_color.jl b/test/traversals/greedy_color.jl index ff2a4c181..30af252bd 100644 --- a/test/traversals/greedy_color.jl +++ b/test/traversals/greedy_color.jl @@ -1,5 +1,4 @@ @testset "Greedy Coloring" begin - g3 = star_graph(10) for g in testgraphs(g3) @@ -17,7 +16,7 @@ for op_sort in (true, false) C = @inferred(greedy_color(g, reps=5, sort_degree=op_sort)) - @test C.num_colors <= maximum(degree(g))+1 + @test C.num_colors <= maximum(degree(g)) + 1 correct = true for e in edges(g) C.colors[src(e)] == C.colors[dst(e)] && (correct = false) diff --git a/test/traversals/maxadjvisit.jl b/test/traversals/maxadjvisit.jl index 7dffaec26..286717ccd 100644 --- a/test/traversals/maxadjvisit.jl +++ b/test/traversals/maxadjvisit.jl @@ -6,19 +6,19 @@ # Original example by Stoer wedges = [ - (1, 2, 2.), - (1, 5, 3.), - (2, 3, 3.), - (2, 5, 2.), - (2, 6, 2.), - (3, 4, 4.), - (3, 7, 2.), - (4, 7, 2.), - (4, 8, 2.), - (5, 6, 3.), - (6, 7, 1.), - (7, 8, 3.)] - + (1, 2, 2.0), + (1, 5, 3.0), + (2, 3, 3.0), + (2, 5, 2.0), + (2, 6, 2.0), + (3, 4, 4.0), + (3, 7, 2.0), + (4, 7, 2.0), + (4, 8, 2.0), + (5, 6, 3.0), + (6, 7, 1.0), + (7, 8, 3.0), + ] m = length(wedges) eweights = spzeros(nv(gx), nv(gx)) @@ -29,23 +29,23 @@ eweights[d, s] = w end for g in testgraphs(gx) - @test nv(g) == 8 - @test ne(g) == m + @test nv(g) == 8 + @test ne(g) == m - parity, bestcut = @inferred(mincut(g, eweights)) + parity, bestcut = @inferred(mincut(g, eweights)) - @test length(parity) == 8 - @test parity == [2, 2, 1, 1, 2, 2, 1, 1] - @test bestcut == 4.0 + @test length(parity) == 8 + @test parity == [2, 2, 1, 1, 2, 2, 1, 1] + @test bestcut == 4.0 - parity, bestcut = @inferred(mincut(g)) + parity, bestcut = @inferred(mincut(g)) - @test length(parity) == 8 - @test parity == [2, 1, 1, 1, 1, 1, 1, 1] - @test bestcut == 2.0 + @test length(parity) == 8 + @test parity == [2, 1, 1, 1, 1, 1, 1, 1] + @test bestcut == 2.0 - v = @inferred(maximum_adjacency_visit(g)) - @test v == Vector{Int64}([1, 2, 5, 6, 3, 7, 4, 8]) + v = @inferred(maximum_adjacency_visit(g)) + @test v == Vector{Int64}([1, 2, 5, 6, 3, 7, 4, 8]) end g1 = SimpleGraph(1) diff --git a/test/traversals/randomwalks.jl b/test/traversals/randomwalks.jl index 326685f45..fecefca96 100644 --- a/test/traversals/randomwalks.jl +++ b/test/traversals/randomwalks.jl @@ -7,7 +7,7 @@ function is_nonbacktracking(walk) n = length(walk) if n < 3 - return true + return true end for i in 1:(n - 2) if walk[i + 2] == walk[i] @@ -31,56 +31,56 @@ end gx = path_digraph(10) for g in testdigraphs(gx) - @test @inferred(randomwalk(g, 1, 5, rng=rng)) == [1:5;] - @test @inferred(randomwalk(g, 2, 100, rng=rng)) == [2:10;] - @test_throws BoundsError randomwalk(g, 20, 20, rng=rng) - @test @inferred(non_backtracking_randomwalk(g, 10, 20, rng=rng)) == [10] - @test @inferred(non_backtracking_randomwalk(g, 1, 20, rng=rng)) == [1:10;] + @test @inferred(randomwalk(g, 1, 5, rng=rng)) == [1:5;] + @test @inferred(randomwalk(g, 2, 100, rng=rng)) == [2:10;] + @test_throws BoundsError randomwalk(g, 20, 20, rng=rng) + @test @inferred(non_backtracking_randomwalk(g, 10, 20, rng=rng)) == [10] + @test @inferred(non_backtracking_randomwalk(g, 1, 20, rng=rng)) == [1:10;] end gx = path_graph(10) for g in testgraphs(gx) - @test @inferred(self_avoiding_walk(g, 1, 20, rng=rng)) == [1:10;] - @test_throws BoundsError self_avoiding_walk(g, 20, 20, rng=rng) - @test @inferred(non_backtracking_randomwalk(g, 1, 20, rng=rng)) == [1:10;] - @test_throws BoundsError non_backtracking_randomwalk(g, 20, 20, rng=rng) + @test @inferred(self_avoiding_walk(g, 1, 20, rng=rng)) == [1:10;] + @test_throws BoundsError self_avoiding_walk(g, 20, 20, rng=rng) + @test @inferred(non_backtracking_randomwalk(g, 1, 20, rng=rng)) == [1:10;] + @test_throws BoundsError non_backtracking_randomwalk(g, 20, 20, rng=rng) end gx = SimpleDiGraph(path_graph(10)) for g in testdigraphs(gx) - @test @inferred(non_backtracking_randomwalk(g, 1, 20, rng=rng)) == [1:10;] - @test_throws BoundsError non_backtracking_randomwalk(g, 20, 20, rng=rng) + @test @inferred(non_backtracking_randomwalk(g, 1, 20, rng=rng)) == [1:10;] + @test_throws BoundsError non_backtracking_randomwalk(g, 20, 20, rng=rng) end gx = cycle_graph(10) for g in testgraphs(gx) - visited = @inferred(non_backtracking_randomwalk(g, 1, 20, rng=rng)) - @test visited == [1:10; 1:10;] || visited == [1; 10:-1:1; 10:-1:2;] + visited = @inferred(non_backtracking_randomwalk(g, 1, 20, rng=rng)) + @test visited == [1:10; 1:10] || visited == [1; 10:-1:1; 10:-1:2] end gx = cycle_digraph(10) for g in testdigraphs(gx) - @test @inferred(non_backtracking_randomwalk(g, 1, 20, rng=rng)) == [1:10; 1:10;] + @test @inferred(non_backtracking_randomwalk(g, 1, 20, rng=rng)) == [1:10; 1:10] end n = 10 gx = cycle_graph(n) - for k = 3:(n - 1) + for k in 3:(n - 1) add_edge!(gx, 1, k) end for g in testgraphs(gx) - for len = 1:(3 * n) - @test test_nbw(g, 1, len) - @test test_nbw(g, 2, len) - end + for len in 1:(3 * n) + @test test_nbw(g, 1, len) + @test test_nbw(g, 2, len) + end end - #test to make sure it works with self loops. + # test to make sure it works with self loops. add_edge!(gx, 1, 1) for g in testgraphs(gx) - for len = 1:(3 * n) - @test test_nbw(g, 1, len) - @test test_nbw(g, 2, len) - end + for len in 1:(3 * n) + @test test_nbw(g, 1, len) + @test test_nbw(g, 2, len) + end end end diff --git a/test/utils.jl b/test/utils.jl index 6244378e0..f9e343908 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -19,10 +19,10 @@ end # tests if isbounded has the correct behaviour - bounded_int_types = [Int8, Int16, Int32, Int64, Int128, - UInt8, UInt16, UInt32, UInt64, UInt128, - Int, Bool] - unbounded_int_types = [BigInt, Signed, Unsigned, Integer, Union{Int8, UInt8}] + bounded_int_types = [ + Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UInt128, Int, Bool + ] + unbounded_int_types = [BigInt, Signed, Unsigned, Integer, Union{Int8,UInt8}] for T in bounded_int_types @test Graphs.isbounded(T) == true @test Graphs.isbounded(T(0)) == true @@ -51,7 +51,6 @@ end @testset "Unweighted Contiguous Partition" begin - p = @inferred(Graphs.unweighted_contiguous_partition(4, 2)) @test p == [1:2, 3:4] @@ -63,7 +62,6 @@ end end @testset "Greedy Contiguous Partition" begin - p = @inferred(Graphs.greedy_contiguous_partition([1, 1, 1, 3], 2)) @test p == [1:3, 4:4] @@ -75,7 +73,6 @@ end end @testset "Optimal Contiguous Partition" begin - p = @inferred(Graphs.optimal_contiguous_partition([1, 1, 1, 3], 2)) @test p == [1:3, 4:4] diff --git a/test/vertexcover/degree_vertex_cover.jl b/test/vertexcover/degree_vertex_cover.jl index 2b068f715..4ec4fc35a 100644 --- a/test/vertexcover/degree_vertex_cover.jl +++ b/test/vertexcover/degree_vertex_cover.jl @@ -1,18 +1,17 @@ @testset "Degree Vertex Cover" begin - g3 = star_graph(5) for g in testgraphs(g3) c = @inferred(vertex_cover(g, DegreeVertexCover())) - @test c == [1,] + @test c == [1] end - + g4 = complete_graph(5) for g in testgraphs(g4) c = @inferred(vertex_cover(g, DegreeVertexCover())) - @test length(c)== 4 #All except one vertex + @test length(c) == 4 #All except one vertex end - #path_graph(5) with additional edge 2-5 + # path_graph(5) with additional edge 2-5 g5 = Graph([0 1 0 0 0; 1 0 1 0 1; 0 1 0 1 0; 0 0 1 0 1; 0 1 0 1 0]) for g in testgraphs(g5) c = @inferred(vertex_cover(g, DegreeVertexCover())) diff --git a/test/vertexcover/random_vertex_cover.jl b/test/vertexcover/random_vertex_cover.jl index 2dd5636af..b574581f5 100644 --- a/test/vertexcover/random_vertex_cover.jl +++ b/test/vertexcover/random_vertex_cover.jl @@ -1,5 +1,4 @@ @testset "Random Vertex Cover" begin - g0 = SimpleGraph(0) for g in testgraphs(g0) c = @inferred(vertex_cover(g, RandomVertexCover(); rng=StableRNG(3))) @@ -15,21 +14,21 @@ add_edge!(g1, 1, 1) for g in testgraphs(g1) c = @inferred(vertex_cover(g, RandomVertexCover(); rng=StableRNG(3))) - @test c == [1,] + @test c == [1] end g3 = star_graph(5) for g in testgraphs(g3) c = @inferred(vertex_cover(g, RandomVertexCover(); rng=StableRNG(3))) - @test (length(c)== 2 && (c[1] == 1 || c[2] == 1)) + @test (length(c) == 2 && (c[1] == 1 || c[2] == 1)) end g4 = complete_graph(5) for g in testgraphs(g4) c = @inferred(vertex_cover(g, RandomVertexCover(); rng=StableRNG(3))) - @test length(c)== 4 #All except one vertex + @test length(c) == 4 #All except one vertex end - + g5 = path_graph(5) for g in testgraphs(g5) c = @inferred(vertex_cover(g, RandomVertexCover(); rng=StableRNG(3))) @@ -42,6 +41,8 @@ for g in testgraphs(g5) c = @inferred(vertex_cover(g, RandomVertexCover(); rng=StableRNG(3))) sort!(c) - @test (c == [1, 2, 3, 4] || c == [1, 2, 3, 4, 5] || c == [2, 3, 4] || c == [2, 3, 4, 5]) + @test ( + c == [1, 2, 3, 4] || c == [1, 2, 3, 4, 5] || c == [2, 3, 4] || c == [2, 3, 4, 5] + ) end end