Skip to content

Improve isapprox #216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 24, 2020
Merged

Improve isapprox #216

merged 1 commit into from
Aug 24, 2020

Conversation

kimikage
Copy link
Collaborator

@kimikage kimikage commented Aug 17, 2020

This changes the association between absolute tolerance and relative tolerance from additive (+) to max to match the behavior in Base of Julia v1 (cf. #209 (comment)).

This also fixes an overflow problem and a rounding problem.

julia> isapprox(-0.5Q0f7, -1Q0f7, rtol=0.5, atol=0)
false # before
true  # after

julia> isapprox(1.0Q6f1, 1.5Q6f1, rtol=0.3, atol=0) # 1.5 * 0.3 < eps(Q6f1)
true  # before
false # after

julia> isapprox(0N0f8, 1N0f8, atol=1.1)
ERROR: ArgumentError: Normed{UInt8,8} is an 8-bit type representing 256 values from 0.0 to 1.0; cannot represent 1.1 # before
true # after

The use of isapprox in a "strict" manner is rare, and I think the impact of these breaking changes are actually minor. However, these changes are difficult to notice and can affect the test results, so I labeled this PR with the "breaking" label as a marker.

This also speeds up the operation with default tolerance settings.

Fixes #209

@kimikage kimikage added the breaking Major breaking change label Aug 17, 2020
@codecov
Copy link

codecov bot commented Aug 17, 2020

Codecov Report

Merging #216 into master will increase coverage by 0.68%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #216      +/-   ##
==========================================
+ Coverage   88.55%   89.24%   +0.68%     
==========================================
  Files           6        6              
  Lines         498      502       +4     
==========================================
+ Hits          441      448       +7     
+ Misses         57       54       -3     
Impacted Files Coverage Δ
src/FixedPointNumbers.jl 86.88% <100.00%> (+1.49%) ⬆️
src/normed.jl 90.69% <0.00%> (-0.59%) ⬇️
src/fixed.jl 91.83% <0.00%> (+1.93%) ⬆️

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 5dcaf97...3a50c77. Read the comment docs.

This changes the association between absolute tolerance and relative tolerance
from additive (`+`) to `max` to match the behavior in `Base` of Julia v1.
This also fixes an overflow problem and a rounding problem.
This speeds up the operation with default tolerance settings.
@kimikage
Copy link
Collaborator Author

kimikage commented Aug 17, 2020

Benchmark

julia> versioninfo()
Julia Version 1.5.0
Commit 96786e22cc (2020-08-01 23:44 UTC)
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-9.0.1 (ORCJIT, skylake)

julia> x_n0f8 = collect(rand(N0f8, 1024, 1024)); y_n0f8 = collect(rand(N0f8, 1024, 1024));

julia> x_q0f7 = collect(rand(Q0f7, 1024, 1024)); y_q0f7 = collect(rand(Q0f7, 1024, 1024));

julia> @btime isapprox.($x_n0f8, $y_n0f8);
  3.226 ms (4 allocations: 132.31 KiB)   # before
  140.900 μs (4 allocations: 132.31 KiB) # after
  
julia> @btime isapprox.($x_q0f7, $y_q0f7);
  156.800 μs (4 allocations: 132.31 KiB) # before
  140.801 μs (4 allocations: 132.31 KiB) # after
  
julia> @btime isapprox.($x_n0f8, $y_n0f8; atol=0.1);
  3.583 ms (4 allocations: 132.31 KiB)   # before
  140.700 μs (4 allocations: 132.31 KiB) # after

julia> @btime isapprox.($x_q0f7, $y_q0f7; atol=0.1);
  156.700 μs (4 allocations: 132.31 KiB) # before
  140.901 μs (4 allocations: 132.31 KiB) # after

julia> @btime isapprox.($x_n0f8, $y_n0f8; atol=0, rtol=0.1);
  3.289 ms (4 allocations: 132.31 KiB) # before (somewhat broken)
  3.906 ms (4 allocations: 132.31 KiB) # after
  
julia> @btime isapprox.($x_q0f7, $y_q0f7; atol=0, rtol=0.1);
  4.384 ms (4 allocations: 132.31 KiB) # before (somewhat broken)
  4.132 ms (4 allocations: 132.31 KiB) # after
  
julia> @btime isapprox.($x_n0f8, $y_n0f8; rtol=0.1); # This behaves differently before and after this PR.
  3.286 ms (4 allocations: 132.31 KiB)   # before
  560.699 μs (4 allocations: 132.31 KiB) # after

julia> @btime isapprox.($x_q0f7, $y_q0f7; rtol=0.1); # This behaves differently before and after this PR.
  4.435 ms (4 allocations: 132.31 KiB)   # before
  561.299 μs (4 allocations: 132.31 KiB) # after

rtol is having trouble with vectorization. This is due to kwargs, but I haven't found a solution. The workaround is to use atol=zero(typeof(x)) or the default atol, i.e. eps(typeof(x)).

In this benchmark, the results are in BitArray. This is why I used "1024×1024" instead of "1000×1000". Misalignment can lead to a fatal stall.

unsigned(m.i - n.i) <= unsigned(max(atol, zero(X)).i)
end
function _isapprox_atol(m::X, n::X, atol) where {X <: FixedPoint}
unsigned(m.i - n.i) <= div(atol, eps(X))
Copy link
Member

Choose a reason for hiding this comment

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

I'm surprised this div isn't slow. I know that since the denominator is a compile-time constant there are some optimizations available, but I am definitely surprised.

Copy link
Collaborator Author

@kimikage kimikage Aug 23, 2020

Choose a reason for hiding this comment

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

Because atol does not depend on the elements of the array, it can be calculated outside the loop. That's not surprising, but it's surprising that Julia understands that. 😄

BTW, division is not so slow in Skylake and later generations. (In the case of the div for floating point numbers, the function call of fmod messes it up, though. 😭) It is probably more efficient to use rawone than eps, but Q0f7, for example, cannot use rawone.

@kimikage
Copy link
Collaborator Author

Thanks for your review.
If we find any speed bottlenecks in the applications, I will tweak isapprox again. I expect there is some room for improvement in isapprox in ColorTypes.

@kimikage kimikage merged commit a22506b into JuliaMath:master Aug 24, 2020
@kimikage kimikage deleted the isapprox branch August 24, 2020 02:06
@kimikage kimikage mentioned this pull request Apr 30, 2024
38 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking Major breaking change
Projects
None yet
Development

Successfully merging this pull request may close these issues.

isapprox does not handle typemin(::FixedPoint{<:Signed}) correctly
2 participants