Skip to content

Commit

Permalink
Merge pull request #5 from ReactiveBayes/dev-v1.0.2
Browse files Browse the repository at this point in the history
Fix ForwardDiff compatibility
  • Loading branch information
bvdmitri authored Apr 23, 2024
2 parents aa1a589 + 31a00d8 commit 3e9953e
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 13 deletions.
6 changes: 4 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
name = "TinyHugeNumbers"
uuid = "783c9a47-75a3-44ac-a16b-f1ab7b3acf04"
authors = ["Bagaev Dmitry <[email protected]> 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"]
22 changes: 19 additions & 3 deletions src/TinyHugeNumbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,24 @@ 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` (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

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
57 changes: 49 additions & 8 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
using TinyHugeNumbers
using Test
using TinyHugeNumbers, Aqua, Test

Aqua.test_all(TinyHugeNumbers, deps_compat=(; check_extras=false, check_weakdeps=true))

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
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

Expand Down Expand Up @@ -77,7 +78,7 @@ struct ArbitraryFloatType <: AbstractFloat end

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 [+, -, *, /, >, >=, <, <=]
Expand Down Expand Up @@ -109,3 +110,43 @@ 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

@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

0 comments on commit 3e9953e

Please sign in to comment.