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

Commit

Permalink
tests passing
Browse files Browse the repository at this point in the history
  • Loading branch information
matbesancon committed Jan 4, 2018
1 parent 03485b8 commit 3286885
Show file tree
Hide file tree
Showing 17 changed files with 1,842 additions and 9 deletions.
7 changes: 2 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,8 @@ notifications:
git:
depth: 99999999

## uncomment the following lines to allow failures on nightly julia
## (tests will run but not make your overall status red)
#matrix:
# allow_failures:
# - julia: nightly
allow_failures:
- julia: nightly

## uncomment and modify the following lines to manually install system packages
#addons:
Expand Down
23 changes: 21 additions & 2 deletions src/LightGraphsFlows.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
module LightGraphsFlows

# package code goes here
import LightGraphs
const lg = LightGraphs
using SimpleTraits: @traitfn, @traitimpl
import SimpleTraits

end # module
import Base: getindex, size, transpose, ctranspose

include("maximum_flow.jl")
include("edmonds_karp.jl")
include("dinic.jl")
include("boykov_kolmogorov.jl")
include("push_relabel.jl")
include("multiroute_flow.jl")
include("kishimoto.jl")
include("ext_multiroute_flow.jl")

# flow
export
maximum_flow, EdmondsKarpAlgorithm, DinicAlgorithm, BoykovKolmogorovAlgorithm, PushRelabelAlgorithm,
multiroute_flow, KishimotoAlgorithm, ExtendedMultirouteFlowAlgorithm

end
200 changes: 200 additions & 0 deletions src/boykov_kolmogorov.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
"""
boykov_kolmogorov_impl(residual_graph, source, target, capacity_matrix)
Compute the max-flow/min-cut between `source` and `target` for `residual_graph`
using the Boykov-Kolmogorov algorithm.
Return the maximum flow in the network, the flow matrix and the partition
`{S,T}` in the form of a vector of 0's, 1's and 2's.
### References
- BOYKOV, Y.; KOLMOGOROV, V., 2004. An Experimental Comparison of
Min-Cut/Max-Flow Algorithms for Energy Minimization in Vision.
### Author
- Júlio Hoffimann Mendes (juliohm@stanford.edu)
"""
function boykov_kolmogorov_impl end
# see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax
@traitfn function boykov_kolmogorov_impl{T, U, AG<:lg.AbstractGraph{U}}(
residual_graph::AG::lg.IsDirected, # the input graph
source::Integer, # the source vertex
target::Integer, # the target vertex
capacity_matrix::AbstractMatrix{T} # edge flow capacities
)
n = lg.nv(residual_graph)

flow = 0
flow_matrix = zeros(T, n, n)

TREE = zeros(U, n)
TREE[source] = U(1)
TREE[target] = U(2)

PARENT = zeros(U, n)

A = [source, target]
O = Vector{U}()

while true
# growth stage
path = find_path!(residual_graph, source, target, flow_matrix, capacity_matrix, PARENT, TREE, A)

isempty(path) && break

# augmentation stage
flow += augment!(path, flow_matrix, capacity_matrix, PARENT, TREE, O)

# adoption stage
adopt!(residual_graph, source, target, flow_matrix, capacity_matrix, PARENT, TREE, A, O)
end

return flow, flow_matrix, TREE
end

# see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax
@traitfn function find_path!{T, AG<:lg.AbstractGraph{T}}(
residual_graph::AG::lg.IsDirected, # the input graph
source::Integer, # the source vertex
target::Integer, # the target vertex
flow_matrix::AbstractMatrix, # the current flow matrix
capacity_matrix::AbstractMatrix, # edge flow capacities
PARENT::Vector, # parent table
TREE::Vector, # tree table
A::Vector # active set
)
tree_cap(p, q) = TREE[p] == one(T) ? capacity_matrix[p, q] - flow_matrix[p, q] :
capacity_matrix[q, p] - flow_matrix[q, p]
while !isempty(A)
p = last(A)
for q in lg.neighbors(residual_graph, p)
if tree_cap(p, q) > 0
if TREE[q] == zero(T)
TREE[q] = TREE[p]
PARENT[q] = p
unshift!(A, q)
end
if TREE[q] zero(T) && TREE[q] TREE[p]
# p -> source
path_to_source = [p]
while PARENT[p] zero(T)
p = PARENT[p]
push!(path_to_source, p)
end

# q -> target
path_to_target = [q]
while PARENT[q] zero(T)
q = PARENT[q]
push!(path_to_target, q)
end

# source -> target
path = [reverse!(path_to_source); path_to_target]

if path[1] == source && path[end] == target
return path
elseif path[1] == target && path[end] == source
return reverse!(path)
end
end
end
end
pop!(A)
end

return Vector{T}()
end

function augment!(
path::AbstractVector, # path from source to target
flow_matrix::AbstractMatrix, # the current flow matrix
capacity_matrix::AbstractMatrix, # edge flow capacities
PARENT::Vector, # parent table
TREE::Vector, # tree table
O::Vector # orphan set
)

T = eltype(path)
# bottleneck capacity
Δ = Inf
for i = 1:(length(path) - 1)
p, q = path[i:(i + 1)]
cap = capacity_matrix[p, q] - flow_matrix[p, q]
cap < Δ &&= cap)
end

# update residual graph
for i = 1:(length(path) - 1)
p, q = path[i:(i + 1)]
flow_matrix[p, q] += Δ
flow_matrix[q, p] -= Δ

