From cf15123ea00f9fc185121912140173baf52562e0 Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 8 Dec 2023 16:07:27 -0500 Subject: [PATCH 01/32] New PartitionedGraph Interface and Type --- src/NamedGraphs.jl | 9 ++ src/partitioning/abstractpartitionedge.jl | 6 + src/partitioning/abstractpartitionedgraph.jl | 116 ++++++++++++++++++ src/partitioning/abstractpartitionvertex.jl | 3 + src/partitioning/partitionedge.jl | 8 ++ src/partitioning/partitionedgraph.jl | 118 +++++++++++++++++++ src/partitioning/partitionvertex.jl | 5 + src/traversals/trees_and_forests.jl | 4 +- test/test_partitionedgraph.jl | 75 ++++++++++++ 9 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 src/partitioning/abstractpartitionedge.jl create mode 100644 src/partitioning/abstractpartitionedgraph.jl create mode 100644 src/partitioning/abstractpartitionvertex.jl create mode 100644 src/partitioning/partitionedge.jl create mode 100644 src/partitioning/partitionedgraph.jl create mode 100644 src/partitioning/partitionvertex.jl create mode 100644 test/test_partitionedgraph.jl diff --git a/src/NamedGraphs.jl b/src/NamedGraphs.jl index 8de2717..2557007 100644 --- a/src/NamedGraphs.jl +++ b/src/NamedGraphs.jl @@ -119,11 +119,20 @@ 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("partitioning", "abstractpartitionedge.jl")) +include(joinpath("partitioning", "partitionedge.jl")) +include(joinpath("partitioning", "abstractpartitionvertex.jl")) +include(joinpath("partitioning", "partitionvertex.jl")) +include(joinpath("partitioning", "abstractpartitionedgraph.jl")) +include(joinpath("partitioning", "partitionedgraph.jl")) # TODO: reexport Graphs.jl (except for `Graphs.contract`) export NamedGraph, NamedDiGraph, NamedEdge, + PartitionedGraph, + PartitionEdge, + PartitionVertex, Key, ⊔, named_binary_tree, diff --git a/src/partitioning/abstractpartitionedge.jl b/src/partitioning/abstractpartitionedge.jl new file mode 100644 index 0000000..ba7b6a5 --- /dev/null +++ b/src/partitioning/abstractpartitionedge.jl @@ -0,0 +1,6 @@ +abstract type AbstractPartitionEdge{V} <: AbstractNamedEdge{V} end + +edge(pe::AbstractPartitionEdge) = not_implemented() + +src(pe::AbstractPartitionEdge) = src(edge(pe)) +dst(pe::AbstractPartitionEdge) = dst(edge(pe)) diff --git a/src/partitioning/abstractpartitionedgraph.jl b/src/partitioning/abstractpartitionedgraph.jl new file mode 100644 index 0000000..443c1df --- /dev/null +++ b/src/partitioning/abstractpartitionedgraph.jl @@ -0,0 +1,116 @@ +abstract type AbstractPartitionedGraph{V,PV} <: AbstractNamedGraph{V} end + +#Needed for interface +partitioned_graph(pg::AbstractPartitionedGraph) = not_implemented() +graph(pg::AbstractPartitionedGraph) = not_implemented() +partition_vertices(pg::AbstractPartitionedGraph, partition_vertex) = not_implemented() +partition_vertex(pg::AbstractPartitionedGraph, vertex) = not_implemented() +copy(pg::AbstractPartitionedGraph) = not_implemented() +function add_to_vertex_map!(pg::AbstractPartitionedGraph, vertex, partition_vertex) + return not_implemented() +end +rem_from_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() + +vertices(pg::AbstractPartitionedGraph) = vertices(graph(pg)) +parent_graph(pg::AbstractPartitionedGraph) = parent_graph(graph(pg)) +function vertex_to_parent_vertex(pg::AbstractPartitionedGraph, vertex) + return vertex_to_parent_vertex(graph(pg), vertex) +end +edgetype(pg::AbstractPartitionedGraph) = edgetype(graph(pg)) +function parent_graph_type(G::Type{<:AbstractPartitionedGraph}) + return fieldtype(fieldtype(G, :graph), :parent_graph) +end +directed_graph(G::Type{<:AbstractPartitionedGraph}) = directed_graph(fieldtype(G, :graph)) +function undirected_graph(G::Type{<:AbstractPartitionedGraph}) + return unddirected_graph(fieldtype(G, :graph)) +end +function has_vertex(pg::AbstractPartitionedGraph, pv::AbstractPartitionVertex) + return has_vertex(partitioned_graph(pg), underlying_vertex(pv)) +end +function has_edge(pg::AbstractPartitionedGraph, pe::AbstractPartitionEdge) + return has_edge(partitioned_graph(pg), edge(pe)) +end + +function partition_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) + return edgetype(partitioned_graph(pg))( + partition_vertex(pg, src(edge)) => partition_vertex(pg, dst(edge)) + ) +end + +function is_boundary_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) + p_edge = partition_edge(pg, edge) + return src(p_edge) == dst(p_edge) +end + +function partition_edges(pg::AbstractPartitionedGraph, p_edge) + psrc_vs, pdst_vs = partition_vertices(pg, src(p_edge)), + partition_vertices(pg, dst(p_edge)) + psrc_subgraph, pdst_subgraph, full_subgraph = subgraph(graph(pg), psrc_vs), + subgraph(pg, pdst_vs), + subgraph(pg, vcat(psrc_vs, pdst_vs)) + + return setdiff( + NamedGraphs.edges(full_subgraph), + vcat(NamedGraphs.edges(psrc_subgraph), NamedGraphs.edges(pdst_subgraph)), + ) +end + +function partition_edges(pg::AbstractPartitionedGraph, p_edge::AbstractPartitionEdge) + return partition_edges(pg, edge(p_edge)) +end + +function add_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) + add_edge!(graph(pg), edge) + pg_edge = partition_edge(pg, edge) + if src(pg_edge) != dst(pg_edge) + add_edge!(partitioned_graph(pg), pg_edge) + end +end + +function rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) + pg_edge = partition_edge(pg, edge) + if has_edge(partitioned_graph(pg), pg_edge) + g_edges = partition_edges(pg, pg_edge) + if length(g_edges) == 1 + rem_edge!(partitioned_graph(pg), pg_edge) + end + end + + return rem_edge!(graph(pg), edge) +end + +#Vertex addition and removal. I think it's important not to allow addition of a vertex without specification of PV +function add_vertex!(pg::AbstractPartitionedGraph, vertex, partition_vertex) + add_vertex!(graph(pg), vertex) + if partition_vertex ∉ vertices(partitioned_graph(pg)) + add_vertex!(partitioned_graph(pg), partition_vertex) + end + + return add_to_vertex_map!(pg, vertex, partition_vertex) +end + +function add_vertices!( + pg::AbstractPartitionedGraph, vertices::Vector, partition_vertices::Vector +) + @assert length(vertices) == length(partition_vertices) + for (v, pv) in zip(vertices, partition_vertices) + add_vertex!(pg, v, pv) + end +end + +function add_vertices!(pg::AbstractPartitionedGraph, vertices::Vector, partition_vertex) + return add_vertices!(pg, vertices, fill(partition_vertex, length(vertices))) +end + +function rem_vertex!(pg::AbstractPartitionedGraph, vertex) + rem_vertex!(graph(pg), vertex) + pv = partition_vertex(pg, vertex) + if length(partition_vertices(pg, pv)) == 1 + rem_vertex!(partitioned_graph(pg), pv) + end + return rem_from_vertex_map!(pg, vertex) +end + +function add_vertex!(pg::AbstractPartitionedGraph, vertex) + return error("Need to specify a partition where the new vertex will go.") +end diff --git a/src/partitioning/abstractpartitionvertex.jl b/src/partitioning/abstractpartitionvertex.jl new file mode 100644 index 0000000..18181ce --- /dev/null +++ b/src/partitioning/abstractpartitionvertex.jl @@ -0,0 +1,3 @@ +abstract type AbstractPartitionVertex{V} <: Any where {V} end + +underlying_vertex(pv::AbstractPartitionVertex) = not_implemented() diff --git a/src/partitioning/partitionedge.jl b/src/partitioning/partitionedge.jl new file mode 100644 index 0000000..d7ea163 --- /dev/null +++ b/src/partitioning/partitionedge.jl @@ -0,0 +1,8 @@ +struct PartitionEdge{V,E<:AbstractEdge{V}} <: AbstractPartitionEdge{V} + edge::E +end + +edge(pe::PartitionEdge) = getfield(pe, :edge) +src(pe::PartitionEdge) = PartitionVertex(src(edge(pe))) +dst(pe::PartitionEdge) = PartitionVertex(dst(edge(pe))) +PartitionEdge(p::Pair) = PartitionEdge(NamedEdge(p.first => p.second)) diff --git a/src/partitioning/partitionedgraph.jl b/src/partitioning/partitionedgraph.jl new file mode 100644 index 0000000..d550a16 --- /dev/null +++ b/src/partitioning/partitionedgraph.jl @@ -0,0 +1,118 @@ +struct PartitionedGraph{V,PV,G<:AbstractNamedGraph{V},PG<:AbstractNamedGraph{PV}} <: + AbstractPartitionedGraph{V,PV} + graph::G + partitioned_graph::PG + partition_vertices::Dictionary{PV,Vector{V}} + partition_vertex::Dictionary{V,PV} +end + +##Constructors. +function PartitionedGraph{V,PV,G,PG}( + g::AbstractNamedGraph{V}, partition_vertices::Dictionary{PV,Vector{V}} +) where {V,PV,G<:AbstractNamedGraph{V},PG<:AbstractNamedGraph{PV}} + pvs = keys(partition_vertices) + pg = NamedGraph(pvs) + partition_vertex = Dictionary{V,PV}() + for v in vertices(g) + v_pvs = Set(findall(pv -> v ∈ partition_vertices[pv], pvs)) + @assert length(v_pvs) == 1 + insert!(partition_vertex, v, first(v_pvs)) + end + + for e in edges(g) + pv_src, pv_dst = partition_vertex[src(e)], partition_vertex[dst(e)] + pe = NamedEdge(pv_src => pv_dst) + if pv_src != pv_dst && !has_edge(pg, pe) + add_edge!(pg, pe) + end + end + + return PartitionedGraph(g, pg, partition_vertices, partition_vertex) +end + +function PartitionedGraph( + g::AbstractNamedGraph{V}, partition_vertices::Dictionary{PV,Vector{V}} +) where {V,PV} + return PartitionedGraph{V,PV,NamedGraph{V},NamedGraph{PV}}(g, partition_vertices) +end + +function PartitionedGraph{V,Int64,G,NamedGraph{Int64}}( + g::AbstractNamedGraph{V}, partition_vertices::Vector{Vector{V}} +) where {V,G<:NamedGraph{V}} + partition_vertices_dict = Dictionary{Int64,Vector{V}}( + [i for i in 1:length(partition_vertices)], partition_vertices + ) + return PartitionedGraph{V,Int64,NamedGraph{V},NamedGraph{Int64}}( + g, partition_vertices_dict + ) +end + +function PartitionedGraph( + g::AbstractNamedGraph{V}, partition_vertices::Vector{Vector{V}} +) where {V} + return PartitionedGraph{V,Int64,NamedGraph{V},NamedGraph{Int64}}(g, partition_vertices) +end + +function PartitionedGraph{V,V,G,G}(vertices::Vector{V}) where {V,G<:NamedGraph{V}} + return PartitionedGraph(NamedGraph{V}(vertices), [[v] for v in vertices]) +end + +function PartitionedGraph(vertices::Vector{V}) where {V} + return PartitionedGraph{V,V,NamedGraph{V},NamedGraph{V}}(vertices) +end + +#Needed for interface +partitioned_graph(pg::PartitionedGraph) = getfield(pg, :partitioned_graph) +graph(pg::PartitionedGraph) = getfield(pg, :graph) +partition_vertices(pg::PartitionedGraph) = getfield(pg, :partition_vertices) +partition_vertex(pg::PartitionedGraph) = getfield(pg, :partition_vertex) +function partition_vertices(pg::PartitionedGraph, partition_vertex) + return partition_vertices(pg)[partition_vertex] +end +partition_vertex(pg::PartitionedGraph, vertex) = partition_vertex(pg)[vertex] +function copy(pg::PartitionedGraph) + return PartitionedGraph( + copy(graph(pg)), + copy(partitioned_graph(pg)), + copy(partition_vertices(pg)), + copy(partition_vertex(pg)), + ) +end + +function add_to_vertex_map!(pg::PartitionedGraph, vertex, partition_vertex) + if partition_vertex ∉ keys(partition_vertices(pg)) + insert!(partition_vertices(pg), partition_vertex, [vertex]) + else + pg.partition_vertices[partition_vertex] = unique( + vcat(partition_vertices(pg, partition_vertex), [vertex]) + ) + end + + return insert!(NamedGraphs.partition_vertex(pg), vertex, partition_vertex) +end + +function rem_from_vertex_map!(pg::PartitionedGraph, vertex) + pv = partition_vertex(pg, vertex) + vs = partition_vertices(pg, pv) + delete!(partition_vertices(pg), pv) + if length(vs) != 1 + insert!(partition_vertices(pg), pv, setdiff(vs, [vertex])) + end + return delete!(pg.partition_vertex, vertex) +end + +### PartitionedGraph Specific Functions +function induced_subgraph(pg::PartitionedGraph, vertices::Vector) + sub_pg_graph, _ = induced_subgraph(graph(pg), vertices) + sub_partition_vertices = deepcopy(partition_vertices(pg)) + for pv in NamedGraphs.vertices(partitioned_graph(pg)) + vs = intersect(vertices, sub_partition_vertices[pv]) + if !isempty(vs) + sub_partition_vertices[pv] = vs + else + delete!(sub_partition_vertices, pv) + end + end + + return PartitionedGraph(sub_pg_graph, sub_partition_vertices), nothing +end diff --git a/src/partitioning/partitionvertex.jl b/src/partitioning/partitionvertex.jl new file mode 100644 index 0000000..eef4e16 --- /dev/null +++ b/src/partitioning/partitionvertex.jl @@ -0,0 +1,5 @@ +struct PartitionVertex{V} <: AbstractPartitionVertex{V} + vertex::V +end + +underlying_vertex(pv::PartitionVertex) = getfield(pv, :vertex) diff --git a/src/traversals/trees_and_forests.jl b/src/traversals/trees_and_forests.jl index de433ee..7f9c996 100644 --- a/src/traversals/trees_and_forests.jl +++ b/src/traversals/trees_and_forests.jl @@ -35,7 +35,7 @@ end #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) - return reduce(union, (spanning_tree(g[vs]) for vs in connected_components(g))) + 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 @@ -54,3 +54,5 @@ function forest_cover(g::AbstractNamedGraph; spanning_tree=spanning_tree) return forests end + +#forest_cover(g::PartitionedGraph; kwargs...) = not_implemented() diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl new file mode 100644 index 0000000..5bd59af --- /dev/null +++ b/test/test_partitionedgraph.jl @@ -0,0 +1,75 @@ +using Test +using NamedGraphs +using NamedGraphs: + spanning_forest, + subvertices, + spanning_tree, + forest_cover, + rem_edge!, + PartitionEdge, + rem_edges!, + underlying_vertex +using Dictionaries +using Graphs + +@testset "Test Partition Constructors" begin + nx, ny = 10, 10 + g = named_grid((nx, ny)) + + partitions = [[(i, j) for j in 1:ny] for i in 1:nx] + pg = PartitionedGraph(g, partitions) + @test vertextype(pg.partitioned_graph) == Int64 + @test vertextype(pg.graph) == vertextype(g) + + partition_dict = Dictionary([first(partition) for partition in partitions], partitions) + pg = PartitionedGraph(g, partition_dict) + @test vertextype(pg.partitioned_graph) == vertextype(g) + @test vertextype(pg.graph) == vertextype(g) + + pg = PartitionedGraph([i for i in 1:nx]) + @test pg.graph == pg.partitioned_graph +end + +@testset "Test Partition Addition and Removal" begin + nx, ny = 10, 10 + g = named_grid((nx, ny)) + + partitions = [[(i, j) for j in 1:ny] for i in 1:nx] + pg = PartitionedGraph(g, partitions) + + pv = 5 + v_set = pg.partition_vertices[pv] + edges_involving_v_set = filter( + e -> !isempty(intersect(v_set, [src(e), dst(e)])), edges(pg) + ) + + #Strip the middle column from pg + NamedGraphs.rem_vertices!(pg, v_set) + @test !is_connected(pg.graph) && !is_connected(pg.partitioned_graph) + @test !haskey(pg.partition_vertices, pv) + @test !has_vertex(pg, PartitionVertex(pv)) + + #Add the column back + NamedGraphs.add_vertices!(pg, v_set, pv) + NamedGraphs.add_edges!(pg, edges_involving_v_set) + @test is_connected(pg.graph) && is_path_graph(pg.partitioned_graph) + @test haskey(pg.partition_vertices, pv) + @test has_vertex(pg, PartitionVertex(pv)) +end + +@testset "Test NamedGraphs Functions on Partitioned Graph" begin + functions = [is_tree, NamedGraphs.default_root_vertex, spanning_tree, spanning_forest] + gs = [ + named_comb_tree((4, 4)), + named_grid((2, 2, 2)), + NamedGraph(random_regular_graph(12, 3)), + NamedGraphs.triangular_lattice_graph(7, 7), + ] + + for f in functions + for g in gs + pg = PartitionedGraph(g, [vertices(g)]) + @test f(pg) == f(pg.graph) + end + end +end From 4dfa7c06ff98753d219579a378718c9b0390ffbb Mon Sep 17 00:00:00 2001 From: Joey Date: Mon, 11 Dec 2023 17:00:37 -0500 Subject: [PATCH 02/32] Deeper PartitionedGraph Interface. Added KaHyPar and Metis Backending --- Project.toml | 4 + examples/partitioning.jl | 11 ++ .../abstractpartitionedge.jl | 0 .../abstractpartitionedgraph.jl | 80 ++++++++++--- .../abstractpartitionvertex.jl | 0 .../partitionedgraphs}/partitionedge.jl | 0 .../partitionedgraphs}/partitionedgraph.jl | 8 ++ src/Graphs/partitionedgraphs/partitioning.jl | 106 ++++++++++++++++++ .../partitionedgraphs}/partitionvertex.jl | 0 src/NamedGraphs.jl | 24 +++- src/abstractnamedgraph.jl | 19 ++-- src/requires/kahypar.jl | 33 ++++++ src/requires/metis.jl | 33 ++++++ test/test_partitionedgraph.jl | 29 +++-- 14 files changed, 312 insertions(+), 35 deletions(-) create mode 100644 examples/partitioning.jl rename src/{partitioning => Graphs/partitionedgraphs}/abstractpartitionedge.jl (100%) rename src/{partitioning => Graphs/partitionedgraphs}/abstractpartitionedgraph.jl (62%) rename src/{partitioning => Graphs/partitionedgraphs}/abstractpartitionvertex.jl (100%) rename src/{partitioning => Graphs/partitionedgraphs}/partitionedge.jl (100%) rename src/{partitioning => Graphs/partitionedgraphs}/partitionedgraph.jl (92%) create mode 100644 src/Graphs/partitionedgraphs/partitioning.jl rename src/{partitioning => Graphs/partitionedgraphs}/partitionvertex.jl (100%) create mode 100644 src/requires/kahypar.jl create mode 100644 src/requires/metis.jl diff --git a/Project.toml b/Project.toml index 10d1188..465b690 100644 --- a/Project.toml +++ b/Project.toml @@ -9,9 +9,11 @@ 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" SimpleTraits = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66" +Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" SymRCM = "286e6d88-80af-4590-acc9-0001b223b9bd" [compat] @@ -24,6 +26,8 @@ SimpleTraits = "0.9" SparseArrays = "1.7" SplitApplyCombine = "1.2.2" SymRCM = "0.2.1" +Suppressor = "0.2" +Requires = "1.3" julia = "1.7" [extras] diff --git a/examples/partitioning.jl b/examples/partitioning.jl new file mode 100644 index 0000000..7f9a2fc --- /dev/null +++ b/examples/partitioning.jl @@ -0,0 +1,11 @@ +using Graphs +using KaHyPar +using Metis + +g = named_grid((4,4)) +npartitions = 4 + +pg_kahypar = PartitionedGraph(g; npartitions, backend="KaHyPar") +pg_metis = PartitionedGraph(g; npartitions, backend="KaHyPar") + +@show length(vertices(pg_kahypar.partitioned_graph)) == length(vertices(pg_metis.partitioned_graph)) diff --git a/src/partitioning/abstractpartitionedge.jl b/src/Graphs/partitionedgraphs/abstractpartitionedge.jl similarity index 100% rename from src/partitioning/abstractpartitionedge.jl rename to src/Graphs/partitionedgraphs/abstractpartitionedge.jl diff --git a/src/partitioning/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl similarity index 62% rename from src/partitioning/abstractpartitionedgraph.jl rename to src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index 443c1df..bad270e 100644 --- a/src/partitioning/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -24,11 +24,11 @@ directed_graph(G::Type{<:AbstractPartitionedGraph}) = directed_graph(fieldtype(G function undirected_graph(G::Type{<:AbstractPartitionedGraph}) return unddirected_graph(fieldtype(G, :graph)) end -function has_vertex(pg::AbstractPartitionedGraph, pv::AbstractPartitionVertex) - return has_vertex(partitioned_graph(pg), underlying_vertex(pv)) +function has_partition_vertex(pg::AbstractPartitionedGraph, vertex) + return has_vertex(partitioned_graph(pg), vertex) end -function has_edge(pg::AbstractPartitionedGraph, pe::AbstractPartitionEdge) - return has_edge(partitioned_graph(pg), edge(pe)) +function has_partition_edge(pg::AbstractPartitionedGraph, edge) + return has_edge(partitioned_graph(pg), edge) end function partition_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) @@ -42,9 +42,9 @@ function is_boundary_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) return src(p_edge) == dst(p_edge) end -function partition_edges(pg::AbstractPartitionedGraph, p_edge) - psrc_vs, pdst_vs = partition_vertices(pg, src(p_edge)), - partition_vertices(pg, dst(p_edge)) +function partition_edges(pg::AbstractPartitionedGraph, partition_edge::AbstractEdge) + psrc_vs, pdst_vs = partition_vertices(pg, src(partition_edge)), + partition_vertices(pg, dst(partition_edge)) psrc_subgraph, pdst_subgraph, full_subgraph = subgraph(graph(pg), psrc_vs), subgraph(pg, pdst_vs), subgraph(pg, vcat(psrc_vs, pdst_vs)) @@ -55,10 +55,6 @@ function partition_edges(pg::AbstractPartitionedGraph, p_edge) ) end -function partition_edges(pg::AbstractPartitionedGraph, p_edge::AbstractPartitionEdge) - return partition_edges(pg, edge(p_edge)) -end - function add_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) add_edge!(graph(pg), edge) pg_edge = partition_edge(pg, edge) @@ -79,12 +75,32 @@ function rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) return rem_edge!(graph(pg), edge) end +function rem_partition_edge!(pg::AbstractPartitionedGraph, partition_edge::AbstractEdge) + rem_edges!(pg, partition_edges(pg, partition_edge)) +end + +function rem_partition_edge(pg::AbstractPartitionedGraph, partition_edge::AbstractEdge) + pg_new = deepcopy(pg) + rem_partition_edge!(pg_new, partition_edge) + return pg_new +end + +function rem_partition_edges!(pg::AbstractPartitionedGraph, partition_edges::Vector{AbstractEdge}) + for p_edge in partition_edges + rem_partition_edge!(pg, p_edge) + end +end + +function rem_partition_edges(pg::AbstractPartitionedGraph, partition_edges::Vector{AbstractEdge}) + pg_new = deepcopy(pg) + rem_partition_edges!(pg_new, partition_edges) + return pg_new +end + #Vertex addition and removal. I think it's important not to allow addition of a vertex without specification of PV function add_vertex!(pg::AbstractPartitionedGraph, vertex, partition_vertex) add_vertex!(graph(pg), vertex) - if partition_vertex ∉ vertices(partitioned_graph(pg)) - add_vertex!(partitioned_graph(pg), partition_vertex) - end + add_vertex!(partitioned_graph(pg), partition_vertex) return add_to_vertex_map!(pg, vertex, partition_vertex) end @@ -111,6 +127,42 @@ function rem_vertex!(pg::AbstractPartitionedGraph, vertex) return rem_from_vertex_map!(pg, vertex) end +function rem_partition_vertex!(pg::AbstractPartitionedGraph, partition_vertex) + rem_vertices!(pg, partition_vertices(pg, partition_vertex)) +end + +function rem_partition_vertex(pg::AbstractPartitionedGraph, partition_vertex) + pg_new = deepcopy(pg) + rem_partition_vertex!(pg_new, partition_vertex) + return pg_new +end + +function rem_partition_vertices!(pg::AbstractPartitionedGraph, partition_vertices::Vector) + for pv in partition_vertices + rem_partition_vertex!(pg, pv) + end +end + +function rem_partition_vertices(pg::AbstractPartitionedGraph, partition_vertices::Vector) + pg_new = deepcopy(rem_partition_vertex) + rem_partition_vertices!(pg_new, partition_vertices) + return pg_new +end + function add_vertex!(pg::AbstractPartitionedGraph, vertex) return error("Need to specify a partition where the new vertex will go.") end + +function (pg1::AbstractPartitionedGraph == pg2::AbstractPartitionedGraph) + + if fieldnames(typeof(pg1)) != fieldnames(typeof(pg1)) + return false + end + + for field in fieldnames(typeof(pg1)) + if getfield(pg1, field) != getfield(pg2, field) + return false + end + end + return true +end diff --git a/src/partitioning/abstractpartitionvertex.jl b/src/Graphs/partitionedgraphs/abstractpartitionvertex.jl similarity index 100% rename from src/partitioning/abstractpartitionvertex.jl rename to src/Graphs/partitionedgraphs/abstractpartitionvertex.jl diff --git a/src/partitioning/partitionedge.jl b/src/Graphs/partitionedgraphs/partitionedge.jl similarity index 100% rename from src/partitioning/partitionedge.jl rename to src/Graphs/partitionedgraphs/partitionedge.jl diff --git a/src/partitioning/partitionedgraph.jl b/src/Graphs/partitionedgraphs/partitionedgraph.jl similarity index 92% rename from src/partitioning/partitionedgraph.jl rename to src/Graphs/partitionedgraphs/partitionedgraph.jl index d550a16..c1ebaaa 100644 --- a/src/partitioning/partitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/partitionedgraph.jl @@ -61,6 +61,14 @@ function PartitionedGraph(vertices::Vector{V}) where {V} return PartitionedGraph{V,V,NamedGraph{V},NamedGraph{V}}(vertices) end +function PartitionedGraph(g::AbstractNamedGraph{V}; npartitions=nothing, + nvertices_per_partition=nothing, + backend=current_partitioning_backend(), + kwargs...) where{V} + partition_vertices = partition(g; npartitions, nvertices_per_partition, backend, kwargs...) + return PartitionedGraph(g, partition_vertices) +end + #Needed for interface partitioned_graph(pg::PartitionedGraph) = getfield(pg, :partitioned_graph) graph(pg::PartitionedGraph) = getfield(pg, :graph) diff --git a/src/Graphs/partitionedgraphs/partitioning.jl b/src/Graphs/partitionedgraphs/partitioning.jl new file mode 100644 index 0000000..2c58f4c --- /dev/null +++ b/src/Graphs/partitionedgraphs/partitioning.jl @@ -0,0 +1,106 @@ +""" +Graph partitioning backend +""" +struct Backend{T} end + +Backend(s::Symbol) = Backend{s}() +Backend(s::String) = Backend(Symbol(s)) +Backend(backend::Backend) = backend + +macro Backend_str(s) + return :(Backend{$(Expr(:quote, Symbol(s)))}) +end + +""" +Current default graph partitioning backend +""" +const CURRENT_PARTITIONING_BACKEND = Ref{Union{Missing,Backend}}(missing) + +""" +Get the graph partitioning backend +""" +current_partitioning_backend() = CURRENT_PARTITIONING_BACKEND[] + +""" +Set the graph partitioning backend +""" +function set_partitioning_backend!(backend::Union{Missing,Backend,String}) + CURRENT_PARTITIONING_BACKEND[] = Backend(backend) + 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 +) + return npartitions +end + +function _npartitions( + g::AbstractGraph, npartitions::Nothing, nvertices_per_partition::Integer +) + return nv(g) ÷ nvertices_per_partition +end + +function _npartitions(g::AbstractGraph, npartitions::Int, nvertices_per_partition::Int) + return error("Can't specify both `npartitions` and `nvertices_per_partition`") +end + +function _npartitions( + g::AbstractGraph, npartitions::Nothing, nvertices_per_partition::Nothing +) + return error("Must specify either `npartitions` or `nvertices_per_partition`") +end + +function partition( + g::Graph; + 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 + if (_npartitions(g, npartitions, nvertices_per_partition) == 1) + return group(v -> 1, collect(vertices(g))) + end + + return partition( + Backend(backend), g, _npartitions(g, npartitions, nvertices_per_partition); kwargs... + ) +end + +function partition( + g::AbstractNamedGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... +) + vertex_partitions = partition( + 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 \ No newline at end of file diff --git a/src/partitioning/partitionvertex.jl b/src/Graphs/partitionedgraphs/partitionvertex.jl similarity index 100% rename from src/partitioning/partitionvertex.jl rename to src/Graphs/partitionedgraphs/partitionvertex.jl diff --git a/src/NamedGraphs.jl b/src/NamedGraphs.jl index 2557007..78c65f6 100644 --- a/src/NamedGraphs.jl +++ b/src/NamedGraphs.jl @@ -8,6 +8,8 @@ using SimpleTraits using SparseArrays using SplitApplyCombine using SymRCM +using Suppressor +using Requires using Graphs.SimpleGraphs @@ -119,12 +121,13 @@ 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("partitioning", "abstractpartitionedge.jl")) -include(joinpath("partitioning", "partitionedge.jl")) -include(joinpath("partitioning", "abstractpartitionvertex.jl")) -include(joinpath("partitioning", "partitionvertex.jl")) -include(joinpath("partitioning", "abstractpartitionedgraph.jl")) -include(joinpath("partitioning", "partitionedgraph.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")) # TODO: reexport Graphs.jl (except for `Graphs.contract`) export NamedGraph, @@ -195,4 +198,13 @@ export NamedGraph, steiner_tree, weights + 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") + ) + end + end # module AbstractNamedGraphs diff --git a/src/abstractnamedgraph.jl b/src/abstractnamedgraph.jl index 78cf47f..d83e836 100644 --- a/src/abstractnamedgraph.jl +++ b/src/abstractnamedgraph.jl @@ -381,10 +381,7 @@ function union(graph1::AbstractNamedGraph, graph2::AbstractNamedGraph) return union_graph end -function add_vertex!(graph::AbstractNamedGraph, vertex) - if vertex ∈ vertices(graph) - throw(ArgumentError("Duplicate vertices are not allowed")) - end +function insert_vertex!(graph::AbstractNamedGraph, vertex) add_vertex!(parent_graph(graph)) # Update the vertex list push!(vertices(graph), vertex) @@ -394,6 +391,15 @@ function add_vertex!(graph::AbstractNamedGraph, vertex) return graph end +function add_vertex!(graph::AbstractNamedGraph, vertex) + if vertex ∈ vertices(graph) + return false + else + insert_vertex!(graph, vertex) + return true + end +end + function rem_vertex!(graph::AbstractNamedGraph, vertex) parent_vertex = vertex_to_parent_vertex(graph, vertex) rem_vertex!(parent_graph(graph), parent_vertex) @@ -471,9 +477,8 @@ function merge_vertices( graph::AbstractNamedGraph, merge_vertices; merged_vertex=first(merge_vertices) ) merged_graph = copy(graph) - if !has_vertex(graph, merged_vertex) - add_vertex!(merged_graph, merged_vertex) - end + add_vertex!(merged_graph, merged_vertex) + for vertex in merge_vertices for e in incident_edges(graph, vertex; dir=:both) merged_edge = rename_vertices(v -> v == vertex ? merged_vertex : v, e) diff --git a/src/requires/kahypar.jl b/src/requires/kahypar.jl new file mode 100644 index 0000000..98a1a4b --- /dev/null +++ b/src/requires/kahypar.jl @@ -0,0 +1,33 @@ +set_partitioning_backend!(Backend"KaHyPar"()) + +# https://github.com/kahypar/KaHyPar.jl/issues/20 +KaHyPar.HyperGraph(g::SimpleGraph) = incidence_matrix(g) + +""" + partition(::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 partition( + ::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 + partitions = @suppress KaHyPar.partition(g, npartitions; configuration, kwargs...) + return groupfind(partitions .+ 1) +end diff --git a/src/requires/metis.jl b/src/requires/metis.jl new file mode 100644 index 0000000..fb43b2c --- /dev/null +++ b/src/requires/metis.jl @@ -0,0 +1,33 @@ +set_partitioning_backend!(Backend"Metis"()) + +""" + partition(::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 partition( + ::Backend"Metis", g::SimpleGraph, npartitions::Integer; alg="recursive", kwargs... +) + metis_alg = metis_algs[alg] + partitions = Metis.partition(g, npartitions; alg=metis_alg, kwargs...) + return groupfind(Int.(partitions)) +end + +## #= +## Metis.partition(G, n; alg = :KWAY) +## +## 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 partition(g::Metis.Graph, npartitions::Integer) +## return Metis.partition(g, npartitions; alg=:KWAY) +## end +## +## function partition(g::Graph, npartitions::Integer) +## return partition(Metis.graph(adjacency_matrix(g)), npartitions) +## end diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index 5bd59af..e3df566 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -1,4 +1,5 @@ using Test +using KaHyPar using NamedGraphs using NamedGraphs: spanning_forest, @@ -8,11 +9,13 @@ using NamedGraphs: rem_edge!, PartitionEdge, rem_edges!, - underlying_vertex + underlying_vertex, + has_partition_vertex, + partition using Dictionaries using Graphs -@testset "Test Partition Constructors" begin +@testset "Test Partitioned Graph Constructors" begin nx, ny = 10, 10 g = named_grid((nx, ny)) @@ -30,7 +33,7 @@ using Graphs @test pg.graph == pg.partitioned_graph end -@testset "Test Partition Addition and Removal" begin +@testset "Test Partitioned Graph Vertex/Edge Addition and Removal" begin nx, ny = 10, 10 g = named_grid((nx, ny)) @@ -43,22 +46,31 @@ end e -> !isempty(intersect(v_set, [src(e), dst(e)])), edges(pg) ) - #Strip the middle column from pg + #Strip the middle column from pg via the partitioned graph vertex, and make a new pg + pg_stripped = NamedGraphs.rem_partition_vertex(pg, pv) + @test !is_connected(pg_stripped.graph) && !is_connected(pg_stripped.partitioned_graph) + @test !haskey(pg_stripped.partition_vertices, pv) + @test !has_partition_vertex(pg_stripped, pv) + + #Strip the middle column from pg directly and via the graph vertices, do it in place NamedGraphs.rem_vertices!(pg, v_set) @test !is_connected(pg.graph) && !is_connected(pg.partitioned_graph) @test !haskey(pg.partition_vertices, pv) - @test !has_vertex(pg, PartitionVertex(pv)) + @test !has_partition_vertex(pg, pv) + + #Test both are the same + @test pg == pg_stripped - #Add the column back + #Add the column back to the in place graph NamedGraphs.add_vertices!(pg, v_set, pv) NamedGraphs.add_edges!(pg, edges_involving_v_set) @test is_connected(pg.graph) && is_path_graph(pg.partitioned_graph) @test haskey(pg.partition_vertices, pv) - @test has_vertex(pg, PartitionVertex(pv)) + @test has_partition_vertex(pg, pv) end @testset "Test NamedGraphs Functions on Partitioned Graph" begin - functions = [is_tree, NamedGraphs.default_root_vertex, spanning_tree, spanning_forest] + functions = [is_tree, NamedGraphs.default_root_vertex, spanning_tree, spanning_forest, center, diameter, radius] gs = [ named_comb_tree((4, 4)), named_grid((2, 2, 2)), @@ -73,3 +85,4 @@ end end end end + From a25e288fac0de3cea721aaeda69f15394f674d44 Mon Sep 17 00:00:00 2001 From: Joey Date: Tue, 12 Dec 2023 17:05:17 -0500 Subject: [PATCH 03/32] Utilised PartionVertex and PartitionEdge Types --- examples/partitioning.jl | 5 +- .../abstractpartitionedge.jl | 7 +- .../abstractpartitionedgraph.jl | 112 +++++++++--------- .../abstractpartitionvertex.jl | 3 +- src/Graphs/partitionedgraphs/partitionedge.jl | 6 +- .../partitionedgraphs/partitionedgraph.jl | 79 +++++++++--- src/Graphs/partitionedgraphs/partitioning.jl | 2 +- .../partitionedgraphs/partitionvertex.jl | 2 +- src/NamedGraphs.jl | 16 +-- test/test_partitionedgraph.jl | 51 +++++--- 10 files changed, 176 insertions(+), 107 deletions(-) diff --git a/examples/partitioning.jl b/examples/partitioning.jl index 7f9a2fc..4cf6a60 100644 --- a/examples/partitioning.jl +++ b/examples/partitioning.jl @@ -2,10 +2,11 @@ using Graphs using KaHyPar using Metis -g = named_grid((4,4)) +g = named_grid((4, 4)) npartitions = 4 pg_kahypar = PartitionedGraph(g; npartitions, backend="KaHyPar") pg_metis = PartitionedGraph(g; npartitions, backend="KaHyPar") -@show length(vertices(pg_kahypar.partitioned_graph)) == length(vertices(pg_metis.partitioned_graph)) +@show length(vertices(pg_kahypar.partitioned_graph)) == + length(vertices(pg_metis.partitioned_graph)) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedge.jl b/src/Graphs/partitionedgraphs/abstractpartitionedge.jl index ba7b6a5..e907238 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedge.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedge.jl @@ -1,6 +1,7 @@ abstract type AbstractPartitionEdge{V} <: AbstractNamedEdge{V} end -edge(pe::AbstractPartitionEdge) = not_implemented() +parent(pe::AbstractPartitionEdge) = not_implemented() +src(pe::AbstractPartitionEdge) = not_implemented() +dst(pe::AbstractPartitionEdge) = not_implemented() -src(pe::AbstractPartitionEdge) = src(edge(pe)) -dst(pe::AbstractPartitionEdge) = dst(edge(pe)) +#Don't have the vertices wrapped. But wrap them with source and edge. diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index bad270e..61eceb8 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -3,13 +3,19 @@ abstract type AbstractPartitionedGraph{V,PV} <: AbstractNamedGraph{V} end #Needed for interface partitioned_graph(pg::AbstractPartitionedGraph) = not_implemented() graph(pg::AbstractPartitionedGraph) = not_implemented() -partition_vertices(pg::AbstractPartitionedGraph, partition_vertex) = not_implemented() +function vertices(pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex) + return not_implemented() +end partition_vertex(pg::AbstractPartitionedGraph, vertex) = not_implemented() copy(pg::AbstractPartitionedGraph) = not_implemented() function add_to_vertex_map!(pg::AbstractPartitionedGraph, vertex, partition_vertex) return not_implemented() end rem_from_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() +partition_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) = not_implemented() +function edges(pg::AbstractPartitionedGraph, partition_edge::AbstractPartitionEdge) + return not_implemented() +end vertices(pg::AbstractPartitionedGraph) = vertices(graph(pg)) parent_graph(pg::AbstractPartitionedGraph) = parent_graph(graph(pg)) @@ -24,17 +30,11 @@ directed_graph(G::Type{<:AbstractPartitionedGraph}) = directed_graph(fieldtype(G function undirected_graph(G::Type{<:AbstractPartitionedGraph}) return unddirected_graph(fieldtype(G, :graph)) end -function has_partition_vertex(pg::AbstractPartitionedGraph, vertex) - return has_vertex(partitioned_graph(pg), vertex) -end -function has_partition_edge(pg::AbstractPartitionedGraph, edge) - return has_edge(partitioned_graph(pg), edge) +function has_vertex(pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex) + return has_vertex(partitioned_graph(pg), parent(partition_vertex)) end - -function partition_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) - return edgetype(partitioned_graph(pg))( - partition_vertex(pg, src(edge)) => partition_vertex(pg, dst(edge)) - ) +function has_edge(pg::AbstractPartitionedGraph, edge::AbstractPartitionEdge) + return has_edge(partitioned_graph(pg), parent(partition_edge)) end function is_boundary_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) @@ -42,22 +42,9 @@ function is_boundary_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) return src(p_edge) == dst(p_edge) end -function partition_edges(pg::AbstractPartitionedGraph, partition_edge::AbstractEdge) - psrc_vs, pdst_vs = partition_vertices(pg, src(partition_edge)), - partition_vertices(pg, dst(partition_edge)) - psrc_subgraph, pdst_subgraph, full_subgraph = subgraph(graph(pg), psrc_vs), - subgraph(pg, pdst_vs), - subgraph(pg, vcat(psrc_vs, pdst_vs)) - - return setdiff( - NamedGraphs.edges(full_subgraph), - vcat(NamedGraphs.edges(psrc_subgraph), NamedGraphs.edges(pdst_subgraph)), - ) -end - function add_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) add_edge!(graph(pg), edge) - pg_edge = partition_edge(pg, edge) + pg_edge = parent(partition_edge(pg, edge)) if src(pg_edge) != dst(pg_edge) add_edge!(partitioned_graph(pg), pg_edge) end @@ -66,7 +53,7 @@ end function rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) pg_edge = partition_edge(pg, edge) if has_edge(partitioned_graph(pg), pg_edge) - g_edges = partition_edges(pg, pg_edge) + g_edges = edges(pg, pg_edge) if length(g_edges) == 1 rem_edge!(partitioned_graph(pg), pg_edge) end @@ -75,38 +62,46 @@ function rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) return rem_edge!(graph(pg), edge) end -function rem_partition_edge!(pg::AbstractPartitionedGraph, partition_edge::AbstractEdge) - rem_edges!(pg, partition_edges(pg, partition_edge)) +function rem_edge!(pg::AbstractPartitionedGraph, partition_edge::AbstractPartitionEdge) + return rem_edges!(pg, edges(pg, parent(partition_edge))) end -function rem_partition_edge(pg::AbstractPartitionedGraph, partition_edge::AbstractEdge) - pg_new = deepcopy(pg) +function rem_edge(pg::AbstractPartitionedGraph, partition_edge::AbstractPartitionEdge) + pg_new = copy(pg) rem_partition_edge!(pg_new, partition_edge) return pg_new end -function rem_partition_edges!(pg::AbstractPartitionedGraph, partition_edges::Vector{AbstractEdge}) - for p_edge in partition_edges - rem_partition_edge!(pg, p_edge) +function rem_partition_edges!( + pg::AbstractPartitionedGraph, partition_edges::Vector{AbstractPartitionEdge} +) + for pe in partition_edges + rem_edge!(pg, pe) end end -function rem_partition_edges(pg::AbstractPartitionedGraph, partition_edges::Vector{AbstractEdge}) - pg_new = deepcopy(pg) +function rem_partition_edges( + pg::AbstractPartitionedGraph, partition_edges::Vector{AbstractPartitionEdge} +) + pg_new = copy(pg) rem_partition_edges!(pg_new, partition_edges) return pg_new end #Vertex addition and removal. I think it's important not to allow addition of a vertex without specification of PV -function add_vertex!(pg::AbstractPartitionedGraph, vertex, partition_vertex) +function add_vertex!( + pg::AbstractPartitionedGraph, vertex, partition_vertex::AbstractPartitionVertex +) add_vertex!(graph(pg), vertex) - add_vertex!(partitioned_graph(pg), partition_vertex) + add_vertex!(partitioned_graph(pg), parent(partition_vertex)) return add_to_vertex_map!(pg, vertex, partition_vertex) end function add_vertices!( - pg::AbstractPartitionedGraph, vertices::Vector, partition_vertices::Vector + pg::AbstractPartitionedGraph, + vertices::Vector, + partition_vertices::Vector{V} where {V<:AbstractPartitionVertex}, ) @assert length(vertices) == length(partition_vertices) for (v, pv) in zip(vertices, partition_vertices) @@ -114,38 +109,46 @@ function add_vertices!( end end -function add_vertices!(pg::AbstractPartitionedGraph, vertices::Vector, partition_vertex) +function add_vertices!( + pg::AbstractPartitionedGraph, vertices::Vector, partition_vertex::AbstractPartitionVertex +) return add_vertices!(pg, vertices, fill(partition_vertex, length(vertices))) end function rem_vertex!(pg::AbstractPartitionedGraph, vertex) rem_vertex!(graph(pg), vertex) pv = partition_vertex(pg, vertex) - if length(partition_vertices(pg, pv)) == 1 - rem_vertex!(partitioned_graph(pg), pv) + if length(vertices(pg, pv)) == 1 + rem_vertex!(partitioned_graph(pg), parent(pv)) end return rem_from_vertex_map!(pg, vertex) end -function rem_partition_vertex!(pg::AbstractPartitionedGraph, partition_vertex) - rem_vertices!(pg, partition_vertices(pg, partition_vertex)) +function rem_vertex!( + pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex +) + return rem_vertices!(pg, vertices(pg, partition_vertex)) end -function rem_partition_vertex(pg::AbstractPartitionedGraph, partition_vertex) - pg_new = deepcopy(pg) - rem_partition_vertex!(pg_new, partition_vertex) +function rem_vertex(pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex) + pg_new = copy(pg) + rem_vertex!(pg_new, partition_vertex) return pg_new end -function rem_partition_vertices!(pg::AbstractPartitionedGraph, partition_vertices::Vector) +function rem_vertices!( + pg::AbstractPartitionedGraph, partition_vertices::Vector{AbstractPartitionVertex} +) for pv in partition_vertices - rem_partition_vertex!(pg, pv) + rem_vertex!(pg, pv) end end -function rem_partition_vertices(pg::AbstractPartitionedGraph, partition_vertices::Vector) - pg_new = deepcopy(rem_partition_vertex) - rem_partition_vertices!(pg_new, partition_vertices) +function rem_vertices( + pg::AbstractPartitionedGraph, partition_vertices::Vector{AbstractPartitionVertex} +) + pg_new = copy(rem_partition_vertex) + rem_vertices!(pg_new, partition_vertices) return pg_new end @@ -154,15 +157,14 @@ function add_vertex!(pg::AbstractPartitionedGraph, vertex) end function (pg1::AbstractPartitionedGraph == pg2::AbstractPartitionedGraph) - if fieldnames(typeof(pg1)) != fieldnames(typeof(pg1)) return false end for field in fieldnames(typeof(pg1)) - if getfield(pg1, field) != getfield(pg2, field) - return false - end + if getfield(pg1, field) != getfield(pg2, field) + return false + end end return true end diff --git a/src/Graphs/partitionedgraphs/abstractpartitionvertex.jl b/src/Graphs/partitionedgraphs/abstractpartitionvertex.jl index 18181ce..856da00 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionvertex.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionvertex.jl @@ -1,3 +1,4 @@ abstract type AbstractPartitionVertex{V} <: Any where {V} end -underlying_vertex(pv::AbstractPartitionVertex) = not_implemented() +#Parent, wrap, unwrap, vertex? +parent(pv::AbstractPartitionVertex) = not_implemented() diff --git a/src/Graphs/partitionedgraphs/partitionedge.jl b/src/Graphs/partitionedgraphs/partitionedge.jl index d7ea163..597641a 100644 --- a/src/Graphs/partitionedgraphs/partitionedge.jl +++ b/src/Graphs/partitionedgraphs/partitionedge.jl @@ -2,7 +2,7 @@ struct PartitionEdge{V,E<:AbstractEdge{V}} <: AbstractPartitionEdge{V} edge::E end -edge(pe::PartitionEdge) = getfield(pe, :edge) -src(pe::PartitionEdge) = PartitionVertex(src(edge(pe))) -dst(pe::PartitionEdge) = PartitionVertex(dst(edge(pe))) +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(p.first => p.second)) diff --git a/src/Graphs/partitionedgraphs/partitionedgraph.jl b/src/Graphs/partitionedgraphs/partitionedgraph.jl index c1ebaaa..aa11802 100644 --- a/src/Graphs/partitionedgraphs/partitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/partitionedgraph.jl @@ -61,11 +61,16 @@ function PartitionedGraph(vertices::Vector{V}) where {V} return PartitionedGraph{V,V,NamedGraph{V},NamedGraph{V}}(vertices) end -function PartitionedGraph(g::AbstractNamedGraph{V}; npartitions=nothing, +function PartitionedGraph( + g::AbstractNamedGraph{V}; + npartitions=nothing, nvertices_per_partition=nothing, backend=current_partitioning_backend(), - kwargs...) where{V} - partition_vertices = partition(g; npartitions, nvertices_per_partition, backend, kwargs...) + kwargs..., +) where {V} + partition_vertices = partition( + g; npartitions, nvertices_per_partition, backend, kwargs... + ) return PartitionedGraph(g, partition_vertices) end @@ -74,37 +79,64 @@ partitioned_graph(pg::PartitionedGraph) = getfield(pg, :partitioned_graph) graph(pg::PartitionedGraph) = getfield(pg, :graph) partition_vertices(pg::PartitionedGraph) = getfield(pg, :partition_vertices) partition_vertex(pg::PartitionedGraph) = getfield(pg, :partition_vertex) -function partition_vertices(pg::PartitionedGraph, partition_vertex) - return partition_vertices(pg)[partition_vertex] +function vertices(pg::PartitionedGraph, partition_vert::PartitionVertex) + return partition_vertices(pg)[parent(partition_vert)] +end +function vertices( + pg::PartitionedGraph, partition_verts::Vector{V} +) where {V<:PartitionVertex} + return unique(reduce(vcat, [vertices(pg, pv) for pv in partition_verts])) +end +function partition_vertex(pg::PartitionedGraph, vertex) + return PartitionVertex(partition_vertex(pg)[vertex]) end -partition_vertex(pg::PartitionedGraph, vertex) = partition_vertex(pg)[vertex] + +function partition_edge(pg::PartitionedGraph, edge::AbstractEdge) + return PartitionEdge( + parent(partition_vertex(pg, src(edge))) => parent(partition_vertex(pg, dst(edge))) + ) +end + +function partition_edges(pg::PartitionedGraph, partition_edge::PartitionEdge) + psrc_vs, pdst_vs = vertices(pg, PartitionVertex(src(partition_edge))), + vertices(pg, PartitionVertex(dst(partition_edge))) + psrc_subgraph, pdst_subgraph, full_subgraph = subgraph(graph(pg), psrc_vs), + subgraph(pg, pdst_vs), + subgraph(pg, vcat(psrc_vs, pdst_vs)) + + return setdiff( + NamedGraphs.edges(full_subgraph), + vcat(NamedGraphs.edges(psrc_subgraph), NamedGraphs.edges(pdst_subgraph)), + ) +end + +#Copy on dictionaries is dodgy?! function copy(pg::PartitionedGraph) return PartitionedGraph( copy(graph(pg)), copy(partitioned_graph(pg)), - copy(partition_vertices(pg)), - copy(partition_vertex(pg)), + copy_keys_values(partition_vertices(pg)), + copy_keys_values(partition_vertex(pg)), ) end -function add_to_vertex_map!(pg::PartitionedGraph, vertex, partition_vertex) - if partition_vertex ∉ keys(partition_vertices(pg)) - insert!(partition_vertices(pg), partition_vertex, [vertex]) +function add_to_vertex_map!(pg::PartitionedGraph, vertex, partition_vertex::PartitionVertex) + pv = parent(partition_vertex) + if pv ∉ keys(partition_vertices(pg)) + insert!(partition_vertices(pg), pv, [vertex]) else - pg.partition_vertices[partition_vertex] = unique( - vcat(partition_vertices(pg, partition_vertex), [vertex]) - ) + pg.partition_vertices[pv] = unique(vcat(vertices(pg, partition_vertex), [vertex])) end - return insert!(NamedGraphs.partition_vertex(pg), vertex, partition_vertex) + return insert!(NamedGraphs.partition_vertex(pg), vertex, pv) end function rem_from_vertex_map!(pg::PartitionedGraph, vertex) pv = partition_vertex(pg, vertex) - vs = partition_vertices(pg, pv) - delete!(partition_vertices(pg), pv) + vs = vertices(pg, pv) + delete!(partition_vertices(pg), parent(pv)) if length(vs) != 1 - insert!(partition_vertices(pg), pv, setdiff(vs, [vertex])) + insert!(partition_vertices(pg), parent(pv), setdiff(vs, [vertex])) end return delete!(pg.partition_vertex, vertex) end @@ -112,7 +144,7 @@ end ### PartitionedGraph Specific Functions function induced_subgraph(pg::PartitionedGraph, vertices::Vector) sub_pg_graph, _ = induced_subgraph(graph(pg), vertices) - sub_partition_vertices = deepcopy(partition_vertices(pg)) + sub_partition_vertices = copy_keys_values(partition_vertices(pg)) for pv in NamedGraphs.vertices(partitioned_graph(pg)) vs = intersect(vertices, sub_partition_vertices[pv]) if !isempty(vs) @@ -124,3 +156,12 @@ function induced_subgraph(pg::PartitionedGraph, vertices::Vector) return PartitionedGraph(sub_pg_graph, sub_partition_vertices), nothing end + +### PartitionedGraph Specific Functions +function induced_subgraph( + pg::PartitionedGraph, partition_verts::Vector{V} +) where {V<:PartitionVertex} + return induced_subgraph(pg, vertices(pg, partition_verts)) +end + +#induced_subgraph(pg, partition_vertex/vertices) diff --git a/src/Graphs/partitionedgraphs/partitioning.jl b/src/Graphs/partitionedgraphs/partitioning.jl index 2c58f4c..61f1ae0 100644 --- a/src/Graphs/partitionedgraphs/partitioning.jl +++ b/src/Graphs/partitionedgraphs/partitioning.jl @@ -103,4 +103,4 @@ function partition( parent_vertices_to_vertices(g, vertex_partition) for vertex_partition in vertex_partitions ] -end \ No newline at end of file +end diff --git a/src/Graphs/partitionedgraphs/partitionvertex.jl b/src/Graphs/partitionedgraphs/partitionvertex.jl index eef4e16..8a66ff7 100644 --- a/src/Graphs/partitionedgraphs/partitionvertex.jl +++ b/src/Graphs/partitionedgraphs/partitionvertex.jl @@ -2,4 +2,4 @@ struct PartitionVertex{V} <: AbstractPartitionVertex{V} vertex::V end -underlying_vertex(pv::PartitionVertex) = getfield(pv, :vertex) +parent(pv::PartitionVertex) = getfield(pv, :vertex) diff --git a/src/NamedGraphs.jl b/src/NamedGraphs.jl index 78c65f6..3560396 100644 --- a/src/NamedGraphs.jl +++ b/src/NamedGraphs.jl @@ -198,13 +198,13 @@ export NamedGraph, steiner_tree, weights - 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") - ) - end +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") + ) +end end # module AbstractNamedGraphs diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index e3df566..6c61897 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -9,9 +9,10 @@ using NamedGraphs: rem_edge!, PartitionEdge, rem_edges!, - underlying_vertex, - has_partition_vertex, - partition + partition, + PartitionVertex, + parent, + _npartitions using Dictionaries using Graphs @@ -40,23 +41,23 @@ end partitions = [[(i, j) for j in 1:ny] for i in 1:nx] pg = PartitionedGraph(g, partitions) - pv = 5 - v_set = pg.partition_vertices[pv] + pv = PartitionVertex(5) + v_set = vertices(pg, pv) edges_involving_v_set = filter( e -> !isempty(intersect(v_set, [src(e), dst(e)])), edges(pg) ) #Strip the middle column from pg via the partitioned graph vertex, and make a new pg - pg_stripped = NamedGraphs.rem_partition_vertex(pg, pv) + pg_stripped = NamedGraphs.rem_vertex(pg, pv) @test !is_connected(pg_stripped.graph) && !is_connected(pg_stripped.partitioned_graph) - @test !haskey(pg_stripped.partition_vertices, pv) - @test !has_partition_vertex(pg_stripped, pv) + @test !haskey(pg_stripped.partition_vertices, parent(pv)) + @test !has_vertex(pg_stripped, pv) #Strip the middle column from pg directly and via the graph vertices, do it in place NamedGraphs.rem_vertices!(pg, v_set) @test !is_connected(pg.graph) && !is_connected(pg.partitioned_graph) - @test !haskey(pg.partition_vertices, pv) - @test !has_partition_vertex(pg, pv) + @test !haskey(pg.partition_vertices, parent(pv)) + @test !has_vertex(pg, pv) #Test both are the same @test pg == pg_stripped @@ -65,12 +66,35 @@ end NamedGraphs.add_vertices!(pg, v_set, pv) NamedGraphs.add_edges!(pg, edges_involving_v_set) @test is_connected(pg.graph) && is_path_graph(pg.partitioned_graph) - @test haskey(pg.partition_vertices, pv) - @test has_partition_vertex(pg, pv) + @test haskey(pg.partition_vertices, parent(pv)) + @test has_vertex(pg, pv) +end + +@testset "Test Partitioned Graph Subgraph Functionality" begin + n, z = 20, 4 + nvertices_per_partition = 4 + g = NamedGraph(random_regular_graph(n, z)) + npartitions = _npartitions(g, nothing, nvertices_per_partition) + partitions = [ + [nvertices_per_partition * (i - 1) + j for j in 1:nvertices_per_partition] for + i in 1:npartitions + ] + pg = PartitionedGraph(g, partitions) + + pg_1 = subgraph(pg, partitions[1]) + pg_2 = subgraph(pg, [PartitionVertex(1)]) end @testset "Test NamedGraphs Functions on Partitioned Graph" begin - functions = [is_tree, NamedGraphs.default_root_vertex, spanning_tree, spanning_forest, center, diameter, radius] + functions = [ + is_tree, + NamedGraphs.default_root_vertex, + spanning_tree, + spanning_forest, + center, + diameter, + radius, + ] gs = [ named_comb_tree((4, 4)), named_grid((2, 2, 2)), @@ -85,4 +109,3 @@ end end end end - From 04a5ef99d9fd93433dae8b82fdd7cdc3ca26b078 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 13 Dec 2023 09:04:37 -0500 Subject: [PATCH 04/32] Formatting --- examples/partitioning.jl | 6 +++++- test/test_partitionedgraph.jl | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/partitioning.jl b/examples/partitioning.jl index 4cf6a60..a064dea 100644 --- a/examples/partitioning.jl +++ b/examples/partitioning.jl @@ -9,4 +9,8 @@ pg_kahypar = PartitionedGraph(g; npartitions, backend="KaHyPar") pg_metis = PartitionedGraph(g; npartitions, backend="KaHyPar") @show length(vertices(pg_kahypar.partitioned_graph)) == - length(vertices(pg_metis.partitioned_graph)) + length(vertices(pg_metis.partitioned_graph)) == + npartitions + +@show typeof(pg_kahypar) <: PartitionedGraph +@show typeof(pg_metis) <: PartitionedGraph diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index 6c61897..069dafd 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -83,6 +83,8 @@ end pg_1 = subgraph(pg, partitions[1]) pg_2 = subgraph(pg, [PartitionVertex(1)]) + + @test pg_1 == pg_2 end @testset "Test NamedGraphs Functions on Partitioned Graph" begin From e4d23ffe5da9eda99d55ba045672200583993d96 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 13 Dec 2023 09:39:29 -0500 Subject: [PATCH 05/32] Cleaned up partitioning example --- examples/partitioning.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/partitioning.jl b/examples/partitioning.jl index a064dea..507c1bf 100644 --- a/examples/partitioning.jl +++ b/examples/partitioning.jl @@ -1,4 +1,4 @@ -using Graphs +using NamedGraphs using KaHyPar using Metis @@ -6,7 +6,7 @@ g = named_grid((4, 4)) npartitions = 4 pg_kahypar = PartitionedGraph(g; npartitions, backend="KaHyPar") -pg_metis = PartitionedGraph(g; npartitions, backend="KaHyPar") +pg_metis = PartitionedGraph(g; npartitions, backend="Metis") @show length(vertices(pg_kahypar.partitioned_graph)) == length(vertices(pg_metis.partitioned_graph)) == From 295ccd821ffbc3fad01989923fc7250771c50ba3 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 13 Dec 2023 16:17:50 -0500 Subject: [PATCH 06/32] Added better vertex addition/removal options. Removed partitioning example from run_examples. --- src/abstractnamedgraph.jl | 31 ++++++++++++++++++++++++++++--- test/test_examples.jl | 4 +++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/abstractnamedgraph.jl b/src/abstractnamedgraph.jl index d83e836..d2b15eb 100644 --- a/src/abstractnamedgraph.jl +++ b/src/abstractnamedgraph.jl @@ -381,7 +381,7 @@ function union(graph1::AbstractNamedGraph, graph2::AbstractNamedGraph) return union_graph end -function insert_vertex!(graph::AbstractNamedGraph, vertex) +function set_vertex!(graph::AbstractNamedGraph, vertex) add_vertex!(parent_graph(graph)) # Update the vertex list push!(vertices(graph), vertex) @@ -391,16 +391,24 @@ function insert_vertex!(graph::AbstractNamedGraph, vertex) return graph end +function insert_vertex!(graph::AbstractNamedGraph, vertex) + if vertex ∈ vertices(graph) + error("Duplicate vertices not allowed!") + else + set_vertex!(graph, vertex) + end +end + function add_vertex!(graph::AbstractNamedGraph, vertex) if vertex ∈ vertices(graph) return false else - insert_vertex!(graph, vertex) + set_vertex!(graph, vertex) return true end end -function rem_vertex!(graph::AbstractNamedGraph, vertex) +function unset_vertex!(graph::AbstractNamedGraph, vertex) parent_vertex = vertex_to_parent_vertex(graph, vertex) rem_vertex!(parent_graph(graph), parent_vertex) @@ -420,6 +428,23 @@ function rem_vertex!(graph::AbstractNamedGraph, vertex) return graph end +function rem_vertex!(graph::AbstractNamedGraph, vertex) + if vertex ∉ vertices(graph) + return false + else + unset_vertex!(graph, vertex) + return true + end +end + +function delete_vertex!(graph::AbstractNamedGraph, vertex) + if vertex ∉ vertices(graph) + error("vertex not in graph!") + else + unset_vertex!(graph, vertex) + end +end + function add_vertices!(graph::AbstractNamedGraph, vs::Vector) for vertex in vs add_vertex!(graph, vertex) diff --git a/test/test_examples.jl b/test/test_examples.jl index 96a383e..b489ee3 100644 --- a/test/test_examples.jl +++ b/test/test_examples.jl @@ -4,7 +4,9 @@ using Suppressor using Test examples_path = joinpath(pkgdir(NamedGraphs), "examples") -@testset "Run examples: $filename" for filename in readdir(examples_path) +examples_to_exclude = ["partitioning.jl"] +@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 From 16a1834102c0d77d4491fcba87d8559bc80e95e1 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 13 Dec 2023 16:24:19 -0500 Subject: [PATCH 07/32] Removed KaHyPar dependency from test --- test/test_partitionedgraph.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index 069dafd..64e70f0 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -1,5 +1,4 @@ using Test -using KaHyPar using NamedGraphs using NamedGraphs: spanning_forest, From 9d4a99220480b090fc688e30864e0646310f4517 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 14 Dec 2023 10:08:03 -0500 Subject: [PATCH 08/32] Better naming --- .../abstractpartitionedgraph.jl | 14 ++-- .../partitionedgraphs/partitionedgraph.jl | 84 +++++++++---------- src/Graphs/partitionedgraphs/partitioning.jl | 8 +- src/requires/kahypar.jl | 2 +- src/requires/metis.jl | 2 +- test/test_partitionedgraph.jl | 7 +- 6 files changed, 57 insertions(+), 60 deletions(-) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index 61eceb8..e7b8303 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -6,12 +6,10 @@ graph(pg::AbstractPartitionedGraph) = not_implemented() function vertices(pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex) return not_implemented() end -partition_vertex(pg::AbstractPartitionedGraph, vertex) = not_implemented() +which_partition(pg::AbstractPartitionedGraph, vertex) = not_implemented() copy(pg::AbstractPartitionedGraph) = not_implemented() -function add_to_vertex_map!(pg::AbstractPartitionedGraph, vertex, partition_vertex) - return not_implemented() -end -rem_from_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() +delete_from_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() +insert_to_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() partition_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) = not_implemented() function edges(pg::AbstractPartitionedGraph, partition_edge::AbstractPartitionEdge) return not_implemented() @@ -95,7 +93,7 @@ function add_vertex!( add_vertex!(graph(pg), vertex) add_vertex!(partitioned_graph(pg), parent(partition_vertex)) - return add_to_vertex_map!(pg, vertex, partition_vertex) + return insert_to_vertex_map!(pg, vertex, partition_vertex) end function add_vertices!( @@ -117,11 +115,11 @@ end function rem_vertex!(pg::AbstractPartitionedGraph, vertex) rem_vertex!(graph(pg), vertex) - pv = partition_vertex(pg, vertex) + pv = which_partition(pg, vertex) if length(vertices(pg, pv)) == 1 rem_vertex!(partitioned_graph(pg), parent(pv)) end - return rem_from_vertex_map!(pg, vertex) + return delete_from_vertex_map!(pg, vertex) end function rem_vertex!( diff --git a/src/Graphs/partitionedgraphs/partitionedgraph.jl b/src/Graphs/partitionedgraphs/partitionedgraph.jl index aa11802..32454ae 100644 --- a/src/Graphs/partitionedgraphs/partitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/partitionedgraph.jl @@ -2,48 +2,48 @@ struct PartitionedGraph{V,PV,G<:AbstractNamedGraph{V},PG<:AbstractNamedGraph{PV} AbstractPartitionedGraph{V,PV} graph::G partitioned_graph::PG - partition_vertices::Dictionary{PV,Vector{V}} - partition_vertex::Dictionary{V,PV} + partitioned_vertices::Dictionary{PV,Vector{V}} + which_partition::Dictionary{V,PV} end ##Constructors. function PartitionedGraph{V,PV,G,PG}( - g::AbstractNamedGraph{V}, partition_vertices::Dictionary{PV,Vector{V}} + g::AbstractNamedGraph{V}, partitioned_vertices::Dictionary{PV,Vector{V}} ) where {V,PV,G<:AbstractNamedGraph{V},PG<:AbstractNamedGraph{PV}} - pvs = keys(partition_vertices) + pvs = keys(partitioned_vertices) pg = NamedGraph(pvs) - partition_vertex = Dictionary{V,PV}() + which_partition = Dictionary{V,PV}() for v in vertices(g) - v_pvs = Set(findall(pv -> v ∈ partition_vertices[pv], pvs)) + v_pvs = Set(findall(pv -> v ∈ partitioned_vertices[pv], pvs)) @assert length(v_pvs) == 1 - insert!(partition_vertex, v, first(v_pvs)) + insert!(which_partition, v, first(v_pvs)) end for e in edges(g) - pv_src, pv_dst = partition_vertex[src(e)], partition_vertex[dst(e)] + pv_src, pv_dst = which_partition[src(e)], which_partition[dst(e)] pe = NamedEdge(pv_src => pv_dst) if pv_src != pv_dst && !has_edge(pg, pe) add_edge!(pg, pe) end end - return PartitionedGraph(g, pg, partition_vertices, partition_vertex) + return PartitionedGraph(g, pg, partitioned_vertices, which_partition) end function PartitionedGraph( - g::AbstractNamedGraph{V}, partition_vertices::Dictionary{PV,Vector{V}} + g::AbstractNamedGraph{V}, partitioned_vertices::Dictionary{PV,Vector{V}} ) where {V,PV} - return PartitionedGraph{V,PV,NamedGraph{V},NamedGraph{PV}}(g, partition_vertices) + return PartitionedGraph{V,PV,NamedGraph{V},NamedGraph{PV}}(g, partitioned_vertices) end function PartitionedGraph{V,Int64,G,NamedGraph{Int64}}( - g::AbstractNamedGraph{V}, partition_vertices::Vector{Vector{V}} + g::AbstractNamedGraph{V}, partitioned_vertices::Vector{Vector{V}} ) where {V,G<:NamedGraph{V}} - partition_vertices_dict = Dictionary{Int64,Vector{V}}( - [i for i in 1:length(partition_vertices)], partition_vertices + partitioned_vertices_dict = Dictionary{Int64,Vector{V}}( + [i for i in 1:length(partitioned_vertices)], partitioned_vertices ) return PartitionedGraph{V,Int64,NamedGraph{V},NamedGraph{Int64}}( - g, partition_vertices_dict + g, partitioned_vertices_dict ) end @@ -68,32 +68,32 @@ function PartitionedGraph( backend=current_partitioning_backend(), kwargs..., ) where {V} - partition_vertices = partition( + partitioned_vertices = partition_vertices( g; npartitions, nvertices_per_partition, backend, kwargs... ) - return PartitionedGraph(g, partition_vertices) + return PartitionedGraph(g, partitioned_vertices) end #Needed for interface partitioned_graph(pg::PartitionedGraph) = getfield(pg, :partitioned_graph) graph(pg::PartitionedGraph) = getfield(pg, :graph) -partition_vertices(pg::PartitionedGraph) = getfield(pg, :partition_vertices) -partition_vertex(pg::PartitionedGraph) = getfield(pg, :partition_vertex) +partitioned_vertices(pg::PartitionedGraph) = getfield(pg, :partitioned_vertices) +which_partition(pg::PartitionedGraph) = getfield(pg, :which_partition) function vertices(pg::PartitionedGraph, partition_vert::PartitionVertex) - return partition_vertices(pg)[parent(partition_vert)] + return partitioned_vertices(pg)[parent(partition_vert)] end function vertices( pg::PartitionedGraph, partition_verts::Vector{V} ) where {V<:PartitionVertex} return unique(reduce(vcat, [vertices(pg, pv) for pv in partition_verts])) end -function partition_vertex(pg::PartitionedGraph, vertex) - return PartitionVertex(partition_vertex(pg)[vertex]) +function which_partition(pg::PartitionedGraph, vertex) + return PartitionVertex(which_partition(pg)[vertex]) end function partition_edge(pg::PartitionedGraph, edge::AbstractEdge) return PartitionEdge( - parent(partition_vertex(pg, src(edge))) => parent(partition_vertex(pg, dst(edge))) + parent(which_partition(pg, src(edge))) => parent(which_partition(pg, dst(edge))) ) end @@ -115,46 +115,48 @@ function copy(pg::PartitionedGraph) return PartitionedGraph( copy(graph(pg)), copy(partitioned_graph(pg)), - copy_keys_values(partition_vertices(pg)), - copy_keys_values(partition_vertex(pg)), + copy_keys_values(partitioned_vertices(pg)), + copy_keys_values(which_partition(pg)), ) end -function add_to_vertex_map!(pg::PartitionedGraph, vertex, partition_vertex::PartitionVertex) +function insert_to_vertex_map!( + pg::PartitionedGraph, vertex, partition_vertex::PartitionVertex +) pv = parent(partition_vertex) - if pv ∉ keys(partition_vertices(pg)) - insert!(partition_vertices(pg), pv, [vertex]) + if pv ∉ keys(partitioned_vertices(pg)) + insert!(partitioned_vertices(pg), pv, [vertex]) else - pg.partition_vertices[pv] = unique(vcat(vertices(pg, partition_vertex), [vertex])) + pg.partitioned_vertices[pv] = unique(vcat(vertices(pg, partition_vertex), [vertex])) end - return insert!(NamedGraphs.partition_vertex(pg), vertex, pv) + return insert!(NamedGraphs.which_partition(pg), vertex, pv) end -function rem_from_vertex_map!(pg::PartitionedGraph, vertex) - pv = partition_vertex(pg, vertex) +function delete_from_vertex_map!(pg::PartitionedGraph, vertex) + pv = which_partition(pg, vertex) vs = vertices(pg, pv) - delete!(partition_vertices(pg), parent(pv)) + delete!(partitioned_vertices(pg), parent(pv)) if length(vs) != 1 - insert!(partition_vertices(pg), parent(pv), setdiff(vs, [vertex])) + insert!(partitioned_vertices(pg), parent(pv), setdiff(vs, [vertex])) end - return delete!(pg.partition_vertex, vertex) + return delete!(pg.which_partition, vertex) end ### PartitionedGraph Specific Functions function induced_subgraph(pg::PartitionedGraph, vertices::Vector) sub_pg_graph, _ = induced_subgraph(graph(pg), vertices) - sub_partition_vertices = copy_keys_values(partition_vertices(pg)) + sub_partitioned_vertices = copy_keys_values(partitioned_vertices(pg)) for pv in NamedGraphs.vertices(partitioned_graph(pg)) - vs = intersect(vertices, sub_partition_vertices[pv]) + vs = intersect(vertices, sub_partitioned_vertices[pv]) if !isempty(vs) - sub_partition_vertices[pv] = vs + sub_partitioned_vertices[pv] = vs else - delete!(sub_partition_vertices, pv) + delete!(sub_partitioned_vertices, pv) end end - return PartitionedGraph(sub_pg_graph, sub_partition_vertices), nothing + return PartitionedGraph(sub_pg_graph, sub_partitioned_vertices), nothing end ### PartitionedGraph Specific Functions @@ -163,5 +165,3 @@ function induced_subgraph( ) where {V<:PartitionVertex} return induced_subgraph(pg, vertices(pg, partition_verts)) end - -#induced_subgraph(pg, partition_vertex/vertices) diff --git a/src/Graphs/partitionedgraphs/partitioning.jl b/src/Graphs/partitionedgraphs/partitioning.jl index 61f1ae0..05551b5 100644 --- a/src/Graphs/partitionedgraphs/partitioning.jl +++ b/src/Graphs/partitionedgraphs/partitioning.jl @@ -71,7 +71,7 @@ function _npartitions( return error("Must specify either `npartitions` or `nvertices_per_partition`") end -function partition( +function partition_vertices( g::Graph; npartitions=nothing, nvertices_per_partition=nothing, @@ -84,15 +84,15 @@ function partition( return group(v -> 1, collect(vertices(g))) end - return partition( + return partition_vertices( Backend(backend), g, _npartitions(g, npartitions, nvertices_per_partition); kwargs... ) end -function partition( +function partition_vertices( g::AbstractNamedGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... ) - vertex_partitions = partition( + vertex_partitions = partition_vertices( parent_graph(g); npartitions, nvertices_per_partition, kwargs... ) #[inv(vertex_to_parent_vertex(g))[v] for v in partitions] diff --git a/src/requires/kahypar.jl b/src/requires/kahypar.jl index 98a1a4b..1bf9cb5 100644 --- a/src/requires/kahypar.jl +++ b/src/requires/kahypar.jl @@ -11,7 +11,7 @@ KaHyPar.HyperGraph(g::SimpleGraph) = incidence_matrix(g) - :connectivity => "km1_kKaHyPar_sea20.ini" - imbalance::Number=0.03 """ -function partition( +function partition_vertices( ::Backend"KaHyPar", g::SimpleGraph, npartitions::Integer; diff --git a/src/requires/metis.jl b/src/requires/metis.jl index fb43b2c..db0056b 100644 --- a/src/requires/metis.jl +++ b/src/requires/metis.jl @@ -8,7 +8,7 @@ The partition algorithm is defined by the `alg` keyword: - :KWAY: multilevel k-way partitioning - :RECURSIVE: multilevel recursive bisection """ -function partition( +function partition_vertices( ::Backend"Metis", g::SimpleGraph, npartitions::Integer; alg="recursive", kwargs... ) metis_alg = metis_algs[alg] diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index 64e70f0..f0eef52 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -8,7 +8,6 @@ using NamedGraphs: rem_edge!, PartitionEdge, rem_edges!, - partition, PartitionVertex, parent, _npartitions @@ -49,13 +48,13 @@ end #Strip the middle column from pg via the partitioned graph vertex, and make a new pg pg_stripped = NamedGraphs.rem_vertex(pg, pv) @test !is_connected(pg_stripped.graph) && !is_connected(pg_stripped.partitioned_graph) - @test !haskey(pg_stripped.partition_vertices, parent(pv)) + @test !haskey(pg_stripped.partitioned_vertices, parent(pv)) @test !has_vertex(pg_stripped, pv) #Strip the middle column from pg directly and via the graph vertices, do it in place NamedGraphs.rem_vertices!(pg, v_set) @test !is_connected(pg.graph) && !is_connected(pg.partitioned_graph) - @test !haskey(pg.partition_vertices, parent(pv)) + @test !haskey(pg.partitioned_vertices, parent(pv)) @test !has_vertex(pg, pv) #Test both are the same @@ -65,7 +64,7 @@ end NamedGraphs.add_vertices!(pg, v_set, pv) NamedGraphs.add_edges!(pg, edges_involving_v_set) @test is_connected(pg.graph) && is_path_graph(pg.partitioned_graph) - @test haskey(pg.partition_vertices, parent(pv)) + @test haskey(pg.partitioned_vertices, parent(pv)) @test has_vertex(pg, pv) end From a4b9589b698305a60cef02f97574e1cb49830194 Mon Sep 17 00:00:00 2001 From: Joseph Tindall <51231103+JoeyT1994@users.noreply.github.com> Date: Fri, 15 Dec 2023 11:09:50 -0500 Subject: [PATCH 09/32] Update src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl Co-authored-by: Matt Fishman --- src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index e7b8303..dc280b6 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -71,7 +71,7 @@ function rem_edge(pg::AbstractPartitionedGraph, partition_edge::AbstractPartitio end function rem_partition_edges!( - pg::AbstractPartitionedGraph, partition_edges::Vector{AbstractPartitionEdge} + pg::AbstractPartitionedGraph, partition_edges::Vector{<:AbstractPartitionEdge} ) for pe in partition_edges rem_edge!(pg, pe) From 30ed16c234236d47e67d627a8dba13ab5062ce64 Mon Sep 17 00:00:00 2001 From: Joseph Tindall <51231103+JoeyT1994@users.noreply.github.com> Date: Fri, 15 Dec 2023 11:09:59 -0500 Subject: [PATCH 10/32] Update src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl Co-authored-by: Matt Fishman --- src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index dc280b6..fcff1da 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -99,7 +99,7 @@ end function add_vertices!( pg::AbstractPartitionedGraph, vertices::Vector, - partition_vertices::Vector{V} where {V<:AbstractPartitionVertex}, + partition_vertices::Vector{<:AbstractPartitionVertex}, ) @assert length(vertices) == length(partition_vertices) for (v, pv) in zip(vertices, partition_vertices) From ee15533dfe823ae70fca06bcca6d357fafabc978 Mon Sep 17 00:00:00 2001 From: Joseph Tindall <51231103+JoeyT1994@users.noreply.github.com> Date: Fri, 15 Dec 2023 11:10:09 -0500 Subject: [PATCH 11/32] Update src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl Co-authored-by: Matt Fishman --- src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index fcff1da..bc0fcd9 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -135,7 +135,7 @@ function rem_vertex(pg::AbstractPartitionedGraph, partition_vertex::AbstractPart end function rem_vertices!( - pg::AbstractPartitionedGraph, partition_vertices::Vector{AbstractPartitionVertex} + pg::AbstractPartitionedGraph, partition_vertices::Vector{<:AbstractPartitionVertex} ) for pv in partition_vertices rem_vertex!(pg, pv) From 92b310836f6b938349b1f23a4d7c2a00167d5eff Mon Sep 17 00:00:00 2001 From: Joseph Tindall <51231103+JoeyT1994@users.noreply.github.com> Date: Fri, 15 Dec 2023 11:10:18 -0500 Subject: [PATCH 12/32] Update src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl Co-authored-by: Matt Fishman --- src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index bc0fcd9..b02a7ba 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -143,7 +143,7 @@ function rem_vertices!( end function rem_vertices( - pg::AbstractPartitionedGraph, partition_vertices::Vector{AbstractPartitionVertex} + pg::AbstractPartitionedGraph, partition_vertices::Vector{<:AbstractPartitionVertex} ) pg_new = copy(rem_partition_vertex) rem_vertices!(pg_new, partition_vertices) From a782cc3c20830ec41d8488c26db2a83942da846a Mon Sep 17 00:00:00 2001 From: Joseph Tindall <51231103+JoeyT1994@users.noreply.github.com> Date: Fri, 15 Dec 2023 11:10:27 -0500 Subject: [PATCH 13/32] Update src/Graphs/partitionedgraphs/partitionedgraph.jl Co-authored-by: Matt Fishman --- src/Graphs/partitionedgraphs/partitionedgraph.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Graphs/partitionedgraphs/partitionedgraph.jl b/src/Graphs/partitionedgraphs/partitionedgraph.jl index 32454ae..57a75dd 100644 --- a/src/Graphs/partitionedgraphs/partitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/partitionedgraph.jl @@ -62,7 +62,7 @@ function PartitionedGraph(vertices::Vector{V}) where {V} end function PartitionedGraph( - g::AbstractNamedGraph{V}; + g::AbstractNamedGraph; npartitions=nothing, nvertices_per_partition=nothing, backend=current_partitioning_backend(), From fd2fde244991fef7930ce79e1e9e9d5711642da3 Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 15 Dec 2023 13:48:35 -0500 Subject: [PATCH 14/32] Improved naming and interface --- .../abstractpartitionedgraph.jl | 37 +++++++++++-------- .../partitionedgraphs/partitionedgraph.jl | 34 ++++++++--------- test/test_partitionedgraph.jl | 1 - 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index b02a7ba..84978cc 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -2,7 +2,7 @@ abstract type AbstractPartitionedGraph{V,PV} <: AbstractNamedGraph{V} end #Needed for interface partitioned_graph(pg::AbstractPartitionedGraph) = not_implemented() -graph(pg::AbstractPartitionedGraph) = not_implemented() +unpartitioned_graph(pg::AbstractPartitionedGraph) = not_implemented() function vertices(pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex) return not_implemented() end @@ -15,12 +15,15 @@ function edges(pg::AbstractPartitionedGraph, partition_edge::AbstractPartitionEd return not_implemented() end -vertices(pg::AbstractPartitionedGraph) = vertices(graph(pg)) -parent_graph(pg::AbstractPartitionedGraph) = parent_graph(graph(pg)) +vertices(pg::AbstractPartitionedGraph) = vertices(unpartitioned_graph(pg)) +#edges(pg::AbstractPartitionedGraph) = edges(unpartitioned_graph(pg)) +#nv(pg::AbstractPartitionedGraph) = nv(unpartitioned_graph(pg)) +#outneighbors(pg::AbstractPartitionedGraph, vertex) = outneighbors(unpartitioned_graph(pg), vertex) +parent_graph(pg::AbstractPartitionedGraph) = parent_graph(unpartitioned_graph(pg)) function vertex_to_parent_vertex(pg::AbstractPartitionedGraph, vertex) - return vertex_to_parent_vertex(graph(pg), vertex) + return vertex_to_parent_vertex(unpartitioned_graph(pg), vertex) end -edgetype(pg::AbstractPartitionedGraph) = edgetype(graph(pg)) +edgetype(pg::AbstractPartitionedGraph) = edgetype(unpartitioned_graph(pg)) function parent_graph_type(G::Type{<:AbstractPartitionedGraph}) return fieldtype(fieldtype(G, :graph), :parent_graph) end @@ -41,7 +44,7 @@ function is_boundary_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) end function add_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) - add_edge!(graph(pg), edge) + add_edge!(unpartitioned_graph(pg), edge) pg_edge = parent(partition_edge(pg, edge)) if src(pg_edge) != dst(pg_edge) add_edge!(partitioned_graph(pg), pg_edge) @@ -57,7 +60,7 @@ function rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) end end - return rem_edge!(graph(pg), edge) + return rem_edge!(unpartitioned_graph(pg), edge) end function rem_edge!(pg::AbstractPartitionedGraph, partition_edge::AbstractPartitionEdge) @@ -66,11 +69,11 @@ end function rem_edge(pg::AbstractPartitionedGraph, partition_edge::AbstractPartitionEdge) pg_new = copy(pg) - rem_partition_edge!(pg_new, partition_edge) + rem_edge!(pg_new, partition_edge) return pg_new end -function rem_partition_edges!( +function rem_edges!( pg::AbstractPartitionedGraph, partition_edges::Vector{<:AbstractPartitionEdge} ) for pe in partition_edges @@ -78,11 +81,11 @@ function rem_partition_edges!( end end -function rem_partition_edges( +function rem_edges( pg::AbstractPartitionedGraph, partition_edges::Vector{AbstractPartitionEdge} ) pg_new = copy(pg) - rem_partition_edges!(pg_new, partition_edges) + rem_edges!(pg_new, partition_edges) return pg_new end @@ -90,7 +93,7 @@ end function add_vertex!( pg::AbstractPartitionedGraph, vertex, partition_vertex::AbstractPartitionVertex ) - add_vertex!(graph(pg), vertex) + add_vertex!(unpartitioned_graph(pg), vertex) add_vertex!(partitioned_graph(pg), parent(partition_vertex)) return insert_to_vertex_map!(pg, vertex, partition_vertex) @@ -114,7 +117,7 @@ function add_vertices!( end function rem_vertex!(pg::AbstractPartitionedGraph, vertex) - rem_vertex!(graph(pg), vertex) + rem_vertex!(unpartitioned_graph(pg), vertex) pv = which_partition(pg, vertex) if length(vertices(pg, pv)) == 1 rem_vertex!(partitioned_graph(pg), parent(pv)) @@ -155,14 +158,16 @@ function add_vertex!(pg::AbstractPartitionedGraph, vertex) end function (pg1::AbstractPartitionedGraph == pg2::AbstractPartitionedGraph) - if fieldnames(typeof(pg1)) != fieldnames(typeof(pg1)) + if unpartitioned_graph(pg1) != unpartitioned_graph(pg2) || + !issetequal(vertices(pg1), vertices(pg2)) return false end - for field in fieldnames(typeof(pg1)) - if getfield(pg1, field) != getfield(pg2, field) + for v in vertices(pg1) + if which_partition(pg1, v) != which_partition(pg2, v) return false end end + return true end diff --git a/src/Graphs/partitionedgraphs/partitionedgraph.jl b/src/Graphs/partitionedgraphs/partitionedgraph.jl index 57a75dd..6291f8c 100644 --- a/src/Graphs/partitionedgraphs/partitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/partitionedgraph.jl @@ -1,4 +1,4 @@ -struct PartitionedGraph{V,PV,G<:AbstractNamedGraph{V},PG<:AbstractNamedGraph{PV}} <: +struct PartitionedGraph{V,PV,G<:AbstractGraph{V},PG<:AbstractGraph{PV}} <: AbstractPartitionedGraph{V,PV} graph::G partitioned_graph::PG @@ -8,8 +8,8 @@ end ##Constructors. function PartitionedGraph{V,PV,G,PG}( - g::AbstractNamedGraph{V}, partitioned_vertices::Dictionary{PV,Vector{V}} -) where {V,PV,G<:AbstractNamedGraph{V},PG<:AbstractNamedGraph{PV}} + g::AbstractGraph{V}, partitioned_vertices::Dictionary{PV,Vector{V}} +) where {V,PV,G<:AbstractGraph{V},PG<:AbstractGraph{PV}} pvs = keys(partitioned_vertices) pg = NamedGraph(pvs) which_partition = Dictionary{V,PV}() @@ -31,26 +31,24 @@ function PartitionedGraph{V,PV,G,PG}( end function PartitionedGraph( - g::AbstractNamedGraph{V}, partitioned_vertices::Dictionary{PV,Vector{V}} + g::AbstractGraph{V}, partitioned_vertices::Dictionary{PV,Vector{V}} ) where {V,PV} return PartitionedGraph{V,PV,NamedGraph{V},NamedGraph{PV}}(g, partitioned_vertices) end -function PartitionedGraph{V,Int64,G,NamedGraph{Int64}}( - g::AbstractNamedGraph{V}, partitioned_vertices::Vector{Vector{V}} +function PartitionedGraph{V,Int,G,NamedGraph{Int}}( + g::AbstractGraph{V}, partitioned_vertices::Vector{Vector{V}} ) where {V,G<:NamedGraph{V}} - partitioned_vertices_dict = Dictionary{Int64,Vector{V}}( + partitioned_vertices_dict = Dictionary{Int,Vector{V}}( [i for i in 1:length(partitioned_vertices)], partitioned_vertices ) - return PartitionedGraph{V,Int64,NamedGraph{V},NamedGraph{Int64}}( - g, partitioned_vertices_dict - ) + return PartitionedGraph{V,Int,NamedGraph{V},NamedGraph{Int}}(g, partitioned_vertices_dict) end function PartitionedGraph( - g::AbstractNamedGraph{V}, partition_vertices::Vector{Vector{V}} + g::AbstractGraph{V}, partition_vertices::Vector{Vector{V}} ) where {V} - return PartitionedGraph{V,Int64,NamedGraph{V},NamedGraph{Int64}}(g, partition_vertices) + return PartitionedGraph{V,Int,NamedGraph{V},NamedGraph{Int}}(g, partition_vertices) end function PartitionedGraph{V,V,G,G}(vertices::Vector{V}) where {V,G<:NamedGraph{V}} @@ -62,12 +60,12 @@ function PartitionedGraph(vertices::Vector{V}) where {V} end function PartitionedGraph( - g::AbstractNamedGraph; + g::AbstractGraph; npartitions=nothing, nvertices_per_partition=nothing, backend=current_partitioning_backend(), kwargs..., -) where {V} +) partitioned_vertices = partition_vertices( g; npartitions, nvertices_per_partition, backend, kwargs... ) @@ -76,7 +74,7 @@ end #Needed for interface partitioned_graph(pg::PartitionedGraph) = getfield(pg, :partitioned_graph) -graph(pg::PartitionedGraph) = getfield(pg, :graph) +unpartitioned_graph(pg::PartitionedGraph) = getfield(pg, :graph) partitioned_vertices(pg::PartitionedGraph) = getfield(pg, :partitioned_vertices) which_partition(pg::PartitionedGraph) = getfield(pg, :which_partition) function vertices(pg::PartitionedGraph, partition_vert::PartitionVertex) @@ -100,7 +98,7 @@ end function partition_edges(pg::PartitionedGraph, partition_edge::PartitionEdge) psrc_vs, pdst_vs = vertices(pg, PartitionVertex(src(partition_edge))), vertices(pg, PartitionVertex(dst(partition_edge))) - psrc_subgraph, pdst_subgraph, full_subgraph = subgraph(graph(pg), psrc_vs), + psrc_subgraph, pdst_subgraph, full_subgraph = subgraph(unpartitioned_graph(pg), psrc_vs), subgraph(pg, pdst_vs), subgraph(pg, vcat(psrc_vs, pdst_vs)) @@ -113,7 +111,7 @@ end #Copy on dictionaries is dodgy?! function copy(pg::PartitionedGraph) return PartitionedGraph( - copy(graph(pg)), + copy(unpartitioned_graph(pg)), copy(partitioned_graph(pg)), copy_keys_values(partitioned_vertices(pg)), copy_keys_values(which_partition(pg)), @@ -145,7 +143,7 @@ end ### PartitionedGraph Specific Functions function induced_subgraph(pg::PartitionedGraph, vertices::Vector) - sub_pg_graph, _ = induced_subgraph(graph(pg), vertices) + sub_pg_graph, _ = induced_subgraph(unpartitioned_graph(pg), vertices) sub_partitioned_vertices = copy_keys_values(partitioned_vertices(pg)) for pv in NamedGraphs.vertices(partitioned_graph(pg)) vs = intersect(vertices, sub_partitioned_vertices[pv]) diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index f0eef52..2edc41a 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -1,5 +1,4 @@ using Test -using NamedGraphs using NamedGraphs: spanning_forest, subvertices, From 2c7d1743f9aff3a0ed3b734e085163ea65cdb4af Mon Sep 17 00:00:00 2001 From: Joseph Tindall <51231103+JoeyT1994@users.noreply.github.com> Date: Mon, 18 Dec 2023 09:24:16 -0500 Subject: [PATCH 15/32] Update src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl Co-authored-by: Matt Fishman --- src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index 84978cc..88fb9f3 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -82,7 +82,7 @@ function rem_edges!( end function rem_edges( - pg::AbstractPartitionedGraph, partition_edges::Vector{AbstractPartitionEdge} + pg::AbstractPartitionedGraph, partition_edges::Vector{<:AbstractPartitionEdge} ) pg_new = copy(pg) rem_edges!(pg_new, partition_edges) From e69ddb8b6f85fdd69c1c916d7056366c3d03592f Mon Sep 17 00:00:00 2001 From: Joey Date: Mon, 18 Dec 2023 09:27:21 -0500 Subject: [PATCH 16/32] Improved equality of partitioned graph check --- src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index 88fb9f3..34ddc1c 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -159,7 +159,7 @@ end function (pg1::AbstractPartitionedGraph == pg2::AbstractPartitionedGraph) if unpartitioned_graph(pg1) != unpartitioned_graph(pg2) || - !issetequal(vertices(pg1), vertices(pg2)) + partitioned_graph(pg1) != partitioned_graph(pg2) return false end From 392e87f6b45ad6a43fa85e6d505b9a15432582ee Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 21 Dec 2023 14:35:42 -0500 Subject: [PATCH 17/32] Better testing --- .../partitionedgraphs/partitionedgraph.jl | 9 +- src/NamedGraphs.jl | 2 + src/requires/kahypar.jl | 2 +- src/requires/metis.jl | 2 +- test/test_partitionedgraph.jl | 82 ++++++++++++------- 5 files changed, 63 insertions(+), 34 deletions(-) diff --git a/src/Graphs/partitionedgraphs/partitionedgraph.jl b/src/Graphs/partitionedgraphs/partitionedgraph.jl index 6291f8c..b870e9f 100644 --- a/src/Graphs/partitionedgraphs/partitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/partitionedgraph.jl @@ -157,9 +157,16 @@ function induced_subgraph(pg::PartitionedGraph, vertices::Vector) return PartitionedGraph(sub_pg_graph, sub_partitioned_vertices), nothing end -### PartitionedGraph Specific Functions function induced_subgraph( pg::PartitionedGraph, partition_verts::Vector{V} ) where {V<:PartitionVertex} return induced_subgraph(pg, vertices(pg, partition_verts)) end + +function subgraph(pg::PartitionedGraph, partition_vertex::PartitionVertex) + return first(induced_subgraph(unpartitioned_graph(pg), vertices(pg, [partition_vertex]))) +end + +function induced_subgraph(pg::PartitionedGraph, partition_vertex::PartitionVertex) + return subgraph(pg, partition_vertex), nothing +end diff --git a/src/NamedGraphs.jl b/src/NamedGraphs.jl index 3560396..5e9dfa2 100644 --- a/src/NamedGraphs.jl +++ b/src/NamedGraphs.jl @@ -179,6 +179,7 @@ export NamedGraph, neighborhood, neighborhood_dists, neighbors, + partitioned_graph, path_digraph, path_graph, periphery, @@ -196,6 +197,7 @@ export NamedGraph, outdegrees, mincut_partitions, steiner_tree, + unpartitioned_graph, weights function __init__() diff --git a/src/requires/kahypar.jl b/src/requires/kahypar.jl index 1bf9cb5..eecb0ef 100644 --- a/src/requires/kahypar.jl +++ b/src/requires/kahypar.jl @@ -4,7 +4,7 @@ set_partitioning_backend!(Backend"KaHyPar"()) KaHyPar.HyperGraph(g::SimpleGraph) = incidence_matrix(g) """ - partition(::Backend"KaHyPar", g::Graph, npartiations::Integer; objective="edge_cut", alg="kway", kwargs...) + partition_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" diff --git a/src/requires/metis.jl b/src/requires/metis.jl index db0056b..d88562d 100644 --- a/src/requires/metis.jl +++ b/src/requires/metis.jl @@ -1,7 +1,7 @@ set_partitioning_backend!(Backend"Metis"()) """ - partition(::Backend"Metis", g::AbstractGraph, npartitions::Integer; alg="recursive") + partition_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: diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index 2edc41a..5488e36 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -1,4 +1,5 @@ using Test +using NamedGraphs using NamedGraphs: spanning_forest, subvertices, @@ -19,16 +20,26 @@ using Graphs partitions = [[(i, j) for j in 1:ny] for i in 1:nx] pg = PartitionedGraph(g, partitions) - @test vertextype(pg.partitioned_graph) == Int64 - @test vertextype(pg.graph) == vertextype(g) + @test vertextype(partitioned_graph(pg)) == Int64 + @test vertextype(unpartitioned_graph(pg)) == vertextype(g) + @test is_tree(partitioned_graph(pg)) + @test nv(pg) == nx * ny + @test nv(partitioned_graph(pg)) == nx partition_dict = Dictionary([first(partition) for partition in partitions], partitions) pg = PartitionedGraph(g, partition_dict) - @test vertextype(pg.partitioned_graph) == vertextype(g) - @test vertextype(pg.graph) == vertextype(g) + @test vertextype(partitioned_graph(pg)) == vertextype(g) + @test vertextype(unpartitioned_graph(pg)) == vertextype(g) + @test is_tree(partitioned_graph(pg)) + @test nv(pg) == nx * ny + @test nv(partitioned_graph(pg)) == nx pg = PartitionedGraph([i for i in 1:nx]) - @test pg.graph == pg.partitioned_graph + @test unpartitioned_graph(pg) == partitioned_graph(pg) + @test nv(pg) == nx + @test nv(partitioned_graph(pg)) == nx + @test ne(pg) == 0 + @test ne(partitioned_graph(pg)) == 0 end @testset "Test Partitioned Graph Vertex/Edge Addition and Removal" begin @@ -40,48 +51,53 @@ end pv = PartitionVertex(5) v_set = vertices(pg, pv) - edges_involving_v_set = filter( - e -> !isempty(intersect(v_set, [src(e), dst(e)])), edges(pg) - ) + edges_involving_v_set = boundary_edges(g, v_set) #Strip the middle column from pg via the partitioned graph vertex, and make a new pg - pg_stripped = NamedGraphs.rem_vertex(pg, pv) - @test !is_connected(pg_stripped.graph) && !is_connected(pg_stripped.partitioned_graph) - @test !haskey(pg_stripped.partitioned_vertices, parent(pv)) - @test !has_vertex(pg_stripped, pv) - - #Strip the middle column from pg directly and via the graph vertices, do it in place - NamedGraphs.rem_vertices!(pg, v_set) - @test !is_connected(pg.graph) && !is_connected(pg.partitioned_graph) - @test !haskey(pg.partitioned_vertices, parent(pv)) + NamedGraphs.rem_vertex!(pg, pv) + @test !is_connected(unpartitioned_graph(pg)) && !is_connected(partitioned_graph(pg)) + @test parent(pv) ∉ vertices(partitioned_graph(pg)) @test !has_vertex(pg, pv) - - #Test both are the same - @test pg == pg_stripped + @test nv(pg) == (nx - 1) * ny + @test nv(partitioned_graph(pg)) == nx - 1 + @test !is_tree(partitioned_graph(pg)) #Add the column back to the in place graph NamedGraphs.add_vertices!(pg, v_set, pv) NamedGraphs.add_edges!(pg, edges_involving_v_set) - @test is_connected(pg.graph) && is_path_graph(pg.partitioned_graph) - @test haskey(pg.partitioned_vertices, parent(pv)) + @test is_connected(pg.graph) && is_path_graph(partitioned_graph(pg)) + @test parent(pv) ∈ vertices(partitioned_graph(pg)) @test has_vertex(pg, pv) + @test is_tree(partitioned_graph(pg)) + @test nv(pg) == nx * ny + @test nv(partitioned_graph(pg)) == nx end @testset "Test Partitioned Graph Subgraph Functionality" begin - n, z = 20, 4 - nvertices_per_partition = 4 + n, z = 12, 4 g = NamedGraph(random_regular_graph(n, z)) - npartitions = _npartitions(g, nothing, nvertices_per_partition) - partitions = [ - [nvertices_per_partition * (i - 1) + j for j in 1:nvertices_per_partition] for - i in 1:npartitions - ] + partitions = dictionary([ + 1 => [1, 2, 3], 2 => [4, 5, 6], 3 => [7, 8, 9], 4 => [10, 11, 12] + ]) pg = PartitionedGraph(g, partitions) - pg_1 = subgraph(pg, partitions[1]) - pg_2 = subgraph(pg, [PartitionVertex(1)]) + subgraph_partitioned_vertices = [1, 2] + subgraph_vertices = reduce( + vcat, [partitions[spv] for spv in subgraph_partitioned_vertices] + ) + pg_1 = subgraph(pg, PartitionVertex.(subgraph_partitioned_vertices)) + pg_2 = subgraph(pg, subgraph_vertices) @test pg_1 == pg_2 + @test nv(pg_1) == length(subgraph_vertices) + @test nv(partitioned_graph(pg_1)) == length(subgraph_partitioned_vertices) + + subgraph_partitioned_vertex = 3 + subgraph_vertices = partitions[subgraph_partitioned_vertex] + g_1 = subgraph(pg, PartitionVertex(subgraph_partitioned_vertex)) + pg_1 = subgraph(pg, subgraph_vertices) + @test unpartitioned_graph(pg_1) == subgraph(g, subgraph_vertices) + @test g_1 == subgraph(g, subgraph_vertices) end @testset "Test NamedGraphs Functions on Partitioned Graph" begin @@ -105,6 +121,10 @@ end for g in gs pg = PartitionedGraph(g, [vertices(g)]) @test f(pg) == f(pg.graph) + @test nv(pg) == nv(g) + @test nv(partitioned_graph(pg)) == 1 + @test ne(pg) == ne(g) + @test ne(partitioned_graph(pg)) == 0 end end end From 4548c98fb7b42dd63ebceb2412cccb1b97812eb1 Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 22 Dec 2023 11:45:09 +0000 Subject: [PATCH 18/32] Simplified constructor for PartitionedGraph, less type constraints --- examples/partitioning.jl | 8 +-- .../partitionedgraphs/partitionedgraph.jl | 53 ++++--------------- 2 files changed, 13 insertions(+), 48 deletions(-) diff --git a/examples/partitioning.jl b/examples/partitioning.jl index 507c1bf..14fd353 100644 --- a/examples/partitioning.jl +++ b/examples/partitioning.jl @@ -8,9 +8,9 @@ npartitions = 4 pg_kahypar = PartitionedGraph(g; npartitions, backend="KaHyPar") pg_metis = PartitionedGraph(g; npartitions, backend="Metis") -@show length(vertices(pg_kahypar.partitioned_graph)) == - length(vertices(pg_metis.partitioned_graph)) == +@show length(NamedGraphs.vertices(partitioned_graph(pg_kahypar))) == + length(NamedGraphs.vertices(partitioned_graph(pg_metis))) == npartitions -@show typeof(pg_kahypar) <: PartitionedGraph -@show typeof(pg_metis) <: PartitionedGraph +@show pg_kahypar isa PartitionedGraph +@show pg_metis isa PartitionedGraph diff --git a/src/Graphs/partitionedgraphs/partitionedgraph.jl b/src/Graphs/partitionedgraphs/partitionedgraph.jl index b870e9f..943fad7 100644 --- a/src/Graphs/partitionedgraphs/partitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/partitionedgraph.jl @@ -2,17 +2,15 @@ struct PartitionedGraph{V,PV,G<:AbstractGraph{V},PG<:AbstractGraph{PV}} <: AbstractPartitionedGraph{V,PV} graph::G partitioned_graph::PG - partitioned_vertices::Dictionary{PV,Vector{V}} - which_partition::Dictionary{V,PV} + partitioned_vertices::Dictionary + which_partition::Dictionary end ##Constructors. -function PartitionedGraph{V,PV,G,PG}( - g::AbstractGraph{V}, partitioned_vertices::Dictionary{PV,Vector{V}} -) where {V,PV,G<:AbstractGraph{V},PG<:AbstractGraph{PV}} +function PartitionedGraph(g::AbstractGraph, partitioned_vertices) pvs = keys(partitioned_vertices) pg = NamedGraph(pvs) - which_partition = Dictionary{V,PV}() + which_partition = Dictionary() for v in vertices(g) v_pvs = Set(findall(pv -> v ∈ partitioned_vertices[pv], pvs)) @assert length(v_pvs) == 1 @@ -27,48 +25,15 @@ function PartitionedGraph{V,PV,G,PG}( end end - return PartitionedGraph(g, pg, partitioned_vertices, which_partition) + return PartitionedGraph(g, pg, Dictionary(partitioned_vertices), which_partition) end -function PartitionedGraph( - g::AbstractGraph{V}, partitioned_vertices::Dictionary{PV,Vector{V}} -) where {V,PV} - return PartitionedGraph{V,PV,NamedGraph{V},NamedGraph{PV}}(g, partitioned_vertices) +function PartitionedGraph(partitioned_vertices) + return PartitionedGraph(NamedGraph(keys(partitioned_vertices)), partitioned_vertices) end -function PartitionedGraph{V,Int,G,NamedGraph{Int}}( - g::AbstractGraph{V}, partitioned_vertices::Vector{Vector{V}} -) where {V,G<:NamedGraph{V}} - partitioned_vertices_dict = Dictionary{Int,Vector{V}}( - [i for i in 1:length(partitioned_vertices)], partitioned_vertices - ) - return PartitionedGraph{V,Int,NamedGraph{V},NamedGraph{Int}}(g, partitioned_vertices_dict) -end - -function PartitionedGraph( - g::AbstractGraph{V}, partition_vertices::Vector{Vector{V}} -) where {V} - return PartitionedGraph{V,Int,NamedGraph{V},NamedGraph{Int}}(g, partition_vertices) -end - -function PartitionedGraph{V,V,G,G}(vertices::Vector{V}) where {V,G<:NamedGraph{V}} - return PartitionedGraph(NamedGraph{V}(vertices), [[v] for v in vertices]) -end - -function PartitionedGraph(vertices::Vector{V}) where {V} - return PartitionedGraph{V,V,NamedGraph{V},NamedGraph{V}}(vertices) -end - -function PartitionedGraph( - g::AbstractGraph; - npartitions=nothing, - nvertices_per_partition=nothing, - backend=current_partitioning_backend(), - kwargs..., -) - partitioned_vertices = partition_vertices( - g; npartitions, nvertices_per_partition, backend, kwargs... - ) +function PartitionedGraph(g::AbstractGraph; kwargs...) + partitioned_vertices = partition_vertices(g; kwargs...) return PartitionedGraph(g, partitioned_vertices) end From 830667dc8133fd884aa8ca6009b991d00a65244c Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 23 Dec 2023 09:05:08 +0000 Subject: [PATCH 19/32] Better type based functions --- examples/partitioning.jl | 4 +- src/Graphs/abstractgraph.jl | 2 +- .../abstractpartitionedgraph.jl | 53 +++++++++++----- src/Graphs/partitionedgraphs/partitionedge.jl | 2 +- .../partitionedgraphs/partitionedgraph.jl | 62 ++++++++++--------- src/NamedGraphs.jl | 1 + src/abstractnamedgraph.jl | 8 +-- src/namedgraph.jl | 4 +- test/test_partitionedgraph.jl | 13 +--- 9 files changed, 82 insertions(+), 67 deletions(-) diff --git a/examples/partitioning.jl b/examples/partitioning.jl index 14fd353..687913b 100644 --- a/examples/partitioning.jl +++ b/examples/partitioning.jl @@ -8,9 +8,7 @@ npartitions = 4 pg_kahypar = PartitionedGraph(g; npartitions, backend="KaHyPar") pg_metis = PartitionedGraph(g; npartitions, backend="Metis") -@show length(NamedGraphs.vertices(partitioned_graph(pg_kahypar))) == - length(NamedGraphs.vertices(partitioned_graph(pg_metis))) == - npartitions +@show nv(partitioned_graph(pg_kahypar)) == nv(partitioned_graph(pg_metis)) == npartitions @show pg_kahypar isa PartitionedGraph @show pg_metis isa PartitionedGraph diff --git a/src/Graphs/abstractgraph.jl b/src/Graphs/abstractgraph.jl index 06b4409..abd8e23 100644 --- a/src/Graphs/abstractgraph.jl +++ b/src/Graphs/abstractgraph.jl @@ -43,7 +43,7 @@ end # to avoid method overwrite warnings, see: # https://github.com/mauro3/SimpleTraits.jl#method-overwritten-warnings @traitfn function undirected_graph(graph::::IsDirected) - undigraph = undirected_graph(typeof(graph))(vertices(graph)) + undigraph = undirected_graph_type(typeof(graph))(vertices(graph)) for e in edges(graph) # TODO: Check for repeated edges? add_edge!(undigraph, e) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index 34ddc1c..a6a0738 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -3,34 +3,34 @@ abstract type AbstractPartitionedGraph{V,PV} <: AbstractNamedGraph{V} end #Needed for interface partitioned_graph(pg::AbstractPartitionedGraph) = not_implemented() unpartitioned_graph(pg::AbstractPartitionedGraph) = not_implemented() -function vertices(pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex) - return not_implemented() -end which_partition(pg::AbstractPartitionedGraph, vertex) = not_implemented() copy(pg::AbstractPartitionedGraph) = not_implemented() delete_from_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() insert_to_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() -partition_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) = not_implemented() +partition_edge(pg::AbstractPartitionedGraph, edge) = not_implemented() function edges(pg::AbstractPartitionedGraph, partition_edge::AbstractPartitionEdge) return not_implemented() end +vertices(pg::AbstractPartitionedGraph, pv::AbstractPartitionVertex) = not_implemented() +function vertices( + pg::AbstractPartitionedGraph, partition_verts::Vector{V} +) where {V<:AbstractPartitionVertex} + return not_implemented() +end +parent_graph_type(G::Type{<:AbstractPartitionedGraph}) = not_implemented() +directed_graph_type(G::Type{<:AbstractPartitionedGraph}) = not_implemented() +undirected_graph_type(G::Type{<:AbstractPartitionedGraph}) = not_implemented() +#Functions for the abstract type vertices(pg::AbstractPartitionedGraph) = vertices(unpartitioned_graph(pg)) -#edges(pg::AbstractPartitionedGraph) = edges(unpartitioned_graph(pg)) -#nv(pg::AbstractPartitionedGraph) = nv(unpartitioned_graph(pg)) -#outneighbors(pg::AbstractPartitionedGraph, vertex) = outneighbors(unpartitioned_graph(pg), vertex) parent_graph(pg::AbstractPartitionedGraph) = parent_graph(unpartitioned_graph(pg)) function vertex_to_parent_vertex(pg::AbstractPartitionedGraph, vertex) return vertex_to_parent_vertex(unpartitioned_graph(pg), vertex) end edgetype(pg::AbstractPartitionedGraph) = edgetype(unpartitioned_graph(pg)) -function parent_graph_type(G::Type{<:AbstractPartitionedGraph}) - return fieldtype(fieldtype(G, :graph), :parent_graph) -end -directed_graph(G::Type{<:AbstractPartitionedGraph}) = directed_graph(fieldtype(G, :graph)) -function undirected_graph(G::Type{<:AbstractPartitionedGraph}) - return unddirected_graph(fieldtype(G, :graph)) -end +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, partition_vertex::AbstractPartitionVertex) return has_vertex(partitioned_graph(pg), parent(partition_vertex)) end @@ -49,6 +49,8 @@ function add_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) if src(pg_edge) != dst(pg_edge) add_edge!(partitioned_graph(pg), pg_edge) end + + return pg end function rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) @@ -79,6 +81,8 @@ function rem_edges!( for pe in partition_edges rem_edge!(pg, pe) end + + return pg end function rem_edges( @@ -108,6 +112,8 @@ function add_vertices!( for (v, pv) in zip(vertices, partition_vertices) add_vertex!(pg, v, pv) end + + return pg end function add_vertices!( @@ -117,12 +123,13 @@ function add_vertices!( end function rem_vertex!(pg::AbstractPartitionedGraph, vertex) - rem_vertex!(unpartitioned_graph(pg), vertex) pv = which_partition(pg, vertex) - if length(vertices(pg, pv)) == 1 + delete_from_vertex_map!(pg, pv, vertex) + rem_vertex!(unpartitioned_graph(pg), vertex) + if iszero(nv(pg, pv)) rem_vertex!(partitioned_graph(pg), parent(pv)) end - return delete_from_vertex_map!(pg, vertex) + return pg end function rem_vertex!( @@ -143,6 +150,8 @@ function rem_vertices!( for pv in partition_vertices rem_vertex!(pg, pv) end + + return pg end function rem_vertices( @@ -171,3 +180,13 @@ function (pg1::AbstractPartitionedGraph == pg2::AbstractPartitionedGraph) return true end + +function subgraph(pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex) + return first(induced_subgraph(unpartitioned_graph(pg), vertices(pg, [partition_vertex]))) +end + +function induced_subgraph( + pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex +) + return subgraph(pg, partition_vertex), nothing +end diff --git a/src/Graphs/partitionedgraphs/partitionedge.jl b/src/Graphs/partitionedgraphs/partitionedge.jl index 597641a..4607b9d 100644 --- a/src/Graphs/partitionedgraphs/partitionedge.jl +++ b/src/Graphs/partitionedgraphs/partitionedge.jl @@ -5,4 +5,4 @@ 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(p.first => p.second)) +PartitionEdge(p::Pair) = PartitionEdge(NamedEdge(first(p) => last(p))) diff --git a/src/Graphs/partitionedgraphs/partitionedgraph.jl b/src/Graphs/partitionedgraphs/partitionedgraph.jl index 943fad7..33d06b7 100644 --- a/src/Graphs/partitionedgraphs/partitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/partitionedgraph.jl @@ -42,12 +42,21 @@ partitioned_graph(pg::PartitionedGraph) = getfield(pg, :partitioned_graph) unpartitioned_graph(pg::PartitionedGraph) = getfield(pg, :graph) partitioned_vertices(pg::PartitionedGraph) = getfield(pg, :partitioned_vertices) which_partition(pg::PartitionedGraph) = getfield(pg, :which_partition) +parent_graph_type(PG::Type{<:PartitionedGraph}) = fieldtype(PG, :graph) +function directed_graph_type(PG::Type{<:PartitionedGraph}) + return directed_graph_type(fieldtype(PG, :graph)) +end +function undirected_graph_type(PG::Type{<:PartitionedGraph}) + return undirected_graph_type(fieldtype(PG, :graph)) +end function vertices(pg::PartitionedGraph, partition_vert::PartitionVertex) - return partitioned_vertices(pg)[parent(partition_vert)] + if haskey(partitioned_vertices(pg), parent(partition_vert)) + return partitioned_vertices(pg)[parent(partition_vert)] + else + return [] + end end -function vertices( - pg::PartitionedGraph, partition_verts::Vector{V} -) where {V<:PartitionVertex} +function vertices(pg::PartitionedGraph, partition_verts::Vector{<:PartitionVertex}) return unique(reduce(vcat, [vertices(pg, pv) for pv in partition_verts])) end function which_partition(pg::PartitionedGraph, vertex) @@ -61,19 +70,15 @@ function partition_edge(pg::PartitionedGraph, edge::AbstractEdge) end function partition_edges(pg::PartitionedGraph, partition_edge::PartitionEdge) - psrc_vs, pdst_vs = vertices(pg, PartitionVertex(src(partition_edge))), - vertices(pg, PartitionVertex(dst(partition_edge))) - psrc_subgraph, pdst_subgraph, full_subgraph = subgraph(unpartitioned_graph(pg), psrc_vs), - subgraph(pg, pdst_vs), - subgraph(pg, vcat(psrc_vs, pdst_vs)) - - return setdiff( - NamedGraphs.edges(full_subgraph), - vcat(NamedGraphs.edges(psrc_subgraph), NamedGraphs.edges(pdst_subgraph)), - ) + psrc_vs = vertices(pg, PartitionVertex(src(partition_edge))) + pdst_vs = vertices(pg, PartitionVertex(dst(partition_edge))) + psrc_subgraph = subgraph(unpartitioned_graph(pg), psrc_vs) + pdst_subgraph = subgraph(pg, pdst_vs) + full_subgraph = subgraph(pg, vcat(psrc_vs, pdst_vs)) + + return setdiff(edges(full_subgraph), vcat(edges(psrc_subgraph), edges(pdst_subgraph))) end -#Copy on dictionaries is dodgy?! function copy(pg::PartitionedGraph) return PartitionedGraph( copy(unpartitioned_graph(pg)), @@ -93,17 +98,26 @@ function insert_to_vertex_map!( pg.partitioned_vertices[pv] = unique(vcat(vertices(pg, partition_vertex), [vertex])) end - return insert!(NamedGraphs.which_partition(pg), vertex, pv) + insert!(which_partition(pg), vertex, pv) + return pg end function delete_from_vertex_map!(pg::PartitionedGraph, vertex) pv = which_partition(pg, vertex) - vs = vertices(pg, pv) - delete!(partitioned_vertices(pg), parent(pv)) + return delete_from_vertex_map!(pg, pv, vertex) +end + +function delete_from_vertex_map!( + pg::PartitionedGraph, partitioned_vertex::PartitionVertex, vertex +) + vs = vertices(pg, partitioned_vertex) + delete!(partitioned_vertices(pg), parent(partitioned_vertex)) if length(vs) != 1 - insert!(partitioned_vertices(pg), parent(pv), setdiff(vs, [vertex])) + insert!(partitioned_vertices(pg), parent(partitioned_vertex), setdiff(vs, [vertex])) end - return delete!(pg.which_partition, vertex) + + delete!(which_partition(pg), vertex) + return partitioned_vertex end ### PartitionedGraph Specific Functions @@ -127,11 +141,3 @@ function induced_subgraph( ) where {V<:PartitionVertex} return induced_subgraph(pg, vertices(pg, partition_verts)) end - -function subgraph(pg::PartitionedGraph, partition_vertex::PartitionVertex) - return first(induced_subgraph(unpartitioned_graph(pg), vertices(pg, [partition_vertex]))) -end - -function induced_subgraph(pg::PartitionedGraph, partition_vertex::PartitionVertex) - return subgraph(pg, partition_vertex), nothing -end diff --git a/src/NamedGraphs.jl b/src/NamedGraphs.jl index 5e9dfa2..e3f1e0a 100644 --- a/src/NamedGraphs.jl +++ b/src/NamedGraphs.jl @@ -179,6 +179,7 @@ export NamedGraph, neighborhood, neighborhood_dists, neighbors, + nv, partitioned_graph, path_digraph, path_graph, diff --git a/src/abstractnamedgraph.jl b/src/abstractnamedgraph.jl index d2b15eb..876f74d 100644 --- a/src/abstractnamedgraph.jl +++ b/src/abstractnamedgraph.jl @@ -36,8 +36,8 @@ end # TODO: rename `edge_type`? edgetype(graph::AbstractNamedGraph) = not_implemented() -directed_graph(G::Type{<:AbstractNamedGraph}) = not_implemented() -undirected_graph(G::Type{<:AbstractNamedGraph}) = not_implemented() +directed_graph_type(G::Type{<:AbstractNamedGraph}) = not_implemented() +undirected_graph_type(G::Type{<:AbstractNamedGraph}) = not_implemented() # In terms of `parent_graph_type` # is_directed(::Type{<:AbstractNamedGraph}) = not_implemented() @@ -61,7 +61,7 @@ zero(G::Type{<:AbstractNamedGraph}) = G() # TODO: Implement using `copyto!`? function directed_graph(graph::AbstractNamedGraph) - digraph = directed_graph(typeof(graph))(vertices(graph)) + digraph = directed_graph_type(typeof(graph))(vertices(graph)) for e in edges(graph) add_edge!(digraph, e) add_edge!(digraph, reverse(e)) @@ -530,7 +530,7 @@ function tree(graph::AbstractNamedGraph, parents) n = length(parents) # TODO: Use `directed_graph` here to make more generic? ## t = GenericNamedGraph(DiGraph(n), vertices(graph)) - t = directed_graph(typeof(graph))(vertices(graph)) + t = directed_graph_type(typeof(graph))(vertices(graph)) for destination in eachindex(parents) source = parents[destination] if source != destination diff --git a/src/namedgraph.jl b/src/namedgraph.jl index 14fed66..aaaf1fd 100644 --- a/src/namedgraph.jl +++ b/src/namedgraph.jl @@ -159,10 +159,10 @@ function set_vertices(graph::GenericNamedGraph, vertices) return GenericNamedGraph(parent_graph(graph), vertices) end -function directed_graph(G::Type{<:GenericNamedGraph}) +function directed_graph_type(G::Type{<:GenericNamedGraph}) return GenericNamedGraph{vertextype(G),directed_graph(parent_graph_type(G))} end -function undirected_graph(G::Type{<:GenericNamedGraph}) +function undirected_graph_type(G::Type{<:GenericNamedGraph}) return GenericNamedGraph{vertextype(G),undirected_graph(parent_graph_type(G))} end diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index 5488e36..a099373 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -1,16 +1,7 @@ using Test using NamedGraphs using NamedGraphs: - spanning_forest, - subvertices, - spanning_tree, - forest_cover, - rem_edge!, - PartitionEdge, - rem_edges!, - PartitionVertex, - parent, - _npartitions + spanning_forest, spanning_tree, forest_cover, PartitionEdge, PartitionVertex, parent using Dictionaries using Graphs @@ -120,7 +111,7 @@ end for f in functions for g in gs pg = PartitionedGraph(g, [vertices(g)]) - @test f(pg) == f(pg.graph) + @test f(pg) == f(unpartitioned_graph(pg)) @test nv(pg) == nv(g) @test nv(partitioned_graph(pg)) == 1 @test ne(pg) == ne(g) From 3c996dd6512c01448de4666fb01e170286d5112e Mon Sep 17 00:00:00 2001 From: Joey Date: Sat, 23 Dec 2023 09:12:26 +0000 Subject: [PATCH 20/32] Use function instead of field accessor --- src/Graphs/partitionedgraphs/partitionedgraph.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Graphs/partitionedgraphs/partitionedgraph.jl b/src/Graphs/partitionedgraphs/partitionedgraph.jl index 33d06b7..9d37cfa 100644 --- a/src/Graphs/partitionedgraphs/partitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/partitionedgraph.jl @@ -95,7 +95,7 @@ function insert_to_vertex_map!( if pv ∉ keys(partitioned_vertices(pg)) insert!(partitioned_vertices(pg), pv, [vertex]) else - pg.partitioned_vertices[pv] = unique(vcat(vertices(pg, partition_vertex), [vertex])) + partitioned_vertices(pg)[pv] = unique(vcat(vertices(pg, partition_vertex), [vertex])) end insert!(which_partition(pg), vertex, pv) From 4d627cd5df68dd1b502f11358ae93f623e9cd889 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 3 Jan 2024 10:49:24 -0500 Subject: [PATCH 21/32] Removed implementation of directed(partitioned)graph). Better has_vertex(pg) functionality --- .../partitionedgraphs/abstractpartitionedgraph.jl | 13 +++++++------ src/Graphs/partitionedgraphs/partitionedgraph.jl | 12 +----------- test/test_partitionedgraph.jl | 10 +--------- 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index a6a0738..aff6bc2 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -4,6 +4,7 @@ abstract type AbstractPartitionedGraph{V,PV} <: AbstractNamedGraph{V} end partitioned_graph(pg::AbstractPartitionedGraph) = not_implemented() unpartitioned_graph(pg::AbstractPartitionedGraph) = not_implemented() which_partition(pg::AbstractPartitionedGraph, vertex) = not_implemented() +partitioned_vertices(pg::AbstractPartitionedGraph) = not_implemented() copy(pg::AbstractPartitionedGraph) = not_implemented() delete_from_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() insert_to_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() @@ -17,9 +18,9 @@ function vertices( ) where {V<:AbstractPartitionVertex} return not_implemented() end -parent_graph_type(G::Type{<:AbstractPartitionedGraph}) = not_implemented() -directed_graph_type(G::Type{<:AbstractPartitionedGraph}) = not_implemented() -undirected_graph_type(G::Type{<:AbstractPartitionedGraph}) = not_implemented() +parent_graph_type(PG::Type{<:AbstractPartitionedGraph}) = not_implemented() +directed_graph_type(PG::Type{<:AbstractPartitionedGraph}) = not_implemented() +undirected_graph_type(PG::Type{<:AbstractPartitionedGraph}) = not_implemented() #Functions for the abstract type vertices(pg::AbstractPartitionedGraph) = vertices(unpartitioned_graph(pg)) @@ -30,9 +31,9 @@ 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, partition_vertex::AbstractPartitionVertex) - return has_vertex(partitioned_graph(pg), parent(partition_vertex)) + return haskey(partitioned_vertices(pg), parent(partition_vertex)) && + has_vertex(partitioned_graph(pg), parent(partition_vertex)) end function has_edge(pg::AbstractPartitionedGraph, edge::AbstractPartitionEdge) return has_edge(partitioned_graph(pg), parent(partition_edge)) @@ -126,7 +127,7 @@ function rem_vertex!(pg::AbstractPartitionedGraph, vertex) pv = which_partition(pg, vertex) delete_from_vertex_map!(pg, pv, vertex) rem_vertex!(unpartitioned_graph(pg), vertex) - if iszero(nv(pg, pv)) + if !haskey(partitioned_vertices(pg), parent(pv)) rem_vertex!(partitioned_graph(pg), parent(pv)) end return pg diff --git a/src/Graphs/partitionedgraphs/partitionedgraph.jl b/src/Graphs/partitionedgraphs/partitionedgraph.jl index 9d37cfa..33c71a2 100644 --- a/src/Graphs/partitionedgraphs/partitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/partitionedgraph.jl @@ -43,18 +43,8 @@ unpartitioned_graph(pg::PartitionedGraph) = getfield(pg, :graph) partitioned_vertices(pg::PartitionedGraph) = getfield(pg, :partitioned_vertices) which_partition(pg::PartitionedGraph) = getfield(pg, :which_partition) parent_graph_type(PG::Type{<:PartitionedGraph}) = fieldtype(PG, :graph) -function directed_graph_type(PG::Type{<:PartitionedGraph}) - return directed_graph_type(fieldtype(PG, :graph)) -end -function undirected_graph_type(PG::Type{<:PartitionedGraph}) - return undirected_graph_type(fieldtype(PG, :graph)) -end function vertices(pg::PartitionedGraph, partition_vert::PartitionVertex) - if haskey(partitioned_vertices(pg), parent(partition_vert)) - return partitioned_vertices(pg)[parent(partition_vert)] - else - return [] - end + return partitioned_vertices(pg)[parent(partition_vert)] end function vertices(pg::PartitionedGraph, partition_verts::Vector{<:PartitionVertex}) return unique(reduce(vcat, [vertices(pg, pv) for pv in partition_verts])) diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index a099373..a6d2e3e 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -92,15 +92,7 @@ end end @testset "Test NamedGraphs Functions on Partitioned Graph" begin - functions = [ - is_tree, - NamedGraphs.default_root_vertex, - spanning_tree, - spanning_forest, - center, - diameter, - radius, - ] + functions = [is_tree, NamedGraphs.default_root_vertex, center, diameter, radius] gs = [ named_comb_tree((4, 4)), named_grid((2, 2, 2)), From 3a44997efa5f2695c162cc3075fad7696ea3e33c Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 3 Jan 2024 10:55:44 -0500 Subject: [PATCH 22/32] Simplified has_vertex check for partitioned_graph --- src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index aff6bc2..16fcdcc 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -31,10 +31,8 @@ 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, partition_vertex::AbstractPartitionVertex) - return haskey(partitioned_vertices(pg), parent(partition_vertex)) && - has_vertex(partitioned_graph(pg), parent(partition_vertex)) -end +has_vertex(pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex) = has_vertex(partitioned_graph(pg), parent(partition_vertex)) + function has_edge(pg::AbstractPartitionedGraph, edge::AbstractPartitionEdge) return has_edge(partitioned_graph(pg), parent(partition_edge)) end From 53ee51dbaca7ddf548b970cb03b48960d8e5f965 Mon Sep 17 00:00:00 2001 From: Joey Date: Mon, 8 Jan 2024 09:59:58 -0500 Subject: [PATCH 23/32] Formatting --- src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index 16fcdcc..ce74293 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -31,7 +31,9 @@ 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)) -has_vertex(pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex) = has_vertex(partitioned_graph(pg), parent(partition_vertex)) +function has_vertex(pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex) + return has_vertex(partitioned_graph(pg), parent(partition_vertex)) +end function has_edge(pg::AbstractPartitionedGraph, edge::AbstractPartitionEdge) return has_edge(partitioned_graph(pg), parent(partition_edge)) From bf82a6dcdaf9a4e249bf96acf8bd6c38b32ac6d2 Mon Sep 17 00:00:00 2001 From: Joey Date: Mon, 8 Jan 2024 14:55:03 -0500 Subject: [PATCH 24/32] Defined rem/add_vertex as core function for abstractgraph interface --- src/Graphs/abstractgraph.jl | 27 ++++++++++++ src/abstractnamedgraph.jl | 85 ++++++++++++------------------------- 2 files changed, 53 insertions(+), 59 deletions(-) diff --git a/src/Graphs/abstractgraph.jl b/src/Graphs/abstractgraph.jl index abd8e23..b1ef70b 100644 --- a/src/Graphs/abstractgraph.jl +++ b/src/Graphs/abstractgraph.jl @@ -1,5 +1,7 @@ directed_graph(::Type{<:AbstractGraph}) = error("Not implemented") undirected_graph(::Type{<:AbstractGraph}) = error("Not implemented") +add_vertex!(graph::AbstractGraph, vertex) = error("Not implemented") +rem_vertex!(graph::AbstractGraph, vertex) = error("Not implemented") # TODO: Implement generic version for `IsDirected` # directed_graph(G::Type{IsDirected}) = G @@ -384,6 +386,31 @@ function mincut_partitions(graph::AbstractGraph, distmx=weights(graph)) return parts[1], parts[2] end +function insert_vertex!(graph::AbstractGraph, vertex) + z = add_vertex!(graph, vertex) + if !z + error("Duplicate vertices are not allowed") + else + return graph + end +end + +function delete_vertex!(graph::AbstractGraph, vertex) + z = rem_vertex!(graph, vertex) + if !z + error("Vertex not in graph") + else + return graph + end +end + +function add_vertices!(graph::AbstractGraph, vs::Vector) + for vertex in vs + add_vertex!(graph, vertex) + end + return graph +end + """Remove a list of edges from a graph g""" function rem_edges!(g::AbstractGraph, edges) for e in edges diff --git a/src/abstractnamedgraph.jl b/src/abstractnamedgraph.jl index 876f74d..28727f0 100644 --- a/src/abstractnamedgraph.jl +++ b/src/abstractnamedgraph.jl @@ -381,75 +381,42 @@ function union(graph1::AbstractNamedGraph, graph2::AbstractNamedGraph) return union_graph end -function set_vertex!(graph::AbstractNamedGraph, vertex) - add_vertex!(parent_graph(graph)) - # Update the vertex list - push!(vertices(graph), vertex) - # Update the reverse map - # TODO: Make this more generic - insert!(graph.vertex_to_parent_vertex, vertex, last(parent_vertices(graph))) - return graph -end - -function insert_vertex!(graph::AbstractNamedGraph, vertex) - if vertex ∈ vertices(graph) - error("Duplicate vertices not allowed!") - else - set_vertex!(graph, vertex) - end -end - -function add_vertex!(graph::AbstractNamedGraph, vertex) - if vertex ∈ vertices(graph) - return false - else - set_vertex!(graph, vertex) - return true - end -end - -function unset_vertex!(graph::AbstractNamedGraph, vertex) - parent_vertex = vertex_to_parent_vertex(graph, vertex) - rem_vertex!(parent_graph(graph), parent_vertex) - - # Insert the last vertex into the position of the vertex - # that is being deleted, then remove the last vertex. - last_vertex = last(vertices(graph)) - vertices(graph)[parent_vertex] = last_vertex - last_vertex = pop!(vertices(graph)) - - # Insert the last vertex into the position of the vertex - # that is being deleted, then remove the last vertex. - # TODO: Make this more generic - graph.vertex_to_parent_vertex[last_vertex] = parent_vertex - # TODO: Make this more generic - delete!(graph.vertex_to_parent_vertex, vertex) - - return graph -end - function rem_vertex!(graph::AbstractNamedGraph, vertex) if vertex ∉ vertices(graph) return false else - unset_vertex!(graph, vertex) + parent_vertex = vertex_to_parent_vertex(graph, vertex) + rem_vertex!(parent_graph(graph), parent_vertex) + + # Insert the last vertex into the position of the vertex + # that is being deleted, then remove the last vertex. + last_vertex = last(vertices(graph)) + vertices(graph)[parent_vertex] = last_vertex + last_vertex = pop!(vertices(graph)) + + # Insert the last vertex into the position of the vertex + # that is being deleted, then remove the last vertex. + # TODO: Make this more generic + graph.vertex_to_parent_vertex[last_vertex] = parent_vertex + # TODO: Make this more generic + delete!(graph.vertex_to_parent_vertex, vertex) + return true end end -function delete_vertex!(graph::AbstractNamedGraph, vertex) - if vertex ∉ vertices(graph) - error("vertex not in graph!") +function add_vertex!(graph::AbstractNamedGraph, vertex) + if vertex ∈ vertices(graph) + return false else - unset_vertex!(graph, vertex) - end -end - -function add_vertices!(graph::AbstractNamedGraph, vs::Vector) - for vertex in vs - add_vertex!(graph, vertex) + add_vertex!(parent_graph(graph)) + # Update the vertex list + push!(vertices(graph), vertex) + # Update the reverse map + # TODO: Make this more generic + insert!(graph.vertex_to_parent_vertex, vertex, last(parent_vertices(graph))) + return true end - return graph end is_directed(G::Type{<:AbstractNamedGraph}) = is_directed(parent_graph_type(G)) From bef1b839cff07b6adc012a8ad8b292466da2118e Mon Sep 17 00:00:00 2001 From: Joey Date: Mon, 8 Jan 2024 15:56:28 -0500 Subject: [PATCH 25/32] Better variable names and function structure --- src/Graphs/abstractgraph.jl | 16 +++++------ src/abstractnamedgraph.jl | 53 +++++++++++++++++++------------------ test/test_examples.jl | 2 +- 3 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/Graphs/abstractgraph.jl b/src/Graphs/abstractgraph.jl index b1ef70b..3d3b0d4 100644 --- a/src/Graphs/abstractgraph.jl +++ b/src/Graphs/abstractgraph.jl @@ -387,21 +387,21 @@ function mincut_partitions(graph::AbstractGraph, distmx=weights(graph)) end function insert_vertex!(graph::AbstractGraph, vertex) - z = add_vertex!(graph, vertex) - if !z + in_graph = !add_vertex!(graph, vertex) + if in_graph error("Duplicate vertices are not allowed") - else - return graph end + + return graph end function delete_vertex!(graph::AbstractGraph, vertex) - z = rem_vertex!(graph, vertex) - if !z + in_graph = rem_vertex!(graph, vertex) + if !in_graph error("Vertex not in graph") - else - return graph end + + return graph end function add_vertices!(graph::AbstractGraph, vs::Vector) diff --git a/src/abstractnamedgraph.jl b/src/abstractnamedgraph.jl index 28727f0..319fbda 100644 --- a/src/abstractnamedgraph.jl +++ b/src/abstractnamedgraph.jl @@ -384,39 +384,40 @@ end function rem_vertex!(graph::AbstractNamedGraph, vertex) if vertex ∉ vertices(graph) return false - else - parent_vertex = vertex_to_parent_vertex(graph, vertex) - rem_vertex!(parent_graph(graph), parent_vertex) - - # Insert the last vertex into the position of the vertex - # that is being deleted, then remove the last vertex. - last_vertex = last(vertices(graph)) - vertices(graph)[parent_vertex] = last_vertex - last_vertex = pop!(vertices(graph)) - - # Insert the last vertex into the position of the vertex - # that is being deleted, then remove the last vertex. - # TODO: Make this more generic - graph.vertex_to_parent_vertex[last_vertex] = parent_vertex - # TODO: Make this more generic - delete!(graph.vertex_to_parent_vertex, vertex) - - return true end + + parent_vertex = vertex_to_parent_vertex(graph, vertex) + rem_vertex!(parent_graph(graph), parent_vertex) + + # Insert the last vertex into the position of the vertex + # that is being deleted, then remove the last vertex. + last_vertex = last(vertices(graph)) + vertices(graph)[parent_vertex] = last_vertex + last_vertex = pop!(vertices(graph)) + + # Insert the last vertex into the position of the vertex + # that is being deleted, then remove the last vertex. + # TODO: Make this more generic + graph.vertex_to_parent_vertex[last_vertex] = parent_vertex + # TODO: Make this more generic + delete!(graph.vertex_to_parent_vertex, vertex) + + return true end function add_vertex!(graph::AbstractNamedGraph, vertex) if vertex ∈ vertices(graph) return false - else - add_vertex!(parent_graph(graph)) - # Update the vertex list - push!(vertices(graph), vertex) - # Update the reverse map - # TODO: Make this more generic - insert!(graph.vertex_to_parent_vertex, vertex, last(parent_vertices(graph))) - return true end + + add_vertex!(parent_graph(graph)) + # Update the vertex list + push!(vertices(graph), vertex) + # Update the reverse map + # TODO: Make this more generic + insert!(graph.vertex_to_parent_vertex, vertex, last(parent_vertices(graph))) + + return true end is_directed(G::Type{<:AbstractNamedGraph}) = is_directed(parent_graph_type(G)) diff --git a/test/test_examples.jl b/test/test_examples.jl index b489ee3..01b7da5 100644 --- a/test/test_examples.jl +++ b/test/test_examples.jl @@ -4,7 +4,7 @@ using Suppressor using Test examples_path = joinpath(pkgdir(NamedGraphs), "examples") -examples_to_exclude = ["partitioning.jl"] +examples_to_exclude = [] @testset "Run examples: $filename" for filename in setdiff(readdir(examples_path), examples_to_exclude) if endswith(filename, ".jl") From 50103864d7b4cb3cae965dc2dd2066c7793d5a12 Mon Sep 17 00:00:00 2001 From: Joey Date: Mon, 8 Jan 2024 16:07:58 -0500 Subject: [PATCH 26/32] Added test dependencies --- test/Project.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Project.toml b/test/Project.toml index 4b36d4e..2a66a4c 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -7,3 +7,5 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Weave = "44d3d7a6-8a23-5bf8-98c5-b353f8df5ec9" +KaHyPar = "2a6221f6-aa48-11e9-3542-2d9e0ef01880" +Metis = "2679e427-3c69-5b7f-982b-ece356f1e94b" From 7f32e17ee29d5cf405d3724c880c0b6ef7b8611b Mon Sep 17 00:00:00 2001 From: Joey Date: Mon, 8 Jan 2024 16:47:11 -0500 Subject: [PATCH 27/32] Only add KaHyPar dependency to example if not Windows --- examples/partitioning.jl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/partitioning.jl b/examples/partitioning.jl index 687913b..47a0e8b 100644 --- a/examples/partitioning.jl +++ b/examples/partitioning.jl @@ -1,14 +1,16 @@ using NamedGraphs -using KaHyPar using Metis g = named_grid((4, 4)) npartitions = 4 -pg_kahypar = PartitionedGraph(g; npartitions, backend="KaHyPar") pg_metis = PartitionedGraph(g; npartitions, backend="Metis") -@show nv(partitioned_graph(pg_kahypar)) == nv(partitioned_graph(pg_metis)) == npartitions - -@show pg_kahypar isa PartitionedGraph @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 From c0d220894adeef4a49578eb832557e3912889e55 Mon Sep 17 00:00:00 2001 From: Joseph Tindall <51231103+JoeyT1994@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:44:27 -0500 Subject: [PATCH 28/32] Update src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl Co-authored-by: Matt Fishman --- src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index ce74293..daba944 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -101,7 +101,8 @@ function add_vertex!( add_vertex!(unpartitioned_graph(pg), vertex) add_vertex!(partitioned_graph(pg), parent(partition_vertex)) - return insert_to_vertex_map!(pg, vertex, partition_vertex) + insert_to_vertex_map!(pg, vertex, partition_vertex) + return pg end function add_vertices!( From 134d39c56c76e10c28887e93bc030d05b05dabae Mon Sep 17 00:00:00 2001 From: Joseph Tindall <51231103+JoeyT1994@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:44:35 -0500 Subject: [PATCH 29/32] Update src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl Co-authored-by: Matt Fishman --- src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index daba944..9a748e7 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -121,7 +121,8 @@ end function add_vertices!( pg::AbstractPartitionedGraph, vertices::Vector, partition_vertex::AbstractPartitionVertex ) - return add_vertices!(pg, vertices, fill(partition_vertex, length(vertices))) + add_vertices!(pg, vertices, fill(partition_vertex, length(vertices))) + return pg end function rem_vertex!(pg::AbstractPartitionedGraph, vertex) From 30dfeb075462cfbab971b77379861576ebdb14a4 Mon Sep 17 00:00:00 2001 From: Joseph Tindall <51231103+JoeyT1994@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:44:44 -0500 Subject: [PATCH 30/32] Update src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl Co-authored-by: Matt Fishman --- src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index 9a748e7..7c55fed 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -138,7 +138,8 @@ end function rem_vertex!( pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex ) - return rem_vertices!(pg, vertices(pg, partition_vertex)) + rem_vertices!(pg, vertices(pg, partition_vertex)) + return pg end function rem_vertex(pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex) From acad1f1705cb83771a2117c366675b6997b230d6 Mon Sep 17 00:00:00 2001 From: Joey Date: Tue, 9 Jan 2024 16:52:20 -0500 Subject: [PATCH 31/32] Removed redundant function. Formatting --- .../abstractpartitionedgraph.jl | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl index 7c55fed..5ef0692 100644 --- a/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl +++ b/src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl @@ -62,7 +62,6 @@ function rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) rem_edge!(partitioned_graph(pg), pg_edge) end end - return rem_edge!(unpartitioned_graph(pg), edge) end @@ -82,7 +81,6 @@ function rem_edges!( for pe in partition_edges rem_edge!(pg, pe) end - return pg end @@ -100,7 +98,6 @@ function add_vertex!( ) add_vertex!(unpartitioned_graph(pg), vertex) add_vertex!(partitioned_graph(pg), parent(partition_vertex)) - insert_to_vertex_map!(pg, vertex, partition_vertex) return pg end @@ -148,24 +145,6 @@ function rem_vertex(pg::AbstractPartitionedGraph, partition_vertex::AbstractPart return pg_new end -function rem_vertices!( - pg::AbstractPartitionedGraph, partition_vertices::Vector{<:AbstractPartitionVertex} -) - for pv in partition_vertices - rem_vertex!(pg, pv) - end - - return pg -end - -function rem_vertices( - pg::AbstractPartitionedGraph, partition_vertices::Vector{<:AbstractPartitionVertex} -) - pg_new = copy(rem_partition_vertex) - rem_vertices!(pg_new, partition_vertices) - return pg_new -end - function add_vertex!(pg::AbstractPartitionedGraph, vertex) return error("Need to specify a partition where the new vertex will go.") end @@ -175,13 +154,11 @@ function (pg1::AbstractPartitionedGraph == pg2::AbstractPartitionedGraph) partitioned_graph(pg1) != partitioned_graph(pg2) return false end - for v in vertices(pg1) if which_partition(pg1, v) != which_partition(pg2, v) return false end end - return true end From c8931372b666e08945316cecd84e78b020415829 Mon Sep 17 00:00:00 2001 From: Joey Date: Wed, 10 Jan 2024 09:46:02 -0500 Subject: [PATCH 32/32] Cleanup. Avoid type piracy --- src/Graphs/abstractgraph.jl | 6 ------ test/test_partitionedgraph.jl | 20 ++++++++++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Graphs/abstractgraph.jl b/src/Graphs/abstractgraph.jl index 3d3b0d4..f24e003 100644 --- a/src/Graphs/abstractgraph.jl +++ b/src/Graphs/abstractgraph.jl @@ -1,7 +1,5 @@ directed_graph(::Type{<:AbstractGraph}) = error("Not implemented") undirected_graph(::Type{<:AbstractGraph}) = error("Not implemented") -add_vertex!(graph::AbstractGraph, vertex) = error("Not implemented") -rem_vertex!(graph::AbstractGraph, vertex) = error("Not implemented") # TODO: Implement generic version for `IsDirected` # directed_graph(G::Type{IsDirected}) = G @@ -391,7 +389,6 @@ function insert_vertex!(graph::AbstractGraph, vertex) if in_graph error("Duplicate vertices are not allowed") end - return graph end @@ -400,7 +397,6 @@ function delete_vertex!(graph::AbstractGraph, vertex) if !in_graph error("Vertex not in graph") end - return graph end @@ -477,7 +473,6 @@ function random_bfs_tree(g::AbstractGraph, s; maxiter=1000 * (nv(g) + ne(g))) end end end - isempty_Q = isempty(Q) if isempty_Q break @@ -486,6 +481,5 @@ function random_bfs_tree(g::AbstractGraph, s; maxiter=1000 * (nv(g) + ne(g))) if !isempty_Q error("Search failed to cover the graph in time. Consider increasing maxiter.") end - return g_out end diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index a6d2e3e..35faf72 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -1,7 +1,15 @@ using Test using NamedGraphs using NamedGraphs: - spanning_forest, spanning_tree, forest_cover, PartitionEdge, PartitionVertex, parent + spanning_forest, + spanning_tree, + forest_cover, + PartitionEdge, + PartitionVertex, + parent, + default_root_vertex, + triangular_lattice_graph, + add_edges! using Dictionaries using Graphs @@ -45,7 +53,7 @@ end edges_involving_v_set = boundary_edges(g, v_set) #Strip the middle column from pg via the partitioned graph vertex, and make a new pg - NamedGraphs.rem_vertex!(pg, pv) + rem_vertex!(pg, pv) @test !is_connected(unpartitioned_graph(pg)) && !is_connected(partitioned_graph(pg)) @test parent(pv) ∉ vertices(partitioned_graph(pg)) @test !has_vertex(pg, pv) @@ -54,8 +62,8 @@ end @test !is_tree(partitioned_graph(pg)) #Add the column back to the in place graph - NamedGraphs.add_vertices!(pg, v_set, pv) - NamedGraphs.add_edges!(pg, edges_involving_v_set) + add_vertices!(pg, v_set, pv) + add_edges!(pg, edges_involving_v_set) @test is_connected(pg.graph) && is_path_graph(partitioned_graph(pg)) @test parent(pv) ∈ vertices(partitioned_graph(pg)) @test has_vertex(pg, pv) @@ -92,12 +100,12 @@ end end @testset "Test NamedGraphs Functions on Partitioned Graph" begin - functions = [is_tree, NamedGraphs.default_root_vertex, center, diameter, radius] + 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)), - NamedGraphs.triangular_lattice_graph(7, 7), + triangular_lattice_graph(7, 7), ] for f in functions