Skip to content

Commit d1de6c5

Browse files
authored
Add more Graphs.jl wrappers, such as mincut
2 parents 0f3b94d + c9d3452 commit d1de6c5

18 files changed

+817
-70
lines changed

Project.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,21 @@ version = "0.1.5"
77
AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
88
Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4"
99
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
10+
GraphsFlows = "06909019-6f44-4949-96fc-b9d9aaa02889"
11+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1012
SimpleTraits = "699a6c99-e7fa-54fc-8d76-47d257e15c1d"
13+
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
14+
SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66"
15+
SymRCM = "286e6d88-80af-4590-acc9-0001b223b9bd"
1116

1217
[compat]
1318
AbstractTrees = "0.3, 0.4"
1419
Dictionaries = "0.3"
1520
Graphs = "1"
21+
GraphsFlows = "0.1.1"
1622
SimpleTraits = "0.9"
23+
SplitApplyCombine = "1.2.2"
24+
SymRCM = "0.2.1"
1725
julia = "1.7"
1826

1927
[extras]

examples/boundary.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using NamedGraphs
2+
using Graphs
3+
4+
g = named_grid((5, 5))
5+
subgraph_vertices = [
6+
(2, 2),
7+
(2, 3),
8+
(2, 4),
9+
(3, 2),
10+
(3, 3),
11+
(3, 4),
12+
(4, 2),
13+
(4, 3),
14+
(4, 4),
15+
]
16+
vs = @show boundary_vertices(g, subgraph_vertices)
17+
vs = @show inner_boundary_vertices(g, subgraph_vertices)
18+
vs = @show outer_boundary_vertices(g, subgraph_vertices)
19+
es = @show boundary_edges(g, subgraph_vertices)

examples/mincut.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using NamedGraphs
2+
using GraphsFlows
3+
4+
g = NamedGraph(path_graph(4), ["A", "B", "C", "D"])
5+
6+
part1, part2 = mincut_partitions(g)
7+
@show part1, part2
8+
9+
part1, part2 = mincut_partitions(g, "A", "D")
10+
@show part1, part2
11+
12+
weights = Dict{Any,Float64}()
13+
weights["A", "B"] = 3.0
14+
weights["B", "C"] = 2.0
15+
weights["C", "D"] = 3.0
16+
17+
part1, part2 = mincut_partitions(g, weights)
18+
@show part1, part2
19+
20+
part1, part2 = mincut_partitions(g, "A", "D", weights)
21+
@show part1, part2

src/Graphs/abstractgraph.jl

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@ end
2323
return digraph
2424
end
2525

26+
@traitfn function directed_graph(graph::AbstractSimpleGraph::(!IsDirected))
27+
digraph = directed_graph(typeof(graph))()
28+
for v in vertices(graph)
29+
add_vertex!(digraph)
30+
end
31+
for e in edges(graph)
32+
add_edge!(digraph, e)
33+
add_edge!(digraph, reverse(e))
34+
end
35+
return digraph
36+
end
37+
2638
@traitfn undirected_graph(graph::::(!IsDirected)) = graph
2739

2840
# TODO: Handle metadata in a generic way
@@ -53,6 +65,22 @@ function rename_vertices(g::AbstractGraph, name_map)
5365
return rename_vertices(v -> name_map[v], g)
5466
end
5567

68+
function permute_vertices(graph::AbstractGraph, permutation::Vector)
69+
return subgraph(graph, vertices(graph)[permutation])
70+
end
71+
72+
# Uniform interface for `outneighbors`, `inneighbors`, and `all_neighbors`
73+
function _neighbors(graph::AbstractGraph, vertex; dir=:out)
74+
if dir == :out
75+
return outneighbors(graph, vertex)
76+
elseif dir == :in
77+
return inneighbors(graph, vertex)
78+
elseif dir == :both
79+
return all_neighbors(graph, vertex)
80+
end
81+
return error("`_neighbors(graph::AbstractGraph, vertex; dir)` with `dir = $(dir) not implemented. Use either `dir = :out`, `dir = :in`, or `dir = :both`.")
82+
end
83+
5684
# Returns just the edges of a directed graph,
5785
# but both edge directions of an undirected graph.
5886
# TODO: Move to NamedGraphs.jl
@@ -162,10 +190,13 @@ function in_incident_edges(graph::AbstractGraph, vertex)
162190
]
163191
end
164192

193+
# TODO: Only return one set of `:out` edges for undirected graphs if `dir=:both`.
165194
function all_incident_edges(graph::AbstractGraph, vertex)
166195
return out_incident_edges(graph, vertex) in_incident_edges(graph, vertex)
167196
end
168197

