Skip to content
This repository was archived by the owner on Oct 21, 2021. It is now read-only.

Commit dc9c9a8

Browse files
committed
Edmonds-Karp Max Flow/Min Cut
Compatibility with v0.3 More 0.3 compatibility
1 parent 04b198d commit dc9c9a8

File tree

5 files changed

+194
-4
lines changed

5 files changed

+194
-4
lines changed

doc/source/algorithms.rst

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Graph Algorithms
99
- topological sorting
1010
- shortest paths: Dijkstra, Floyd-Warshall, A*
1111
- minimum spanning trees: Prim, Kruskal
12-
- flow: Minimum Cut
12+
- flow: Minimum s-t Cut, Maximum Flow, Simple Minimum Cut
1313
- random graph generation
1414
- more algorithms are being implemented
1515

@@ -342,10 +342,33 @@ Kruskal's algorithm finds a minimum spanning tree (or forest) by gradually uniti
342342
Flow
343343
-----------------------
344344
345-
This package implements Simple Minimum Cut
345+
This package implements Minimum s-t Cut, Maximum Flow, and Simple Minimum Cut
346+
347+
348+
Minimum s-t Cut
349+
~~~~~~~~~~~~~~~
350+
351+
The minimum cut that separates vertex s and vertex t.
352+
353+
.. py:function:: min_st_cut(graph, capacity)
354+
355+
:param graph: the input graph
356+
:param capacity: the edge capacities (vector of floats)
357+
358+
:returns: ``(parity, bestcut)``, where ``parity`` is a vector of boolean values that determines the partition and ``bestcut`` is the weight of the cut that makes this partition.
359+
360+
Maximum Flow
361+
~~~~~~~~~~~~
362+
363+
.. py:function:: max_flow(graph, capacity)
364+
365+
:param graph: the input graph
366+
:param capacity: the edge capacities (vector of floats)
367+
368+
:returns: ``maxflow``, where ``maxflow`` is the maximum flow of the network.
346369
347370
Simple Minimum Cut
348-
~~~~~~~~~~~~~~~~~
371+
~~~~~~~~~~~~~~~~~~
349372
350373
Stoer's simple minimum cut gets the minimum cut of an undirected graph.
351374

src/Graphs.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ export
7171
# maximum_adjacency_visit
7272
MaximumAdjacency, AbstractMASVisitor, min_cut, maximum_adjacency_visit,
7373

74+
# edmonds_karp
75+
min_st_cut, max_flow,
76+
7477
# connected_components
7578
connected_components, strongly_connected_components,
7679

@@ -136,6 +139,7 @@ include("breadth_first_visit.jl")
136139
include("depth_first_visit.jl")
137140
include("maximum_adjacency_visit.jl")
138141

142+
include("edmonds_karp.jl")
139143
include("connected_components.jl")
140144
include("dijkstra_spath.jl")
141145
include("bellmanford.jl")

