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

handling floating point errors #31

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

etiennedeg
Copy link
Member

I don't know how to use Trait.
This code should pass the tests for both integers and floats

@codecov
Copy link

codecov bot commented Sep 19, 2019

Codecov Report

Merging #31 into master will increase coverage by 32.61%.
The diff coverage is 100%.

Impacted file tree graph

@@             Coverage Diff             @@
##           master      #31       +/-   ##
===========================================
+ Coverage   65.65%   98.26%   +32.61%     
===========================================
  Files          10       10               
  Lines         428      289      -139     
===========================================
+ Hits          281      284        +3     
+ Misses        147        5      -142
Impacted Files Coverage Δ
src/dinic.jl 100% <100%> (+43.18%) ⬆️
src/maximum_flow.jl 78.26% <100%> (+10.07%) ⬆️
src/push_relabel.jl 100% <100%> (+55.93%) ⬆️
src/mincut.jl 100% <0%> (+7.69%) ⬆️
src/multiroute_flow.jl 100% <0%> (+9.52%) ⬆️
src/ext_multiroute_flow.jl 100% <0%> (+20%) ⬆️
src/kishimoto.jl 100% <0%> (+22.22%) ⬆️
src/boykov_kolmogorov.jl 100% <0%> (+31.7%) ⬆️
... and 3 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 016dfd5...fe2aeff. Read the comment docs.


Test if the value is equal to zero. It handles floating point errors.
"""
function is_zero end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is not necessary, you can use isapprox(x, 0) or x ≈ 0 in the code

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default isapprox is not well suited for our problem. If isapprox(0.2+0.1, 0.3) is returning true, isapprox(0.2+0.1-0.3, 0.0) is returning false. In fact, x ≈ 0 is equivalent to x == 0.
As we compare to 0, rtol is just useless, so we must define an atol.
If we want the tolerance to rely on the type, we must separate Float and Integer case (as the default rtol do), because eps is not defined for Integer types.
This is not an absurd way o do it as rtol is defined quite similarly:

# default tolerance arguments
rtoldefault(::Type{T}) where {T<:AbstractFloat} = sqrt(eps(T))
rtoldefault(::Type{<:Real}) = 0
function rtoldefault(x::Union{T,Type{T}}, y::Union{S,Type{S}}, atol::Real) where {T<:Number,S<:Number}
    rtol = max(rtoldefault(real(T)),rtoldefault(real(S)))
    return atol > 0 ? zero(rtol) : rtol
end

See Base.isapprox
and floatfuncs lines 285-291 for the definition of rtol

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would set_zero_subnormals() help here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, 0.1+0.2-0.3 is much greater than subnormal numbers

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After experimentation, comparing to the epsilon is not sufficient for large float.
It seems sqrt(epsilon) is a common choice for tolerance, and it is justified by mathematical error analysis.
Maybe an even better choice would be to choose the tolerance depending on the capacity matrix values. For example, tol = sqrt(eps(maximum(capacity_matrix) or tol = sqrt(eps(sum(capacity_matrix)

@etiennedeg
Copy link
Member Author

I also run into an infinite loop with Dinic's algorithm.
For other algorithms, this should be fine, I see no comparisons between floats

@coveralls
Copy link

coveralls commented Nov 25, 2019

Coverage Status

Coverage increased (+34.004%) to 99.659% when pulling 60f2f8a on etienneINSA:floatingPoint into 016dfd5 on JuliaGraphs:master.

@matbesancon
Copy link
Member

I also run into an infinite loop with Dinic's algorithm.

Did you fix this in this PR or is it still in progress?

@etiennedeg
Copy link
Member Author

etiennedeg commented Nov 25, 2019

I also run into an infinite loop with Dinic's algorithm.

Did you fix this in this PR or is it still in progress?

Yes, this is fixed with the new commit

@etiennedeg
Copy link
Member Author

I forgot the sqrt for the epsilon tolerance.
This should be ok, except if we want to adapt the tolerance depending on the norm of the capacity matrix

@@ -178,3 +178,19 @@ function maximum_flow(
end
return maximum_flow(flow_graph, source, target, capacity_matrix, algorithm)
end

"""
is_zero(value)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could replace iszero with isapprox directly, which handles both integers and abstract floats correctly

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already discussed of that point, unfortunately eps is not defined for integers

The default isapprox is not well suited for our problem. If isapprox(0.2+0.1, 0.3) is returning true, isapprox(0.2+0.1-0.3, 0.0) is returning false. In fact, x ≈ 0 is equivalent to x == 0.
As we compare to 0, rtol is just useless, so we must define an atol.
If we want the tolerance to rely on the type, we must separate Float and Integer case (as the default rtol do), because eps is not defined for Integer types.
This is not an absurd way o do it as rtol is defined quite similarly:

# default tolerance arguments
rtoldefault(::Type{T}) where {T<:AbstractFloat} = sqrt(eps(T))
rtoldefault(::Type{<:Real}) = 0
function rtoldefault(x::Union{T,Type{T}}, y::Union{S,Type{S}}, atol::Real) where {T<:Number,S<:Number}
    rtol = max(rtoldefault(real(T)),rtoldefault(real(S)))
    return atol > 0 ? zero(rtol) : rtol
end

See Base.isapprox
and floatfuncs lines 285-291 for the definition of rtol

src/push_relabel.jl Outdated Show resolved Hide resolved
@matbesancon
Copy link
Member

@etienneINSA what's the status on this?

@etiennedeg
Copy link
Member Author

Sorry for the delay, I think this can be merged, if you agree on the fact that we need a separate function to compare the excess against 0

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants