Skip to content

Commit 4adb081

Browse files
authored
fix floating point (#3)
1 parent a868aaa commit 4adb081

File tree

8 files changed

+68
-22
lines changed

8 files changed

+68
-22
lines changed

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ makedocs(
1212
"Multiroute flows" => "multiroute.md",
1313
"Min-cost flows" => "mincost.md",
1414
"Min-cut" => "mincut.md",
15+
"Internals" => "internals.md",
1516
]
1617
)
1718

docs/src/index.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# GraphsFlows.jl: flow algorithms for Graphs.jl
1+
# GraphsFlows.jl: flow algorithms for Graphs.jl
22

33
```@meta
44
CurrentModule = GraphsFlows
@@ -8,12 +8,6 @@ DocTestSetup = quote
88
end
99
```
1010

11-
```@autodocs
12-
Modules = [GraphsFlows]
13-
Pages = ["GraphsFlows.jl"]
14-
Order = [:function, :type]
15-
```
16-
1711
This is the documentation page for `GraphsFlows`. In all pages, we assume
1812
GraphsFlows has been imported into scope and that Graphs.jl has been imported.
1913

docs/src/internals.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Internals
2+
3+
```@docs
4+
GraphsFlows.DefaultCapacity
5+
GraphsFlows.AbstractFlowAlgorithm
6+
GraphsFlows.residual
7+
GraphsFlows.is_zero
8+
```

docs/src/maxflow.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
## Max flow algorithms
22

