From 4d9c298f30a971be2cc2719a2a85ded9c97217b8 Mon Sep 17 00:00:00 2001 From: Marcelo Forets Date: Mon, 9 Mar 2020 10:32:33 -0300 Subject: [PATCH] Renaming (cont.) (#16) * Rename Affine->FullAffine, AFF->Affine, AF->Aff * Correct imports * Correct imports * Fix Project.toml and bump version * Update .yml files * Add constructors with 1 or several intervals and tests * Fix tests etc * update test and add missing methods Co-authored-by: David Sanders --- .travis.yml | 2 +- Project.toml | 8 +- appveyor.yml | 2 +- src/AFF.jl | 157 ---------------------------------------- src/AffineArithmetic.jl | 124 ++----------------------------- src/{AF.jl => aff.jl} | 66 ++++++++--------- src/affine.jl | 101 ++++++++++++++++++++++++++ src/full_affine.jl | 113 +++++++++++++++++++++++++++++ test/runtests.jl | 57 +++++++-------- 9 files changed, 286 insertions(+), 344 deletions(-) delete mode 100644 src/AFF.jl rename src/{AF.jl => aff.jl} (62%) create mode 100644 src/affine.jl create mode 100644 src/full_affine.jl diff --git a/.travis.yml b/.travis.yml index 74e15d8..9935a51 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,8 @@ os: - osx julia: - - 1.0 - 1.1 + - 1.2 - nightly notifications: diff --git a/Project.toml b/Project.toml index 36194b1..eddff00 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "AffineArithmetic" uuid = "2e89c364-fad6-56cb-99bd-ebadcd2cf8d2" authors = ["dpsanders"] -version = "0.1.0" +version = "0.2.0" [deps] IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" @@ -9,8 +9,10 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [compat] -IntervalArithmetic = "≥ 0.15.0" -julia = "≥ 1.0.0" +IntervalArithmetic = "0.16" +Polynomials = "0.6" +StaticArrays = "0.12" +julia = "1" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/appveyor.yml b/appveyor.yml index dfc809a..7de9dc7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,7 @@ environment: matrix: - - julia_version: 1 - julia_version: 1.1 + - julia_version: 1.2 - julia_version: nightly platform: diff --git a/src/AFF.jl b/src/AFF.jl deleted file mode 100644 index 387c256..0000000 --- a/src/AFF.jl +++ /dev/null @@ -1,157 +0,0 @@ -using StaticArrays -using IntervalArithmetic -import IntervalArithmetic: interval - -using LinearAlgebra - -import Base: +, *, ^, -, sqrt, inv - -""" -Affine form with center `c`, affine components `γ` and error `Δ`. - -Variant where Δ is an interval -""" -struct AFF{N,T<:AbstractFloat} - affine::AF{N,T} - range::Interval{T} -end - -interval(x::AFF) = x.range - - -function Base.show(io::IO, C::AFF{N,T}) where {N,T} - print(io, "affine=", C.affine, "; range=", C.range) -end - -# ==(C::Affine, D::Affine) = C.c == D.c && C.γ == D.γ - -""" -Make an `AF` based on an interval, which is number `i` of `n` total variables. -""" -function AFF(X::Interval{T}, n, i) where {T} - return AFF(AF(X, n, i), X) -end - -for op in (:+, :*, :-) - @eval function $op(x::AFF, y::AFF) - affine = $op(x.affine, y.affine) - - range = $op(x.range, y.range) - - range = range ∩ interval(affine) - - return AFF(affine, range) - end -end - -for op in (:sqrt, :inv) - @eval function $op(x::AFF) - affine = $op(x.affine, x.range) - - range = $op(x.range) - - range = range ∩ interval(affine) - - return AFF(affine, range) - end -end - - - - - -*(x::AFF, α::Real) = AFF(α*x.affine, α*x.range) -*(α::Real, x::AFF) = x * α - -+(x::AFF, α::Real) = AFF(α+x.affine, α + x.range) -+(α::Real, x::AFF) = x + α - --(x::AFF) = AFF(-x.affine, -x.range) --(x::AFF, α::Real) = AFF(x.affine - α, x.range - α) --(α::Real, x::AFF) = α + (-x) - -/(x::AFF, α::Real) = AFF(x.affine/α, x.range/α) - -function ^(x::AFF, n::Integer) - - invert = false - - if n < 0 - invert = true - n = -n - # @show n - end - - result = Base.power_by_squaring(x, n) - - if invert - result = inv(result) - end - - return result -end -Base.literal_pow(::typeof(^), x::AFF, ::Val{p}) where {T,p} = x^p - -# x = AF{2,Float64}(0.0, SVector(1.0, 0.0), 0..0) -# y = AF{2,Float64}(0.0, SVector(0.0, 1.0), 0..0) - -# -# x = AF(3..5, 2, 1) -# y = AF(2..4, 2, 2) -# -# 3-x -# interval(3-x) -# -# x + y -# -# -# interval(x+y) -# -# x * y -# interval(x * y) -# -# interval(x * y) -# interval(x) * interval(y) -# -# z = AF(-1..1, 1, 1) -# z^2 -# interval(z^2) -# -# using Polynomials -# -# p = Poly([-3, 1]) -# p2 = p^8 -# -# x = 4 ± 1e-4 -# y = AF(x, 1, 1) -# -# interval(y) -# interval(p2(x)) -# interval(p2(y)) -# -# @time interval(p2(y)) -# -# -# f( (x, y) ) = [x^3 + y, (x - y)^2] -# -# X = IntervalBox(-1..1, -1..1) -# -# f(X) -# -# xx = AF(X[1], 2, 1) -# yy = AF(X[2], 2, 2) -# -# interval.(f((xx, yy))) -# -# f(X) -# -# -# -# -# x = AF(4..6, 1, 1) # example from Messine -# f(x) = x * (10 - x) -# -# f(x) -# interval(f(x)) -# -# interval(10*x - x^2) diff --git a/src/AffineArithmetic.jl b/src/AffineArithmetic.jl index 29a1471..80507e2 100644 --- a/src/AffineArithmetic.jl +++ b/src/AffineArithmetic.jl @@ -7,126 +7,12 @@ import Base: +, -, *, /, ^, ==, show -export Affine, reset_affine_index +include("aff.jl") +include("affine.jl") +include("full_affine.jl") -include("AF1.jl") -include("AF.jl") -include("AFF.jl") -export AF1, AF, AFF -const affine_index = [1] # which affine vector index to use - -reset_affine_index() = affine_index[1] = 1 - -@doc """ -An affine quantity for affine arithmetic. -The usual way to create an affine quantity is from an interval `X`, via `Affine(X)`. -""" -struct Affine{T<:AbstractFloat} - c::T # mid-point - γ::Vector{T} # error terms -end - - -function show(io::IO, C::Affine) - print(io, "⟨", C.c, "; ", C.γ, "⟩") -end - -==(C::Affine, D::Affine) = C.c == D.c && C.γ == D.γ - -@doc """ - Affine(X::Interval) - -Construct a new `Affine` quantity from an `Interval`. -""" -function Affine(X::Interval) - c = mid(X) - r = radius(X) - - index = affine_index[1] - affine_index[1] += 1 - - γ = zeros(index) - γ[end] = r - - return Affine(c, γ) -end - -# conversion of numerical constant to affine: -Affine(c::Real) = Affine(c, Float64[]) - - -range(C::Affine) = C.c + sum(abs.(C.γ))*(-1..1) - -range(X::Interval) = X - -# morally: -# +(C::Affine, D::Afine) = Affine(C.c + D.c, C.γ + D.γ) - -for op in (:+, :-) - @eval function $op(C::Affine, D::Affine) - k = length(C.γ) - l = length(D.γ) - - # account for unequal lengths: - m = min(k, l) - common_γ = $op(C.γ[1:m], D.γ[1:m]) - - if l >= k - γ = [ common_γ; D.γ[m+1:l] ] - else - γ = [ common_γ; C.γ[m+1:k] ] - end - - Affine($op(C.c, D.c), γ) - end -end - -+(C::Affine, α::Real) = Affine(C.c + α, C.γ) -+(α::Real, C::Affine) = C + α - --(C::Affine, α::Real) = Affine(C.c - α, C.γ) --(α::Real, C::Affine) = Affine(α - C.c, [-x for x in C.γ]) - -function *(C::Affine, D::Affine) - - c = C.c - d = D.c - - k = length(C.γ) - l = length(D.γ) - - # account for unequal lengths: - m = min(k, l) - common_γ = d * C.γ[1:m] + c * D.γ[1:m] - - error_bound = sum(abs.(C.γ)) * sum(abs.(D.γ)) - - if l >= k - γ = [ common_γ; c*D.γ[m+1:l]; error_bound ] - else - γ = [ common_γ; d*C.γ[m+1:k]; error_bound ] - end - - # |γ|₁ = sum(abs.(γ)) - - Affine(C.c * D.c, γ) -end - -*(α::Real, C::Affine) = Affine(α*C.c, α*C.γ) -*(C::Affine, α::Real) = α * C - -^(x::Affine, n::Integer) = Base.power_by_squaring(x, n) - -Base.literal_pow(::typeof(^), x::Affine{T}, ::Val{p}) where {T,p} = x^p - -eltype(C::Affine{T}) where T = T -zero(C::Affine{T}) where T = Affine(zero(T)) -zero(::Type{Affine{T}}) where T = Affine(zero(T)) - -one(C::Affine{T}) where T = Affine(one(T)) -one(::Type{Affine{T}}) where T = Affine(one(T)) - -#one(C::Affine) +export Affine, affine +export FullAffine, reset_affine_index end diff --git a/src/AF.jl b/src/aff.jl similarity index 62% rename from src/AF.jl rename to src/aff.jl index 8fff54c..6442c90 100644 --- a/src/AF.jl +++ b/src/aff.jl @@ -11,40 +11,40 @@ Affine form with center `c`, affine components `γ` and error `Δ`. Variant where Δ is an interval """ -struct AF{N,T<:AbstractFloat} +struct Aff{N,T<:AbstractFloat} c::T # mid-point γ::SVector{N,T} # affine terms Δ::Interval{T} # error term end -function Base.show(io::IO, C::AF{N,T}) where {N,T} +function Base.show(io::IO, C::Aff{N,T}) where {N,T} print(io, "⟨", C.c, "; ", C.γ, "; ", C.Δ, "⟩") end # ==(C::Affine, D::Affine) = C.c == D.c && C.γ == D.γ """ -Make an `AF` based on an interval, which is number `i` of `n` total variables. +Make an `Aff` based on an interval, which is number `i` of `n` total variables. """ -function AF(X::Interval{T}, n, i) where {T} +function Aff(X::Interval{T}, n, i) where {T} c = mid(X) r = radius(X) γ = SVector(ntuple(j->i==j ? r : zero(r), n)) - return AF(c, γ, Interval{T}(0)) + return Aff(c, γ, Interval{T}(0)) end -+(x::AF{N,T}, y::AF{N,T}) where {N,T} = AF(x.c + y.c, x.γ .+ y.γ, x.Δ + y.Δ) ++(x::Aff{N,T}, y::Aff{N,T}) where {N,T} = Aff(x.c + y.c, x.γ .+ y.γ, x.Δ + y.Δ) --(x::AF{N,T}, y::AF{N,T}) where {N,T} = AF(x.c - y.c, x.γ .- y.γ, x.Δ - y.Δ) +-(x::Aff{N,T}, y::Aff{N,T}) where {N,T} = Aff(x.c - y.c, x.γ .- y.γ, x.Δ - y.Δ) -interval(C::AF) = C.c + sum(abs.(C.γ))*(-1..1) + C.Δ +interval(C::Aff) = C.c + sum(abs.(C.γ))*(-1..1) + C.Δ -function *(x::AF{N,T}, y::AF{N,T}) where {N,T} +function *(x::Aff{N,T}, y::Aff{N,T}) where {N,T} c = x.c * y.c γ = x.c .* y.γ + y.c .* x.γ @@ -60,23 +60,23 @@ function *(x::AF{N,T}, y::AF{N,T}) where {N,T} Δ += x.Δ * y.Δ - return AF(c, γ, Δ) + return Aff(c, γ, Δ) end -*(x::AF, α::Real) = AF(α*x.c, α.*x.γ, α*x.Δ) -*(α::Real, x::AF) = x * α +*(x::Aff, α::Real) = Aff(α*x.c, α.*x.γ, α*x.Δ) +*(α::Real, x::Aff) = x * α -+(x::AF, α::Real) = AF(α+x.c, x.γ, x.Δ) -+(α::Real, x::AF) = x + α ++(x::Aff, α::Real) = Aff(α+x.c, x.γ, x.Δ) ++(α::Real, x::Aff) = x + α --(x::AF) = AF(-x.c, .-(x.γ), -x.Δ) --(x::AF, α::Real) = AF(x.c - α, x.γ, x.Δ) --(α::Real, x::AF) = α + (-x) +-(x::Aff) = Aff(-x.c, .-(x.γ), -x.Δ) +-(x::Aff, α::Real) = Aff(x.c - α, x.γ, x.Δ) +-(α::Real, x::Aff) = α + (-x) -/(x::AF, α::Real) = AF(x.c/α, x.γ/α, x.Δ/α) +/(x::Aff, α::Real) = Aff(x.c/α, x.γ/α, x.Δ/α) -function ^(x::AF, n::Integer) +function ^(x::Aff, n::Integer) invert = false @@ -95,14 +95,14 @@ function ^(x::AF, n::Integer) return result end -Base.literal_pow(::typeof(^), x::AF, ::Val{p}) where {T,p} = x^p +Base.literal_pow(::typeof(^), x::Aff, ::Val{p}) where {T,p} = x^p -x = AF{2,Float64}(0.0, SVector(1.0, 0.0), 0..0) -y = AF{2,Float64}(0.0, SVector(0.0, 1.0), 0..0) +x = Aff{2,Float64}(0.0, SVector(1.0, 0.0), 0..0) +y = Aff{2,Float64}(0.0, SVector(0.0, 1.0), 0..0) -x = AF(3..5, 2, 1) -y = AF(2..4, 2, 2) +x = Aff(3..5, 2, 1) +y = Aff(2..4, 2, 2) # # 3-x # interval(3-x) @@ -118,7 +118,7 @@ y = AF(2..4, 2, 2) # interval(x * y) # interval(x) * interval(y) # -# z = AF(-1..1, 1, 1) +# z = Aff(-1..1, 1, 1) # z^2 # interval(z^2) # @@ -128,7 +128,7 @@ y = AF(2..4, 2, 2) # p2 = p^8 # # x = 4 ± 1e-4 -# y = AF(x, 1, 1) +# y = Aff(x, 1, 1) # # interval(y) # interval(p2(x)) @@ -143,8 +143,8 @@ y = AF(2..4, 2, 2) # # f(X) # -# xx = AF(X[1], 2, 1) -# yy = AF(X[2], 2, 2) +# xx = Aff(X[1], 2, 1) +# yy = Aff(X[2], 2, 2) # # interval.(f((xx, yy))) # @@ -153,7 +153,7 @@ y = AF(2..4, 2, 2) # # # -# x = AF(4..6, 1, 1) # example from Messine +# x = Aff(4..6, 1, 1) # example from Messine # f(x) = x * (10 - x) # # f(x) @@ -162,16 +162,16 @@ y = AF(2..4, 2, 2) # interval(10*x - x^2) "General formula for affine approximation of nonlinear functions" -function affine_approx(x::AF, α, ζ, δ) +function affine_approx(x::Aff, α, ζ, δ) c = α * x.c + ζ γ = α .* x.γ δ += α * x.Δ # interval - return AF(c, γ, δ) + return Aff(c, γ, δ) end -function Base.sqrt(x::AF, X=interval(x)) +function Base.sqrt(x::Aff, X=interval(x)) a, b = X.lo, X.hi @@ -185,7 +185,7 @@ function Base.sqrt(x::AF, X=interval(x)) return affine_approx(x, α, ζ, δ) end -function Base.inv(x::AF, X=interval(x)) +function Base.inv(x::Aff, X=interval(x)) a, b = X.lo, X.hi diff --git a/src/affine.jl b/src/affine.jl new file mode 100644 index 0000000..b858531 --- /dev/null +++ b/src/affine.jl @@ -0,0 +1,101 @@ +using StaticArrays +using IntervalArithmetic +import IntervalArithmetic: interval + +using LinearAlgebra + +import Base: +, *, ^, -, sqrt, inv + +""" +Affine form with center `c`, affine components `γ` and error `Δ`. + +Variant where Δ is an interval +""" +struct Affine{N,T<:AbstractFloat} + affine::Aff{N,T} + range::Interval{T} +end + +interval(x::Affine) = x.range +range(x::Affine) = x.range + +eltype(::Affine{N, T}) where {N, T} = T +zero(::Affine{N, T}) where {N, T} = Affine(Interval(zero(T))) +zero(::Type{Affine{T}}) where {N, T} = Affine(Interval(zero(T))) + +one(::Affine{T}) where T = Affine(Interval(one(T))) +one(::Type{Affine{T}}) where T = Affine(Interval(one(T))) + +function Base.show(io::IO, C::Affine{N,T}) where {N,T} + print(io, "affine=", C.affine, "; range=", C.range) +end + +# ==(C::Affine, D::Affine) = C.c == D.c && C.γ == D.γ + +""" +Make an `Affine` based on an interval, which is number `i` of `n` total variables. +""" +function Affine(X::Interval{T}, n, i) where {T} + return Affine(Aff(X, n, i), X) +end + +Affine(X::Interval) = Affine(X, 1, 1) +Affine(X::Number) = Affine(Interval(X), 1, 1) + +affine(Xs::Interval...) = Affine.(Xs, length(Xs), 1:length(Xs)) + +for op in (:+, :*, :-) + @eval function $op(x::Affine, y::Affine) + affine = $op(x.affine, y.affine) + + range = $op(x.range, y.range) + + range = range ∩ interval(affine) + + return Affine(affine, range) + end +end + +for op in (:sqrt, :inv) + @eval function $op(x::Affine) + affine = $op(x.affine, x.range) + + range = $op(x.range) + + range = range ∩ interval(affine) + + return Affine(affine, range) + end +end + +*(x::Affine, α::Real) = Affine(α*x.affine, α*x.range) +*(α::Real, x::Affine) = x * α + ++(x::Affine, α::Real) = Affine(α+x.affine, α + x.range) ++(α::Real, x::Affine) = x + α + +-(x::Affine) = Affine(-x.affine, -x.range) +-(x::Affine, α::Real) = Affine(x.affine - α, x.range - α) +-(α::Real, x::Affine) = α + (-x) + +/(x::Affine, α::Real) = Affine(x.affine/α, x.range/α) + +function ^(x::Affine, n::Integer) + + invert = false + + if n < 0 + invert = true + n = -n + # @show n + end + + result = Base.power_by_squaring(x, n) + + if invert + result = inv(result) + end + + return result +end +Base.literal_pow(::typeof(^), x::Affine, ::Val{p}) where {T,p} = x^p diff --git a/src/full_affine.jl b/src/full_affine.jl new file mode 100644 index 0000000..58eebe6 --- /dev/null +++ b/src/full_affine.jl @@ -0,0 +1,113 @@ + +const affine_index = [1] # which affine vector index to use + +reset_affine_index() = affine_index[1] = 1 + +@doc """ +An affine quantity for affine arithmetic. +The usual way to create an affine quantity is from an interval `X`, via `FullAffine(X)`. +""" +struct FullAffine{T<:AbstractFloat} + c::T # mid-point + γ::Vector{T} # error terms +end + + +function show(io::IO, C::FullAffine) + print(io, "⟨", C.c, "; ", C.γ, "⟩") +end + +==(C::FullAffine, D::FullAffine) = C.c == D.c && C.γ == D.γ + +@doc """ + FullAffine(X::Interval) + +Construct a new `FullAffine` quantity from an `Interval`. +""" +function FullAffine(X::Interval) + c = mid(X) + r = radius(X) + + index = affine_index[1] + affine_index[1] += 1 + + γ = zeros(index) + γ[end] = r + + return FullAffine(c, γ) +end + +# conversion of numerical constant to affine: +FullAffine(c::Real) = FullAffine(c, Float64[]) + +range(C::FullAffine) = C.c + sum(abs.(C.γ))*(-1..1) +range(X::Interval) = X + +# morally: +# +(C::FullAffine, D::Afine) = FullAffine(C.c + D.c, C.γ + D.γ) + +for op in (:+, :-) + @eval function $op(C::FullAffine, D::FullAffine) + k = length(C.γ) + l = length(D.γ) + + # account for unequal lengths: + m = min(k, l) + common_γ = $op(C.γ[1:m], D.γ[1:m]) + + if l >= k + γ = [ common_γ; D.γ[m+1:l] ] + else + γ = [ common_γ; C.γ[m+1:k] ] + end + + FullAffine($op(C.c, D.c), γ) + end +end + ++(C::FullAffine, α::Real) = FullAffine(C.c + α, C.γ) ++(α::Real, C::FullAffine) = C + α + +-(C::FullAffine, α::Real) = FullAffine(C.c - α, C.γ) +-(α::Real, C::FullAffine) = FullAffine(α - C.c, [-x for x in C.γ]) + +function *(C::FullAffine, D::FullAffine) + + c = C.c + d = D.c + + k = length(C.γ) + l = length(D.γ) + + # account for unequal lengths: + m = min(k, l) + common_γ = d * C.γ[1:m] + c * D.γ[1:m] + + error_bound = sum(abs.(C.γ)) * sum(abs.(D.γ)) + + if l >= k + γ = [ common_γ; c*D.γ[m+1:l]; error_bound ] + else + γ = [ common_γ; d*C.γ[m+1:k]; error_bound ] + end + + # |γ|₁ = sum(abs.(γ)) + + FullAffine(C.c * D.c, γ) +end + +*(α::Real, C::FullAffine) = FullAffine(α*C.c, α*C.γ) +*(C::FullAffine, α::Real) = α * C + +^(x::FullAffine, n::Integer) = Base.power_by_squaring(x, n) + +Base.literal_pow(::typeof(^), x::FullAffine{T}, ::Val{p}) where {T,p} = x^p + +eltype(C::FullAffine{T}) where T = T +zero(C::FullAffine{T}) where T = FullAffine(zero(T)) +zero(::Type{FullAffine{T}}) where T = FullAffine(zero(T)) + +one(C::FullAffine{T}) where T = FullAffine(one(T)) +one(::Type{FullAffine{T}}) where T = FullAffine(one(T)) + +#one(C::FullAffine) diff --git a/test/runtests.jl b/test/runtests.jl index f88961d..d238903 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,54 +1,41 @@ -using AffineArithmetic -using IntervalArithmetic -using Polynomials - +using AffineArithmetic, IntervalArithmetic, Polynomials, StaticArrays using Test -@testset "Constructor" begin - C = Affine(0.0) - @test C == Affine(0.0, Float64[]) - - C = Affine(1.0) - @test C == Affine(1.0, Float64[]) - - C = Affine(1.0, [3.0, 4.0]) - @test C.c == 1.0 - @test C.γ == [3.0, 4.0] - - reset_affine_index() -end - -reset_affine_index() +using AffineArithmetic: Aff @testset "Construction from intervals" begin - X = 1..3 X_a = Affine(X) - @test X_a == Affine(2.0, [1.0]) + X_a isa Affine Y = 2..4 Y_a = Affine(Y) - @test Y_a == Affine(3.0, [0.0, 1.0]) + @test Y_a.range == 2..4 + @test Y_a.affine == Aff(3.0, SVector{1, Float64}(1.0), 0..0) + + sum_a = X_a + Y_a + @test sum_a.range == 3..7 + @test sum_a.affine == Aff(5.0, SVector{1, Float64}(2.0), 0..0) - @test X_a + Y_a == Affine(5.0, [1.0, 1.0]) - @test X_a * Y_a == Affine(6.0, [3.0, 2.0, 1.0]) + prod_a = X_a * Y_a + @test prod_a.range == 2..12 + @test prod_a.affine == Aff(6.0, SVector{1, Float64}(5.0), 0..1) end @testset "Small powers and range" begin - X = 1..3 Y = Affine(X) f(x) = x^2 - x + 1 @test range(f(X)) == -1..9 - @test range(f(Y)) == -1..7 # affine is a little better + @test range(f(Y)) == 0..7 # affine is a little better g(x) = (x - 1)^3 @test range(g(X)) == 0..8 - @test range(g(Y)) == -6..8 # affine is significantly worse + @test range(g(Y)) == 0..8 # affine gives the same end reset_affine_index() @@ -60,12 +47,13 @@ reset_affine_index() Y = Affine(-1..1) XX = [X, Y] - @test XX == [Affine(0.0, [1.0]), Affine(0.0, [0.0, 1.0])] + @test XX == [Affine(Aff(0.0, SVector{1, Float64}(1.0), 0..0), -1..1), + Affine(Aff(0.0, SVector{1, Float64}(1.0), 0..0), -1..1)] - @test A * XX == [Affine(0.0, [0.5, 1.0]), Affine(0.0, [-0.5, 0.5])] + @test A * XX == [Affine(Aff(0.0, SVector{1, Float64}(1.5), 0..0), -1.5 .. 1.5), + Affine(Aff(0.0, SVector{1, Float64}(0.0), 0..0), 0..0)] end - reset_affine_index() @testset "Range of polynomial" begin @@ -79,3 +67,12 @@ reset_affine_index() @test (-70..70) ⊆ range(p2(x)) @test range(p2(y)) ⊆ 0.998..1.002 # affine is extremely much better! end + +@testset "Constructors with intervals" begin + X = Affine(1..2) + @test X isa Affine + @test X - X == Affine(0..0) + + x, y = affine(1..2, 3..4) + @test interval(x + y - x - y) == 0..0 +end