Skip to content

Commit

Permalink
Partitioned graphs
Browse files Browse the repository at this point in the history
  • Loading branch information
mtfishman authored Jan 10, 2024
2 parents 1b356a9 + c893137 commit 40f0edd
Show file tree
Hide file tree
Showing 19 changed files with 723 additions and 31 deletions.
4 changes: 4 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
GraphsFlows = "06909019-6f44-4949-96fc-b9d9aaa02889"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
SimpleTraits = "699a6c99-e7fa-54fc-8d76-47d257e15c1d"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66"
Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb"
SymRCM = "286e6d88-80af-4590-acc9-0001b223b9bd"

[compat]
Expand All @@ -24,6 +26,8 @@ SimpleTraits = "0.9"
SparseArrays = "1.7"
SplitApplyCombine = "1.2.2"
SymRCM = "0.2.1"
Suppressor = "0.2"
Requires = "1.3"
julia = "1.7"

[extras]
Expand Down
16 changes: 16 additions & 0 deletions examples/partitioning.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using NamedGraphs
using Metis

g = named_grid((4, 4))
npartitions = 4

pg_metis = PartitionedGraph(g; npartitions, backend="Metis")

@show pg_metis isa PartitionedGraph

if !Sys.iswindows()
using KaHyPar
pg_kahypar = PartitionedGraph(g; npartitions, backend="KaHyPar")
@show nv(partitioned_graph(pg_kahypar)) == nv(partitioned_graph(pg_metis)) == npartitions
@show pg_kahypar isa PartitionedGraph
end
27 changes: 24 additions & 3 deletions src/Graphs/abstractgraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ end
# to avoid method overwrite warnings, see:
# https://github.com/mauro3/SimpleTraits.jl#method-overwritten-warnings
@traitfn function undirected_graph(graph::::IsDirected)
undigraph = undirected_graph(typeof(graph))(vertices(graph))
undigraph = undirected_graph_type(typeof(graph))(vertices(graph))
for e in edges(graph)
# TODO: Check for repeated edges?
add_edge!(undigraph, e)
Expand Down Expand Up @@ -384,6 +384,29 @@ function mincut_partitions(graph::AbstractGraph, distmx=weights(graph))
return parts[1], parts[2]
end

function insert_vertex!(graph::AbstractGraph, vertex)
in_graph = !add_vertex!(graph, vertex)
if in_graph
error("Duplicate vertices are not allowed")
end
return graph
end

function delete_vertex!(graph::AbstractGraph, vertex)
in_graph = rem_vertex!(graph, vertex)
if !in_graph
error("Vertex not in graph")
end
return graph
end

function add_vertices!(graph::AbstractGraph, vs::Vector)
for vertex in vs
add_vertex!(graph, vertex)
end
return graph
end

"""Remove a list of edges from a graph g"""
function rem_edges!(g::AbstractGraph, edges)
for e in edges
Expand Down Expand Up @@ -450,7 +473,6 @@ function random_bfs_tree(g::AbstractGraph, s; maxiter=1000 * (nv(g) + ne(g)))
end
end
end

isempty_Q = isempty(Q)
if isempty_Q
break
Expand All @@ -459,6 +481,5 @@ function random_bfs_tree(g::AbstractGraph, s; maxiter=1000 * (nv(g) + ne(g)))
if !isempty_Q
error("Search failed to cover the graph in time. Consider increasing maxiter.")
end

return g_out
end
7 changes: 7 additions & 0 deletions src/Graphs/partitionedgraphs/abstractpartitionedge.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
abstract type AbstractPartitionEdge{V} <: AbstractNamedEdge{V} end

parent(pe::AbstractPartitionEdge) = not_implemented()
src(pe::AbstractPartitionEdge) = not_implemented()
dst(pe::AbstractPartitionEdge) = not_implemented()

#Don't have the vertices wrapped. But wrap them with source and edge.
173 changes: 173 additions & 0 deletions src/Graphs/partitionedgraphs/abstractpartitionedgraph.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
abstract type AbstractPartitionedGraph{V,PV} <: AbstractNamedGraph{V} end

#Needed for interface
partitioned_graph(pg::AbstractPartitionedGraph) = not_implemented()
unpartitioned_graph(pg::AbstractPartitionedGraph) = not_implemented()
which_partition(pg::AbstractPartitionedGraph, vertex) = not_implemented()
partitioned_vertices(pg::AbstractPartitionedGraph) = not_implemented()
copy(pg::AbstractPartitionedGraph) = not_implemented()
delete_from_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented()
insert_to_vertex_map!(pg::AbstractPartitionedGraph, vertex) = not_implemented()
partition_edge(pg::AbstractPartitionedGraph, edge) = not_implemented()
function edges(pg::AbstractPartitionedGraph, partition_edge::AbstractPartitionEdge)
return not_implemented()
end
vertices(pg::AbstractPartitionedGraph, pv::AbstractPartitionVertex) = not_implemented()
function vertices(
pg::AbstractPartitionedGraph, partition_verts::Vector{V}
) where {V<:AbstractPartitionVertex}
return not_implemented()
end
parent_graph_type(PG::Type{<:AbstractPartitionedGraph}) = not_implemented()
directed_graph_type(PG::Type{<:AbstractPartitionedGraph}) = not_implemented()
undirected_graph_type(PG::Type{<:AbstractPartitionedGraph}) = not_implemented()

