Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "ThickNumbers"
uuid = "b57aa878-5b76-4266-befc-f8e007760995"
authors = ["Tim Holy <tim.holy@gmail.com> and contributors"]
version = "1.1.0"
version = "1.1.1"

[deps]
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Expand Down
13 changes: 11 additions & 2 deletions ext/ThickNumbersForwardDiffExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ end

ForwardDiff.can_dual(::Type{<:ThickNumber}) = true

function ForwardDiff.dual_definition_retval(::Val{T}, val::ThickNumber, deriv::ThickNumber, partial::Partials) where {T}
# The value is a ThickNumber (the function evaluated over a set), but a diffrule
# derivative may be an ordinary `Real` (e.g. the integer coefficient from a power
# rule), so `deriv` is left as `Number`. `val::ThickNumber` keeps these from
# overlapping ForwardDiff's all-`Real` methods.
function ForwardDiff.dual_definition_retval(::Val{T}, val::ThickNumber, deriv::Number, partial::Partials) where {T}
return Dual{T}(val, deriv * partial)
end
function ForwardDiff.dual_definition_retval(::Val{T}, val::ThickNumber, deriv1::ThickNumber, partial1::Partials, deriv2::ThickNumber, partial2::Partials) where {T}
function ForwardDiff.dual_definition_retval(::Val{T}, val::ThickNumber, deriv1::Number, partial1::Partials, deriv2::Number, partial2::Partials) where {T}
return Dual{T}(val, ForwardDiff._mul_partials(partial1, partial2, deriv1, deriv2))
end

Expand All @@ -41,6 +45,11 @@ ForwardDiff._div_partial(partial::Real, x::ThickNumber) = partial / x
ForwardDiff._div_partial(partial::ThickNumber, x::Real) = partial / x
ForwardDiff._div_partial(partial::ThickNumber, x::ThickNumber) = partial / x

# ForwardDiff's `iszero_tuple` tests each partial with `==`, which ThickNumber
# disables. Test exact-zeroness with `iszero` instead (defined for ThickNumber and,
# recursively, for nested Duals over ThickNumbers).
ForwardDiff.iszero_tuple(tup::NTuple{N,V}) where {N,V<:ThickLike} = all(iszero, tup)

Base.promote_rule(::Type{TN}, ::Type{Dual{T,V,N}}) where {TN<:ThickNumber,T,V<:Number,N} = Dual{T, promote_dual(TN, V),N}

promote_dual(::Type{TN}, ::Type{V}) where {TN<:ThickNumber,V} = promote_type(TN, V)
Expand Down
11 changes: 11 additions & 0 deletions src/ThickNumbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,17 @@ so returns zero. Otherwise, it returns the minimum absolute value of the endpoin
"""
mig(x::ThickNumber{T}) where T = zero(T) ∈ x ? zero(T) : min(abs(loval(x)), abs(hival(x)))

# A `Real` is a degenerate thick number `[x, x]`, so both its maximum and minimum
# absolute value are `abs(x)`. Provides `mag`/`mig` for code that mixes thick and
# point values.
mag(x::Real) = abs(x)
mig(x::Real) = abs(x)

# A thick number is zero exactly when it is the degenerate set `[0, 0]`. Unlike
# `==`, this is unambiguous, so it is defined (and needed by ForwardDiff, which
# tests partials for exact zero).
Base.iszero(x::ThickNumber) = iszero(loval(x)) & iszero(hival(x))

Base.promote_rule(::Type{ThickNumber{T}}, ::Type{ThickNumber{S}}) where {T<:Number,S<:Number} = ThickNumber{promote_type(T,S)}

## Trait functions and constants
Expand Down
10 changes: 10 additions & 0 deletions test/extensions/forwarddiff.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ using IntervalArith
ddf2(x) = ForwardDiff.derivative(df2, x)
@test ddf2(b) ≐ 1

# Integer powers: `^(::Dual, ::Integer)` tests its partials for zero via
# `iszero_tuple`, and the power-rule coefficients reach `dual_definition_retval`
# as ordinary `Real`s rather than ThickNumbers.
g(x) = x^4
dg(x) = ForwardDiff.derivative(g, x)
ddg(x) = ForwardDiff.derivative(dg, x)
xi = Interval(1.0, 2.0)
@test dg(xi) ≐ 4*xi^3
@test ddg(xi) ≐ 12*xi^2

# abs
dabs(x) = ForwardDiff.derivative(abs, x)
ddabs(x) = ForwardDiff.derivative(dabs, x)
Expand Down
10 changes: 10 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ using IntervalArith
@test mag(x) === 3.0
@test mag(y) === 3.0
@test mag(Interval(-5, 1)) === 5
# A Real is a degenerate thick number, so mag and mig are its absolute value
@test mag(-3.0) === 3.0
@test mig(-3.0) === 3.0
@test mag(2) === 2
@test mig(2) === 2
# iszero: true only for the degenerate set [0, 0]
@test iszero(Interval(0, 0))
@test !iszero(Interval(0, 1))
@test !iszero(Interval(-1, 0))
@test !iszero(Interval(-1, 1))
@test lohi(Interval{Float64}, 1, 3) === x
@test midrad(Interval{Float64}, 2, 1) === x
@test basetype(Interval{Float64}) === Interval
Expand Down
Loading