This repository has been archived by the owner on Oct 22, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
03485b8
commit 3286885
Showing
17 changed files
with
1,842 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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))) |
Oops, something went wrong.