From cf3d549cbc5f12b562c14cad9903ce4782c2f975 Mon Sep 17 00:00:00 2001 From: Bagaev Dmitry Date: Tue, 23 Apr 2024 10:07:54 +0200 Subject: [PATCH 1/3] Fix ForwardDiff compatibility --- Project.toml | 6 ++++-- src/TinyHugeNumbers.jl | 19 ++++++++++++++++--- test/runtests.jl | 41 ++++++++++++++++++++++++++++++++--------- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/Project.toml b/Project.toml index d377770..a4ba07d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,13 +1,15 @@ name = "TinyHugeNumbers" uuid = "783c9a47-75a3-44ac-a16b-f1ab7b3acf04" authors = ["Bagaev Dmitry and contributors"] -version = "1.0.1" +version = "1.0.2" [compat] julia = "1" [extras] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test"] +test = ["Aqua", "ForwardDiff", "Test"] diff --git a/src/TinyHugeNumbers.jl b/src/TinyHugeNumbers.jl index 3ebc6c4..023f516 100644 --- a/src/TinyHugeNumbers.jl +++ b/src/TinyHugeNumbers.jl @@ -120,8 +120,21 @@ const huge = HugeNumber() ## ------------------------------------------------------------------------------------ ## -Base.promote_rule(::Type{Union{TinyNumber, HugeNumber}}, ::Type{T}) where {T} = T -Base.promote_rule(::Type{TinyNumber}, ::Type{HugeNumber}) = Union{TinyNumber, HugeNumber} -Base.promote_rule(::Type{HugeNumber}, ::Type{TinyNumber}) = Union{TinyNumber, HugeNumber} +# A special structure that is used to promote `TinyNumber` and `HugeNumber` to the same type +# but it cannot be instantiated, this might be useful in situations like `clamp(value, tiny, huge)` +# in this case Julia attempts first to promote `tiny` and `huge` to the same type and then +# uses the result to promote `value` to the resulting type. However, there is no "common" type for +# both `tiny` and `huge` so we introduce a special structure that will accomodate that +# see also: https://github.com/ReactiveBayes/TinyHugeNumbers.jl/issues/3 +struct PromoteTinyOrHuge + PromoteTinyOrHuge() = error("Cannot instantiate an internal structure for promotion.") +end + +Base.promote_rule(::Type{T}, ::Type{PromoteTinyOrHuge}) where {T<:Real} = T +Base.promote_rule(::Type{PromoteTinyOrHuge}, ::Type{PromoteTinyOrHuge}) = PromoteTinyOrHuge +Base.promote_rule(::Type{TinyNumber}, ::Type{HugeNumber}) = PromoteTinyOrHuge + +Base.convert(::Type{PromoteTinyOrHuge}, ::TinyNumber) = error("Cannot convert `tiny` to `huge`. Are you trying to put `tiny` and `huge` in the same container (e.g. `Array`)?") +Base.convert(::Type{PromoteTinyOrHuge}, ::HugeNumber) = error("Cannot convert `huge` to `tiny`. Are you trying to put `tiny` and `huge` in the same container (e.g. `Array`)?") end diff --git a/test/runtests.jl b/test/runtests.jl index 2f08d04..eede59a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,18 +1,18 @@ -using TinyHugeNumbers -using Test +using TinyHugeNumbers, Aqua, Test -import TinyHugeNumbers: TinyNumber, HugeNumber - -struct ArbitraryFloatType <: AbstractFloat end +Aqua.test_all(TinyHugeNumbers, deps_compat = (; check_extras = false, check_weakdeps = true)) @testset "TinyHugeNumbers.jl" begin + import TinyHugeNumbers: TinyNumber, HugeNumber + + struct ArbitraryFloatType <: AbstractFloat end - Base.eps(::Type{ ArbitraryFloatType }) = 0.1 - Base.convert(::Type{ ArbitraryFloatType }, ::Integer) = ArbitraryFloatType() # for testing + Base.eps(::Type{ArbitraryFloatType}) = 0.1 + Base.convert(::Type{ArbitraryFloatType}, ::Integer) = ArbitraryFloatType() # for testing @test repr(tiny) == "tiny" @test repr(huge) == "huge" - + @test typeof(tiny) === TinyNumber @test typeof(huge) === HugeNumber @@ -75,9 +75,12 @@ struct ArbitraryFloatType <: AbstractFloat end @test @inferred(promote_type(T, HugeNumber, TinyNumber)) == T end + @test_throws "Cannot convert `tiny` to `huge`" [tiny, huge] + @test_throws "Cannot convert `huge` to `tiny`" [huge, tiny] + for a in (1, 1.0, 0, 0.0, 1.0f0, 0.0f0, Int32(0), Int32(1), big"1", big"1.0", big"0", big"0.0") T = typeof(a) - for v in [tiny, huge] + for v in Real[tiny, huge] V = typeof(v) for op in [+, -, *, /, >, >=, <, <=] @@ -109,3 +112,23 @@ struct ArbitraryFloatType <: AbstractFloat end end end + +@testset "ForwardDiff.jl compatibility" begin + import ForwardDiff + + f(x) = clamp(x, tiny, huge) + + @test @inferred(ForwardDiff.derivative(f, 1.0)) === 1.0 + @test @inferred(ForwardDiff.derivative(f, 2.0)) === 1.0 + @test @inferred(ForwardDiff.derivative(f, 0.0)) === 0.0 + @test @inferred(ForwardDiff.derivative(f, huge+1.0)) === 0.0 + @test @inferred(ForwardDiff.derivative(f, tiny-1.0)) === 0.0 + + g(x) = clamp(x^2, tiny, huge) + + @test @inferred(ForwardDiff.derivative(g, 1.0)) === 2.0 + @test @inferred(ForwardDiff.derivative(g, 2.0)) === 4.0 + @test @inferred(ForwardDiff.derivative(g, 0.0)) === 0.0 + @test @inferred(ForwardDiff.derivative(g, huge+1.0)) === 0.0 + @test @inferred(ForwardDiff.derivative(g, tiny-1.0)) === 2(tiny-1.0) +end From 73a3af482de14e9006c79ac7683b381fd6ae6f77 Mon Sep 17 00:00:00 2001 From: Bagaev Dmitry Date: Tue, 23 Apr 2024 10:14:30 +0200 Subject: [PATCH 2/3] more tests --- src/TinyHugeNumbers.jl | 5 ++++- test/runtests.jl | 35 ++++++++++++++++++++++++++--------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/TinyHugeNumbers.jl b/src/TinyHugeNumbers.jl index 023f516..5418ae0 100644 --- a/src/TinyHugeNumbers.jl +++ b/src/TinyHugeNumbers.jl @@ -124,8 +124,11 @@ const huge = HugeNumber() # but it cannot be instantiated, this might be useful in situations like `clamp(value, tiny, huge)` # in this case Julia attempts first to promote `tiny` and `huge` to the same type and then # uses the result to promote `value` to the resulting type. However, there is no "common" type for -# both `tiny` and `huge` so we introduce a special structure that will accomodate that +# both `tiny` and `huge` (except for the `Union` but we can't use it either since it introduces ambiguities) +# so we introduce a special structure that will accomodate that # see also: https://github.com/ReactiveBayes/TinyHugeNumbers.jl/issues/3 +# note: as a result, we cannot store `tiny` and `huge` in the same container (e.g. `Array`), +# but `[ 1.0, tiny, huge ]` will work just fine struct PromoteTinyOrHuge PromoteTinyOrHuge() = error("Cannot instantiate an internal structure for promotion.") end diff --git a/test/runtests.jl b/test/runtests.jl index eede59a..3a08cb3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,6 @@ using TinyHugeNumbers, Aqua, Test -Aqua.test_all(TinyHugeNumbers, deps_compat = (; check_extras = false, check_weakdeps = true)) +Aqua.test_all(TinyHugeNumbers, deps_compat=(; check_extras=false, check_weakdeps=true)) @testset "TinyHugeNumbers.jl" begin import TinyHugeNumbers: TinyNumber, HugeNumber @@ -75,9 +75,6 @@ Aqua.test_all(TinyHugeNumbers, deps_compat = (; check_extras = false, check_weak @test @inferred(promote_type(T, HugeNumber, TinyNumber)) == T end - @test_throws "Cannot convert `tiny` to `huge`" [tiny, huge] - @test_throws "Cannot convert `huge` to `tiny`" [huge, tiny] - for a in (1, 1.0, 0, 0.0, 1.0f0, 0.0f0, Int32(0), Int32(1), big"1", big"1.0", big"0", big"0.0") T = typeof(a) for v in Real[tiny, huge] @@ -115,20 +112,40 @@ end @testset "ForwardDiff.jl compatibility" begin import ForwardDiff - + f(x) = clamp(x, tiny, huge) @test @inferred(ForwardDiff.derivative(f, 1.0)) === 1.0 @test @inferred(ForwardDiff.derivative(f, 2.0)) === 1.0 @test @inferred(ForwardDiff.derivative(f, 0.0)) === 0.0 - @test @inferred(ForwardDiff.derivative(f, huge+1.0)) === 0.0 - @test @inferred(ForwardDiff.derivative(f, tiny-1.0)) === 0.0 + @test @inferred(ForwardDiff.derivative(f, huge + 1.0)) === 0.0 + @test @inferred(ForwardDiff.derivative(f, tiny - 1.0)) === 0.0 g(x) = clamp(x^2, tiny, huge) @test @inferred(ForwardDiff.derivative(g, 1.0)) === 2.0 @test @inferred(ForwardDiff.derivative(g, 2.0)) === 4.0 @test @inferred(ForwardDiff.derivative(g, 0.0)) === 0.0 - @test @inferred(ForwardDiff.derivative(g, huge+1.0)) === 0.0 - @test @inferred(ForwardDiff.derivative(g, tiny-1.0)) === 2(tiny-1.0) + @test @inferred(ForwardDiff.derivative(g, huge + 1.0)) === 0.0 + @test @inferred(ForwardDiff.derivative(g, tiny - 1.0)) === 2(tiny - 1.0) +end + +@testset "Storing `tiny` and `huge` in arrays" begin + @static if VERSION >= v"1.10" + @test_throws "Cannot convert `tiny` to `huge`" [tiny, huge] + @test_throws "Cannot convert `huge` to `tiny`" [huge, tiny] + else + @test_throws ErrorException [tiny, huge] + @test_throws ErrorException [huge, tiny] + end + + for a in (1.0, 0.0, 1.0f0, 0.0f0, big"1.0", big"0.0") + @test [a, tiny, huge] == [a, tiny(a), huge(a)] + @test [tiny, a, huge] == [tiny(a), a, huge(a)] + @test [tiny, huge, a] == [tiny(a), huge(a), a] + + @test [a, huge, tiny] == [a, huge(a), tiny(a)] + @test [huge, a, tiny] == [huge(a), a, tiny(a)] + @test [huge, tiny, a] == [huge(a), tiny(a), a] + end end From 31a00d855ab06bac6a282eb7c12a99d3b32db01a Mon Sep 17 00:00:00 2001 From: Bagaev Dmitry Date: Tue, 23 Apr 2024 11:00:43 +0200 Subject: [PATCH 3/3] fix for Julia v1.0 --- test/runtests.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 3a08cb3..624f02e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,11 +2,12 @@ using TinyHugeNumbers, Aqua, Test Aqua.test_all(TinyHugeNumbers, deps_compat=(; check_extras=false, check_weakdeps=true)) -@testset "TinyHugeNumbers.jl" begin - import TinyHugeNumbers: TinyNumber, HugeNumber +import TinyHugeNumbers: TinyNumber, HugeNumber - struct ArbitraryFloatType <: AbstractFloat end +struct ArbitraryFloatType <: AbstractFloat end +@testset "TinyHugeNumbers.jl" begin + Base.eps(::Type{ArbitraryFloatType}) = 0.1 Base.convert(::Type{ArbitraryFloatType}, ::Integer) = ArbitraryFloatType() # for testing