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

Edmonds-Karp Max Flow/Min Cut #205

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions doc/source/algorithms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Graph Algorithms
- topological sorting
- shortest paths: Dijkstra, Floyd-Warshall, A*
- minimum spanning trees: Prim, Kruskal
- flow: Minimum Cut
- flow: Minimum s-t Cut, Maximum Flow, Simple Minimum Cut
- random graph generation
- more algorithms are being implemented

Expand Down Expand Up @@ -342,10 +342,33 @@ Kruskal's algorithm finds a minimum spanning tree (or forest) by gradually uniti
Flow
-----------------------

This package implements Simple Minimum Cut
This package implements Minimum s-t Cut, Maximum Flow, and Simple Minimum Cut


Minimum s-t Cut
~~~~~~~~~~~~~~~

The minimum cut that separates vertex s and vertex t.

.. py:function:: min_st_cut(graph, capacity)

:param graph: the input graph
:param capacity: the edge capacities (vector of floats)

: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.

Maximum Flow
~~~~~~~~~~~~

.. py:function:: max_flow(graph, capacity)

:param graph: the input graph
:param capacity: the edge capacities (vector of floats)

:returns: ``maxflow``, where ``maxflow`` is the maximum flow of the network.

Simple Minimum Cut
~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~

Stoer's simple minimum cut gets the minimum cut of an undirected graph.

Expand Down
4 changes: 4 additions & 0 deletions src/Graphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ export
# maximum_adjacency_visit
MaximumAdjacency, AbstractMASVisitor, min_cut, maximum_adjacency_visit,

# edmonds_karp
min_st_cut, max_flow,

# connected_components
connected_components, strongly_connected_components,

Expand Down Expand Up @@ -136,6 +139,7 @@ include("breadth_first_visit.jl")
include("depth_first_visit.jl")
include("maximum_adjacency_visit.jl")

include("edmonds_karp.jl")
include("connected_components.jl")
include("dijkstra_spath.jl")
include("bellmanford.jl")
Expand Down
122 changes: 122 additions & 0 deletions src/edmonds_karp.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
function min_st_cut{V,E}(g::AbstractGraph{V,E},s::V,t::V,capacity::Vector{Float64})
@graph_requires g incidence_list vertex_list
@assert is_directed(g)

r = residual_graph(g,s,capacity)
flow = edmonds_karp_max_flow!(r,s,t)
parity = dfs(r,s)
return parity, flow
end

function max_flow{V,E}(g::AbstractGraph{V,E},s::V,t::V,capacity::Vector{Float64})
@graph_requires g incidence_list vertex_list
@assert is_directed(g)

r = residual_graph(g,s,capacity)
return edmonds_karp_max_flow!(r,s,t)
end

function residual_graph{V,E}(g::AbstractGraph{V,E},s::V,capacity::Vector{Float64})
visited = fill(false,num_vertices(g))
res_g = inclist(vertices(g),ExEdge{V},is_directed=true)
residual_graph_sub!(g,res_g,s,visited,capacity)
return res_g
end

function residual_graph_sub!{V,E1,E2}(g::AbstractGraph{V,E1},r::AbstractGraph{V,E2},
s::V, visited::Vector{Bool}, capacity::Vector{Float64})
visited[vertex_index(s,g)] = true
for edge in out_edges(s,g)
i = edge_index(edge); u = edge.source; v = edge.target
d1 = Dict{UTF8String,Any}(); d1["capacity"] = capacity[i]; d1["flow"] = 0
d2 = Dict{UTF8String,Any}(); d2["capacity"] = capacity[i]; d2["flow"] = capacity[i]
edge = ExEdge(i, u, v, d1)
rev_edge = ExEdge(i, v, u, d2)
add_edge!(r,edge)
add_edge!(r,rev_edge)
if !visited[vertex_index(v,g)]
residual_graph_sub!(g,r,v,visited,capacity)
end
end
end

function edmonds_karp_max_flow!{V,E}(g::AbstractGraph{V,E},s::V,t::V)
flow = 0

while true

#run BFS to find shortest s-t path
#store edges taken to get to each vertex in 'pred'
pred = bfs(g,s,t)

#stop if we weren't able to find a path from s to t
if !haskey(pred,t)
break
end

#Otherwise see how much flow we can send
df = Inf
edge = pred[t]
while true
df = min(df, edge.attributes["capacity"] - edge.attributes["flow"])
if haskey(pred,edge.source)
edge = pred[edge.source]
else
break
end
end

#and update edges by that amount
edge = pred[t]
while true
#find rev edge
t_edges = out_edges(edge.target,g)
idx = find(x-> x.target==edge.source,t_edges) #there should be only one!
rev_edge = t_edges[idx[1]]

edge.attributes["flow"] += df
rev_edge.attributes["flow"] -= df

if haskey(pred,edge.source)
edge = pred[edge.source]
else
break
end
end
flow += df
end
return flow
end

function bfs{V,E}(g::AbstractGraph{V,E},s::V,t::V)
q = DataStructures.Queue(V)
enqueue!(q,s)

pred = Dict{V,E}()

while length(q) > 0
cur = dequeue!(q)
for edge in out_edges(cur,g)
if !haskey(pred,edge.target) && edge.target != s && edge.attributes["capacity"] > edge.attributes["flow"]
pred[edge.target] = edge
enqueue!(q,edge.target)
end
end
end
return pred
end

function dfs{V,E}(g::AbstractGraph{V,E},s::V)
colormap = fill(false,num_vertices(g))
return dfs!(g,s,colormap)
end

function dfs!{V,E}(g::AbstractGraph{V,E},s::V, colormap::Vector{Bool})
colormap[vertex_index(s,g)] = true
for edge in out_edges(s,g)
if edge.attributes["capacity"] > edge.attributes["flow"] && !colormap[vertex_index(edge.target,g)]
dfs!(g,edge.target,colormap)
end
end
return colormap
end
40 changes: 40 additions & 0 deletions test/edmonds_karp.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Graphs
using Base.Test

#example from wikipedia

g = inclist(collect(1:7),is_directed=true)

#(u, v, c) edge from u to v with capacity c
inputs = [
(1, 2, 3.),
(1, 4, 3.),
(2, 3, 4.),
(3, 1, 3.),
(3, 4, 1.),
(3, 5, 2.),
(4, 5, 2.),
(4, 6, 6.),
(5, 2, 1.),
(5, 7, 1.),
(6, 7, 9.)]


m = length(inputs)
c = zeros(m)
for i = 1 : m
add_edge!(g, inputs[i][1],inputs[i][2])
c[i] = inputs[i][3]
end

@assert num_vertices(g) == 7
@assert num_edges(g) == 11

parity, f = min_st_cut(g,1,7,c)

@test length(parity) == 7
@test parity == Bool[true,true,true,false,true,false,false]
@test f == 5.0

f = max_flow(g,1,7,c)
@test f == 5.0
3 changes: 2 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ tests = [
"cliques",
"random",
"generators",
"maximum_adjacency_visit" ]
"maximum_adjacency_visit",
"edmonds_karp"]


for t in tests
Expand Down