198+
# TODO: Same as `edges(subgraph(graph, [vertex; neighbors(graph, vertex)]))`.
199+
# TODO: Only return one set of `:out` edges for undirected graphs if `dir=:both`.
169200
"""
170201
incident_edges(graph::AbstractGraph, vertex; dir=:out)
171202
@@ -221,11 +252,13 @@ end
221252
end
222253

223254
# Paths for undirected tree-like graphs
255+
# TODO: Use `a_star`.
224256
@traitfn function vertex_path(graph::::(!IsDirected), s, t)
225257
dfs_tree_graph = dfs_tree(graph, t)
226258
return vertex_path(dfs_tree_graph, s, t)
227259
end
228260

261+
# TODO: Use `a_star`.
229262
@traitfn function edge_path(graph::::(!IsDirected), s, t)
230263
dfs_tree_graph = dfs_tree(graph, t)
231264
return edge_path(dfs_tree_graph, s, t)
@@ -295,7 +328,12 @@ end
295328
end
296329

297330
# Paths for directed tree-like graphs
331+
# TODO: Use `a_star`, make specialized versions:
332+
# `vertex_path(graph::::IsTree, ...)`
333+
# or
334+
# `tree_vertex_path(graph, ...)`
298335
@traitfn function vertex_path(graph::::IsDirected, s, t)
336+
# @assert is_tree(graph)
299337
vertices = eltype(graph)[s]
300338
while vertices[end] != t
301339
parent = parent_vertex(graph, vertices[end])
@@ -305,9 +343,22 @@ end
305343
return vertices
306344
end
307345

346+
# TODO: Use `a_star`, make specialized versions:
347+
# `vertex_path(graph::::IsTree, ...)`
348+
# or
349+
# `tree_vertex_path(graph, ...)`
308350
@traitfn function edge_path(graph::::IsDirected, s, t)
351+
# @assert is_tree(graph)
309352
vertices = vertex_path(graph, s, t)
310353
isnothing(vertices) && return nothing
311354
pop!(vertices)
312355
return [edgetype(graph)(vertex, parent_vertex(graph, vertex)) for vertex in vertices]
313356
end
357+
358+
function mincut_partitions(
359+
graph::AbstractGraph,
360+
distmx=weights(graph),
361+
)
362+
parts = groupfind(first(mincut(graph, distmx)))
363+
return parts[1], parts[2]
364+
end

src/Graphs/boundary.jl

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# https://en.wikipedia.org/wiki/Boundary_(graph_theory)
2+
function boundary_edges(graph::AbstractGraph, subgraph_vertices; dir=:out)
3+
E = edgetype(graph)
4+
subgraph_vertices_set = Set(subgraph_vertices)
5+
subgraph_complement = setdiff(Set(vertices(graph)), subgraph_vertices_set)
6+
boundary_es = Vector{E}()
7+
for subgraph_vertex in subgraph_vertices_set
8+
for e in incident_edges(graph, subgraph_vertex; dir)
9+
if src(e) subgraph_complement || dst(e) subgraph_complement
10+
push!(boundary_es, e)
11+
end
12+
end
13+
end
14+
return boundary_es
15+
end
16+
17+
# https://en.wikipedia.org/wiki/Boundary_(graph_theory)
18+
# See implementation of `Graphs.neighborhood_dists` as a reference.
19+
function inner_boundary_vertices(graph::AbstractGraph, subgraph_vertices; dir=:out)
20+
V = vertextype(graph)
21+
subgraph_vertices_set = Set(subgraph_vertices)
22+
subgraph_complement = setdiff(Set(vertices(graph)), subgraph_vertices_set)
23+
inner_boundary_vs = Vector{V}()
24+
for subgraph_vertex in subgraph_vertices_set
25+
for subgraph_vertex_neighbor in _neighbors(graph, subgraph_vertex; dir)
26+
if subgraph_vertex_neighbor subgraph_complement
27+
push!(inner_boundary_vs, subgraph_vertex)
28+
break
29+
end
30+
end
31+
end
32+
return inner_boundary_vs
33+
end
34+
35+
# https://en.wikipedia.org/wiki/Boundary_(graph_theory)
36+
# See implementation of `Graphs.neighborhood_dists` as a reference.
37+
function outer_boundary_vertices(graph::AbstractGraph, subgraph_vertices; dir=:out)
38+
V = vertextype(graph)
39+
subgraph_vertices_set = Set(subgraph_vertices)
40+
subgraph_complement = setdiff(Set(vertices(graph)), subgraph_vertices_set)
41+
outer_boundary_vs = Set{V}()
42+
for subgraph_vertex in subgraph_vertices_set
43+
for subgraph_vertex_neighbor in _neighbors(graph, subgraph_vertex; dir)
44+
if subgraph_vertex_neighbor subgraph_complement
45+
push!(outer_boundary_vs, subgraph_vertex_neighbor)
46+
end
47+
end
48+
end
49+
return [v for v in outer_boundary_vs]
50+
end
51+
52+
function boundary_vertices(graph::AbstractGraph, subgraph_vertices; dir=:out)
53+
return inner_boundary_vertices(graph, subgraph_vertices; dir)
54+
end

src/Graphs/shortestpaths.jl

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
function dijkstra_parents(
2+
graph::AbstractGraph,
3+
vertex,
4+
distmx=weights(graph)
5+
)
6+
return dijkstra_shortest_paths(
7+
graph,
8+
[vertex],
9+
distmx;
10+
allpaths=false,
11+
trackvertices=false).parents
12+
end
13+
14+
function dijkstra_mst(
15+
graph::AbstractGraph,
16+
vertex,
17+
distmx=weights(graph)
18+
)
19+
parents = dijkstra_shortest_paths(
20+
graph,
21+
[vertex],
22+
distmx;
23+
allpaths=false,
24+
trackvertices=false).parents
25+
mst = Vector{edgetype(graph)}()
26+
for src in eachindex(parents)
27+
dst = parents[src]
28+
if src dst
29+
push!(mst, edgetype(graph)(src, dst))
30+
end
31+
end
32+
return mst
33+
end
34+
35+
function dijkstra_tree(
36+
graph::AbstractGraph,
37+
vertex,
38+
distmx=weights(graph),
39+
)
40+
return tree(graph, dijkstra_parents(graph, vertex, distmx))
41+
end

src/Graphs/symrcm.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SymRCM.symrcm overload
2+
function symrcm(graph::AbstractGraph)
3+
return symrcm(adjacency_matrix(graph))
4+
end
5+
6+
function symrcm_permute(graph::AbstractGraph)
7+
return permute_vertices(graph, symrcm(graph))
8+
end

0 commit comments

Comments
 (0)