3+
```@docs
4+
maximum_flow
5+
EdmondsKarpAlgorithm
6+
DinicAlgorithm
7+
PushRelabelAlgorithm
8+
BoykovKolmogorovAlgorithm
9+
```
10+
311
```@autodocs
412
Modules = [GraphsFlows]
513
Pages = ["maxflow.jl", "boykov_kolmogorov.jl", "push_relabel.jl", "dinic.jl", "edmonds_karp.jl"]

src/dinic.jl

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,19 @@ function dinic_impl end
1111
residual_graph::::Graphs.IsDirected, # the input graph
1212
source::Integer, # the source vertex
1313
target::Integer, # the target vertex
14-
capacity_matrix::AbstractMatrix{T} # edge flow capacities
14+
capacity_matrix::AbstractMatrix{T}; # edge flow capacities
15+
tolerance::T = (T <: AbstractFloat) ? sqrt(eps(T)) : zero(T)
1516
) where {T}
17+
1618
n = Graphs.nv(residual_graph) # number of vertexes
1719
flow_matrix = zeros(T, n, n) # initialize flow matrix
1820
P = zeros(Int, n) # Sharable parent vector
1921

2022
flow = 0
2123

2224
while true
23-
augment = blocking_flow!(residual_graph, source, target, capacity_matrix, flow_matrix, P)
24-
augment == 0 && break
25+
augment = blocking_flow!(residual_graph, source, target, capacity_matrix, flow_matrix, P; tolerance = tolerance)
26+
is_zero(augment; atol = tolerance) && break
2527
flow += augment
2628
end
2729
return flow, flow_matrix
@@ -42,7 +44,8 @@ function blocking_flow! end
4244
target::Integer, # the target vertex
4345
capacity_matrix::AbstractMatrix{T}, # edge flow capacities
4446
flow_matrix::AbstractMatrix, # the current flow matrix
45-
P::AbstractVector{Int} # Parent vector to store Level Graph
47+
P::AbstractVector{Int}; # Parent vector to store Level Graph
48+
tolerance = (T <: AbstractFloat) ? sqrt(eps(T)) : zero(T)
4649
) where {T}
4750
n = Graphs.nv(residual_graph) # number of vertexes
4851
fill!(P, -1)
@@ -80,7 +83,7 @@ function blocking_flow! end
8083
end
8184
end
8285

83-
flow == 0 && continue # Flow cannot be augmented along path
86+
is_zero(flow; atol = tolerance) && continue # Flow cannot be augmented along path
8487

8588
v = target
8689
u = bv
@@ -108,11 +111,13 @@ blocking_flow(
108111
source::Integer, # the source vertex
109112
target::Integer, # the target vertex
110113
capacity_matrix::AbstractMatrix, # edge flow capacities
111-
flow_matrix::AbstractMatrix, # the current flow matrix
112-
) = blocking_flow!(
114+
flow_matrix::AbstractMatrix{T}; # the current flow matrix
115+
tolerance = (T <: AbstractFloat) ? sqrt(eps(T)) : zero(T)
116+
) where {T}= blocking_flow!(
113117
residual_graph,
114118
source,
115119
target,
116120
capacity_matrix,
117121
flow_matrix,
118-
zeros(Int, Graphs.nv(residual_graph)))
122+
zeros(Int, Graphs.nv(residual_graph));
123+
tolerance = tolerance)

src/maximum_flow.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,11 @@ function maximum_flow(
178178
end
179179
return maximum_flow(flow_graph, source, target, capacity_matrix, algorithm)
180180
end
181+
182+
"""
183+
is_zero(value; tolerance)
184+
185+
Test if the value is equal to zero. It handles floating point errors.
186+
"""
187+
is_zero(value::T; atol = sqrt(eps(T))) where {T<:AbstractFloat} = isapprox(value, zero(T), atol = atol)
188+
is_zero(value; atol) = iszero(value)

src/push_relabel.jl

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ function push_relabel end
1212
residual_graph::::Graphs.IsDirected, # the input graph
1313
source::Integer, # the source vertex
1414
target::Integer, # the target vertex
15-
capacity_matrix::AbstractMatrix{T} # edge flow capacities
15+
capacity_matrix::AbstractMatrix{T}; # edge flow capacities
16+
tolerance::T = (T <: AbstractFloat) ? sqrt(eps(T)) : zero(T)
1617
) where {T}
1718

1819
n = Graphs.nv(residual_graph)
@@ -43,7 +44,7 @@ function push_relabel end
4344
while length(Q) > 0
4445
v = pop!(Q)
4546
active[v] = false
46-
discharge!(residual_graph, v, capacity_matrix, flow_matrix, excess, height, active, count, Q)
47+
discharge!(residual_graph, v, capacity_matrix, flow_matrix, excess, height, active, count, Q; tolerance = tolerance)
4748
end
4849

4950
return sum([flow_matrix[v, target] for v in Graphs.inneighbors(residual_graph, target)]), flow_matrix
@@ -180,20 +181,21 @@ function discharge! end
180181
@traitfn function discharge!(
181182
residual_graph::::Graphs.IsDirected, # the input graph
182183
v::Integer, # vertex to be discharged
183-
capacity_matrix::AbstractMatrix,
184+
capacity_matrix::AbstractMatrix{T},
184185
flow_matrix::AbstractMatrix,
185186
excess::AbstractVector,
186187
height::AbstractVector{Int},
187188
active::AbstractVector{Bool},
188189
count::AbstractVector{Int},
189-
Q::AbstractVector # FIFO queue
190-
)
190+
Q::AbstractVector; # FIFO queue
191+
tolerance = (T <: AbstractFloat) ? sqrt(eps(T)) : zero(T)
192+
) where {T}
191193
for to in Graphs.outneighbors(residual_graph, v)
192-
excess[v] == 0 && break
194+
is_zero(excess[v]; atol = tolerance) && break
193195
push_flow!(residual_graph, v, to, capacity_matrix, flow_matrix, excess, height, active, Q)
194196
end
195197

196-
if excess[v] > 0
198+
if ! is_zero(excess[v]; atol = tolerance)
197199
if count[height[v] + 1] == 1
198200
gap!(residual_graph, height[v], excess, height, active, count, Q)
199201
else

test/push_relabel.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,24 @@
9191
0 0 0 0 1 0]
9292
g448 = Graphs.DiGraph(M448)
9393
@test maximum_flow(g448, 1, 2, M448, algorithm=PushRelabelAlgorithm())[1] == 1
94+
95+
# Non regression test for floating point error (issue #28 in LightGraphsFlows.jl)
96+
M28 = [0 0 1 1 1 0 0 0
97+
0 0 0 0 0 0 0 0
98+
0 0 0 0 0 1 1 1
99+
0 0 0 0 0 1 1 1
100+
0 0 0 0 0 1 1 1
101+
0 1 0 0 0 0 0 0
102+
0 1 0 0 0 0 0 0
103+
0 1 0 0 0 0 0 0]
104+
g28 = Graphs.DiGraph(M28)
105+
C28 = [0. 0. 0.1 0.1 0.1 0. 0. 0.
106+
0. 0. 0. 0. 0. 0. 0. 0.
107+
0. 0. 0. 0. 0. 1. 1. 1.
108+
0. 0. 0. 0. 0. 1. 1. 1.
109+
0. 0. 0. 0. 0. 1. 1. 1.
110+
0. 0. 0. 0. 0. 0. 0. 0.
111+
0. 0. 0. 0. 0. 0. 0. 0.
112+
0. 0. 0. 0. 0. 0. 0. 0.]
113+
@test maximum_flow(g28, 1, 2, C28, algorithm=PushRelabelAlgorithm())[1] == 0
94114
end

0 commit comments

Comments
 (0)