# create orphans
if flow_matrix[p, q] == capacity_matrix[p, q]
if TREE[p] == TREE[q] == one(T)
PARENT[q] = zero(T)
unshift!(O, q)
end
if TREE[p] == TREE[q] == 2
PARENT[p] = zero(T)
unshift!(O, p)
end
end
end

return Δ
end

@traitfn function adopt!{T, AG<:lg.AbstractGraph{T}}(
residual_graph::AG::lg.IsDirected, # the input graph
source::Integer, # the source vertex
target::Integer, # the target vertex
flow_matrix::AbstractMatrix, # the current flow matrix
capacity_matrix::AbstractMatrix, # edge flow capacities
PARENT::Vector, # parent table
TREE::Vector, # tree table
A::Vector, # active set
O::Vector # orphan set
)
tree_cap(p, q) = TREE[p] == 1 ? capacity_matrix[p, q] - flow_matrix[p, q] :
capacity_matrix[q, p] - flow_matrix[q, p]
while !isempty(O)
p = pop!(O)
# try to find parent that is not an orphan
parent_found = false
for q in lg.neighbors(residual_graph, p)
if TREE[q] == TREE[p] && tree_cap(q, p) > 0
# check if "origin" is either source or target
o = q
while PARENT[o] 0
o = PARENT[o]
end
if o == source || o == target
parent_found = true
PARENT[p] = q
break
end
end
end

if !parent_found
# scan all neighbors and make the orphan a free node
for q in lg.neighbors(residual_graph, p)
if TREE[q] == TREE[p]
if tree_cap(q, p) > 0
unshift!(A, q)
end
if PARENT[q] == p
PARENT[q] = zero(T)
unshift!(O, q)
end
end
end

TREE[p] = zero(T)
B = setdiff(A, p)
resize!(A, length(B))[:] = B
end
end
end
118 changes: 118 additions & 0 deletions src/dinic.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
"""
function dinic_impl(residual_graph, source, target, capacity_matrix)
Compute the maximum flow between the `source` and `target` for `residual_graph`
with edge flow capacities in `capacity_matrix` using
[Dinic\'s Algorithm](https://en.wikipedia.org/wiki/Dinic%27s_algorithm).
Return the value of the maximum flow as well as the final flow matrix.
"""
function dinic_impl end
@traitfn function dinic_impl{T}(
residual_graph::::lg.IsDirected, # the input graph
source::Integer, # the source vertex
target::Integer, # the target vertex
capacity_matrix::AbstractMatrix{T} # edge flow capacities
)
n = lg.nv(residual_graph) # number of vertexes
flow_matrix = zeros(T, n, n) # initialize flow matrix
P = zeros(Int, n) # Sharable parent vector

flow = 0

while true
augment = blocking_flow!(residual_graph, source, target, capacity_matrix, flow_matrix, P)
augment == 0 && break
flow += augment
end
return flow, flow_matrix
end




"""
blocking_flow!(residual_graph, source, target, capacity_matrix, flow-matrix, P)
Like `blocking_flow`, but requires a preallocated parent vector `P`.
"""
function blocking_flow! end
@traitfn function blocking_flow!{T}(
residual_graph::::lg.IsDirected, # the input graph
source::Integer, # the source vertex
target::Integer, # the target vertex
capacity_matrix::AbstractMatrix{T}, # edge flow capacities
flow_matrix::AbstractMatrix, # the current flow matrix
P::AbstractVector{Int} # Parent vector to store Level Graph
)
n = lg.nv(residual_graph) # number of vertexes
fill!(P, -1)
P[source] = -2

Q = [source]
sizehint!(Q, n)

while length(Q) > 0 # Construct the Level Graph using BFS
u = pop!(Q)
for v in lg.out_neighbors(residual_graph, u)
if P[v] == -1 && capacity_matrix[u, v] > flow_matrix[u, v]
P[v] = u
unshift!(Q, v)
end
end
end

P[target] == -1 && return 0 # BFS couldn't reach the target

total_flow = 0

for bv in lg.in_neighbors(residual_graph, target) # Trace all possible routes to source
flow = typemax(T)
v = target
u = bv
while v != source
if u == -1 # Vertex unreachable from source
flow = 0
break
else
flow = min(flow, capacity_matrix[u, v] - flow_matrix[u, v])
v = u
u = P[u]
end
end

flow == 0 && continue # Flow cannot be augmented along path

v = target
u = bv
while v != source # Augment flow along path
flow_matrix[u, v] += flow
flow_matrix[v, u] -= flow
v = u
u = P[u]
end

total_flow += flow
end
return total_flow
end

"""
blocking_flow(residual_graph, source, target, capacity_matrix, flow-matrix)
Use BFS to identify a blocking flow in the `residual_graph` with current flow
matrix `flow_matrix`and then backtrack from `target` to `source`,
augmenting flow along all possible paths.
"""
blocking_flow(
residual_graph::lg.AbstractGraph, # the input graph
source::Integer, # the source vertex
target::Integer, # the target vertex
capacity_matrix::AbstractMatrix, # edge flow capacities
flow_matrix::AbstractMatrix, # the current flow matrix
) = blocking_flow!(
residual_graph,
source,
target,
capacity_matrix,
flow_matrix,
zeros(Int, lg.nv(residual_graph)))
Loading

0 comments on commit 3286885

Please sign in to comment.