Skip to content

Update to IntervalArithmetic v0.22 #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
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
1 change: 0 additions & 1 deletion .codecov.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: actions/cache@v1
- uses: actions/cache@v4
env:
cache-name: cache-artifacts
with:
Expand Down
6 changes: 3 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
name = "AffineArithmetic"
uuid = "2e89c364-fad6-56cb-99bd-ebadcd2cf8d2"
authors = ["dpsanders"]
version = "0.2.1"
version = "0.3.0"

[deps]
IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"

[compat]
IntervalArithmetic = "0.16, 0.17, 0.18, 0.19, 0.20"
IntervalArithmetic = "0.22"
Polynomials = "0.6, 1, 2, 3"
StaticArrays = "0.12, 1"
julia = "1"
julia = "1.9"

[extras]
Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# `AffineArithmetic.jl`
# AffineArithmetic

[![Build Status](https://github.com/juliaintervals/AffineArithmetic.jl/workflows/CI/badge.svg)](https://github.com/juliaintervals/AffineArithmetic.jl/actions)

Expand Down
30 changes: 15 additions & 15 deletions src/aff.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function Base.show(io::IO, C::Aff{N,T}) where {N,T}
print(io, "⟨", C.c, "; ", C.γ, "; ", C.Δ, "⟩")
end

==(C::Aff, D::Aff) = C.c == D.c && C.γ == D.γ && C.Δ == D.Δ
==(C::Aff, D::Aff) = C.c == D.c && C.γ == D.γ && isequal_interval(C.Δ, D.Δ)

"""
Make an `Aff` based on an interval, which is number `i` of `n` total variables.
Expand All @@ -33,30 +33,30 @@ function Aff(X::Interval{T}, n, i) where {T}

γ = SVector(ntuple(j->i==j ? r : zero(r), n))

return Aff(c, γ, Interval{T}(0))
return Aff(c, γ, zero(Interval{T}))
end

+(x::Aff{N,T}, y::Aff{N,T}) where {N,T} = Aff(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::Aff) = C.c + sum(abs.(C.γ))*(-1..1) + C.Δ
interval(C::Aff) = C.c + sum(abs.(C.γ))*interval(-1, 1) + C.Δ


function *(x::Aff{N,T}, y::Aff{N,T}) where {N,T}
c = x.c * y.c

γ = x.c .* y.γ + y.c .* x.γ

Δ = (x.γ ⋅ y.γ) * (0..1) # ϵ_i^2
Δ = (x.γ ⋅ y.γ) * interval(0, 1) # ϵ_i^2

if N > 1
Δ += sum(x.γ[i] * y.γ[j] for i in 1:N, j in 1:N if i ≠ j) * (-1..1) # ϵ_i * ϵ_j
Δ += sum(x.γ[i] * y.γ[j] for i in 1:N, j in 1:N if i ≠ j) * interval(-1, 1) # ϵ_i * ϵ_j
end

Δ += (x.c + sum(abs.(x.γ))*(-1..1)) * y.Δ
Δ += (y.c + sum(abs.(y.γ))*(-1..1)) * x.Δ
Δ += (x.c + sum(abs.(x.γ))*interval(-1, 1)) * y.Δ
Δ += (y.c + sum(abs.(y.γ))*interval(-1, 1)) * x.Δ

Δ += x.Δ * y.Δ

Expand Down Expand Up @@ -97,12 +97,12 @@ end

Base.literal_pow(::typeof(^), x::Aff, ::Val{p}) where {p} = x^p

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 = 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 = Aff(3..5, 2, 1)
y = Aff(2..4, 2, 2)
# x = Aff(3..5, 2, 1)
# y = Aff(2..4, 2, 2)
#
# 3-x
# interval(3-x)
Expand Down Expand Up @@ -173,29 +173,29 @@ end

function Base.sqrt(x::Aff, X=interval(x))

a, b = X.lo, X.hi
a, b = bounds(X)

# @show a, b

# min-range: de Figuereido book, pg. 64
α = 1 / (2*√b)
ζ = (√b) / 2
δ = ( (√(b) - √(a))^2 / (2*√b) ) * (-1..1)
δ = ( (√(b) - √(a))^2 / (2*√b) ) * interval(-1, 1)

return affine_approx(x, α, ζ, δ)
end

function Base.inv(x::Aff, X=interval(x))

a, b = X.lo, X.hi
a, b = bounds(X)

# @show a, b

# min-range: de Figuereido book, pg. 70
α = -1 / (b^2)
d = interval(1/b - α*b, 1/a - α*a)
ζ = mid(d)
δ = radius(d) * (-1..1)
δ = radius(d) * interval(-1, 1)

if a < 0
ζ = -ζ
Expand Down
17 changes: 9 additions & 8 deletions src/affine.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,18 @@ 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 {T} = Affine(Interval(zero(T)))

one(::Affine{T}) where T = Affine(Interval(one(T)))
one(::Type{Affine{T}}) where T = Affine(Interval(one(T)))
zero(::Affine{N, T}) where {N, T} = Affine(zero(Interval{T}))
zero(::Type{Affine{N, T}}) where {N, T} = Affine(zero(Interval{T}))

one(::Affine{N, T}) where {N, T} = Affine(one(Interval{T}))
one(::Type{Affine{N, T}}) where {N, T} = Affine(one(Interval{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.affine == D.affine && C.range == D.range
==(C::Affine, D::Affine) = C.affine == D.affine && isequal_interval(C.range, D.range)

"""
Make an `Affine` based on an interval, which is number `i` of `n` total variables.
Expand All @@ -40,7 +41,7 @@ function Affine(X::Interval{T}, n, i) where {T}
end

Affine(X::Interval) = Affine(X, 1, 1)
Affine(X::Number) = Affine(Interval(X), 1, 1)
Affine(X::Number) = Affine(interval(X), 1, 1)

affine(Xs::Interval...) = Affine.(Xs, length(Xs), 1:length(Xs))

Expand All @@ -50,7 +51,7 @@ for op in (:+, :*, :-)

range = $op(x.range, y.range)

range = rangeinterval(affine)
range = intersect_interval(range, interval(affine))

return Affine(affine, range)
end
Expand All @@ -62,7 +63,7 @@ for op in (:sqrt, :inv)

range = $op(x.range)

range = rangeinterval(affine)
range = intersect_interval(range, interval(affine))

return Affine(affine, range)
end
Expand Down
3 changes: 1 addition & 2 deletions src/full_affine.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

const affine_index = [1] # which affine vector index to use

reset_affine_index() = affine_index[1] = 1
Expand Down Expand Up @@ -40,7 +39,7 @@ end
# conversion of numerical constant to affine:
FullAffine(c::Real) = FullAffine(c, Float64[])

range(C::FullAffine) = C.c + sum(abs.(C.γ))*(-1..1)
range(C::FullAffine) = C.c + sum(abs.(C.γ))*interval(-1, 1)
range(X::Interval) = X

# morally:
Expand Down
52 changes: 26 additions & 26 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,54 @@ using Test
using AffineArithmetic: Aff

@testset "Construction from intervals" begin
X = 1..3
X = interval(1, 3)
X_a = Affine(X)
X_a isa Affine

Y = 2..4
Y = interval(2, 4)
Y_a = Affine(Y)
@test Y_a.range == 2..4
@test Y_a.affine == Aff(3.0, SVector{1, Float64}(1.0), 0..0)
@test isequal_interval(Y_a.range, interval(2, 4))
@test Y_a.affine == Aff(3.0, SVector{1, Float64}(1.0), interval(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 isequal_interval(sum_a.range, interval(3, 7))
@test sum_a.affine == Aff(5.0, SVector{1, Float64}(2.0), interval(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)
@test isequal_interval(prod_a.range, interval(2, 12))
@test prod_a.affine == Aff(6.0, SVector{1, Float64}(5.0), interval(0, 1))
end

@testset "Small powers and range" begin
X = 1..3
X = interval(1, 3)
Y = Affine(X)

f(x) = x^2 - x + 1

@test range(f(X)) == -1..9
@test range(f(Y)) == 0..7 # affine is a little better
@test isequal_interval(range(f(X)), interval(-1, 9))
@test isequal_interval(range(f(Y)), interval(0, 7)) # affine is a little better


g(x) = (x - 1)^3

@test range(g(X)) == 0..8
@test range(g(Y)) == 0..8 # affine gives the same
@test isequal_interval(range(g(X)), interval(0, 8))
@test isequal_interval(range(g(Y)), interval(0, 8)) # affine gives the same
end

reset_affine_index()

@testset "Matrix multiplication" begin
A = 0.5 * [1 2; -1 1]

X = Affine(-1..1)
Y = Affine(-1..1)
X = Affine(interval(-1, 1))
Y = Affine(interval(-1, 1))

XX = [X, Y]
@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 XX == [Affine(Aff(0.0, SVector{1, Float64}(1.0), interval(0)), interval(-1, 1)),
Affine(Aff(0.0, SVector{1, Float64}(1.0), interval(0)), interval(-1, 1))]

@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)]
@test A * XX == [Affine(Aff(0.0, SVector{1, Float64}(1.5), interval(0)), interval(-1.5, 1.5)),
Affine(Aff(0.0, SVector{1, Float64}(0.0), interval(0)), interval(0))]
end

reset_affine_index()
Expand All @@ -61,18 +61,18 @@ reset_affine_index()
p = Polynomial([-3, 1]) # x - 3
p2 = p^8

x = 4 ± 1e-4
x = interval(4, 1e-4; format = :midpoint)
y = Affine(x)

@test (-70..70)range(p2(x))
@test range(p2(y))0.998..1.002 # affine is extremely much better!
@test issubset_interval(interval(-70, 70), range(p2(x)))
@test issubset_interval(range(p2(y)), interval(0.998, 1.002)) # affine is extremely much better!
end

@testset "Constructors with intervals" begin
X = Affine(1..2)
X = Affine(interval(1, 2))
@test X isa Affine
@test X - X == Affine(0..0)
@test X - X == Affine(interval(0))

x, y = affine(1..2, 3..4)
@test interval(x + y - x - y) == 0..0
x, y = affine(interval(1, 2), interval(3, 4))
@test isequal_interval(interval(x + y - x - y), interval(0))
end
Loading