diff --git a/Project.toml b/Project.toml index 7ff45d2..c25031d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,36 +1,52 @@ name = "NamedGraphs" uuid = "678767b0-92e7-4007-89e4-4527a8725b19" authors = ["Matthew Fishman and contributors"] -version = "0.3.0" +version = "0.4.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" -GraphsFlows = "06909019-6f44-4949-96fc-b9d9aaa02889" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -Requires = "ae029012-a4dd-5104-9daa-d747884805df" +PackageExtensionCompat = "65ce6f38-6b18-4e1d-a461-8949797d7930" SimpleTraits = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" + +[weakdeps] +GraphsFlows = "06909019-6f44-4949-96fc-b9d9aaa02889" +KaHyPar = "2a6221f6-aa48-11e9-3542-2d9e0ef01880" +Metis = "2679e427-3c69-5b7f-982b-ece356f1e94b" SymRCM = "286e6d88-80af-4590-acc9-0001b223b9bd" +[extensions] +NamedGraphsGraphsFlowsExt = "GraphsFlows" +NamedGraphsKaHyParExt = "KaHyPar" +NamedGraphsMetisExt = "Metis" +NamedGraphsSymRCMExt = "SymRCM" + [compat] AbstractTrees = "0.3, 0.4" Dictionaries = "0.4" Graphs = "1.8" GraphsFlows = "0.1.1" +KaHyPar = "0.3.1" LinearAlgebra = "1.7" +Metis = "1.4" +PackageExtensionCompat = "1" SimpleTraits = "0.9" SparseArrays = "1.7" SplitApplyCombine = "1.2.2" -SymRCM = "0.2.1" Suppressor = "0.2" -Requires = "1.3" +SymRCM = "0.2.1" julia = "1.7" [extras] +GraphsFlows = "06909019-6f44-4949-96fc-b9d9aaa02889" +KaHyPar = "2a6221f6-aa48-11e9-3542-2d9e0ef01880" +Metis = "2679e427-3c69-5b7f-982b-ece356f1e94b" +SymRCM = "286e6d88-80af-4590-acc9-0001b223b9bd" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] diff --git a/README.md b/README.md index cf2bd81..d8f2870 100644 --- a/README.md +++ b/README.md @@ -41,13 +41,15 @@ Graphs.jl operations, and two implementations: `NamedGraph` and `NamedDiGraph`. `NamedGraph` simply takes a set of names for the vertices of the graph. For example: ```julia -julia> using Graphs +julia> using Graphs: grid, has_edge, has_vertex, neighbors -julia> using NamedGraphs +julia> using NamedGraphs: NamedGraph + +julia> using NamedGraphs.GraphsExtensions: ⊔, disjoint_union, subgraph, rename_vertices julia> g = NamedGraph(grid((4,)), ["A", "B", "C", "D"]) NamedGraph{String} with 4 vertices: -4-element Indices{String} +4-element Dictionaries.Indices{String} "A" "B" "C" @@ -80,7 +82,7 @@ julia> neighbors(g, "B") julia> subgraph(g, ["A", "B"]) NamedGraph{String} with 2 vertices: -2-element Indices{String} +2-element Dictionaries.Indices{String} "A" "B" @@ -107,7 +109,7 @@ julia> dims = (2, 2) julia> g = NamedGraph(grid(dims), Tuple.(CartesianIndices(dims))) NamedGraph{Tuple{Int64, Int64}} with 4 vertices: -4-element Indices{Tuple{Int64, Int64}} +4-element Dictionaries.Indices{Tuple{Int64, Int64}} (1, 1) (2, 1) (1, 2) @@ -151,7 +153,7 @@ You can use vertex names to get [induced subgraphs](https://juliagraphs.org/Grap ```julia julia> subgraph(v -> v[1] == 1, g) NamedGraph{Tuple{Int64, Int64}} with 2 vertices: -2-element Indices{Tuple{Int64, Int64}} +2-element Dictionaries.Indices{Tuple{Int64, Int64}} (1, 1) (1, 2) @@ -161,7 +163,7 @@ and 1 edge(s): julia> subgraph(v -> v[2] == 2, g) NamedGraph{Tuple{Int64, Int64}} with 2 vertices: -2-element Indices{Tuple{Int64, Int64}} +2-element Dictionaries.Indices{Tuple{Int64, Int64}} (1, 2) (2, 2) @@ -171,7 +173,7 @@ and 1 edge(s): julia> subgraph(g, [(1, 1), (2, 2)]) NamedGraph{Tuple{Int64, Int64}} with 2 vertices: -2-element Indices{Tuple{Int64, Int64}} +2-element Dictionaries.Indices{Tuple{Int64, Int64}} (1, 1) (2, 2) @@ -185,7 +187,7 @@ You can also take [disjoint unions](https://en.wikipedia.org/wiki/Disjoint_union ```julia julia> g₁ = g NamedGraph{Tuple{Int64, Int64}} with 4 vertices: -4-element Indices{Tuple{Int64, Int64}} +4-element Dictionaries.Indices{Tuple{Int64, Int64}} (1, 1) (2, 1) (1, 2) @@ -200,7 +202,7 @@ and 4 edge(s): julia> g₂ = g NamedGraph{Tuple{Int64, Int64}} with 4 vertices: -4-element Indices{Tuple{Int64, Int64}} +4-element Dictionaries.Indices{Tuple{Int64, Int64}} (1, 1) (2, 1) (1, 2) @@ -215,7 +217,7 @@ and 4 edge(s): julia> disjoint_union(g₁, g₂) NamedGraph{Tuple{Tuple{Int64, Int64}, Int64}} with 8 vertices: -8-element Indices{Tuple{Tuple{Int64, Int64}, Int64}} +8-element Dictionaries.Indices{Tuple{Tuple{Int64, Int64}, Int64}} ((1, 1), 1) ((2, 1), 1) ((1, 2), 1) @@ -238,7 +240,7 @@ and 8 edge(s): julia> g₁ ⊔ g₂ # Same as above NamedGraph{Tuple{Tuple{Int64, Int64}, Int64}} with 8 vertices: -8-element Indices{Tuple{Tuple{Int64, Int64}, Int64}} +8-element Dictionaries.Indices{Tuple{Tuple{Int64, Int64}, Int64}} ((1, 1), 1) ((2, 1), 1) ((1, 2), 1) @@ -278,7 +280,7 @@ The original graphs can be obtained from subgraphs: ```julia julia> rename_vertices(first, subgraph(v -> v[2] == 1, g₁ ⊔ g₂)) NamedGraph{Tuple{Int64, Int64}} with 4 vertices: -4-element Indices{Tuple{Int64, Int64}} +4-element Dictionaries.Indices{Tuple{Int64, Int64}} (1, 1) (2, 1) (1, 2) @@ -293,7 +295,7 @@ and 4 edge(s): julia> rename_vertices(first, subgraph(v -> v[2] == 2, g₁ ⊔ g₂)) NamedGraph{Tuple{Int64, Int64}} with 4 vertices: -4-element Indices{Tuple{Int64, Int64}} +4-element Dictionaries.Indices{Tuple{Int64, Int64}} (1, 1) (2, 1) (1, 2) @@ -315,8 +317,9 @@ and 4 edge(s): This file was generated with [Weave.jl](https://github.com/JunoLab/Weave.jl) with the following commands: ```julia -using NamedGraphs, Weave -weave( +using NamedGraphs: NamedGraphs +using Weave: Weave +Weave.weave( joinpath(pkgdir(NamedGraphs), "examples", "README.jl"); doctype="github", out_path=pkgdir(NamedGraphs), diff --git a/examples/README.jl b/examples/README.jl index 6382a55..a0a7596 100644 --- a/examples/README.jl +++ b/examples/README.jl @@ -25,8 +25,9 @@ #' `NamedGraph` simply takes a set of names for the vertices of the graph. For example: #+ term=true -using Graphs -using NamedGraphs +using Graphs: grid, has_edge, has_vertex, neighbors +using NamedGraphs: NamedGraph +using NamedGraphs.GraphsExtensions: ⊔, disjoint_union, subgraph, rename_vertices g = NamedGraph(grid((4,)), ["A", "B", "C", "D"]) #'Common operations are defined as you would expect: @@ -94,8 +95,9 @@ rename_vertices(first, subgraph(v -> v[2] == 2, g₁ ⊔ g₂)) #' This file was generated with [Weave.jl](https://github.com/JunoLab/Weave.jl) with the following commands: #+ eval=false -using NamedGraphs, Weave -weave( +using NamedGraphs: NamedGraphs +using Weave: Weave +Weave.weave( joinpath(pkgdir(NamedGraphs), "examples", "README.jl"); doctype="github", out_path=pkgdir(NamedGraphs), diff --git a/examples/boundary.jl b/examples/boundary.jl index 6f665d9..0d606c5 100644 --- a/examples/boundary.jl +++ b/examples/boundary.jl @@ -1,5 +1,6 @@ -using NamedGraphs -using Graphs +using NamedGraphs.GraphsExtensions: + boundary_edges, boundary_vertices, inner_boundary_vertices, outer_boundary_vertices +using NamedGraphs.NamedGraphGenerators: named_grid g = named_grid((5, 5)) subgraph_vertices = [(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2), (4, 3), (4, 4)] diff --git a/examples/disjoint_union.jl b/examples/disjoint_union.jl index 1a6a8d8..60d62c3 100644 --- a/examples/disjoint_union.jl +++ b/examples/disjoint_union.jl @@ -1,5 +1,6 @@ -using Graphs -using NamedGraphs +using Graphs: grid +using NamedGraphs: NamedGraph +using NamedGraphs.GraphsExtensions: ⊔, subgraph g1 = NamedGraph(grid((2, 2)), Tuple.(CartesianIndices((2, 2)))) g2 = NamedGraph(grid((2, 2)), Tuple.(CartesianIndices((2, 2)))) diff --git a/examples/mincut.jl b/examples/mincut.jl index 971932e..92dfa3e 100644 --- a/examples/mincut.jl +++ b/examples/mincut.jl @@ -1,5 +1,7 @@ -using NamedGraphs -using GraphsFlows +using Graphs: path_graph +using GraphsFlows: GraphsFlows +using NamedGraphs: NamedGraph +using NamedGraphs.GraphsExtensions: mincut_partitions g = NamedGraph(path_graph(4), ["A", "B", "C", "D"]) diff --git a/examples/multidimgraph_1d.jl b/examples/multidimgraph_1d.jl index 3d0f94a..b015bdb 100644 --- a/examples/multidimgraph_1d.jl +++ b/examples/multidimgraph_1d.jl @@ -1,5 +1,6 @@ -using Graphs -using NamedGraphs +using Graphs: grid, has_edge, has_vertex, ne, nv +using NamedGraphs: NamedGraph +using NamedGraphs.GraphsExtensions: ⊔, subgraph parent_graph = grid((4,)) vs = ["A", "B", "C", "D"] diff --git a/examples/multidimgraph_2d.jl b/examples/multidimgraph_2d.jl index 20923c1..3cd95d5 100644 --- a/examples/multidimgraph_2d.jl +++ b/examples/multidimgraph_2d.jl @@ -1,5 +1,6 @@ -using Graphs -using NamedGraphs +using Graphs: grid, has_edge, has_vertex, nv +using NamedGraphs: NamedGraph +using NamedGraphs.GraphsExtensions: ⊔, subgraph parent_graph = grid((2, 2)) vs = [("X", 1), ("X", 2), ("Y", 1), ("Y", 2)] @@ -11,14 +12,14 @@ g = NamedGraph(parent_graph, vs) @show !has_edge(g, ("X", 2) => ("Y", 1)) @show has_edge(g, ("X", 2) => ("Y", 2)) -g_sub = g[[("X", 1)]] +g_sub = subgraph(g, [("X", 1)]) @show has_vertex(g_sub, ("X", 1)) @show !has_vertex(g_sub, ("X", 2)) @show !has_vertex(g_sub, ("Y", 1)) @show !has_vertex(g_sub, ("Y", 2)) -g_sub = g[[("X", 1), ("X", 2)]] +g_sub = subgraph(g, [("X", 1), ("X", 2)]) @show has_vertex(g_sub, ("X", 1)) @show has_vertex(g_sub, ("X", 2)) diff --git a/examples/namedgraph.jl b/examples/namedgraph.jl index 3296d41..80e9eca 100644 --- a/examples/namedgraph.jl +++ b/examples/namedgraph.jl @@ -1,5 +1,6 @@ -using Graphs -using NamedGraphs +using Graphs: add_edge!, grid, has_edge, has_vertex, neighbors +using NamedGraphs: NamedGraph +using NamedGraphs.GraphsExtensions: subgraph g = NamedGraph(grid((4,)), ["A", "B", "C", "D"]) diff --git a/examples/partitioning.jl b/examples/partitioning.jl deleted file mode 100644 index 47a0e8b..0000000 --- a/examples/partitioning.jl +++ /dev/null @@ -1,16 +0,0 @@ -using NamedGraphs -using Metis - -g = named_grid((4, 4)) -npartitions = 4 - -pg_metis = PartitionedGraph(g; npartitions, backend="Metis") - -@show pg_metis isa PartitionedGraph - -if !Sys.iswindows() - using KaHyPar - pg_kahypar = PartitionedGraph(g; npartitions, backend="KaHyPar") - @show nv(partitioned_graph(pg_kahypar)) == nv(partitioned_graph(pg_metis)) == npartitions - @show pg_kahypar isa PartitionedGraph -end diff --git a/ext/NamedGraphsGraphsFlowsExt/NamedGraphsGraphsFlowsExt.jl b/ext/NamedGraphsGraphsFlowsExt/NamedGraphsGraphsFlowsExt.jl new file mode 100644 index 0000000..241de46 --- /dev/null +++ b/ext/NamedGraphsGraphsFlowsExt/NamedGraphsGraphsFlowsExt.jl @@ -0,0 +1,63 @@ +module NamedGraphsGraphsFlowsExt +using Graphs: AbstractGraph, IsDirected +using GraphsFlows: GraphsFlows +using NamedGraphs: + NamedGraphs, + AbstractNamedGraph, + DefaultNamedCapacity, + _symmetrize, + dist_matrix_to_parent_dist_matrix, + parent_graph, + parent_vertices_to_vertices, + vertex_to_parent_vertex +using NamedGraphs.GraphsExtensions: GraphsExtensions, directed_graph +using SimpleTraits: SimpleTraits, @traitfn + +@traitfn function NamedGraphs.dist_matrix_to_parent_dist_matrix( + graph::AbstractNamedGraph::IsDirected, dist_matrix::DefaultNamedCapacity +) + return GraphsFlows.DefaultCapacity(graph) +end + +@traitfn function GraphsFlows.mincut( + graph::AbstractNamedGraph::IsDirected, + source, + target, + capacity_matrix=DefaultNamedCapacity(graph), + algorithm::GraphsFlows.AbstractFlowAlgorithm=GraphsFlows.PushRelabelAlgorithm(), +) + parent_part1, parent_part2, flow = GraphsFlows.mincut( + directed_graph(parent_graph(graph)), + vertex_to_parent_vertex(graph, source), + vertex_to_parent_vertex(graph, target), + dist_matrix_to_parent_dist_matrix(graph, capacity_matrix), + algorithm, + ) + part1 = parent_vertices_to_vertices(graph, parent_part1) + part2 = parent_vertices_to_vertices(graph, parent_part2) + return (part1, part2, flow) +end + +@traitfn function GraphsFlows.mincut( + graph::AbstractNamedGraph::(!IsDirected), + source, + target, + capacity_matrix=DefaultNamedCapacity(graph), + algorithm::GraphsFlows.AbstractFlowAlgorithm=GraphsFlows.PushRelabelAlgorithm(), +) + return GraphsFlows.mincut( + directed_graph(graph), source, target, _symmetrize(capacity_matrix), algorithm + ) +end + +function GraphsExtensions.mincut_partitions( + graph::AbstractGraph, + source, + target, + capacity_matrix=DefaultNamedCapacity(graph), + algorithm::GraphsFlows.AbstractFlowAlgorithm=GraphsFlows.PushRelabelAlgorithm(), +) + part1, part2, flow = GraphsFlows.mincut(graph, source, target, capacity_matrix, algorithm) + return part1, part2 +end +end diff --git a/ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl b/ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl new file mode 100644 index 0000000..1c1cbb0 --- /dev/null +++ b/ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl @@ -0,0 +1,55 @@ +module NamedGraphsKaHyParExt +using Graphs: AbstractSimpleGraph, incidence_matrix +using KaHyPar: KaHyPar +using NamedGraphs.GraphsExtensions: GraphsExtensions, @Backend_str +using SplitApplyCombine: groupfind +using Suppressor: @suppress + +GraphsExtensions.set_partitioning_backend!(Backend"kahypar"()) + +# KaHyPar configuration options +# +# configurations = readdir(joinpath(pkgdir(KaHyPar), "src", "config")) +# "cut_kKaHyPar_sea20.ini" +# "cut_rKaHyPar_sea20.ini" +# "km1_kKaHyPar-E_sea20.ini" +# "km1_kKaHyPar_eco_sea20.ini" +# "km1_kKaHyPar_sea20.ini" +# "km1_rKaHyPar_sea20.ini" +# +const KAHYPAR_ALGS = Dict([ + (objective="edge_cut", alg="kway") => "cut_kKaHyPar_sea20.ini", + (objective="edge_cut", alg="recursive") => "cut_rKaHyPar_sea20.ini", + (objective="connectivity", alg="kway") => "km1_kKaHyPar_sea20.ini", + (objective="connectivity", alg="recursive") => "km1_rKaHyPar_sea20.ini", +]) + +""" +partitioned_vertices(::Backend"kahypar", g::Graph, npartiations::Integer; objective="edge_cut", alg="kway", kwargs...) + +- default_configuration => "cut_kKaHyPar_sea20.ini" +- :edge_cut => "cut_kKaHyPar_sea20.ini" +- :connectivity => "km1_kKaHyPar_sea20.ini" +- imbalance::Number=0.03 +""" +function GraphsExtensions.partitioned_vertices( + ::Backend"kahypar", + g::AbstractSimpleGraph, + npartitions::Integer; + objective="edge_cut", + alg="kway", + configuration=nothing, + kwargs..., +) + if isnothing(configuration) + configuration = joinpath( + pkgdir(KaHyPar), "src", "config", KAHYPAR_ALGS[(; objective=objective, alg=alg)] + ) + end + # https://github.com/kahypar/KaHyPar.jl/issues/20 + partitioned_verts = @suppress KaHyPar.partition( + incidence_matrix(g), npartitions; configuration, kwargs... + ) + return groupfind(partitioned_verts .+ 1) +end +end diff --git a/ext/NamedGraphsMetisExt/NamedGraphsMetisExt.jl b/ext/NamedGraphsMetisExt/NamedGraphsMetisExt.jl new file mode 100644 index 0000000..bb76017 --- /dev/null +++ b/ext/NamedGraphsMetisExt/NamedGraphsMetisExt.jl @@ -0,0 +1,28 @@ +module NamedGraphsMetisExt +using Graphs: AbstractSimpleGraph +using Metis: Metis +using NamedGraphs.GraphsExtensions: GraphsExtensions, @Backend_str +using SplitApplyCombine: groupfind + +GraphsExtensions.set_partitioning_backend!(Backend"metis"()) + +# Metis configuration options +const METIS_ALGS = Dict(["kway" => :KWAY, "recursive" => :RECURSIVE]) + +""" + partitioned_vertices(::Backend"metis", g::AbstractGraph, npartitions::Integer; alg="recursive") + +Partition the graph `G` in `n` parts. +The partition algorithm is defined by the `alg` keyword: + - :KWAY: multilevel k-way partitioning + - :RECURSIVE: multilevel recursive bisection +""" +function GraphsExtensions.partitioned_vertices( + ::Backend"metis", g::AbstractSimpleGraph, npartitions::Integer; alg="recursive", kwargs... +) + metis_alg = METIS_ALGS[alg] + partitioned_verts = Metis.partition(g, npartitions; alg=metis_alg, kwargs...) + return groupfind(Int.(partitioned_verts)) +end + +end diff --git a/ext/NamedGraphsSymRCMExt/NamedGraphsSymRCMExt.jl b/ext/NamedGraphsSymRCMExt/NamedGraphsSymRCMExt.jl new file mode 100644 index 0000000..f616472 --- /dev/null +++ b/ext/NamedGraphsSymRCMExt/NamedGraphsSymRCMExt.jl @@ -0,0 +1,9 @@ +module NamedGraphsSymRCMExt +using Graphs: AbstractGraph, adjacency_matrix +using NamedGraphs.GraphsExtensions: GraphsExtensions +using SymRCM: SymRCM + +function GraphsExtensions.symrcm_perm(graph::AbstractGraph) + return SymRCM.symrcm(adjacency_matrix(graph)) +end +end diff --git a/src/Dictionaries/dictionary.jl b/src/Dictionaries/dictionary.jl deleted file mode 100644 index 8ab2e86..0000000 --- a/src/Dictionaries/dictionary.jl +++ /dev/null @@ -1,4 +0,0 @@ -# Dictionaries.jl patch -# TODO: delete once fixed in Dictionaries.jl -# TODO: Move to Dictionaries.jl file in NamedGraphs.jl -convert(::Type{Dictionary{I,T}}, dict::Dictionary{I,T}) where {I,T} = dict diff --git a/src/Graphs/generators/namedgraphs.jl b/src/Graphs/generators/namedgraphs.jl deleted file mode 100644 index 8929997..0000000 --- a/src/Graphs/generators/namedgraphs.jl +++ /dev/null @@ -1,75 +0,0 @@ -"""Generate a graph which corresponds to a hexagonal tiling of the plane. There are m rows and n columns of hexagons. -Based off of the generator in Networkx hexagonal_lattice_graph()""" -function hexagonal_lattice_graph(m::Int64, n::Int64; periodic=false) - M = 2 * m - rows = [i for i in 1:(M + 2)] - cols = [i for i in 1:(n + 1)] - - if periodic && (n % 2 == 1 || m < 2 || n < 2) - error("Periodic Hexagonal Lattice needs m > 1, n > 1 and n even") - end - - G = NamedGraph([(j, i) for i in cols for j in rows]) - - col_edges = [(j, i) => (j + 1, i) for i in cols for j in rows[1:(M + 1)]] - row_edges = [(j, i) => (j, i + 1) for i in cols[1:n] for j in rows if i % 2 == j % 2] - add_edges!(G, col_edges) - add_edges!(G, row_edges) - rem_vertex!(G, (M + 2, 1)) - rem_vertex!(G, ((M + 1) * (n % 2) + 1, n + 1)) - - if periodic == true - for i in cols[1:n] - G = merge_vertices(G, [(1, i), (M + 1, i)]) - end - - for i in cols[2:(n + 1)] - G = merge_vertices(G, [(2, i), (M + 2, i)]) - end - - for j in rows[2:M] - G = merge_vertices(G, [(j, 1), (j, n + 1)]) - end - - rem_vertex!(G, (M + 1, n + 1)) - end - - return G -end - -"""Generate a graph which corresponds to a equilateral triangle tiling of the plane. There are m rows and n columns of triangles. -Based off of the generator in Networkx triangular_lattice_graph()""" -function triangular_lattice_graph(m::Int64, n::Int64; periodic=false) - N = floor(Int64, (n + 1) / 2.0) - rows = [i for i in 1:(m + 1)] - cols = [i for i in 1:(N + 1)] - - if periodic && (n < 5 || m < 3) - error("Periodic Triangular Lattice needs m > 2, n > 4") - end - - G = NamedGraph([(j, i) for i in cols for j in rows]) - - grid_edges1 = [(j, i) => (j, i + 1) for j in rows for i in cols[1:N]] - grid_edges2 = [(j, i) => (j + 1, i) for j in rows[1:m] for i in cols] - add_edges!(G, vcat(grid_edges1, grid_edges2)) - - diagonal_edges1 = [(j, i) => (j + 1, i + 1) for j in rows[2:2:m] for i in cols[1:N]] - diagonal_edges2 = [(j, i + 1) => (j + 1, i) for j in rows[1:2:m] for i in cols[1:N]] - add_edges!(G, vcat(diagonal_edges1, diagonal_edges2)) - - if periodic == true - for i in cols - G = merge_vertices(G, [(1, i), (m + 1, i)]) - end - - for j in rows[1:m] - G = merge_vertices(G, [(j, 1), (j, N + 1)]) - end - - elseif n % 2 == 1 - rem_vertices!(G, [(j, N + 1) for j in rows[2:2:(m + 1)]]) - end - - return G -end diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedge.jl b/src/Graphs/partitionedgraphs/abstractpartitionedge.jl deleted file mode 100644 index 3470ed3..0000000 --- a/src/Graphs/partitionedgraphs/abstractpartitionedge.jl +++ /dev/null @@ -1,8 +0,0 @@ -abstract type AbstractPartitionEdge{V} <: AbstractNamedEdge{V} end - -parent(pe::AbstractPartitionEdge) = not_implemented() -src(pe::AbstractPartitionEdge) = not_implemented() -dst(pe::AbstractPartitionEdge) = not_implemented() -reverse(pe::AbstractPartitionEdge) = not_implemented() - -#Don't have the vertices wrapped. But wrap them with source and edge. diff --git a/src/Graphs/partitionedgraphs/partitionedge.jl b/src/Graphs/partitionedgraphs/partitionedge.jl deleted file mode 100644 index a7b88c6..0000000 --- a/src/Graphs/partitionedgraphs/partitionedge.jl +++ /dev/null @@ -1,10 +0,0 @@ -struct PartitionEdge{V,E<:AbstractEdge{V}} <: AbstractPartitionEdge{V} - edge::E -end - -parent(pe::PartitionEdge) = getfield(pe, :edge) -src(pe::PartitionEdge) = PartitionVertex(src(parent(pe))) -dst(pe::PartitionEdge) = PartitionVertex(dst(parent(pe))) -PartitionEdge(p::Pair) = PartitionEdge(NamedEdge(first(p) => last(p))) -PartitionEdge(vsrc, vdst) = PartitionEdge(vsrc => vdst) -reverse(pe::PartitionEdge) = PartitionEdge(reverse(parent(pe))) diff --git a/src/Graphs/simplegraph.jl b/src/Graphs/simplegraph.jl deleted file mode 100644 index b1b0b78..0000000 --- a/src/Graphs/simplegraph.jl +++ /dev/null @@ -1,13 +0,0 @@ -######################################################################## -# Graphs.SimpleGraphs extensions - -# TODO: Move to `SimpleGraph` file -# TODO: Use trait dispatch to do no-ops when appropriate -directed_graph(G::Type{<:SimpleGraph}) = SimpleDiGraph{vertextype(G)} -undirected_graph(G::Type{<:SimpleGraph}) = G -directed_graph(G::Type{<:SimpleDiGraph}) = G -undirected_graph(G::Type{<:SimpleDiGraph}) = SimpleGraph{vertextype(G)} - -function set_vertices(graph::AbstractSimpleGraph, vertices::Vector) - return GenericNamedGraph(graph, vertices) -end diff --git a/src/Graphs/symrcm.jl b/src/Graphs/symrcm.jl deleted file mode 100644 index a5daada..0000000 --- a/src/Graphs/symrcm.jl +++ /dev/null @@ -1,8 +0,0 @@ -# SymRCM.symrcm overload -function symrcm(graph::AbstractGraph) - return symrcm(adjacency_matrix(graph)) -end - -function symrcm_permute(graph::AbstractGraph) - return permute_vertices(graph, symrcm(graph)) -end diff --git a/src/NamedGraphs.jl b/src/NamedGraphs.jl index 7c7e6ee..c28c480 100644 --- a/src/NamedGraphs.jl +++ b/src/NamedGraphs.jl @@ -1,219 +1,25 @@ module NamedGraphs -using AbstractTrees -using Dictionaries -using Graphs -using GraphsFlows -using LinearAlgebra -using SimpleTraits -using SparseArrays -using SplitApplyCombine -using SymRCM -using Suppressor -using Requires - -using Graphs.SimpleGraphs - -# General utility functions -not_implemented() = error("Not implemented") - -import Base: - convert, - copy, - eltype, - getindex, - hcat, - hvncat, - show, - to_index, - to_indices, - union, - vcat, - zero -# abstractnamedgraph.jl -import Graphs: - a_star, - add_edge!, - add_vertex!, - add_vertices!, - adjacency_matrix, - all_neighbors, - bellman_ford_shortest_paths, - bfs_parents, - bfs_tree, - blockdiag, - boruvka_mst, - center, - common_neighbors, - connected_components, - connected_components!, - degree, - degree_histogram, - desopo_pape_shortest_paths, - diameter, - dijkstra_shortest_paths, - dst, - dfs_parents, - dfs_tree, - eccentricity, - edges, - edgetype, - enumerate_paths, - floyd_warshall_shortest_paths, - has_edge, - has_path, - has_vertex, - indegree, - induced_subgraph, - inneighbors, - is_connected, - is_cyclic, - is_directed, - is_strongly_connected, - is_weakly_connected, - johnson_shortest_paths, - kruskal_mst, - merge_vertices, - merge_vertices!, - mincut, - ne, - neighbors, - neighborhood, - neighborhood_dists, - nv, - outdegree, - outneighbors, - prim_mst, - periphery, - radius, - rem_vertex!, - rem_edge!, - spfa_shortest_paths, - src, - steiner_tree, - topological_sort_by_dfs, - tree, - vertices, - yen_k_shortest_paths -import SymRCM: symrcm - -# abstractnamededge.jl -import Base: Pair, Tuple, show, ==, hash, eltype, convert -import Graphs: AbstractEdge, src, dst, reverse, reverse! - -include(joinpath("Base", "key.jl")) -include(joinpath("Dictionaries", "dictionary.jl")) -include(joinpath("Graphs", "abstractgraph.jl")) -include(joinpath("Graphs", "shortestpaths.jl")) -include(joinpath("Graphs", "boundary.jl")) -include(joinpath("Graphs", "symrcm.jl")) -include(joinpath("Graphs", "simplegraph.jl")) +include("lib/Keys/src/Keys.jl") +include("lib/GraphGenerators/src/GraphGenerators.jl") +include("lib/GraphsExtensions/src/GraphsExtensions.jl") +include("utils.jl") include("abstractnamededge.jl") include("namededge.jl") include("abstractnamedgraph.jl") +include("decorate.jl") include("shortestpaths.jl") include("distance.jl") include("distances_and_capacities.jl") -include(joinpath("steiner_tree", "steiner_tree.jl")) -include(joinpath("traversals", "dfs.jl")) -include(joinpath("traversals", "trees_and_forests.jl")) +include("steiner_tree.jl") +include("dfs.jl") include("namedgraph.jl") -include(joinpath("generators", "named_staticgraphs.jl")) -include(joinpath("Graphs", "generators", "staticgraphs.jl")) -include(joinpath("Graphs", "generators", "namedgraphs.jl")) -include(joinpath("Graphs", "generators", "decoratedgraphs.jl")) -include(joinpath("Graphs", "partitionedgraphs", "abstractpartitionedge.jl")) -include(joinpath("Graphs", "partitionedgraphs", "abstractpartitionvertex.jl")) -include(joinpath("Graphs", "partitionedgraphs", "abstractpartitionedgraph.jl")) -include(joinpath("Graphs", "partitionedgraphs", "partitioning.jl")) -include(joinpath("Graphs", "partitionedgraphs", "partitionedge.jl")) -include(joinpath("Graphs", "partitionedgraphs", "partitionvertex.jl")) -include(joinpath("Graphs", "partitionedgraphs", "partitionedgraph.jl")) +include("lib/NamedGraphGenerators/src/NamedGraphGenerators.jl") +include("lib/PartitionedGraphs/src/PartitionedGraphs.jl") -# TODO: reexport Graphs.jl (except for `Graphs.contract`) -export NamedGraph, - NamedDiGraph, - NamedEdge, - PartitionedGraph, - PartitionEdge, - PartitionVertex, - Key, - ⊔, - named_binary_tree, - named_grid, - named_path_graph, - named_path_digraph, - # AbstractGraph - boundary_edges, - boundary_vertices, - child_vertices, - dijkstra_mst, - dijkstra_parents, - directed_graph, - edge_path, - inner_boundary_vertices, - is_leaf, - is_path_graph, - is_self_loop, - leaf_vertices, - outer_boundary_vertices, - permute_vertices, - parent_vertex, - subgraph, - symrcm, - symrcm_permute, - undirected_graph, - vertex_path, - vertextype, - # Graphs.jl - a_star, - adjacency_matrix, - center, - diameter, - dijkstra_shortest_paths, - dijkstra_tree, - disjoint_union, - eccentricity, - eccentricities, - incident_edges, - comb_tree, - named_comb_tree, - neighborhood, - neighborhood_dists, - neighbors, - nv, - partitioned_graph, - partitionedge, - partitionedges, - partitionvertex, - partitionvertices, - partitioned_vertices, - path_digraph, - path_graph, - periphery, - pre_order_dfs_vertices, - post_order_dfs_vertices, - post_order_dfs_edges, - radius, - rename_vertices, - degree, - degrees, - indegree, - indegrees, - is_tree, - outdegree, - outdegrees, - mincut_partitions, - steiner_tree, - unpartitioned_graph, - weights +export NamedGraph, NamedDiGraph, NamedEdge +using PackageExtensionCompat: @require_extensions function __init__() - @require KaHyPar = "2a6221f6-aa48-11e9-3542-2d9e0ef01880" include( - joinpath("requires", "kahypar.jl") - ) - @require Metis = "2679e427-3c69-5b7f-982b-ece356f1e94b" include( - joinpath("requires", "metis.jl") - ) + @require_extensions +end end - -end # module AbstractNamedGraphs diff --git a/src/abstractnamededge.jl b/src/abstractnamededge.jl index 70fe72e..1c3d88b 100644 --- a/src/abstractnamededge.jl +++ b/src/abstractnamededge.jl @@ -1,38 +1,54 @@ +using Graphs: Graphs, AbstractEdge, dst, src +using .GraphsExtensions: GraphsExtensions, convert_vertextype, rename_vertices + abstract type AbstractNamedEdge{V} <: AbstractEdge{V} end -eltype(::Type{<:AbstractNamedEdge{V}}) where {V} = V +Base.eltype(::Type{<:AbstractNamedEdge{V}}) where {V} = V -src(e::AbstractNamedEdge) = not_implemented() -dst(e::AbstractNamedEdge) = not_implemented() +Graphs.src(e::AbstractNamedEdge) = not_implemented() +Graphs.dst(e::AbstractNamedEdge) = not_implemented() -convert_vertextype(::Type{V}, E::Type{<:AbstractNamedEdge{V}}) where {V} = E -convert_vertextype(::Type, E::Type{<:AbstractNamedEdge}) = not_implemented() +function GraphsExtensions.convert_vertextype( + ::Type{V}, E::Type{<:AbstractNamedEdge{V}} +) where {V} + return E +end +function GraphsExtensions.convert_vertextype(::Type, E::Type{<:AbstractNamedEdge}) + return not_implemented() +end -function show(io::IO, mime::MIME"text/plain", e::AbstractNamedEdge) +function Base.show(io::IO, mime::MIME"text/plain", e::AbstractNamedEdge) show(io, src(e)) print(io, " => ") show(io, dst(e)) return nothing end -show(io::IO, edge::AbstractNamedEdge) = show(io, MIME"text/plain"(), edge) +Base.show(io::IO, edge::AbstractNamedEdge) = show(io, MIME"text/plain"(), edge) # Conversions -Pair(e::AbstractNamedEdge) = Pair(src(e), dst(e)) -Tuple(e::AbstractNamedEdge) = (src(e), dst(e)) +Base.Pair(e::AbstractNamedEdge) = Pair(src(e), dst(e)) +Base.Tuple(e::AbstractNamedEdge) = (src(e), dst(e)) # Convenience functions -reverse(e::AbstractNamedEdge) = typeof(e)(dst(e), src(e)) -function ==(e1::AbstractNamedEdge, e2::AbstractNamedEdge) +Base.reverse(e::AbstractNamedEdge) = typeof(e)(dst(e), src(e)) +function Base.:(==)(e1::AbstractNamedEdge, e2::AbstractNamedEdge) return (src(e1) == src(e2) && dst(e1) == dst(e2)) end -hash(e::AbstractNamedEdge, h::UInt) = hash(src(e), hash(dst(e), h)) +Base.hash(e::AbstractNamedEdge, h::UInt) = hash(src(e), hash(dst(e), h)) +# TODO: Define generic version in `GraphsExtensions`. +# TODO: Define generic `set_vertices` in `GraphsExtensions`. set_src(e::AbstractNamedEdge, src) = set_vertices(e, src, dst(e)) +# TODO: Define generic version in `GraphsExtensions`. +# TODO: Define generic `set_vertices` in `GraphsExtensions`. set_dst(e::AbstractNamedEdge, dst) = set_vertices(e, src(e), dst) -rename_vertices(f::Function, e::AbstractNamedEdge) = set_vertices(e, f(src(e)), f(dst(e))) +function GraphsExtensions.rename_vertices(f::Function, e::AbstractNamedEdge) + # TODO: Define generic `set_vertices` in `GraphsExtensions`. + return set_vertices(e, f(src(e)), f(dst(e))) +end -function rename_vertices(e::AbstractNamedEdge, name_map) +function GraphsExtensions.rename_vertices(e::AbstractNamedEdge, name_map) return rename_vertices(v -> name_map[v], e) end diff --git a/src/abstractnamedgraph.jl b/src/abstractnamedgraph.jl index df82183..581f6f3 100644 --- a/src/abstractnamedgraph.jl +++ b/src/abstractnamedgraph.jl @@ -1,10 +1,49 @@ +using Dictionaries: set! +using Graphs: + Graphs, + AbstractGraph, + IsDirected, + a_star, + add_edge!, + adjacency_matrix, + bfs_parents, + boruvka_mst, + connected_components, + degree, + edges, + has_path, + indegree, + inneighbors, + is_connected, + is_cyclic, + kruskal_mst, + ne, + neighborhood, + neighborhood_dists, + nv, + outdegree, + prim_mst, + rem_edge!, + spfa_shortest_paths, + vertices, + weights +using Graphs.SimpleGraphs: SimpleDiGraph, SimpleEdge +using .GraphsExtensions: + GraphsExtensions, + directed_graph, + incident_edges, + partitioned_vertices, + rename_vertices, + subgraph +using SimpleTraits: SimpleTraits, @traitfn + abstract type AbstractNamedGraph{V} <: AbstractGraph{V} end # # Required for interface # -vertices(graph::AbstractNamedGraph) = not_implemented() +Graphs.vertices(graph::AbstractNamedGraph) = not_implemented() parent_graph(graph::AbstractNamedGraph) = not_implemented() # TODO: Require this for the interface, or implement as: @@ -12,10 +51,14 @@ parent_graph(graph::AbstractNamedGraph) = not_implemented() # ? parent_graph_type(graph::AbstractNamedGraph) = not_implemented() -rem_vertex!(graph::AbstractNamedGraph, vertex) = not_implemented() -add_vertex!(graph::AbstractNamedGraph, vertex) = not_implemented() +Graphs.rem_vertex!(graph::AbstractNamedGraph, vertex) = not_implemented() +Graphs.add_vertex!(graph::AbstractNamedGraph, vertex) = not_implemented() + +GraphsExtensions.rename_vertices(f::Function, g::AbstractNamedGraph) = not_implemented() -rename_vertices(f::Function, g::AbstractNamedGraph) = not_implemented() +function GraphsExtensions.permute_vertices(graph::AbstractNamedGraph, permutation) + return subgraph(graph, parent_vertices_to_vertices(graph, permutation)) +end # Convert vertex to parent vertex # Inverse map of `parent_vertex_to_vertex`. @@ -25,15 +68,16 @@ vertex_to_parent_vertex(graph::AbstractNamedGraph, vertex) = not_implemented() # Use `vertices`, assumes `vertices` is indexed by a parent vertex (a Vector for linear indexed parent vertices, a dictionary in general). parent_vertex_to_vertex(graph::AbstractNamedGraph, parent_vertex) = not_implemented() -# TODO: rename `edge_type`? -edgetype(graph::AbstractNamedGraph) = not_implemented() -directed_graph_type(G::Type{<:AbstractNamedGraph}) = not_implemented() -undirected_graph_type(G::Type{<:AbstractNamedGraph}) = not_implemented() +Graphs.edgetype(graph::AbstractNamedGraph) = not_implemented() + +# TODO: Define generic version in `GraphsExtensions`. +GraphsExtensions.directed_graph_type(G::Type{<:AbstractNamedGraph}) = not_implemented() +GraphsExtensions.undirected_graph_type(G::Type{<:AbstractNamedGraph}) = not_implemented() # In terms of `parent_graph_type` # is_directed(::Type{<:AbstractNamedGraph}) = not_implemented() -convert_vertextype(::Type, ::AbstractNamedGraph) = not_implemented() +GraphsExtensions.convert_vertextype(::Type, ::AbstractNamedGraph) = not_implemented() # TODO: implement as: # @@ -46,16 +90,9 @@ convert_vertextype(::Type, ::AbstractNamedGraph) = not_implemented() # for e in edges(graph) # add_edge!(graph_copy, e) # end -copy(graph::AbstractNamedGraph) = not_implemented() +Base.copy(graph::AbstractNamedGraph) = not_implemented() -# This should be overloaded for multi-dimensional indexing. -# Get the subset of vertices of the graph, for example -# for an input slice `subvertices(graph, "X", :)`. -function subvertices(graph::AbstractNamedGraph, vertices) - return not_implemented() -end - -function merge_vertices!( +function Graphs.merge_vertices!( graph::AbstractNamedGraph, merge_vertices; merged_vertex=first(merge_vertices) ) return not_implemented() @@ -81,10 +118,10 @@ function parent_vertex_to_vertex(graph::AbstractNamedGraph) return Base.Fix1(parent_vertex_to_vertex, graph) end -zero(G::Type{<:AbstractNamedGraph}) = G() +Base.zero(G::Type{<:AbstractNamedGraph}) = G() # TODO: Implement using `copyto!`? -function directed_graph(graph::AbstractNamedGraph) +function GraphsExtensions.directed_graph(graph::AbstractNamedGraph) digraph = directed_graph_type(typeof(graph))(vertices(graph)) for e in edges(graph) add_edge!(digraph, e) @@ -94,14 +131,10 @@ function directed_graph(graph::AbstractNamedGraph) end # Default, can overload -eltype(graph::AbstractNamedGraph) = eltype(vertices(graph)) +Base.eltype(graph::AbstractNamedGraph) = eltype(vertices(graph)) parent_eltype(graph::AbstractNamedGraph) = eltype(parent_graph(graph)) -function subvertices(graph::AbstractNamedGraph{V}, vertices::Vector{V}) where {V} - return vertices -end - function vertices_to_parent_vertices(graph::AbstractNamedGraph, vertices) return map(vertex_to_parent_vertex(graph), vertices) end @@ -154,12 +187,17 @@ function parent_edges_to_edges(graph::AbstractNamedGraph, parent_edges) return map(parent_edge_to_edge(graph), parent_edges) end -function edges(graph::AbstractNamedGraph) +function Graphs.edges(graph::AbstractNamedGraph) return parent_edges_to_edges(graph, parent_edges(graph)) end # TODO: write in terms of a generic function. -for f in [:outneighbors, :inneighbors, :all_neighbors, :neighbors] +for f in [ + :(Graphs.outneighbors), + :(Graphs.inneighbors), + :(Graphs.all_neighbors), + :(Graphs.neighbors), +] @eval begin function $f(graph::AbstractNamedGraph, vertex) parent_vertices = $f(parent_graph(graph), vertex_to_parent_vertex(graph, vertex)) @@ -174,33 +212,42 @@ for f in [:outneighbors, :inneighbors, :all_neighbors, :neighbors] end end -common_neighbors(g::AbstractNamedGraph, u, v) = intersect(neighbors(g, u), neighbors(g, v)) +function Graphs.common_neighbors(g::AbstractNamedGraph, u, v) + return intersect(neighbors(g, u), neighbors(g, v)) +end -_indegree(graph::AbstractNamedGraph, vertex) = length(inneighbors(graph, vertex)) -_outdegree(graph::AbstractNamedGraph, vertex) = length(outneighbors(graph, vertex)) +namedgraph_indegree(graph::AbstractNamedGraph, vertex) = length(inneighbors(graph, vertex)) +function namedgraph_outdegree(graph::AbstractNamedGraph, vertex) + return length(outneighbors(graph, vertex)) +end -indegree(graph::AbstractNamedGraph, vertex) = _indegree(graph, vertex) -outdegree(graph::AbstractNamedGraph, vertex) = _outdegree(graph, vertex) +Graphs.indegree(graph::AbstractNamedGraph, vertex) = namedgraph_indegree(graph, vertex) +Graphs.outdegree(graph::AbstractNamedGraph, vertex) = namedgraph_outdegree(graph, vertex) # Fix for ambiguity error with `AbstractGraph` version -indegree(graph::AbstractNamedGraph, vertex::Integer) = _indegree(graph, vertex) -outdegree(graph::AbstractNamedGraph, vertex::Integer) = _outdegree(graph, vertex) +function Graphs.indegree(graph::AbstractNamedGraph, vertex::Integer) + return namedgraph_indegree(graph, vertex) +end +function Graphs.outdegree(graph::AbstractNamedGraph, vertex::Integer) + return namedgraph_outdegree(graph, vertex) +end -@traitfn function _degree(graph::AbstractNamedGraph::IsDirected, vertex) +@traitfn function namedgraph_degree(graph::AbstractNamedGraph::IsDirected, vertex) return indegree(graph, vertex) + outdegree(graph, vertex) end -@traitfn _degree(graph::AbstractNamedGraph::(!IsDirected), vertex) = indegree(graph, vertex) +@traitfn namedgraph_degree(graph::AbstractNamedGraph::(!IsDirected), vertex) = + indegree(graph, vertex) -function degree(graph::AbstractNamedGraph, vertex) - return _degree(graph::AbstractNamedGraph, vertex) +function Graphs.degree(graph::AbstractNamedGraph, vertex) + return namedgraph_degree(graph::AbstractNamedGraph, vertex) end # Fix for ambiguity error with `AbstractGraph` version -function degree(graph::AbstractNamedGraph, vertex::Integer) - return _degree(graph::AbstractNamedGraph, vertex) +function Graphs.degree(graph::AbstractNamedGraph, vertex::Integer) + return namedgraph_degree(graph::AbstractNamedGraph, vertex) end -function degree_histogram(g::AbstractNamedGraph, degfn=degree) +function Graphs.degree_histogram(g::AbstractNamedGraph, degfn=degree) hist = Dictionary{Int,Int}() for v in vertices(g) # minimize allocations by for d in degfn(g, v) # iterating over vertices @@ -210,7 +257,7 @@ function degree_histogram(g::AbstractNamedGraph, degfn=degree) return hist end -function _neighborhood( +function namedgraph_neighborhood( graph::AbstractNamedGraph, vertex, d, distmx=weights(graph); dir=:out ) parent_distmx = dist_matrix_to_parent_dist_matrix(graph, distmx) @@ -222,25 +269,27 @@ function _neighborhood( ] end -function neighborhood(graph::AbstractNamedGraph, vertex, d, distmx=weights(graph); dir=:out) - return _neighborhood(graph, vertex, d, distmx; dir) +function Graphs.neighborhood( + graph::AbstractNamedGraph, vertex, d, distmx=weights(graph); dir=:out +) + return namedgraph_neighborhood(graph, vertex, d, distmx; dir) end # Fix for ambiguity error with `AbstractGraph` version -function neighborhood( +function Graphs.neighborhood( graph::AbstractNamedGraph, vertex::Integer, d, distmx=weights(graph); dir=:out ) - return _neighborhood(graph, vertex, d, distmx; dir) + return namedgraph_neighborhood(graph, vertex, d, distmx; dir) end # Fix for ambiguity error with `AbstractGraph` version -function neighborhood( +function Graphs.neighborhood( graph::AbstractNamedGraph, vertex::Integer, d, distmx::AbstractMatrix{<:Real}; dir=:out ) - return _neighborhood(graph, vertex, d, distmx; dir) + return namedgraph_neighborhood(graph, vertex, d, distmx; dir) end -function _neighborhood_dists(graph::AbstractNamedGraph, vertex, d, distmx; dir) +function namedgraph_neighborhood_dists(graph::AbstractNamedGraph, vertex, d, distmx; dir) parent_distmx = dist_matrix_to_parent_dist_matrix(graph, distmx) parent_vertices_and_dists = neighborhood_dists( parent_graph(graph), vertex_to_parent_vertex(graph, vertex), d, parent_distmx; dir @@ -251,83 +300,58 @@ function _neighborhood_dists(graph::AbstractNamedGraph, vertex, d, distmx; dir) ] end -function neighborhood_dists( +function Graphs.neighborhood_dists( graph::AbstractNamedGraph, vertex, d, distmx=weights(graph); dir=:out ) - return _neighborhood_dists(graph, vertex, d, distmx; dir) + return namedgraph_neighborhood_dists(graph, vertex, d, distmx; dir) end # Fix for ambiguity error with `AbstractGraph` version -function neighborhood_dists( +function Graphs.neighborhood_dists( graph::AbstractNamedGraph, vertex::Integer, d, distmx=weights(graph); dir=:out ) - return _neighborhood_dists(graph, vertex, d, distmx; dir) + return namedgraph_neighborhood_dists(graph, vertex, d, distmx; dir) end # Fix for ambiguity error with `AbstractGraph` version -function neighborhood_dists( +function Graphs.neighborhood_dists( graph::AbstractNamedGraph, vertex::Integer, d, distmx::AbstractMatrix{<:Real}; dir=:out ) - return _neighborhood_dists(graph, vertex, d, distmx; dir) + return namedgraph_neighborhood_dists(graph, vertex, d, distmx; dir) end -function _mincut(graph::AbstractNamedGraph, distmx) +function namedgraph_mincut(graph::AbstractNamedGraph, distmx) parent_distmx = dist_matrix_to_parent_dist_matrix(graph, distmx) - parent_parity, bestcut = mincut(parent_graph(graph), parent_distmx) + parent_parity, bestcut = Graphs.mincut(parent_graph(graph), parent_distmx) return Dictionary(vertices(graph), parent_parity), bestcut end -function mincut(graph::AbstractNamedGraph, distmx=weights(graph)) - return _mincut(graph, distmx) +function Graphs.mincut(graph::AbstractNamedGraph, distmx=weights(graph)) + return namedgraph_mincut(graph, distmx) end -function mincut(graph::AbstractNamedGraph, distmx::AbstractMatrix{<:Real}) - return _mincut(graph, distmx) +function Graphs.mincut(graph::AbstractNamedGraph, distmx::AbstractMatrix{<:Real}) + return namedgraph_mincut(graph, distmx) end -@traitfn function GraphsFlows.mincut( - graph::AbstractNamedGraph::IsDirected, - source, - target, - capacity_matrix=DefaultNamedCapacity(graph), - algorithm::GraphsFlows.AbstractFlowAlgorithm=PushRelabelAlgorithm(), -) - parent_part1, parent_part2, flow = GraphsFlows.mincut( - directed_graph(parent_graph(graph)), - vertex_to_parent_vertex(graph, source), - vertex_to_parent_vertex(graph, target), - dist_matrix_to_parent_dist_matrix(graph, capacity_matrix), - algorithm, - ) - part1 = parent_vertices_to_vertices(graph, parent_part1) - part2 = parent_vertices_to_vertices(graph, parent_part2) - return (part1, part2, flow) -end - -@traitfn function GraphsFlows.mincut( - graph::AbstractNamedGraph::(!IsDirected), - source, - target, - capacity_matrix=DefaultNamedCapacity(graph), - algorithm::GraphsFlows.AbstractFlowAlgorithm=PushRelabelAlgorithm(), +# TODO: Make this more generic? +function GraphsExtensions.partitioned_vertices( + g::AbstractNamedGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... ) - return GraphsFlows.mincut( - directed_graph(graph), source, target, _symmetrize(capacity_matrix), algorithm + vertex_partitions = partitioned_vertices( + parent_graph(g); npartitions, nvertices_per_partition, kwargs... ) + #[inv(vertex_to_parent_vertex(g))[v] for v in partitions] + # TODO: output the reverse of this dictionary (a Vector of Vector + # of the vertices in each partition). + # return Dictionary(vertices(g), partitions) + return [ + parent_vertices_to_vertices(g, vertex_partition) for + vertex_partition in vertex_partitions + ] end -function mincut_partitions( - graph::AbstractGraph, - source, - target, - capacity_matrix=DefaultNamedCapacity(graph), - algorithm::GraphsFlows.AbstractFlowAlgorithm=PushRelabelAlgorithm(), -) - part1, part2, flow = GraphsFlows.mincut(graph, source, target, capacity_matrix, algorithm) - return part1, part2 -end - -function _a_star( +function namedgraph_a_star( graph::AbstractNamedGraph, source, destination, @@ -347,18 +371,20 @@ function _a_star( return parent_edges_to_edges(graph, parent_shortest_path) end -function a_star(graph::AbstractNamedGraph, source, destination, args...) - return _a_star(graph, source, destination, args...) +function Graphs.a_star(graph::AbstractNamedGraph, source, destination, args...) + return namedgraph_a_star(graph, source, destination, args...) end # Fix ambiguity error with `AbstractGraph` version -function a_star( +function Graphs.a_star( graph::AbstractNamedGraph{U}, source::Integer, destination::Integer, args... ) where {U<:Integer} - return _a_star(graph, source, destination, args...) + return namedgraph_a_star(graph, source, destination, args...) end -function spfa_shortest_paths(graph::AbstractNamedGraph, vertex, distmx=weights(graph)) +function Graphs.spfa_shortest_paths( + graph::AbstractNamedGraph, vertex, distmx=weights(graph) +) parent_distmx = dist_matrix_to_parent_dist_matrix(graph, distmx) parent_shortest_paths = spfa_shortest_paths( parent_graph(graph), vertex_to_parent_vertex(graph, vertex), parent_distmx @@ -366,48 +392,48 @@ function spfa_shortest_paths(graph::AbstractNamedGraph, vertex, distmx=weights(g return Dictionary(vertices(graph), parent_shortest_paths) end -function boruvka_mst( +function Graphs.boruvka_mst( g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real}=weights(g); minimize=true ) parent_mst, weights = boruvka_mst(parent_graph(g), distmx; minimize) return parent_edges_to_edges(g, parent_mst), weights end -function kruskal_mst( +function Graphs.kruskal_mst( g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real}=weights(g); minimize=true ) parent_mst = kruskal_mst(parent_graph(g), distmx; minimize) return parent_edges_to_edges(g, parent_mst) end -function prim_mst(g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real}=weights(g)) +function Graphs.prim_mst(g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real}=weights(g)) parent_mst = prim_mst(parent_graph(g), distmx) return parent_edges_to_edges(g, parent_mst) end -function add_edge!(graph::AbstractNamedGraph, edge::AbstractEdge) +function Graphs.add_edge!(graph::AbstractNamedGraph, edge::AbstractEdge) add_edge!(parent_graph(graph), edge_to_parent_edge(graph, edge)) return graph end # handles single-argument edge constructors such as pairs and tuples -add_edge!(g::AbstractNamedGraph, edge) = add_edge!(g, edgetype(g)(edge)) -add_edge!(g::AbstractNamedGraph, src, dst) = add_edge!(g, edgetype(g)(src, dst)) +Graphs.add_edge!(g::AbstractNamedGraph, edge) = add_edge!(g, edgetype(g)(edge)) +Graphs.add_edge!(g::AbstractNamedGraph, src, dst) = add_edge!(g, edgetype(g)(src, dst)) -function rem_edge!(graph::AbstractNamedGraph, edge) +function Graphs.rem_edge!(graph::AbstractNamedGraph, edge) rem_edge!(parent_graph(graph), edge_to_parent_edge(graph, edge)) return graph end -function has_edge(graph::AbstractNamedGraph, edge::AbstractNamedEdge) +function Graphs.has_edge(graph::AbstractNamedGraph, edge::AbstractNamedEdge) return has_edge(parent_graph(graph), edge_to_parent_edge(graph, edge)) end # handles two-argument edge constructors like src,dst -has_edge(g::AbstractNamedGraph, edge) = has_edge(g, edgetype(g)(edge)) -has_edge(g::AbstractNamedGraph, src, dst) = has_edge(g, edgetype(g)(src, dst)) +Graphs.has_edge(g::AbstractNamedGraph, edge) = has_edge(g, edgetype(g)(edge)) +Graphs.has_edge(g::AbstractNamedGraph, src, dst) = has_edge(g, edgetype(g)(src, dst)) -function has_path( +function Graphs.has_path( graph::AbstractNamedGraph, source, destination; exclude_vertices=vertextype(graph)[] ) return has_path( @@ -418,7 +444,7 @@ function has_path( ) end -function union(graph1::AbstractNamedGraph, graph2::AbstractNamedGraph) +function Base.union(graph1::AbstractNamedGraph, graph2::AbstractNamedGraph) union_graph = promote_type(typeof(graph1), typeof(graph2))() union_vertices = union(vertices(graph1), vertices(graph2)) for v in union_vertices @@ -433,7 +459,7 @@ function union(graph1::AbstractNamedGraph, graph2::AbstractNamedGraph) return union_graph end -function union( +function Base.union( graph1::AbstractNamedGraph, graph2::AbstractNamedGraph, graph3::AbstractNamedGraph, @@ -442,26 +468,26 @@ function union( return union(union(graph1, graph2), graph3, graph_rest...) end -is_directed(G::Type{<:AbstractNamedGraph}) = is_directed(parent_graph_type(G)) +Graphs.is_directed(G::Type{<:AbstractNamedGraph}) = is_directed(parent_graph_type(G)) -is_directed(graph::AbstractNamedGraph) = is_directed(parent_graph(graph)) +Graphs.is_directed(graph::AbstractNamedGraph) = is_directed(parent_graph(graph)) -is_connected(graph::AbstractNamedGraph) = is_connected(parent_graph(graph)) +Graphs.is_connected(graph::AbstractNamedGraph) = is_connected(parent_graph(graph)) -is_cyclic(graph::AbstractNamedGraph) = is_cyclic(parent_graph(graph)) +Graphs.is_cyclic(graph::AbstractNamedGraph) = is_cyclic(parent_graph(graph)) -@traitfn function reverse(graph::AbstractNamedGraph::IsDirected) +@traitfn function Base.reverse(graph::AbstractNamedGraph::IsDirected) reversed_parent_graph = reverse(parent_graph(graph)) return h end -@traitfn function reverse!(g::AbstractNamedGraph::IsDirected) +@traitfn function Base.reverse!(g::AbstractNamedGraph::IsDirected) g.fadjlist, g.badjlist = g.badjlist, g.fadjlist return g end -# TODO: Move to namedgraph.jl, or make the output generic? -function blockdiag(graph1::AbstractNamedGraph, graph2::AbstractNamedGraph) +# TODO: Move to `namedgraph.jl`, or make the output generic? +function Graphs.blockdiag(graph1::AbstractNamedGraph, graph2::AbstractNamedGraph) new_parent_graph = blockdiag(parent_graph(graph1), parent_graph(graph2)) new_vertices = vcat(vertices(graph1), vertices(graph2)) @assert allunique(new_vertices) @@ -469,20 +495,20 @@ function blockdiag(graph1::AbstractNamedGraph, graph2::AbstractNamedGraph) end # TODO: What `args` are needed? -nv(graph::AbstractNamedGraph, args...) = nv(parent_graph(graph), args...) +Graphs.nv(graph::AbstractNamedGraph, args...) = nv(parent_graph(graph), args...) # TODO: What `args` are needed? -ne(graph::AbstractNamedGraph, args...) = ne(parent_graph(graph), args...) +Graphs.ne(graph::AbstractNamedGraph, args...) = ne(parent_graph(graph), args...) # TODO: What `args` are needed? -function adjacency_matrix(graph::AbstractNamedGraph, args...) +function Graphs.adjacency_matrix(graph::AbstractNamedGraph, args...) return adjacency_matrix(parent_graph(graph), args...) end -function connected_components(graph::AbstractNamedGraph) +function Graphs.connected_components(graph::AbstractNamedGraph) parent_connected_components = connected_components(parent_graph(graph)) return map(parent_vertices_to_vertices(graph), parent_connected_components) end -function merge_vertices( +function Graphs.merge_vertices( graph::AbstractNamedGraph, merge_vertices; merged_vertex=first(merge_vertices) ) merged_graph = copy(graph) @@ -509,7 +535,7 @@ end # Overload Graphs.tree. Used for bfs_tree and dfs_tree # traversal algorithms. -function tree(graph::AbstractNamedGraph, parents) +function Graphs.tree(graph::AbstractNamedGraph, parents) n = length(parents) # TODO: Use `directed_graph` here to make more generic? ## t = GenericNamedGraph(DiGraph(n), vertices(graph)) @@ -524,13 +550,13 @@ function tree(graph::AbstractNamedGraph, parents) end function namedgraph_bfs_tree(graph::AbstractNamedGraph, vertex; kwargs...) - return tree(graph, bfs_parents(graph, vertex; kwargs...)) + return Graphs.tree(graph, bfs_parents(graph, vertex; kwargs...)) end # Disambiguation from Graphs.bfs_tree -function bfs_tree(graph::AbstractNamedGraph, vertex::Integer; kwargs...) +function Graphs.bfs_tree(graph::AbstractNamedGraph, vertex::Integer; kwargs...) return namedgraph_bfs_tree(graph, vertex; kwargs...) end -function bfs_tree(graph::AbstractNamedGraph, vertex; kwargs...) +function Graphs.bfs_tree(graph::AbstractNamedGraph, vertex; kwargs...) return namedgraph_bfs_tree(graph, vertex; kwargs...) end @@ -549,11 +575,11 @@ function namedgraph_bfs_parents(graph::AbstractNamedGraph, vertex; kwargs...) vertices_graph = parent_vertices_to_vertices(graph, parent_vertices(graph)) return Dictionary(vertices_graph, parent_vertices_to_vertices(graph, parent_bfs_parents)) end -# Disambiguation from Graphs.bfs_tree -function bfs_parents(graph::AbstractNamedGraph, vertex::Integer; kwargs...) +# Disambiguation from Graphs.jl +function Graphs.bfs_parents(graph::AbstractNamedGraph, vertex::Integer; kwargs...) return namedgraph_bfs_parents(graph, vertex; kwargs...) end -function bfs_parents(graph::AbstractNamedGraph, vertex; kwargs...) +function Graphs.bfs_parents(graph::AbstractNamedGraph, vertex; kwargs...) return namedgraph_bfs_parents(graph, vertex; kwargs...) end @@ -561,7 +587,7 @@ end # Printing # -function show(io::IO, mime::MIME"text/plain", graph::AbstractNamedGraph) +function Base.show(io::IO, mime::MIME"text/plain", graph::AbstractNamedGraph) println(io, "$(typeof(graph)) with $(nv(graph)) vertices:") show(io, mime, vertices(graph)) println(io, "\n") @@ -573,13 +599,13 @@ function show(io::IO, mime::MIME"text/plain", graph::AbstractNamedGraph) return nothing end -show(io::IO, graph::AbstractNamedGraph) = show(io, MIME"text/plain"(), graph) +Base.show(io::IO, graph::AbstractNamedGraph) = show(io, MIME"text/plain"(), graph) # # Convenience functions # -function (g1::AbstractNamedGraph == g2::AbstractNamedGraph) +function Base.:(==)(g1::AbstractNamedGraph, g2::AbstractNamedGraph) issetequal(vertices(g1), vertices(g2)) || return false for v in vertices(g1) issetequal(inneighbors(g1, v), inneighbors(g2, v)) || return false diff --git a/src/Graphs/generators/decoratedgraphs.jl b/src/decorate.jl similarity index 60% rename from src/Graphs/generators/decoratedgraphs.jl rename to src/decorate.jl index ac357d1..d8f9468 100644 --- a/src/Graphs/generators/decoratedgraphs.jl +++ b/src/decorate.jl @@ -1,4 +1,10 @@ -function decorate_graph_edges(g::NamedGraph; edge_map::Function=e -> named_grid((1,))) +using Graphs: add_edge!, dst, edges, neighbors, rem_edge!, rem_vertex!, src, vertices +using Graphs.SimpleGraphs: SimpleGraph +using .GraphsExtensions: GraphsExtensions, add_edges! + +function GraphsExtensions.decorate_graph_edges( + g::AbstractNamedGraph; edge_map::Function=Returns(SimpleGraph(1)) +) g_dec = copy(g) es = edges(g_dec) for e in es @@ -12,7 +18,9 @@ function decorate_graph_edges(g::NamedGraph; edge_map::Function=e -> named_grid( return g_dec end -function decorate_graph_vertices(g::NamedGraph; vertex_map::Function=v -> named_grid((1,))) +function GraphsExtensions.decorate_graph_vertices( + g::AbstractNamedGraph; vertex_map::Function=Returns(SimpleGraph(1)) +) g_dec = copy(g) vs = vertices(g_dec) for v in vs diff --git a/src/traversals/dfs.jl b/src/dfs.jl similarity index 67% rename from src/traversals/dfs.jl rename to src/dfs.jl index 84b1b9d..268c629 100644 --- a/src/traversals/dfs.jl +++ b/src/dfs.jl @@ -1,14 +1,17 @@ -@traitfn function topological_sort_by_dfs(g::AbstractNamedGraph::IsDirected) +using Graphs: Graphs, dfs_parents, dfs_tree, topological_sort_by_dfs +using SimpleTraits: SimpleTraits, @traitfn + +@traitfn function Graphs.topological_sort_by_dfs(g::AbstractNamedGraph::IsDirected) return parent_vertices_to_vertices(g, topological_sort_by_dfs(parent_graph(g))) end function namedgraph_dfs_tree(graph::AbstractNamedGraph, vertex; kwargs...) - return tree(graph, dfs_parents(graph, vertex; kwargs...)) + return Graphs.tree(graph, dfs_parents(graph, vertex; kwargs...)) end -function dfs_tree(graph::AbstractNamedGraph, vertex::Integer; kwargs...) +function Graphs.dfs_tree(graph::AbstractNamedGraph, vertex::Integer; kwargs...) return namedgraph_dfs_tree(graph, vertex; kwargs...) end -function dfs_tree(graph::AbstractNamedGraph, vertex; kwargs...) +function Graphs.dfs_tree(graph::AbstractNamedGraph, vertex; kwargs...) return namedgraph_dfs_tree(graph, vertex; kwargs...) end @@ -27,10 +30,10 @@ function namedgraph_dfs_parents(graph::AbstractNamedGraph, vertex; kwargs...) vertices_graph = parent_vertices_to_vertices(graph, parent_vertices(graph)) return Dictionary(vertices_graph, parent_vertices_to_vertices(graph, parent_dfs_parents)) end -# Disambiguation from Graphs.dfs_tree -function dfs_parents(graph::AbstractNamedGraph, vertex::Integer; kwargs...) +# Disambiguation from Graphs.dfs_parents +function Graphs.dfs_parents(graph::AbstractNamedGraph, vertex::Integer; kwargs...) return namedgraph_dfs_parents(graph, vertex; kwargs...) end -function dfs_parents(graph::AbstractNamedGraph, vertex; kwargs...) +function Graphs.dfs_parents(graph::AbstractNamedGraph, vertex; kwargs...) return namedgraph_dfs_parents(graph, vertex; kwargs...) end diff --git a/src/distance.jl b/src/distance.jl index f37ed20..413f7ae 100644 --- a/src/distance.jl +++ b/src/distance.jl @@ -1,30 +1,25 @@ -function _eccentricity(graph::AbstractNamedGraph, vertex, distmx) +using Graphs: Graphs, dijkstra_shortest_paths, weights +using .GraphsExtensions: eccentricities + +function namedgraph_eccentricity(graph::AbstractNamedGraph, vertex, distmx) e = maximum(dijkstra_shortest_paths(graph, [vertex], distmx).dists) e == typemax(e) && @warn("Infinite path length detected for vertex $vertex") return e end -function eccentricity(graph::AbstractNamedGraph, vertex, distmx=weights(graph)) - return _eccentricity(graph, vertex, distmx) +function Graphs.eccentricity(graph::AbstractNamedGraph, vertex, distmx=weights(graph)) + return namedgraph_eccentricity(graph, vertex, distmx) end # Fix for ambiguity error with `AbstractGraph` -function eccentricity( +function Graphs.eccentricity( graph::AbstractNamedGraph, vertex::Integer, distmx::AbstractMatrix{<:Real} ) - return _eccentricity(graph, vertex, distmx) -end - -function eccentricity(graph::AbstractNamedGraph, vertex, distmx::AbstractMatrix) - return _eccentricity(graph, vertex, distmx) + return namedgraph_eccentricity(graph, vertex, distmx) end -# function eccentricity(graph::AbstractNamedGraph, ::AbstractMatrix) - -eccentricities(graph::AbstractGraph) = eccentricities(graph, vertices(graph)) - -function eccentricities(graph::AbstractGraph, vs, distmx=weights(graph)) - return map(vertex -> eccentricity(graph, vertex, distmx), vs) +function Graphs.eccentricity(graph::AbstractNamedGraph, vertex, distmx::AbstractMatrix) + return namedgraph_eccentricity(graph, vertex, distmx) end function eccentricities_center(eccentricities) @@ -38,54 +33,54 @@ end eccentricities_radius(eccentricities) = minimum(eccentricities) eccentricities_diameter(eccentricities) = maximum(eccentricities) -function _center(graph::AbstractNamedGraph, distmx) +function namedgraph_center(graph::AbstractNamedGraph, distmx) return eccentricities_center(eccentricities(graph, vertices(graph), distmx)) end -function center(graph::AbstractNamedGraph, distmx=weights(graph)) - return _center(graph, distmx) +function Graphs.center(graph::AbstractNamedGraph, distmx=weights(graph)) + return namedgraph_center(graph, distmx) end # Fix for ambiguity error with `AbstractGraph` -function center(graph::AbstractNamedGraph, distmx::AbstractMatrix) - return _center(graph, distmx) +function Graphs.center(graph::AbstractNamedGraph, distmx::AbstractMatrix) + return namedgraph_center(graph, distmx) end -function _radius(graph::AbstractNamedGraph, distmx) +function namedgraph_radius(graph::AbstractNamedGraph, distmx) return eccentricities_radius(eccentricities(graph, vertices(graph), distmx)) end -function radius(graph::AbstractNamedGraph, distmx=weights(graph)) - return _radius(graph, distmx) +function Graphs.radius(graph::AbstractNamedGraph, distmx=weights(graph)) + return namedgraph_radius(graph, distmx) end # Fix for ambiguity error with `AbstractGraph` -function radius(graph::AbstractNamedGraph, distmx::AbstractMatrix) - return _radius(graph, distmx) +function Graphs.radius(graph::AbstractNamedGraph, distmx::AbstractMatrix) + return namedgraph_radius(graph, distmx) end -function _diameter(graph::AbstractNamedGraph, distmx) +function namedgraph_diameter(graph::AbstractNamedGraph, distmx) return eccentricities_diameter(eccentricities(graph, vertices(graph), distmx)) end -function diameter(graph::AbstractNamedGraph, distmx=weights(graph)) - return _diameter(graph, distmx) +function Graphs.diameter(graph::AbstractNamedGraph, distmx=weights(graph)) + return namedgraph_diameter(graph, distmx) end # Fix for ambiguity error with `AbstractGraph` -function diameter(graph::AbstractNamedGraph, distmx::AbstractMatrix) - return _diameter(graph, distmx) +function Graphs.diameter(graph::AbstractNamedGraph, distmx::AbstractMatrix) + return namedgraph_diameter(graph, distmx) end -function _periphery(graph::AbstractNamedGraph, distmx) +function namedgraph_periphery(graph::AbstractNamedGraph, distmx) return eccentricities_periphery(eccentricities(graph, vertices(graph), distmx)) end -function periphery(graph::AbstractNamedGraph, distmx=weights(graph)) - return _periphery(graph, distmx) +function Graphs.periphery(graph::AbstractNamedGraph, distmx=weights(graph)) + return namedgraph_periphery(graph, distmx) end # Fix for ambiguity error with `AbstractGraph` -function periphery(graph::AbstractNamedGraph, distmx::AbstractMatrix) - return _periphery(graph, distmx) +function Graphs.periphery(graph::AbstractNamedGraph, distmx::AbstractMatrix) + return namedgraph_periphery(graph, distmx) end diff --git a/src/distances_and_capacities.jl b/src/distances_and_capacities.jl index ce408dc..e384123 100644 --- a/src/distances_and_capacities.jl +++ b/src/distances_and_capacities.jl @@ -1,7 +1,16 @@ +using Dictionaries: AbstractDictionary +using Graphs: Graphs, IsDirected, dst, edges, nv, src +using .GraphsExtensions: directed_graph +using LinearAlgebra: Symmetric +using SimpleTraits: SimpleTraits, @traitfn +using SparseArrays: sparse, spzeros + +# TODO: Move to `GraphsExtensions`. function _symmetrize(dist::AbstractMatrix) return sparse(Symmetric(dist)) end +# TODO: Move to `GraphsExtensions`. function _symmetrize(dist) symmetrized_dist = copy(dist) for k in keys(dist) @@ -10,6 +19,7 @@ function _symmetrize(dist) return symmetrized_dist end +# TODO: Move to `GraphsExtensions`. function _symmetrize(dist::AbstractDictionary) symmetrized_dist = copy(dist) for k in keys(dist) @@ -21,7 +31,9 @@ end getindex_dist_matrix(dist_matrix, I...) = dist_matrix[I...] getindex_dist_matrix(dist_matrix::AbstractDictionary, I...) = dist_matrix[I] -function _dist_matrix_to_parent_dist_matrix(graph::AbstractNamedGraph, dist_matrix) +function namedgraph_dist_matrix_to_parent_dist_matrix( + graph::AbstractNamedGraph, dist_matrix +) parent_dist_matrix = spzeros(valtype(dist_matrix), nv(graph), nv(graph)) for e in edges(graph) parent_e = edge_to_parent_edge(graph, e) @@ -35,13 +47,13 @@ end @traitfn function dist_matrix_to_parent_dist_matrix( graph::AbstractNamedGraph::IsDirected, dist_matrix ) - return _dist_matrix_to_parent_dist_matrix(graph, dist_matrix) + return namedgraph_dist_matrix_to_parent_dist_matrix(graph, dist_matrix) end @traitfn function dist_matrix_to_parent_dist_matrix( graph::AbstractNamedGraph::(!IsDirected), dist_matrix ) - return _symmetrize(_dist_matrix_to_parent_dist_matrix(graph, dist_matrix)) + return _symmetrize(namedgraph_dist_matrix_to_parent_dist_matrix(graph, dist_matrix)) end function dist_matrix_to_parent_dist_matrix( @@ -70,9 +82,3 @@ end # Base.size(d::DefaultNamedCapacity) = (Int(d.nv), Int(d.nv)) # Base.transpose(d::DefaultNamedCapacity) = DefaultNamedCapacity(reverse(d.flow_graph)) # Base.adjoint(d::DefaultNamedCapacity) = DefaultNamedCapacity(reverse(d.flow_graph)) - -@traitfn function dist_matrix_to_parent_dist_matrix( - graph::AbstractNamedGraph::IsDirected, dist_matrix::DefaultNamedCapacity -) - return GraphsFlows.DefaultCapacity(graph) -end diff --git a/src/generators/named_staticgraphs.jl b/src/generators/named_staticgraphs.jl deleted file mode 100644 index 1a2365b..0000000 --- a/src/generators/named_staticgraphs.jl +++ /dev/null @@ -1,92 +0,0 @@ -function parent(tree::SimpleDiGraph, v::Integer) - return only(inneighbors(tree, v)) -end - -function children(tree::SimpleDiGraph, v::Integer) - return outneighbors(tree, v) -end - -function set_named_vertices!( - named_vertices::Vector, - tree::SimpleDiGraph, - simple_parent::Integer, - named_parent; - child_name=identity, -) - simple_children = children(tree, simple_parent) - for n in 1:length(simple_children) - simple_child = simple_children[n] - named_child = (named_parent..., child_name(n)) - named_vertices[simple_child] = named_child - set_named_vertices!(named_vertices, tree, simple_child, named_child; child_name) - end - return named_vertices -end - -# TODO: Use vectors as vertex names? -# k = 3: -# 1 => (1,) -# 2 => (1, 1) -# 3 => (1, 2) -# 4 => (1, 1, 1) -# 5 => (1, 1, 2) -# 6 => (1, 2, 1) -# 7 => (1, 2, 2) -function named_bfs_tree_vertices( - simple_graph::SimpleGraph, source::Integer=1; source_name=1, child_name=identity -) - tree = bfs_tree(simple_graph, source) - named_vertices = Vector{Tuple}(undef, nv(simple_graph)) - named_source = (source_name,) - named_vertices[source] = named_source - set_named_vertices!(named_vertices, tree, source, named_source; child_name) - return named_vertices -end - -function named_bfs_tree( - simple_graph::SimpleGraph, source::Integer=1; source_name=1, child_name=identity -) - named_vertices = named_bfs_tree_vertices(simple_graph, source; source_name, child_name) - return NamedGraph(simple_graph, named_vertices) -end - -function named_binary_tree( - k::Integer, source::Integer=1; source_name=1, child_name=identity -) - simple_graph = binary_tree(k) - return named_bfs_tree(simple_graph, source; source_name, child_name) -end - -function named_path_graph(dim::Integer) - return NamedGraph(path_graph(dim)) -end - -function named_path_digraph(dim::Integer) - return NamedDiGraph(path_digraph(dim)) -end - -function named_grid(dim::Integer; kwargs...) - simple_graph = grid((dim,); kwargs...) - return NamedGraph(simple_graph) -end - -function named_grid(dims; kwargs...) - simple_graph = grid(dims; kwargs...) - return NamedGraph(simple_graph, Tuple.(CartesianIndices(Tuple(dims)))) -end - -function named_comb_tree(dims::Tuple) - simple_graph = comb_tree(dims) - return NamedGraph(simple_graph, Tuple.(CartesianIndices(Tuple(dims)))) -end - -function named_comb_tree(tooth_lengths::Vector{<:Integer}) - @assert all(>(0), tooth_lengths) - simple_graph = comb_tree(tooth_lengths) - nx = length(tooth_lengths) - ny = maximum(tooth_lengths) - vertices = filter(Tuple.(CartesianIndices((nx, ny)))) do (jx, jy) - jy <= tooth_lengths[jx] - end - return NamedGraph(simple_graph, vertices) -end diff --git a/src/Graphs/generators/staticgraphs.jl b/src/lib/GraphGenerators/src/GraphGenerators.jl similarity index 89% rename from src/Graphs/generators/staticgraphs.jl rename to src/lib/GraphGenerators/src/GraphGenerators.jl index 7d36371..e37d12e 100644 --- a/src/Graphs/generators/staticgraphs.jl +++ b/src/lib/GraphGenerators/src/GraphGenerators.jl @@ -1,3 +1,7 @@ +module GraphGenerators +using Dictionaries: Dictionary +using Graphs: SimpleGraph, add_edge! + function comb_tree(dims::Tuple) @assert length(dims) == 2 nx, ny = dims @@ -24,3 +28,4 @@ function comb_tree(tooth_lengths::Vector{<:Integer}) end return graph end +end diff --git a/src/lib/GraphsExtensions/src/GraphsExtensions.jl b/src/lib/GraphsExtensions/src/GraphsExtensions.jl new file mode 100644 index 0000000..93460cd --- /dev/null +++ b/src/lib/GraphsExtensions/src/GraphsExtensions.jl @@ -0,0 +1,9 @@ +module GraphsExtensions +include("abstractgraph.jl") +include("boundary.jl") +include("shortestpaths.jl") +include("symrcm.jl") +include("partitioning.jl") +include("trees_and_forests.jl") +include("simplegraph.jl") +end diff --git a/src/Graphs/abstractgraph.jl b/src/lib/GraphsExtensions/src/abstractgraph.jl similarity index 86% rename from src/Graphs/abstractgraph.jl rename to src/lib/GraphsExtensions/src/abstractgraph.jl index 394ecfa..18c1b75 100644 --- a/src/Graphs/abstractgraph.jl +++ b/src/lib/GraphsExtensions/src/abstractgraph.jl @@ -1,7 +1,44 @@ -directed_graph(::Type{<:AbstractGraph}) = error("Not implemented") -undirected_graph(::Type{<:AbstractGraph}) = error("Not implemented") +using AbstractTrees: AbstractTrees, PostOrderDFS, PreOrderDFS +using Dictionaries: Dictionary, Indices, dictionary +using Graphs: + Graphs, + AbstractEdge, + AbstractGraph, + IsDirected, + Δ, + add_edge!, + add_vertex!, + degree, + dfs_tree, + eccentricity, + edgetype, + indegree, + induced_subgraph, + inneighbors, + outdegree, + outneighbors, + ne, + neighbors, + nv, + rem_edge!, + rem_vertex!, + weights +using Graphs.SimpleGraphs: AbstractSimpleGraph +using SimpleTraits: SimpleTraits, @traitfn +using SplitApplyCombine: groupfind + +not_implemented() = error("Not implemented") + +is_self_loop(e::AbstractEdge) = src(e) == dst(e) +is_self_loop(e::Pair) = first(e) == last(e) + +directed_graph_type(::Type{<:AbstractGraph}) = not_implemented() +undirected_graph_type(::Type{<:AbstractGraph}) = not_implemented() # TODO: Implement generic version for `IsDirected` -# directed_graph(G::Type{IsDirected}) = G +# directed_graph_type(G::Type{IsDirected}) = G + +directed_graph_type(g::AbstractGraph) = directed_graph_type(typeof(g)) +undirected_graph_type(g::AbstractGraph) = undirected_graph_type(typeof(g)) @traitfn directed_graph(graph::::IsDirected) = graph @@ -12,7 +49,7 @@ end # TODO: Handle metadata in a generic way @traitfn function directed_graph(graph::::(!IsDirected)) - digraph = directed_graph(typeof(graph))() + digraph = directed_graph_type(typeof(graph))() for v in vertices(graph) add_vertex!(digraph, v) end @@ -24,7 +61,7 @@ end end @traitfn function directed_graph(graph::AbstractSimpleGraph::(!IsDirected)) - digraph = directed_graph(typeof(graph))() + digraph = directed_graph_type(typeof(graph))() for v in vertices(graph) add_vertex!(digraph) end @@ -63,9 +100,8 @@ function rename_vertices(g::AbstractGraph, name_map) return rename_vertices(v -> name_map[v], g) end -# TODO: This isn't really a generic `AbstractGraph` function! -function permute_vertices(graph::AbstractGraph, permutation::Vector) - return subgraph(graph, parent_vertices_to_vertices(graph, permutation)) +function permute_vertices(graph::AbstractGraph, permutation) + return not_implemented() end # Uniform interface for `outneighbors`, `inneighbors`, and `all_neighbors` @@ -379,24 +415,14 @@ end end function mincut_partitions(graph::AbstractGraph, distmx=weights(graph)) - parts = groupfind(first(mincut(graph, distmx))) + parts = groupfind(first(Graphs.mincut(graph, distmx))) return parts[1], parts[2] end -function insert_vertex!(graph::AbstractGraph, vertex) - in_graph = !add_vertex!(graph, vertex) - if in_graph - error("Duplicate vertices are not allowed") - end - return graph -end - -function delete_vertex!(graph::AbstractGraph, vertex) - in_graph = rem_vertex!(graph, vertex) - if !in_graph - error("Vertex not in graph") - end - return graph +function add_vertex(g::AbstractGraph, vs) + g = copy(g) + add_vertex!(g, vs) + return g end function add_vertices!(graph::AbstractGraph, vs::Vector) @@ -406,17 +432,35 @@ function add_vertices!(graph::AbstractGraph, vs::Vector) return graph end -"""Remove a list of edges from a graph g""" -function rem_edges!(g::AbstractGraph, edges) - for e in edges - rem_edge!(g, e) +function add_vertices(g::AbstractGraph, vs) + g = copy(g) + add_vertices!(g, vs) + return g +end + +function rem_vertex(g::AbstractGraph, vs) + g = copy(g) + rem_vertex!(g, vs) + return g +end + +"""Remove a list of vertices from a graph g""" +function rem_vertices!(g::AbstractGraph, vs) + for v in vs + rem_vertex!(g, v) end return g end -function rem_edges(g::AbstractGraph, edges) +function rem_vertices(g::AbstractGraph, vs) g = copy(g) - rem_edges!(g, edges) + rem_vertices!(g, vs) + return g +end + +function add_edge(g::AbstractGraph, edge) + g = copy(g) + add_edge!(g, edges) return g end @@ -434,27 +478,48 @@ function add_edges(g::AbstractGraph, edges) return g end -"""Remove a list of vertices from a graph g""" -function rem_vertices!(g::AbstractGraph, vs) - for v in vs - rem_vertex!(g, v) - end +function rem_edge(g::AbstractGraph, edge) + g = copy(g) + rem_edge!(g, edges) + return g +end +"""Remove a list of edges from a graph g""" +function rem_edges!(g::AbstractGraph, edges) + for e in edges + rem_edge!(g, e) + end return g end -function rem_vertices(g::AbstractGraph, vs) +function rem_edges(g::AbstractGraph, edges) g = copy(g) - rem_vertices!(g, vs) + rem_edges!(g, edges) return g end +eccentricities(graph::AbstractGraph) = eccentricities(graph, vertices(graph)) + +function eccentricities(graph::AbstractGraph, vs, distmx=weights(graph)) + return map(vertex -> eccentricity(graph, vertex, distmx), vs) +end + +function decorate_graph_edges(g::AbstractGraph; kwargs...) + return not_implemented() +end + +function decorate_graph_vertices(g::AbstractGraph; kwargs...) + return not_implemented() +end + """ Do a BFS search to construct a tree, but do it with randomness to avoid generating the same tree. Based on Int. J. Comput. Their Appl. 15 pp 177-186 (2008). Edges will point away from source vertex s.""" function random_bfs_tree(g::AbstractGraph, s; maxiter=1000 * (nv(g) + ne(g))) Q = [s] d = map(v -> v == s ? 0.0 : Inf, Indices(vertices(g))) visited = [s] - g_out = NamedDiGraph(vertices(g)) + + # TODO: This fails for `SimpleDiGraph`. + g_out = directed_graph_type(g)(vertices(g)) isempty_Q = false for iter in 1:maxiter diff --git a/src/Graphs/boundary.jl b/src/lib/GraphsExtensions/src/boundary.jl similarity index 97% rename from src/Graphs/boundary.jl rename to src/lib/GraphsExtensions/src/boundary.jl index 98ff919..812e4a3 100644 --- a/src/Graphs/boundary.jl +++ b/src/lib/GraphsExtensions/src/boundary.jl @@ -1,3 +1,5 @@ +using Graphs: AbstractGraph, dst, src, vertices + # https://en.wikipedia.org/wiki/Boundary_(graph_theory) function boundary_edges(graph::AbstractGraph, subgraph_vertices; dir=:out) E = edgetype(graph) diff --git a/src/Graphs/partitionedgraphs/partitioning.jl b/src/lib/GraphsExtensions/src/partitioning.jl similarity index 55% rename from src/Graphs/partitionedgraphs/partitioning.jl rename to src/lib/GraphsExtensions/src/partitioning.jl index 42347b0..1511476 100644 --- a/src/Graphs/partitionedgraphs/partitioning.jl +++ b/src/lib/GraphsExtensions/src/partitioning.jl @@ -1,3 +1,6 @@ +using Graphs: AbstractGraph, AbstractSimpleGraph, nv, vertices +using SplitApplyCombine: group + """ Graph partitioning backend """ @@ -29,26 +32,6 @@ function set_partitioning_backend!(backend::Union{Missing,Backend,String}) return nothing end -# KaHyPar configuration options -# -# configurations = readdir(joinpath(pkgdir(KaHyPar), "src", "config")) -# "cut_kKaHyPar_sea20.ini" -# "cut_rKaHyPar_sea20.ini" -# "km1_kKaHyPar-E_sea20.ini" -# "km1_kKaHyPar_eco_sea20.ini" -# "km1_kKaHyPar_sea20.ini" -# "km1_rKaHyPar_sea20.ini" -# -const kahypar_configurations = Dict([ - (objective="edge_cut", alg="kway") => "cut_kKaHyPar_sea20.ini", - (objective="edge_cut", alg="recursive") => "cut_rKaHyPar_sea20.ini", - (objective="connectivity", alg="kway") => "km1_kKaHyPar_sea20.ini", - (objective="connectivity", alg="recursive") => "km1_rKaHyPar_sea20.ini", -]) - -# Metis configuration options -const metis_algs = Dict(["kway" => :KWAY, "recursive" => :RECURSIVE]) - function _npartitions( g::AbstractGraph, npartitions::Integer, nvertices_per_partition::Nothing ) @@ -72,35 +55,22 @@ function _npartitions( end function partitioned_vertices( - g::Graph; + g::AbstractSimpleGraph; npartitions=nothing, nvertices_per_partition=nothing, backend=current_partitioning_backend(), kwargs..., ) - #Metis cannot handle the edge case npartitions = 1, so we will fix it here for now - #Is this now + # Metis cannot handle the edge case npartitions = 1, so we will fix it here for now. + # TODO: Check if this is still needed, or move to `NamedGraphsMetisExt`. if (_npartitions(g, npartitions, nvertices_per_partition) == 1) return group(v -> 1, collect(vertices(g))) end - return partitioned_vertices( Backend(backend), g, _npartitions(g, npartitions, nvertices_per_partition); kwargs... ) end -function partitioned_vertices( - g::AbstractGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... -) - vertex_partitions = partitioned_vertices( - parent_graph(g); npartitions, nvertices_per_partition, kwargs... - ) - #[inv(vertex_to_parent_vertex(g))[v] for v in partitions] - # TODO: output the reverse of this dictionary (a Vector of Vector - # of the vertices in each partition). - # return Dictionary(vertices(g), partitions) - return [ - parent_vertices_to_vertices(g, vertex_partition) for - vertex_partition in vertex_partitions - ] +function partitioned_vertices(g::AbstractGraph; kwargs...) + return not_implemented() end diff --git a/src/Graphs/shortestpaths.jl b/src/lib/GraphsExtensions/src/shortestpaths.jl similarity index 82% rename from src/Graphs/shortestpaths.jl rename to src/lib/GraphsExtensions/src/shortestpaths.jl index 3791aa7..7853ec1 100644 --- a/src/Graphs/shortestpaths.jl +++ b/src/lib/GraphsExtensions/src/shortestpaths.jl @@ -1,3 +1,5 @@ +using Graphs: Graphs, dijkstra_shortest_paths, edgetype, weights + function dijkstra_parents(graph::AbstractGraph, vertex, distmx=weights(graph)) return dijkstra_shortest_paths( graph, [vertex], distmx; allpaths=false, trackvertices=false @@ -20,5 +22,5 @@ function dijkstra_mst(graph::AbstractGraph, vertex, distmx=weights(graph)) end function dijkstra_tree(graph::AbstractGraph, vertex, distmx=weights(graph)) - return tree(graph, dijkstra_parents(graph, vertex, distmx)) + return Graphs.tree(graph, dijkstra_parents(graph, vertex, distmx)) end diff --git a/src/lib/GraphsExtensions/src/simplegraph.jl b/src/lib/GraphsExtensions/src/simplegraph.jl new file mode 100644 index 0000000..31be24a --- /dev/null +++ b/src/lib/GraphsExtensions/src/simplegraph.jl @@ -0,0 +1,9 @@ +using Graphs.SimpleGraphs: SimpleDiGraph, SimpleGraph + +directed_graph_type(G::Type{<:SimpleGraph}) = SimpleDiGraph{vertextype(G)} +# TODO: Use traits to make this more general. +undirected_graph_type(G::Type{<:SimpleGraph}) = G + +# TODO: Use traits to make this more general. +directed_graph_type(G::Type{<:SimpleDiGraph}) = G +undirected_graph_type(G::Type{<:SimpleDiGraph}) = SimpleGraph{vertextype(G)} diff --git a/src/lib/GraphsExtensions/src/symrcm.jl b/src/lib/GraphsExtensions/src/symrcm.jl new file mode 100644 index 0000000..3b59abc --- /dev/null +++ b/src/lib/GraphsExtensions/src/symrcm.jl @@ -0,0 +1,9 @@ +# Symmetric sparse reverse Cuthill-McKee ordering +# https://en.wikipedia.org/wiki/Cuthill%E2%80%93McKee_algorithm +# https://github.com/PetrKryslUCSD/SymRCM.jl +# https://github.com/rleegates/CuthillMcKee.jl +function symrcm_perm end + +function symrcm_permute(graph::AbstractGraph) + return permute_vertices(graph, symrcm_perm(graph)) +end diff --git a/src/traversals/trees_and_forests.jl b/src/lib/GraphsExtensions/src/trees_and_forests.jl similarity index 54% rename from src/traversals/trees_and_forests.jl rename to src/lib/GraphsExtensions/src/trees_and_forests.jl index 7f9c996..9167677 100644 --- a/src/traversals/trees_and_forests.jl +++ b/src/lib/GraphsExtensions/src/trees_and_forests.jl @@ -1,3 +1,7 @@ +using Graphs: IsDirected, bfs_tree, connected_components, edges, edgetype +using .GraphsExtensions: random_bfs_tree, rem_edges, undirected_graph +using SimpleTraits: SimpleTraits, @traitfn + abstract type SpanningTreeAlgorithm end struct BFS <: SpanningTreeAlgorithm end @@ -9,41 +13,42 @@ default_spanning_tree_alg() = BFS() default_root_vertex(g) = last(findmax(eccentricities(g))) function spanning_tree( - g::AbstractNamedGraph; alg=default_spanning_tree_alg(), root_vertex=default_root_vertex(g) + g::AbstractGraph; alg=default_spanning_tree_alg(), root_vertex=default_root_vertex(g) ) return spanning_tree(alg, g; root_vertex) end @traitfn function spanning_tree( - ::BFS, g::AbstractNamedGraph::(!IsDirected); root_vertex=default_root_vertex(g) + ::BFS, g::AbstractGraph::(!IsDirected); root_vertex=default_root_vertex(g) ) return undirected_graph(bfs_tree(g, root_vertex)) end @traitfn function spanning_tree( - ::RandomBFS, g::AbstractNamedGraph::(!IsDirected); root_vertex=default_root_vertex(g) + ::RandomBFS, g::AbstractGraph::(!IsDirected); root_vertex=default_root_vertex(g) ) return undirected_graph(random_bfs_tree(g, root_vertex)) end @traitfn function spanning_tree( - ::DFS, g::AbstractNamedGraph::(!IsDirected); root_vertex=default_root_vertex(g) + ::DFS, g::AbstractGraph::(!IsDirected); root_vertex=default_root_vertex(g) ) return undirected_graph(dfs_tree(g, root_vertex)) end -#Given a graph, split it into its connected components, construct a spanning tree, using the function spanning_tree, over each of them +# Given a graph, split it into its connected components, construct a spanning tree, using the function spanning_tree, over each of them # and take the union. -function spanning_forest(g::AbstractNamedGraph; spanning_tree=spanning_tree) +function spanning_forest(g::AbstractGraph; spanning_tree=spanning_tree) return reduce(union, (spanning_tree(subgraph(g, vs)) for vs in connected_components(g))) end -#Given an undirected graph g with vertex set V, build a set of forests (each with vertex set V) which covers all edges in g +# TODO: Create a generic version in `GraphsExtensions`. +# Given an undirected graph g with vertex set V, build a set of forests (each with vertex set V) which covers all edges in g # (see https://en.wikipedia.org/wiki/Arboricity) We do not find the minimum but our tests show this algorithm performs well -function forest_cover(g::AbstractNamedGraph; spanning_tree=spanning_tree) +function forest_cover(g::AbstractGraph; spanning_tree=spanning_tree) edges_collected = edgetype(g)[] remaining_edges = edges(g) - forests = NamedGraph[] + forests = typeof(g)[] while !isempty(remaining_edges) g_reduced = rem_edges(g, edges_collected) g_reduced_spanning_forest = spanning_forest(g_reduced; spanning_tree) @@ -51,8 +56,8 @@ function forest_cover(g::AbstractNamedGraph; spanning_tree=spanning_tree) push!(forests, g_reduced_spanning_forest) setdiff!(remaining_edges, edges(g_reduced_spanning_forest)) end - return forests end -#forest_cover(g::PartitionedGraph; kwargs...) = not_implemented() +# TODO: Define in `NamedGraphs.PartitionedGraphs`. +# forest_cover(g::PartitionedGraph; kwargs...) = not_implemented() diff --git a/src/Base/key.jl b/src/lib/Keys/src/Keys.jl similarity index 69% rename from src/Base/key.jl rename to src/lib/Keys/src/Keys.jl index 69ec301..b419c07 100644 --- a/src/Base/key.jl +++ b/src/lib/Keys/src/Keys.jl @@ -1,3 +1,4 @@ +module Keys """ Key{K} @@ -19,24 +20,26 @@ struct Key{K} end Key(I...) = Key(I) -show(io::IO, I::Key) = print(io, "Key(", I.I, ")") +Base.show(io::IO, I::Key) = print(io, "Key(", I.I, ")") ## For indexing into `AbstractArray` # This allows linear indexing `A[Key(2)]`. # Overload of `Base.to_index`. -to_index(I::Key) = I.I +Base.to_index(I::Key) = I.I # This allows cartesian indexing `A[Key(CartesianIndex(1, 2))]`. # Overload of `Base.to_indices`. -to_indices(A::AbstractArray, I::Tuple{Key{<:CartesianIndex}}) = I[1].I.I +Base.to_indices(A::AbstractArray, I::Tuple{Key{<:CartesianIndex}}) = I[1].I.I # This would allow syntax like `A[Key(1, 2)]`, should we support that? # Overload of `Base.to_indices`. # to_indices(A::AbstractArray, I::Tuple{Key}) = I[1].I -getindex(d::AbstractDict, I::Key) = d[I.I] +Base.getindex(d::AbstractDict, I::Key) = d[I.I] # Fix ambiguity error with Base -getindex(d::Dict, I::Key) = d[I.I] +Base.getindex(d::Dict, I::Key) = d[I.I] -getindex(d::AbstractDictionary, I::Key) = d[I.I] +using Dictionaries: AbstractDictionary +Base.getindex(d::AbstractDictionary, I::Key) = d[I.I] +end diff --git a/src/lib/NamedGraphGenerators/src/NamedGraphGenerators.jl b/src/lib/NamedGraphGenerators/src/NamedGraphGenerators.jl new file mode 100644 index 0000000..a15f739 --- /dev/null +++ b/src/lib/NamedGraphGenerators/src/NamedGraphGenerators.jl @@ -0,0 +1,191 @@ +module NamedGraphGenerators +using Graphs: + IsDirected, + bfs_tree, + binary_tree, + grid, + inneighbors, + merge_vertices, + nv, + outneighbors, + path_graph, + rem_vertex! +using Graphs.SimpleGraphs: AbstractSimpleGraph +using ..GraphGenerators: comb_tree +using ..GraphsExtensions: add_edges!, rem_vertices! +using ..NamedGraphs: NamedGraph +using SimpleTraits: SimpleTraits, @traitfn + +## TODO: Bring this back in some form? +## TODO: Move to `GraphsExtensions`? +## @traitfn function parent(tree::AbstractSimpleGraph::IsDirected, v::Integer) +## return only(inneighbors(tree, v)) +## end + +## TODO: Move to `GraphsExtensions`? +@traitfn function children(tree::AbstractSimpleGraph::IsDirected, v::Integer) + return outneighbors(tree, v) +end + +## TODO: Move to `GraphsExtensions`? +@traitfn function set_named_vertices!( + named_vertices::AbstractVector, + tree::AbstractSimpleGraph::IsDirected, + simple_parent::Integer, + named_parent; + child_name=identity, +) + simple_children = children(tree, simple_parent) + for n in 1:length(simple_children) + simple_child = simple_children[n] + named_child = (named_parent..., child_name(n)) + named_vertices[simple_child] = named_child + set_named_vertices!(named_vertices, tree, simple_child, named_child; child_name) + end + return named_vertices +end + +# TODO: Use vectors as vertex names? +# k = 3: +# 1 => (1,) +# 2 => (1, 1) +# 3 => (1, 2) +# 4 => (1, 1, 1) +# 5 => (1, 1, 2) +# 6 => (1, 2, 1) +# 7 => (1, 2, 2) +function named_bfs_tree_vertices( + simple_graph::AbstractSimpleGraph, source::Integer=1; source_name=1, child_name=identity +) + tree = bfs_tree(simple_graph, source) + named_vertices = Vector{Tuple}(undef, nv(simple_graph)) + named_source = (source_name,) + named_vertices[source] = named_source + set_named_vertices!(named_vertices, tree, source, named_source; child_name) + return named_vertices +end + +function named_bfs_tree( + simple_graph::AbstractSimpleGraph, source::Integer=1; source_name=1, child_name=identity +) + named_vertices = named_bfs_tree_vertices(simple_graph, source; source_name, child_name) + return NamedGraph(simple_graph, named_vertices) +end + +function named_binary_tree( + k::Integer, source::Integer=1; source_name=1, child_name=identity +) + simple_graph = binary_tree(k) + return named_bfs_tree(simple_graph, source; source_name, child_name) +end + +function named_path_graph(dim::Integer) + return NamedGraph(path_graph(dim)) +end + +function named_path_digraph(dim::Integer) + return NamedDiGraph(path_digraph(dim)) +end + +function named_grid(dim::Integer; kwargs...) + simple_graph = grid((dim,); kwargs...) + return NamedGraph(simple_graph) +end + +function named_grid(dims; kwargs...) + simple_graph = grid(dims; kwargs...) + return NamedGraph(simple_graph, Tuple.(CartesianIndices(Tuple(dims)))) +end + +function named_comb_tree(dims::Tuple) + simple_graph = comb_tree(dims) + return NamedGraph(simple_graph, Tuple.(CartesianIndices(Tuple(dims)))) +end + +function named_comb_tree(tooth_lengths::AbstractVector{<:Integer}) + @assert all(>(0), tooth_lengths) + simple_graph = comb_tree(tooth_lengths) + nx = length(tooth_lengths) + ny = maximum(tooth_lengths) + vertices = filter(Tuple.(CartesianIndices((nx, ny)))) do (jx, jy) + jy <= tooth_lengths[jx] + end + return NamedGraph(simple_graph, vertices) +end + +"""Generate a graph which corresponds to a hexagonal tiling of the plane. There are m rows and n columns of hexagons. +Based off of the generator in Networkx hexagonal_lattice_graph()""" +function named_hexagonal_lattice_graph(m::Integer, n::Integer; periodic=false) + M = 2 * m + rows = [i for i in 1:(M + 2)] + cols = [i for i in 1:(n + 1)] + + if periodic && (n % 2 == 1 || m < 2 || n < 2) + error("Periodic Hexagonal Lattice needs m > 1, n > 1 and n even") + end + + G = NamedGraph([(j, i) for i in cols for j in rows]) + + col_edges = [(j, i) => (j + 1, i) for i in cols for j in rows[1:(M + 1)]] + row_edges = [(j, i) => (j, i + 1) for i in cols[1:n] for j in rows if i % 2 == j % 2] + add_edges!(G, col_edges) + add_edges!(G, row_edges) + rem_vertex!(G, (M + 2, 1)) + rem_vertex!(G, ((M + 1) * (n % 2) + 1, n + 1)) + + if periodic == true + for i in cols[1:n] + G = merge_vertices(G, [(1, i), (M + 1, i)]) + end + + for i in cols[2:(n + 1)] + G = merge_vertices(G, [(2, i), (M + 2, i)]) + end + + for j in rows[2:M] + G = merge_vertices(G, [(j, 1), (j, n + 1)]) + end + + rem_vertex!(G, (M + 1, n + 1)) + end + + return G +end + +"""Generate a graph which corresponds to a equilateral triangle tiling of the plane. There are m rows and n columns of triangles. +Based off of the generator in Networkx triangular_lattice_graph()""" +function named_triangular_lattice_graph(m::Integer, n::Integer; periodic=false) + N = floor(Int64, (n + 1) / 2.0) + rows = [i for i in 1:(m + 1)] + cols = [i for i in 1:(N + 1)] + + if periodic && (n < 5 || m < 3) + error("Periodic Triangular Lattice needs m > 2, n > 4") + end + + G = NamedGraph([(j, i) for i in cols for j in rows]) + + grid_edges1 = [(j, i) => (j, i + 1) for j in rows for i in cols[1:N]] + grid_edges2 = [(j, i) => (j + 1, i) for j in rows[1:m] for i in cols] + add_edges!(G, vcat(grid_edges1, grid_edges2)) + + diagonal_edges1 = [(j, i) => (j + 1, i + 1) for j in rows[2:2:m] for i in cols[1:N]] + diagonal_edges2 = [(j, i + 1) => (j + 1, i) for j in rows[1:2:m] for i in cols[1:N]] + add_edges!(G, vcat(diagonal_edges1, diagonal_edges2)) + + if periodic == true + for i in cols + G = merge_vertices(G, [(1, i), (m + 1, i)]) + end + + for j in rows[1:m] + G = merge_vertices(G, [(j, 1), (j, N + 1)]) + end + + elseif n % 2 == 1 + rem_vertices!(G, [(j, N + 1) for j in rows[2:2:(m + 1)]]) + end + + return G +end +end diff --git a/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl b/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl new file mode 100644 index 0000000..57a6b71 --- /dev/null +++ b/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl @@ -0,0 +1,8 @@ +module PartitionedGraphs +include("abstractpartitionvertex.jl") +include("abstractpartitionedge.jl") +include("abstractpartitionedgraph.jl") +include("partitionvertex.jl") +include("partitionedge.jl") +include("partitionedgraph.jl") +end diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedge.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedge.jl new file mode 100644 index 0000000..57188e6 --- /dev/null +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedge.jl @@ -0,0 +1,11 @@ +using Graphs: Graphs +using ..NamedGraphs: AbstractNamedEdge + +abstract type AbstractPartitionEdge{V} <: AbstractNamedEdge{V} end + +Base.parent(pe::AbstractPartitionEdge) = not_implemented() +Graphs.src(pe::AbstractPartitionEdge) = not_implemented() +Graphs.dst(pe::AbstractPartitionEdge) = not_implemented() +Base.reverse(pe::AbstractPartitionEdge) = not_implemented() + +#Don't have the vertices wrapped. But wrap them with source and edge. diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl similarity index 57% rename from src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl rename to src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index 74d82df..83d3b15 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -1,3 +1,14 @@ +using Graphs: + Graphs, AbstractEdge, add_vertex!, dst, edgetype, has_vertex, rem_vertex!, src, vertices +using ..NamedGraphs: + NamedGraphs, + AbstractNamedGraph, + parent_graph, + parent_graph_type, + parent_vertex_to_vertex, + vertex_to_parent_vertex +using ..NamedGraphs.GraphsExtensions: GraphsExtensions, add_vertices!, rem_vertices! + abstract type AbstractPartitionedGraph{V,PV} <: AbstractNamedGraph{V} end #Needed for interface @@ -6,42 +17,56 @@ unpartitioned_graph(pg::AbstractPartitionedGraph) = not_implemented() partitionvertex(pg::AbstractPartitionedGraph, vertex) = not_implemented() partitionvertices(pg::AbstractPartitionedGraph, verts) = not_implemented() partitionvertices(pg::AbstractPartitionedGraph) = not_implemented() -copy(pg::AbstractPartitionedGraph) = not_implemented() +Base.copy(pg::AbstractPartitionedGraph) = not_implemented() delete_from_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() insert_to_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() partitionedge(pg::AbstractPartitionedGraph, edge) = not_implemented() partitionedges(pg::AbstractPartitionedGraph, edges) = not_implemented() partitionedges(pg::AbstractPartitionedGraph) = not_implemented() -function edges(pg::AbstractPartitionedGraph, partitionedge::AbstractPartitionEdge) +function Graphs.edges(pg::AbstractPartitionedGraph, partitionedge::AbstractPartitionEdge) + return not_implemented() +end +function Graphs.vertices(pg::AbstractPartitionedGraph, pv::AbstractPartitionVertex) return not_implemented() end -vertices(pg::AbstractPartitionedGraph, pv::AbstractPartitionVertex) = not_implemented() -function vertices( +function Graphs.vertices( pg::AbstractPartitionedGraph, partitionverts::Vector{V} ) where {V<:AbstractPartitionVertex} return not_implemented() end -parent_graph_type(PG::Type{<:AbstractPartitionedGraph}) = not_implemented() -directed_graph_type(PG::Type{<:AbstractPartitionedGraph}) = not_implemented() -undirected_graph_type(PG::Type{<:AbstractPartitionedGraph}) = not_implemented() +NamedGraphs.parent_graph_type(PG::Type{<:AbstractPartitionedGraph}) = not_implemented() +function GraphsExtensions.directed_graph_type(PG::Type{<:AbstractPartitionedGraph}) + return not_implemented() +end +function GraphsExtensions.undirected_graph_type(PG::Type{<:AbstractPartitionedGraph}) + return not_implemented() +end #Functions for the abstract type -vertices(pg::AbstractPartitionedGraph) = vertices(unpartitioned_graph(pg)) -parent_graph(pg::AbstractPartitionedGraph) = parent_graph(unpartitioned_graph(pg)) -function vertex_to_parent_vertex(pg::AbstractPartitionedGraph, vertex) +Graphs.vertices(pg::AbstractPartitionedGraph) = vertices(unpartitioned_graph(pg)) +function NamedGraphs.parent_graph(pg::AbstractPartitionedGraph) + return parent_graph(unpartitioned_graph(pg)) +end +function NamedGraphs.vertex_to_parent_vertex(pg::AbstractPartitionedGraph, vertex) return vertex_to_parent_vertex(unpartitioned_graph(pg), vertex) end -function parent_vertex_to_vertex(pg::AbstractPartitionedGraph, parent_vertex) +function NamedGraphs.parent_vertex_to_vertex(pg::AbstractPartitionedGraph, parent_vertex) return parent_vertex_to_vertex(unpartitioned_graph(pg), parent_vertex) end -edgetype(pg::AbstractPartitionedGraph) = edgetype(unpartitioned_graph(pg)) -parent_graph_type(pg::AbstractPartitionedGraph) = parent_graph_type(unpartitioned_graph(pg)) -nv(pg::AbstractPartitionedGraph, pv::AbstractPartitionVertex) = length(vertices(pg, pv)) -function has_vertex(pg::AbstractPartitionedGraph, partitionvertex::AbstractPartitionVertex) +Graphs.edgetype(pg::AbstractPartitionedGraph) = edgetype(unpartitioned_graph(pg)) +function NamedGraphs.parent_graph_type(pg::AbstractPartitionedGraph) + return parent_graph_type(unpartitioned_graph(pg)) +end +function Graphs.nv(pg::AbstractPartitionedGraph, pv::AbstractPartitionVertex) + return length(vertices(pg, pv)) +end +function Graphs.has_vertex( + pg::AbstractPartitionedGraph, partitionvertex::AbstractPartitionVertex +) return has_vertex(partitioned_graph(pg), parent(partitionvertex)) end -function has_edge(pg::AbstractPartitionedGraph, partitionedge::AbstractPartitionEdge) +function Graphs.has_edge(pg::AbstractPartitionedGraph, partitionedge::AbstractPartitionEdge) return has_edge(partitioned_graph(pg), parent(partitionedge)) end @@ -50,7 +75,7 @@ function is_boundary_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) return src(p_edge) == dst(p_edge) end -function add_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) +function Graphs.add_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) add_edge!(unpartitioned_graph(pg), edge) pg_edge = parent(partitionedge(pg, edge)) if src(pg_edge) != dst(pg_edge) @@ -60,7 +85,7 @@ function add_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) return pg end -function rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) +function Graphs.rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) pg_edge = partitionedge(pg, edge) if has_edge(partitioned_graph(pg), pg_edge) g_edges = edges(pg, pg_edge) @@ -71,35 +96,14 @@ function rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) return rem_edge!(unpartitioned_graph(pg), edge) end -function rem_edge!(pg::AbstractPartitionedGraph, partitionedge::AbstractPartitionEdge) - return rem_edges!(pg, edges(pg, parent(partitionedge))) -end - -function rem_edge(pg::AbstractPartitionedGraph, partitionedge::AbstractPartitionEdge) - pg_new = copy(pg) - rem_edge!(pg_new, partitionedge) - return pg_new -end - -function rem_edges!( - pg::AbstractPartitionedGraph, partitionedges::Vector{<:AbstractPartitionEdge} -) - for pe in partitionedges - rem_edge!(pg, pe) - end - return pg -end - -function rem_edges( - pg::AbstractPartitionedGraph, partitionedges::Vector{<:AbstractPartitionEdge} +function Graphs.rem_edge!( + pg::AbstractPartitionedGraph, partitionedge::AbstractPartitionEdge ) - pg_new = copy(pg) - rem_edges!(pg_new, partitionedges) - return pg_new + return rem_edges!(pg, edges(pg, parent(partitionedge))) end #Vertex addition and removal. I think it's important not to allow addition of a vertex without specification of PV -function add_vertex!( +function Graphs.add_vertex!( pg::AbstractPartitionedGraph, vertex, partitionvertex::AbstractPartitionVertex ) add_vertex!(unpartitioned_graph(pg), vertex) @@ -108,7 +112,7 @@ function add_vertex!( return pg end -function add_vertices!( +function GraphsExtensions.add_vertices!( pg::AbstractPartitionedGraph, vertices::Vector, partitionvertices::Vector{<:AbstractPartitionVertex}, @@ -121,14 +125,14 @@ function add_vertices!( return pg end -function add_vertices!( +function GraphsExtensions.add_vertices!( pg::AbstractPartitionedGraph, vertices::Vector, partitionvertex::AbstractPartitionVertex ) add_vertices!(pg, vertices, fill(partitionvertex, length(vertices))) return pg end -function rem_vertex!(pg::AbstractPartitionedGraph, vertex) +function Graphs.rem_vertex!(pg::AbstractPartitionedGraph, vertex) pv = partitionvertex(pg, vertex) delete_from_vertex_map!(pg, pv, vertex) rem_vertex!(unpartitioned_graph(pg), vertex) @@ -138,22 +142,26 @@ function rem_vertex!(pg::AbstractPartitionedGraph, vertex) return pg end -function rem_vertex!(pg::AbstractPartitionedGraph, partitionvertex::AbstractPartitionVertex) +function Graphs.rem_vertex!( + pg::AbstractPartitionedGraph, partitionvertex::AbstractPartitionVertex +) rem_vertices!(pg, vertices(pg, partitionvertex)) return pg end -function rem_vertex(pg::AbstractPartitionedGraph, partitionvertex::AbstractPartitionVertex) +function GraphsExtensions.rem_vertex( + pg::AbstractPartitionedGraph, partitionvertex::AbstractPartitionVertex +) pg_new = copy(pg) rem_vertex!(pg_new, partitionvertex) return pg_new end -function add_vertex!(pg::AbstractPartitionedGraph, vertex) +function Graphs.add_vertex!(pg::AbstractPartitionedGraph, vertex) return error("Need to specify a partition where the new vertex will go.") end -function (pg1::AbstractPartitionedGraph == pg2::AbstractPartitionedGraph) +function Base.:(==)(pg1::AbstractPartitionedGraph, pg2::AbstractPartitionedGraph) if unpartitioned_graph(pg1) != unpartitioned_graph(pg2) || partitioned_graph(pg1) != partitioned_graph(pg2) return false @@ -166,11 +174,13 @@ function (pg1::AbstractPartitionedGraph == pg2::AbstractPartitionedGraph) return true end -function subgraph(pg::AbstractPartitionedGraph, partitionvertex::AbstractPartitionVertex) +function GraphsExtensions.subgraph( + pg::AbstractPartitionedGraph, partitionvertex::AbstractPartitionVertex +) return first(induced_subgraph(unpartitioned_graph(pg), vertices(pg, [partitionvertex]))) end -function induced_subgraph( +function Graphs.induced_subgraph( pg::AbstractPartitionedGraph, partitionvertex::AbstractPartitionVertex ) return subgraph(pg, partitionvertex), nothing diff --git a/src/Graphs/partitionedgraphs/abstractpartitionvertex.jl b/src/lib/PartitionedGraphs/src/abstractpartitionvertex.jl similarity index 60% rename from src/Graphs/partitionedgraphs/abstractpartitionvertex.jl rename to src/lib/PartitionedGraphs/src/abstractpartitionvertex.jl index 856da00..31bc58b 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionvertex.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionvertex.jl @@ -1,4 +1,4 @@ abstract type AbstractPartitionVertex{V} <: Any where {V} end #Parent, wrap, unwrap, vertex? -parent(pv::AbstractPartitionVertex) = not_implemented() +Base.parent(pv::AbstractPartitionVertex) = not_implemented() diff --git a/src/lib/PartitionedGraphs/src/partitionedge.jl b/src/lib/PartitionedGraphs/src/partitionedge.jl new file mode 100644 index 0000000..d76dcd8 --- /dev/null +++ b/src/lib/PartitionedGraphs/src/partitionedge.jl @@ -0,0 +1,12 @@ +using Graphs: Graphs, AbstractEdge, dst, src + +struct PartitionEdge{V,E<:AbstractEdge{V}} <: AbstractPartitionEdge{V} + edge::E +end + +Base.parent(pe::PartitionEdge) = getfield(pe, :edge) +Graphs.src(pe::PartitionEdge) = PartitionVertex(src(parent(pe))) +Graphs.dst(pe::PartitionEdge) = PartitionVertex(dst(parent(pe))) +PartitionEdge(p::Pair) = PartitionEdge(NamedEdge(first(p) => last(p))) +PartitionEdge(vsrc, vdst) = PartitionEdge(vsrc => vdst) +Base.reverse(pe::PartitionEdge) = PartitionEdge(reverse(parent(pe))) diff --git a/src/Graphs/partitionedgraphs/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl similarity index 82% rename from src/Graphs/partitionedgraphs/partitionedgraph.jl rename to src/lib/PartitionedGraphs/src/partitionedgraph.jl index 23c5a3e..0997b92 100644 --- a/src/Graphs/partitionedgraphs/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -1,3 +1,12 @@ +using Dictionaries: Dictionary +using Graphs: + AbstractEdge, AbstractGraph, add_edge!, edges, has_edge, induced_subgraph, vertices +using .GraphsExtensions: + GraphsExtensions, boundary_edges, is_self_loop, partitioned_vertices +using ..NamedGraphs: NamedEdge, NamedGraph + +# TODO: Parametrize `partitioned_vertices` and `which_partition`, +# see https://github.com/mtfishman/NamedGraphs.jl/issues/63. struct PartitionedGraph{V,PV,G<:AbstractGraph{V},PG<:AbstractGraph{PV}} <: AbstractPartitionedGraph{V,PV} graph::G @@ -10,13 +19,13 @@ end function PartitionedGraph(g::AbstractGraph, partitioned_vertices) pvs = keys(partitioned_vertices) pg = NamedGraph(pvs) + # TODO: Make this type more specific. which_partition = Dictionary() for v in vertices(g) v_pvs = Set(findall(pv -> v ∈ partitioned_vertices[pv], pvs)) @assert length(v_pvs) == 1 insert!(which_partition, v, first(v_pvs)) end - for e in edges(g) pv_src, pv_dst = which_partition[src(e)], which_partition[dst(e)] pe = NamedEdge(pv_src => pv_dst) @@ -24,7 +33,6 @@ function PartitionedGraph(g::AbstractGraph, partitioned_vertices) add_edge!(pg, pe) end end - return PartitionedGraph(g, pg, Dictionary(partitioned_vertices), which_partition) end @@ -40,14 +48,16 @@ end #Needed for interface partitioned_graph(pg::PartitionedGraph) = getfield(pg, :partitioned_graph) unpartitioned_graph(pg::PartitionedGraph) = getfield(pg, :graph) -partitioned_vertices(pg::PartitionedGraph) = getfield(pg, :partitioned_vertices) +function GraphsExtensions.partitioned_vertices(pg::PartitionedGraph) + return getfield(pg, :partitioned_vertices) +end which_partition(pg::PartitionedGraph) = getfield(pg, :which_partition) -parent_graph_type(PG::Type{<:PartitionedGraph}) = fieldtype(PG, :graph) -function vertices(pg::PartitionedGraph, partitionvert::PartitionVertex) +NamedGraphs.parent_graph_type(PG::Type{<:PartitionedGraph}) = fieldtype(PG, :graph) +function Graphs.vertices(pg::PartitionedGraph, partitionvert::PartitionVertex) return partitioned_vertices(pg)[parent(partitionvert)] end -function vertices(pg::PartitionedGraph, partitionverts::Vector{<:PartitionVertex}) - return unique(reduce(vcat, [vertices(pg, pv) for pv in partitionverts])) +function Graphs.vertices(pg::PartitionedGraph, partitionverts::Vector{<:PartitionVertex}) + return unique(reduce(vcat, Iterators.map(pv -> vertices(pg, pv), partitionverts))) end function partitionvertex(pg::PartitionedGraph, vertex) return PartitionVertex(which_partition(pg)[vertex]) @@ -69,9 +79,6 @@ end partitionedge(pg::PartitionedGraph, p::Pair) = partitionedge(pg, edgetype(pg)(p)) -is_self_loop(e::AbstractEdge) = src(e) == dst(e) -is_self_loop(e::Pair) = first(e) == last(e) - function partitionedges(pg::PartitionedGraph, edges::Vector) return filter(!is_self_loop, unique([partitionedge(pg, e) for e in edges])) end @@ -80,7 +87,7 @@ function partitionedges(pg::PartitionedGraph) return PartitionEdge.(edges(partitioned_graph(pg))) end -function edges(pg::PartitionedGraph, partitionedge::PartitionEdge) +function Graphs.edges(pg::PartitionedGraph, partitionedge::PartitionEdge) psrc_vs = vertices(pg, PartitionVertex(src(partitionedge))) pdst_vs = vertices(pg, PartitionVertex(dst(partitionedge))) psrc_subgraph = subgraph(unpartitioned_graph(pg), psrc_vs) @@ -90,7 +97,7 @@ function edges(pg::PartitionedGraph, partitionedge::PartitionEdge) return setdiff(edges(full_subgraph), vcat(edges(psrc_subgraph), edges(pdst_subgraph))) end -function edges(pg::PartitionedGraph, partitionedges::Vector{<:PartitionEdge}) +function Graphs.edges(pg::PartitionedGraph, partitionedges::Vector{<:PartitionEdge}) return unique(reduce(vcat, [edges(pg, pe) for pe in partitionedges])) end @@ -106,7 +113,7 @@ function boundary_partitionedges( return boundary_partitionedges(pg, [partitionvertex]; kwargs...) end -function copy(pg::PartitionedGraph) +function Base.copy(pg::PartitionedGraph) return PartitionedGraph( copy(unpartitioned_graph(pg)), copy(partitioned_graph(pg)), diff --git a/src/Graphs/partitionedgraphs/partitionvertex.jl b/src/lib/PartitionedGraphs/src/partitionvertex.jl similarity index 56% rename from src/Graphs/partitionedgraphs/partitionvertex.jl rename to src/lib/PartitionedGraphs/src/partitionvertex.jl index 8a66ff7..6773da5 100644 --- a/src/Graphs/partitionedgraphs/partitionvertex.jl +++ b/src/lib/PartitionedGraphs/src/partitionvertex.jl @@ -2,4 +2,4 @@ struct PartitionVertex{V} <: AbstractPartitionVertex{V} vertex::V end -parent(pv::PartitionVertex) = getfield(pv, :vertex) +Base.parent(pv::PartitionVertex) = getfield(pv, :vertex) diff --git a/src/namededge.jl b/src/namededge.jl index 99a859c..ed793d6 100644 --- a/src/namededge.jl +++ b/src/namededge.jl @@ -1,3 +1,6 @@ +using Graphs: Graphs +using .GraphsExtensions: GraphsExtensions + struct NamedEdge{V} <: AbstractNamedEdge{V} src::V dst::V @@ -6,21 +9,22 @@ end NamedEdge(src::V, dst::V) where {V} = NamedEdge{V}(src, dst) NamedEdge(src::S, dst::D) where {S,D} = NamedEdge{promote_type(S, D)}(src, dst) -convert_vertextype(V::Type, ::Type{<:NamedEdge}) = NamedEdge{V} +GraphsExtensions.convert_vertextype(V::Type, ::Type{<:NamedEdge}) = NamedEdge{V} -src(e::NamedEdge) = e.src -dst(e::NamedEdge) = e.dst +Graphs.src(e::NamedEdge) = e.src +Graphs.dst(e::NamedEdge) = e.dst NamedEdge{V}(e::NamedEdge{V}) where {V} = e NamedEdge(e::NamedEdge) = e NamedEdge{V}(e::AbstractNamedEdge) where {V} = NamedEdge{V}(e.src, e.dst) -convert(E::Type{<:NamedEdge}, e::NamedEdge) = E(e) +Base.convert(E::Type{<:NamedEdge}, e::NamedEdge) = E(e) NamedEdge(t::Tuple) = NamedEdge(t[1], t[2]) NamedEdge(p::Pair) = NamedEdge(p.first, p.second) NamedEdge{V}(p::Pair) where {V} = NamedEdge{V}(p.first, p.second) NamedEdge{V}(t::Tuple) where {V} = NamedEdge{V}(t[1], t[2]) +# TODO: Define generic `set_vertices` in `GraphsExtensions`. set_vertices(e::NamedEdge, src, dst) = NamedEdge(src, dst) diff --git a/src/namedgraph.jl b/src/namedgraph.jl index f16b23a..95a9f50 100644 --- a/src/namedgraph.jl +++ b/src/namedgraph.jl @@ -1,3 +1,19 @@ +using Dictionaries: Dictionary +using Graphs: + Graphs, + AbstractGraph, + add_edge!, + add_vertex!, + edgetype, + has_edge, + is_directed, + outneighbors, + rem_vertex!, + vertices +using Graphs.SimpleGraphs: AbstractSimpleGraph, SimpleDiGraph, SimpleGraph +using .GraphsExtensions: + GraphsExtensions, vertextype, directed_graph_type, undirected_graph_type + struct GenericNamedGraph{V,G<:AbstractSimpleGraph{Int}} <: AbstractNamedGraph{V} parent_graph::G parent_vertex_to_vertex::Vector{V} @@ -15,9 +31,9 @@ function parent_vertex_to_vertex(graph::GenericNamedGraph, parent_vertex) end # TODO: Order them according to the internal ordering? -vertices(graph::GenericNamedGraph) = keys(graph.vertex_to_parent_vertex) +Graphs.vertices(graph::GenericNamedGraph) = keys(graph.vertex_to_parent_vertex) -function add_vertex!(graph::GenericNamedGraph, vertex) +function Graphs.add_vertex!(graph::GenericNamedGraph, vertex) if vertex ∈ vertices(graph) return false end @@ -29,7 +45,7 @@ function add_vertex!(graph::GenericNamedGraph, vertex) return true end -function rem_vertex!(graph::GenericNamedGraph, vertex) +function Graphs.rem_vertex!(graph::GenericNamedGraph, vertex) if vertex ∉ vertices(graph) return false end @@ -45,12 +61,16 @@ function rem_vertex!(graph::GenericNamedGraph, vertex) return true end -function rename_vertices(f::Function, g::GenericNamedGraph) +function GraphsExtensions.rename_vertices(f::Function, g::GenericNamedGraph) # TODO: Could be `set_vertices(g, f.(g.parent_vertex_to_vertex))`. return GenericNamedGraph(g.parent_graph, f.(g.parent_vertex_to_vertex)) end -function convert_vertextype(V::Type, graph::GenericNamedGraph) +function GraphsExtensions.rename_vertices(f::Function, g::AbstractSimpleGraph) + return rename_vertices(f, GenericNamedGraph(g)) +end + +function GraphsExtensions.convert_vertextype(V::Type, graph::GenericNamedGraph) return GenericNamedGraph( parent_graph(graph), convert(Vector{V}, graph.parent_vertex_to_vertex) ) @@ -168,25 +188,21 @@ GenericNamedGraph() = GenericNamedGraph(Any[]) # TODO: implement as: # graph = set_parent_graph(graph, copy(parent_graph(graph))) # graph = set_vertices(graph, copy(vertices(graph))) -function copy(graph::GenericNamedGraph) +function Base.copy(graph::GenericNamedGraph) return GenericNamedGraph(copy(graph.parent_graph), copy(graph.parent_vertex_to_vertex)) end -edgetype(G::Type{<:GenericNamedGraph}) = NamedEdge{vertextype(G)} -edgetype(graph::GenericNamedGraph) = edgetype(typeof(graph)) - -function set_vertices(graph::GenericNamedGraph, vertices) - return GenericNamedGraph(parent_graph(graph), vertices) -end +Graphs.edgetype(G::Type{<:GenericNamedGraph}) = NamedEdge{vertextype(G)} +Graphs.edgetype(graph::GenericNamedGraph) = edgetype(typeof(graph)) -function directed_graph_type(G::Type{<:GenericNamedGraph}) - return GenericNamedGraph{vertextype(G),directed_graph(parent_graph_type(G))} +function GraphsExtensions.directed_graph_type(G::Type{<:GenericNamedGraph}) + return GenericNamedGraph{vertextype(G),directed_graph_type(parent_graph_type(G))} end -function undirected_graph_type(G::Type{<:GenericNamedGraph}) - return GenericNamedGraph{vertextype(G),undirected_graph(parent_graph_type(G))} +function GraphsExtensions.undirected_graph_type(G::Type{<:GenericNamedGraph}) + return GenericNamedGraph{vertextype(G),undirected_graph_type(parent_graph_type(G))} end -is_directed(G::Type{<:GenericNamedGraph}) = is_directed(parent_graph_type(G)) +Graphs.is_directed(G::Type{<:GenericNamedGraph}) = is_directed(parent_graph_type(G)) # TODO: Implement an edgelist version function namedgraph_induced_subgraph(graph::AbstractGraph, subvertices) @@ -202,11 +218,11 @@ function namedgraph_induced_subgraph(graph::AbstractGraph, subvertices) return subgraph, nothing end -function induced_subgraph(graph::AbstractNamedGraph, subvertices) +function Graphs.induced_subgraph(graph::AbstractNamedGraph, subvertices) return namedgraph_induced_subgraph(graph, subvertices) end -function induced_subgraph(graph::AbstractNamedGraph, subvertices::Vector{<:Integer}) +function Graphs.induced_subgraph(graph::AbstractNamedGraph, subvertices::Vector{<:Integer}) return namedgraph_induced_subgraph(graph, subvertices) end diff --git a/src/requires/kahypar.jl b/src/requires/kahypar.jl deleted file mode 100644 index d7709d3..0000000 --- a/src/requires/kahypar.jl +++ /dev/null @@ -1,33 +0,0 @@ -set_partitioning_backend!(Backend"KaHyPar"()) - -# https://github.com/kahypar/KaHyPar.jl/issues/20 -KaHyPar.HyperGraph(g::SimpleGraph) = incidence_matrix(g) - -""" -partitioned_vertices(::Backend"KaHyPar", g::Graph, npartiations::Integer; objective="edge_cut", alg="kway", kwargs...) - -- default_configuration => "cut_kKaHyPar_sea20.ini" -- :edge_cut => "cut_kKaHyPar_sea20.ini" -- :connectivity => "km1_kKaHyPar_sea20.ini" -- imbalance::Number=0.03 -""" -function partitioned_vertices( - ::Backend"KaHyPar", - g::SimpleGraph, - npartitions::Integer; - objective="edge_cut", - alg="kway", - configuration=nothing, - kwargs..., -) - if isnothing(configuration) - configuration = joinpath( - pkgdir(KaHyPar), - "src", - "config", - kahypar_configurations[(objective=objective, alg=alg)], - ) - end - partitioned_verts = @suppress KaHyPar.partition(g, npartitions; configuration, kwargs...) - return groupfind(partitioned_verts .+ 1) -end diff --git a/src/requires/metis.jl b/src/requires/metis.jl deleted file mode 100644 index 2c8a319..0000000 --- a/src/requires/metis.jl +++ /dev/null @@ -1,17 +0,0 @@ -set_partitioning_backend!(Backend"Metis"()) - -""" - partitioned_vertices(::Backend"Metis", g::AbstractGraph, npartitions::Integer; alg="recursive") - -Partition the graph `G` in `n` parts. -The partition algorithm is defined by the `alg` keyword: - - :KWAY: multilevel k-way partitioning - - :RECURSIVE: multilevel recursive bisection -""" -function partitioned_vertices( - ::Backend"Metis", g::SimpleGraph, npartitions::Integer; alg="recursive", kwargs... -) - metis_alg = metis_algs[alg] - partitioned_verts = Metis.partition(g, npartitions; alg=metis_alg, kwargs...) - return groupfind(Int.(partitioned_verts)) -end diff --git a/src/shortestpaths.jl b/src/shortestpaths.jl index a7fc997..8b9398a 100644 --- a/src/shortestpaths.jl +++ b/src/shortestpaths.jl @@ -1,3 +1,6 @@ +using Dictionaries: Dictionary +using Graphs: Graphs, dijkstra_shortest_paths, weights + """ struct NamedDijkstraState{V,T} @@ -46,7 +49,7 @@ function parent_path_state_to_path_state( ) end -function _dijkstra_shortest_paths( +function namedgraph_dijkstra_shortest_paths( graph::AbstractNamedGraph, srcs, distmx=weights(graph); @@ -63,34 +66,34 @@ function _dijkstra_shortest_paths( return parent_path_state_to_path_state(graph, parent_path_state) end -function dijkstra_shortest_paths( +function Graphs.dijkstra_shortest_paths( graph::AbstractNamedGraph, srcs, distmx=weights(graph); kwargs... ) - return _dijkstra_shortest_paths(graph, srcs, distmx; kwargs...) + return namedgraph_dijkstra_shortest_paths(graph, srcs, distmx; kwargs...) end # Fix ambiguity error with `AbstractGraph` version -function dijkstra_shortest_paths( +function Graphs.dijkstra_shortest_paths( graph::AbstractNamedGraph, srcs::Vector{<:Integer}, distmx::AbstractMatrix{<:Real}=weights(graph); kwargs..., ) - return _dijkstra_shortest_paths(graph, srcs, distmx; kwargs...) + return namedgraph_dijkstra_shortest_paths(graph, srcs, distmx; kwargs...) end -function dijkstra_shortest_paths( +function Graphs.dijkstra_shortest_paths( graph::AbstractNamedGraph, vertex::Integer, distmx::AbstractMatrix; kwargs... ) - return _dijkstra_shortest_paths(graph, [vertex], distmx; kwargs...) + return namedgraph_dijkstra_shortest_paths(graph, [vertex], distmx; kwargs...) end for f in [ - :bellman_ford_shortest_paths, - :desopo_pape_shortest_paths, - :floyd_warshall_shortest_paths, - :johnson_shortest_paths, - :yen_k_shortest_paths, + :(Graphs.bellman_ford_shortest_paths), + :(Graphs.desopo_pape_shortest_paths), + :(Graphs.floyd_warshall_shortest_paths), + :(Graphs.johnson_shortest_paths), + :(Graphs.yen_k_shortest_paths), ] @eval begin function $f(graph::AbstractNamedGraph, args...; kwargs...) diff --git a/src/steiner_tree/steiner_tree.jl b/src/steiner_tree.jl similarity index 70% rename from src/steiner_tree/steiner_tree.jl rename to src/steiner_tree.jl index 7b425aa..e889ce8 100644 --- a/src/steiner_tree/steiner_tree.jl +++ b/src/steiner_tree.jl @@ -1,4 +1,7 @@ -@traitfn function steiner_tree( +using Graphs: Graphs, IsDirected, nv, steiner_tree +using SimpleTraits: SimpleTraits, @traitfn + +@traitfn function Graphs.steiner_tree( g::AbstractNamedGraph::(!IsDirected), term_vert, distmx=weights(g) ) parent_tree = steiner_tree( diff --git a/src/utils.jl b/src/utils.jl new file mode 100644 index 0000000..d292515 --- /dev/null +++ b/src/utils.jl @@ -0,0 +1 @@ +not_implemented() = error("Not implemented") diff --git a/test/Project.toml b/test/Project.toml index 82b72d8..e96a9f3 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -8,5 +8,6 @@ NamedGraphs = "678767b0-92e7-4007-89e4-4527a8725b19" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" +SymRCM = "286e6d88-80af-4590-acc9-0001b223b9bd" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Weave = "44d3d7a6-8a23-5bf8-98c5-b353f8df5ec9" diff --git a/test/test_abstractgraph.jl b/test/test_abstractgraph.jl index 5725251..79bc3ff 100644 --- a/test/test_abstractgraph.jl +++ b/test/test_abstractgraph.jl @@ -1,6 +1,17 @@ -using Test -using Graphs -using NamedGraphs +@eval module $(gensym()) +using Graphs: binary_tree, dfs_tree, edgetype, grid, path_graph +using NamedGraphs.GraphGenerators: comb_tree +using NamedGraphs.GraphsExtensions: + is_leaf, + is_path_graph, + edge_path, + leaf_vertices, + post_order_dfs_vertices, + pre_order_dfs_vertices, + vertex_path +using NamedGraphs.NamedGraphGenerators: + named_binary_tree, named_comb_tree, named_grid, named_path_graph +using Test: @test, @testset @testset "Tree graph paths" begin # undirected trees @@ -89,3 +100,4 @@ end @test !is_leaf(dng, (1, 1)) @test issetequal(leaf_vertices(dng), [(1, 2), (3, 2)]) end +end diff --git a/test/test_abstractnamedgraph.jl b/test/test_abstractnamedgraph.jl index b281782..b08398a 100644 --- a/test/test_abstractnamedgraph.jl +++ b/test/test_abstractnamedgraph.jl @@ -1,7 +1,11 @@ -using Test -using Dictionaries -using Graphs -using NamedGraphs +@eval module $(gensym()) +using Dictionaries: Dictionary +using Graphs: + DiGraph, Graph, a_star, add_edge!, edges, grid, has_edge, has_vertex, rem_edge!, vertices +using NamedGraphs: NamedGraphs, NamedDiGraph, NamedGraph +using NamedGraphs.GraphsExtensions: rename_vertices +using NamedGraphs.NamedGraphGenerators: named_grid +using Test: @test, @testset @testset "AbstractNamedGraph equality" begin # NamedGraph @@ -144,3 +148,4 @@ end @test has_edge(nddg_function, (2, "X") => (2, "Y")) @test !has_edge(nddg_function, (2, "Y") => (2, "X")) end +end diff --git a/test/test_add_rem_edges.jl b/test/test_add_rem_edges.jl index a536649..7806de5 100644 --- a/test/test_add_rem_edges.jl +++ b/test/test_add_rem_edges.jl @@ -1,7 +1,9 @@ -using Test -using NamedGraphs -using NamedGraphs: add_edges, add_edges!, rem_edges, rem_edges! -using Graphs +@eval module $(gensym()) +using NamedGraphs: NamedGraph +using NamedGraphs.GraphsExtensions: add_edges!, rem_edges! +using NamedGraphs.NamedGraphGenerators: named_grid +using Graphs: has_edge, is_connected +using Test: @test, @testset @testset "Adding and Removing Edge Lists" begin g = named_grid((2, 2)) @@ -15,3 +17,4 @@ using Graphs add_edges!(g, [(i,) => (i + 1,) for i in 1:(n - 1)]) @test is_connected(g) end +end diff --git a/test/test_examples.jl b/test/test_examples.jl index 01b7da5..7fbba04 100644 --- a/test/test_examples.jl +++ b/test/test_examples.jl @@ -1,13 +1,11 @@ -using Graphs -using NamedGraphs -using Suppressor -using Test - -examples_path = joinpath(pkgdir(NamedGraphs), "examples") -examples_to_exclude = [] -@testset "Run examples: $filename" for filename in - setdiff(readdir(examples_path), examples_to_exclude) - if endswith(filename, ".jl") - @suppress include(joinpath(examples_path, filename)) - end +@eval module $(gensym()) +using NamedGraphs: NamedGraphs +using Suppressor: @suppress +using Test: @test, @testset +filenames = filter(endswith(".jl"), readdir(joinpath(pkgdir(NamedGraphs), "examples"))) +@testset "Run examples: $filename" for filename in filenames + @test Returns(true)( + @suppress include(joinpath(pkgdir(NamedGraphs), "examples", filename)) + ) +end end diff --git a/test/test_graphdecoration.jl b/test/test_graphdecoration.jl index 899d1b8..7230be8 100644 --- a/test/test_graphdecoration.jl +++ b/test/test_graphdecoration.jl @@ -1,7 +1,8 @@ -using Test -using Graphs -using NamedGraphs -using NamedGraphs: decorate_graph_edges, decorate_graph_vertices, hexagonal_lattice_graph +@eval module $(gensym()) +using Graphs: a_star, edges, vertices +using NamedGraphs.GraphsExtensions: decorate_graph_edges, decorate_graph_vertices +using NamedGraphs.NamedGraphGenerators: named_grid, named_hexagonal_lattice_graph +using Test: @test, @testset @testset "Decorated Graphs" begin L = 4 @@ -30,7 +31,7 @@ using NamedGraphs: decorate_graph_edges, decorate_graph_vertices, hexagonal_latt @test length(a_star(g_2d_Lieb_heavy, (1, 1), (2, 2))) == 8 #Create Hexagon (loops are size 6) - g_hexagon = hexagonal_lattice_graph(3, 6) + g_hexagon = named_hexagonal_lattice_graph(3, 6) #Create Heavy Hexagon (loops are size 12) g_heavy_hexagon = decorate_graph_edges(g_hexagon) @@ -47,3 +48,4 @@ using NamedGraphs: decorate_graph_edges, decorate_graph_vertices, hexagonal_latt @test length(a_star(g_1d, (1, 1), (L, 1))) == length(a_star(g_comb, ((1,), (1, 1)), ((1,), (L, 1)))) end +end diff --git a/test/test_base.jl b/test/test_keys.jl similarity index 79% rename from test/test_base.jl rename to test/test_keys.jl index 078a586..bc195d5 100644 --- a/test/test_base.jl +++ b/test/test_keys.jl @@ -1,6 +1,7 @@ -using Dictionaries -using NamedGraphs -using Test +@eval module $(gensym()) +using Dictionaries: Dictionary +using NamedGraphs.Keys: Key +using Test: @test, @test_throws, @testset @testset "Tree Base extensions" begin @testset "Test Key indexing" begin @@ -22,3 +23,4 @@ using Test @test A["X"] == A[Key("X")] end end +end diff --git a/test/test_multidimgraph.jl b/test/test_multidimgraph.jl index d22b553..824cfb2 100644 --- a/test/test_multidimgraph.jl +++ b/test/test_multidimgraph.jl @@ -1,7 +1,8 @@ -using Graphs -using NamedGraphs -using Random -using Test +@eval module $(gensym()) +using Graphs: add_edge!, add_vertex!, grid, has_edge, has_vertex, ne, nv +using NamedGraphs: NamedGraph +using NamedGraphs.GraphsExtensions: ⊔, disjoint_union, subgraph +using Test: @test, @testset @testset "NamedGraph" begin parent_graph = grid((2, 2)) @@ -110,3 +111,4 @@ end @test ne(g) == 1 @test has_edge(g, ("X", 1) => ("Y", 2)) end +end diff --git a/test/test_namedgraph.jl b/test/test_namedgraph.jl index dd4b97c..cd008c1 100644 --- a/test/test_namedgraph.jl +++ b/test/test_namedgraph.jl @@ -1,8 +1,90 @@ -using Graphs -using GraphsFlows -using NamedGraphs -using NamedGraphs.Dictionaries -using Test +@eval module $(gensym()) +using Dictionaries: Dictionary, Indices +using Graphs: + Edge, + δ, + Δ, + a_star, + add_edge!, + add_vertex!, + adjacency_matrix, + bellman_ford_shortest_paths, + bfs_parents, + bfs_tree, + boruvka_mst, + center, + common_neighbors, + connected_components, + degree, + degree_histogram, + desopo_pape_shortest_paths, + dfs_parents, + dfs_tree, + diameter, + dijkstra_shortest_paths, + dst, + eccentricity, + edges, + edgetype, + floyd_warshall_shortest_paths, + grid, + has_edge, + has_path, + has_self_loops, + has_vertex, + indegree, + is_connected, + is_cyclic, + is_directed, + is_ordered, + johnson_shortest_paths, + kruskal_mst, + merge_vertices, + ne, + neighborhood, + neighborhood_dists, + neighbors, + nv, + outdegree, + path_digraph, + path_graph, + periphery, + prim_mst, + radius, + rem_vertex!, + spfa_shortest_paths, + src, + steiner_tree, + topological_sort_by_dfs, + vertices, + yen_k_shortest_paths +using Graphs.SimpleGraphs: SimpleDiGraph +using GraphsFlows: GraphsFlows +using NamedGraphs: NamedEdge, NamedDiGraph, NamedGraph +using NamedGraphs.GraphsExtensions: + GraphsExtensions, + ⊔, + boundary_edges, + boundary_vertices, + degrees, + eccentricities, + dijkstra_mst, + dijkstra_parents, + dijkstra_tree, + incident_edges, + indegrees, + inner_boundary_vertices, + mincut_partitions, + outdegrees, + outer_boundary_vertices, + permute_vertices, + rename_vertices, + subgraph, + symrcm_perm, + symrcm_permute +using NamedGraphs.NamedGraphGenerators: named_binary_tree, named_grid, named_path_graph +using SymRCM: SymRCM +using Test: @test, @test_broken, @testset @testset "NamedEdge" begin @test is_ordered(NamedEdge("A", "B")) @@ -504,7 +586,7 @@ end gp = symrcm_permute(g) @test g == gp - pp = symrcm(g) + pp = symrcm_perm(g) @test pp == reverse(invperm(p)) gp′ = permute_vertices(g, pp) @@ -568,3 +650,4 @@ end end end end +end diff --git a/test/test_namedgraphgenerators.jl b/test/test_namedgraphgenerators.jl index 6b086e7..2b06456 100644 --- a/test/test_namedgraphgenerators.jl +++ b/test/test_namedgraphgenerators.jl @@ -1,45 +1,47 @@ -using Test -using NamedGraphs -using NamedGraphs: hexagonal_lattice_graph, triangular_lattice_graph -using Graphs -using Random +@eval module $(gensym()) +using Graphs: edges, neighbors, vertices +using NamedGraphs.GraphsExtensions: is_path_graph +using NamedGraphs.NamedGraphGenerators: + named_hexagonal_lattice_graph, named_triangular_lattice_graph +using Test: @test, @testset @testset "Named Graph Generators" begin - g = hexagonal_lattice_graph(1, 1) + g = named_hexagonal_lattice_graph(1, 1) #Should just be 1 hexagon @test is_path_graph(g) #Check consistency with the output of hexagonal_lattice_graph(7,7) in networkx - g = hexagonal_lattice_graph(7, 7) + g = named_hexagonal_lattice_graph(7, 7) @test length(vertices(g)) == 126 @test length(edges(g)) == 174 #Check all vertices have degree 3 in the periodic case - g = hexagonal_lattice_graph(6, 6; periodic=true) + g = named_hexagonal_lattice_graph(6, 6; periodic=true) degree_dist = [length(neighbors(g, v)) for v in vertices(g)] @test all(d -> d == 3, degree_dist) - g = triangular_lattice_graph(1, 1) + g = named_triangular_lattice_graph(1, 1) #Should just be 1 triangle @test is_path_graph(g) - g = hexagonal_lattice_graph(2, 1) + g = named_hexagonal_lattice_graph(2, 1) dims = maximum(vertices(g)) @test dims[1] > dims[2] - g = triangular_lattice_graph(2, 1) + g = named_triangular_lattice_graph(2, 1) dims = maximum(vertices(g)) @test dims[1] > dims[2] #Check consistency with the output of triangular_lattice_graph(7,7) in networkx - g = triangular_lattice_graph(7, 7) + g = named_triangular_lattice_graph(7, 7) @test length(vertices(g)) == 36 @test length(edges(g)) == 84 #Check all vertices have degree 6 in the periodic case - g = triangular_lattice_graph(6, 6; periodic=true) + g = named_triangular_lattice_graph(6, 6; periodic=true) degree_dist = [length(neighbors(g, v)) for v in vertices(g)] @test all(d -> d == 6, degree_dist) end +end diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index c103a48..49db662 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -1,18 +1,47 @@ -using Test -using NamedGraphs -using NamedGraphs: +@eval module $(gensym()) +using Graphs: + center, + diameter, + edges, + has_vertex, + is_connected, + is_tree, + ne, + nv, + radius, + random_regular_graph, + rem_vertex!, + vertices +using Metis: Metis +using NamedGraphs: NamedEdge, NamedGraph +using NamedGraphs.GraphsExtensions: + add_edges!, + add_vertices!, + boundary_edges, + default_root_vertex, + forest_cover, + is_path_graph, + is_self_loop, spanning_forest, spanning_tree, - forest_cover, + subgraph, + vertextype +using NamedGraphs.NamedGraphGenerators: + named_comb_tree, named_grid, named_triangular_lattice_graph +using NamedGraphs.PartitionedGraphs: PartitionEdge, + PartitionedGraph, PartitionVertex, boundary_partitionedges, - parent, - default_root_vertex, - triangular_lattice_graph, - add_edges! -using Dictionaries -using Graphs + partitioned_graph, + partitionedge, + partitionedges, + partitionvertex, + partitionvertices, + unpartitioned_graph +using Dictionaries: Dictionary, dictionary +using Pkg: Pkg +using Test: @test, @testset @testset "Test Partitioned Graph Constructors" begin nx, ny = 10, 10 @@ -147,14 +176,13 @@ end end @testset "Test NamedGraphs Functions on Partitioned Graph" begin - functions = [is_tree, default_root_vertex, center, diameter, radius] - gs = [ + functions = (is_tree, default_root_vertex, center, diameter, radius) + gs = ( named_comb_tree((4, 4)), named_grid((2, 2, 2)), NamedGraph(random_regular_graph(12, 3)), - triangular_lattice_graph(7, 7), - ] - + named_triangular_lattice_graph(7, 7), + ) for f in functions for g in gs pg = PartitionedGraph(g, [vertices(g)]) @@ -166,3 +194,20 @@ end end end end + +@testset "Graph partitioning" begin + g = named_grid((4, 4)) + npartitions = 4 + backends = ["metis"] + if !Sys.iswindows() + # `KaHyPar` doesn't work on Windows. + Pkg.add("KaHyPar"; io=devnull) + push!(backends, "kahypar") + end + for backend in backends + pg = PartitionedGraph(g; npartitions, backend="metis") + @test pg isa PartitionedGraph + @test nv(partitioned_graph(pg)) == npartitions + end +end +end diff --git a/test/test_random_bfs_tree.jl b/test/test_random_bfs_tree.jl index 2b2495e..dead373 100644 --- a/test/test_random_bfs_tree.jl +++ b/test/test_random_bfs_tree.jl @@ -1,26 +1,24 @@ -using Test -using NamedGraphs -using NamedGraphs: random_bfs_tree -using Graphs -using Random +@eval module $(gensym()) +using Graphs: bfs_tree, edges, is_connected, vertices +using NamedGraphs.GraphsExtensions: random_bfs_tree +using NamedGraphs.NamedGraphGenerators: named_grid +using Random: Random +using Test: @test, @testset @testset "Random Bfs Tree" begin g = named_grid((10, 10)) - s = (5, 5) - Random.seed!(1234) g_randtree1 = random_bfs_tree(g, s) g_nonrandtree1 = bfs_tree(g, s) Random.seed!(1434) g_randtree2 = random_bfs_tree(g, s) g_nonrandtree2 = bfs_tree(g, s) - @test length(edges(g_randtree1)) == length(vertices(g_randtree1)) - 1 && is_connected(g_randtree1) @test length(edges(g_randtree2)) == length(vertices(g_randtree2)) - 1 && is_connected(g_randtree2) - @test edges(g_randtree1) != edges(g_randtree2) @test edges(g_nonrandtree1) == edges(g_nonrandtree2) end +end diff --git a/test/test_staticgraphs.jl b/test/test_staticgraphs.jl index fa77f2d..bd682fd 100644 --- a/test/test_staticgraphs.jl +++ b/test/test_staticgraphs.jl @@ -1,7 +1,9 @@ -using Test -using Graphs -using NamedGraphs -using Random +@eval module $(gensym()) +using Graphs: ne, neighbors, nv, vertices +using NamedGraphs.GraphGenerators: comb_tree +using NamedGraphs.NamedGraphGenerators: named_comb_tree +using Random: Random +using Test: @test, @testset @testset "Comb tree constructors" begin Random.seed!(1234) @@ -12,7 +14,7 @@ using Random @test nv(ct1) == prod(dim) @test ne(ct1) == prod(dim) - 1 nct1 = named_comb_tree(dim) - for v in Graphs.vertices(nct1) # naming collising with other test + for v in vertices(nct1) for n in neighbors(nct1, v) if v[2] == 1 @test ((abs.(v .- n) == (1, 0)) ⊻ (abs.(v .- n) == (0, 1))) @@ -28,7 +30,7 @@ using Random @test nv(ct2) == sum(tooth_lengths) @test ne(ct2) == sum(tooth_lengths) - 1 nct2 = named_comb_tree(tooth_lengths) - for v in Graphs.vertices(nct2) # naming collising with other test + for v in vertices(nct2) for n in neighbors(nct2, v) if v[2] == 1 @test ((abs.(v .- n) == (1, 0)) ⊻ (abs.(v .- n) == (0, 1))) @@ -38,3 +40,4 @@ using Random end end end +end diff --git a/test/test_trees_and_forests.jl b/test/test_trees_and_forests.jl index a42a775..d86feaf 100644 --- a/test/test_trees_and_forests.jl +++ b/test/test_trees_and_forests.jl @@ -1,32 +1,29 @@ -using Test -using Graphs -using NamedGraphs -using NamedGraphs: all_edges, forest_cover, spanning_tree +@eval module $(gensym()) +using Test: @test, @testset +using Graphs: connected_components, edges, is_tree, vertices +using NamedGraphs: NamedGraph +using NamedGraphs.GraphsExtensions: GraphsExtensions, all_edges, forest_cover, spanning_tree +using NamedGraphs.NamedGraphGenerators: + named_comb_tree, named_grid, named_hexagonal_lattice_graph, named_triangular_lattice_graph -module TestTreesAndForests -using NamedGraphs -using NamedGraphs: hexagonal_lattice_graph, triangular_lattice_graph gs = [ ("Chain", named_grid((6, 1))), ("Cubic Lattice", named_grid((3, 3, 3))), - ("Hexagonal Grid", hexagonal_lattice_graph(6, 6)), + ("Hexagonal Grid", named_hexagonal_lattice_graph(6, 6)), ("Comb Tree", named_comb_tree((4, 4))), ("Square lattice", named_grid((10, 10))), - ("Triangular Grid", triangular_lattice_graph(5, 5; periodic=true)), + ("Triangular Grid", named_triangular_lattice_graph(5, 5; periodic=true)), ] -algs = [NamedGraphs.BFS(), NamedGraphs.DFS(), NamedGraphs.RandomBFS()] -end - -@testset "Test Spanning Trees $g_string, $alg" for (g_string, g) in TestTreesAndForests.gs, - alg in TestTreesAndForests.algs +algs = (GraphsExtensions.BFS(), GraphsExtensions.DFS(), GraphsExtensions.RandomBFS()) +@testset "Test Spanning Trees $g_string, $alg" for (g_string, g) in gs, alg in algs s_tree = spanning_tree(g; alg) @test is_tree(s_tree) @test issetequal(vertices(s_tree), vertices(g)) @test issubset(all_edges(s_tree), all_edges(g)) end -@testset "Test Forest Cover $g_string" for (g_string, g) in TestTreesAndForests.gs +@testset "Test Forest Cover $g_string" for (g_string, g) in gs cover = forest_cover(g) cover_edges = reduce(vcat, edges.(cover)) @test issetequal(cover_edges, edges(g)) @@ -36,3 +33,4 @@ end @test all(is_tree.(trees)) end end +end