From e45385ad5639128f13258f03cd0942f928b72e98 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Mon, 3 Nov 2025 16:03:30 -0500 Subject: [PATCH 01/36] Rename `PartitionsGraphView` -> `QuotientGraph` and `partitions_graph` -> `quotient_graph`. --- .../src/abstractpartitionedgraph.jl | 18 +++++++++--------- .../PartitionedGraphs/src/partitionedgraph.jl | 2 +- ...partitionsgraphview.jl => quotientgraph.jl} | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) rename src/lib/PartitionedGraphs/src/{partitionsgraphview.jl => quotientgraph.jl} (71%) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index 45ccce7..9f095e8 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -16,7 +16,7 @@ using ..NamedGraphs.GraphsExtensions: abstract type AbstractPartitionedGraph{V, PV} <: AbstractNamedGraph{V} end #Needed for interface -partitions_graph(pg::AbstractPartitionedGraph) = not_implemented() +quotient_graph(pg::AbstractPartitionedGraph) = not_implemented() unpartitioned_graph(pg::AbstractPartitionedGraph) = not_implemented() function unpartitioned_graph_type(pg::Type{<:AbstractPartitionedGraph}) return not_implemented() @@ -75,11 +75,11 @@ end function Graphs.has_vertex( pg::AbstractPartitionedGraph, partitionvertex::AbstractPartitionVertex ) - return has_vertex(partitions_graph(pg), parent(partitionvertex)) + return has_vertex(quotient_graph(pg), parent(partitionvertex)) end function Graphs.has_edge(pg::AbstractPartitionedGraph, partitionedge::AbstractPartitionEdge) - return has_edge(partitions_graph(pg), parent(partitionedge)) + return has_edge(quotient_graph(pg), parent(partitionedge)) end function is_boundary_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) @@ -91,17 +91,17 @@ function Graphs.add_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) add_edge!(unpartitioned_graph(pg), edge) pg_edge = parent(partitionedge(pg, edge)) if src(pg_edge) != dst(pg_edge) - add_edge!(partitions_graph(pg), pg_edge) + add_edge!(quotient_graph(pg), pg_edge) end return pg end function Graphs.rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) pg_edge = partitionedge(pg, edge) - if has_edge(partitions_graph(pg), pg_edge) + if has_edge(quotient_graph(pg), pg_edge) g_edges = edges(pg, pg_edge) if length(g_edges) == 1 - rem_edge!(partitions_graph(pg), pg_edge) + rem_edge!(quotient_graph(pg), pg_edge) end end return rem_edge!(unpartitioned_graph(pg), edge) @@ -118,7 +118,7 @@ function Graphs.add_vertex!( pg::AbstractPartitionedGraph, vertex, partitionvertex::AbstractPartitionVertex ) add_vertex!(unpartitioned_graph(pg), vertex) - add_vertex!(partitions_graph(pg), parent(partitionvertex)) + add_vertex!(quotient_graph(pg), parent(partitionvertex)) insert_to_vertex_map!(pg, vertex, partitionvertex) return pg end @@ -148,7 +148,7 @@ function Graphs.rem_vertex!(pg::AbstractPartitionedGraph, vertex) delete_from_vertex_map!(pg, pv, vertex) rem_vertex!(unpartitioned_graph(pg), vertex) if !haskey(partitioned_vertices(pg), parent(pv)) - rem_vertex!(partitions_graph(pg), parent(pv)) + rem_vertex!(quotient_graph(pg), parent(pv)) end return pg end @@ -174,7 +174,7 @@ end function Base.:(==)(pg1::AbstractPartitionedGraph, pg2::AbstractPartitionedGraph) if unpartitioned_graph(pg1) != unpartitioned_graph(pg2) || - partitions_graph(pg1) != partitions_graph(pg2) + quotient_graph(pg1) != quotient_graph(pg2) return false end for v in vertices(pg1) diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index 9192d61..cd3277a 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -46,7 +46,7 @@ function PartitionedGraph(g::AbstractGraph; kwargs...) end #Needed for interface -partitions_graph(pg::PartitionedGraph) = PartitionsGraphView(pg) +quotient_graph(pg::PartitionedGraph) = QuotientGraph(pg) unpartitioned_graph(pg::PartitionedGraph) = getfield(pg, :graph) function unpartitioned_graph_type(graph_type::Type{<:PartitionedGraph}) return fieldtype(graph_type, :graph) diff --git a/src/lib/PartitionedGraphs/src/partitionsgraphview.jl b/src/lib/PartitionedGraphs/src/quotientgraph.jl similarity index 71% rename from src/lib/PartitionedGraphs/src/partitionsgraphview.jl rename to src/lib/PartitionedGraphs/src/quotientgraph.jl index 461d8ab..f319633 100644 --- a/src/lib/PartitionedGraphs/src/partitionsgraphview.jl +++ b/src/lib/PartitionedGraphs/src/quotientgraph.jl @@ -1,16 +1,16 @@ -struct PartitionsGraphView{V, G <: AbstractGraph{V}} <: AbstractNamedGraph{V} +struct QuotientGraph{V, G <: AbstractGraph{V}} <: AbstractNamedGraph{V} graph::G end -Base.copy(g::PartitionsGraphView) = PartitionsGraphView(copy(g.graph)) +Base.copy(g::QuotientGraph) = QuotientGraph(copy(g.graph)) using Graphs: AbstractGraph -partitions_graph(g::AbstractGraph) = PartitionsGraphView(PartitionedGraph(g, [vertices(g)])) +quotient_graph(g::AbstractGraph) = QuotientGraph(PartitionedGraph(g, [vertices(g)])) # Graphs.jl and NamedGraphs.jl interface overloads for `PartitionsGraphView` wrapping # a `PartitionedGraph`. function NamedGraphs.position_graph_type( - type::Type{<:PartitionsGraphView{V, G}} + type::Type{<:QuotientGraph{V, G}} ) where {V, G <: PartitionedGraph{V}} return fieldtype(fieldtype(fieldtype(type, :graph), :partitions_graph), :position_graph) end @@ -28,7 +28,7 @@ for f in [ ] @eval begin function $f( - g::PartitionsGraphView{V, G}, args...; kwargs... + g::QuotientGraph{V, G}, args...; kwargs... ) where {V, G <: PartitionedGraph{V}} return $f(g.graph.partitions_graph, args...; kwargs...) end From 609c855166a3ecc4dc2c44b2bffef5938470bf1b Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Mon, 3 Nov 2025 16:41:33 -0500 Subject: [PATCH 02/36] Rename `PartitionVertex` -> `SuperVertex` and `PartitionEdge` -> `SuperEdge`; fix tests --- .../src/PartitionedGraphs.jl | 10 +- .../src/abstractpartitionedge.jl | 11 -- .../src/abstractpartitionedgraph.jl | 86 +++++++------- .../src/abstractpartitionvertex.jl | 4 - .../src/abstractsuperedge.jl | 11 ++ .../src/abstractsupervertex.jl | 4 + .../PartitionedGraphs/src/partitionedge.jl | 12 -- .../PartitionedGraphs/src/partitionedgraph.jl | 74 ++++++------ .../PartitionedGraphs/src/partitionvertex.jl | 5 - src/lib/PartitionedGraphs/src/superedge.jl | 12 ++ src/lib/PartitionedGraphs/src/supervertex.jl | 5 + test/test_partitionedgraph.jl | 112 +++++++++--------- 12 files changed, 173 insertions(+), 173 deletions(-) delete mode 100644 src/lib/PartitionedGraphs/src/abstractpartitionedge.jl delete mode 100644 src/lib/PartitionedGraphs/src/abstractpartitionvertex.jl create mode 100644 src/lib/PartitionedGraphs/src/abstractsuperedge.jl create mode 100644 src/lib/PartitionedGraphs/src/abstractsupervertex.jl delete mode 100644 src/lib/PartitionedGraphs/src/partitionedge.jl delete mode 100644 src/lib/PartitionedGraphs/src/partitionvertex.jl create mode 100644 src/lib/PartitionedGraphs/src/superedge.jl create mode 100644 src/lib/PartitionedGraphs/src/supervertex.jl diff --git a/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl b/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl index a14d5ef..7390ee9 100644 --- a/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl +++ b/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl @@ -1,9 +1,9 @@ module PartitionedGraphs -include("abstractpartitionvertex.jl") -include("abstractpartitionedge.jl") +include("abstractsupervertex.jl") +include("abstractsuperedge.jl") include("abstractpartitionedgraph.jl") -include("partitionvertex.jl") -include("partitionedge.jl") +include("supervertex.jl") +include("superedge.jl") include("partitionedgraph.jl") -include("partitionsgraphview.jl") +include("quotientgraph.jl") end diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedge.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedge.jl deleted file mode 100644 index 57188e6..0000000 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedge.jl +++ /dev/null @@ -1,11 +0,0 @@ -using Graphs: Graphs -using ..NamedGraphs: AbstractNamedEdge - -abstract type AbstractPartitionEdge{V} <: AbstractNamedEdge{V} end - -Base.parent(pe::AbstractPartitionEdge) = not_implemented() -Graphs.src(pe::AbstractPartitionEdge) = not_implemented() -Graphs.dst(pe::AbstractPartitionEdge) = not_implemented() -Base.reverse(pe::AbstractPartitionEdge) = not_implemented() - -#Don't have the vertices wrapped. But wrap them with source and edge. diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index 9f095e8..e59c16b 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -21,28 +21,28 @@ unpartitioned_graph(pg::AbstractPartitionedGraph) = not_implemented() function unpartitioned_graph_type(pg::Type{<:AbstractPartitionedGraph}) return not_implemented() end -partitionvertex(pg::AbstractPartitionedGraph, vertex) = not_implemented() -partitionvertices(pg::AbstractPartitionedGraph, verts) = not_implemented() -partitionvertices(pg::AbstractPartitionedGraph) = not_implemented() +supervertex(pg::AbstractPartitionedGraph, vertex) = not_implemented() +supervertex(pg::AbstractPartitionedGraph, verts) = not_implemented() +supervertex(pg::AbstractPartitionedGraph) = not_implemented() Base.copy(pg::AbstractPartitionedGraph) = not_implemented() delete_from_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() insert_to_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() -partitionedge(pg::AbstractPartitionedGraph, edge) = not_implemented() -partitionedges(pg::AbstractPartitionedGraph, edges) = not_implemented() -partitionedges(pg::AbstractPartitionedGraph) = not_implemented() +superedge(pg::AbstractPartitionedGraph, edge) = not_implemented() +superedges(pg::AbstractPartitionedGraph, edges) = not_implemented() +superedges(pg::AbstractPartitionedGraph) = not_implemented() function unpartitioned_graph_type(pg::AbstractPartitionedGraph) return typeof(unpartitioned_graph(pg)) end -function Graphs.edges(pg::AbstractPartitionedGraph, partitionedge::AbstractPartitionEdge) +function Graphs.edges(pg::AbstractPartitionedGraph, superedge::AbstractSuperEdge) return not_implemented() end -function Graphs.vertices(pg::AbstractPartitionedGraph, pv::AbstractPartitionVertex) +function Graphs.vertices(pg::AbstractPartitionedGraph, supervertex::AbstractSuperVertex) return not_implemented() end function Graphs.vertices( - pg::AbstractPartitionedGraph, partitionverts::Vector{V} - ) where {V <: AbstractPartitionVertex} + pg::AbstractPartitionedGraph, supervertices::Vector{V} + ) where {V <: AbstractSuperVertex} return not_implemented() end function GraphsExtensions.directed_graph_type(PG::Type{<:AbstractPartitionedGraph}) @@ -69,27 +69,27 @@ function NamedGraphs.ordered_vertices(pg::AbstractPartitionedGraph) return NamedGraphs.ordered_vertices(unpartitioned_graph(pg)) end Graphs.edgetype(pg::AbstractPartitionedGraph) = edgetype(unpartitioned_graph(pg)) -function Graphs.nv(pg::AbstractPartitionedGraph, pv::AbstractPartitionVertex) - return length(vertices(pg, pv)) +function Graphs.nv(pg::AbstractPartitionedGraph, sv::AbstractSuperVertex) + return length(vertices(pg, sv)) end function Graphs.has_vertex( - pg::AbstractPartitionedGraph, partitionvertex::AbstractPartitionVertex + pg::AbstractPartitionedGraph, supervertex::AbstractSuperVertex ) - return has_vertex(quotient_graph(pg), parent(partitionvertex)) + return has_vertex(quotient_graph(pg), parent(supervertex)) end -function Graphs.has_edge(pg::AbstractPartitionedGraph, partitionedge::AbstractPartitionEdge) - return has_edge(quotient_graph(pg), parent(partitionedge)) +function Graphs.has_edge(pg::AbstractPartitionedGraph, superedge::AbstractSuperEdge) + return has_edge(quotient_graph(pg), parent(superedge)) end function is_boundary_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) - p_edge = partitionedge(pg, edge) + p_edge = superedge(pg, edge) return src(p_edge) == dst(p_edge) end function Graphs.add_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) add_edge!(unpartitioned_graph(pg), edge) - pg_edge = parent(partitionedge(pg, edge)) + pg_edge = parent(superedge(pg, edge)) if src(pg_edge) != dst(pg_edge) add_edge!(quotient_graph(pg), pg_edge) end @@ -97,7 +97,7 @@ function Graphs.add_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) end function Graphs.rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) - pg_edge = partitionedge(pg, edge) + pg_edge = superedge(pg, edge) if has_edge(quotient_graph(pg), pg_edge) g_edges = edges(pg, pg_edge) if length(g_edges) == 1 @@ -108,63 +108,63 @@ function Graphs.rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) end function Graphs.rem_edge!( - pg::AbstractPartitionedGraph, partitionedge::AbstractPartitionEdge + pg::AbstractPartitionedGraph, superedge::AbstractSuperEdge ) - return rem_edges!(pg, edges(pg, parent(partitionedge))) + return rem_edges!(pg, edges(pg, parent(superedge))) end #Vertex addition and removal. I think it's important not to allow addition of a vertex without specification of PV function Graphs.add_vertex!( - pg::AbstractPartitionedGraph, vertex, partitionvertex::AbstractPartitionVertex + pg::AbstractPartitionedGraph, vertex, supervertex::AbstractSuperVertex ) add_vertex!(unpartitioned_graph(pg), vertex) - add_vertex!(quotient_graph(pg), parent(partitionvertex)) - insert_to_vertex_map!(pg, vertex, partitionvertex) + add_vertex!(quotient_graph(pg), parent(supervertex)) + insert_to_vertex_map!(pg, vertex, supervertex) return pg end function GraphsExtensions.add_vertices!( pg::AbstractPartitionedGraph, vertices::Vector, - partitionvertices::Vector{<:AbstractPartitionVertex}, + supervertices::Vector{<:AbstractSuperVertex}, ) - @assert length(vertices) == length(partitionvertices) - for (v, pv) in zip(vertices, partitionvertices) - add_vertex!(pg, v, pv) + @assert length(vertices) == length(supervertices) + for (v, sv) in zip(vertices, supervertices) + add_vertex!(pg, v, sv) end return pg end function GraphsExtensions.add_vertices!( - pg::AbstractPartitionedGraph, vertices::Vector, partitionvertex::AbstractPartitionVertex + pg::AbstractPartitionedGraph, vertices::Vector, supervertex::AbstractSuperVertex ) - add_vertices!(pg, vertices, fill(partitionvertex, length(vertices))) + add_vertices!(pg, vertices, fill(supervertex, length(vertices))) return pg end function Graphs.rem_vertex!(pg::AbstractPartitionedGraph, vertex) - pv = partitionvertex(pg, vertex) - delete_from_vertex_map!(pg, pv, vertex) + sv = supervertex(pg, vertex) + delete_from_vertex_map!(pg, sv, vertex) rem_vertex!(unpartitioned_graph(pg), vertex) - if !haskey(partitioned_vertices(pg), parent(pv)) - rem_vertex!(quotient_graph(pg), parent(pv)) + if !haskey(partitioned_vertices(pg), parent(sv)) + rem_vertex!(quotient_graph(pg), parent(sv)) end return pg end function Graphs.rem_vertex!( - pg::AbstractPartitionedGraph, partitionvertex::AbstractPartitionVertex + pg::AbstractPartitionedGraph, supervertex::AbstractSuperVertex ) - rem_vertices!(pg, vertices(pg, partitionvertex)) + rem_vertices!(pg, vertices(pg, supervertex)) return pg end function GraphsExtensions.rem_vertex( - pg::AbstractPartitionedGraph, partitionvertex::AbstractPartitionVertex + pg::AbstractPartitionedGraph, supervertex::AbstractSuperVertex ) pg_new = copy(pg) - rem_vertex!(pg_new, partitionvertex) + rem_vertex!(pg_new, supervertex) return pg_new end @@ -178,7 +178,7 @@ function Base.:(==)(pg1::AbstractPartitionedGraph, pg2::AbstractPartitionedGraph return false end for v in vertices(pg1) - if partitionvertex(pg1, v) != partitionvertex(pg2, v) + if supervertex(pg1, v) != supervertex(pg2, v) return false end end @@ -186,13 +186,13 @@ function Base.:(==)(pg1::AbstractPartitionedGraph, pg2::AbstractPartitionedGraph end function GraphsExtensions.subgraph( - pg::AbstractPartitionedGraph, partitionvertex::AbstractPartitionVertex + pg::AbstractPartitionedGraph, supervertex::AbstractSuperVertex ) - return first(induced_subgraph(unpartitioned_graph(pg), vertices(pg, [partitionvertex]))) + return first(induced_subgraph(unpartitioned_graph(pg), vertices(pg, [supervertex]))) end function Graphs.induced_subgraph( - pg::AbstractPartitionedGraph, partitionvertex::AbstractPartitionVertex + pg::AbstractPartitionedGraph, supervertex::AbstractSuperVertex ) - return subgraph(pg, partitionvertex), nothing + return subgraph(pg, supervertex), nothing end diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionvertex.jl b/src/lib/PartitionedGraphs/src/abstractpartitionvertex.jl deleted file mode 100644 index 31bc58b..0000000 --- a/src/lib/PartitionedGraphs/src/abstractpartitionvertex.jl +++ /dev/null @@ -1,4 +0,0 @@ -abstract type AbstractPartitionVertex{V} <: Any where {V} end - -#Parent, wrap, unwrap, vertex? -Base.parent(pv::AbstractPartitionVertex) = not_implemented() diff --git a/src/lib/PartitionedGraphs/src/abstractsuperedge.jl b/src/lib/PartitionedGraphs/src/abstractsuperedge.jl new file mode 100644 index 0000000..f4fe71d --- /dev/null +++ b/src/lib/PartitionedGraphs/src/abstractsuperedge.jl @@ -0,0 +1,11 @@ +using Graphs: Graphs +using ..NamedGraphs: AbstractNamedEdge + +abstract type AbstractSuperEdge{V} <: AbstractNamedEdge{V} end + +Base.parent(se::AbstractSuperEdge) = not_implemented() +Graphs.src(se::AbstractSuperEdge) = not_implemented() +Graphs.dst(se::AbstractSuperEdge) = not_implemented() +Base.reverse(se::AbstractSuperEdge) = not_implemented() + +#Don't have the vertices wrapped. But wrap them with source and edge. diff --git a/src/lib/PartitionedGraphs/src/abstractsupervertex.jl b/src/lib/PartitionedGraphs/src/abstractsupervertex.jl new file mode 100644 index 0000000..62d3f75 --- /dev/null +++ b/src/lib/PartitionedGraphs/src/abstractsupervertex.jl @@ -0,0 +1,4 @@ +abstract type AbstractSuperVertex{V} <: Any where {V} end + +#Parent, wrap, unwrap, vertex? +Base.parent(sv::AbstractSuperVertex) = not_implemented() diff --git a/src/lib/PartitionedGraphs/src/partitionedge.jl b/src/lib/PartitionedGraphs/src/partitionedge.jl deleted file mode 100644 index d473522..0000000 --- a/src/lib/PartitionedGraphs/src/partitionedge.jl +++ /dev/null @@ -1,12 +0,0 @@ -using Graphs: Graphs, AbstractEdge, dst, src - -struct PartitionEdge{V, E <: AbstractEdge{V}} <: AbstractPartitionEdge{V} - edge::E -end - -Base.parent(pe::PartitionEdge) = getfield(pe, :edge) -Graphs.src(pe::PartitionEdge) = PartitionVertex(src(parent(pe))) -Graphs.dst(pe::PartitionEdge) = PartitionVertex(dst(parent(pe))) -PartitionEdge(p::Pair) = PartitionEdge(NamedEdge(first(p) => last(p))) -PartitionEdge(vsrc, vdst) = PartitionEdge(vsrc => vdst) -Base.reverse(pe::PartitionEdge) = PartitionEdge(reverse(parent(pe))) diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index cd3277a..cbd0fce 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -55,43 +55,43 @@ function GraphsExtensions.partitioned_vertices(pg::PartitionedGraph) return getfield(pg, :partitioned_vertices) end which_partition(pg::PartitionedGraph) = getfield(pg, :which_partition) -function Graphs.vertices(pg::PartitionedGraph, partitionvert::PartitionVertex) - return partitioned_vertices(pg)[parent(partitionvert)] +function Graphs.vertices(pg::PartitionedGraph, supervertex::SuperVertex) + return partitioned_vertices(pg)[parent(supervertex)] end -function Graphs.vertices(pg::PartitionedGraph, partitionverts::Vector{<:PartitionVertex}) - return unique(reduce(vcat, Iterators.map(pv -> vertices(pg, pv), partitionverts))) +function Graphs.vertices(pg::PartitionedGraph, supervertices::Vector{<:SuperVertex}) + return unique(reduce(vcat, Iterators.map(sv -> vertices(pg, sv), supervertices))) end -function partitionvertex(pg::PartitionedGraph, vertex) - return PartitionVertex(which_partition(pg)[vertex]) +function supervertex(pg::PartitionedGraph, vertex) + return SuperVertex(which_partition(pg)[vertex]) end -function partitionvertices(pg::PartitionedGraph, verts) - return unique(partitionvertex(pg, v) for v in verts) +function supervertices(pg::PartitionedGraph, verts) + return unique(supervertex(pg, v) for v in verts) end -function partitionvertices(pg::PartitionedGraph) - return PartitionVertex.(vertices(pg.partitions_graph)) +function supervertices(pg::PartitionedGraph) + return SuperVertex.(vertices(pg.partitions_graph)) end -function partitionedge(pg::PartitionedGraph, edge::AbstractEdge) - return PartitionEdge( - parent(partitionvertex(pg, src(edge))) => parent(partitionvertex(pg, dst(edge))) +function superedge(pg::PartitionedGraph, edge::AbstractEdge) + return SuperEdge( + parent(supervertex(pg, src(edge))) => parent(supervertex(pg, dst(edge))) ) end -partitionedge(pg::PartitionedGraph, p::Pair) = partitionedge(pg, edgetype(pg)(p)) +superedge(pg::PartitionedGraph, p::Pair) = superedge(pg, edgetype(pg)(p)) -function partitionedges(pg::PartitionedGraph, edges::Vector) - return filter(!is_self_loop, unique([partitionedge(pg, e) for e in edges])) +function superedges(pg::PartitionedGraph, edges::Vector) + return filter(!is_self_loop, unique([superedge(pg, e) for e in edges])) end -function partitionedges(pg::PartitionedGraph) - return PartitionEdge.(edges(pg.partitions_graph)) +function superedges(pg::PartitionedGraph) + return SuperEdge.(edges(pg.partitions_graph)) end -function Graphs.edges(pg::PartitionedGraph, partitionedge::PartitionEdge) - psrc_vs = vertices(pg, src(partitionedge)) - pdst_vs = vertices(pg, dst(partitionedge)) +function Graphs.edges(pg::PartitionedGraph, superedge::SuperEdge) + psrc_vs = vertices(pg, src(superedge)) + pdst_vs = vertices(pg, dst(superedge)) psrc_subgraph, _ = induced_subgraph(unpartitioned_graph(pg), psrc_vs) pdst_subgraph, _ = induced_subgraph(pg, pdst_vs) full_subgraph, _ = induced_subgraph(pg, vcat(psrc_vs, pdst_vs)) @@ -99,20 +99,20 @@ function Graphs.edges(pg::PartitionedGraph, partitionedge::PartitionEdge) return setdiff(edges(full_subgraph), vcat(edges(psrc_subgraph), edges(pdst_subgraph))) end -function Graphs.edges(pg::PartitionedGraph, partitionedges::Vector{<:PartitionEdge}) - return unique(reduce(vcat, [edges(pg, pe) for pe in partitionedges])) +function Graphs.edges(pg::PartitionedGraph, superedges::Vector{<:SuperEdge}) + return unique(reduce(vcat, [edges(pg, se) for se in superedges])) end -function boundary_partitionedges(pg::PartitionedGraph, partitionvertices; kwargs...) - return PartitionEdge.( - boundary_edges(pg.partitions_graph, parent.(partitionvertices); kwargs...) +function boundary_superedges(pg::PartitionedGraph, supervertices; kwargs...) + return SuperEdge.( + boundary_edges(pg.partitions_graph, parent.(supervertices); kwargs...) ) end -function boundary_partitionedges( - pg::PartitionedGraph, partitionvertex::PartitionVertex; kwargs... +function boundary_superedges( + pg::PartitionedGraph, supervertex::SuperVertex; kwargs... ) - return boundary_partitionedges(pg, [partitionvertex]; kwargs...) + return boundary_superedges(pg, [supervertex]; kwargs...) end function Base.copy(pg::PartitionedGraph) @@ -125,13 +125,13 @@ function Base.copy(pg::PartitionedGraph) end function insert_to_vertex_map!( - pg::PartitionedGraph, vertex, partitionvertex::PartitionVertex + pg::PartitionedGraph, vertex, supervertex::SuperVertex ) - pv = parent(partitionvertex) + pv = parent(supervertex) if pv ∉ keys(partitioned_vertices(pg)) insert!(partitioned_vertices(pg), pv, [vertex]) else - partitioned_vertices(pg)[pv] = unique(vcat(vertices(pg, partitionvertex), [vertex])) + partitioned_vertices(pg)[pv] = unique(vcat(vertices(pg, supervertex), [vertex])) end insert!(which_partition(pg), vertex, pv) @@ -139,12 +139,12 @@ function insert_to_vertex_map!( end function delete_from_vertex_map!(pg::PartitionedGraph, vertex) - pv = partitionvertex(pg, vertex) - return delete_from_vertex_map!(pg, pv, vertex) + sv = supervertex(pg, vertex) + return delete_from_vertex_map!(pg, sv, vertex) end function delete_from_vertex_map!( - pg::PartitionedGraph, partitioned_vertex::PartitionVertex, vertex + pg::PartitionedGraph, partitioned_vertex::SuperVertex, vertex ) vs = vertices(pg, partitioned_vertex) delete!(partitioned_vertices(pg), parent(partitioned_vertex)) @@ -173,9 +173,9 @@ function partitionedgraph_induced_subgraph(pg::PartitionedGraph, vertices::Vecto end function partitionedgraph_induced_subgraph( - pg::PartitionedGraph, partitionverts::Vector{<:PartitionVertex} + pg::PartitionedGraph, supervertices::Vector{<:SuperVertex} ) - return induced_subgraph(pg, vertices(pg, partitionverts)) + return induced_subgraph(pg, vertices(pg, supervertices)) end function Graphs.induced_subgraph(pg::PartitionedGraph, vertices) diff --git a/src/lib/PartitionedGraphs/src/partitionvertex.jl b/src/lib/PartitionedGraphs/src/partitionvertex.jl deleted file mode 100644 index a2d46c6..0000000 --- a/src/lib/PartitionedGraphs/src/partitionvertex.jl +++ /dev/null @@ -1,5 +0,0 @@ -struct PartitionVertex{V} <: AbstractPartitionVertex{V} - vertex::V -end - -Base.parent(pv::PartitionVertex) = getfield(pv, :vertex) diff --git a/src/lib/PartitionedGraphs/src/superedge.jl b/src/lib/PartitionedGraphs/src/superedge.jl new file mode 100644 index 0000000..4ce55df --- /dev/null +++ b/src/lib/PartitionedGraphs/src/superedge.jl @@ -0,0 +1,12 @@ +using Graphs: Graphs, AbstractEdge, dst, src + +struct SuperEdge{V, E <: AbstractEdge{V}} <: AbstractSuperEdge{V} + edge::E +end + +Base.parent(se::SuperEdge) = getfield(se, :edge) +Graphs.src(se::SuperEdge) = SuperVertex(src(parent(se))) +Graphs.dst(se::SuperEdge) = SuperVertex(dst(parent(se))) +SuperEdge(p::Pair) = SuperEdge(NamedEdge(first(p) => last(p))) +SuperEdge(vsrc, vdst) = SuperEdge(vsrc => vdst) +Base.reverse(se::SuperEdge) = SuperEdge(reverse(parent(se))) diff --git a/src/lib/PartitionedGraphs/src/supervertex.jl b/src/lib/PartitionedGraphs/src/supervertex.jl new file mode 100644 index 0000000..16d5e29 --- /dev/null +++ b/src/lib/PartitionedGraphs/src/supervertex.jl @@ -0,0 +1,5 @@ +struct SuperVertex{V} <: AbstractSuperVertex{V} + vertex::V +end + +Base.parent(sv::SuperVertex) = getfield(sv, :vertex) diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index e3c35d3..ecf5b63 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -35,16 +35,16 @@ using NamedGraphs.NamedGraphGenerators: named_comb_tree, named_grid, named_triangular_lattice_graph using NamedGraphs.OrderedDictionaries: OrderedDictionary using NamedGraphs.PartitionedGraphs: - PartitionEdge, - PartitionVertex, + SuperEdge, + SuperVertex, PartitionedGraph, - PartitionsGraphView, - boundary_partitionedges, - partitions_graph, - partitionedge, - partitionedges, - partitionvertex, - partitionvertices, + QuotientGraph, + boundary_superedges, + quotient_graph, + superedge, + superedges, + supervertex, + supervertices, unpartitioned_graph using Dictionaries: Dictionary, dictionary using Pkg: Pkg @@ -57,20 +57,20 @@ using Test: @test, @testset #Partition it column-wise (into a 1D chain) partitions = [[(i, j) for j in 1:ny] for i in 1:nx] pg = PartitionedGraph(g, partitions) - @test vertextype(partitions_graph(pg)) == Int64 + @test vertextype(quotient_graph(pg)) == Int64 @test vertextype(unpartitioned_graph(pg)) == vertextype(g) - @test isa(partitionvertices(pg), OrderedDictionary{Int64, PartitionVertex{Int64}}) - @test isa(partitionedges(pg), Vector{PartitionEdge{Int64, NamedEdge{Int64}}}) - @test is_tree(partitions_graph(pg)) + @test isa(supervertices(pg), OrderedDictionary{Int64, SuperVertex{Int64}}) + @test isa(superedges(pg), Vector{SuperEdge{Int64, NamedEdge{Int64}}}) + @test is_tree(quotient_graph(pg)) @test nv(pg) == nx * ny - @test nv(partitions_graph(pg)) == nx + @test nv(quotient_graph(pg)) == nx pg_c = copy(pg) @test pg_c == pg #PartionsGraphView test - pgv = PartitionsGraphView(pg) - @test vertices(pgv) == parent.(partitionvertices(pg)) - @test edges(pgv) == parent.(partitionedges(pg)) + pgv = QuotientGraph(pg) + @test vertices(pgv) == parent.(supervertices(pg)) + @test edges(pgv) == parent.(superedges(pg)) @test is_tree(pgv) == true @test neighbors(pgv, 1) == [2] @test issetequal(vertices(subgraph(pgv, [2, 3, 4])), [2, 3, 4]) @@ -79,29 +79,29 @@ using Test: @test, @testset #Same partitioning but with a dictionary constructor partition_dict = Dictionary([first(partition) for partition in partitions], partitions) pg = PartitionedGraph(g, partition_dict) - @test vertextype(partitions_graph(pg)) == vertextype(g) + @test vertextype(quotient_graph(pg)) == vertextype(g) @test vertextype(unpartitioned_graph(pg)) == vertextype(g) @test isa( - partitionvertices(pg), - OrderedDictionary{Tuple{Int64, Int64}, PartitionVertex{Tuple{Int64, Int64}}}, + supervertices(pg), + OrderedDictionary{Tuple{Int64, Int64}, SuperVertex{Tuple{Int64, Int64}}}, ) @test isa( - partitionedges(pg), - Vector{PartitionEdge{Tuple{Int64, Int64}, NamedEdge{Tuple{Int64, Int64}}}}, + superedges(pg), + Vector{SuperEdge{Tuple{Int64, Int64}, NamedEdge{Tuple{Int64, Int64}}}}, ) - @test is_tree(partitions_graph(pg)) + @test is_tree(quotient_graph(pg)) @test nv(pg) == nx * ny - @test nv(partitions_graph(pg)) == nx + @test nv(quotient_graph(pg)) == nx pg_c = copy(pg) @test pg_c == pg #Partition the whole thing into just 1 vertex pg = PartitionedGraph([i for i in 1:nx]) - @test unpartitioned_graph(pg) == partitions_graph(pg) + @test unpartitioned_graph(pg) == quotient_graph(pg) @test nv(pg) == nx - @test nv(partitions_graph(pg)) == nx + @test nv(quotient_graph(pg)) == nx @test ne(pg) == 0 - @test ne(partitions_graph(pg)) == 0 + @test ne(quotient_graph(pg)) == 0 pg_c = copy(pg) @test pg_c == pg end @@ -113,25 +113,25 @@ end #Partition it column-wise (into a square grid) partitions = [[(i, j, k) for k in 1:nz] for i in 1:nx for j in 1:ny] pg = PartitionedGraph(g, partitions) - @test Set(partitionvertices(pg)) == Set(partitionvertices(pg, vertices(g))) - @test Set(partitionedges(pg)) == Set(partitionedges(pg, edges(g))) - @test is_self_loop(partitionedge(pg, (1, 1, 1) => (1, 1, 2))) - @test !is_self_loop(partitionedge(pg, (1, 2, 1) => (1, 1, 1))) - @test partitionvertex(pg, (1, 1, 1)) == partitionvertex(pg, (1, 1, nz)) - @test partitionvertex(pg, (2, 1, 1)) != partitionvertex(pg, (1, 1, nz)) - - @test partitionedge(pg, (1, 1, 1) => (2, 1, 1)) == - partitionedge(pg, (1, 1, 2) => (2, 1, 2)) + @test Set(supervertices(pg)) == Set(supervertices(pg, vertices(g))) + @test Set(superedges(pg)) == Set(superedges(pg, edges(g))) + @test is_self_loop(superedge(pg, (1, 1, 1) => (1, 1, 2))) + @test !is_self_loop(superedge(pg, (1, 2, 1) => (1, 1, 1))) + @test supervertex(pg, (1, 1, 1)) == supervertex(pg, (1, 1, nz)) + @test supervertex(pg, (2, 1, 1)) != supervertex(pg, (1, 1, nz)) + + @test superedge(pg, (1, 1, 1) => (2, 1, 1)) == + superedge(pg, (1, 1, 2) => (2, 1, 2)) inter_column_edges = [(1, 1, i) => (2, 1, i) for i in 1:nz] - @test length(partitionedges(pg, inter_column_edges)) == 1 - @test length(partitionvertices(pg, [(1, 2, i) for i in 1:nz])) == 1 - @test all([length(edges(pg, pe)) == nz for pe in partitionedges(pg)]) + @test length(superedges(pg, inter_column_edges)) == 1 + @test length(supervertices(pg, [(1, 2, i) for i in 1:nz])) == 1 + @test all([length(edges(pg, pe)) == nz for pe in superedges(pg)]) - boundary_sizes = [length(boundary_partitionedges(pg, pv)) for pv in partitionvertices(pg)] + boundary_sizes = [length(boundary_superedges(pg, pv)) for pv in supervertices(pg)] #Partitions into a square grid so each partition should have maximum 4 incoming edges and minimum 2 @test maximum(boundary_sizes) == 4 @test minimum(boundary_sizes) == 2 - @test isempty(boundary_partitionedges(pg, partitionvertices(pg))) + @test isempty(boundary_superedges(pg, supervertices(pg))) end @testset "Test Partitioned Graph Vertex/Edge Addition and Removal" begin @@ -141,28 +141,28 @@ end partitions = [[(i, j) for j in 1:ny] for i in 1:nx] pg = PartitionedGraph(g, partitions) - pv = PartitionVertex(5) + pv = SuperVertex(5) v_set = vertices(pg, pv) 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 rem_vertex!(pg, pv) - @test !is_connected(unpartitioned_graph(pg)) && !is_connected(partitions_graph(pg)) - @test parent(pv) ∉ vertices(partitions_graph(pg)) + @test !is_connected(unpartitioned_graph(pg)) && !is_connected(quotient_graph(pg)) + @test parent(pv) ∉ vertices(quotient_graph(pg)) @test !has_vertex(pg, pv) @test nv(pg) == (nx - 1) * ny - @test nv(partitions_graph(pg)) == nx - 1 - @test !is_tree(partitions_graph(pg)) + @test nv(quotient_graph(pg)) == nx - 1 + @test !is_tree(quotient_graph(pg)) #Add the column back to the in place graph add_vertices!(pg, v_set, pv) add_edges!(pg, edges_involving_v_set) - @test is_connected(pg.graph) && is_path_graph(partitions_graph(pg)) - @test parent(pv) ∈ vertices(partitions_graph(pg)) + @test is_connected(pg.graph) && is_path_graph(quotient_graph(pg)) + @test parent(pv) ∈ vertices(quotient_graph(pg)) @test has_vertex(pg, pv) - @test is_tree(partitions_graph(pg)) + @test is_tree(quotient_graph(pg)) @test nv(pg) == nx * ny - @test nv(partitions_graph(pg)) == nx + @test nv(quotient_graph(pg)) == nx end @testset "Test Partitioned Graph Subgraph Functionality" begin @@ -180,15 +180,15 @@ end vcat, [partitions[spv] for spv in subgraph_partitioned_vertices] ) - pg_1 = subgraph(pg, PartitionVertex.(subgraph_partitioned_vertices)) + pg_1 = subgraph(pg, SuperVertex.(subgraph_partitioned_vertices)) pg_2 = subgraph(pg, subgraph_vertices) @test pg_1 == pg_2 @test nv(pg_1) == length(subgraph_vertices) - @test nv(partitions_graph(pg_1)) == length(subgraph_partitioned_vertices) + @test nv(quotient_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)) + g_1 = subgraph(pg, SuperVertex(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) @@ -207,9 +207,9 @@ end pg = PartitionedGraph(g, [vertices(g)]) @test f(pg) == f(unpartitioned_graph(pg)) @test nv(pg) == nv(g) - @test nv(partitions_graph(pg)) == 1 + @test nv(quotient_graph(pg)) == 1 @test ne(pg) == ne(g) - @test ne(partitions_graph(pg)) == 0 + @test ne(quotient_graph(pg)) == 0 end end end @@ -226,7 +226,7 @@ end for backend in backends pg = PartitionedGraph(g; npartitions, backend = "metis") @test pg isa PartitionedGraph - @test nv(partitions_graph(pg)) == npartitions + @test nv(quotient_graph(pg)) == npartitions end end end From c8805cd4aad7f989df015d31cb2e6014615c927f Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Mon, 3 Nov 2025 16:46:52 -0500 Subject: [PATCH 03/36] Remove some duplicate methods overwriting each other --- src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index e59c16b..f2fc61d 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -22,13 +22,11 @@ function unpartitioned_graph_type(pg::Type{<:AbstractPartitionedGraph}) return not_implemented() end supervertex(pg::AbstractPartitionedGraph, vertex) = not_implemented() -supervertex(pg::AbstractPartitionedGraph, verts) = not_implemented() supervertex(pg::AbstractPartitionedGraph) = not_implemented() Base.copy(pg::AbstractPartitionedGraph) = not_implemented() delete_from_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() insert_to_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() superedge(pg::AbstractPartitionedGraph, edge) = not_implemented() -superedges(pg::AbstractPartitionedGraph, edges) = not_implemented() superedges(pg::AbstractPartitionedGraph) = not_implemented() function unpartitioned_graph_type(pg::AbstractPartitionedGraph) return typeof(unpartitioned_graph(pg)) From 345ef6b75ad449f1d4866cf3b11c1d65ee1c83bf Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Mon, 3 Nov 2025 16:52:50 -0500 Subject: [PATCH 04/36] Version bump --- Project.toml | 2 +- docs/Project.toml | 2 +- examples/Project.toml | 2 +- test/Project.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Project.toml b/Project.toml index 7a69e9d..883294d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "NamedGraphs" uuid = "678767b0-92e7-4007-89e4-4527a8725b19" authors = ["Matthew Fishman , Joseph Tindall and contributors"] -version = "0.7.4" +version = "0.8.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" diff --git a/docs/Project.toml b/docs/Project.toml index 16ff40a..2309122 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -6,4 +6,4 @@ NamedGraphs = "678767b0-92e7-4007-89e4-4527a8725b19" [compat] Documenter = "1.10" Literate = "2.20.1" -NamedGraphs = "0.7" +NamedGraphs = "0.8" diff --git a/examples/Project.toml b/examples/Project.toml index 123e6c3..07edffc 100644 --- a/examples/Project.toml +++ b/examples/Project.toml @@ -4,4 +4,4 @@ NamedGraphs = "678767b0-92e7-4007-89e4-4527a8725b19" [compat] Graphs = "1.12.0" -NamedGraphs = "0.7.0" +NamedGraphs = "0.8.0" diff --git a/test/Project.toml b/test/Project.toml index e75deaf..b78e64e 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -24,7 +24,7 @@ Graphs = "1.12" GraphsFlows = "0.1.1" KaHyPar = "0.3.1" Metis = "1.5.0" -NamedGraphs = "0.7.0" +NamedGraphs = "0.8.0" SafeTestsets = "0.1.0" SimpleGraphAlgorithms = "0.6.0" SimpleGraphConverter = "0.1.0" From c412e315b6a1bac5d49f51470fe8a4f7a5e4bdff Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 4 Nov 2025 09:50:51 -0500 Subject: [PATCH 05/36] Remove `quotient_graph` function in favour of a direct call to `QuotientGraph`. --- .../src/abstractpartitionedgraph.jl | 18 +++---- .../PartitionedGraphs/src/partitionedgraph.jl | 1 - .../PartitionedGraphs/src/quotientgraph.jl | 7 +-- test/test_partitionedgraph.jl | 47 +++++++++---------- 4 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index f2fc61d..da2ccb2 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -16,7 +16,7 @@ using ..NamedGraphs.GraphsExtensions: abstract type AbstractPartitionedGraph{V, PV} <: AbstractNamedGraph{V} end #Needed for interface -quotient_graph(pg::AbstractPartitionedGraph) = not_implemented() +QuotientGraph(pg::AbstractPartitionedGraph) = not_implemented() unpartitioned_graph(pg::AbstractPartitionedGraph) = not_implemented() function unpartitioned_graph_type(pg::Type{<:AbstractPartitionedGraph}) return not_implemented() @@ -73,11 +73,11 @@ end function Graphs.has_vertex( pg::AbstractPartitionedGraph, supervertex::AbstractSuperVertex ) - return has_vertex(quotient_graph(pg), parent(supervertex)) + return has_vertex(QuotientGraph(pg), parent(supervertex)) end function Graphs.has_edge(pg::AbstractPartitionedGraph, superedge::AbstractSuperEdge) - return has_edge(quotient_graph(pg), parent(superedge)) + return has_edge(QuotientGraph(pg), parent(superedge)) end function is_boundary_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) @@ -89,17 +89,17 @@ function Graphs.add_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) add_edge!(unpartitioned_graph(pg), edge) pg_edge = parent(superedge(pg, edge)) if src(pg_edge) != dst(pg_edge) - add_edge!(quotient_graph(pg), pg_edge) + add_edge!(QuotientGraph(pg), pg_edge) end return pg end function Graphs.rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) pg_edge = superedge(pg, edge) - if has_edge(quotient_graph(pg), pg_edge) + if has_edge(QuotientGraph(pg), pg_edge) g_edges = edges(pg, pg_edge) if length(g_edges) == 1 - rem_edge!(quotient_graph(pg), pg_edge) + rem_edge!(QuotientGraph(pg), pg_edge) end end return rem_edge!(unpartitioned_graph(pg), edge) @@ -116,7 +116,7 @@ function Graphs.add_vertex!( pg::AbstractPartitionedGraph, vertex, supervertex::AbstractSuperVertex ) add_vertex!(unpartitioned_graph(pg), vertex) - add_vertex!(quotient_graph(pg), parent(supervertex)) + add_vertex!(QuotientGraph(pg), parent(supervertex)) insert_to_vertex_map!(pg, vertex, supervertex) return pg end @@ -146,7 +146,7 @@ function Graphs.rem_vertex!(pg::AbstractPartitionedGraph, vertex) delete_from_vertex_map!(pg, sv, vertex) rem_vertex!(unpartitioned_graph(pg), vertex) if !haskey(partitioned_vertices(pg), parent(sv)) - rem_vertex!(quotient_graph(pg), parent(sv)) + rem_vertex!(QuotientGraph(pg), parent(sv)) end return pg end @@ -172,7 +172,7 @@ end function Base.:(==)(pg1::AbstractPartitionedGraph, pg2::AbstractPartitionedGraph) if unpartitioned_graph(pg1) != unpartitioned_graph(pg2) || - quotient_graph(pg1) != quotient_graph(pg2) + QuotientGraph(pg1) != QuotientGraph(pg2) return false end for v in vertices(pg1) diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index cbd0fce..a24349e 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -46,7 +46,6 @@ function PartitionedGraph(g::AbstractGraph; kwargs...) end #Needed for interface -quotient_graph(pg::PartitionedGraph) = QuotientGraph(pg) unpartitioned_graph(pg::PartitionedGraph) = getfield(pg, :graph) function unpartitioned_graph_type(graph_type::Type{<:PartitionedGraph}) return fieldtype(graph_type, :graph) diff --git a/src/lib/PartitionedGraphs/src/quotientgraph.jl b/src/lib/PartitionedGraphs/src/quotientgraph.jl index f319633..6e287e8 100644 --- a/src/lib/PartitionedGraphs/src/quotientgraph.jl +++ b/src/lib/PartitionedGraphs/src/quotientgraph.jl @@ -1,11 +1,12 @@ +using Graphs: AbstractGraph + struct QuotientGraph{V, G <: AbstractGraph{V}} <: AbstractNamedGraph{V} graph::G + QuotientGraph(g::G) where {V, G<:AbstractPartitionedGraph{V}} = new{V,G}(g) end Base.copy(g::QuotientGraph) = QuotientGraph(copy(g.graph)) - -using Graphs: AbstractGraph -quotient_graph(g::AbstractGraph) = QuotientGraph(PartitionedGraph(g, [vertices(g)])) +QuotientGraph(g::AbstractGraph) = QuotientGraph(PartitionedGraph(g, [vertices(g)])) # Graphs.jl and NamedGraphs.jl interface overloads for `PartitionsGraphView` wrapping # a `PartitionedGraph`. diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index ecf5b63..9124c98 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -35,12 +35,11 @@ using NamedGraphs.NamedGraphGenerators: named_comb_tree, named_grid, named_triangular_lattice_graph using NamedGraphs.OrderedDictionaries: OrderedDictionary using NamedGraphs.PartitionedGraphs: - SuperEdge, - SuperVertex, PartitionedGraph, QuotientGraph, + SuperEdge, + SuperVertex, boundary_superedges, - quotient_graph, superedge, superedges, supervertex, @@ -57,13 +56,13 @@ using Test: @test, @testset #Partition it column-wise (into a 1D chain) partitions = [[(i, j) for j in 1:ny] for i in 1:nx] pg = PartitionedGraph(g, partitions) - @test vertextype(quotient_graph(pg)) == Int64 + @test vertextype(QuotientGraph(pg)) == Int64 @test vertextype(unpartitioned_graph(pg)) == vertextype(g) @test isa(supervertices(pg), OrderedDictionary{Int64, SuperVertex{Int64}}) @test isa(superedges(pg), Vector{SuperEdge{Int64, NamedEdge{Int64}}}) - @test is_tree(quotient_graph(pg)) + @test is_tree(QuotientGraph(pg)) @test nv(pg) == nx * ny - @test nv(quotient_graph(pg)) == nx + @test nv(QuotientGraph(pg)) == nx pg_c = copy(pg) @test pg_c == pg @@ -79,7 +78,7 @@ using Test: @test, @testset #Same partitioning but with a dictionary constructor partition_dict = Dictionary([first(partition) for partition in partitions], partitions) pg = PartitionedGraph(g, partition_dict) - @test vertextype(quotient_graph(pg)) == vertextype(g) + @test vertextype(QuotientGraph(pg)) == vertextype(g) @test vertextype(unpartitioned_graph(pg)) == vertextype(g) @test isa( supervertices(pg), @@ -89,19 +88,19 @@ using Test: @test, @testset superedges(pg), Vector{SuperEdge{Tuple{Int64, Int64}, NamedEdge{Tuple{Int64, Int64}}}}, ) - @test is_tree(quotient_graph(pg)) + @test is_tree(QuotientGraph(pg)) @test nv(pg) == nx * ny - @test nv(quotient_graph(pg)) == nx + @test nv(QuotientGraph(pg)) == nx pg_c = copy(pg) @test pg_c == pg #Partition the whole thing into just 1 vertex pg = PartitionedGraph([i for i in 1:nx]) - @test unpartitioned_graph(pg) == quotient_graph(pg) + @test unpartitioned_graph(pg) == QuotientGraph(pg) @test nv(pg) == nx - @test nv(quotient_graph(pg)) == nx + @test nv(QuotientGraph(pg)) == nx @test ne(pg) == 0 - @test ne(quotient_graph(pg)) == 0 + @test ne(QuotientGraph(pg)) == 0 pg_c = copy(pg) @test pg_c == pg end @@ -147,22 +146,22 @@ end #Strip the middle column from pg via the partitioned graph vertex, and make a new pg rem_vertex!(pg, pv) - @test !is_connected(unpartitioned_graph(pg)) && !is_connected(quotient_graph(pg)) - @test parent(pv) ∉ vertices(quotient_graph(pg)) + @test !is_connected(unpartitioned_graph(pg)) && !is_connected(QuotientGraph(pg)) + @test parent(pv) ∉ vertices(QuotientGraph(pg)) @test !has_vertex(pg, pv) @test nv(pg) == (nx - 1) * ny - @test nv(quotient_graph(pg)) == nx - 1 - @test !is_tree(quotient_graph(pg)) + @test nv(QuotientGraph(pg)) == nx - 1 + @test !is_tree(QuotientGraph(pg)) #Add the column back to the in place graph add_vertices!(pg, v_set, pv) add_edges!(pg, edges_involving_v_set) - @test is_connected(pg.graph) && is_path_graph(quotient_graph(pg)) - @test parent(pv) ∈ vertices(quotient_graph(pg)) + @test is_connected(pg.graph) && is_path_graph(QuotientGraph(pg)) + @test parent(pv) ∈ vertices(QuotientGraph(pg)) @test has_vertex(pg, pv) - @test is_tree(quotient_graph(pg)) + @test is_tree(QuotientGraph(pg)) @test nv(pg) == nx * ny - @test nv(quotient_graph(pg)) == nx + @test nv(QuotientGraph(pg)) == nx end @testset "Test Partitioned Graph Subgraph Functionality" begin @@ -184,7 +183,7 @@ end pg_2 = subgraph(pg, subgraph_vertices) @test pg_1 == pg_2 @test nv(pg_1) == length(subgraph_vertices) - @test nv(quotient_graph(pg_1)) == length(subgraph_partitioned_vertices) + @test nv(QuotientGraph(pg_1)) == length(subgraph_partitioned_vertices) subgraph_partitioned_vertex = 3 subgraph_vertices = partitions[subgraph_partitioned_vertex] @@ -207,9 +206,9 @@ end pg = PartitionedGraph(g, [vertices(g)]) @test f(pg) == f(unpartitioned_graph(pg)) @test nv(pg) == nv(g) - @test nv(quotient_graph(pg)) == 1 + @test nv(QuotientGraph(pg)) == 1 @test ne(pg) == ne(g) - @test ne(quotient_graph(pg)) == 0 + @test ne(QuotientGraph(pg)) == 0 end end end @@ -226,7 +225,7 @@ end for backend in backends pg = PartitionedGraph(g; npartitions, backend = "metis") @test pg isa PartitionedGraph - @test nv(quotient_graph(pg)) == npartitions + @test nv(QuotientGraph(pg)) == npartitions end end end From 2791da436c9c71ffe5bf64cd4637f0ef7d9f8766 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 4 Nov 2025 09:57:43 -0500 Subject: [PATCH 06/36] Runic formatting --- src/lib/PartitionedGraphs/src/quotientgraph.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/quotientgraph.jl b/src/lib/PartitionedGraphs/src/quotientgraph.jl index 6e287e8..e8a0198 100644 --- a/src/lib/PartitionedGraphs/src/quotientgraph.jl +++ b/src/lib/PartitionedGraphs/src/quotientgraph.jl @@ -1,8 +1,8 @@ using Graphs: AbstractGraph -struct QuotientGraph{V, G <: AbstractGraph{V}} <: AbstractNamedGraph{V} +struct QuotientGraph{V, G <: AbstractPartitionedGraph{V}} <: AbstractNamedGraph{V} graph::G - QuotientGraph(g::G) where {V, G<:AbstractPartitionedGraph{V}} = new{V,G}(g) + QuotientGraph(g::G) where {V, G <: AbstractPartitionedGraph{V}} = new{V, G}(g) end Base.copy(g::QuotientGraph) = QuotientGraph(copy(g.graph)) From 431ce5d3eda43a394401a0fc0020bec398129c5a Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Wed, 5 Nov 2025 16:42:06 -0500 Subject: [PATCH 07/36] Overhaul PartitionedGraphs Library --- .../NamedGraphsKaHyParExt.jl | 4 +- .../NamedGraphsMetisExt.jl | 4 +- src/abstractnamedgraph.jl | 118 ++++----- src/lib/GraphsExtensions/src/partitioning.jl | 8 +- .../src/abstractpartitionedgraph.jl | 232 ++++++++++++------ .../src/abstractsuperedge.jl | 25 +- .../src/abstractsupervertex.jl | 26 +- .../PartitionedGraphs/src/partitionedgraph.jl | 155 ++++++------ .../PartitionedGraphs/src/quotientgraph.jl | 55 +++-- test/test_partitionedgraph.jl | 47 ++-- 10 files changed, 409 insertions(+), 265 deletions(-) diff --git a/ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl b/ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl index c19fe1d..25756d2 100644 --- a/ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl +++ b/ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl @@ -27,14 +27,14 @@ const KAHYPAR_ALGS = Dict( ) """ -partitioned_vertices(::Backend"kahypar", g::Graph, npartiations::Integer; objective="edge_cut", alg="kway", kwargs...) +partitions(::Backend"kahypar", g::Graph, npartiations::Integer; objective="edge_cut", alg="kway", kwargs...) - default_configuration => "cut_kKaHyPar_sea20.ini" - :edge_cut => "cut_kKaHyPar_sea20.ini" - :connectivity => "km1_kKaHyPar_sea20.ini" - imbalance::Number=0.03 """ -function GraphsExtensions.partitioned_vertices( +function GraphsExtensions.partitions( ::Backend"kahypar", g::AbstractSimpleGraph, npartitions::Integer; diff --git a/ext/NamedGraphsMetisExt/NamedGraphsMetisExt.jl b/ext/NamedGraphsMetisExt/NamedGraphsMetisExt.jl index 9738c37..df51ba1 100644 --- a/ext/NamedGraphsMetisExt/NamedGraphsMetisExt.jl +++ b/ext/NamedGraphsMetisExt/NamedGraphsMetisExt.jl @@ -10,14 +10,14 @@ GraphsExtensions.set_partitioning_backend!(Backend"metis"()) const METIS_ALGS = Dict(["kway" => :KWAY, "recursive" => :RECURSIVE]) """ - partitioned_vertices(::Backend"metis", g::AbstractGraph, npartitions::Integer; alg="recursive") + partitions(::Backend"metis", g::AbstractGraph, npartitions::Integer; alg="recursive") Partition the graph `G` in `n` parts. The partition algorithm is defined by the `alg` keyword: - :KWAY: multilevel k-way partitioning - :RECURSIVE: multilevel recursive bisection """ -function GraphsExtensions.partitioned_vertices( +function GraphsExtensions.partitions( ::Backend"metis", g::AbstractSimpleGraph, npartitions::Integer; alg = "recursive", kwargs... ) metis_alg = METIS_ALGS[alg] diff --git a/src/abstractnamedgraph.jl b/src/abstractnamedgraph.jl index 61cdc26..36c78fd 100644 --- a/src/abstractnamedgraph.jl +++ b/src/abstractnamedgraph.jl @@ -32,7 +32,7 @@ using .GraphsExtensions: GraphsExtensions, directed_graph, incident_edges, - partitioned_vertices, + partitions, rename_vertices, subgraph using SimpleTraits: SimpleTraits, Not, @traitfn @@ -91,8 +91,8 @@ GraphsExtensions.convert_vertextype(::Type, ::AbstractNamedGraph) = not_implemen Base.copy(graph::AbstractNamedGraph) = not_implemented() function Graphs.merge_vertices!( - graph::AbstractNamedGraph, merge_vertices; merged_vertex = first(merge_vertices) - ) + graph::AbstractNamedGraph, merge_vertices; merged_vertex=first(merge_vertices) +) return not_implemented() end @@ -144,11 +144,11 @@ end # TODO: write in terms of a generic function. for f in [ - :(Graphs.outneighbors), - :(Graphs.inneighbors), - :(Graphs.all_neighbors), - :(Graphs.neighbors), - ] + :(Graphs.outneighbors), + :(Graphs.inneighbors), + :(Graphs.all_neighbors), + :(Graphs.neighbors), +] @eval begin function $f(graph::AbstractNamedGraph, vertex) position_vertices = $f(position_graph(graph), vertex_positions(graph)[vertex]) @@ -199,8 +199,8 @@ function Graphs.degree(graph::AbstractNamedGraph, vertex::Integer) return namedgraph_degree(graph::AbstractNamedGraph, vertex) end -function Graphs.degree_histogram(g::AbstractNamedGraph, degfn = degree) - hist = Dictionary{Int, Int}() +function Graphs.degree_histogram(g::AbstractNamedGraph, degfn=degree) + hist = Dictionary{Int,Int}() for v in vertices(g) # minimize allocations by for d in degfn(g, v) # iterating over vertices set!(hist, d, get(hist, d, 0) + 1) @@ -210,8 +210,8 @@ function Graphs.degree_histogram(g::AbstractNamedGraph, degfn = degree) end function namedgraph_neighborhood( - graph::AbstractNamedGraph, vertex, d, distmx = weights(graph); dir = :out - ) + graph::AbstractNamedGraph, vertex, d, distmx=weights(graph); dir=:out +) position_distmx = dist_matrix_to_position_dist_matrix(graph, distmx) position_vertices = neighborhood( position_graph(graph), vertex_positions(graph)[vertex], d, position_distmx; dir @@ -220,22 +220,22 @@ function namedgraph_neighborhood( end function Graphs.neighborhood( - graph::AbstractNamedGraph, vertex, d, distmx = weights(graph); dir = :out - ) + graph::AbstractNamedGraph, vertex, d, distmx=weights(graph); dir=:out +) return namedgraph_neighborhood(graph, vertex, d, distmx; dir) end # Fix for ambiguity error with `AbstractGraph` version function Graphs.neighborhood( - graph::AbstractNamedGraph, vertex::Integer, d, distmx = weights(graph); dir = :out - ) + graph::AbstractNamedGraph, vertex::Integer, d, distmx=weights(graph); dir=:out +) return namedgraph_neighborhood(graph, vertex, d, distmx; dir) end # Fix for ambiguity error with `AbstractGraph` version function Graphs.neighborhood( - graph::AbstractNamedGraph, vertex::Integer, d, distmx::AbstractMatrix{<:Real}; dir = :out - ) + graph::AbstractNamedGraph, vertex::Integer, d, distmx::AbstractMatrix{<:Real}; dir=:out +) return namedgraph_neighborhood(graph, vertex, d, distmx; dir) end @@ -246,27 +246,27 @@ function namedgraph_neighborhood_dists(graph::AbstractNamedGraph, vertex, d, dis ) return [ (ordered_vertices(graph)[position_vertex], dist) for - (position_vertex, dist) in position_vertices_and_dists + (position_vertex, dist) in position_vertices_and_dists ] end function Graphs.neighborhood_dists( - graph::AbstractNamedGraph, vertex, d, distmx = weights(graph); dir = :out - ) + graph::AbstractNamedGraph, vertex, d, distmx=weights(graph); dir=:out +) return namedgraph_neighborhood_dists(graph, vertex, d, distmx; dir) end # Fix for ambiguity error with `AbstractGraph` version function Graphs.neighborhood_dists( - graph::AbstractNamedGraph, vertex::Integer, d, distmx = weights(graph); dir = :out - ) + graph::AbstractNamedGraph, vertex::Integer, d, distmx=weights(graph); dir=:out +) return namedgraph_neighborhood_dists(graph, vertex, d, distmx; dir) end # Fix for ambiguity error with `AbstractGraph` version function Graphs.neighborhood_dists( - graph::AbstractNamedGraph, vertex::Integer, d, distmx::AbstractMatrix{<:Real}; dir = :out - ) + graph::AbstractNamedGraph, vertex::Integer, d, distmx::AbstractMatrix{<:Real}; dir=:out +) return namedgraph_neighborhood_dists(graph, vertex, d, distmx; dir) end @@ -276,7 +276,7 @@ function namedgraph_mincut(graph::AbstractNamedGraph, distmx) return Dictionary(vertices(graph), position_parity), bestcut end -function Graphs.mincut(graph::AbstractNamedGraph, distmx = weights(graph)) +function Graphs.mincut(graph::AbstractNamedGraph, distmx=weights(graph)) return namedgraph_mincut(graph, distmx) end @@ -285,10 +285,10 @@ function Graphs.mincut(graph::AbstractNamedGraph, distmx::AbstractMatrix{<:Real} end # TODO: Make this more generic? -function GraphsExtensions.partitioned_vertices( - graph::AbstractNamedGraph; npartitions = nothing, nvertices_per_partition = nothing, kwargs... - ) - vertex_partitions = partitioned_vertices( +function GraphsExtensions.partitions( + graph::AbstractNamedGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... +) + vertex_partitions = partitions( position_graph(graph); npartitions, nvertices_per_partition, kwargs... ) # TODO: output the reverse of this dictionary (a Vector of Vector @@ -300,13 +300,13 @@ function GraphsExtensions.partitioned_vertices( end function namedgraph_a_star( - graph::AbstractNamedGraph, - source, - destination, - distmx = weights(graph), - heuristic::Function = (v -> zero(eltype(distmx))), - edgetype_to_return = edgetype(graph), - ) + graph::AbstractNamedGraph, + source, + destination, + distmx=weights(graph), + heuristic::Function=(v -> zero(eltype(distmx))), + edgetype_to_return=edgetype(graph), +) position_distmx = dist_matrix_to_position_dist_matrix(graph, distmx) position_shortest_path = a_star( position_graph(graph), @@ -325,21 +325,21 @@ end # Fix ambiguity error with `AbstractGraph` version function Graphs.a_star( - graph::AbstractNamedGraph{U}, source::Integer, destination::Integer, args... - ) where {U <: Integer} + graph::AbstractNamedGraph{U}, source::Integer, destination::Integer, args... +) where {U<:Integer} return namedgraph_a_star(graph, source, destination, args...) end # Fix ambiguity error with `AbstractGraph` version function Graphs.a_star( - graph::AbstractNamedGraph, source::Integer, destination::Integer, args... - ) + graph::AbstractNamedGraph, source::Integer, destination::Integer, args... +) return namedgraph_a_star(graph, source, destination, args...) end function Graphs.spfa_shortest_paths( - graph::AbstractNamedGraph, vertex, distmx = weights(graph) - ) + graph::AbstractNamedGraph, vertex, distmx=weights(graph) +) position_distmx = dist_matrix_to_position_dist_matrix(graph, distmx) position_shortest_paths = spfa_shortest_paths( position_graph(graph), vertex_positions(graph)[vertex], position_distmx @@ -348,20 +348,20 @@ function Graphs.spfa_shortest_paths( end function Graphs.boruvka_mst( - g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real} = weights(g); minimize = true - ) + g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real}=weights(g); minimize=true +) position_mst, weights = boruvka_mst(position_graph(g), distmx; minimize) return map(e -> position_edge_to_edge(g, e), position_mst), weights end function Graphs.kruskal_mst( - g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real} = weights(g); minimize = true - ) + g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real}=weights(g); minimize=true +) position_mst = kruskal_mst(position_graph(g), distmx; minimize) return map(e -> position_edge_to_edge(g, e), position_mst) end -function Graphs.prim_mst(g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real} = weights(g)) +function Graphs.prim_mst(g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real}=weights(g)) position_mst = prim_mst(position_graph(g), distmx) return map(e -> position_edge_to_edge(g, e), position_mst) end @@ -386,13 +386,13 @@ Graphs.has_edge(g::AbstractNamedGraph, edge) = has_edge(g, edgetype(g)(edge)) Graphs.has_edge(g::AbstractNamedGraph, src, dst) = has_edge(g, edgetype(g)(src, dst)) function Graphs.has_path( - graph::AbstractNamedGraph, source, destination; exclude_vertices = vertextype(graph)[] - ) + graph::AbstractNamedGraph, source, destination; exclude_vertices=vertextype(graph)[] +) return has_path( position_graph(graph), vertex_positions(graph)[source], vertex_positions(graph)[destination]; - exclude_vertices = map(v -> vertex_positions(graph)[v], exclude_vertices), + exclude_vertices=map(v -> vertex_positions(graph)[v], exclude_vertices), ) end @@ -412,11 +412,11 @@ function Base.union(graph1::AbstractNamedGraph, graph2::AbstractNamedGraph) end function Base.union( - graph1::AbstractNamedGraph, - graph2::AbstractNamedGraph, - graph3::AbstractNamedGraph, - graph_rest::AbstractNamedGraph..., - ) + graph1::AbstractNamedGraph, + graph2::AbstractNamedGraph, + graph3::AbstractNamedGraph, + graph_rest::AbstractNamedGraph..., +) return union(union(graph1, graph2), graph3, graph_rest...) end @@ -463,12 +463,12 @@ function Graphs.connected_components(graph::AbstractNamedGraph) end function Graphs.merge_vertices( - graph::AbstractNamedGraph, merge_vertices; merged_vertex = first(merge_vertices) - ) + graph::AbstractNamedGraph, merge_vertices; merged_vertex=first(merge_vertices) +) merged_graph = copy(graph) add_vertex!(merged_graph, merged_vertex) for vertex in merge_vertices - for e in incident_edges(graph, vertex; dir = :both) + for e in incident_edges(graph, vertex; dir=:both) merged_edge = rename_vertices(v -> v == vertex ? merged_vertex : v, e) if src(merged_edge) ≠ dst(merged_edge) add_edge!(merged_graph, merged_edge) diff --git a/src/lib/GraphsExtensions/src/partitioning.jl b/src/lib/GraphsExtensions/src/partitioning.jl index c9d5c5e..267c005 100644 --- a/src/lib/GraphsExtensions/src/partitioning.jl +++ b/src/lib/GraphsExtensions/src/partitioning.jl @@ -54,7 +54,7 @@ function _npartitions( return error("Must specify either `npartitions` or `nvertices_per_partition`") end -function partitioned_vertices( +function partitions( g::AbstractSimpleGraph; npartitions = nothing, nvertices_per_partition = nothing, @@ -66,11 +66,9 @@ function partitioned_vertices( if (_npartitions(g, npartitions, nvertices_per_partition) == 1) return group(v -> 1, collect(vertices(g))) end - return partitioned_vertices( + return partitions( Backend(backend), g, _npartitions(g, npartitions, nvertices_per_partition); kwargs... ) end -function partitioned_vertices(g::AbstractGraph; kwargs...) - return not_implemented() -end +# partitionings(g::AbstractGraph) = [vertices(g)] diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index da2ccb2..5a5828e 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -1,6 +1,7 @@ using Graphs: - Graphs, AbstractEdge, + AbstractGraph, + Graphs, add_vertex!, dst, edgetype, @@ -11,45 +12,125 @@ using Graphs: vertices using ..NamedGraphs: NamedGraphs, AbstractNamedGraph using ..NamedGraphs.GraphsExtensions: - GraphsExtensions, add_vertices!, not_implemented, rem_vertices! + GraphsExtensions, add_vertices!, not_implemented, rem_vertices!, subgraph -abstract type AbstractPartitionedGraph{V, PV} <: AbstractNamedGraph{V} end +# Essential method for the interface +partitioned_vertices(g::AbstractGraph) = [vertices(g)] -#Needed for interface -QuotientGraph(pg::AbstractPartitionedGraph) = not_implemented() -unpartitioned_graph(pg::AbstractPartitionedGraph) = not_implemented() -function unpartitioned_graph_type(pg::Type{<:AbstractPartitionedGraph}) - return not_implemented() +# Don't need to overload this +partitioned_vertices(g::AbstractGraph, sv::AbstractSuperVertex) = partitioned_vertices(g)[parent(sv)] +function partitioned_vertices(g::AbstractGraph, svs::Vector{<:AbstractSuperVertex}) + return mapreduce(sv -> partitioned_vertices(g, sv), vcat, svs) end -supervertex(pg::AbstractPartitionedGraph, vertex) = not_implemented() -supervertex(pg::AbstractPartitionedGraph) = not_implemented() -Base.copy(pg::AbstractPartitionedGraph) = not_implemented() -delete_from_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() -insert_to_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented() -superedge(pg::AbstractPartitionedGraph, edge) = not_implemented() -superedges(pg::AbstractPartitionedGraph) = not_implemented() -function unpartitioned_graph_type(pg::AbstractPartitionedGraph) - return typeof(unpartitioned_graph(pg)) + +# Optional functions for the interface + +## Return the partition that a vertex belongs to +function findpartition(pvs, vertex) + rv = findfirst(pv -> vertex ∈ pv, pvs) + if isnothing(rv) + error("Vertex $vertex not found in any partition.") + end + return rv end +findpartition(g::AbstractGraph, vertex) = findpartition(partitioned_vertices(g), vertex) -function Graphs.edges(pg::AbstractPartitionedGraph, superedge::AbstractSuperEdge) - return not_implemented() +function partitioned_edges(g::AbstractGraph, pvs = partitioned_vertices(g)) + + SVT = keytype(pvs) + ET = edgetype(g) + rv = Dictionary{NamedEdge{SVT}, Vector{ET}}() + + for e in edges(g) + pv_src = findpartition(pvs, src(e)) + pv_dst = findpartition(pvs, dst(e)) + se = NamedEdge(pv_src => pv_dst) + if is_self_loop(se) + continue + end + push!(get!(rv, se, typeof(e)[]), e) + end + return rv end -function Graphs.vertices(pg::AbstractPartitionedGraph, supervertex::AbstractSuperVertex) - return not_implemented() +partitioned_edges(g::AbstractGraph, se::AbstractSuperEdge) = partitioned_edges(g)[parent(se)] +function partitioned_edges(g::AbstractGraph, ses::Vector{<:AbstractSuperEdge}) + return mapreduce(se -> partitioned_edges(g, se), vcat, ses) +end + +quotient_vertices(g) = keys(partitioned_vertices(g)) +quotient_edges(g, pvs = partitioned_vertices(g)) = keys(partitioned_edges(g, pvs)) + +function quotient_graph(g::AbstractGraph) + qg = NamedGraph(quotient_vertices(g)) + add_edges!(qg, quotient_edges(g)) + return qg end -function Graphs.vertices( - pg::AbstractPartitionedGraph, supervertices::Vector{V} - ) where {V <: AbstractSuperVertex} + +function quotient_graph(g::AbstractGraph, pvs) + qg = NamedGraph(keys(pvs)) + add_edges!(qg, quotient_edges(g, pvs)) + return qg +end + + +""" +abstract type AbstractPartitionedGraph{V, PV} <: AbstractNamedGraph{V} + +To use `AbstractPartitionedGraph` one should defined `unpartitioned_graph` that returns +an underlying graph *without* any partitioning. One should also define: +""" +abstract type AbstractPartitionedGraph{V, PV} <: AbstractNamedGraph{V} end + +#Needed for interface +unpartitioned_graph(::AbstractPartitionedGraph) = not_implemented() +Base.copy(::AbstractPartitionedGraph) = not_implemented() + +super_vertex_type(::Type{<:AbstractPartitionedGraph}) = not_implemented() +super_edge_type(::Type{<:AbstractPartitionedGraph}) = not_implemented() + +supervertex(::AbstractPartitionedGraph, vertex) = not_implemented() +superedge(::AbstractPartitionedGraph, edge) = not_implemented() + +delete_from_vertex_map!(::AbstractPartitionedGraph, vertex) = not_implemented() +insert_to_vertex_map!(::AbstractPartitionedGraph, vertex) = not_implemented() + +function unpartitioned_graph_type(::Type{<:AbstractPartitionedGraph}) return not_implemented() end -function GraphsExtensions.directed_graph_type(PG::Type{<:AbstractPartitionedGraph}) +function GraphsExtensions.directed_graph_type(::Type{<:AbstractPartitionedGraph}) return not_implemented() end -function GraphsExtensions.undirected_graph_type(PG::Type{<:AbstractPartitionedGraph}) +function GraphsExtensions.undirected_graph_type(::Type{<:AbstractPartitionedGraph}) return not_implemented() end +# Derived (by default) +super_vertex_type(pg::AbstractPartitionedGraph) = super_vertex_type(typeof(pg)) +super_edge_type(pg::AbstractPartitionedGraph) = super_edge_type(typeof(pg)) + +function unpartitioned_graph_type(pg::AbstractPartitionedGraph) + return typeof(unpartitioned_graph(pg)) +end + +""" + supervertices(pg::AbstractPartitionedGraph, vs = vertices(pg)) + +Return all unique super vertices corresponding to the set vertices `vs` of the graph `pg`. +""" +function supervertices(pg::AbstractPartitionedGraph, vs = vertices(pg)) + return unique(map(v -> supervertex(pg,v), vs)) +end + +""" + superedges(pg::AbstractPartitionedGraph, es = edges(pg)) + +Return all unique super edges corresponding to the set edges `es` of the graph `pg`. +""" +function superedges(pg::AbstractPartitionedGraph, es = edges(pg)) + return filter!(!is_self_loop, unique(map(e -> superedge(pg, e), es))) +end +superedge(pg::AbstractPartitionedGraph, p::Pair) = superedge(pg, edgetype(pg)(p)) + # AbstractGraph interface. function Graphs.is_directed(graph_type::Type{<:AbstractPartitionedGraph}) return is_directed(unpartitioned_graph_type(graph_type)) @@ -57,6 +138,43 @@ end #Functions for the abstract type Graphs.vertices(pg::AbstractPartitionedGraph) = vertices(unpartitioned_graph(pg)) + +""" + vertices(pg::AbstractPartitionedGraph, supervertex::AbstractSuperEdge) + vertices(pg::AbstractPartitionedGraph, supervertices::Vector{AbstractSuperEdge}) + +Return the set of vertices in the partitioned graph `pg` that correspond to the super vertex +`supervertex` or set of super vertices `supervertex`. +""" +function Graphs.vertices(pg::AbstractGraph, supervertex::AbstractSuperVertex) + return partitioned_vertices(pg)[parent(supervertex)] +end +function Graphs.vertices(pg::AbstractPartitionedGraph, supervertices::Vector{<:AbstractSuperVertex}) + return unique(reduce(vcat, Iterators.map(sv -> vertices(pg, sv), supervertices))) +end + +Graphs.edges(pg::AbstractPartitionedGraph) = edges(unpartitioned_graph(pg)) + +""" + edges(pg::AbstractPartitionedGraph, superedge::AbstractSuperEdge) + edges(pg::AbstractPartitionedGraph, superedges::Vector{AbstractSuperEdge}) + +Return the set of edges in the partitioned graph `pg` that correspond to the super edge ` +superedge` or set of super edges `superedges`. +""" +function Graphs.edges(pg::AbstractPartitionedGraph, superedge::AbstractSuperEdge) + psrc_vs = vertices(pg, src(superedge)) + pdst_vs = vertices(pg, dst(superedge)) + psrc_subgraph, _ = induced_subgraph(unpartitioned_graph(pg), psrc_vs) + pdst_subgraph, _ = induced_subgraph(pg, pdst_vs) + full_subgraph, _ = induced_subgraph(pg, vcat(psrc_vs, pdst_vs)) + + return setdiff(edges(full_subgraph), vcat(edges(psrc_subgraph), edges(pdst_subgraph))) +end +function Graphs.edges(pg::AbstractPartitionedGraph, superedges::Vector{<:AbstractSuperEdge}) + return unique(reduce(vcat, [edges(pg, se) for se in superedges])) +end + function NamedGraphs.position_graph(pg::AbstractPartitionedGraph) return NamedGraphs.position_graph(unpartitioned_graph(pg)) end @@ -67,56 +185,29 @@ function NamedGraphs.ordered_vertices(pg::AbstractPartitionedGraph) return NamedGraphs.ordered_vertices(unpartitioned_graph(pg)) end Graphs.edgetype(pg::AbstractPartitionedGraph) = edgetype(unpartitioned_graph(pg)) -function Graphs.nv(pg::AbstractPartitionedGraph, sv::AbstractSuperVertex) - return length(vertices(pg, sv)) -end -function Graphs.has_vertex( - pg::AbstractPartitionedGraph, supervertex::AbstractSuperVertex - ) - return has_vertex(QuotientGraph(pg), parent(supervertex)) -end - -function Graphs.has_edge(pg::AbstractPartitionedGraph, superedge::AbstractSuperEdge) - return has_edge(QuotientGraph(pg), parent(superedge)) -end function is_boundary_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) p_edge = superedge(pg, edge) return src(p_edge) == dst(p_edge) end -function Graphs.add_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) - add_edge!(unpartitioned_graph(pg), edge) - pg_edge = parent(superedge(pg, edge)) - if src(pg_edge) != dst(pg_edge) - add_edge!(QuotientGraph(pg), pg_edge) - end - return pg -end - function Graphs.rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) pg_edge = superedge(pg, edge) - if has_edge(QuotientGraph(pg), pg_edge) + if has_edge(QuotientView(pg), pg_edge) g_edges = edges(pg, pg_edge) if length(g_edges) == 1 - rem_edge!(QuotientGraph(pg), pg_edge) + rem_edge!(QuotientView(pg), pg_edge) end end return rem_edge!(unpartitioned_graph(pg), edge) end -function Graphs.rem_edge!( - pg::AbstractPartitionedGraph, superedge::AbstractSuperEdge - ) - return rem_edges!(pg, edges(pg, parent(superedge))) -end - #Vertex addition and removal. I think it's important not to allow addition of a vertex without specification of PV function Graphs.add_vertex!( pg::AbstractPartitionedGraph, vertex, supervertex::AbstractSuperVertex ) add_vertex!(unpartitioned_graph(pg), vertex) - add_vertex!(QuotientGraph(pg), parent(supervertex)) + add_vertex!(QuotientView(pg), parent(supervertex)) insert_to_vertex_map!(pg, vertex, supervertex) return pg end @@ -141,30 +232,11 @@ function GraphsExtensions.add_vertices!( return pg end -function Graphs.rem_vertex!(pg::AbstractPartitionedGraph, vertex) - sv = supervertex(pg, vertex) - delete_from_vertex_map!(pg, sv, vertex) - rem_vertex!(unpartitioned_graph(pg), vertex) - if !haskey(partitioned_vertices(pg), parent(sv)) - rem_vertex!(QuotientGraph(pg), parent(sv)) - end - return pg -end - -function Graphs.rem_vertex!( - pg::AbstractPartitionedGraph, supervertex::AbstractSuperVertex - ) - rem_vertices!(pg, vertices(pg, supervertex)) - return pg -end - -function GraphsExtensions.rem_vertex( - pg::AbstractPartitionedGraph, supervertex::AbstractSuperVertex - ) - pg_new = copy(pg) - rem_vertex!(pg_new, supervertex) - return pg_new +function Graphs.rem_vertex!(pg::AbstractPartitionedGraph, vertex::AbstractSuperVertex) + return rem_super_vertex!(pg, vertex) end + +Graphs.rem_vertex!(::AbstractPartitionedGraph, vertex) = not_implemented() function Graphs.add_vertex!(pg::AbstractPartitionedGraph, vertex) return error("Need to specify a partition where the new vertex will go.") @@ -172,7 +244,7 @@ end function Base.:(==)(pg1::AbstractPartitionedGraph, pg2::AbstractPartitionedGraph) if unpartitioned_graph(pg1) != unpartitioned_graph(pg2) || - QuotientGraph(pg1) != QuotientGraph(pg2) + QuotientView(pg1) != QuotientView(pg2) return false end for v in vertices(pg1) diff --git a/src/lib/PartitionedGraphs/src/abstractsuperedge.jl b/src/lib/PartitionedGraphs/src/abstractsuperedge.jl index f4fe71d..8236f46 100644 --- a/src/lib/PartitionedGraphs/src/abstractsuperedge.jl +++ b/src/lib/PartitionedGraphs/src/abstractsuperedge.jl @@ -1,11 +1,24 @@ -using Graphs: Graphs +using Graphs: Graphs, AbstractGraph, ne, has_edge, rem_edge! +using ..NamedGraphs.GraphsExtensions: GraphsExtensions, not_implemented, rem_edges!, rem_edge using ..NamedGraphs: AbstractNamedEdge abstract type AbstractSuperEdge{V} <: AbstractNamedEdge{V} end -Base.parent(se::AbstractSuperEdge) = not_implemented() -Graphs.src(se::AbstractSuperEdge) = not_implemented() -Graphs.dst(se::AbstractSuperEdge) = not_implemented() -Base.reverse(se::AbstractSuperEdge) = not_implemented() +Base.parent(::AbstractSuperEdge) = not_implemented() +Graphs.src(::AbstractSuperEdge) = not_implemented() +Graphs.dst(::AbstractSuperEdge) = not_implemented() +Base.reverse(::AbstractSuperEdge) = not_implemented() -#Don't have the vertices wrapped. But wrap them with source and edge. +function Graphs.has_edge(g::AbstractGraph, se::AbstractSuperEdge) + return parent(se) in quotient_edges(g) +end + +Graphs.ne(g::AbstractGraph, se::AbstractSuperEdge) = length(quotient_edges(g, se)) + +function Graphs.rem_edge!(g::AbstractGraph, se::AbstractSuperEdge) + edges_to_remove = partitioned_edges(g, se) + rem_edges!(g, edges_to_remove) + return g +end + +GraphsExtensions.rem_edge(g, sv) = rem_edge!(copy(g), sv) diff --git a/src/lib/PartitionedGraphs/src/abstractsupervertex.jl b/src/lib/PartitionedGraphs/src/abstractsupervertex.jl index 62d3f75..5f19d0d 100644 --- a/src/lib/PartitionedGraphs/src/abstractsupervertex.jl +++ b/src/lib/PartitionedGraphs/src/abstractsupervertex.jl @@ -1,4 +1,28 @@ +using Graphs: Graphs, AbstractGraph, nv, has_vertex, rem_vertex! +using ..NamedGraphs: AbstractNamedGraph +using ..NamedGraphs.GraphsExtensions: GraphsExtensions, not_implemented, rem_vertices!, rem_vertex + abstract type AbstractSuperVertex{V} <: Any where {V} end #Parent, wrap, unwrap, vertex? -Base.parent(sv::AbstractSuperVertex) = not_implemented() +Base.parent(::AbstractSuperVertex) = not_implemented() + +# Avoid method ambiguity +Graphs.has_vertex(g::AbstractGraph, sv::AbstractSuperVertex) = has_super_vertex(g, sv) +Graphs.has_vertex(g::AbstractNamedGraph, sv::AbstractSuperVertex) = has_super_vertex(g, sv) + +function has_super_vertex(g::AbstractGraph, supervertex::AbstractSuperVertex) + return parent(supervertex) in quotient_vertices(g) +end + +Graphs.nv(g::AbstractGraph, sv::AbstractSuperVertex) = length(partitioned_vertices(g, sv)) + +function rem_super_vertex!(g::AbstractGraph, sv::AbstractSuperVertex) + vertices_to_remove = partitioned_vertices(g, sv) + rem_vertices!(g, vertices_to_remove) + return g +end + +Graphs.rem_vertex!(g::AbstractGraph, sv::AbstractSuperVertex) = rem_super_vertex!(g, sv) + +GraphsExtensions.rem_vertex(g, sv) = rem_vertex!(copy(g), sv) diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index a24349e..d2ed346 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -1,26 +1,34 @@ using Dictionaries: Dictionary using Graphs: - AbstractEdge, AbstractGraph, add_edge!, edges, has_edge, induced_subgraph, vertices -using .GraphsExtensions: - GraphsExtensions, boundary_edges, is_self_loop, partitioned_vertices + AbstractEdge, AbstractGraph, add_edge!, edges, has_edge, induced_subgraph, vertices, dst, src, edgetype +using .GraphsExtensions: GraphsExtensions, boundary_edges, is_self_loop, partitions using ..NamedGraphs: NamedEdge, NamedGraph # TODO: Parametrize `partitioned_vertices` and `which_partition`, # see https://github.com/mtfishman/NamedGraphs.jl/issues/63. -struct PartitionedGraph{V, PV, G <: AbstractGraph{V}, PG <: AbstractGraph{PV}} <: - AbstractPartitionedGraph{V, PV} +struct PartitionedGraph{V, PV, G <: AbstractGraph{V}, NV} <: AbstractPartitionedGraph{V, PV} graph::G - partitions_graph::PG - partitioned_vertices::Dictionary - which_partition::Dictionary + quotient_graph::NamedGraph{PV} + partitioned_vertices::Dictionary{PV, Vector{NV}} + which_partition::Dictionary{V, PV} end +partitioned_vertices(pg::PartitionedGraph) = pg.partitioned_vertices + +quotient_graph(pg::PartitionedGraph) = pg.quotient_graph +quotient_edges(pg::PartitionedGraph) = edges(pg.quotient_graph) +quotient_vertices(pg::PartitionedGraph) = vertices(pg.quotient_graph) + +super_vertex_type(::Type{<:PartitionedGraph{V}}) where {V} = SuperVertex{V} +super_edge_type(G::Type{<:PartitionedGraph{V}}) where {V} = SuperEdge{V, edgetype(G)} + +Graphs.edgetype(::Type{<:PartitionedGraph{V,PV,G}}) where {V,PV,G} = edgetype(G) + ##Constructors. -function PartitionedGraph(g::AbstractGraph, partitioned_vertices) +function PartitionedGraph(g::AbstractGraph{V}, partitioned_vertices) where {V} pvs = keys(partitioned_vertices) - pg = NamedGraph(pvs) - # TODO: Make this type more specific. - which_partition = Dictionary() + qg = NamedGraph(pvs) + which_partition = Dictionary{V, eltype(pvs)}() for v in vertices(g) v_pvs = Set(findall(pv -> v ∈ partitioned_vertices[pv], pvs)) @assert length(v_pvs) == 1 @@ -29,11 +37,16 @@ function PartitionedGraph(g::AbstractGraph, partitioned_vertices) for e in edges(g) 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) + if pv_src != pv_dst && !has_edge(qg, pe) + add_edge!(qg, pe) end end - return PartitionedGraph(g, pg, Dictionary(partitioned_vertices), which_partition) + return PartitionedGraph( + g, + qg, + map(v -> [v;], Dictionary(partitioned_vertices)), + which_partition + ) end function PartitionedGraph(partitioned_vertices) @@ -41,7 +54,7 @@ function PartitionedGraph(partitioned_vertices) end function PartitionedGraph(g::AbstractGraph; kwargs...) - partitioned_verts = partitioned_vertices(g; kwargs...) + partitioned_verts = partitions(g; kwargs...) return PartitionedGraph(g, partitioned_verts) end @@ -50,27 +63,13 @@ unpartitioned_graph(pg::PartitionedGraph) = getfield(pg, :graph) function unpartitioned_graph_type(graph_type::Type{<:PartitionedGraph}) return fieldtype(graph_type, :graph) end -function GraphsExtensions.partitioned_vertices(pg::PartitionedGraph) - return getfield(pg, :partitioned_vertices) -end -which_partition(pg::PartitionedGraph) = getfield(pg, :which_partition) -function Graphs.vertices(pg::PartitionedGraph, supervertex::SuperVertex) - return partitioned_vertices(pg)[parent(supervertex)] -end -function Graphs.vertices(pg::PartitionedGraph, supervertices::Vector{<:SuperVertex}) - return unique(reduce(vcat, Iterators.map(sv -> vertices(pg, sv), supervertices))) -end -function supervertex(pg::PartitionedGraph, vertex) - return SuperVertex(which_partition(pg)[vertex]) -end +findpartition(pg::PartitionedGraph, vertex) = pg.which_partition[vertex] -function supervertices(pg::PartitionedGraph, verts) - return unique(supervertex(pg, v) for v in verts) +function supervertex(pg::PartitionedGraph, vertex) + return SuperVertex(findpartition(pg, vertex)) end -function supervertices(pg::PartitionedGraph) - return SuperVertex.(vertices(pg.partitions_graph)) -end +supervertices(pg::PartitionedGraph) = SuperVertex.(vertices(quotient_graph(pg))) function superedge(pg::PartitionedGraph, edge::AbstractEdge) return SuperEdge( @@ -78,33 +77,11 @@ function superedge(pg::PartitionedGraph, edge::AbstractEdge) ) end -superedge(pg::PartitionedGraph, p::Pair) = superedge(pg, edgetype(pg)(p)) - -function superedges(pg::PartitionedGraph, edges::Vector) - return filter(!is_self_loop, unique([superedge(pg, e) for e in edges])) -end - -function superedges(pg::PartitionedGraph) - return SuperEdge.(edges(pg.partitions_graph)) -end - -function Graphs.edges(pg::PartitionedGraph, superedge::SuperEdge) - psrc_vs = vertices(pg, src(superedge)) - pdst_vs = vertices(pg, dst(superedge)) - psrc_subgraph, _ = induced_subgraph(unpartitioned_graph(pg), psrc_vs) - pdst_subgraph, _ = induced_subgraph(pg, pdst_vs) - full_subgraph, _ = induced_subgraph(pg, vcat(psrc_vs, pdst_vs)) - - return setdiff(edges(full_subgraph), vcat(edges(psrc_subgraph), edges(pdst_subgraph))) -end - -function Graphs.edges(pg::PartitionedGraph, superedges::Vector{<:SuperEdge}) - return unique(reduce(vcat, [edges(pg, se) for se in superedges])) -end +superedges(pg::PartitionedGraph) = map(SuperEdge, edges(QuotientView(pg))) function boundary_superedges(pg::PartitionedGraph, supervertices; kwargs...) return SuperEdge.( - boundary_edges(pg.partitions_graph, parent.(supervertices); kwargs...) + boundary_edges(quotient_graph(pg), parent.(supervertices); kwargs...) ) end @@ -117,9 +94,9 @@ end function Base.copy(pg::PartitionedGraph) return PartitionedGraph( copy(unpartitioned_graph(pg)), - copy(pg.partitions_graph), + copy(quotient_graph(pg)), copy(partitioned_vertices(pg)), - copy(which_partition(pg)), + copy(pg.which_partition), ) end @@ -127,39 +104,71 @@ function insert_to_vertex_map!( pg::PartitionedGraph, vertex, supervertex::SuperVertex ) pv = parent(supervertex) - if pv ∉ keys(partitioned_vertices(pg)) - insert!(partitioned_vertices(pg), pv, [vertex]) - else - partitioned_vertices(pg)[pv] = unique(vcat(vertices(pg, supervertex), [vertex])) - end - insert!(which_partition(pg), vertex, pv) + push!(get!(pg.partitioned_vertices, pv, []), vertex) + unique!(pg.partitioned_vertices[pv]) + + insert!(pg.which_partition, vertex, pv) + return pg end -function delete_from_vertex_map!(pg::PartitionedGraph, vertex) +function delete_from_vertex_map!(pg::PartitionedGraph{V}, vertex::V) where {V} sv = supervertex(pg, vertex) return delete_from_vertex_map!(pg, sv, vertex) end function delete_from_vertex_map!( - pg::PartitionedGraph, partitioned_vertex::SuperVertex, vertex - ) + pg::PartitionedGraph{V}, partitioned_vertex::SuperVertex, vertex::V +) where {V} vs = vertices(pg, partitioned_vertex) - delete!(partitioned_vertices(pg), parent(partitioned_vertex)) + + delete!(pg.partitioned_vertices, parent(partitioned_vertex)) + if length(vs) != 1 - insert!(partitioned_vertices(pg), parent(partitioned_vertex), setdiff(vs, [vertex])) + insert!(pg.partitioned_vertices, parent(partitioned_vertex), setdiff(vs, [vertex])) end - delete!(which_partition(pg), vertex) + delete!(pg.which_partition, vertex) return partitioned_vertex end +function Graphs.rem_vertex!(pg::PartitionedGraph{V}, vertex::V) where {V} + sv = supervertex(pg, vertex) + + delete_from_vertex_map!(pg, sv, vertex) + + rem_vertex!(unpartitioned_graph(pg), vertex) + + if !haskey(partitioned_vertices(pg), parent(sv)) + rem_vertex!(pg.quotient_graph, parent(sv)) + end + + return pg +end + +function Graphs.add_vertex!(pg::PartitionedGraph{V}, vertex::V, sv::SuperVertex) where {V} + add_vertex!(pg.graph, vertex) + add_vertex!(pg.quotient_graph, parent(sv)) + insert_to_vertex_map!(pg, vertex, sv) + return pg +end + +function Graphs.add_edge!(pg::PartitionedGraph, edge) + @assert edge isa edgetype(pg) + add_edge!(pg.graph, edge) + pg_edge = parent(superedge(pg, edge)) + if src(pg_edge) != dst(pg_edge) + add_edge!(pg.quotient_graph, pg_edge) + end + return pg +end + ### PartitionedGraph Specific Functions function partitionedgraph_induced_subgraph(pg::PartitionedGraph, vertices::Vector) sub_pg_graph, _ = induced_subgraph(unpartitioned_graph(pg), vertices) sub_partitioned_vertices = copy(partitioned_vertices(pg)) - for pv in NamedGraphs.vertices(pg.partitions_graph) + for pv in NamedGraphs.vertices(QuotientView(pg)) vs = intersect(vertices, sub_partitioned_vertices[pv]) if !isempty(vs) sub_partitioned_vertices[pv] = vs diff --git a/src/lib/PartitionedGraphs/src/quotientgraph.jl b/src/lib/PartitionedGraphs/src/quotientgraph.jl index e8a0198..e9d4bb8 100644 --- a/src/lib/PartitionedGraphs/src/quotientgraph.jl +++ b/src/lib/PartitionedGraphs/src/quotientgraph.jl @@ -1,25 +1,51 @@ -using Graphs: AbstractGraph +using Graphs: AbstractGraph, rem_vertex!, vertices, edges +using .GraphsExtensions: add_edges! +using ..NamedGraphs: NamedGraph, position_graph_type -struct QuotientGraph{V, G <: AbstractPartitionedGraph{V}} <: AbstractNamedGraph{V} +struct QuotientView{V, G <: AbstractGraph{V}} <: AbstractNamedGraph{V} graph::G - QuotientGraph(g::G) where {V, G <: AbstractPartitionedGraph{V}} = new{V, G}(g) end -Base.copy(g::QuotientGraph) = QuotientGraph(copy(g.graph)) -QuotientGraph(g::AbstractGraph) = QuotientGraph(PartitionedGraph(g, [vertices(g)])) +function quotient_graph(g::QuotientView) + qg = NamedGraph(vertices(g)) + add_edges!(qg, edges(g)) + return qg +end + +quotient_graph_type(::Type{<:QuotientView{V}}) where {V} = NamedGraph{V} + +Graphs.vertices(qg::QuotientView) = quotient_vertices(qg.graph) +Graphs.edges(qg::QuotientView) = quotient_edges(qg.graph) + +Base.copy(g::QuotientView) = QuotientView(copy(g.graph)) # Graphs.jl and NamedGraphs.jl interface overloads for `PartitionsGraphView` wrapping # a `PartitionedGraph`. function NamedGraphs.position_graph_type( - type::Type{<:QuotientGraph{V, G}} + type::Type{<:QuotientView{V, G}} ) where {V, G <: PartitionedGraph{V}} - return fieldtype(fieldtype(fieldtype(type, :graph), :partitions_graph), :position_graph) + return position_graph_type(quotient_graph_type(type)) +end + +function Graphs.rem_vertex!(qg::QuotientView, v) + g = qg.graph + rem_vertex!(g, super_vertex_type(g)(v)) end +function Graphs.rem_edge!(qg::QuotientView, v) + g = qg.graph + rem_edge!(g, super_edge_type(g)(v)) +end + +function Graphs.add_vertex!(qg::QuotientView, v) + g = qg.graph + add_vertex!(g, super_vertex_type(g)(v)) +end +function Graphs.add_edge!(qg::QuotientView, v) + g = qg.graph + add_edge!(g, super_edge_type(g)(v)) +end + for f in [ - :(Graphs.add_vertex!), - :(Graphs.edges), - :(Graphs.vertices), - :(Graphs.rem_vertex!), :(NamedGraphs.edgetype), :(NamedGraphs.namedgraph_induced_subgraph), :(NamedGraphs.ordered_vertices), @@ -29,9 +55,10 @@ for f in [ ] @eval begin function $f( - g::QuotientGraph{V, G}, args...; kwargs... - ) where {V, G <: PartitionedGraph{V}} - return $f(g.graph.partitions_graph, args...; kwargs...) + g::QuotientView{V, G}, args...; kwargs... + ) where {V, G} + return $f(quotient_graph(g), args...; kwargs...) end end end + diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index 9124c98..72a96b8 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -36,7 +36,7 @@ using NamedGraphs.NamedGraphGenerators: using NamedGraphs.OrderedDictionaries: OrderedDictionary using NamedGraphs.PartitionedGraphs: PartitionedGraph, - QuotientGraph, + QuotientView, SuperEdge, SuperVertex, boundary_superedges, @@ -56,18 +56,18 @@ using Test: @test, @testset #Partition it column-wise (into a 1D chain) partitions = [[(i, j) for j in 1:ny] for i in 1:nx] pg = PartitionedGraph(g, partitions) - @test vertextype(QuotientGraph(pg)) == Int64 + @test vertextype(QuotientView(pg)) == Int64 @test vertextype(unpartitioned_graph(pg)) == vertextype(g) @test isa(supervertices(pg), OrderedDictionary{Int64, SuperVertex{Int64}}) @test isa(superedges(pg), Vector{SuperEdge{Int64, NamedEdge{Int64}}}) - @test is_tree(QuotientGraph(pg)) + @test is_tree(QuotientView(pg)) @test nv(pg) == nx * ny - @test nv(QuotientGraph(pg)) == nx + @test nv(QuotientView(pg)) == nx pg_c = copy(pg) @test pg_c == pg #PartionsGraphView test - pgv = QuotientGraph(pg) + pgv = QuotientView(pg) @test vertices(pgv) == parent.(supervertices(pg)) @test edges(pgv) == parent.(superedges(pg)) @test is_tree(pgv) == true @@ -78,7 +78,7 @@ using Test: @test, @testset #Same partitioning but with a dictionary constructor partition_dict = Dictionary([first(partition) for partition in partitions], partitions) pg = PartitionedGraph(g, partition_dict) - @test vertextype(QuotientGraph(pg)) == vertextype(g) + @test vertextype(QuotientView(pg)) == vertextype(g) @test vertextype(unpartitioned_graph(pg)) == vertextype(g) @test isa( supervertices(pg), @@ -88,19 +88,19 @@ using Test: @test, @testset superedges(pg), Vector{SuperEdge{Tuple{Int64, Int64}, NamedEdge{Tuple{Int64, Int64}}}}, ) - @test is_tree(QuotientGraph(pg)) + @test is_tree(QuotientView(pg)) @test nv(pg) == nx * ny - @test nv(QuotientGraph(pg)) == nx + @test nv(QuotientView(pg)) == nx pg_c = copy(pg) @test pg_c == pg #Partition the whole thing into just 1 vertex pg = PartitionedGraph([i for i in 1:nx]) - @test unpartitioned_graph(pg) == QuotientGraph(pg) + @test unpartitioned_graph(pg) == QuotientView(pg) @test nv(pg) == nx - @test nv(QuotientGraph(pg)) == nx + @test nv(QuotientView(pg)) == nx @test ne(pg) == 0 - @test ne(QuotientGraph(pg)) == 0 + @test ne(QuotientView(pg)) == 0 pg_c = copy(pg) @test pg_c == pg end @@ -146,22 +146,23 @@ end #Strip the middle column from pg via the partitioned graph vertex, and make a new pg rem_vertex!(pg, pv) - @test !is_connected(unpartitioned_graph(pg)) && !is_connected(QuotientGraph(pg)) - @test parent(pv) ∉ vertices(QuotientGraph(pg)) + @test !is_connected(unpartitioned_graph(pg)) && !is_connected(QuotientView(pg)) + @test parent(pv) ∉ vertices(QuotientView(pg)) @test !has_vertex(pg, pv) @test nv(pg) == (nx - 1) * ny - @test nv(QuotientGraph(pg)) == nx - 1 - @test !is_tree(QuotientGraph(pg)) + @test nv(QuotientView(pg)) == nx - 1 + @test !is_tree(QuotientView(pg)) #Add the column back to the in place graph add_vertices!(pg, v_set, pv) add_edges!(pg, edges_involving_v_set) - @test is_connected(pg.graph) && is_path_graph(QuotientGraph(pg)) - @test parent(pv) ∈ vertices(QuotientGraph(pg)) + @test is_connected(pg.graph) + @test is_path_graph(QuotientView(pg)) + @test parent(pv) ∈ vertices(QuotientView(pg)) @test has_vertex(pg, pv) - @test is_tree(QuotientGraph(pg)) + @test is_tree(QuotientView(pg)) @test nv(pg) == nx * ny - @test nv(QuotientGraph(pg)) == nx + @test nv(QuotientView(pg)) == nx end @testset "Test Partitioned Graph Subgraph Functionality" begin @@ -183,7 +184,7 @@ end pg_2 = subgraph(pg, subgraph_vertices) @test pg_1 == pg_2 @test nv(pg_1) == length(subgraph_vertices) - @test nv(QuotientGraph(pg_1)) == length(subgraph_partitioned_vertices) + @test nv(QuotientView(pg_1)) == length(subgraph_partitioned_vertices) subgraph_partitioned_vertex = 3 subgraph_vertices = partitions[subgraph_partitioned_vertex] @@ -206,9 +207,9 @@ end pg = PartitionedGraph(g, [vertices(g)]) @test f(pg) == f(unpartitioned_graph(pg)) @test nv(pg) == nv(g) - @test nv(QuotientGraph(pg)) == 1 + @test nv(QuotientView(pg)) == 1 @test ne(pg) == ne(g) - @test ne(QuotientGraph(pg)) == 0 + @test ne(QuotientView(pg)) == 0 end end end @@ -225,7 +226,7 @@ end for backend in backends pg = PartitionedGraph(g; npartitions, backend = "metis") @test pg isa PartitionedGraph - @test nv(QuotientGraph(pg)) == npartitions + @test nv(QuotientView(pg)) == npartitions end end end From a9d77a57246bf577f3bae7e2fc3d5441c2adf538 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Wed, 5 Nov 2025 16:55:22 -0500 Subject: [PATCH 08/36] Remove `AbstractSuperVertex` --- .../src/PartitionedGraphs.jl | 3 +- .../src/abstractpartitionedgraph.jl | 20 ++++++------- .../src/abstractsupervertex.jl | 28 ------------------- src/lib/PartitionedGraphs/src/supervertex.jl | 24 +++++++++++++++- 4 files changed, 34 insertions(+), 41 deletions(-) delete mode 100644 src/lib/PartitionedGraphs/src/abstractsupervertex.jl diff --git a/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl b/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl index 7390ee9..c0ad008 100644 --- a/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl +++ b/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl @@ -1,8 +1,7 @@ module PartitionedGraphs -include("abstractsupervertex.jl") include("abstractsuperedge.jl") -include("abstractpartitionedgraph.jl") include("supervertex.jl") +include("abstractpartitionedgraph.jl") include("superedge.jl") include("partitionedgraph.jl") include("quotientgraph.jl") diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index 5a5828e..ead780f 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -18,8 +18,8 @@ using ..NamedGraphs.GraphsExtensions: partitioned_vertices(g::AbstractGraph) = [vertices(g)] # Don't need to overload this -partitioned_vertices(g::AbstractGraph, sv::AbstractSuperVertex) = partitioned_vertices(g)[parent(sv)] -function partitioned_vertices(g::AbstractGraph, svs::Vector{<:AbstractSuperVertex}) +partitioned_vertices(g::AbstractGraph, sv::SuperVertex) = partitioned_vertices(g)[parent(sv)] +function partitioned_vertices(g::AbstractGraph, svs::Vector{<:SuperVertex}) return mapreduce(sv -> partitioned_vertices(g, sv), vcat, svs) end @@ -146,10 +146,10 @@ Graphs.vertices(pg::AbstractPartitionedGraph) = vertices(unpartitioned_graph(pg) Return the set of vertices in the partitioned graph `pg` that correspond to the super vertex `supervertex` or set of super vertices `supervertex`. """ -function Graphs.vertices(pg::AbstractGraph, supervertex::AbstractSuperVertex) +function Graphs.vertices(pg::AbstractGraph, supervertex::SuperVertex) return partitioned_vertices(pg)[parent(supervertex)] end -function Graphs.vertices(pg::AbstractPartitionedGraph, supervertices::Vector{<:AbstractSuperVertex}) +function Graphs.vertices(pg::AbstractPartitionedGraph, supervertices::Vector{<:SuperVertex}) return unique(reduce(vcat, Iterators.map(sv -> vertices(pg, sv), supervertices))) end @@ -204,7 +204,7 @@ end #Vertex addition and removal. I think it's important not to allow addition of a vertex without specification of PV function Graphs.add_vertex!( - pg::AbstractPartitionedGraph, vertex, supervertex::AbstractSuperVertex + pg::AbstractPartitionedGraph, vertex, supervertex::SuperVertex ) add_vertex!(unpartitioned_graph(pg), vertex) add_vertex!(QuotientView(pg), parent(supervertex)) @@ -215,7 +215,7 @@ end function GraphsExtensions.add_vertices!( pg::AbstractPartitionedGraph, vertices::Vector, - supervertices::Vector{<:AbstractSuperVertex}, + supervertices::Vector{<:SuperVertex}, ) @assert length(vertices) == length(supervertices) for (v, sv) in zip(vertices, supervertices) @@ -226,13 +226,13 @@ function GraphsExtensions.add_vertices!( end function GraphsExtensions.add_vertices!( - pg::AbstractPartitionedGraph, vertices::Vector, supervertex::AbstractSuperVertex + pg::AbstractPartitionedGraph, vertices::Vector, supervertex::SuperVertex ) add_vertices!(pg, vertices, fill(supervertex, length(vertices))) return pg end -function Graphs.rem_vertex!(pg::AbstractPartitionedGraph, vertex::AbstractSuperVertex) +function Graphs.rem_vertex!(pg::AbstractPartitionedGraph, vertex::SuperVertex) return rem_super_vertex!(pg, vertex) end @@ -256,13 +256,13 @@ function Base.:(==)(pg1::AbstractPartitionedGraph, pg2::AbstractPartitionedGraph end function GraphsExtensions.subgraph( - pg::AbstractPartitionedGraph, supervertex::AbstractSuperVertex + pg::AbstractPartitionedGraph, supervertex::SuperVertex ) return first(induced_subgraph(unpartitioned_graph(pg), vertices(pg, [supervertex]))) end function Graphs.induced_subgraph( - pg::AbstractPartitionedGraph, supervertex::AbstractSuperVertex + pg::AbstractPartitionedGraph, supervertex::SuperVertex ) return subgraph(pg, supervertex), nothing end diff --git a/src/lib/PartitionedGraphs/src/abstractsupervertex.jl b/src/lib/PartitionedGraphs/src/abstractsupervertex.jl deleted file mode 100644 index 5f19d0d..0000000 --- a/src/lib/PartitionedGraphs/src/abstractsupervertex.jl +++ /dev/null @@ -1,28 +0,0 @@ -using Graphs: Graphs, AbstractGraph, nv, has_vertex, rem_vertex! -using ..NamedGraphs: AbstractNamedGraph -using ..NamedGraphs.GraphsExtensions: GraphsExtensions, not_implemented, rem_vertices!, rem_vertex - -abstract type AbstractSuperVertex{V} <: Any where {V} end - -#Parent, wrap, unwrap, vertex? -Base.parent(::AbstractSuperVertex) = not_implemented() - -# Avoid method ambiguity -Graphs.has_vertex(g::AbstractGraph, sv::AbstractSuperVertex) = has_super_vertex(g, sv) -Graphs.has_vertex(g::AbstractNamedGraph, sv::AbstractSuperVertex) = has_super_vertex(g, sv) - -function has_super_vertex(g::AbstractGraph, supervertex::AbstractSuperVertex) - return parent(supervertex) in quotient_vertices(g) -end - -Graphs.nv(g::AbstractGraph, sv::AbstractSuperVertex) = length(partitioned_vertices(g, sv)) - -function rem_super_vertex!(g::AbstractGraph, sv::AbstractSuperVertex) - vertices_to_remove = partitioned_vertices(g, sv) - rem_vertices!(g, vertices_to_remove) - return g -end - -Graphs.rem_vertex!(g::AbstractGraph, sv::AbstractSuperVertex) = rem_super_vertex!(g, sv) - -GraphsExtensions.rem_vertex(g, sv) = rem_vertex!(copy(g), sv) diff --git a/src/lib/PartitionedGraphs/src/supervertex.jl b/src/lib/PartitionedGraphs/src/supervertex.jl index 16d5e29..ef453a8 100644 --- a/src/lib/PartitionedGraphs/src/supervertex.jl +++ b/src/lib/PartitionedGraphs/src/supervertex.jl @@ -1,5 +1,27 @@ -struct SuperVertex{V} <: AbstractSuperVertex{V} +using ..NamedGraphs: AbstractNamedGraph + +struct SuperVertex{V} vertex::V end Base.parent(sv::SuperVertex) = getfield(sv, :vertex) + +# Avoid method ambiguity +Graphs.has_vertex(g::AbstractGraph, sv::SuperVertex) = has_super_vertex(g, sv) +Graphs.has_vertex(g::AbstractNamedGraph, sv::SuperVertex) = has_super_vertex(g, sv) + +function has_super_vertex(g::AbstractGraph, supervertex::SuperVertex) + return parent(supervertex) in quotient_vertices(g) +end + +Graphs.nv(g::AbstractGraph, sv::SuperVertex) = length(partitioned_vertices(g, sv)) + +function rem_super_vertex!(g::AbstractGraph, sv::SuperVertex) + vertices_to_remove = partitioned_vertices(g, sv) + rem_vertices!(g, vertices_to_remove) + return g +end + +Graphs.rem_vertex!(g::AbstractGraph, sv::SuperVertex) = rem_super_vertex!(g, sv) + +GraphsExtensions.rem_vertex(g, sv) = rem_vertex!(copy(g), sv) From 68ecf0d5d35192a296944115f9390cc2aad0c740 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Wed, 5 Nov 2025 17:06:46 -0500 Subject: [PATCH 09/36] Remove `AbstractSuperEdge` --- .../src/PartitionedGraphs.jl | 3 +-- .../src/abstractpartitionedgraph.jl | 16 ++++++------- .../src/abstractsuperedge.jl | 24 ------------------- src/lib/PartitionedGraphs/src/superedge.jl | 20 ++++++++++++++-- src/lib/PartitionedGraphs/src/supervertex.jl | 2 ++ 5 files changed, 29 insertions(+), 36 deletions(-) delete mode 100644 src/lib/PartitionedGraphs/src/abstractsuperedge.jl diff --git a/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl b/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl index c0ad008..331130c 100644 --- a/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl +++ b/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl @@ -1,8 +1,7 @@ module PartitionedGraphs -include("abstractsuperedge.jl") include("supervertex.jl") -include("abstractpartitionedgraph.jl") include("superedge.jl") +include("abstractpartitionedgraph.jl") include("partitionedgraph.jl") include("quotientgraph.jl") end diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index ead780f..d3d0ff9 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -52,8 +52,8 @@ function partitioned_edges(g::AbstractGraph, pvs = partitioned_vertices(g)) end return rv end -partitioned_edges(g::AbstractGraph, se::AbstractSuperEdge) = partitioned_edges(g)[parent(se)] -function partitioned_edges(g::AbstractGraph, ses::Vector{<:AbstractSuperEdge}) +partitioned_edges(g::AbstractGraph, se::SuperEdge) = partitioned_edges(g)[parent(se)] +function partitioned_edges(g::AbstractGraph, ses::Vector{<:SuperEdge}) return mapreduce(se -> partitioned_edges(g, se), vcat, ses) end @@ -140,8 +140,8 @@ end Graphs.vertices(pg::AbstractPartitionedGraph) = vertices(unpartitioned_graph(pg)) """ - vertices(pg::AbstractPartitionedGraph, supervertex::AbstractSuperEdge) - vertices(pg::AbstractPartitionedGraph, supervertices::Vector{AbstractSuperEdge}) + vertices(pg::AbstractPartitionedGraph, supervertex::SuperEdge) + vertices(pg::AbstractPartitionedGraph, supervertices::Vector{SuperEdge}) Return the set of vertices in the partitioned graph `pg` that correspond to the super vertex `supervertex` or set of super vertices `supervertex`. @@ -156,13 +156,13 @@ end Graphs.edges(pg::AbstractPartitionedGraph) = edges(unpartitioned_graph(pg)) """ - edges(pg::AbstractPartitionedGraph, superedge::AbstractSuperEdge) - edges(pg::AbstractPartitionedGraph, superedges::Vector{AbstractSuperEdge}) + edges(pg::AbstractPartitionedGraph, superedge::SuperEdge) + edges(pg::AbstractPartitionedGraph, superedges::Vector{SuperEdge}) Return the set of edges in the partitioned graph `pg` that correspond to the super edge ` superedge` or set of super edges `superedges`. """ -function Graphs.edges(pg::AbstractPartitionedGraph, superedge::AbstractSuperEdge) +function Graphs.edges(pg::AbstractPartitionedGraph, superedge::SuperEdge) psrc_vs = vertices(pg, src(superedge)) pdst_vs = vertices(pg, dst(superedge)) psrc_subgraph, _ = induced_subgraph(unpartitioned_graph(pg), psrc_vs) @@ -171,7 +171,7 @@ function Graphs.edges(pg::AbstractPartitionedGraph, superedge::AbstractSuperEdge return setdiff(edges(full_subgraph), vcat(edges(psrc_subgraph), edges(pdst_subgraph))) end -function Graphs.edges(pg::AbstractPartitionedGraph, superedges::Vector{<:AbstractSuperEdge}) +function Graphs.edges(pg::AbstractPartitionedGraph, superedges::Vector{<:SuperEdge}) return unique(reduce(vcat, [edges(pg, se) for se in superedges])) end diff --git a/src/lib/PartitionedGraphs/src/abstractsuperedge.jl b/src/lib/PartitionedGraphs/src/abstractsuperedge.jl deleted file mode 100644 index 8236f46..0000000 --- a/src/lib/PartitionedGraphs/src/abstractsuperedge.jl +++ /dev/null @@ -1,24 +0,0 @@ -using Graphs: Graphs, AbstractGraph, ne, has_edge, rem_edge! -using ..NamedGraphs.GraphsExtensions: GraphsExtensions, not_implemented, rem_edges!, rem_edge -using ..NamedGraphs: AbstractNamedEdge - -abstract type AbstractSuperEdge{V} <: AbstractNamedEdge{V} end - -Base.parent(::AbstractSuperEdge) = not_implemented() -Graphs.src(::AbstractSuperEdge) = not_implemented() -Graphs.dst(::AbstractSuperEdge) = not_implemented() -Base.reverse(::AbstractSuperEdge) = not_implemented() - -function Graphs.has_edge(g::AbstractGraph, se::AbstractSuperEdge) - return parent(se) in quotient_edges(g) -end - -Graphs.ne(g::AbstractGraph, se::AbstractSuperEdge) = length(quotient_edges(g, se)) - -function Graphs.rem_edge!(g::AbstractGraph, se::AbstractSuperEdge) - edges_to_remove = partitioned_edges(g, se) - rem_edges!(g, edges_to_remove) - return g -end - -GraphsExtensions.rem_edge(g, sv) = rem_edge!(copy(g), sv) diff --git a/src/lib/PartitionedGraphs/src/superedge.jl b/src/lib/PartitionedGraphs/src/superedge.jl index 4ce55df..daf9cab 100644 --- a/src/lib/PartitionedGraphs/src/superedge.jl +++ b/src/lib/PartitionedGraphs/src/superedge.jl @@ -1,6 +1,8 @@ -using Graphs: Graphs, AbstractEdge, dst, src +using Graphs: AbstractGraph, Graphs, AbstractEdge, dst, src, ne +using ..NamedGraphs: AbstractNamedEdge +using ..NamedGraphs.GraphsExtensions: GraphsExtensions, not_implemented, rem_edges!, rem_edge -struct SuperEdge{V, E <: AbstractEdge{V}} <: AbstractSuperEdge{V} +struct SuperEdge{V, E <: AbstractEdge{V}} <: AbstractNamedEdge{V} edge::E end @@ -10,3 +12,17 @@ Graphs.dst(se::SuperEdge) = SuperVertex(dst(parent(se))) SuperEdge(p::Pair) = SuperEdge(NamedEdge(first(p) => last(p))) SuperEdge(vsrc, vdst) = SuperEdge(vsrc => vdst) Base.reverse(se::SuperEdge) = SuperEdge(reverse(parent(se))) + +function Graphs.has_edge(g::AbstractGraph, se::SuperEdge) + return parent(se) in quotient_edges(g) +end + +Graphs.ne(g::AbstractGraph, se::SuperEdge) = length(quotient_edges(g, se)) + +function Graphs.rem_edge!(g::AbstractGraph, se::SuperEdge) + edges_to_remove = partitioned_edges(g, se) + rem_edges!(g, edges_to_remove) + return g +end + +GraphsExtensions.rem_edge(g, sv) = rem_edge!(copy(g), sv) diff --git a/src/lib/PartitionedGraphs/src/supervertex.jl b/src/lib/PartitionedGraphs/src/supervertex.jl index ef453a8..14da279 100644 --- a/src/lib/PartitionedGraphs/src/supervertex.jl +++ b/src/lib/PartitionedGraphs/src/supervertex.jl @@ -1,4 +1,6 @@ +using Graphs: AbstractGraph, Graphs, nv using ..NamedGraphs: AbstractNamedGraph +using ..NamedGraphs.GraphsExtensions: GraphsExtensions, rem_vertices! struct SuperVertex{V} vertex::V From 242a24b0716213ad1f08a95a44041d00e6d8e411 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Wed, 5 Nov 2025 17:11:25 -0500 Subject: [PATCH 10/36] Simplify and remove methods related to adding/removing indices --- .../PartitionedGraphs/src/quotientgraph.jl | 20 ++++--------------- src/lib/PartitionedGraphs/src/superedge.jl | 2 -- src/lib/PartitionedGraphs/src/supervertex.jl | 2 -- 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/quotientgraph.jl b/src/lib/PartitionedGraphs/src/quotientgraph.jl index e9d4bb8..eb38d7b 100644 --- a/src/lib/PartitionedGraphs/src/quotientgraph.jl +++ b/src/lib/PartitionedGraphs/src/quotientgraph.jl @@ -27,23 +27,11 @@ function NamedGraphs.position_graph_type( return position_graph_type(quotient_graph_type(type)) end -function Graphs.rem_vertex!(qg::QuotientView, v) - g = qg.graph - rem_vertex!(g, super_vertex_type(g)(v)) -end -function Graphs.rem_edge!(qg::QuotientView, v) - g = qg.graph - rem_edge!(g, super_edge_type(g)(v)) -end +Graphs.rem_vertex!(qg::QuotientView, v) = rem_vertex!(qg.graph, SuperVertex(v)) +Graphs.rem_edge!(qg::QuotientView, v) = rem_edge!(qg.graph, SuperEdge(v)) -function Graphs.add_vertex!(qg::QuotientView, v) - g = qg.graph - add_vertex!(g, super_vertex_type(g)(v)) -end -function Graphs.add_edge!(qg::QuotientView, v) - g = qg.graph - add_edge!(g, super_edge_type(g)(v)) -end +Graphs.add_vertex!(qg::QuotientView, v) = add_vertex!(qg.graph, SuperVertex(v)) +Graphs.add_edge!(qg::QuotientView, v) = add_edge!(qg.graph, SuperEdge(v)) for f in [ :(NamedGraphs.edgetype), diff --git a/src/lib/PartitionedGraphs/src/superedge.jl b/src/lib/PartitionedGraphs/src/superedge.jl index daf9cab..174583c 100644 --- a/src/lib/PartitionedGraphs/src/superedge.jl +++ b/src/lib/PartitionedGraphs/src/superedge.jl @@ -24,5 +24,3 @@ function Graphs.rem_edge!(g::AbstractGraph, se::SuperEdge) rem_edges!(g, edges_to_remove) return g end - -GraphsExtensions.rem_edge(g, sv) = rem_edge!(copy(g), sv) diff --git a/src/lib/PartitionedGraphs/src/supervertex.jl b/src/lib/PartitionedGraphs/src/supervertex.jl index 14da279..d8cf383 100644 --- a/src/lib/PartitionedGraphs/src/supervertex.jl +++ b/src/lib/PartitionedGraphs/src/supervertex.jl @@ -25,5 +25,3 @@ function rem_super_vertex!(g::AbstractGraph, sv::SuperVertex) end Graphs.rem_vertex!(g::AbstractGraph, sv::SuperVertex) = rem_super_vertex!(g, sv) - -GraphsExtensions.rem_vertex(g, sv) = rem_vertex!(copy(g), sv) From 004de0b2280dc106bb4b3508f8f3f9a014ffbddb Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Thu, 6 Nov 2025 09:23:00 -0500 Subject: [PATCH 11/36] Move some functionality to `AbstractGraph` --- .../src/abstractpartitionedgraph.jl | 30 ++++++++++--- .../PartitionedGraphs/src/partitionedgraph.jl | 43 ++----------------- 2 files changed, 29 insertions(+), 44 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index d3d0ff9..092d5f4 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -17,11 +17,6 @@ using ..NamedGraphs.GraphsExtensions: # Essential method for the interface partitioned_vertices(g::AbstractGraph) = [vertices(g)] -# Don't need to overload this -partitioned_vertices(g::AbstractGraph, sv::SuperVertex) = partitioned_vertices(g)[parent(sv)] -function partitioned_vertices(g::AbstractGraph, svs::Vector{<:SuperVertex}) - return mapreduce(sv -> partitioned_vertices(g, sv), vcat, svs) -end # Optional functions for the interface @@ -72,6 +67,31 @@ function quotient_graph(g::AbstractGraph, pvs) return qg end +supervertex(pg::AbstractGraph, vertex) = SuperVertex(findpartition(pg, vertex)) +supervertices(pg::AbstractGraph) = SuperVertex.(quotient_vertices(pg)) + +function superedge(pg::AbstractGraph, edge::AbstractEdge) + return SuperEdge(findpartition(pg, src(edge)) => findpartition(pg, dst(edge))) +end +superedges(pg::AbstractGraph) = SuperEdge.(quotient_edges(pg)) + +function boundary_superedges(pg::AbstractGraph, supervertices; kwargs...) + return SuperEdge.( + boundary_edges(quotient_graph(pg), parent.(supervertices); kwargs...) + ) +end + +function boundary_superedges( + pg::AbstractGraph, supervertex::SuperVertex; kwargs... + ) + return boundary_superedges(pg, [supervertex]; kwargs...) +end + +function partitionedgraph_induced_subgraph( + pg::AbstractGraph, supervertices::Vector{<:SuperVertex} + ) + return induced_subgraph(pg, vertices(pg, supervertices)) +end """ abstract type AbstractPartitionedGraph{V, PV} <: AbstractNamedGraph{V} diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index d2ed346..c429e26 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -19,9 +19,6 @@ quotient_graph(pg::PartitionedGraph) = pg.quotient_graph quotient_edges(pg::PartitionedGraph) = edges(pg.quotient_graph) quotient_vertices(pg::PartitionedGraph) = vertices(pg.quotient_graph) -super_vertex_type(::Type{<:PartitionedGraph{V}}) where {V} = SuperVertex{V} -super_edge_type(G::Type{<:PartitionedGraph{V}}) where {V} = SuperEdge{V, edgetype(G)} - Graphs.edgetype(::Type{<:PartitionedGraph{V,PV,G}}) where {V,PV,G} = edgetype(G) ##Constructors. @@ -65,37 +62,11 @@ function unpartitioned_graph_type(graph_type::Type{<:PartitionedGraph}) end findpartition(pg::PartitionedGraph, vertex) = pg.which_partition[vertex] -function supervertex(pg::PartitionedGraph, vertex) - return SuperVertex(findpartition(pg, vertex)) -end - -supervertices(pg::PartitionedGraph) = SuperVertex.(vertices(quotient_graph(pg))) - -function superedge(pg::PartitionedGraph, edge::AbstractEdge) - return SuperEdge( - parent(supervertex(pg, src(edge))) => parent(supervertex(pg, dst(edge))) - ) -end - -superedges(pg::PartitionedGraph) = map(SuperEdge, edges(QuotientView(pg))) - -function boundary_superedges(pg::PartitionedGraph, supervertices; kwargs...) - return SuperEdge.( - boundary_edges(quotient_graph(pg), parent.(supervertices); kwargs...) - ) -end - -function boundary_superedges( - pg::PartitionedGraph, supervertex::SuperVertex; kwargs... - ) - return boundary_superedges(pg, [supervertex]; kwargs...) -end - function Base.copy(pg::PartitionedGraph) return PartitionedGraph( - copy(unpartitioned_graph(pg)), - copy(quotient_graph(pg)), - copy(partitioned_vertices(pg)), + copy(pg.graph), + copy(pg.quotient_graph), + copy(pg.partitioned_vertices), copy(pg.which_partition), ) end @@ -168,7 +139,7 @@ end function partitionedgraph_induced_subgraph(pg::PartitionedGraph, vertices::Vector) sub_pg_graph, _ = induced_subgraph(unpartitioned_graph(pg), vertices) sub_partitioned_vertices = copy(partitioned_vertices(pg)) - for pv in NamedGraphs.vertices(QuotientView(pg)) + for pv in quotient_vertices(pg) vs = intersect(vertices, sub_partitioned_vertices[pv]) if !isempty(vs) sub_partitioned_vertices[pv] = vs @@ -180,12 +151,6 @@ function partitionedgraph_induced_subgraph(pg::PartitionedGraph, vertices::Vecto return PartitionedGraph(sub_pg_graph, sub_partitioned_vertices), nothing end -function partitionedgraph_induced_subgraph( - pg::PartitionedGraph, supervertices::Vector{<:SuperVertex} - ) - return induced_subgraph(pg, vertices(pg, supervertices)) -end - function Graphs.induced_subgraph(pg::PartitionedGraph, vertices) return partitionedgraph_induced_subgraph(pg, vertices) end From f42ba960f35ece65bc22894de1a44202f6611cb1 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Thu, 6 Nov 2025 15:54:46 -0500 Subject: [PATCH 12/36] Further improvements to interface. --- .../src/abstractpartitionedgraph.jl | 159 +++--------------- .../PartitionedGraphs/src/partitionedgraph.jl | 62 ++++--- .../PartitionedGraphs/src/quotientgraph.jl | 3 - src/lib/PartitionedGraphs/src/superedge.jl | 27 +++ src/lib/PartitionedGraphs/src/supervertex.jl | 44 ++++- 5 files changed, 139 insertions(+), 156 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index 092d5f4..5c7b283 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -14,46 +14,50 @@ using ..NamedGraphs: NamedGraphs, AbstractNamedGraph using ..NamedGraphs.GraphsExtensions: GraphsExtensions, add_vertices!, not_implemented, rem_vertices!, subgraph -# Essential method for the interface +# Essential methods for fast quotient graph construction. partitioned_vertices(g::AbstractGraph) = [vertices(g)] +quotient_edges(g::AbstractGraph, pvs = partitioned_vertices(g)) = keys(partitioned_edges(g, pvs)) +# Overload this for fast inverse mapping for vertices and edges +find_quotient_vertex(g::AbstractGraph, vertex) = find_quotient_vertex(partitioned_vertices(g), vertex) -# Optional functions for the interface - -## Return the partition that a vertex belongs to -function findpartition(pvs, vertex) +function find_quotient_vertex(pvs, vertex) rv = findfirst(pv -> vertex ∈ pv, pvs) if isnothing(rv) error("Vertex $vertex not found in any partition.") end return rv end -findpartition(g::AbstractGraph, vertex) = findpartition(partitioned_vertices(g), vertex) -function partitioned_edges(g::AbstractGraph, pvs = partitioned_vertices(g)) +function find_quotient_edge(g::AbstractGraph, edge, pvs = nothing) + if !has_edge(g, edge) + throw(ArgumentError("Graph does not have an edge $edge")) + end + gp = isnothing(pvs) ? g : pvs + qv_src = find_quotient_vertex(gp, src(edge)) + qv_dst = find_quotient_vertex(gp, dst(edge)) + return NamedEdge(qv_src => qv_dst) +end - SVT = keytype(pvs) - ET = edgetype(g) - rv = Dictionary{NamedEdge{SVT}, Vector{ET}}() +function partitioned_edges(g::AbstractGraph, pvs = nothing) + if isnothing(pvs) + pvs = partitioned_vertices(g) + end + + rv = Dictionary{NamedEdge{keytype(pvs)}, Vector{edgetype(g)}}() for e in edges(g) - pv_src = findpartition(pvs, src(e)) - pv_dst = findpartition(pvs, dst(e)) - se = NamedEdge(pv_src => pv_dst) + se = find_quotient_edge(g, e, pvs) if is_self_loop(se) continue end push!(get!(rv, se, typeof(e)[]), e) end + return rv end -partitioned_edges(g::AbstractGraph, se::SuperEdge) = partitioned_edges(g)[parent(se)] -function partitioned_edges(g::AbstractGraph, ses::Vector{<:SuperEdge}) - return mapreduce(se -> partitioned_edges(g, se), vcat, ses) -end quotient_vertices(g) = keys(partitioned_vertices(g)) -quotient_edges(g, pvs = partitioned_vertices(g)) = keys(partitioned_edges(g, pvs)) function quotient_graph(g::AbstractGraph) qg = NamedGraph(quotient_vertices(g)) @@ -67,13 +71,10 @@ function quotient_graph(g::AbstractGraph, pvs) return qg end -supervertex(pg::AbstractGraph, vertex) = SuperVertex(findpartition(pg, vertex)) -supervertices(pg::AbstractGraph) = SuperVertex.(quotient_vertices(pg)) - -function superedge(pg::AbstractGraph, edge::AbstractEdge) - return SuperEdge(findpartition(pg, src(edge)) => findpartition(pg, dst(edge))) +function is_boundary_edge(pg::AbstractGraph, edge::AbstractEdge) + p_edge = superedge(pg, edge) + return src(p_edge) == dst(p_edge) end -superedges(pg::AbstractGraph) = SuperEdge.(quotient_edges(pg)) function boundary_superedges(pg::AbstractGraph, supervertices; kwargs...) return SuperEdge.( @@ -87,12 +88,6 @@ function boundary_superedges( return boundary_superedges(pg, [supervertex]; kwargs...) end -function partitionedgraph_induced_subgraph( - pg::AbstractGraph, supervertices::Vector{<:SuperVertex} - ) - return induced_subgraph(pg, vertices(pg, supervertices)) -end - """ abstract type AbstractPartitionedGraph{V, PV} <: AbstractNamedGraph{V} @@ -105,15 +100,6 @@ abstract type AbstractPartitionedGraph{V, PV} <: AbstractNamedGraph{V} end unpartitioned_graph(::AbstractPartitionedGraph) = not_implemented() Base.copy(::AbstractPartitionedGraph) = not_implemented() -super_vertex_type(::Type{<:AbstractPartitionedGraph}) = not_implemented() -super_edge_type(::Type{<:AbstractPartitionedGraph}) = not_implemented() - -supervertex(::AbstractPartitionedGraph, vertex) = not_implemented() -superedge(::AbstractPartitionedGraph, edge) = not_implemented() - -delete_from_vertex_map!(::AbstractPartitionedGraph, vertex) = not_implemented() -insert_to_vertex_map!(::AbstractPartitionedGraph, vertex) = not_implemented() - function unpartitioned_graph_type(::Type{<:AbstractPartitionedGraph}) return not_implemented() end @@ -124,32 +110,10 @@ function GraphsExtensions.undirected_graph_type(::Type{<:AbstractPartitionedGrap return not_implemented() end -# Derived (by default) -super_vertex_type(pg::AbstractPartitionedGraph) = super_vertex_type(typeof(pg)) -super_edge_type(pg::AbstractPartitionedGraph) = super_edge_type(typeof(pg)) - function unpartitioned_graph_type(pg::AbstractPartitionedGraph) return typeof(unpartitioned_graph(pg)) end -""" - supervertices(pg::AbstractPartitionedGraph, vs = vertices(pg)) - -Return all unique super vertices corresponding to the set vertices `vs` of the graph `pg`. -""" -function supervertices(pg::AbstractPartitionedGraph, vs = vertices(pg)) - return unique(map(v -> supervertex(pg,v), vs)) -end - -""" - superedges(pg::AbstractPartitionedGraph, es = edges(pg)) - -Return all unique super edges corresponding to the set edges `es` of the graph `pg`. -""" -function superedges(pg::AbstractPartitionedGraph, es = edges(pg)) - return filter!(!is_self_loop, unique(map(e -> superedge(pg, e), es))) -end -superedge(pg::AbstractPartitionedGraph, p::Pair) = superedge(pg, edgetype(pg)(p)) # AbstractGraph interface. function Graphs.is_directed(graph_type::Type{<:AbstractPartitionedGraph}) @@ -158,43 +122,8 @@ end #Functions for the abstract type Graphs.vertices(pg::AbstractPartitionedGraph) = vertices(unpartitioned_graph(pg)) - -""" - vertices(pg::AbstractPartitionedGraph, supervertex::SuperEdge) - vertices(pg::AbstractPartitionedGraph, supervertices::Vector{SuperEdge}) - -Return the set of vertices in the partitioned graph `pg` that correspond to the super vertex -`supervertex` or set of super vertices `supervertex`. -""" -function Graphs.vertices(pg::AbstractGraph, supervertex::SuperVertex) - return partitioned_vertices(pg)[parent(supervertex)] -end -function Graphs.vertices(pg::AbstractPartitionedGraph, supervertices::Vector{<:SuperVertex}) - return unique(reduce(vcat, Iterators.map(sv -> vertices(pg, sv), supervertices))) -end - Graphs.edges(pg::AbstractPartitionedGraph) = edges(unpartitioned_graph(pg)) -""" - edges(pg::AbstractPartitionedGraph, superedge::SuperEdge) - edges(pg::AbstractPartitionedGraph, superedges::Vector{SuperEdge}) - -Return the set of edges in the partitioned graph `pg` that correspond to the super edge ` -superedge` or set of super edges `superedges`. -""" -function Graphs.edges(pg::AbstractPartitionedGraph, superedge::SuperEdge) - psrc_vs = vertices(pg, src(superedge)) - pdst_vs = vertices(pg, dst(superedge)) - psrc_subgraph, _ = induced_subgraph(unpartitioned_graph(pg), psrc_vs) - pdst_subgraph, _ = induced_subgraph(pg, pdst_vs) - full_subgraph, _ = induced_subgraph(pg, vcat(psrc_vs, pdst_vs)) - - return setdiff(edges(full_subgraph), vcat(edges(psrc_subgraph), edges(pdst_subgraph))) -end -function Graphs.edges(pg::AbstractPartitionedGraph, superedges::Vector{<:SuperEdge}) - return unique(reduce(vcat, [edges(pg, se) for se in superedges])) -end - function NamedGraphs.position_graph(pg::AbstractPartitionedGraph) return NamedGraphs.position_graph(unpartitioned_graph(pg)) end @@ -206,32 +135,6 @@ function NamedGraphs.ordered_vertices(pg::AbstractPartitionedGraph) end Graphs.edgetype(pg::AbstractPartitionedGraph) = edgetype(unpartitioned_graph(pg)) -function is_boundary_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge) - p_edge = superedge(pg, edge) - return src(p_edge) == dst(p_edge) -end - -function Graphs.rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge) - pg_edge = superedge(pg, edge) - if has_edge(QuotientView(pg), pg_edge) - g_edges = edges(pg, pg_edge) - if length(g_edges) == 1 - rem_edge!(QuotientView(pg), pg_edge) - end - end - return rem_edge!(unpartitioned_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 Graphs.add_vertex!( - pg::AbstractPartitionedGraph, vertex, supervertex::SuperVertex - ) - add_vertex!(unpartitioned_graph(pg), vertex) - add_vertex!(QuotientView(pg), parent(supervertex)) - insert_to_vertex_map!(pg, vertex, supervertex) - return pg -end - function GraphsExtensions.add_vertices!( pg::AbstractPartitionedGraph, vertices::Vector, @@ -252,13 +155,9 @@ function GraphsExtensions.add_vertices!( return pg end -function Graphs.rem_vertex!(pg::AbstractPartitionedGraph, vertex::SuperVertex) - return rem_super_vertex!(pg, vertex) -end - -Graphs.rem_vertex!(::AbstractPartitionedGraph, vertex) = not_implemented() +Graphs.rem_vertex!(::AbstractPartitionedGraph{V}, vertex::V) where {V} = not_implemented() -function Graphs.add_vertex!(pg::AbstractPartitionedGraph, vertex) +function Graphs.add_vertex!(::AbstractPartitionedGraph, vertex) return error("Need to specify a partition where the new vertex will go.") end @@ -275,9 +174,7 @@ function Base.:(==)(pg1::AbstractPartitionedGraph, pg2::AbstractPartitionedGraph return true end -function GraphsExtensions.subgraph( - pg::AbstractPartitionedGraph, supervertex::SuperVertex - ) +function GraphsExtensions.subgraph( pg::AbstractPartitionedGraph, supervertex::SuperVertex) return first(induced_subgraph(unpartitioned_graph(pg), vertices(pg, [supervertex]))) end diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index c429e26..94558be 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -3,6 +3,7 @@ using Graphs: AbstractEdge, AbstractGraph, add_edge!, edges, has_edge, induced_subgraph, vertices, dst, src, edgetype using .GraphsExtensions: GraphsExtensions, boundary_edges, is_self_loop, partitions using ..NamedGraphs: NamedEdge, NamedGraph +using ..NamedGraphs.OrderedDictionaries: OrderedDictionary # TODO: Parametrize `partitioned_vertices` and `which_partition`, # see https://github.com/mtfishman/NamedGraphs.jl/issues/63. @@ -13,11 +14,12 @@ struct PartitionedGraph{V, PV, G <: AbstractGraph{V}, NV} <: AbstractPartitioned which_partition::Dictionary{V, PV} end +# Interface overloads partitioned_vertices(pg::PartitionedGraph) = pg.partitioned_vertices - -quotient_graph(pg::PartitionedGraph) = pg.quotient_graph quotient_edges(pg::PartitionedGraph) = edges(pg.quotient_graph) quotient_vertices(pg::PartitionedGraph) = vertices(pg.quotient_graph) +find_quotient_vertex(pg::PartitionedGraph, vertex) = pg.which_partition[vertex] +quotient_graph(pg::PartitionedGraph) = pg.quotient_graph Graphs.edgetype(::Type{<:PartitionedGraph{V,PV,G}}) where {V,PV,G} = edgetype(G) @@ -60,7 +62,6 @@ unpartitioned_graph(pg::PartitionedGraph) = getfield(pg, :graph) function unpartitioned_graph_type(graph_type::Type{<:PartitionedGraph}) return fieldtype(graph_type, :graph) end -findpartition(pg::PartitionedGraph, vertex) = pg.which_partition[vertex] function Base.copy(pg::PartitionedGraph) return PartitionedGraph( @@ -85,34 +86,42 @@ function insert_to_vertex_map!( end function delete_from_vertex_map!(pg::PartitionedGraph{V}, vertex::V) where {V} - sv = supervertex(pg, vertex) + sv = find_quotient_vertex(pg, vertex) return delete_from_vertex_map!(pg, sv, vertex) end function delete_from_vertex_map!( - pg::PartitionedGraph{V}, partitioned_vertex::SuperVertex, vertex::V + pg::PartitionedGraph{V}, sv::SuperVertex, vertex::V ) where {V} - vs = vertices(pg, partitioned_vertex) + return delete_from_vertex_map!(pg, parent(sv), vertex) +end - delete!(pg.partitioned_vertices, parent(partitioned_vertex)) +function delete_from_vertex_map!( + pg::PartitionedGraph{V, PV}, qv::PV, vertex::V +) where {V, PV} + + vs = partitioned_vertices(pg)[qv] + + delete!(pg.partitioned_vertices, qv) if length(vs) != 1 - insert!(pg.partitioned_vertices, parent(partitioned_vertex), setdiff(vs, [vertex])) + insert!(pg.partitioned_vertices, qv, setdiff(vs, [vertex])) end delete!(pg.which_partition, vertex) - return partitioned_vertex + return pg end function Graphs.rem_vertex!(pg::PartitionedGraph{V}, vertex::V) where {V} - sv = supervertex(pg, vertex) + qv = find_quotient_vertex(pg, vertex) - delete_from_vertex_map!(pg, sv, vertex) + delete_from_vertex_map!(pg, qv, vertex) - rem_vertex!(unpartitioned_graph(pg), vertex) + rem_vertex!(pg.graph, vertex) - if !haskey(partitioned_vertices(pg), parent(sv)) - rem_vertex!(pg.quotient_graph, parent(sv)) + # If the super-vertex is now empty, remove it from the quotient graph + if !haskey(pg.partitioned_vertices, qv) + rem_vertex!(pg.quotient_graph, qv) end return pg @@ -125,16 +134,33 @@ function Graphs.add_vertex!(pg::PartitionedGraph{V}, vertex::V, sv::SuperVertex) return pg end -function Graphs.add_edge!(pg::PartitionedGraph, edge) +function Graphs.add_edge!(pg::PartitionedGraph, edge::AbstractEdge) @assert edge isa edgetype(pg) add_edge!(pg.graph, edge) - pg_edge = parent(superedge(pg, edge)) + pg_edge = find_quotient_edge(pg, edge) if src(pg_edge) != dst(pg_edge) add_edge!(pg.quotient_graph, pg_edge) end return pg end +function Graphs.rem_edge!(pg::PartitionedGraph, se::SuperEdge) + return rem_edge!(pg.quotient_graph, parent(se)) +end +function Graphs.rem_edge!(pg::PartitionedGraph, edge::AbstractEdge) + @assert edge isa edgetype(pg) + # This already checks if the edge is in pg + se = superedge(pg, edge) + if se in quotient_edges(pg) + g_edges = edges(pg, se) + if length(g_edges) == 1 + # Remove the entire super-edge + return rem_edge!(pg, se) + end + end + return rem_edge!(pg.graph, edge) +end + ### PartitionedGraph Specific Functions function partitionedgraph_induced_subgraph(pg::PartitionedGraph, vertices::Vector) sub_pg_graph, _ = induced_subgraph(unpartitioned_graph(pg), vertices) @@ -151,10 +177,6 @@ function partitionedgraph_induced_subgraph(pg::PartitionedGraph, vertices::Vecto return PartitionedGraph(sub_pg_graph, sub_partitioned_vertices), nothing end -function Graphs.induced_subgraph(pg::PartitionedGraph, vertices) - return partitionedgraph_induced_subgraph(pg, vertices) -end - # Fixes ambiguity error with `Graphs.jl`. function Graphs.induced_subgraph(pg::PartitionedGraph, vertices::Vector{<:Integer}) return partitionedgraph_induced_subgraph(pg, vertices) diff --git a/src/lib/PartitionedGraphs/src/quotientgraph.jl b/src/lib/PartitionedGraphs/src/quotientgraph.jl index eb38d7b..e2ba3a9 100644 --- a/src/lib/PartitionedGraphs/src/quotientgraph.jl +++ b/src/lib/PartitionedGraphs/src/quotientgraph.jl @@ -30,9 +30,6 @@ end Graphs.rem_vertex!(qg::QuotientView, v) = rem_vertex!(qg.graph, SuperVertex(v)) Graphs.rem_edge!(qg::QuotientView, v) = rem_edge!(qg.graph, SuperEdge(v)) -Graphs.add_vertex!(qg::QuotientView, v) = add_vertex!(qg.graph, SuperVertex(v)) -Graphs.add_edge!(qg::QuotientView, v) = add_edge!(qg.graph, SuperEdge(v)) - for f in [ :(NamedGraphs.edgetype), :(NamedGraphs.namedgraph_induced_subgraph), diff --git a/src/lib/PartitionedGraphs/src/superedge.jl b/src/lib/PartitionedGraphs/src/superedge.jl index 174583c..49a3e4d 100644 --- a/src/lib/PartitionedGraphs/src/superedge.jl +++ b/src/lib/PartitionedGraphs/src/superedge.jl @@ -6,6 +6,19 @@ struct SuperEdge{V, E <: AbstractEdge{V}} <: AbstractNamedEdge{V} edge::E end +superedge(pg::AbstractGraph, edge::AbstractEdge) = SuperEdge(find_quotient_edge(pg, edge)) +superedge(pg::AbstractGraph, p::Pair) = superedge(pg, edgetype(pg)(p)) + +""" + superedges(pg::AbstractPartitionedGraph, es = edges(pg)) + +Return all unique super edges corresponding to the set edges `es` of the graph `pg`. +""" +superedges(pg::AbstractGraph) = SuperEdge.(quotient_edges(pg)) +function superedges(pg::AbstractGraph, es) + return filter!(!is_self_loop, unique(map(e -> superedge(pg, e), es))) +end + Base.parent(se::SuperEdge) = getfield(se, :edge) Graphs.src(se::SuperEdge) = SuperVertex(src(parent(se))) Graphs.dst(se::SuperEdge) = SuperVertex(dst(parent(se))) @@ -13,6 +26,20 @@ SuperEdge(p::Pair) = SuperEdge(NamedEdge(first(p) => last(p))) SuperEdge(vsrc, vdst) = SuperEdge(vsrc => vdst) Base.reverse(se::SuperEdge) = SuperEdge(reverse(parent(se))) +""" + edges(pg::AbstractGraph, superedge::SuperEdge) + edges(pg::AbstractGraph, superedges::Vector{SuperEdge}) + +Return the set of edges in the partitioned graph `pg` that correspond to the super edge ` +superedge` or set of super edges `superedges`. +""" +function Graphs.edges(pg::AbstractGraph, superedge::SuperEdge) + return partitioned_edges(pg)[parent(superedge)] +end +function Graphs.edges(pg::AbstractGraph, superedges::Vector{<:SuperEdge}) + return unique(reduce(vcat, [edges(pg, se) for se in superedges])) +end + function Graphs.has_edge(g::AbstractGraph, se::SuperEdge) return parent(se) in quotient_edges(g) end diff --git a/src/lib/PartitionedGraphs/src/supervertex.jl b/src/lib/PartitionedGraphs/src/supervertex.jl index d8cf383..5974396 100644 --- a/src/lib/PartitionedGraphs/src/supervertex.jl +++ b/src/lib/PartitionedGraphs/src/supervertex.jl @@ -8,6 +8,30 @@ end Base.parent(sv::SuperVertex) = getfield(sv, :vertex) +supervertex(g::AbstractGraph, vertex) = SuperVertex(find_quotient_vertex(g, vertex)) + +""" + supervertices(g::AbstractGraph, vs = vertices(pg)) + +Return all unique super vertices corresponding to the set vertices `vs` of the graph `pg`. +""" +supervertices(g::AbstractGraph) = SuperVertex.(quotient_vertices(g)) +supervertices(g::AbstractGraph, vs) = unique(map(v -> supervertex(g,v), vs)) + +""" + vertices(g::AbstractGraph, supervertex::SuperVertex) + vertices(g::AbstractGraph, supervertices::Vector{SuperVertex}) + +Return the set of vertices in the graph `g` that correspond to the super vertex +`supervertex` or set of super vertices `supervertex`. +""" +function Graphs.vertices(g::AbstractGraph, supervertex::SuperVertex) + return partitioned_vertices(g)[parent(supervertex)] +end +function Graphs.vertices(g::AbstractGraph, supervertices::Vector{<:SuperVertex}) + return unique(mapreduce(sv -> vertices(g, sv), vcat, supervertices)) +end + # Avoid method ambiguity Graphs.has_vertex(g::AbstractGraph, sv::SuperVertex) = has_super_vertex(g, sv) Graphs.has_vertex(g::AbstractNamedGraph, sv::SuperVertex) = has_super_vertex(g, sv) @@ -16,12 +40,28 @@ function has_super_vertex(g::AbstractGraph, supervertex::SuperVertex) return parent(supervertex) in quotient_vertices(g) end -Graphs.nv(g::AbstractGraph, sv::SuperVertex) = length(partitioned_vertices(g, sv)) +Graphs.nv(g::AbstractGraph, sv::SuperVertex) = length(vertices(g, sv)) function rem_super_vertex!(g::AbstractGraph, sv::SuperVertex) - vertices_to_remove = partitioned_vertices(g, sv) + vertices_to_remove = vertices(g, sv) rem_vertices!(g, vertices_to_remove) return g end Graphs.rem_vertex!(g::AbstractGraph, sv::SuperVertex) = rem_super_vertex!(g, sv) +Graphs.rem_vertex!(g::AbstractNamedGraph, sv::SuperVertex) = rem_super_vertex!(g, sv) + +function Graphs.induced_subgraph( + g::AbstractGraph, supervertices::Union{SuperVertex, Vector{<:SuperVertex}} + ) + return induced_subgraph(g, vertices(pg, supervertices)) +end +function Graphs.induced_subgraph( + g::AbstractNamedGraph, svs::Union{SuperVertex, Vector{<:SuperVertex}} + ) + gsvs = supervertices(g) + if length(setdiff(gsvs, [svs;])) == length(gsvs) + throw(ArgumentError("One or more supervertices not found in graph")) + end + return induced_subgraph(g, vertices(g, svs)) +end From fa5ceba06b453443dd24b4daf0d0c13148e1219d Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Mon, 10 Nov 2025 12:51:10 -0500 Subject: [PATCH 13/36] Rework interface in favour of overloading `quotient_graph` This is to make checking of `quotient_edges` etc easier. --- src/abstractnamedgraph.jl | 3 +- .../src/abstractpartitionedgraph.jl | 67 ++++++++++++------- .../PartitionedGraphs/src/partitionedgraph.jl | 35 ++++------ .../PartitionedGraphs/src/quotientgraph.jl | 35 ++++++---- src/lib/PartitionedGraphs/src/superedge.jl | 24 ++++--- src/lib/PartitionedGraphs/src/supervertex.jl | 32 ++++----- src/namedgraph.jl | 1 - test/test_partitionedgraph.jl | 8 ++- 8 files changed, 112 insertions(+), 93 deletions(-) diff --git a/src/abstractnamedgraph.jl b/src/abstractnamedgraph.jl index 36c78fd..8ae61c2 100644 --- a/src/abstractnamedgraph.jl +++ b/src/abstractnamedgraph.jl @@ -66,7 +66,8 @@ vertex_positions(graph::AbstractNamedGraph) = not_implemented() # returns the corresponding vertex. ordered_vertices(graph::AbstractNamedGraph) = not_implemented() -Graphs.edgetype(graph::AbstractNamedGraph) = not_implemented() +Graphs.edgetype(graph::AbstractNamedGraph) = edgetype(typeof(graph)) +Graphs.edgetype(::Type{<:AbstractNamedGraph}) = not_implemented() # TODO: Define generic version in `GraphsExtensions`. GraphsExtensions.directed_graph_type(G::Type{<:AbstractNamedGraph}) = not_implemented() diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index 5c7b283..db28c58 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -1,3 +1,4 @@ +using Dictionaries: Dictionary using Graphs: AbstractEdge, AbstractGraph, @@ -12,11 +13,31 @@ using Graphs: vertices using ..NamedGraphs: NamedGraphs, AbstractNamedGraph using ..NamedGraphs.GraphsExtensions: - GraphsExtensions, add_vertices!, not_implemented, rem_vertices!, subgraph + GraphsExtensions, add_vertices!, not_implemented, rem_vertices!, subgraph, vertextype -# Essential methods for fast quotient graph construction. +# For you own graph type `g`, you should define a method for this function is you +# desire custom partitioning. partitioned_vertices(g::AbstractGraph) = [vertices(g)] -quotient_edges(g::AbstractGraph, pvs = partitioned_vertices(g)) = keys(partitioned_edges(g, pvs)) + +# For fast quotient edge checking and graph construction, one should overload this function. +function quotient_graph(g::AbstractGraph, pvs = nothing) + if isnothing(pvs) === nothing + pvs = partitioned_vertices(g) + end + + qg = NamedGraph(keys(pvs)) + + for e in edges(g) + qv_src = find_quotient_vertex(pvs, src(e)) + qv_dst = find_quotient_vertex(pvs, dst(e)) + qe = NamedEdge(qv_src => qv_dst) + if qv_src != qv_dst && !has_edge(qg, qe) + add_edge!(qg, qe) + end + end + + return qg +end # Overload this for fast inverse mapping for vertices and edges find_quotient_vertex(g::AbstractGraph, vertex) = find_quotient_vertex(partitioned_vertices(g), vertex) @@ -36,40 +57,33 @@ function find_quotient_edge(g::AbstractGraph, edge, pvs = nothing) gp = isnothing(pvs) ? g : pvs qv_src = find_quotient_vertex(gp, src(edge)) qv_dst = find_quotient_vertex(gp, dst(edge)) - return NamedEdge(qv_src => qv_dst) + return quotient_edgetype(g)(qv_src => qv_dst) end function partitioned_edges(g::AbstractGraph, pvs = nothing) - if isnothing(pvs) + if isnothing(pvs) pvs = partitioned_vertices(g) end - rv = Dictionary{NamedEdge{keytype(pvs)}, Vector{edgetype(g)}}() + dict = Dictionary{quotient_edgetype(g), Vector{edgetype(g)}}() for e in edges(g) - se = find_quotient_edge(g, e, pvs) - if is_self_loop(se) + qe = find_quotient_edge(g, e, pvs) + if is_self_loop(qe) continue end - push!(get!(rv, se, typeof(e)[]), e) + push!(get!(dict, qe, edgetype(g)[]), e) end - return rv -end - -quotient_vertices(g) = keys(partitioned_vertices(g)) - -function quotient_graph(g::AbstractGraph) - qg = NamedGraph(quotient_vertices(g)) - add_edges!(qg, quotient_edges(g)) - return qg + return dict end -function quotient_graph(g::AbstractGraph, pvs) - qg = NamedGraph(keys(pvs)) - add_edges!(qg, quotient_edges(g, pvs)) - return qg +function quotient_vertices(g, pvs = partitioned_vertices(g)) + qg = quotient_graph_type(g)(keys(pvs)) + return vertices(qg) end +quotient_edges(g::AbstractGraph) = edges(quotient_graph(g)) +quotient_edges(g::AbstractGraph, pvs) = edges(quotient_graph(g, pvs)) function is_boundary_edge(pg::AbstractGraph, edge::AbstractEdge) p_edge = superedge(pg, edge) @@ -88,6 +102,11 @@ function boundary_superedges( return boundary_superedges(pg, [supervertex]; kwargs...) end +quotient_graph_type(g) = quotient_graph_type(typeof(g)) +quotient_graph_type(::Type{<:AbstractGraph{V}}) where {V} = NamedGraph{V} +quotient_vertextype(G) = vertextype(quotient_graph_type(G)) +quotient_edgetype(G) = edgetype(quotient_graph_type(G)) + """ abstract type AbstractPartitionedGraph{V, PV} <: AbstractNamedGraph{V} @@ -174,8 +193,8 @@ function Base.:(==)(pg1::AbstractPartitionedGraph, pg2::AbstractPartitionedGraph return true end -function GraphsExtensions.subgraph( pg::AbstractPartitionedGraph, supervertex::SuperVertex) - return first(induced_subgraph(unpartitioned_graph(pg), vertices(pg, [supervertex]))) +function GraphsExtensions.subgraph(pg::AbstractPartitionedGraph, supervertex::SuperVertex) + return first(induced_subgraph(unpartitioned_graph(pg), vertices(pg, supervertex))) end function Graphs.induced_subgraph( diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index 94558be..082c1be 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -16,30 +16,23 @@ end # Interface overloads partitioned_vertices(pg::PartitionedGraph) = pg.partitioned_vertices -quotient_edges(pg::PartitionedGraph) = edges(pg.quotient_graph) -quotient_vertices(pg::PartitionedGraph) = vertices(pg.quotient_graph) -find_quotient_vertex(pg::PartitionedGraph, vertex) = pg.which_partition[vertex] quotient_graph(pg::PartitionedGraph) = pg.quotient_graph +find_quotient_vertex(pg::PartitionedGraph, vertex) = pg.which_partition[vertex] -Graphs.edgetype(::Type{<:PartitionedGraph{V,PV,G}}) where {V,PV,G} = edgetype(G) +quotient_graph_type(::Type{<:AbstractPartitionedGraph{V, PV}}) where {V, PV} = NamedGraph{PV} + +Graphs.edgetype(::Type{<:PartitionedGraph{V, PV, G}}) where {V, PV, G} = edgetype(G) ##Constructors. function PartitionedGraph(g::AbstractGraph{V}, partitioned_vertices) where {V} pvs = keys(partitioned_vertices) - qg = NamedGraph(pvs) which_partition = Dictionary{V, eltype(pvs)}() for v in vertices(g) - v_pvs = Set(findall(pv -> v ∈ partitioned_vertices[pv], pvs)) + v_pvs = Set(findall(pv -> v ∈ pv, partitioned_vertices)) @assert length(v_pvs) == 1 insert!(which_partition, v, first(v_pvs)) end - for e in edges(g) - pv_src, pv_dst = which_partition[src(e)], which_partition[dst(e)] - pe = NamedEdge(pv_src => pv_dst) - if pv_src != pv_dst && !has_edge(qg, pe) - add_edge!(qg, pe) - end - end + qg = quotient_graph(g, partitioned_vertices) return PartitionedGraph( g, qg, @@ -57,6 +50,7 @@ function PartitionedGraph(g::AbstractGraph; kwargs...) return PartitionedGraph(g, partitioned_verts) end + #Needed for interface unpartitioned_graph(pg::PartitionedGraph) = getfield(pg, :graph) function unpartitioned_graph_type(graph_type::Type{<:PartitionedGraph}) @@ -91,14 +85,14 @@ function delete_from_vertex_map!(pg::PartitionedGraph{V}, vertex::V) where {V} end function delete_from_vertex_map!( - pg::PartitionedGraph{V}, sv::SuperVertex, vertex::V -) where {V} + pg::PartitionedGraph{V}, sv::SuperVertex, vertex::V + ) where {V} return delete_from_vertex_map!(pg, parent(sv), vertex) end function delete_from_vertex_map!( - pg::PartitionedGraph{V, PV}, qv::PV, vertex::V -) where {V, PV} + pg::PartitionedGraph{V, PV}, qv::PV, vertex::V + ) where {V, PV} vs = partitioned_vertices(pg)[qv] @@ -144,18 +138,15 @@ function Graphs.add_edge!(pg::PartitionedGraph, edge::AbstractEdge) return pg end -function Graphs.rem_edge!(pg::PartitionedGraph, se::SuperEdge) - return rem_edge!(pg.quotient_graph, parent(se)) -end function Graphs.rem_edge!(pg::PartitionedGraph, edge::AbstractEdge) @assert edge isa edgetype(pg) # This already checks if the edge is in pg se = superedge(pg, edge) - if se in quotient_edges(pg) + if se in superedges(pg) || reverse(se) in superedges(pg) g_edges = edges(pg, se) if length(g_edges) == 1 # Remove the entire super-edge - return rem_edge!(pg, se) + return rem_edge!(pg.quotient_graph, parent(se)) end end return rem_edge!(pg.graph, edge) diff --git a/src/lib/PartitionedGraphs/src/quotientgraph.jl b/src/lib/PartitionedGraphs/src/quotientgraph.jl index e2ba3a9..28f73bf 100644 --- a/src/lib/PartitionedGraphs/src/quotientgraph.jl +++ b/src/lib/PartitionedGraphs/src/quotientgraph.jl @@ -1,4 +1,4 @@ -using Graphs: AbstractGraph, rem_vertex!, vertices, edges +using Graphs: AbstractGraph, rem_vertex!, rem_edge!, vertices, edges using .GraphsExtensions: add_edges! using ..NamedGraphs: NamedGraph, position_graph_type @@ -6,44 +6,51 @@ struct QuotientView{V, G <: AbstractGraph{V}} <: AbstractNamedGraph{V} graph::G end -function quotient_graph(g::QuotientView) - qg = NamedGraph(vertices(g)) +Base.parent(qg::QuotientView) = qg.graph +parent_graph_type(::Type{<:QuotientView{V, G}}) where {V, G} = G + +function Base.convert(GT::Type{<:AbstractGraph}, g::QuotientView) + qg = GT(vertices(g)) add_edges!(qg, edges(g)) return qg end -quotient_graph_type(::Type{<:QuotientView{V}}) where {V} = NamedGraph{V} +NamedGraphs.vertextype(Q::Type{<:QuotientView}) = quotient_vertextype(parent_graph_type(Q)) +NamedGraphs.edgetype(Q::Type{<:QuotientView}) = quotient_edgetype(parent_graph_type(Q)) -Graphs.vertices(qg::QuotientView) = quotient_vertices(qg.graph) -Graphs.edges(qg::QuotientView) = quotient_edges(qg.graph) +Graphs.vertices(qg::QuotientView) = quotient_vertices(parent(qg)) +Graphs.edges(qg::QuotientView) = quotient_edges(parent(qg)) -Base.copy(g::QuotientView) = QuotientView(copy(g.graph)) +Base.copy(g::QuotientView) = QuotientView(copy(parent(g))) # Graphs.jl and NamedGraphs.jl interface overloads for `PartitionsGraphView` wrapping # a `PartitionedGraph`. function NamedGraphs.position_graph_type( type::Type{<:QuotientView{V, G}} ) where {V, G <: PartitionedGraph{V}} - return position_graph_type(quotient_graph_type(type)) + return position_graph_type(quotient_graph_type(parent_graph_type(type))) end -Graphs.rem_vertex!(qg::QuotientView, v) = rem_vertex!(qg.graph, SuperVertex(v)) -Graphs.rem_edge!(qg::QuotientView, v) = rem_edge!(qg.graph, SuperEdge(v)) +function Graphs.rem_vertex!(qg::QuotientView, v) + rem_supervertex!(parent(qg), SuperVertex(v)) + return qg +end +function Graphs.rem_edge!(qg::QuotientView, v) + rem_superedge!(parent(qg), SuperEdge(v)) + return qg +end for f in [ - :(NamedGraphs.edgetype), :(NamedGraphs.namedgraph_induced_subgraph), :(NamedGraphs.ordered_vertices), :(NamedGraphs.position_graph), :(NamedGraphs.vertex_positions), - :(NamedGraphs.vertextype), ] @eval begin function $f( g::QuotientView{V, G}, args...; kwargs... ) where {V, G} - return $f(quotient_graph(g), args...; kwargs...) + return $f(convert(NamedGraph, (g)), args...; kwargs...) end end end - diff --git a/src/lib/PartitionedGraphs/src/superedge.jl b/src/lib/PartitionedGraphs/src/superedge.jl index 49a3e4d..2ed1e09 100644 --- a/src/lib/PartitionedGraphs/src/superedge.jl +++ b/src/lib/PartitionedGraphs/src/superedge.jl @@ -1,4 +1,4 @@ -using Graphs: AbstractGraph, Graphs, AbstractEdge, dst, src, ne +using Graphs: AbstractGraph, Graphs, AbstractEdge, dst, src, ne, has_edge using ..NamedGraphs: AbstractNamedEdge using ..NamedGraphs.GraphsExtensions: GraphsExtensions, not_implemented, rem_edges!, rem_edge @@ -34,20 +34,26 @@ Return the set of edges in the partitioned graph `pg` that correspond to the sup superedge` or set of super edges `superedges`. """ function Graphs.edges(pg::AbstractGraph, superedge::SuperEdge) - return partitioned_edges(pg)[parent(superedge)] + + pes = partitioned_edges(pg) + defval = edgetype(pg)[] + + rv = get(pes, parent(superedge), defval) + if !is_directed(quotient_graph_type(pg)) && isempty(rv) + append!(rv, get(pes, reverse(parent(superedge)), defval)) + end + return rv end function Graphs.edges(pg::AbstractGraph, superedges::Vector{<:SuperEdge}) return unique(reduce(vcat, [edges(pg, se) for se in superedges])) end -function Graphs.has_edge(g::AbstractGraph, se::SuperEdge) - return parent(se) in quotient_edges(g) -end +has_superedge(g::AbstractGraph, se::SuperEdge) = has_edge(quotient_graph(g), parent(se)) Graphs.ne(g::AbstractGraph, se::SuperEdge) = length(quotient_edges(g, se)) -function Graphs.rem_edge!(g::AbstractGraph, se::SuperEdge) - edges_to_remove = partitioned_edges(g, se) - rem_edges!(g, edges_to_remove) - return g +function GraphsExtensions.rem_edges!(g::AbstractGraph, sv::SuperEdge) + rv = rem_edges!(g, edges(g, sv)) + return rv end +rem_superedge!(pg::AbstractGraph, sv::SuperEdge) = rem_edges!(pg, sv) diff --git a/src/lib/PartitionedGraphs/src/supervertex.jl b/src/lib/PartitionedGraphs/src/supervertex.jl index 5974396..4b5bae4 100644 --- a/src/lib/PartitionedGraphs/src/supervertex.jl +++ b/src/lib/PartitionedGraphs/src/supervertex.jl @@ -16,14 +16,14 @@ supervertex(g::AbstractGraph, vertex) = SuperVertex(find_quotient_vertex(g, vert Return all unique super vertices corresponding to the set vertices `vs` of the graph `pg`. """ supervertices(g::AbstractGraph) = SuperVertex.(quotient_vertices(g)) -supervertices(g::AbstractGraph, vs) = unique(map(v -> supervertex(g,v), vs)) +supervertices(g::AbstractGraph, vs) = unique(map(v -> supervertex(g, v), vs)) """ vertices(g::AbstractGraph, supervertex::SuperVertex) vertices(g::AbstractGraph, supervertices::Vector{SuperVertex}) -Return the set of vertices in the graph `g` that correspond to the super vertex -`supervertex` or set of super vertices `supervertex`. +Return the set of vertices in the graph `g` associated with the super vertex +`supervertex` or set of super vertices `supervertices`. """ function Graphs.vertices(g::AbstractGraph, supervertex::SuperVertex) return partitioned_vertices(g)[parent(supervertex)] @@ -32,29 +32,17 @@ function Graphs.vertices(g::AbstractGraph, supervertices::Vector{<:SuperVertex}) return unique(mapreduce(sv -> vertices(g, sv), vcat, supervertices)) end -# Avoid method ambiguity -Graphs.has_vertex(g::AbstractGraph, sv::SuperVertex) = has_super_vertex(g, sv) -Graphs.has_vertex(g::AbstractNamedGraph, sv::SuperVertex) = has_super_vertex(g, sv) - -function has_super_vertex(g::AbstractGraph, supervertex::SuperVertex) - return parent(supervertex) in quotient_vertices(g) +function has_supervertex(g::AbstractGraph, supervertex::SuperVertex) + qg = quotient_graph_type(g)(quotient_vertices(g)) + return has_vertex(qg, parent(supervertex)) end Graphs.nv(g::AbstractGraph, sv::SuperVertex) = length(vertices(g, sv)) -function rem_super_vertex!(g::AbstractGraph, sv::SuperVertex) - vertices_to_remove = vertices(g, sv) - rem_vertices!(g, vertices_to_remove) - return g -end - -Graphs.rem_vertex!(g::AbstractGraph, sv::SuperVertex) = rem_super_vertex!(g, sv) -Graphs.rem_vertex!(g::AbstractNamedGraph, sv::SuperVertex) = rem_super_vertex!(g, sv) - function Graphs.induced_subgraph( g::AbstractGraph, supervertices::Union{SuperVertex, Vector{<:SuperVertex}} ) - return induced_subgraph(g, vertices(pg, supervertices)) + return induced_subgraph(g, vertices(g, supervertices)) end function Graphs.induced_subgraph( g::AbstractNamedGraph, svs::Union{SuperVertex, Vector{<:SuperVertex}} @@ -65,3 +53,9 @@ function Graphs.induced_subgraph( end return induced_subgraph(g, vertices(g, svs)) end + + +function GraphsExtensions.rem_vertices!(g::AbstractGraph, sv::SuperVertex) + return rem_vertices!(g, vertices(g, sv)) +end +rem_supervertex!(pg::AbstractGraph, sv::SuperVertex) = rem_vertices!(pg, sv) diff --git a/src/namedgraph.jl b/src/namedgraph.jl index daa7d56..b54ec66 100644 --- a/src/namedgraph.jl +++ b/src/namedgraph.jl @@ -198,7 +198,6 @@ function Base.copy(graph::GenericNamedGraph) end Graphs.edgetype(graph_type::Type{<:GenericNamedGraph}) = NamedEdge{vertextype(graph_type)} -Graphs.edgetype(graph::GenericNamedGraph) = edgetype(typeof(graph)) function GraphsExtensions.directed_graph_type(graph_type::Type{<:GenericNamedGraph}) return GenericNamedGraph{ diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index 72a96b8..5c50589 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -44,7 +44,9 @@ using NamedGraphs.PartitionedGraphs: superedges, supervertex, supervertices, - unpartitioned_graph + unpartitioned_graph, + rem_supervertex!, + has_supervertex using Dictionaries: Dictionary, dictionary using Pkg: Pkg using Test: @test, @testset @@ -145,7 +147,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 - rem_vertex!(pg, pv) + rem_supervertex!(pg, pv) @test !is_connected(unpartitioned_graph(pg)) && !is_connected(QuotientView(pg)) @test parent(pv) ∉ vertices(QuotientView(pg)) @test !has_vertex(pg, pv) @@ -159,7 +161,7 @@ end @test is_connected(pg.graph) @test is_path_graph(QuotientView(pg)) @test parent(pv) ∈ vertices(QuotientView(pg)) - @test has_vertex(pg, pv) + @test has_supervertex(pg, pv) @test is_tree(QuotientView(pg)) @test nv(pg) == nx * ny @test nv(QuotientView(pg)) == nx From 43c89da662ce0f53a801309c2b397bcc178eca38 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 09:57:09 -0500 Subject: [PATCH 14/36] Add `PartitionedView` type for constructing partitions of an `AbstractGraph` on the fly - Subtypes `AbstractPartitionedView`. - Removes the extra methods for the interface functions. - Add `to_partitioned_vertices` to normalize partitions to a dictionary. --- .../src/PartitionedGraphs.jl | 1 + .../src/abstractpartitionedgraph.jl | 38 ++++++++----------- .../PartitionedGraphs/src/partitionedgraph.jl | 4 +- .../PartitionedGraphs/src/partitionedview.jl | 27 +++++++++++++ 4 files changed, 46 insertions(+), 24 deletions(-) create mode 100644 src/lib/PartitionedGraphs/src/partitionedview.jl diff --git a/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl b/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl index 331130c..8dbf4b3 100644 --- a/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl +++ b/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl @@ -2,6 +2,7 @@ module PartitionedGraphs include("supervertex.jl") include("superedge.jl") include("abstractpartitionedgraph.jl") +include("partitionedview.jl") include("partitionedgraph.jl") include("quotientgraph.jl") end diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index db28c58..d2d8031 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -20,16 +20,13 @@ using ..NamedGraphs.GraphsExtensions: partitioned_vertices(g::AbstractGraph) = [vertices(g)] # For fast quotient edge checking and graph construction, one should overload this function. -function quotient_graph(g::AbstractGraph, pvs = nothing) - if isnothing(pvs) === nothing - pvs = partitioned_vertices(g) - end +function quotient_graph(g::AbstractGraph) - qg = NamedGraph(keys(pvs)) + qg = NamedGraph(quotient_vertices(g)) for e in edges(g) - qv_src = find_quotient_vertex(pvs, src(e)) - qv_dst = find_quotient_vertex(pvs, dst(e)) + qv_src = find_quotient_vertex(g, src(e)) + qv_dst = find_quotient_vertex(g, dst(e)) qe = NamedEdge(qv_src => qv_dst) if qv_src != qv_dst && !has_edge(qg, qe) add_edge!(qg, qe) @@ -40,9 +37,8 @@ function quotient_graph(g::AbstractGraph, pvs = nothing) end # Overload this for fast inverse mapping for vertices and edges -find_quotient_vertex(g::AbstractGraph, vertex) = find_quotient_vertex(partitioned_vertices(g), vertex) - -function find_quotient_vertex(pvs, vertex) +function find_quotient_vertex(g, vertex) + pvs = partitioned_vertices(g) rv = findfirst(pv -> vertex ∈ pv, pvs) if isnothing(rv) error("Vertex $vertex not found in any partition.") @@ -50,25 +46,23 @@ function find_quotient_vertex(pvs, vertex) return rv end -function find_quotient_edge(g::AbstractGraph, edge, pvs = nothing) +function find_quotient_edge(g::AbstractGraph, edge) if !has_edge(g, edge) throw(ArgumentError("Graph does not have an edge $edge")) end - gp = isnothing(pvs) ? g : pvs - qv_src = find_quotient_vertex(gp, src(edge)) - qv_dst = find_quotient_vertex(gp, dst(edge)) + qv_src = find_quotient_vertex(g, src(edge)) + qv_dst = find_quotient_vertex(g, dst(edge)) return quotient_edgetype(g)(qv_src => qv_dst) end -function partitioned_edges(g::AbstractGraph, pvs = nothing) - if isnothing(pvs) - pvs = partitioned_vertices(g) - end +function partitioned_edges(g::AbstractGraph) dict = Dictionary{quotient_edgetype(g), Vector{edgetype(g)}}() for e in edges(g) - qe = find_quotient_edge(g, e, pvs) + + qe = find_quotient_edge(g, e) + if is_self_loop(qe) continue end @@ -78,12 +72,12 @@ function partitioned_edges(g::AbstractGraph, pvs = nothing) return dict end -function quotient_vertices(g, pvs = partitioned_vertices(g)) - qg = quotient_graph_type(g)(keys(pvs)) +function quotient_vertices(g) + QGT = quotient_graph_type(g) + qg = QGT(keys(partitioned_vertices(g))) return vertices(qg) end quotient_edges(g::AbstractGraph) = edges(quotient_graph(g)) -quotient_edges(g::AbstractGraph, pvs) = edges(quotient_graph(g, pvs)) function is_boundary_edge(pg::AbstractGraph, edge::AbstractEdge) p_edge = superedge(pg, edge) diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index 082c1be..413966e 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -32,11 +32,11 @@ function PartitionedGraph(g::AbstractGraph{V}, partitioned_vertices) where {V} @assert length(v_pvs) == 1 insert!(which_partition, v, first(v_pvs)) end - qg = quotient_graph(g, partitioned_vertices) + qg = quotient_graph(PartitionedView(g, partitioned_vertices)) return PartitionedGraph( g, qg, - map(v -> [v;], Dictionary(partitioned_vertices)), + to_partitioned_vertices(partitioned_vertices), which_partition ) end diff --git a/src/lib/PartitionedGraphs/src/partitionedview.jl b/src/lib/PartitionedGraphs/src/partitionedview.jl new file mode 100644 index 0000000..f8ebad5 --- /dev/null +++ b/src/lib/PartitionedGraphs/src/partitionedview.jl @@ -0,0 +1,27 @@ +using ..OrderedDictionaries: OrderedIndices + +struct PartitionedView{V, PV, G <: AbstractGraph{V}} <: AbstractPartitionedGraph{V, PV} + graph::G + partitioned_vertices::Dictionary{PV, Vector{V}} +end + +PartitionedView(g, parts) = PartitionedView(g, to_partitioned_vertices(parts)) + +# Entry point +to_partitioned_vertices(pvs) = to_partitioned_vertices(eltype(pvs), pvs) + +to_partitioned_vertices(::Type, pvs) = to_partitioned_vertices(map(v -> [v;], pvs)) +function to_partitioned_vertices(::Type{<:OrderedIndices}, pvs) + iter_of_vecs = map(pvs) do oi + rv = Vector{eltype(oi)}(undef, length(oi)) + copyto!(rv, oi) + return rv + end + return to_partitioned_vertices(iter_of_vecs) +end + +# Exit point +to_partitioned_vertices(::Type{<:Vector}, pvs) = Dictionary(pvs) + +unpartitioned_graph(pv::PartitionedView) = getfield(pv, :graph) +partitioned_vertices(pv::PartitionedView) = getfield(pv, :partitioned_vertices) From ef7dffa0e79f0872526a2a707bc0039f2de51025 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 09:57:31 -0500 Subject: [PATCH 15/36] Remove unnecessary type parameter in `PartitionedGraph` --- src/lib/PartitionedGraphs/src/partitionedgraph.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index 413966e..2ba00e8 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -7,10 +7,10 @@ using ..NamedGraphs.OrderedDictionaries: OrderedDictionary # TODO: Parametrize `partitioned_vertices` and `which_partition`, # see https://github.com/mtfishman/NamedGraphs.jl/issues/63. -struct PartitionedGraph{V, PV, G <: AbstractGraph{V}, NV} <: AbstractPartitionedGraph{V, PV} +struct PartitionedGraph{V, PV, G <: AbstractGraph{V}} <: AbstractPartitionedGraph{V, PV} graph::G quotient_graph::NamedGraph{PV} - partitioned_vertices::Dictionary{PV, Vector{NV}} + partitioned_vertices::Dictionary{PV, Vector{V}} which_partition::Dictionary{V, PV} end From d1844d42e2a4548ba4af1d6efa5e6d3bda561a57 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 10:03:49 -0500 Subject: [PATCH 16/36] Rename `partitions` -> `partition_vertices` --- ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl | 4 ++-- ext/NamedGraphsMetisExt/NamedGraphsMetisExt.jl | 4 ++-- src/abstractnamedgraph.jl | 6 +++--- src/lib/GraphsExtensions/src/partitioning.jl | 4 ++-- src/lib/PartitionedGraphs/src/partitionedgraph.jl | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl b/ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl index 25756d2..caa731e 100644 --- a/ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl +++ b/ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl @@ -27,14 +27,14 @@ const KAHYPAR_ALGS = Dict( ) """ -partitions(::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" - :connectivity => "km1_kKaHyPar_sea20.ini" - imbalance::Number=0.03 """ -function GraphsExtensions.partitions( +function GraphsExtensions.partition_vertices( ::Backend"kahypar", g::AbstractSimpleGraph, npartitions::Integer; diff --git a/ext/NamedGraphsMetisExt/NamedGraphsMetisExt.jl b/ext/NamedGraphsMetisExt/NamedGraphsMetisExt.jl index df51ba1..89682d9 100644 --- a/ext/NamedGraphsMetisExt/NamedGraphsMetisExt.jl +++ b/ext/NamedGraphsMetisExt/NamedGraphsMetisExt.jl @@ -10,14 +10,14 @@ GraphsExtensions.set_partitioning_backend!(Backend"metis"()) const METIS_ALGS = Dict(["kway" => :KWAY, "recursive" => :RECURSIVE]) """ - partitions(::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: - :KWAY: multilevel k-way partitioning - :RECURSIVE: multilevel recursive bisection """ -function GraphsExtensions.partitions( +function GraphsExtensions.partition_vertices( ::Backend"metis", g::AbstractSimpleGraph, npartitions::Integer; alg = "recursive", kwargs... ) metis_alg = METIS_ALGS[alg] diff --git a/src/abstractnamedgraph.jl b/src/abstractnamedgraph.jl index 8ae61c2..f196bf9 100644 --- a/src/abstractnamedgraph.jl +++ b/src/abstractnamedgraph.jl @@ -32,7 +32,7 @@ using .GraphsExtensions: GraphsExtensions, directed_graph, incident_edges, - partitions, + partition_vertices, rename_vertices, subgraph using SimpleTraits: SimpleTraits, Not, @traitfn @@ -286,10 +286,10 @@ function Graphs.mincut(graph::AbstractNamedGraph, distmx::AbstractMatrix{<:Real} end # TODO: Make this more generic? -function GraphsExtensions.partitions( +function GraphsExtensions.partition_vertices( graph::AbstractNamedGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... ) - vertex_partitions = partitions( + vertex_partitions = partition_vertices( position_graph(graph); npartitions, nvertices_per_partition, kwargs... ) # TODO: output the reverse of this dictionary (a Vector of Vector diff --git a/src/lib/GraphsExtensions/src/partitioning.jl b/src/lib/GraphsExtensions/src/partitioning.jl index 267c005..7e88c61 100644 --- a/src/lib/GraphsExtensions/src/partitioning.jl +++ b/src/lib/GraphsExtensions/src/partitioning.jl @@ -54,7 +54,7 @@ function _npartitions( return error("Must specify either `npartitions` or `nvertices_per_partition`") end -function partitions( +function partition_vertices( g::AbstractSimpleGraph; npartitions = nothing, nvertices_per_partition = nothing, @@ -66,7 +66,7 @@ function partitions( if (_npartitions(g, npartitions, nvertices_per_partition) == 1) return group(v -> 1, collect(vertices(g))) end - return partitions( + return partition_vertices( Backend(backend), g, _npartitions(g, npartitions, nvertices_per_partition); kwargs... ) end diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index 2ba00e8..40cf10b 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -1,8 +1,8 @@ using Dictionaries: Dictionary using Graphs: AbstractEdge, AbstractGraph, add_edge!, edges, has_edge, induced_subgraph, vertices, dst, src, edgetype -using .GraphsExtensions: GraphsExtensions, boundary_edges, is_self_loop, partitions using ..NamedGraphs: NamedEdge, NamedGraph +using ..NamedGraphs.GraphsExtensions: GraphsExtensions, boundary_edges, is_self_loop, partition_vertices using ..NamedGraphs.OrderedDictionaries: OrderedDictionary # TODO: Parametrize `partitioned_vertices` and `which_partition`, @@ -46,7 +46,7 @@ function PartitionedGraph(partitioned_vertices) end function PartitionedGraph(g::AbstractGraph; kwargs...) - partitioned_verts = partitions(g; kwargs...) + partitioned_verts = partition_vertices(g; kwargs...) return PartitionedGraph(g, partitioned_verts) end From f1a01d9ecf4234af06f5a0ded07d5eaf16e9016b Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 10:06:09 -0500 Subject: [PATCH 17/36] Rename `find_quotient_vertex` and `find_quotient_edge` to `quotient_vertex` and `quotient_edge`. --- .../src/abstractpartitionedgraph.jl | 14 +++++++------- src/lib/PartitionedGraphs/src/partitionedgraph.jl | 8 ++++---- src/lib/PartitionedGraphs/src/superedge.jl | 2 +- src/lib/PartitionedGraphs/src/supervertex.jl | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index d2d8031..ef314fe 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -25,8 +25,8 @@ function quotient_graph(g::AbstractGraph) qg = NamedGraph(quotient_vertices(g)) for e in edges(g) - qv_src = find_quotient_vertex(g, src(e)) - qv_dst = find_quotient_vertex(g, dst(e)) + qv_src = quotient_vertex(g, src(e)) + qv_dst = quotient_vertex(g, dst(e)) qe = NamedEdge(qv_src => qv_dst) if qv_src != qv_dst && !has_edge(qg, qe) add_edge!(qg, qe) @@ -37,7 +37,7 @@ function quotient_graph(g::AbstractGraph) end # Overload this for fast inverse mapping for vertices and edges -function find_quotient_vertex(g, vertex) +function quotient_vertex(g, vertex) pvs = partitioned_vertices(g) rv = findfirst(pv -> vertex ∈ pv, pvs) if isnothing(rv) @@ -46,12 +46,12 @@ function find_quotient_vertex(g, vertex) return rv end -function find_quotient_edge(g::AbstractGraph, edge) +function quotient_edge(g::AbstractGraph, edge) if !has_edge(g, edge) throw(ArgumentError("Graph does not have an edge $edge")) end - qv_src = find_quotient_vertex(g, src(edge)) - qv_dst = find_quotient_vertex(g, dst(edge)) + qv_src = quotient_vertex(g, src(edge)) + qv_dst = quotient_vertex(g, dst(edge)) return quotient_edgetype(g)(qv_src => qv_dst) end @@ -61,7 +61,7 @@ function partitioned_edges(g::AbstractGraph) for e in edges(g) - qe = find_quotient_edge(g, e) + qe = quotient_edge(g, e) if is_self_loop(qe) continue diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index 40cf10b..8511642 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -17,7 +17,7 @@ end # Interface overloads partitioned_vertices(pg::PartitionedGraph) = pg.partitioned_vertices quotient_graph(pg::PartitionedGraph) = pg.quotient_graph -find_quotient_vertex(pg::PartitionedGraph, vertex) = pg.which_partition[vertex] +quotient_vertex(pg::PartitionedGraph, vertex) = pg.which_partition[vertex] quotient_graph_type(::Type{<:AbstractPartitionedGraph{V, PV}}) where {V, PV} = NamedGraph{PV} @@ -80,7 +80,7 @@ function insert_to_vertex_map!( end function delete_from_vertex_map!(pg::PartitionedGraph{V}, vertex::V) where {V} - sv = find_quotient_vertex(pg, vertex) + sv = quotient_vertex(pg, vertex) return delete_from_vertex_map!(pg, sv, vertex) end @@ -107,7 +107,7 @@ function delete_from_vertex_map!( end function Graphs.rem_vertex!(pg::PartitionedGraph{V}, vertex::V) where {V} - qv = find_quotient_vertex(pg, vertex) + qv = quotient_vertex(pg, vertex) delete_from_vertex_map!(pg, qv, vertex) @@ -131,7 +131,7 @@ end function Graphs.add_edge!(pg::PartitionedGraph, edge::AbstractEdge) @assert edge isa edgetype(pg) add_edge!(pg.graph, edge) - pg_edge = find_quotient_edge(pg, edge) + pg_edge = quotient_edge(pg, edge) if src(pg_edge) != dst(pg_edge) add_edge!(pg.quotient_graph, pg_edge) end diff --git a/src/lib/PartitionedGraphs/src/superedge.jl b/src/lib/PartitionedGraphs/src/superedge.jl index 2ed1e09..d6cd18b 100644 --- a/src/lib/PartitionedGraphs/src/superedge.jl +++ b/src/lib/PartitionedGraphs/src/superedge.jl @@ -6,7 +6,7 @@ struct SuperEdge{V, E <: AbstractEdge{V}} <: AbstractNamedEdge{V} edge::E end -superedge(pg::AbstractGraph, edge::AbstractEdge) = SuperEdge(find_quotient_edge(pg, edge)) +superedge(pg::AbstractGraph, edge::AbstractEdge) = SuperEdge(quotient_edge(pg, edge)) superedge(pg::AbstractGraph, p::Pair) = superedge(pg, edgetype(pg)(p)) """ diff --git a/src/lib/PartitionedGraphs/src/supervertex.jl b/src/lib/PartitionedGraphs/src/supervertex.jl index 4b5bae4..1a43fcd 100644 --- a/src/lib/PartitionedGraphs/src/supervertex.jl +++ b/src/lib/PartitionedGraphs/src/supervertex.jl @@ -8,7 +8,7 @@ end Base.parent(sv::SuperVertex) = getfield(sv, :vertex) -supervertex(g::AbstractGraph, vertex) = SuperVertex(find_quotient_vertex(g, vertex)) +supervertex(g::AbstractGraph, vertex) = SuperVertex(quotient_vertex(g, vertex)) """ supervertices(g::AbstractGraph, vs = vertices(pg)) From 4436bc2674c2d9d67c03c9c16ed0bdf3f8d00147 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 10:06:56 -0500 Subject: [PATCH 18/36] Remove commented out line of code This default now occurs in `partitioned_vertices`. --- src/lib/GraphsExtensions/src/partitioning.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/GraphsExtensions/src/partitioning.jl b/src/lib/GraphsExtensions/src/partitioning.jl index 7e88c61..0691e30 100644 --- a/src/lib/GraphsExtensions/src/partitioning.jl +++ b/src/lib/GraphsExtensions/src/partitioning.jl @@ -70,5 +70,3 @@ function partition_vertices( Backend(backend), g, _npartitions(g, npartitions, nvertices_per_partition); kwargs... ) end - -# partitionings(g::AbstractGraph) = [vertices(g)] From 2290dbf0c9627b9d4082e97bc0d459d9b46b7773 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 10:08:50 -0500 Subject: [PATCH 19/36] Fix type of in code comment and drop explicit `NamedEdge` construction. --- src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index ef314fe..ebb2d31 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -15,7 +15,7 @@ using ..NamedGraphs: NamedGraphs, AbstractNamedGraph using ..NamedGraphs.GraphsExtensions: GraphsExtensions, add_vertices!, not_implemented, rem_vertices!, subgraph, vertextype -# For you own graph type `g`, you should define a method for this function is you +# For you own graph type `g`, you should define a method for this function if you # desire custom partitioning. partitioned_vertices(g::AbstractGraph) = [vertices(g)] @@ -27,7 +27,7 @@ function quotient_graph(g::AbstractGraph) for e in edges(g) qv_src = quotient_vertex(g, src(e)) qv_dst = quotient_vertex(g, dst(e)) - qe = NamedEdge(qv_src => qv_dst) + qe = qv_src => qv_dst if qv_src != qv_dst && !has_edge(qg, qe) add_edge!(qg, qe) end From a4fc5e9498ea71ee52e26ee760f44ee34cb60ab7 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 10:37:12 -0500 Subject: [PATCH 20/36] Fix definition of `is_boundary_edge` to align with function name; remove type annotation --- src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index ebb2d31..9dd62e2 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -79,9 +79,9 @@ function quotient_vertices(g) end quotient_edges(g::AbstractGraph) = edges(quotient_graph(g)) -function is_boundary_edge(pg::AbstractGraph, edge::AbstractEdge) +function is_boundary_edge(pg::AbstractGraph, edge) p_edge = superedge(pg, edge) - return src(p_edge) == dst(p_edge) + return src(p_edge) != dst(p_edge) end function boundary_superedges(pg::AbstractGraph, supervertices; kwargs...) From 66040f659bf3b484d4a2a1d9803b9603fb9a0de5 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 10:52:33 -0500 Subject: [PATCH 21/36] Add `SubSuperVertex` type for adding vertices to partition graphs - `add_vertex!` not takes the `SuperVertex` argument before the regular vertex argument. - Updated tests to reflect the interface change. --- src/lib/PartitionedGraphs/src/PartitionedGraphs.jl | 1 + src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl | 4 ++++ src/lib/PartitionedGraphs/src/partitionedgraph.jl | 2 +- src/lib/PartitionedGraphs/src/subsupervertex.jl | 6 ++++++ test/test_partitionedgraph.jl | 2 +- 5 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 src/lib/PartitionedGraphs/src/subsupervertex.jl diff --git a/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl b/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl index 8dbf4b3..03803bd 100644 --- a/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl +++ b/src/lib/PartitionedGraphs/src/PartitionedGraphs.jl @@ -1,5 +1,6 @@ module PartitionedGraphs include("supervertex.jl") +include("subsupervertex.jl") include("superedge.jl") include("abstractpartitionedgraph.jl") include("partitionedview.jl") diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index 9dd62e2..b7c5afd 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -174,6 +174,10 @@ function Graphs.add_vertex!(::AbstractPartitionedGraph, vertex) return error("Need to specify a partition where the new vertex will go.") end +function Graphs.add_vertex!(pg::AbstractPartitionedGraph, ssv::SubSuperVertex) + return add_vertex!(pg, ssv.vertex, ssv.subvertex) +end + function Base.:(==)(pg1::AbstractPartitionedGraph, pg2::AbstractPartitionedGraph) if unpartitioned_graph(pg1) != unpartitioned_graph(pg2) || QuotientView(pg1) != QuotientView(pg2) diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index 8511642..a6cf0a2 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -121,7 +121,7 @@ function Graphs.rem_vertex!(pg::PartitionedGraph{V}, vertex::V) where {V} return pg end -function Graphs.add_vertex!(pg::PartitionedGraph{V}, vertex::V, sv::SuperVertex) where {V} +function Graphs.add_vertex!(pg::PartitionedGraph{V}, sv::SuperVertex, vertex::V) where {V} add_vertex!(pg.graph, vertex) add_vertex!(pg.quotient_graph, parent(sv)) insert_to_vertex_map!(pg, vertex, sv) diff --git a/src/lib/PartitionedGraphs/src/subsupervertex.jl b/src/lib/PartitionedGraphs/src/subsupervertex.jl new file mode 100644 index 0000000..ac3597a --- /dev/null +++ b/src/lib/PartitionedGraphs/src/subsupervertex.jl @@ -0,0 +1,6 @@ +struct SubSuperVertex{V, SV} + vertex::SuperVertex{V} + subvertex::SV +end + +Base.getindex(sv::SuperVertex, v) = SubSuperVertex(sv, v) diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index 5c50589..e30e639 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -156,7 +156,7 @@ end @test !is_tree(QuotientView(pg)) #Add the column back to the in place graph - add_vertices!(pg, v_set, pv) + add_vertices!(pg, map(v -> pv[v], v_set)) add_edges!(pg, edges_involving_v_set) @test is_connected(pg.graph) @test is_path_graph(QuotientView(pg)) From 2951717257efb936e8a366a657dc388e54f1a471 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 12:09:14 -0500 Subject: [PATCH 22/36] Fix issues with functions `ne` and `nv` when used with `SuperVertex` and `SuperEdge` args. --- src/abstractnamedgraph.jl | 11 ++++------- src/lib/PartitionedGraphs/src/superedge.jl | 5 ++++- src/lib/PartitionedGraphs/src/supervertex.jl | 7 ++++++- test/test_partitionedgraph.jl | 8 +++++++- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/abstractnamedgraph.jl b/src/abstractnamedgraph.jl index f196bf9..cb75a44 100644 --- a/src/abstractnamedgraph.jl +++ b/src/abstractnamedgraph.jl @@ -447,13 +447,10 @@ function Graphs.blockdiag(graph1::AbstractNamedGraph, graph2::AbstractNamedGraph return GenericNamedGraph(new_position_graph, new_vertices) end -# TODO: What `args` are needed? -Graphs.nv(graph::AbstractNamedGraph, args...) = nv(position_graph(graph), args...) -# TODO: What `args` are needed? -Graphs.ne(graph::AbstractNamedGraph, args...) = ne(position_graph(graph), args...) -# TODO: What `args` are needed? -function Graphs.adjacency_matrix(graph::AbstractNamedGraph, args...) - return adjacency_matrix(position_graph(graph), args...) +Graphs.nv(graph::AbstractNamedGraph) = nv(position_graph(graph)) +Graphs.ne(graph::AbstractNamedGraph) = ne(position_graph(graph)) +function Graphs.adjacency_matrix(graph::AbstractNamedGraph) + return adjacency_matrix(position_graph(graph)) end function Graphs.connected_components(graph::AbstractNamedGraph) diff --git a/src/lib/PartitionedGraphs/src/superedge.jl b/src/lib/PartitionedGraphs/src/superedge.jl index d6cd18b..be34605 100644 --- a/src/lib/PartitionedGraphs/src/superedge.jl +++ b/src/lib/PartitionedGraphs/src/superedge.jl @@ -42,6 +42,9 @@ function Graphs.edges(pg::AbstractGraph, superedge::SuperEdge) if !is_directed(quotient_graph_type(pg)) && isempty(rv) append!(rv, get(pes, reverse(parent(superedge)), defval)) end + + isempty(rv) && throw(ArgumentError("Super edge $superedge not in graph")) + return rv end function Graphs.edges(pg::AbstractGraph, superedges::Vector{<:SuperEdge}) @@ -50,7 +53,7 @@ end has_superedge(g::AbstractGraph, se::SuperEdge) = has_edge(quotient_graph(g), parent(se)) -Graphs.ne(g::AbstractGraph, se::SuperEdge) = length(quotient_edges(g, se)) +Graphs.ne(g::AbstractGraph, se::SuperEdge) = length(edges(g, se)) function GraphsExtensions.rem_edges!(g::AbstractGraph, sv::SuperEdge) rv = rem_edges!(g, edges(g, sv)) diff --git a/src/lib/PartitionedGraphs/src/supervertex.jl b/src/lib/PartitionedGraphs/src/supervertex.jl index 1a43fcd..4b9f8d1 100644 --- a/src/lib/PartitionedGraphs/src/supervertex.jl +++ b/src/lib/PartitionedGraphs/src/supervertex.jl @@ -26,7 +26,12 @@ Return the set of vertices in the graph `g` associated with the super vertex `supervertex` or set of super vertices `supervertices`. """ function Graphs.vertices(g::AbstractGraph, supervertex::SuperVertex) - return partitioned_vertices(g)[parent(supervertex)] + qv = parent(supervertex) + + pvs = partitioned_vertices(g) + haskey(pvs, qv) || throw(ArgumentError("Super vertex $supervertex not in graph")) + + return pvs[qv] end function Graphs.vertices(g::AbstractGraph, supervertices::Vector{<:SuperVertex}) return unique(mapreduce(sv -> vertices(g, sv), vcat, supervertices)) diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index e30e639..d269ec4 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -49,7 +49,7 @@ using NamedGraphs.PartitionedGraphs: has_supervertex using Dictionaries: Dictionary, dictionary using Pkg: Pkg -using Test: @test, @testset +using Test: @test, @testset, @test_throws @testset "Test Partitioned Graph Constructors" begin nx, ny = 10, 10 @@ -92,7 +92,13 @@ using Test: @test, @testset ) @test is_tree(QuotientView(pg)) @test nv(pg) == nx * ny + @test nv(pg, SuperVertex((1, 1))) == ny + @test_throws ArgumentError nv(pg, SuperVertex((11,11))) @test nv(QuotientView(pg)) == nx + @test ne(pg) == (nx - 1) * ny + nx * (ny - 1) + @test ne(pg, SuperEdge((1, 1) => (2, 1))) == ny + @test_throws ArgumentError ne(pg, SuperEdge((1, 1) => (1, 2))) + @test ne(QuotientView(pg)) == 9 pg_c = copy(pg) @test pg_c == pg From c541c2056efbe86e9588aa173b5944aa7e047566 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 12:23:52 -0500 Subject: [PATCH 23/36] Parmaterise directly on the type of the partition mapping. --- .../PartitionedGraphs/src/partitionedgraph.jl | 6 ++--- .../PartitionedGraphs/src/partitionedview.jl | 24 ++++--------------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index a6cf0a2..2690015 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -7,10 +7,10 @@ using ..NamedGraphs.OrderedDictionaries: OrderedDictionary # TODO: Parametrize `partitioned_vertices` and `which_partition`, # see https://github.com/mtfishman/NamedGraphs.jl/issues/63. -struct PartitionedGraph{V, PV, G <: AbstractGraph{V}} <: AbstractPartitionedGraph{V, PV} +struct PartitionedGraph{V, PV, G <: AbstractGraph{V}, P} <: AbstractPartitionedGraph{V, PV} graph::G quotient_graph::NamedGraph{PV} - partitioned_vertices::Dictionary{PV, Vector{V}} + partitioned_vertices::P which_partition::Dictionary{V, PV} end @@ -36,7 +36,7 @@ function PartitionedGraph(g::AbstractGraph{V}, partitioned_vertices) where {V} return PartitionedGraph( g, qg, - to_partitioned_vertices(partitioned_vertices), + Dictionary(partitioned_vertices), which_partition ) end diff --git a/src/lib/PartitionedGraphs/src/partitionedview.jl b/src/lib/PartitionedGraphs/src/partitionedview.jl index f8ebad5..e6ee80b 100644 --- a/src/lib/PartitionedGraphs/src/partitionedview.jl +++ b/src/lib/PartitionedGraphs/src/partitionedview.jl @@ -1,27 +1,13 @@ using ..OrderedDictionaries: OrderedIndices -struct PartitionedView{V, PV, G <: AbstractGraph{V}} <: AbstractPartitionedGraph{V, PV} +struct PartitionedView{V, PV, G <: AbstractGraph{V}, P} <: AbstractPartitionedGraph{V, PV} graph::G - partitioned_vertices::Dictionary{PV, Vector{V}} -end - -PartitionedView(g, parts) = PartitionedView(g, to_partitioned_vertices(parts)) - -# Entry point -to_partitioned_vertices(pvs) = to_partitioned_vertices(eltype(pvs), pvs) - -to_partitioned_vertices(::Type, pvs) = to_partitioned_vertices(map(v -> [v;], pvs)) -function to_partitioned_vertices(::Type{<:OrderedIndices}, pvs) - iter_of_vecs = map(pvs) do oi - rv = Vector{eltype(oi)}(undef, length(oi)) - copyto!(rv, oi) - return rv + partitioned_vertices::P + function PartitionedView(graph::G, partitioned_vertices::P) where {V, G <: AbstractGraph{V}, P} + PV = keytype(partitioned_vertices) + return new{V, PV, G, P}(graph, partitioned_vertices) end - return to_partitioned_vertices(iter_of_vecs) end -# Exit point -to_partitioned_vertices(::Type{<:Vector}, pvs) = Dictionary(pvs) - unpartitioned_graph(pv::PartitionedView) = getfield(pv, :graph) partitioned_vertices(pv::PartitionedView) = getfield(pv, :partitioned_vertices) From d27294e9e617ec9dcf3cef0f8ece3696a7c08a09 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 12:28:01 -0500 Subject: [PATCH 24/36] Remove 3-arg `add_vertex!` method in favour of `add_subsupervertex!` interface function Also removed some dead code. --- .../src/abstractpartitionedgraph.jl | 22 +------------------ .../PartitionedGraphs/src/partitionedgraph.jl | 2 +- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index b7c5afd..2599572 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -148,26 +148,6 @@ function NamedGraphs.ordered_vertices(pg::AbstractPartitionedGraph) end Graphs.edgetype(pg::AbstractPartitionedGraph) = edgetype(unpartitioned_graph(pg)) -function GraphsExtensions.add_vertices!( - pg::AbstractPartitionedGraph, - vertices::Vector, - supervertices::Vector{<:SuperVertex}, - ) - @assert length(vertices) == length(supervertices) - for (v, sv) in zip(vertices, supervertices) - add_vertex!(pg, v, sv) - end - - return pg -end - -function GraphsExtensions.add_vertices!( - pg::AbstractPartitionedGraph, vertices::Vector, supervertex::SuperVertex - ) - add_vertices!(pg, vertices, fill(supervertex, length(vertices))) - return pg -end - Graphs.rem_vertex!(::AbstractPartitionedGraph{V}, vertex::V) where {V} = not_implemented() function Graphs.add_vertex!(::AbstractPartitionedGraph, vertex) @@ -175,7 +155,7 @@ function Graphs.add_vertex!(::AbstractPartitionedGraph, vertex) end function Graphs.add_vertex!(pg::AbstractPartitionedGraph, ssv::SubSuperVertex) - return add_vertex!(pg, ssv.vertex, ssv.subvertex) + return add_subsupervertex!(pg, ssv.vertex, ssv.subvertex) end function Base.:(==)(pg1::AbstractPartitionedGraph, pg2::AbstractPartitionedGraph) diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index 2690015..285a4a2 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -121,7 +121,7 @@ function Graphs.rem_vertex!(pg::PartitionedGraph{V}, vertex::V) where {V} return pg end -function Graphs.add_vertex!(pg::PartitionedGraph{V}, sv::SuperVertex, vertex::V) where {V} +function add_subsupervertex!(pg::PartitionedGraph{V}, sv::SuperVertex, vertex::V) where {V} add_vertex!(pg.graph, vertex) add_vertex!(pg.quotient_graph, parent(sv)) insert_to_vertex_map!(pg, vertex, sv) From 52c97a7ded96f9a7c2a920b61dd4e1cd797cb9ae Mon Sep 17 00:00:00 2001 From: Jack Dunham <72548217+jack-dunham@users.noreply.github.com> Date: Tue, 11 Nov 2025 13:43:50 -0500 Subject: [PATCH 25/36] Fix docstring indent. Co-authored-by: Matt Fishman --- ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl b/ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl index caa731e..7e31afe 100644 --- a/ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl +++ b/ext/NamedGraphsKaHyParExt/NamedGraphsKaHyParExt.jl @@ -27,7 +27,7 @@ const KAHYPAR_ALGS = Dict( ) """ -partition_vertices(::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" From 0a4da53441d40ce58e99fe2e752dfb191515eccf Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 14:09:57 -0500 Subject: [PATCH 26/36] Rename `is_boundary_edge` to `is_partition_boundary_edge`. --- src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index 2599572..614b6a8 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -79,7 +79,7 @@ function quotient_vertices(g) end quotient_edges(g::AbstractGraph) = edges(quotient_graph(g)) -function is_boundary_edge(pg::AbstractGraph, edge) +function is_partition_boundary_edge(pg::AbstractGraph, edge) p_edge = superedge(pg, edge) return src(p_edge) != dst(p_edge) end From 623beae6c75f72000833bfd405e89bd003a10e3d Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 15:02:54 -0500 Subject: [PATCH 27/36] Define `induced_subgraph` method on super-vertices generically. --- .../src/abstractpartitionedgraph.jl | 10 ++----- .../PartitionedGraphs/src/partitionedgraph.jl | 13 +++++----- .../PartitionedGraphs/src/partitionedview.jl | 2 +- src/lib/PartitionedGraphs/src/supervertex.jl | 26 +++++-------------- src/namedgraph.jl | 13 +++++++--- 5 files changed, 25 insertions(+), 39 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index 614b6a8..6541697 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -171,12 +171,6 @@ function Base.:(==)(pg1::AbstractPartitionedGraph, pg2::AbstractPartitionedGraph return true end -function GraphsExtensions.subgraph(pg::AbstractPartitionedGraph, supervertex::SuperVertex) - return first(induced_subgraph(unpartitioned_graph(pg), vertices(pg, supervertex))) -end - -function Graphs.induced_subgraph( - pg::AbstractPartitionedGraph, supervertex::SuperVertex - ) - return subgraph(pg, supervertex), nothing +function NamedGraphs._induced_subgraph(pg::AbstractPartitionedGraph, vlist) + return NamedGraphs._induced_subgraph(unpartitioned_graph(pg), vlist) end diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index 285a4a2..1629103 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -1,7 +1,7 @@ using Dictionaries: Dictionary using Graphs: AbstractEdge, AbstractGraph, add_edge!, edges, has_edge, induced_subgraph, vertices, dst, src, edgetype -using ..NamedGraphs: NamedEdge, NamedGraph +using ..NamedGraphs: NamedGraphs, NamedEdge, NamedGraph using ..NamedGraphs.GraphsExtensions: GraphsExtensions, boundary_edges, is_self_loop, partition_vertices using ..NamedGraphs.OrderedDictionaries: OrderedDictionary @@ -153,11 +153,11 @@ function Graphs.rem_edge!(pg::PartitionedGraph, edge::AbstractEdge) end ### PartitionedGraph Specific Functions -function partitionedgraph_induced_subgraph(pg::PartitionedGraph, vertices::Vector) - sub_pg_graph, _ = induced_subgraph(unpartitioned_graph(pg), vertices) +function partitionedgraph_induced_subgraph(pg::PartitionedGraph, vlist) + sub_pg_graph, _ = induced_subgraph(unpartitioned_graph(pg), vlist) sub_partitioned_vertices = copy(partitioned_vertices(pg)) for pv in quotient_vertices(pg) - vs = intersect(vertices, sub_partitioned_vertices[pv]) + vs = intersect(vlist, sub_partitioned_vertices[pv]) if !isempty(vs) sub_partitioned_vertices[pv] = vs else @@ -168,7 +168,6 @@ function partitionedgraph_induced_subgraph(pg::PartitionedGraph, vertices::Vecto return PartitionedGraph(sub_pg_graph, sub_partitioned_vertices), nothing end -# Fixes ambiguity error with `Graphs.jl`. -function Graphs.induced_subgraph(pg::PartitionedGraph, vertices::Vector{<:Integer}) - return partitionedgraph_induced_subgraph(pg, vertices) +function NamedGraphs._induced_subgraph(pg::PartitionedGraph, vlist) + return partitionedgraph_induced_subgraph(pg, vlist) end diff --git a/src/lib/PartitionedGraphs/src/partitionedview.jl b/src/lib/PartitionedGraphs/src/partitionedview.jl index e6ee80b..7fc7514 100644 --- a/src/lib/PartitionedGraphs/src/partitionedview.jl +++ b/src/lib/PartitionedGraphs/src/partitionedview.jl @@ -3,7 +3,7 @@ using ..OrderedDictionaries: OrderedIndices struct PartitionedView{V, PV, G <: AbstractGraph{V}, P} <: AbstractPartitionedGraph{V, PV} graph::G partitioned_vertices::P - function PartitionedView(graph::G, partitioned_vertices::P) where {V, G <: AbstractGraph{V}, P} + function PartitionedView(graph::G, partitioned_vertices::P) where {V, G <: AbstractGraph{V}, P} PV = keytype(partitioned_vertices) return new{V, PV, G, P}(graph, partitioned_vertices) end diff --git a/src/lib/PartitionedGraphs/src/supervertex.jl b/src/lib/PartitionedGraphs/src/supervertex.jl index 4b9f8d1..96c697d 100644 --- a/src/lib/PartitionedGraphs/src/supervertex.jl +++ b/src/lib/PartitionedGraphs/src/supervertex.jl @@ -1,6 +1,6 @@ -using Graphs: AbstractGraph, Graphs, nv -using ..NamedGraphs: AbstractNamedGraph -using ..NamedGraphs.GraphsExtensions: GraphsExtensions, rem_vertices! +using Graphs: AbstractGraph, Graphs, nv, induced_subgraph +using ..NamedGraphs: NamedGraphs, AbstractNamedGraph +using ..NamedGraphs.GraphsExtensions: GraphsExtensions, rem_vertices!, subgraph struct SuperVertex{V} vertex::V @@ -44,23 +44,11 @@ end Graphs.nv(g::AbstractGraph, sv::SuperVertex) = length(vertices(g, sv)) -function Graphs.induced_subgraph( - g::AbstractGraph, supervertices::Union{SuperVertex, Vector{<:SuperVertex}} - ) - return induced_subgraph(g, vertices(g, supervertices)) -end -function Graphs.induced_subgraph( - g::AbstractNamedGraph, svs::Union{SuperVertex, Vector{<:SuperVertex}} - ) - gsvs = supervertices(g) - if length(setdiff(gsvs, [svs;])) == length(gsvs) - throw(ArgumentError("One or more supervertices not found in graph")) - end - return induced_subgraph(g, vertices(g, svs)) -end - - function GraphsExtensions.rem_vertices!(g::AbstractGraph, sv::SuperVertex) return rem_vertices!(g, vertices(g, sv)) end rem_supervertex!(pg::AbstractGraph, sv::SuperVertex) = rem_vertices!(pg, sv) + +function NamedGraphs.to_vertices(g::AbstractGraph, sv::Union{SV, Vector{SV}}) where {SV <: SuperVertex} + return vertices(g, sv) +end diff --git a/src/namedgraph.jl b/src/namedgraph.jl index b54ec66..5a6eaab 100644 --- a/src/namedgraph.jl +++ b/src/namedgraph.jl @@ -81,6 +81,7 @@ end # Constructors from `AbstractSimpleGraph` # +to_vertices(graph, vertices) = to_vertices(vertices) to_vertices(vertices) = vertices to_vertices(vertices::AbstractArray) = vec(vertices) to_vertices(vertices::Integer) = Base.OneTo(vertices) @@ -97,7 +98,7 @@ function GenericNamedGraph{V, G}( position_graph::AbstractSimpleGraph, vertices ) where {V, G <: AbstractSimpleGraph{Int}} return GenericNamedGraph{V, G}( - convert(G, position_graph), OrderedIndices{V}(to_vertices(vertices)) + convert(G, position_graph), OrderedIndices{V}(to_vertices(position_graph, vertices)) ) end @@ -228,11 +229,15 @@ function namedgraph_induced_subgraph(graph::AbstractGraph, subvertices) return subgraph, nothing end -function Graphs.induced_subgraph(graph::AbstractNamedGraph, subvertices) - return namedgraph_induced_subgraph(graph, subvertices) +function Graphs.induced_subgraph(graph::AbstractNamedGraph, vlist) + return _induced_subgraph(graph, to_vertices(graph, vlist)) +end +# For method ambiguity resolution with Graphs.jl +function Graphs.induced_subgraph(graph::AbstractNamedGraph, vlist::AbstractVector{<:Integer}) + return _induced_subgraph(graph, to_vertices(graph, vlist)) end -function Graphs.induced_subgraph(graph::AbstractNamedGraph, subvertices::Vector{<:Integer}) +function _induced_subgraph(graph::AbstractNamedGraph, subvertices) return namedgraph_induced_subgraph(graph, subvertices) end From abfda682fa59527701328dc529aa45e6029c7fee Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 15:05:52 -0500 Subject: [PATCH 28/36] Code comment styling. --- src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index 6541697..89afa90 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -109,7 +109,7 @@ an underlying graph *without* any partitioning. One should also define: """ abstract type AbstractPartitionedGraph{V, PV} <: AbstractNamedGraph{V} end -#Needed for interface +# Required for interface unpartitioned_graph(::AbstractPartitionedGraph) = not_implemented() Base.copy(::AbstractPartitionedGraph) = not_implemented() From d330d3fff0f5a4da5c3a1c2fac076228d4b25503 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 16:28:27 -0500 Subject: [PATCH 29/36] Runic formatting --- src/abstractnamedgraph.jl | 112 +++++++++++++++++----------------- test/test_partitionedgraph.jl | 4 +- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/abstractnamedgraph.jl b/src/abstractnamedgraph.jl index cb75a44..0efe375 100644 --- a/src/abstractnamedgraph.jl +++ b/src/abstractnamedgraph.jl @@ -92,8 +92,8 @@ GraphsExtensions.convert_vertextype(::Type, ::AbstractNamedGraph) = not_implemen Base.copy(graph::AbstractNamedGraph) = not_implemented() function Graphs.merge_vertices!( - graph::AbstractNamedGraph, merge_vertices; merged_vertex=first(merge_vertices) -) + graph::AbstractNamedGraph, merge_vertices; merged_vertex = first(merge_vertices) + ) return not_implemented() end @@ -145,11 +145,11 @@ end # TODO: write in terms of a generic function. for f in [ - :(Graphs.outneighbors), - :(Graphs.inneighbors), - :(Graphs.all_neighbors), - :(Graphs.neighbors), -] + :(Graphs.outneighbors), + :(Graphs.inneighbors), + :(Graphs.all_neighbors), + :(Graphs.neighbors), + ] @eval begin function $f(graph::AbstractNamedGraph, vertex) position_vertices = $f(position_graph(graph), vertex_positions(graph)[vertex]) @@ -200,8 +200,8 @@ function Graphs.degree(graph::AbstractNamedGraph, vertex::Integer) return namedgraph_degree(graph::AbstractNamedGraph, vertex) end -function Graphs.degree_histogram(g::AbstractNamedGraph, degfn=degree) - hist = Dictionary{Int,Int}() +function Graphs.degree_histogram(g::AbstractNamedGraph, degfn = degree) + hist = Dictionary{Int, Int}() for v in vertices(g) # minimize allocations by for d in degfn(g, v) # iterating over vertices set!(hist, d, get(hist, d, 0) + 1) @@ -211,8 +211,8 @@ function Graphs.degree_histogram(g::AbstractNamedGraph, degfn=degree) end function namedgraph_neighborhood( - graph::AbstractNamedGraph, vertex, d, distmx=weights(graph); dir=:out -) + graph::AbstractNamedGraph, vertex, d, distmx = weights(graph); dir = :out + ) position_distmx = dist_matrix_to_position_dist_matrix(graph, distmx) position_vertices = neighborhood( position_graph(graph), vertex_positions(graph)[vertex], d, position_distmx; dir @@ -221,22 +221,22 @@ function namedgraph_neighborhood( end function Graphs.neighborhood( - graph::AbstractNamedGraph, vertex, d, distmx=weights(graph); dir=:out -) + graph::AbstractNamedGraph, vertex, d, distmx = weights(graph); dir = :out + ) return namedgraph_neighborhood(graph, vertex, d, distmx; dir) end # Fix for ambiguity error with `AbstractGraph` version function Graphs.neighborhood( - graph::AbstractNamedGraph, vertex::Integer, d, distmx=weights(graph); dir=:out -) + graph::AbstractNamedGraph, vertex::Integer, d, distmx = weights(graph); dir = :out + ) return namedgraph_neighborhood(graph, vertex, d, distmx; dir) end # Fix for ambiguity error with `AbstractGraph` version function Graphs.neighborhood( - graph::AbstractNamedGraph, vertex::Integer, d, distmx::AbstractMatrix{<:Real}; dir=:out -) + graph::AbstractNamedGraph, vertex::Integer, d, distmx::AbstractMatrix{<:Real}; dir = :out + ) return namedgraph_neighborhood(graph, vertex, d, distmx; dir) end @@ -247,27 +247,27 @@ function namedgraph_neighborhood_dists(graph::AbstractNamedGraph, vertex, d, dis ) return [ (ordered_vertices(graph)[position_vertex], dist) for - (position_vertex, dist) in position_vertices_and_dists + (position_vertex, dist) in position_vertices_and_dists ] end function Graphs.neighborhood_dists( - graph::AbstractNamedGraph, vertex, d, distmx=weights(graph); dir=:out -) + graph::AbstractNamedGraph, vertex, d, distmx = weights(graph); dir = :out + ) return namedgraph_neighborhood_dists(graph, vertex, d, distmx; dir) end # Fix for ambiguity error with `AbstractGraph` version function Graphs.neighborhood_dists( - graph::AbstractNamedGraph, vertex::Integer, d, distmx=weights(graph); dir=:out -) + graph::AbstractNamedGraph, vertex::Integer, d, distmx = weights(graph); dir = :out + ) return namedgraph_neighborhood_dists(graph, vertex, d, distmx; dir) end # Fix for ambiguity error with `AbstractGraph` version function Graphs.neighborhood_dists( - graph::AbstractNamedGraph, vertex::Integer, d, distmx::AbstractMatrix{<:Real}; dir=:out -) + graph::AbstractNamedGraph, vertex::Integer, d, distmx::AbstractMatrix{<:Real}; dir = :out + ) return namedgraph_neighborhood_dists(graph, vertex, d, distmx; dir) end @@ -277,7 +277,7 @@ function namedgraph_mincut(graph::AbstractNamedGraph, distmx) return Dictionary(vertices(graph), position_parity), bestcut end -function Graphs.mincut(graph::AbstractNamedGraph, distmx=weights(graph)) +function Graphs.mincut(graph::AbstractNamedGraph, distmx = weights(graph)) return namedgraph_mincut(graph, distmx) end @@ -287,8 +287,8 @@ end # TODO: Make this more generic? function GraphsExtensions.partition_vertices( - graph::AbstractNamedGraph; npartitions=nothing, nvertices_per_partition=nothing, kwargs... -) + graph::AbstractNamedGraph; npartitions = nothing, nvertices_per_partition = nothing, kwargs... + ) vertex_partitions = partition_vertices( position_graph(graph); npartitions, nvertices_per_partition, kwargs... ) @@ -301,13 +301,13 @@ function GraphsExtensions.partition_vertices( end function namedgraph_a_star( - graph::AbstractNamedGraph, - source, - destination, - distmx=weights(graph), - heuristic::Function=(v -> zero(eltype(distmx))), - edgetype_to_return=edgetype(graph), -) + graph::AbstractNamedGraph, + source, + destination, + distmx = weights(graph), + heuristic::Function = (v -> zero(eltype(distmx))), + edgetype_to_return = edgetype(graph), + ) position_distmx = dist_matrix_to_position_dist_matrix(graph, distmx) position_shortest_path = a_star( position_graph(graph), @@ -326,21 +326,21 @@ end # Fix ambiguity error with `AbstractGraph` version function Graphs.a_star( - graph::AbstractNamedGraph{U}, source::Integer, destination::Integer, args... -) where {U<:Integer} + graph::AbstractNamedGraph{U}, source::Integer, destination::Integer, args... + ) where {U <: Integer} return namedgraph_a_star(graph, source, destination, args...) end # Fix ambiguity error with `AbstractGraph` version function Graphs.a_star( - graph::AbstractNamedGraph, source::Integer, destination::Integer, args... -) + graph::AbstractNamedGraph, source::Integer, destination::Integer, args... + ) return namedgraph_a_star(graph, source, destination, args...) end function Graphs.spfa_shortest_paths( - graph::AbstractNamedGraph, vertex, distmx=weights(graph) -) + graph::AbstractNamedGraph, vertex, distmx = weights(graph) + ) position_distmx = dist_matrix_to_position_dist_matrix(graph, distmx) position_shortest_paths = spfa_shortest_paths( position_graph(graph), vertex_positions(graph)[vertex], position_distmx @@ -349,20 +349,20 @@ function Graphs.spfa_shortest_paths( end function Graphs.boruvka_mst( - g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real}=weights(g); minimize=true -) + g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real} = weights(g); minimize = true + ) position_mst, weights = boruvka_mst(position_graph(g), distmx; minimize) return map(e -> position_edge_to_edge(g, e), position_mst), weights end function Graphs.kruskal_mst( - g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real}=weights(g); minimize=true -) + g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real} = weights(g); minimize = true + ) position_mst = kruskal_mst(position_graph(g), distmx; minimize) return map(e -> position_edge_to_edge(g, e), position_mst) end -function Graphs.prim_mst(g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real}=weights(g)) +function Graphs.prim_mst(g::AbstractNamedGraph, distmx::AbstractMatrix{<:Real} = weights(g)) position_mst = prim_mst(position_graph(g), distmx) return map(e -> position_edge_to_edge(g, e), position_mst) end @@ -387,13 +387,13 @@ Graphs.has_edge(g::AbstractNamedGraph, edge) = has_edge(g, edgetype(g)(edge)) Graphs.has_edge(g::AbstractNamedGraph, src, dst) = has_edge(g, edgetype(g)(src, dst)) function Graphs.has_path( - graph::AbstractNamedGraph, source, destination; exclude_vertices=vertextype(graph)[] -) + graph::AbstractNamedGraph, source, destination; exclude_vertices = vertextype(graph)[] + ) return has_path( position_graph(graph), vertex_positions(graph)[source], vertex_positions(graph)[destination]; - exclude_vertices=map(v -> vertex_positions(graph)[v], exclude_vertices), + exclude_vertices = map(v -> vertex_positions(graph)[v], exclude_vertices), ) end @@ -413,11 +413,11 @@ function Base.union(graph1::AbstractNamedGraph, graph2::AbstractNamedGraph) end function Base.union( - graph1::AbstractNamedGraph, - graph2::AbstractNamedGraph, - graph3::AbstractNamedGraph, - graph_rest::AbstractNamedGraph..., -) + graph1::AbstractNamedGraph, + graph2::AbstractNamedGraph, + graph3::AbstractNamedGraph, + graph_rest::AbstractNamedGraph..., + ) return union(union(graph1, graph2), graph3, graph_rest...) end @@ -461,12 +461,12 @@ function Graphs.connected_components(graph::AbstractNamedGraph) end function Graphs.merge_vertices( - graph::AbstractNamedGraph, merge_vertices; merged_vertex=first(merge_vertices) -) + graph::AbstractNamedGraph, merge_vertices; merged_vertex = first(merge_vertices) + ) merged_graph = copy(graph) add_vertex!(merged_graph, merged_vertex) for vertex in merge_vertices - for e in incident_edges(graph, vertex; dir=:both) + for e in incident_edges(graph, vertex; dir = :both) merged_edge = rename_vertices(v -> v == vertex ? merged_vertex : v, e) if src(merged_edge) ≠ dst(merged_edge) add_edge!(merged_graph, merged_edge) diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index d269ec4..2437530 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -93,7 +93,7 @@ using Test: @test, @testset, @test_throws @test is_tree(QuotientView(pg)) @test nv(pg) == nx * ny @test nv(pg, SuperVertex((1, 1))) == ny - @test_throws ArgumentError nv(pg, SuperVertex((11,11))) + @test_throws ArgumentError nv(pg, SuperVertex((11, 11))) @test nv(QuotientView(pg)) == nx @test ne(pg) == (nx - 1) * ny + nx * (ny - 1) @test ne(pg, SuperEdge((1, 1) => (2, 1))) == ny @@ -164,7 +164,7 @@ end #Add the column back to the in place graph add_vertices!(pg, map(v -> pv[v], v_set)) add_edges!(pg, edges_involving_v_set) - @test is_connected(pg.graph) + @test is_connected(pg.graph) @test is_path_graph(QuotientView(pg)) @test parent(pv) ∈ vertices(QuotientView(pg)) @test has_supervertex(pg, pv) From 415c612fe4ffa1136d3a4f188b35f65bb0f4e0ce Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 17:30:38 -0500 Subject: [PATCH 30/36] Fallback `quotient_graph_type` now corresponds to the case where provided partitioning is most trival Most trivial as in a vector of vertex groups --- src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index 89afa90..c05e0e9 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -97,7 +97,7 @@ function boundary_superedges( end quotient_graph_type(g) = quotient_graph_type(typeof(g)) -quotient_graph_type(::Type{<:AbstractGraph{V}}) where {V} = NamedGraph{V} +quotient_graph_type(::Type{<:AbstractGraph}) = NamedGraph{Int} quotient_vertextype(G) = vertextype(quotient_graph_type(G)) quotient_edgetype(G) = edgetype(quotient_graph_type(G)) From d4b464dd45c8cf93abe59ac6e46d5e1a1a4791ab Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 17:58:59 -0500 Subject: [PATCH 31/36] `PartitionedGraph` now properly uses the `vertextype` of the underlying graph --- src/lib/PartitionedGraphs/src/partitionedgraph.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index 1629103..749f23c 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -7,7 +7,7 @@ using ..NamedGraphs.OrderedDictionaries: OrderedDictionary # TODO: Parametrize `partitioned_vertices` and `which_partition`, # see https://github.com/mtfishman/NamedGraphs.jl/issues/63. -struct PartitionedGraph{V, PV, G <: AbstractGraph{V}, P} <: AbstractPartitionedGraph{V, PV} +struct PartitionedGraph{V, PV, G <: AbstractGraph, P} <: AbstractPartitionedGraph{V, PV} graph::G quotient_graph::NamedGraph{PV} partitioned_vertices::P @@ -24,9 +24,9 @@ quotient_graph_type(::Type{<:AbstractPartitionedGraph{V, PV}}) where {V, PV} = N Graphs.edgetype(::Type{<:PartitionedGraph{V, PV, G}}) where {V, PV, G} = edgetype(G) ##Constructors. -function PartitionedGraph(g::AbstractGraph{V}, partitioned_vertices) where {V} +function PartitionedGraph(g::AbstractGraph, partitioned_vertices) pvs = keys(partitioned_vertices) - which_partition = Dictionary{V, eltype(pvs)}() + which_partition = Dictionary{vertextype(g), eltype(pvs)}() for v in vertices(g) v_pvs = Set(findall(pv -> v ∈ pv, partitioned_vertices)) @assert length(v_pvs) == 1 From 21d51dd4950c2c458a31de639db61297fb930428 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 17:59:59 -0500 Subject: [PATCH 32/36] Add generic interface functions for partitioning/unpartitioning graphs --- src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl | 4 ++++ src/lib/PartitionedGraphs/src/partitionedgraph.jl | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index c05e0e9..987fe44 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -109,6 +109,10 @@ an underlying graph *without* any partitioning. One should also define: """ abstract type AbstractPartitionedGraph{V, PV} <: AbstractNamedGraph{V} end +departition(pg::AbstractPartitionedGraph) = unpartitioned_graph(pg) +unpartition(pg::AbstractGraph) = pg +unpartition(pg::AbstractPartitionedGraph) = unpartition(departition(pg)) + # Required for interface unpartitioned_graph(::AbstractPartitionedGraph) = not_implemented() Base.copy(::AbstractPartitionedGraph) = not_implemented() diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index 749f23c..608464d 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -14,6 +14,8 @@ struct PartitionedGraph{V, PV, G <: AbstractGraph, P} <: AbstractPartitionedGrap which_partition::Dictionary{V, PV} end +partitionedgraph(g::AbstractGraph, partition) = PartitionedGraph(g, partition) + # Interface overloads partitioned_vertices(pg::PartitionedGraph) = pg.partitioned_vertices quotient_graph(pg::PartitionedGraph) = pg.quotient_graph From 170e51ba2d3f666976e6b89b15d383776b6bbc49 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 18:05:54 -0500 Subject: [PATCH 33/36] The type `QuotientView{V} where {V}` is now constructed such that `V` is the quotient vertex type. --- src/lib/PartitionedGraphs/src/quotientgraph.jl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/quotientgraph.jl b/src/lib/PartitionedGraphs/src/quotientgraph.jl index 28f73bf..114e3ed 100644 --- a/src/lib/PartitionedGraphs/src/quotientgraph.jl +++ b/src/lib/PartitionedGraphs/src/quotientgraph.jl @@ -2,8 +2,9 @@ using Graphs: AbstractGraph, rem_vertex!, rem_edge!, vertices, edges using .GraphsExtensions: add_edges! using ..NamedGraphs: NamedGraph, position_graph_type -struct QuotientView{V, G <: AbstractGraph{V}} <: AbstractNamedGraph{V} +struct QuotientView{V, G <: AbstractGraph} <: AbstractNamedGraph{V} graph::G + QuotientView(graph::G) where {G} = new{quotient_vertextype(G), G}(graph) end Base.parent(qg::QuotientView) = qg.graph @@ -15,7 +16,6 @@ function Base.convert(GT::Type{<:AbstractGraph}, g::QuotientView) return qg end -NamedGraphs.vertextype(Q::Type{<:QuotientView}) = quotient_vertextype(parent_graph_type(Q)) NamedGraphs.edgetype(Q::Type{<:QuotientView}) = quotient_edgetype(parent_graph_type(Q)) Graphs.vertices(qg::QuotientView) = quotient_vertices(parent(qg)) @@ -25,9 +25,7 @@ Base.copy(g::QuotientView) = QuotientView(copy(parent(g))) # Graphs.jl and NamedGraphs.jl interface overloads for `PartitionsGraphView` wrapping # a `PartitionedGraph`. -function NamedGraphs.position_graph_type( - type::Type{<:QuotientView{V, G}} - ) where {V, G <: PartitionedGraph{V}} +function NamedGraphs.position_graph_type(type::Type{<:QuotientView}) return position_graph_type(quotient_graph_type(parent_graph_type(type))) end From bf038fd914b76b2191e2cad9fc08dc1dabf009a3 Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Tue, 11 Nov 2025 18:09:43 -0500 Subject: [PATCH 34/36] Add more unit tests for partitioned graphs interface --- test/test_partitionedgraph.jl | 113 +++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 3 deletions(-) diff --git a/test/test_partitionedgraph.jl b/test/test_partitionedgraph.jl index 2437530..6a6463e 100644 --- a/test/test_partitionedgraph.jl +++ b/test/test_partitionedgraph.jl @@ -1,10 +1,13 @@ @eval module $(gensym()) using Graphs: + AbstractGraph, + Graphs, a_star, center, connected_components, diameter, edges, + has_edge, has_vertex, is_connected, is_directed, @@ -35,18 +38,27 @@ using NamedGraphs.NamedGraphGenerators: named_comb_tree, named_grid, named_triangular_lattice_graph using NamedGraphs.OrderedDictionaries: OrderedDictionary using NamedGraphs.PartitionedGraphs: + AbstractPartitionedGraph, PartitionedGraph, + PartitionedGraphs, + PartitionedView, QuotientView, SuperEdge, SuperVertex, boundary_superedges, + departition, + has_supervertex, + partitioned_edges, + partitioned_vertices, + partitionedgraph, + quotient_graph, + rem_supervertex!, superedge, superedges, supervertex, supervertices, - unpartitioned_graph, - rem_supervertex!, - has_supervertex + unpartition, + unpartitioned_graph using Dictionaries: Dictionary, dictionary using Pkg: Pkg using Test: @test, @testset, @test_throws @@ -237,4 +249,99 @@ end @test nv(QuotientView(pg)) == npartitions end end + +# Not an AbstractPartitionedGraph +struct MyGraph{V, P} <: AbstractGraph{V} + g::NamedGraph{V} + partitioned_vertices::P +end +struct MyFastGraph{V, PV, QG, PE} <: AbstractGraph{V} + g::NamedGraph{V} + partitioned_vertices::PV + quotient_graph::QG + partitioned_edges::PE +end + +Graphs.edges(mg::MyGraph) = edges(mg.g) +Graphs.vertices(mg::MyGraph) = vertices(mg.g) + +Graphs.edgetype(mg::MyGraph) = edgetype(mg.g) +Graphs.has_edge(mg::MyGraph, e) = has_edge(mg.g, e) + +PartitionedGraphs.partitioned_vertices(mg::MyGraph) = mg.partitioned_vertices + +PartitionedGraphs.partitioned_vertices(mg::MyFastGraph) = mg.partitioned_vertices +PartitionedGraphs.quotient_graph(mg::MyFastGraph) = mg.quotient_graph +PartitionedGraphs.partitioned_edges(mg::MyFastGraph) = mg.partitioned_edges + +struct WrapperGraph{V, G <: AbstractGraph{V}} <: AbstractGraph{V} + g::G +end + +Graphs.edges(wg::WrapperGraph) = edges(wg.g) +Graphs.vertices(wg::WrapperGraph) = vertices(wg.g) + +PartitionedGraphs.partitioned_vertices(wg::WrapperGraph) = partitioned_vertices(wg.g) + +@testset "Partitioning of non-partitioned graphs" begin + nx, ny = 4, 4 + + g = named_grid((nx, ny)) + + partitions = [[(i, j) for j in 1:ny] for i in 1:nx] + + @test nv(QuotientView(g)) == 1 + @test ne(QuotientView(g)) == 0 + + @test nv(PartitionedView(g, partitions)) == nx * ny + @test ne(PartitionedView(g, partitions)) == (nx - 1) * ny + nx * (ny - 1) + + @test nv(QuotientView(PartitionedView(g, partitions))) == nx + @test ne(QuotientView(PartitionedView(g, partitions))) == ny - 1 + + qg = quotient_graph(PartitionedView(g, partitions)) + pes = partitioned_edges(PartitionedView(g, partitions)) + + mg = MyGraph(g, partitions) + @test nv(QuotientView(mg)) == nx + @test ne(QuotientView(mg)) == ny - 1 + + @test quotient_graph(mg) == qg + @test partitioned_edges(mg) == pes + + mfg = MyFastGraph(g, partitions, qg, pes) + + # Test overloads are working correctly. + @test partitioned_vertices(mfg) === partitions + @test quotient_graph(mfg) === qg + @test partitioned_edges(mfg) === pes +end + +@testset "Nesting partitions" begin + nx, ny, nz = 3, 4, 5 + g = named_grid((nx, ny, nz)) + + # First partition: columns along z-axis + p1 = Dict((i, j) => [(i, j, k) for k in 1:nz] for i in 1:nx for j in 1:ny) + # Second partition: columns along y-axis + p2 = [[(i, j, k) for j in 1:ny] for i in 1:nx for k in 1:nz] + + wg = WrapperGraph(g) + pwg1 = partitionedgraph(wg, p1) + @test nv(QuotientView(pwg1)) == nx * ny + pwg2 = partitionedgraph(wg, p2) + @test nv(QuotientView(pwg2)) == nx * nz + + pwg1_2 = partitionedgraph(pwg2, p1) + @test nv(QuotientView(pwg1)) == nx * ny + pwg2_1 = partitionedgraph(pwg1, p2) + @test nv(QuotientView(pwg2)) == nx * nz + + @test departition(pwg1_2) == pwg2 + @test departition(pwg2_1) == pwg1 + @test unpartition(pwg1_2) == wg + + p1q = [[(i, j) for j in 1:ny] for i in 1:nx] + partitionedgraph(QuotientView(pwg1), p1q) +end end From b4ab434ed3010fd547dd0af1e5b554a73cbaa5fd Mon Sep 17 00:00:00 2001 From: Jack Dunham Date: Wed, 12 Nov 2025 10:27:22 -0500 Subject: [PATCH 35/36] `unpartition` now works on any `AbstractGraph` assume an implementation of `departition` --- .../PartitionedGraphs/src/abstractpartitionedgraph.jl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl index 987fe44..1f5d651 100644 --- a/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/abstractpartitionedgraph.jl @@ -109,9 +109,16 @@ an underlying graph *without* any partitioning. One should also define: """ abstract type AbstractPartitionedGraph{V, PV} <: AbstractNamedGraph{V} end +# Remove one layer of partitioning. departition(pg::AbstractPartitionedGraph) = unpartitioned_graph(pg) -unpartition(pg::AbstractGraph) = pg -unpartition(pg::AbstractPartitionedGraph) = unpartition(departition(pg)) +departition(g::AbstractGraph) = g + +# Recursively remove all layers of partitioning. +function unpartition(pg::AbstractGraph) + g = departition(pg) + g === pg && return pg + return unpartition(g) +end # Required for interface unpartitioned_graph(::AbstractPartitionedGraph) = not_implemented() From 7364a7cc11af88d77ec197b1c13ba0f4196f3cd1 Mon Sep 17 00:00:00 2001 From: Jack Dunham <72548217+jack-dunham@users.noreply.github.com> Date: Wed, 12 Nov 2025 11:02:45 -0500 Subject: [PATCH 36/36] `PartitionedGraph` now has vertex type parameter matching the vertex type parameter of its underlying graph Co-authored-by: Matt Fishman --- src/lib/PartitionedGraphs/src/partitionedgraph.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/PartitionedGraphs/src/partitionedgraph.jl b/src/lib/PartitionedGraphs/src/partitionedgraph.jl index 608464d..3feecd5 100644 --- a/src/lib/PartitionedGraphs/src/partitionedgraph.jl +++ b/src/lib/PartitionedGraphs/src/partitionedgraph.jl @@ -7,7 +7,7 @@ using ..NamedGraphs.OrderedDictionaries: OrderedDictionary # TODO: Parametrize `partitioned_vertices` and `which_partition`, # see https://github.com/mtfishman/NamedGraphs.jl/issues/63. -struct PartitionedGraph{V, PV, G <: AbstractGraph, P} <: AbstractPartitionedGraph{V, PV} +struct PartitionedGraph{V, PV, G <: AbstractGraph{V}, P} <: AbstractPartitionedGraph{V, PV} graph::G quotient_graph::NamedGraph{PV} partitioned_vertices::P