Skip to content

Commit

Permalink
Fix everything (except the forwarddiff extension)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kolaru committed Feb 7, 2025
1 parent aa069af commit fde8b0a
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 29 deletions.
4 changes: 0 additions & 4 deletions ext/IntervalArithmeticForwardDiffExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,6 @@ function Base.:(^)(x::ExactReal, y::Dual{<:Ty}) where {Ty}
end
end

# resolve ambiguity

Base.convert(::Type{Dual{T,V,N}}, x::ExactReal) where {T,V,N} = Dual{T}(V(x), zero(Partials{N,V}))


# Piecewise functions

Expand Down
3 changes: 1 addition & 2 deletions src/IntervalArithmetic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ const RealIntervalType{T} = Union{BareInterval{T},Interval{T}}
#

include("piecewise.jl")
export Open, Closed, Domain
export Constant, Piecewise, Domain, domain, subdomains, discontinuities, pieces
export Domain, Constant, Piecewise, Domain, domain, subdomains, discontinuities, pieces

#

Expand Down
56 changes: 44 additions & 12 deletions src/piecewise.jl
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
struct Domain{T <: NumTypes, L, R}
struct Domain{L, R, T, S}
lo::T
hi::T
hi::S
end

Domain{L, R}(lo::T, hi::S) where {T, S, L, R} = Domain{L, R, T, S}(lo, hi)
Domain(lo, hi) = Domain{:open, :closed}(lo, hi)
Domain(lo::Tuple, hi::Tuple) = Domain{lo[2], hi[2]}(lo[1], lo[2])
Domain() = Domain{:open, :open}(Inf, -Inf)
Domain(lo::Tuple, hi::Tuple) = Domain{lo[2], hi[2]}(lo[1], hi[1])
Domain(X::Interval) = Domain{:closed, :closed}(inf(X), sup(X))
Domain() = Domain{:open, :open}(Inf, -Inf)

lowerbound(x::Domain{L, R}) where {L, R} = (x.lo, L)
upperbound(x::Domain{L, R}) where {L, R} = (x.hi, R)

inf(x::Domain) = x.lo
sup(x::Domain) = x.hi

function rightof(x, (val, bound))
"""
rightof(val, lowerbound)
Determine if a value is on the right of a lower bound.
"""
function rightof(x::Real, (val, bound))
bound == :closed && return val <= x
return val < x
end
Expand All @@ -27,7 +33,12 @@ function rightof((val1, bound1), (val2, bound2))
return true
end

function leftof(x, (val, bound))
"""
leftof(val, upperbound)
Determine if a value is on the left of an upper bound.
"""
function leftof(x::Real, (val, bound))
bound == :closed && return x <= val
return x < val
end
Expand All @@ -40,6 +51,14 @@ function leftof((val1, bound1), (val2, bound2))
return true
end

function leftof(d1::Domain, d2::Domain)
val1, bound1 = upperbound(d1)
val2, bound2 = lowerbound(d2)

val1 == val2 && return !(bound1 == bound2 == :closed)
return val1 < val2
end

Base.in(x::Real, domain::Domain) = rightof(x, lowerbound(domain)) && leftof(x, upperbound(domain))

function Base.intersect(d1::Domain, d2::Domain)
Expand All @@ -50,15 +69,24 @@ function Base.intersect(d1::Domain, d2::Domain)
return Domain(left, right)
end

function Base.isempty(domain::Domain)
lo, lobound = lowerbound(domain)
hi, hibound = upperbound(domain)

lo == hi && return !(lobound == hibound == :closed)
return lo > hi
end

struct Constant{T}
value::T
end

(constant::Constant)(::Any) = constant.value
(constant::Constant)(::Interval) = interval(constant.value)