#Functions for the abstract type
vertices(pg::AbstractPartitionedGraph) = vertices(unpartitioned_graph(pg))
parent_graph(pg::AbstractPartitionedGraph) = parent_graph(unpartitioned_graph(pg))
function vertex_to_parent_vertex(pg::AbstractPartitionedGraph, vertex)
return vertex_to_parent_vertex(unpartitioned_graph(pg), vertex)
end
edgetype(pg::AbstractPartitionedGraph) = edgetype(unpartitioned_graph(pg))
parent_graph_type(pg::AbstractPartitionedGraph) = parent_graph_type(unpartitioned_graph(pg))
nv(pg::AbstractPartitionedGraph, pv::AbstractPartitionVertex) = length(vertices(pg, pv))
function has_vertex(pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex)
return has_vertex(partitioned_graph(pg), parent(partition_vertex))
end

function has_edge(pg::AbstractPartitionedGraph, edge::AbstractPartitionEdge)
return has_edge(partitioned_graph(pg), parent(partition_edge))
end

function is_boundary_edge(pg::AbstractPartitionedGraph, edge::AbstractEdge)
p_edge = partition_edge(pg, edge)
return src(p_edge) == dst(p_edge)
end

function add_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge)
add_edge!(unpartitioned_graph(pg), edge)
pg_edge = parent(partition_edge(pg, edge))
if src(pg_edge) != dst(pg_edge)
add_edge!(partitioned_graph(pg), pg_edge)
end

return pg
end

function rem_edge!(pg::AbstractPartitionedGraph, edge::AbstractEdge)
pg_edge = partition_edge(pg, edge)
if has_edge(partitioned_graph(pg), pg_edge)
g_edges = edges(pg, pg_edge)
if length(g_edges) == 1
rem_edge!(partitioned_graph(pg), pg_edge)
end
end
return rem_edge!(unpartitioned_graph(pg), edge)
end

function rem_edge!(pg::AbstractPartitionedGraph, partition_edge::AbstractPartitionEdge)
return rem_edges!(pg, edges(pg, parent(partition_edge)))
end

function rem_edge(pg::AbstractPartitionedGraph, partition_edge::AbstractPartitionEdge)
pg_new = copy(pg)
rem_edge!(pg_new, partition_edge)
return pg_new
end

function rem_edges!(
pg::AbstractPartitionedGraph, partition_edges::Vector{<:AbstractPartitionEdge}
)
for pe in partition_edges
rem_edge!(pg, pe)
end
return pg
end

function rem_edges(
pg::AbstractPartitionedGraph, partition_edges::Vector{<:AbstractPartitionEdge}
)
pg_new = copy(pg)
rem_edges!(pg_new, partition_edges)
return pg_new
end

#Vertex addition and removal. I think it's important not to allow addition of a vertex without specification of PV
function add_vertex!(
pg::AbstractPartitionedGraph, vertex, partition_vertex::AbstractPartitionVertex
)
add_vertex!(unpartitioned_graph(pg), vertex)
add_vertex!(partitioned_graph(pg), parent(partition_vertex))
insert_to_vertex_map!(pg, vertex, partition_vertex)
return pg
end

function add_vertices!(
pg::AbstractPartitionedGraph,
vertices::Vector,
partition_vertices::Vector{<:AbstractPartitionVertex},
)
@assert length(vertices) == length(partition_vertices)
for (v, pv) in zip(vertices, partition_vertices)
add_vertex!(pg, v, pv)
end

return pg
end

function add_vertices!(
pg::AbstractPartitionedGraph, vertices::Vector, partition_vertex::AbstractPartitionVertex
)
add_vertices!(pg, vertices, fill(partition_vertex, length(vertices)))
return pg
end

function rem_vertex!(pg::AbstractPartitionedGraph, vertex)
pv = which_partition(pg, vertex)
delete_from_vertex_map!(pg, pv, vertex)
rem_vertex!(unpartitioned_graph(pg), vertex)
if !haskey(partitioned_vertices(pg), parent(pv))
rem_vertex!(partitioned_graph(pg), parent(pv))
end
return pg
end

function rem_vertex!(
pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex
)
rem_vertices!(pg, vertices(pg, partition_vertex))
return pg
end

function rem_vertex(pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex)
pg_new = copy(pg)
rem_vertex!(pg_new, partition_vertex)
return pg_new
end

function add_vertex!(pg::AbstractPartitionedGraph, vertex)
return error("Need to specify a partition where the new vertex will go.")
end

function (pg1::AbstractPartitionedGraph == pg2::AbstractPartitionedGraph)
if unpartitioned_graph(pg1) != unpartitioned_graph(pg2) ||
partitioned_graph(pg1) != partitioned_graph(pg2)
return false
end
for v in vertices(pg1)
if which_partition(pg1, v) != which_partition(pg2, v)
return false
end
end
return true
end

function subgraph(pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex)
return first(induced_subgraph(unpartitioned_graph(pg), vertices(pg, [partition_vertex])))
end

function induced_subgraph(
pg::AbstractPartitionedGraph, partition_vertex::AbstractPartitionVertex
)
return subgraph(pg, partition_vertex), nothing
end
4 changes: 4 additions & 0 deletions src/Graphs/partitionedgraphs/abstractpartitionvertex.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
abstract type AbstractPartitionVertex{V} <: Any where {V} end

#Parent, wrap, unwrap, vertex?
parent(pv::AbstractPartitionVertex) = not_implemented()
8 changes: 8 additions & 0 deletions src/Graphs/partitionedgraphs/partitionedge.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
struct PartitionEdge{V,E<:AbstractEdge{V}} <: AbstractPartitionEdge{V}
edge::E
end

parent(pe::PartitionEdge) = getfield(pe, :edge)
src(pe::PartitionEdge) = PartitionVertex(src(parent(pe)))
dst(pe::PartitionEdge) = PartitionVertex(dst(parent(pe)))
PartitionEdge(p::Pair) = PartitionEdge(NamedEdge(first(p) => last(p)))
Loading

0 comments on commit 40f0edd

Please sign in to comment.