Skip to content
Open
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 docs/src/developers.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,6 @@ It must implement [`loval`](@ref), [`hival`](@ref), [`lohi`](@ref), [`basetype`]
supported arithmetic, and any needed derived methods. These may include [`mid`](@ref),
[`wid`](@ref), [`rad`](@ref), [`mag`](@ref), [`mig`](@ref), [`hull`](@ref),
[`emptyset`](@ref), [`isempty_tn`](@ref), [`isnan_tn`](@ref), [`isinf_tn`](@ref),
[`isfinite_tn`](@ref), and `iszero`.
[`isfinite_tn`](@ref), and [`iszero_tn`](@ref).

Use `isthick(x)` to recognize both kinds of implementation.
5 changes: 5 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ returning `true` would imply that `x == y` *for any choice* `x ∈ X` and `y ∈
unless `X` and `Y` are either empty or each contain only a single value. Concretely, if `1..3` constructs an interval, then `1..3 == 1..3` returning `true` would require that `1.5 == 1.5` and also `1.5 == 2.5` since both `1.5` and `2.5` can be drawn from `1..3`. This is obviously impossible,
thus having `1..3 == 1..3` return `true` would be a violation of the FPTN; it must error instead.

Unary predicates have the same constraint. If `X` contains both zero and nonzero values,
`iszero(X)` cannot return one `Bool`. It therefore errors; [`iszero_tn`](@ref) instead
reports whether `X` is the additive identity `[0, 0]`.

Because numbers are iterable in Julia, set operations like `X ⊆ Y` also cannot be defined (it would require that each number in `X` is a subset of every number in `Y`); however, operations like `intersect(X, Y)` (i.e., `X ∩ Y`) are valid because `x ∩ y` returns `∅` if `x != y` and `∅` is a subset of all other sets.

To avoid violating the FPTN, we replace operators like `==` with custom operators that work only on `ThickNumber{T}` but not `T`. For `Base` Julia functions, a convention is to add `_tn` after the standard function name: `isequal_tn(X, Y)` replaces the "intent" of `isequal(X, Y)`. Often these have unicode equivalents, which typically (though not always) involve a "dot" somewhere in the symbol.
Expand Down Expand Up @@ -60,6 +64,7 @@ You can also check a few basic properties, like whether the values contained in
- [`isfinite_tn(X)`](@ref)
- [`isinf_tn(X)`](@ref)
- [`isnan_tn(X)`](@ref)
- [`iszero_tn(X)`](@ref): whether `X` is the single point zero

### Type information

Expand Down
1 change: 1 addition & 0 deletions docs/src/user_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ wid
isfinite_tn
isinf_tn
isnan_tn
iszero_tn
```

## Comparison operators
Expand Down
8 changes: 4 additions & 4 deletions ext/ThickNumbersForwardDiffExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ 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)
# ForwardDiff needs an unambiguous zero test for thick partials.
ThickNumbers.iszero_tn(d::ThickDual) = iszero_tn(value(d)) & iszero_tn(partials(d))
ThickNumbers.iszero_tn(p::Partials{N,V}) where {N,V<:ThickLike} = all(iszero_tn, p.values)
ForwardDiff.iszero_tuple(tup::NTuple{N,V}) where {N,V<:ThickLike} = all(iszero_tn, 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}

Expand Down
17 changes: 12 additions & 5 deletions src/ThickNumbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export emptyset, hull, issubset_tn, ⫃, is_strict_subset_tn, ⪽, issupset_tn,
export isequal_tn, iseq_tn, ≐, isapprox_tn, ⩪, isless_tn, ≺, ≻, ⪯, ⪰

# Unary
export isfinite_tn, isinf_tn, isnan_tn
export isfinite_tn, isinf_tn, isnan_tn, iszero_tn

# Types

Expand Down Expand Up @@ -320,10 +320,17 @@ mig(x::ThickNumber{T}) where T = zero(T) ∈ x ? zero(T) : min(abs(loval(x)), ab
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))
"""
iszero_tn(x)

Return `true` if both endpoints of `x` are zero, `false` otherwise.
For point numbers, equivalent to `iszero(x)`.
"""
iszero_tn(x::ThickNumber) = iszero(loval(x)) & iszero(hival(x))

iszero_tn(x::Number) = iszero(x)

Base.iszero(::ThickNumber) = throw(FPTNException(iszero, iszero_tn))

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

Expand Down
12 changes: 7 additions & 5 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ using MidRadArith
@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))
# Only [0, 0] is the additive identity.
@test iszero_tn(Interval(0, 0))
@test !iszero_tn(Interval(0, 1))
@test !iszero_tn(Interval(-1, 0))
@test !iszero_tn(Interval(-1, 1))
@test iszero_tn(0.0) && !iszero_tn(1.0)
@test_throws FPTNException iszero(Interval(0, 0))
@test lohi(Interval{Float64}, 1, 3) === x
@test midrad(Interval{Float64}, 2, 1) === x
@test basetype(Interval{Float64}) === Interval
Expand Down
Loading