# TODO Proper type parameters
struct Piecewise
domain::IntervalSet
domain::Vector
fs
continuity::Vector{Int}
singularities::Vector
Expand All @@ -82,14 +110,14 @@ function Piecewise(
s1 = subdomains[k]
s2 = subdomains[k + 1]

if rightof(upperbound(s1), lowerbound(s2))
if !leftof(s1, s2)
throw(ArgumentError("domains are either not ordered or not disjoint"))
end
end

singularities = sup.(subdomains[1:end-1])

return Piecewise(IntervalSet(subdomains), fs, continuity, singularities)
return Piecewise(subdomains, fs, continuity, singularities)
end

function Piecewise(pairs::Vararg{<:Pair} ; continuity = fill(-1, length(pairs) - 1))
Expand All @@ -104,7 +132,7 @@ discontinuities(piecewise::Piecewise, order = 0) = piecewise.singularities[piece

domain_string(x::Domain) = repr(x ; context = IOContext(stdout, :compact => true))

function domain_string(S::IntervalSet)
function domain_string(S::Vector{<:Domain})
r = repr("text/plain", S ; context = IOContext(stdout, :compact => true))
return join(split(r, "\n")[2:end], "")
end
Expand All @@ -128,13 +156,17 @@ function (piecewise::Piecewise)(X::Interval{T}) where {T}
# This logic exploits the fact that subdomains are ordered
lo = lowerbound(set)
istrivial = false

for subdomain in subdomains(piecewise)
if leftof(lo, lowerbound(subdomain))

if !rightof(lo, lowerbound(subdomain))
istrivial = true
break
end

val, bound = lowerbound(subdomain)
val, bound = upperbound(subdomain)

val > upperbound(set)[1] && break

if bound == :closed
lo = (val, :open)
Expand Down
71 changes: 60 additions & 11 deletions test/interval_tests/piecewise.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,55 @@
using IntervalArithmetic: leftof

@testset "Domain" begin
@testset "leftof" begin
d1 = Domain{:open, :closed}(0, 1)
d2 = Domain{:open, :open}(0, 1)
d3 = Domain{:open, :closed}(1, 2)
d4 = Domain{:closed, :closed}(1, 2)

@test !leftof(d1, d2)
@test leftof(d1, d3)
@test !leftof(d1, d4)

@test leftof(d2, d3)
@test leftof(d2, d4)
end

@testset "intersect" begin
d1 = Domain{:closed, :open}(0, 10)
d2 = Domain{:closed, :open}(2, 15)
d3 = Domain{:open, :closed}(4, 7)
d4 = Domain{:open, :closed}(-20, 3)

@test intersect(d1, d2) == Domain{:closed, :open}(2, 10)
@test intersect(d1, d3) == Domain{:open, :closed}(4, 7)
@test intersect(d1, d4) == Domain{:closed, :closed}(0, 3)
@test intersect(d2, d3) == Domain{:open, :closed}(4, 7)
@test intersect(d2, d4) == Domain{:closed, :closed}(2, 3)
@test intersect(d3, d4) == Domain()
end

@testset "isempty" begin
@test isempty(Domain{:open, :open}(1, 1))
@test !isempty(Domain{:closed, :closed}(1, 1))
@test !isempty(Domain{:open, :open}(1, 2))
@test isempty(Domain())
end
end

@testset "Piecewise constructor" begin
d1 = Domain{:open, :closed}(0, 1)
d2 = Domain{:open, :closed}(1, 2)
d3 = Domain{:closed, :closed}(1, 2)

@test_throws ArgumentError Piecewise(d2 => Constant(0), d1 => Constant(1))
@test_throws ArgumentError Piecewise(d1 => Constant(0), d3 => Constant(1))
end

@testset "Step function" begin
step = Piecewise(
Domain{Open, Closed}(-Inf, 0) => Constant(0),
Domain{Open, Open}(0, Inf) => Constant(1)
Domain{:open, :closed}(-Inf, 0) => Constant(0),
Domain{:open, :open}(0, 1000) => Constant(1)
)

@test step(-1) == 0
Expand All @@ -12,12 +60,13 @@
@test decoration(step(interval(4.44))) == com
@test isequal_interval(step(interval(-22.2, 33.3)), interval(0, 1))
@test decoration(step(interval(-11, 11))) == def
@test decoration(step(interval(500, 2000))) == trv
end

@testset "abs" begin
myabs = Piecewise(
Domain{Open, Closed}(-Inf, 0) => x -> -x,
Domain{Open, Open}(0, Inf) => identity ;
Domain{:open, :closed}(-Inf, 0) => x -> -x,
Domain{:open, :open}(0, Inf) => identity ;
continuity = [0]
)

Expand Down Expand Up @@ -46,9 +95,9 @@ end

@testset "Derivatives" begin
slide = Piecewise(
Domain{Open, Closed}(-Inf, -1) => x -> -2x - 1,
Domain{Open, Closed}(-1, 0) => x -> x^2,
Domain{Open, Open}(0, Inf) => Constant(0) ;
Domain{:open, :closed}(-Inf, -1) => x -> -2x - 1,
Domain{:open, :closed}(-1, 0) => x -> x^2,
Domain{:open, :open}(0, Inf) => Constant(0) ;
continuity = [1, 1]
)

Expand Down Expand Up @@ -107,10 +156,10 @@ end

@testset "Singularities" begin
f = Piecewise(
Domain{Open, Closed}(0, 1) => Constant(0),
Domain{Open, Closed}(1, 2) => x -> 0.5x,
Domain{Open, Closed}(2, 3) => Constant(1),
Domain{Open, Open}(3, 4) => x -> (x-3)^2 + 1 ;
Domain{:open, :closed}(0, 1) => Constant(0),
Domain{:open, :closed}(1, 2) => x -> 0.5x,
Domain{:open, :closed}(2, 3) => Constant(1),
Domain{:open, :open}(3, 4) => x -> (x-3)^2 + 1 ;
continuity = [-1, 0, 1]
)

Expand Down

0 comments on commit fde8b0a

Please sign in to comment.