src/edmonds_karp.jl

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
function min_st_cut{V,E}(g::AbstractGraph{V,E},s::V,t::V,capacity::Vector{Float64})
2+
@graph_requires g incidence_list vertex_list
3+
@assert is_directed(g)
4+
5+
r = residual_graph(g,s,capacity)
6+
flow = edmonds_karp_max_flow!(r,s,t)
7+
parity = dfs(r,s)
8+
return parity, flow
9+
end
10+
11+
function max_flow{V,E}(g::AbstractGraph{V,E},s::V,t::V,capacity::Vector{Float64})
12+
@graph_requires g incidence_list vertex_list
13+
@assert is_directed(g)
14+
15+
r = residual_graph(g,s,capacity)
16+
return edmonds_karp_max_flow!(r,s,t)
17+
end
18+
19+
function residual_graph{V,E}(g::AbstractGraph{V,E},s::V,capacity::Vector{Float64})
20+
visited = fill(false,num_vertices(g))
21+
res_g = inclist(vertices(g),ExEdge{V},is_directed=true)
22+
residual_graph_sub!(g,res_g,s,visited,capacity)
23+
return res_g
24+
end
25+
26+
function residual_graph_sub!{V,E1,E2}(g::AbstractGraph{V,E1},r::AbstractGraph{V,E2},
27+
s::V, visited::Vector{Bool}, capacity::Vector{Float64})
28+
visited[vertex_index(s,g)] = true
29+
for edge in out_edges(s,g)
30+
i = edge_index(edge); u = edge.source; v = edge.target
31+
d1 = Dict{UTF8String,Any}(); d1["capacity"] = capacity[i]; d1["flow"] = 0
32+
d2 = Dict{UTF8String,Any}(); d2["capacity"] = capacity[i]; d2["flow"] = capacity[i]
33+
edge = ExEdge(i, u, v, d1)
34+
rev_edge = ExEdge(i, v, u, d2)
35+
add_edge!(r,edge)
36+
add_edge!(r,rev_edge)
37+
if !visited[vertex_index(v,g)]
38+
residual_graph_sub!(g,r,v,visited,capacity)
39+
end
40+
end
41+
end
42+
43+
function edmonds_karp_max_flow!{V,E}(g::AbstractGraph{V,E},s::V,t::V)
44+
flow = 0
45+
46+
while true
47+
48+
#run BFS to find shortest s-t path
49+
#store edges taken to get to each vertex in 'pred'
50+
pred = bfs(g,s,t)
51+
52+
#stop if we weren't able to find a path from s to t
53+
if !haskey(pred,t)
54+
break
55+
end
56+
57+
#Otherwise see how much flow we can send
58+
df = Inf
59+
edge = pred[t]
60+
while true
61+
df = min(df, edge.attributes["capacity"] - edge.attributes["flow"])
62+
if haskey(pred,edge.source)
63+
edge = pred[edge.source]
64+
else
65+
break
66+
end
67+
end
68+
69+
#and update edges by that amount
70+
edge = pred[t]
71+
while true
72+
#find rev edge
73+
t_edges = out_edges(edge.target,g)
74+
idx = find(x-> x.target==edge.source,t_edges) #there should be only one!
75+
rev_edge = t_edges[idx[1]]
76+
77+
edge.attributes["flow"] += df
78+
rev_edge.attributes["flow"] -= df
79+
80+
if haskey(pred,edge.source)
81+
edge = pred[edge.source]
82+
else
83+
break
84+
end
85+
end
86+
flow += df
87+
end
88+
return flow
89+
end
90+
91+
function bfs{V,E}(g::AbstractGraph{V,E},s::V,t::V)
92+
q = DataStructures.Queue(V)
93+
enqueue!(q,s)
94+
95+
pred = Dict{V,E}()
96+
97+
while length(q) > 0
98+
cur = dequeue!(q)
99+
for edge in out_edges(cur,g)
100+
if !haskey(pred,edge.target) && edge.target != s && edge.attributes["capacity"] > edge.attributes["flow"]
101+
pred[edge.target] = edge
102+
enqueue!(q,edge.target)
103+
end
104+
end
105+
end
106+
return pred
107+
end
108+
109+
function dfs{V,E}(g::AbstractGraph{V,E},s::V)
110+
colormap = fill(false,num_vertices(g))
111+
return dfs!(g,s,colormap)
112+
end
113+
114+
function dfs!{V,E}(g::AbstractGraph{V,E},s::V, colormap::Vector{Bool})
115+
colormap[vertex_index(s,g)] = true
116+
for edge in out_edges(s,g)
117+
if edge.attributes["capacity"] > edge.attributes["flow"] && !colormap[vertex_index(edge.target,g)]
118+
dfs!(g,edge.target,colormap)
119+
end
120+
end
121+
return colormap
122+
end

test/edmonds_karp.jl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using Graphs
2+
using Base.Test
3+
4+
#example from wikipedia
5+
6+
g = inclist(collect(1:7),is_directed=true)
7+
8+
#(u, v, c) edge from u to v with capacity c
9+
inputs = [
10+
(1, 2, 3.),
11+
(1, 4, 3.),
12+
(2, 3, 4.),
13+
(3, 1, 3.),
14+
(3, 4, 1.),
15+
(3, 5, 2.),
16+
(4, 5, 2.),
17+
(4, 6, 6.),
18+
(5, 2, 1.),
19+
(5, 7, 1.),
20+
(6, 7, 9.)]
21+
22+
23+
m = length(inputs)
24+
c = zeros(m)
25+
for i = 1 : m
26+
add_edge!(g, inputs[i][1],inputs[i][2])
27+
c[i] = inputs[i][3]
28+
end
29+
30+
@assert num_vertices(g) == 7
31+
@assert num_edges(g) == 11
32+
33+
parity, f = min_st_cut(g,1,7,c)
34+
35+
@test length(parity) == 7
36+
@test parity == Bool[true,true,true,false,true,false,false]
37+
@test f == 5.0
38+
39+
f = max_flow(g,1,7,c)
40+
@test f == 5.0

test/runtests.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ tests = [
1919
"cliques",
2020
"random",
2121
"generators",
22-
"maximum_adjacency_visit" ]
22+
"maximum_adjacency_visit",
23+
"edmonds_karp"]
2324

2425

2526
for t in tests

0 commit comments

Comments
 (0)