From 1070130273a13b956ceff191f91f5c34f08afed6 Mon Sep 17 00:00:00 2001 From: Matthew Wilhelm Date: Wed, 16 Sep 2020 12:27:58 -0400 Subject: [PATCH 01/13] Transfer Files to Fork --- .codecov.yml | 1 + .travis.yml | 29 +++ Project.toml | 7 +- README.md | 6 +- src/StaticTaylorSeries.jl | 225 ++++------------------ src/arithmetic.jl | 106 +++++++++++ src/auxiliary.jl | 28 +++ src/constructors.jl | 42 +++++ src/conversion.jl | 22 +++ src/evaluate.jl | 30 +++ src/functions.jl | 381 ++++++++++++++++++++++++++++++++++++++ src/other_functions.jl | 27 +++ src/power.jl | 69 +++++++ src/printing.jl | 19 ++ test/runtests.jl | 99 +++++++++- 15 files changed, 892 insertions(+), 199 deletions(-) create mode 100644 .codecov.yml create mode 100644 .travis.yml create mode 100644 src/arithmetic.jl create mode 100644 src/auxiliary.jl create mode 100644 src/constructors.jl create mode 100644 src/conversion.jl create mode 100644 src/evaluate.jl create mode 100644 src/functions.jl create mode 100644 src/other_functions.jl create mode 100644 src/power.jl create mode 100644 src/printing.jl diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000..d6c6c1a --- /dev/null +++ b/.codecov.yml @@ -0,0 +1 @@ +comment: false diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..002c261 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,29 @@ +language: julia +#codecov: true +coveralls: true + +os: + - linux + - osx + - windows + +julia: + - 1.2 + - 1.3 + - 1.4 + - 1.5 + - nightly + +jobs: + allow_failures: + - julia: nightly + +after_success: + - julia -e 'import Pkg; Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder()); Codecov.submit(process_folder())' + +notifications: + email: false + +addons: + apt_packages: + - gfortran diff --git a/Project.toml b/Project.toml index b852ed9..9920d56 100644 --- a/Project.toml +++ b/Project.toml @@ -3,8 +3,13 @@ uuid = "91725f60-9ab6-11e9-00d0-87fe6d51fd3a" authors = ["David Sanders "] version = "0.1.0" +[deps] +Requires = "ae029012-a4dd-5104-9daa-d747884805df" +TaylorSeries = "6aa5eb33-94cf-58f4-a9d0-e4b2c4fc25ea" + [compat] -julia = "1" +Requires = "~1" +TaylorSeries = "~0.10" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/README.md b/README.md index a31cce3..1faa401 100644 --- a/README.md +++ b/README.md @@ -1 +1,5 @@ -# StaticTaylorSeries +# StaticTaylorSeries.jl +A Static 1-D Taylor Series Library + +This package contains a static implementation of the Taylor1 structure in TaylorSeries.jl. We intend to +eventually merge this functionality into the TaylorSeries.jl package. diff --git a/src/StaticTaylorSeries.jl b/src/StaticTaylorSeries.jl index 38f977d..19fe572 100644 --- a/src/StaticTaylorSeries.jl +++ b/src/StaticTaylorSeries.jl @@ -1,192 +1,43 @@ module StaticTaylorSeries -export StaticTaylor - -""" -Static Taylor series with variable V, length N (i.e. order N-1) and element type T. -""" -struct StaticTaylor{N,T} - coeffs::NTuple{N,T} -end - - -function coeffstring(t::StaticTaylor, i, variable=:x) - - if i == 1 # order 0 - return string(t.coeffs[i]) - - elseif i == 2 # order 1 - return string(t.coeffs[i], variable) - - else - return string(t.coeffs[i], variable, "^", i-1) - end -end - - -function print_taylor(io::IO, t::StaticTaylor, variable=:x) - - print(io, "(" * join([coeffstring(t, i, variable) for i in 1:length(t.coeffs)], " + ") * ")") - -end - -function Base.show(io::IO, t::StaticTaylor) - print_taylor(io, t) -end - -function Base.show(io::IO, t::StaticTaylor{N,T}) where {N, T<:StaticTaylor} - print_taylor(io, t, :y) -end - -#StaticTaylor(iterable...) = StaticTaylor(SVector(iterable...)) - -StaticTaylor{N}(v::NTuple{N,T}) where {N,T} = StaticTaylor(v) - - - -#StaticTaylor{N}(iterable) where {N} = StaticTaylor{N}(iterable) - -StaticTaylor{N}(iterable...) where {N} = StaticTaylor{N}(iterable) - -# StaticTaylor(v) = StaticTaylor(v) - -StaticTaylor(iterable...) = StaticTaylor{length(iterable)}(iterable) - -import Base:getindex, length, eltype - -getindex(s::StaticTaylor, i::Integer) = s.coeffs[i+1] - -length(s::StaticTaylor{N,T}) where {N,T} = N - -eltype(s::StaticTaylor{N,T}) where {N,T} = T - - -import Base: +, -, * ,^ - -function +(s::StaticTaylor{N,T}, t::StaticTaylor{N,T}) where {N,T} - return StaticTaylor(s.coeffs .+ t.coeffs) +using Requires, TaylorSeries + +import Base: ==, +, -, *, /, ^ + +import Base: iterate, size, eachindex, firstindex, lastindex, + eltype, length, getindex, setindex!, axes, copyto! + +import Base: zero, one, zeros, ones, isinf, isnan, iszero, + convert, promote_rule, promote, show, + real, imag, conj, adjoint, + rem, mod, mod2pi, abs, abs2, + sqrt, exp, log, sin, cos, tan, + asin, acos, atan, sinh, cosh, tanh, + power_by_squaring, + rtoldefault, isfinite, isapprox, rad2deg, deg2rad + +export STaylor1 + +export getcoeff, derivative, integrate, differentiate, + evaluate, evaluate!, inverse, set_taylor1_varname, + show_params_TaylorN, show_monomials, displayBigO, use_show_default, + get_order, get_numvars, set_variables, get_variables, + get_variable_names, get_variable_symbols, + taylor_expand, update!, constant_term, linear_polynomial, + normalize_taylor + +include("constructors.jl") +include("conversion.jl") +include("auxiliary.jl") +include("arithmetic.jl") +include("power.jl") +include("functions.jl") +include("other_functions.jl") +include("evaluate.jl") +include("printing.jl") + +function __init__() + @require IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" include("intervals.jl") end -function +(s::StaticTaylor{N,T}, α::Real) where {N,T} - return StaticTaylor{N,T}(ntuple(i -> i == 1 ? s.coeffs[1] + α : s.coeffs[i], N)) -end - -+(α::Real, s::StaticTaylor) = s + α - --(s::StaticTaylor) = StaticTaylor(.-(s.coeffs)) - -function -(s::StaticTaylor{N,T}, t::StaticTaylor{N,T}) where {N,T} - return StaticTaylor(s.coeffs .- t.coeffs) -end - --(s::StaticTaylor, α::Real) = s + (-α) - --(α::Real, s::StaticTaylor) = -(s - a) - - - - - -Base.literal_pow(::typeof(^), x::StaticTaylor, ::Val{p}) where {p} = x^p - -^(x::StaticTaylor, n::Integer) = Base.power_by_squaring(x, n) - - - -# function *(s::StaticTaylor{N,T}, t::StaticTaylor{N,T}) where {N,T} -# v = SVector(ntuple(k->sum(s[i]*t[k-1-i] for i in 0:k-1), Val(N))) -# return StaticTaylor(v) -# end - -# The following is modified from StaticUnivariatePolynomials.jl -@generated function Base.:*(p1::StaticTaylor{N,T}, p2::StaticTaylor{N,T}, max_degree=N) where {N, T} - exprs = Any[nothing for i in 1:N] - for i in 0 : N-1 # order is N-1 - for j in 0 : N-1 - k = i + j + 1 # setindex does not have offset - - if k > max_degree - continue - end - - if exprs[k] === nothing - exprs[k] = :(p1[$i] * p2[$j]) - else - exprs[k] = :(muladd(p1[$i], p2[$j], $(exprs[k]))) - end - end - end - - # Core.println("Generated code with N=$N:") - # Core.println(exprs) - # Core.println() - - return quote - Base.@_inline_meta - StaticTaylor{N,T}(tuple($(exprs...))) - end -end - -@generated function mult(p1::StaticTaylor{N,T}, p2::StaticTaylor{N,T}, ::Val{max_degree}) where {N, T, max_degree} - exprs = Any[nothing for i in 1:max_degree] - for i in 0 : N-1 # order is N-1 - for j in 0 : N-1 - k = i + j + 1 # setindex does not have offset - - if k > max_degree - continue - end - - if exprs[k] === nothing - exprs[k] = :(p1[$i] * p2[$j]) - else - exprs[k] = :(muladd(p1[$i], p2[$j], $(exprs[k]))) - end - end - end - - # Core.println("Generated code with N=$N:") - # Core.println(exprs) - # Core.println() - - return quote - Base.@_inline_meta - StaticTaylor{max_degree,T}(tuple($(exprs...))) - end -end -# -# @generated function *(p1::StaticTaylor{N,T}, p2::StaticTaylor{N,T}) where {N, T} -# exprs = Any[nothing for i in 1:N] -# for i in 0 : N-1 # order is N-1 -# for j in 0 : N-1 -# k = i + j + 1 # setindex does not have offset -# -# if k > N -# continue -# end -# -# if exprs[k] === nothing -# exprs[k] = :(p1[$i] * p2[$j]) -# else -# exprs[k] = :(muladd(p1[$i], p2[$j], $(exprs[k]))) -# end -# end -# end -# -# # Core.println("Generated code with N=$N:") -# # Core.println(exprs) -# # Core.println() -# -# return quote -# Base.@_inline_meta -# StaticTaylor{N,T}(tuple($(exprs...))) -# end -# end - - -*(s::StaticTaylor, α::Real) = StaticTaylor(α .* s.coeffs) -*(α::Real, s::StaticTaylor) = s * α - -/(s::StaticTaylor, α::Real) = StaticTaylor(s.coeffs ./ α) - end # module diff --git a/src/arithmetic.jl b/src/arithmetic.jl new file mode 100644 index 0000000..19fd305 --- /dev/null +++ b/src/arithmetic.jl @@ -0,0 +1,106 @@ +==(a::STaylor1, b::STaylor1) = (a.coeffs == b.coeffs) + +iszero(a::STaylor1) = all(iszero, a.coeffs) + +zero(::STaylor1{N,T}) where {N, T<:Number} = STaylor1(zero(T), Val(N-1)) +one(::STaylor1{N,T}) where {N, T<:Number} = STaylor1(one(T), Val(N-1)) + +@inline +(a::STaylor1{N,T}, b::STaylor1{N,T}) where {N, T<:Number} = STaylor1(a.coeffs .+ b.coeffs) +@inline -(a::STaylor1{N,T}, b::STaylor1{N,T}) where {N, T<:Number} = STaylor1(a.coeffs .- b.coeffs) +@inline +(a::STaylor1) = a +@inline -(a::STaylor1) = STaylor1(.- a.coeffs) + +function +(a::STaylor1{N,T}, b::T) where {N, T<:Number} + STaylor1{N,T}(ntuple(i -> i == 1 ? a.coeffs[1] + b : a.coeffs[i], Val(N))) +end +function +(b::T, a::STaylor1{N,T}) where {N, T<:Number} + STaylor1{N,T}(ntuple(i -> i == 1 ? a.coeffs[1] + b : a.coeffs[i], Val(N))) +end +function -(a::STaylor1{N,T}, b::T) where {N, T<:Number} + STaylor1{N,T}(ntuple(i -> i == 1 ? a.coeffs[1] - b : a.coeffs[i], Val(N))) +end +-(b::T, a::STaylor1{N,T}) where {N, T<:Number} = b + (-a) + +#+(a::STaylor1{N,T}, b::S) where {N, T<:NumberNotSeries, S<:NumberNotSeries} = +(promote(a,b)...) +#+(a::STaylor1{N,T}, b::STaylor1{N,S}) where {N, T<:NumberNotSeries, S<:NumberNotSeries} = +(promote(a,b)...) +#+(a::STaylor1{N,T}, b::S) where {N, T<:NumberNotSeries, S<:NumberNotSeries} = +(promote(a,b)...) +#+(b::S, a::STaylor1{N,T}) where {N, T<:NumberNotSeries, S<:NumberNotSeries} = +(promote(b,a)...) + +#-(a::STaylor1{N,T}, b::STaylor1{N,S}) where {N, T<:NumberNotSeries, S<:NumberNotSeries} = -(promote(a,b)...) +#-(a::STaylor1{N,T}, b::S) where {N, T<:NumberNotSeries, S<:NumberNotSeries} = -(promote(a,b)...) +#-(b::S, a::STaylor1{N,T}) where {N, T<:NumberNotSeries, S<:NumberNotSeries} = -(promote(b,a)...) + +@generated function *(x::STaylor1{N,T}, y::STaylor1{N,T}) where {T<:Number,N} + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + for j = 1:N + ex_line = :(x.coeffs[1]*y.coeffs[$j]) + for k = 2:j + ex_line = :($ex_line + x.coeffs[$k]*y.coeffs[$(j-k+1)]) + end + sym = syms[j] + ex_line = :($sym = $ex_line) + ex_calc.args[j] = ex_line + end + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + return quote + Base.@_inline_meta + $ex_calc + return STaylor1{N,T}($exout) + end +end + + +function *(a::STaylor1{N,T}, b::T) where {N, T<:Number} + STaylor1{N,T}(b .* a.coeffs) +end +function *(b::T, a::STaylor1{N,T}) where {N, T<:Number} + STaylor1{N,T}(b .* a.coeffs) +end + +function /(a::STaylor1{N,T}, b::T) where {N, T<:Number} + STaylor1{N,T}(a.coeffs ./ b) +end + +@generated function /(a::STaylor1{N,T}, b::STaylor1{N,T}) where {N,T<:Number} + + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + + # add error + ex_line = quote + if iszero(b[0]) + throw(ArgumentError("""The 0th order STaylor1 coefficient + must be non-zero for b, (a/b)(x) is + not differentiable at x=0).""")) + end + end + ex_calc.args[1] = ex_line + + # add recursion relation + for j = 0:(N-1) + ex_line = :(a[$(j)]) + for k = 1:j + sym = syms[j-k+1] + ex_line = :($ex_line - $sym*b[$k]) + end + sym = syms[j+1] + ex_line = :($sym = ($ex_line)/b[0]) + ex_calc.args[j+2] = ex_line + end + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + return quote + Base.@_inline_meta + $ex_calc + return STaylor1{N,T}($exout) + end +end diff --git a/src/auxiliary.jl b/src/auxiliary.jl new file mode 100644 index 0000000..6cea2d0 --- /dev/null +++ b/src/auxiliary.jl @@ -0,0 +1,28 @@ +# getindex STaylor1 +getindex(a::STaylor1, n::Int) = a.coeffs[n+1] +getindex(a::STaylor1, u::UnitRange{Int}) = a.coeffs[u .+ 1] +getindex(a::STaylor1, c::Colon) = a.coeffs[c] +getindex(a::STaylor1, u::StepRange{Int,Int}) = a.coeffs[u .+ 1] + +@inline iterate(a::STaylor1{N,T}, state=0) where {N, T<:Number} = state > N-1 ? nothing : (a.coeffs[state+1], state+1) +@inline firstindex(a::STaylor1) = 0 +@inline lastindex(a::STaylor1{N,T}) where {N, T<:Number} = N-1 +@inline eachindex(s::STaylor1{N,T}) where {N, T<:Number} = UnitRange(0, N-1) +@inline size(s::STaylor1{N,T}) where {N, T<:Number} = N +@inline length(s::STaylor1{N,T}) where {N, T<:Number} = N +@inline get_order(s::STaylor1{N,T}) where {N, T<:Number} = N - 1 +@inline eltype(s::STaylor1{N,T}) where {N, T<:Number} = T +@inline axes(a::STaylor1) = () + +function Base.findfirst(a::STaylor1{N,T}) where {N, T<:Number} + first = findfirst(x->!iszero(x), a.coeffs) + isa(first, Nothing) && return -1 + return first-1 +end +# Finds the last non-zero entry; extended to Taylor1 +function Base.findlast(a::STaylor1{N,T}) where {N, T<:Number} + last = findlast(x->!iszero(x), a.coeffs) + isa(last, Nothing) && return -1 + return last-1 +end +constant_term(a::STaylor1) = a[0] diff --git a/src/constructors.jl b/src/constructors.jl new file mode 100644 index 0000000..ccbffff --- /dev/null +++ b/src/constructors.jl @@ -0,0 +1,42 @@ +######################### STaylor1 +""" + STaylor1{N,T<:Number} <: AbstractSeries{T} + +DataType for polynomial expansions in one independent variable. + +**Fields:** + +- `coeffs :: NTuple{N,T}` Expansion coefficients; the ``i``-th + component is the coefficient of degree ``i-1`` of the expansion. + +Note that `STaylor1` variables are callable. For more information, see +[`evaluate`](@ref). +""" +struct STaylor1{N,T<:Number} <: TaylorSeries.AbstractSeries{T} + coeffs::NTuple{N,T} +end + +## Outer constructors ## + +""" + STaylor1(x::T, v::Val{N}) + +Shortcut to define the independent variable of a `STaylor1{N,T}` polynomial of +given `N` with constant term equal to `x`. +""" +@generated function STaylor1(x::T, v::Val{N}) where {N,T<:Number} + y = Any[:(zero($T)) for i=1:N] + tup = :((x,)) + push!(tup.args, y...) + return quote + Base.@_inline_meta + STaylor1{(N+1),T}($tup) + end +end +function STaylor1(coeffs::Vector{T}, l::Val{L}, v::Val{N}) where {L,N,T<:Number} + STaylor1{(N+1),T}(ntuple(i -> (i > L+1) ? coeffs[i] : zero(T), N+1)) +end +@inline function STaylor1(coeffs::Vector{T}) where {T<:Number} + STaylor1{length(coeffs),T}(tuple(coeffs...)) +end +@inline STaylor1(x::STaylor1{N,T}) where {N,T<:Number} = x diff --git a/src/conversion.jl b/src/conversion.jl new file mode 100644 index 0000000..6dc9513 --- /dev/null +++ b/src/conversion.jl @@ -0,0 +1,22 @@ +# Conversion for STaylor1 +function convert(::Type{STaylor1{N,Rational{T}}}, a::STaylor1{N,S}) where {N,T<:Integer, S<:AbstractFloat} + STaylor1{N,T}(rationalize.(a[:])) +end +function convert(::Type{STaylor1{N,T}}, b::Array{T,1}) where {N,T<:Number} + @assert N == length(b) + STaylor1{N,T}(b) +end +function convert(::Type{STaylor1{N,T}}, b::Array{S,1}) where {N,T<:Number, S<:Number} + @assert N == length(b) + STaylor1{N,T}(convert(Array{T,1},b)) +end +convert(::Type{STaylor1{N,T}}, a::STaylor1{N,T}) where {N,T<:Number} = a +convert(::Type{STaylor1{N,T}}, b::S) where {N, T<:Number, S<:Number} = STaylor1(convert(T,b), Val(N)) +convert(::Type{STaylor1{N,T}}, b::T) where {N, T<:Number} = STaylor1(b, Val(N)) + +promote_rule(::Type{STaylor1{N,T}}, ::Type{STaylor1{N,T}}) where {N, T<:Number} = STaylor1{N,T} +promote_rule(::Type{STaylor1{N,T}}, ::Type{STaylor1{N,S}}) where {N, T<:Number, S<:Number} = STaylor1{N, promote_type(T,S)} +promote_rule(::Type{STaylor1{N,T}}, ::Type{Array{T,1}}) where {N, T<:Number} = STaylor1{N,T} +promote_rule(::Type{STaylor1{N,T}}, ::Type{Array{S,1}}) where {N, T<:Number, S<:Number} = STaylor1{N,promote_type(T,S)} +promote_rule(::Type{STaylor1{N,T}}, ::Type{T}) where {N, T<:Number} = STaylor1{N,T} +promote_rule(::Type{STaylor1{N,T}}, ::Type{S}) where {N, T<:Number, S<:Number} = STaylor1{N,promote_type(T,S)} diff --git a/src/evaluate.jl b/src/evaluate.jl new file mode 100644 index 0000000..57092f9 --- /dev/null +++ b/src/evaluate.jl @@ -0,0 +1,30 @@ +function evaluate(a::STaylor1{N,T}, dx::T) where {N, T<:Number} + @inbounds suma = a[N-1] + @inbounds for k in (N-1):-1:0 + suma = suma*dx + a[k] + end + suma +end +#= +function evaluate(a::STaylor1{N,T}, dx::S) where {N, T<:Number, S<:Number} + suma = a[N-1]*one(dx) + @inbounds for k in (N-1):-1:0 + suma = suma*dx + a[k] + end + suma +end +=# +evaluate(a::STaylor1{N,T}) where {N, T<:Number} = a[0] + +evaluate(x::Union{Array{STaylor1{N,T}}, SubArray{STaylor1{N,T}}}, δt::S) where + {N, T<:Number, S<:Number} = evaluate.(x, δt) +evaluate(a::Union{Array{STaylor1{N,T}}, SubArray{STaylor1{N,T}}}) where + {N, T<:Number} = evaluate.(a, zero(T)) + +(p::STaylor1)(x) = evaluate(p, x) +(p::STaylor1)() = evaluate(p) + +(p::Array{STaylor1{N,T}})(x) where {N,T<:Number} = evaluate.(p, x) +(p::SubArray{STaylor1{N,T}})(x) where {N,T<:Number} = evaluate.(p, x) +(p::Array{STaylor1{N,T}})() where {N,T<:Number} = evaluate.(p) +(p::SubArray{STaylor1{N,T}})() where {N,T<:Number} = evaluate.(p) diff --git a/src/functions.jl b/src/functions.jl new file mode 100644 index 0000000..67729c2 --- /dev/null +++ b/src/functions.jl @@ -0,0 +1,381 @@ +# Functions for STaylor1 +@generated function exp(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + + sym = syms[1] + ex_line = :($(syms[1]) = exp(a[0])) + ex_calc.args[1] = ex_line + + for k in 1:(N-1) + kT = convert(T,k) + sym = syms[k+1] + ex_line = :($kT * a[$k] * $(syms[1])) + @inbounds for i = 1:k-1 + ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + end + ex_line = :(($ex_line)/$kT) + ex_line = :($sym = $ex_line) + ex_calc.args[k+1] = ex_line + end + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + return quote + Base.@_inline_meta + $ex_calc + return STaylor1{N,T}($exout) + end +end + +@generated function log(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + + (N >= 1) && (ex_calc.args[1] = :($(syms[1]) = log(constant_term(a)))) + (N >= 2) && (ex_calc.args[2] = :($(syms[2]) = a[1]/constant_term(a))) + + for k in 2:(N-1) + ex_line = :($(k-1)*a[1]*$(syms[k])) + @inbounds for i = 2:k-1 + ex_line = :($ex_line + $(k-i)*a[$i] * $(syms[k+1-i])) + end + ex_line = :((a[$k] - ($ex_line)/$(convert(T,k)))/constant_term(a)) + ex_line = :($(syms[k+1]) = $ex_line) + ex_calc.args[k+1] = ex_line + end + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + + return quote + Base.@_inline_meta + iszero(constant_term(a)) && throw(ArgumentError(""" + The 0-th order `STaylor1` coefficient must be non-zero + in order to expand `log` around 0. + """)) + $ex_calc + return STaylor1{N,T}($exout) + end +end + +# Functions for STaylor1 +@generated function sin(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + + sym = syms[1] + ex_line = :($(sym) = sin(a[0])) + ex_calc.args[1] = ex_line + + #= + for k in 1:(N-1) + kT = convert(T,k) + sym = syms[k+1] + ex_line = :($kT * a[$k] * $(syms[1])) + @inbounds for i = 1:k-1 + ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + end + ex_line = :(($ex_line)/$kT) + ex_line = :($sym = $ex_line) + ex_calc.args[k+1] = ex_line + end + =# + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + return quote + Base.@_inline_meta + $ex_calc + return STaylor1{N,T}($exout) + end +end + +# Functions for STaylor1 +@generated function cos(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + + sym = syms[1] + ex_line = :($(sym) = cos(a[0])) + ex_calc.args[1] = ex_line + + #= + for k in 1:(N-1) + kT = convert(T,k) + sym = syms[k+1] + ex_line = :($kT * a[$k] * $(syms[1])) + @inbounds for i = 1:k-1 + ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + end + ex_line = :(($ex_line)/$kT) + ex_line = :($sym = $ex_line) + ex_calc.args[k+1] = ex_line + end + =# + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + return quote + Base.@_inline_meta + $ex_calc + return STaylor1{N,T}($exout) + end +end + +# Functions for STaylor1 +@generated function tan(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + + sym = syms[1] + ex_line = :($(sym) = tan(a[0])) + ex_calc.args[1] = ex_line + + #= + for k in 1:(N-1) + kT = convert(T,k) + sym = syms[k+1] + ex_line = :($kT * a[$k] * $(syms[1])) + @inbounds for i = 1:k-1 + ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + end + ex_line = :(($ex_line)/$kT) + ex_line = :($sym = $ex_line) + ex_calc.args[k+1] = ex_line + end + =# + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + return quote + Base.@_inline_meta + $ex_calc + return STaylor1{N,T}($exout) + end +end + +# Functions for STaylor1 +@generated function asin(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + + sym = syms[1] + ex_line = :($(sym) = asin(a[0])) + ex_calc.args[1] = ex_line + + #= + for k in 1:(N-1) + kT = convert(T,k) + sym = syms[k+1] + ex_line = :($kT * a[$k] * $(syms[1])) + @inbounds for i = 1:k-1 + ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + end + ex_line = :(($ex_line)/$kT) + ex_line = :($sym = $ex_line) + ex_calc.args[k+1] = ex_line + end + =# + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + return quote + Base.@_inline_meta + $ex_calc + return STaylor1{N,T}($exout) + end +end + +# Functions for STaylor1 +@generated function acos(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + + sym = syms[1] + ex_line = :($(sym) = acos(a[0])) + ex_calc.args[1] = ex_line + + #= + for k in 1:(N-1) + kT = convert(T,k) + sym = syms[k+1] + ex_line = :($kT * a[$k] * $(syms[1])) + @inbounds for i = 1:k-1 + ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + end + ex_line = :(($ex_line)/$kT) + ex_line = :($sym = $ex_line) + ex_calc.args[k+1] = ex_line + end + =# + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + return quote + Base.@_inline_meta + $ex_calc + return STaylor1{N,T}($exout) + end +end + +# Functions for STaylor1 +@generated function atan(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + + sym = syms[1] + ex_line = :($(sym) = atan(a[0])) + ex_calc.args[1] = ex_line + + #= + for k in 1:(N-1) + kT = convert(T,k) + sym = syms[k+1] + ex_line = :($kT * a[$k] * $(syms[1])) + @inbounds for i = 1:k-1 + ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + end + ex_line = :(($ex_line)/$kT) + ex_line = :($sym = $ex_line) + ex_calc.args[k+1] = ex_line + end + =# + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + return quote + Base.@_inline_meta + $ex_calc + return STaylor1{N,T}($exout) + end +end + +# Functions for STaylor1 +@generated function sinh(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + + sym = syms[1] + ex_line = :($(sym) = sinh(a[0])) + ex_calc.args[1] = ex_line + + #= + for k in 1:(N-1) + kT = convert(T,k) + sym = syms[k+1] + ex_line = :($kT * a[$k] * $(syms[1])) + @inbounds for i = 1:k-1 + ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + end + ex_line = :(($ex_line)/$kT) + ex_line = :($sym = $ex_line) + ex_calc.args[k+1] = ex_line + end + =# + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + return quote + Base.@_inline_meta + $ex_calc + return STaylor1{N,T}($exout) + end +end + +# Functions for STaylor1 +@generated function cosh(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + + sym = syms[1] + ex_line = :($(sym) = cosh(a[0])) + ex_calc.args[1] = ex_line + + #= + for k in 1:(N-1) + kT = convert(T,k) + sym = syms[k+1] + ex_line = :($kT * a[$k] * $(syms[1])) + @inbounds for i = 1:k-1 + ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + end + ex_line = :(($ex_line)/$kT) + ex_line = :($sym = $ex_line) + ex_calc.args[k+1] = ex_line + end + =# + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + return quote + Base.@_inline_meta + $ex_calc + return STaylor1{N,T}($exout) + end +end + +# Functions for STaylor1 +@generated function tanh(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + + sym = syms[1] + ex_line = :($(sym) = tanh(a[0])) + ex_calc.args[1] = ex_line + + #= + for k in 1:(N-1) + kT = convert(T,k) + sym = syms[k+1] + ex_line = :($kT * a[$k] * $(syms[1])) + @inbounds for i = 1:k-1 + ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + end + ex_line = :(($ex_line)/$kT) + ex_line = :($sym = $ex_line) + ex_calc.args[k+1] = ex_line + end + =# + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + return quote + Base.@_inline_meta + $ex_calc + return STaylor1{N,T}($exout) + end +end diff --git a/src/other_functions.jl b/src/other_functions.jl new file mode 100644 index 0000000..f1385ba --- /dev/null +++ b/src/other_functions.jl @@ -0,0 +1,27 @@ +for f in (:real, :imag, :conj) + @eval ($f)(a::STaylor1{N,T}) where {N,T<:Number} = STaylor1{N,T}(($f).(a.coeffs)) +end + +adjoint(a::STaylor1) = conj(a) +isinf(a::STaylor1) = any(isinf.(a.coeffs)) +isnan(a::STaylor1) = any(isnan.(a.coeffs)) + +function abs(a::STaylor1{N,T}) where {N,T<:Real} + if a[0] > zero(T) + return a + elseif a[0] < zero(T) + return -a + else + throw(ArgumentError( + """The 0th order Taylor1 coefficient must be non-zero + (abs(x) is not differentiable at x=0).""")) + end +end + +abs2(a::STaylor1{N,T}) where {N,T<:Real} = a^2 + +deg2rad(z::STaylor1{N, T}) where {N, T<:AbstractFloat} = z * (convert(T, pi) / 180) +deg2rad(z::STaylor1{N, T}) where {N, T<:Real} = z * (convert(float(T), pi) / 180) + +rad2deg(z::STaylor1{N, T}) where {N, T<:AbstractFloat} = z * (180 / convert(T, pi)) +rad2deg(z::STaylor1{N, T}) where {N, T<:Real} = z * (180 / convert(float(T), pi)) diff --git a/src/power.jl b/src/power.jl new file mode 100644 index 0000000..865c6a5 --- /dev/null +++ b/src/power.jl @@ -0,0 +1,69 @@ +function ^(a::STaylor1{N,T}, n::Integer) where {N,T<:Real} + n == 0 && return one(a) + n == 1 && return a + n == 2 && return TaylorSeries.square(a) + #n < 0 && return a^float(n) + return power_by_squaring(a, n) +end + +^(a::STaylor1{N,T}, b::STaylor1{N,T}) where {N,T<:Number} = exp(b*log(a)) + +function power_by_squaring(x::STaylor1{N,T}, p::Integer) where {N,T<:Number} + p == 1 && return x + p == 0 && return one(x) + p == 2 && return square(x) + t = trailing_zeros(p) + 1 + p >>= t + + while (t -= 1) > 0 + x = square(x) + end + + y = x + while p > 0 + t = trailing_zeros(p) + 1 + p >>= t + while (t -= 1) ≥ 0 + x = square(x) + end + y *= x + end + + return y +end + + +@generated function square(a::STaylor1{N,T}) where {N, T<:Number} + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + + sym = syms[1] + ex_line = :($(syms[1]) = a[0]^2) + ex_calc.args[1] = ex_line + + for k in 1:(N-1) + kodd = k%2 + kend = div(k - 2 + kodd, 2) + ex_line = :(a[0] * a[$k]) + @inbounds for i = 1:kend + ex_line = :($ex_line + a[$i] * a[$(k-i)]) + end + ex_line = :(2.0*($ex_line)) # float(2)* TODO: ADD BACK IN + if kodd !== 1 + ex_line = :($ex_line +a[$(div(k,2))]^2) + end + ex_line = :($(syms[k+1]) = $ex_line) + ex_calc.args[k+1] = ex_line + end + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + return quote + Base.@_inline_meta + $ex_calc + return STaylor1{N,T}($exout) + end +end diff --git a/src/printing.jl b/src/printing.jl new file mode 100644 index 0000000..1c59ca8 --- /dev/null +++ b/src/printing.jl @@ -0,0 +1,19 @@ +# printing for static taylor +function coeffstring(t::STaylor1, i, variable=:t) + if i == 1 # order 0 + return string(t.coeffs[i]) + end + if i == 2 # order 1 + return string(t.coeffs[i], variable) + end + return string(t.coeffs[i], variable, "^", i-1) +end + +function print_taylor(io::IO, t::STaylor1, variable=:t) + print(io, "(" * join([coeffstring(t, i, variable) for i in 1:length(t.coeffs)], " + ") * ")") +end + +Base.show(io::IO, t::STaylor1) = print_taylor(io, t) +function Base.show(io::IO, t::STaylor1{N,T}) where {N, T<:STaylor1} + print_taylor(io, t, :t) +end diff --git a/test/runtests.jl b/test/runtests.jl index f59fb86..455653b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,15 +1,94 @@ -using StaticTaylorSeries -using Test +# Skipping tests with intervals, since IntervalArithmetic.jl requires Julia v1.1+ +if !(VERSION < v"1.1" && testfile == "intervals.jl") + using TaylorSeries, STaylor + using Test -@testset "StaticTaylorSeries.jl" begin - t = StaticTaylor(1, 2, 3) + function test_vs_Taylor1(x, y) + flag = true + for i in 0:2 + if x[i] !== y[i] + flag = false + break + end + end + flag + end - @test t + 1 == StaticTaylor(2, 2, 3) - @test t - 1 == StaticTaylor(0, 2, 3) - @test t * 2 == StaticTaylor(2, 4, 6) + @testset "Tests for STaylor1 expansions" begin - @test t^2 == StaticTaylor(1, 4, 10) + @test STaylor1 <: AbstractSeries + @test STaylor1{1,Float64} <: AbstractSeries{Float64} + @test STaylor1([1.0, 2.0]) == STaylor1((1.0, 2.0)) + @test STaylor1(STaylor1((1.0, 2.0))) == STaylor1((1.0, 2.0)) + @test STaylor1(1.0, Val(2)) == STaylor1((1.0, 0.0, 0.0)) - t = StaticTaylor(1.0, 2.0, 3.0) - @test t / 2 == StaticTaylor(0.5, 1.0, 1.5) + + @test +STaylor1([1.0, 2.0, 3.0]) == STaylor1([1.0, 2.0, 3.0]) + @test -STaylor1([1.0, 2.0, 3.0]) == -STaylor1([1.0, 2.0, 3.0]) + @test STaylor1([1.0, 2.0, 3.0]) + STaylor1([3.0, 2.0, 3.0]) == STaylor1([4.0, 4.0, 6.0]) + @test STaylor1([1.0, 2.0, 3.0]) - STaylor1([3.0, 2.0, 4.0]) == STaylor1([-2.0, 0.0, -1.0]) + @test STaylor1([1.0, 2.0, 3.0]) + 2.0 == STaylor1([3.0, 2.0, 3.0]) + @test STaylor1([1.0, 2.0, 3.0]) - 2.0 == STaylor1([-1.0, 2.0, 3.0]) + @test 2.0 + STaylor1([1.0, 2.0, 3.0]) == STaylor1([3.0, 2.0, 3.0]) + @test 2.0 - STaylor1([1.0, 2.0, 3.0]) == STaylor1([1.0, -2.0, -3.0]) + + @test zero(STaylor1([1.0, 2.0, 3.0])) == STaylor1([0.0, 0.0, 0.0]) + @test one(STaylor1([1.0, 2.0, 3.0])) == STaylor1([1.0, 0.0, 0.0]) + + @test isinf(STaylor1([Inf, 2.0, 3.0])) && ~isinf(STaylor1([0.0, 0.0, 0.0])) + @test isnan(STaylor1([NaN, 2.0, 3.0])) && ~isnan(STaylor1([1.0, 0.0, 0.0])) + @test iszero(STaylor1([0.0, 0.0, 0.0])) && ~iszero(STaylor1([0.0, 1.0, 0.0])) + + @test length(STaylor1([0.0, 0.0, 0.0])) == 3 + @test size(STaylor1([0.0, 0.0, 0.0])) == 3 + @test firstindex(STaylor1([0.0, 0.0, 0.0])) == 0 + @test lastindex(STaylor1([0.0, 0.0, 0.0])) == 2 + + st1 = STaylor1([1.0, 2.0, 3.0]) + @test st1(2.0) == 41.0 + @test st1() == 1.00 + st2 = typeof(st1)[st1; st1] + @test st2(2.0)[1] == st2(2.0)[2] == 41.0 + @test st2()[1] == st2()[2] == 1.0 + @test STaylor.evaluate(st1,2.0) == 41.0 + @test STaylor.evaluate(st1) == 1.00 + @test STaylor.evaluate(st2,2.0)[1] == STaylor.evaluate(st2,2.0)[2] == 41.0 + @test STaylor.evaluate(st2)[1] == STaylor.evaluate(st2)[2] == 1.0 + + # check that STaylor1 and Taylor yeild same result + t1 = STaylor1([1.1, 2.1, 3.1]) + t2 = Taylor1([1.1, 2.1, 3.1]) + for f in (exp, abs, log) + @test test_vs_Taylor1(f(t1), f(t2)) + end + t1a = STaylor1([2.1, 2.1, 3.1]) + t2a = Taylor1([2.1, 2.1, 3.1]) + @test isapprox((t1/t1a)[0], (t2/t2a)[0], atol=1E-10) + @test isapprox((t1/t1a)[1], (t2/t2a)[1], atol=1E-10) + @test isapprox((t1/t1a)[2], (t2/t2a)[2], atol=1E-10) + + @test isapprox((t1*t1a)[0], (t2*t2a)[0], atol=1E-10) + @test isapprox((t1*t1a)[1], (t2*t2a)[1], atol=1E-10) + @test isapprox((t1*t1a)[2], (t2*t2a)[2], atol=1E-10) + + @test isapprox(STaylor.square(t1)[0], (t2^2)[0], atol=1E-10) + @test isapprox(STaylor.square(t1)[1], (t2^2)[1], atol=1E-10) + @test isapprox(STaylor.square(t1)[2], (t2^2)[2], atol=1E-10) + + a = STaylor1([0.0, 1.2, 2.3, 4.5, 0.0]) + @test findfirst(a) == 1 + @test findlast(a) == 3 + + a = STaylor1([5.0, 1.2, 2.3, 4.5, 0.0]) + @test isapprox(deg2rad(a)[0], 0.087266, atol=1E-5) + @test isapprox(deg2rad(a)[2], 0.040142, atol=1E-5) + @test isapprox(rad2deg(a)[0], 286.4788975, atol=1E-5) + @test isapprox(rad2deg(a)[2], 131.7802928, atol=1E-5) + @test real(a) == STaylor1([5.0, 1.2, 2.3, 4.5, 0.0]) + @test imag(a) == STaylor1([0.0, 0.0, 0.0, 0.0, 0.0]) + @test adjoint(a) == STaylor1([5.0, 1.2, 2.3, 4.5, 0.0]) + @test conj(a) == STaylor1([5.0, 1.2, 2.3, 4.5, 0.0]) + @test a == abs(a) + @test a == abs(-a) + end end From 2963d68d29847d9e17605f9ed4d149fba50fc604 Mon Sep 17 00:00:00 2001 From: Matthew Wilhelm Date: Wed, 16 Sep 2020 18:12:52 -0400 Subject: [PATCH 02/13] Add static mod and rem --- src/functions.jl | 15 +++++++-------- src/other_functions.jl | 20 ++++++++++++++++++++ test/runtests.jl | 33 +++++++++++++++++++++++---------- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/functions.jl b/src/functions.jl index 67729c2..efe1c1c 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -138,26 +138,25 @@ end # Functions for STaylor1 @generated function tan(a::STaylor1{N,T}) where {N, T <: Number} ex_calc = quote end - append!(ex_calc.args, Any[nothing for i in 1:N]) + append!(ex_calc.args, Any[nothing for i in 1:(N+1)]) syms = Symbol[Symbol("c$i") for i in 1:N] + syms2 = Symbol[Symbol("c2$i") for i in 1:N] - sym = syms[1] - ex_line = :($(sym) = tan(a[0])) - ex_calc.args[1] = ex_line + ex_calc.args[1] = :($(syms[1]) = tan(a[0])) + ex_calc.args[2] = :($(syms2[1]) = ($(syms[1]))^2) - #= for k in 1:(N-1) kT = convert(T,k) sym = syms[k+1] ex_line = :($kT * a[$k] * $(syms[1])) @inbounds for i = 1:k-1 - ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms2[i+1])) end ex_line = :(($ex_line)/$kT) ex_line = :($sym = $ex_line) - ex_calc.args[k+1] = ex_line + ex_calc.args[k+2] = ex_line + #c2 = sqr(c)... end - =# exout = :(($(syms[1]),)) for i = 2:N diff --git a/src/other_functions.jl b/src/other_functions.jl index f1385ba..a5f14ca 100644 --- a/src/other_functions.jl +++ b/src/other_functions.jl @@ -25,3 +25,23 @@ deg2rad(z::STaylor1{N, T}) where {N, T<:Real} = z * (convert(float(T), pi) / 180 rad2deg(z::STaylor1{N, T}) where {N, T<:AbstractFloat} = z * (180 / convert(T, pi)) rad2deg(z::STaylor1{N, T}) where {N, T<:Real} = z * (180 / convert(float(T), pi)) + +function mod(a::STaylor1{N,T}, x::T) where {N, T<:Real} + return STaylor1{N,T}(ntuple(i -> i == 1 ? mod(constant_term(a), x) : a.coeffs[i], Val(N))) +end + +function mod(a::STaylor1{N,T}, x::S) where {N, T<:Real, S<:Real} + R = promote_type(T, S) + a = convert(STaylor1{N,R}, a) + return mod(a, convert(R, x)) +end + +function rem(a::STaylor1{N,T}, x::T) where {N, T<:Real} + return STaylor1{N,T}(ntuple(i -> i == 1 ? rem(constant_term(a), x) : a.coeffs[i], Val(N))) +end + +function rem(a::STaylor1{N,T}, x::S) where {N, T<:Real, S<:Real} + R = promote_type(T, S) + a = convert(STaylor1{N,R}, a) + return rem(a, convert(R, x)) +end diff --git a/test/runtests.jl b/test/runtests.jl index 455653b..2718225 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,7 +1,8 @@ +using Test + # Skipping tests with intervals, since IntervalArithmetic.jl requires Julia v1.1+ if !(VERSION < v"1.1" && testfile == "intervals.jl") - using TaylorSeries, STaylor - using Test + using TaylorSeries, StaticTaylorSeries function test_vs_Taylor1(x, y) flag = true @@ -22,7 +23,6 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") @test STaylor1(STaylor1((1.0, 2.0))) == STaylor1((1.0, 2.0)) @test STaylor1(1.0, Val(2)) == STaylor1((1.0, 0.0, 0.0)) - @test +STaylor1([1.0, 2.0, 3.0]) == STaylor1([1.0, 2.0, 3.0]) @test -STaylor1([1.0, 2.0, 3.0]) == -STaylor1([1.0, 2.0, 3.0]) @test STaylor1([1.0, 2.0, 3.0]) + STaylor1([3.0, 2.0, 3.0]) == STaylor1([4.0, 4.0, 6.0]) @@ -50,10 +50,10 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") st2 = typeof(st1)[st1; st1] @test st2(2.0)[1] == st2(2.0)[2] == 41.0 @test st2()[1] == st2()[2] == 1.0 - @test STaylor.evaluate(st1,2.0) == 41.0 - @test STaylor.evaluate(st1) == 1.00 - @test STaylor.evaluate(st2,2.0)[1] == STaylor.evaluate(st2,2.0)[2] == 41.0 - @test STaylor.evaluate(st2)[1] == STaylor.evaluate(st2)[2] == 1.0 + @test StaticTaylorSeries.evaluate(st1,2.0) == 41.0 + @test StaticTaylorSeries.evaluate(st1) == 1.00 + @test StaticTaylorSeries.evaluate(st2,2.0)[1] == StaticTaylorSeries.evaluate(st2,2.0)[2] == 41.0 + @test StaticTaylorSeries.evaluate(st2)[1] == StaticTaylorSeries.evaluate(st2)[2] == 1.0 # check that STaylor1 and Taylor yeild same result t1 = STaylor1([1.1, 2.1, 3.1]) @@ -61,6 +61,19 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") for f in (exp, abs, log) @test test_vs_Taylor1(f(t1), f(t2)) end + + t1_mod = mod(t1, 2.0) + t2_mod = mod(t2, 2.0) + @test isapprox(t1_mod[0], t2_mod[0], atol=1E-10) + @test isapprox(t1_mod[1], t2_mod[1], atol=1E-10) + @test isapprox(t1_mod[2], t2_mod[2], atol=1E-10) + + t1_rem = rem(t1, 2.0) + t2_rem = rem(t2, 2.0) + @test isapprox(t1_rem[0], t2_rem[0], atol=1E-10) + @test isapprox(t1_rem[1], t2_rem[1], atol=1E-10) + @test isapprox(t1_rem[2], t2_rem[2], atol=1E-10) + t1a = STaylor1([2.1, 2.1, 3.1]) t2a = Taylor1([2.1, 2.1, 3.1]) @test isapprox((t1/t1a)[0], (t2/t2a)[0], atol=1E-10) @@ -71,9 +84,9 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") @test isapprox((t1*t1a)[1], (t2*t2a)[1], atol=1E-10) @test isapprox((t1*t1a)[2], (t2*t2a)[2], atol=1E-10) - @test isapprox(STaylor.square(t1)[0], (t2^2)[0], atol=1E-10) - @test isapprox(STaylor.square(t1)[1], (t2^2)[1], atol=1E-10) - @test isapprox(STaylor.square(t1)[2], (t2^2)[2], atol=1E-10) + @test isapprox(StaticTaylorSeries.square(t1)[0], (t2^2)[0], atol=1E-10) + @test isapprox(StaticTaylorSeries.square(t1)[1], (t2^2)[1], atol=1E-10) + @test isapprox(StaticTaylorSeries.square(t1)[2], (t2^2)[2], atol=1E-10) a = STaylor1([0.0, 1.2, 2.3, 4.5, 0.0]) @test findfirst(a) == 1 From 899394783ce1afff270983a53090173f48077d28 Mon Sep 17 00:00:00 2001 From: Matthew Wilhelm Date: Wed, 16 Sep 2020 18:21:25 -0400 Subject: [PATCH 03/13] Add mod2pi --- src/other_functions.jl | 4 ++++ test/runtests.jl | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/src/other_functions.jl b/src/other_functions.jl index a5f14ca..5fa1faf 100644 --- a/src/other_functions.jl +++ b/src/other_functions.jl @@ -45,3 +45,7 @@ function rem(a::STaylor1{N,T}, x::S) where {N, T<:Real, S<:Real} a = convert(STaylor1{N,R}, a) return rem(a, convert(R, x)) end + +function mod2pi(a::STaylor1{N,T}) where {N, T<:Real} + return STaylor1{N,T}(ntuple(i -> i == 1 ? mod2pi(constant_term(a)) : a.coeffs[i], Val(N))) +end diff --git a/test/runtests.jl b/test/runtests.jl index 2718225..c018c9d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -68,6 +68,12 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") @test isapprox(t1_mod[1], t2_mod[1], atol=1E-10) @test isapprox(t1_mod[2], t2_mod[2], atol=1E-10) + t1_mod2pi = mod2pi(t1) + t2_mod2pi = mod2pi(t2) + @test isapprox(t1_mod2pi[0], t2_mod2pi[0], atol=1E-10) + @test isapprox(t1_mod2pi[1], t2_mod2pi[1], atol=1E-10) + @test isapprox(t1_mod2pi[2], t2_mod2pi[2], atol=1E-10) + t1_rem = rem(t1, 2.0) t2_rem = rem(t2, 2.0) @test isapprox(t1_rem[0], t2_rem[0], atol=1E-10) From 9dae74733434b1e058d695ec877c3f0dc0e555dc Mon Sep 17 00:00:00 2001 From: Matthew Wilhelm Date: Thu, 17 Sep 2020 00:48:36 -0400 Subject: [PATCH 04/13] Start on sqrt function --- src/power.jl | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/power.jl b/src/power.jl index 865c6a5..cfd0674 100644 --- a/src/power.jl +++ b/src/power.jl @@ -67,3 +67,41 @@ end return STaylor1{N,T}($exout) end end + +@generated function sqrt(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + + for k = (lnull + 1):(N-1) + kT = convert(T,k) + sym = syms[k+1] + ex_line = :($kT * a[$k] * $(syms[1])) + @inbounds for i = 1:k-1 + ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + end + ex_line = :(($ex_line)/$kT) + ex_line = :($sym = $ex_line) + ex_calc.args[k+1] = ex_line + end + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + return quote + Base.@_inline_meta + l0nz = findfirst(a) + aux = zero(sqrt(constant_term(a))) + if l0nz < 0 + return + elseif l0nz%2 == 1 + throw(ArgumentError( + """First non-vanishing Taylor1 coefficient must correspond + to an **even power** in order to expand `sqrt` around 0.""")) + end + lnull = div(l0nz, 2) + $ex_calc + return STaylor1{N,T}($exout) + end +end From 0641b429f26de9d162f070e455de914007289928 Mon Sep 17 00:00:00 2001 From: Matthew Wilhelm Date: Thu, 17 Sep 2020 11:41:19 -0400 Subject: [PATCH 05/13] Add inverse --- src/functions.jl | 15 ++++---- src/power.jl | 90 ++++++++++++++++++++++++++++++++++++++++-------- test/runtests.jl | 8 +---- 3 files changed, 83 insertions(+), 30 deletions(-) diff --git a/src/functions.jl b/src/functions.jl index efe1c1c..4ace9ea 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -145,16 +145,15 @@ end ex_calc.args[1] = :($(syms[1]) = tan(a[0])) ex_calc.args[2] = :($(syms2[1]) = ($(syms[1]))^2) - for k in 1:(N-1) - kT = convert(T,k) - sym = syms[k+1] - ex_line = :($kT * a[$k] * $(syms[1])) - @inbounds for i = 1:k-1 - ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms2[i+1])) + for k = 1:(N - 1) + kT = convert(T, k) + ex_line = :($(kT - 1)*a[$(k - 1)]*$(syms2[1])) + @inbounds for i = 1:(k - 1) + ex_line = :($ex_line + $(kT - i) * a[$(k - i)] * $(syms2[i + 1])) end ex_line = :(($ex_line)/$kT) - ex_line = :($sym = $ex_line) - ex_calc.args[k+2] = ex_line + ex_line = :($(syms[k + 1]) = $ex_line) + ex_calc.args[k + 2] = ex_line #c2 = sqr(c)... end diff --git a/src/power.jl b/src/power.jl index cfd0674..a588594 100644 --- a/src/power.jl +++ b/src/power.jl @@ -32,7 +32,6 @@ function power_by_squaring(x::STaylor1{N,T}, p::Integer) where {N,T<:Number} return y end - @generated function square(a::STaylor1{N,T}) where {N, T<:Number} ex_calc = quote end append!(ex_calc.args, Any[nothing for i in 1:N]) @@ -68,12 +67,82 @@ end end end +@generated function inverse(a::STaylor1{N,T}) where {N,T<:Real} + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:(2*N)]) + syms = Symbol[Symbol("c$i") for i in 1:N] + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + + count = 1 + for n = 1:(N - 1) + ex_calc.args[2*count - 1] = :(syms[n + 1] = zdivfpown[n - 1]/n) + ex_calc.args[2*count] = :(zdivfpown *= zdivf) + count += 1 + end + + return quote + Base.@_inline_meta + if a[0] != zero(T) + throw(ArgumentError( + """ + Evaluation of Taylor1 series at 0 is non-zero. For high accuracy, revert + a Taylor1 series with first coefficient 0 and re-expand about f(0). + """)) + end + z = copy(a) + zdivf = z/a + zdivfpown = zdivf + S = eltype(zdivf) + $ex_calc + return STaylor1{N,T}($exout) + end +end + + @generated function sqrt(a::STaylor1{N,T}) where {N, T <: Number} ex_calc = quote end append!(ex_calc.args, Any[nothing for i in 1:N]) syms = Symbol[Symbol("c$i") for i in 1:N] + start_expr = quote + Base.@_inline_meta + l0nz = findfirst(a) + aux = zero(T) + if l0nz < 0 + return + elseif l0nz%2 == 1 + throw(ArgumentError( + """First non-vanishing Taylor1 coefficient must correspond + to an **even power** in order to expand `sqrt` around 0.""")) + end + lnull = div(l0nz, 2) + end + for k = (lnull + 1):(N-1) + + pre_loop = quote + if k == lnull + @inbounds $(syms[k]) = sqrt(aa[2*lnull]) + return continue + end + kodd = (k - lnull)%2 + kend = div(k - lnull - 2 + kodd, 2) + imax = min(lnull + kend, N - 1) + imin = max(lnull + 1, k + lnull - N + 1) + imin ≤ imax && (@inbounds $(syms[k]) = $(syms[imin])*$(syms[k + lnull - imin])) + end + + loop = quote + end + + post_loop = quote + end + + kT = convert(T,k) sym = syms[k+1] ex_line = :($kT * a[$k] * $(syms[1])) @@ -89,19 +158,10 @@ end for i = 2:N push!(exout.args, syms[i]) end + return quote - Base.@_inline_meta - l0nz = findfirst(a) - aux = zero(sqrt(constant_term(a))) - if l0nz < 0 - return - elseif l0nz%2 == 1 - throw(ArgumentError( - """First non-vanishing Taylor1 coefficient must correspond - to an **even power** in order to expand `sqrt` around 0.""")) - end - lnull = div(l0nz, 2) - $ex_calc - return STaylor1{N,T}($exout) - end + $start_expr + $compute_expr + return STaylor1{N,T}($exout) + end end diff --git a/test/runtests.jl b/test/runtests.jl index c018c9d..87476df 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -58,7 +58,7 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") # check that STaylor1 and Taylor yeild same result t1 = STaylor1([1.1, 2.1, 3.1]) t2 = Taylor1([1.1, 2.1, 3.1]) - for f in (exp, abs, log) + for f in (exp, abs, log, mod2pi) @test test_vs_Taylor1(f(t1), f(t2)) end @@ -68,12 +68,6 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") @test isapprox(t1_mod[1], t2_mod[1], atol=1E-10) @test isapprox(t1_mod[2], t2_mod[2], atol=1E-10) - t1_mod2pi = mod2pi(t1) - t2_mod2pi = mod2pi(t2) - @test isapprox(t1_mod2pi[0], t2_mod2pi[0], atol=1E-10) - @test isapprox(t1_mod2pi[1], t2_mod2pi[1], atol=1E-10) - @test isapprox(t1_mod2pi[2], t2_mod2pi[2], atol=1E-10) - t1_rem = rem(t1, 2.0) t2_rem = rem(t2, 2.0) @test isapprox(t1_rem[0], t2_rem[0], atol=1E-10) From e4895fdd73332149281c2eee8f3b245770fd98e3 Mon Sep 17 00:00:00 2001 From: Matthew Wilhelm Date: Thu, 17 Sep 2020 15:23:25 -0400 Subject: [PATCH 06/13] Add sin, cos, sinh, cosh --- src/functions.jl | 162 ++++++++++++++++------------------------------- test/runtests.jl | 13 +++- 2 files changed, 68 insertions(+), 107 deletions(-) diff --git a/src/functions.jl b/src/functions.jl index 4ace9ea..2be6647 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -65,73 +65,48 @@ end end end -# Functions for STaylor1 -@generated function sin(a::STaylor1{N,T}) where {N, T <: Number} +sin(a::STaylor1{N,T}) where {N, T <: Number} = sincos(a)[1] +cos(a::STaylor1{N,T}) where {N, T <: Number} = sincos(a)[2] +@generated function sincos(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end - append!(ex_calc.args, Any[nothing for i in 1:N]) - syms = Symbol[Symbol("c$i") for i in 1:N] + append!(ex_calc.args, Any[nothing for i in 1:(2*N)]) - sym = syms[1] - ex_line = :($(sym) = sin(a[0])) - ex_calc.args[1] = ex_line + syms_s = Symbol[Symbol("c$i") for i in 1:N] + syms_c = Symbol[Symbol("c2$i") for i in 1:N] - #= - for k in 1:(N-1) - kT = convert(T,k) - sym = syms[k+1] - ex_line = :($kT * a[$k] * $(syms[1])) - @inbounds for i = 1:k-1 - ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + ex_line_s = :($(syms_s[1]) = sin(a[0])) + ex_line_c = :($(syms_c[1]) = cos(a[0])) + ex_calc.args[1] = ex_line_s + ex_calc.args[2] = ex_line_c + + for k = 1:(N - 1) + ex_line_s = :(a[1]*$(syms_c[k])) + ex_line_c = :(-a[1]*$(syms_s[k])) + for i = 2:k + ex_line_s = :($ex_line_s + $i*a[$i]*$(syms_c[(k - i + 1)])) + ex_line_c = :($ex_line_c - $i*a[$i]*$(syms_s[(k - i + 1)])) end - ex_line = :(($ex_line)/$kT) - ex_line = :($sym = $ex_line) - ex_calc.args[k+1] = ex_line + ex_line_s = :($(syms_s[k + 1]) = ($ex_line_s)/$k) + ex_line_c = :($(syms_c[k + 1]) = ($ex_line_c)/$k) + ex_calc.args[2*k + 1] = ex_line_s + ex_calc.args[2*k + 2] = ex_line_c end - =# - exout = :(($(syms[1]),)) + exout_s = :(($(syms_s[1]),)) for i = 2:N - push!(exout.args, syms[i]) + push!(exout_s.args, syms_s[i]) end - return quote - Base.@_inline_meta - $ex_calc - return STaylor1{N,T}($exout) - end -end -# Functions for STaylor1 -@generated function cos(a::STaylor1{N,T}) where {N, T <: Number} - ex_calc = quote end - append!(ex_calc.args, Any[nothing for i in 1:N]) - syms = Symbol[Symbol("c$i") for i in 1:N] - - sym = syms[1] - ex_line = :($(sym) = cos(a[0])) - ex_calc.args[1] = ex_line - - #= - for k in 1:(N-1) - kT = convert(T,k) - sym = syms[k+1] - ex_line = :($kT * a[$k] * $(syms[1])) - @inbounds for i = 1:k-1 - ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) - end - ex_line = :(($ex_line)/$kT) - ex_line = :($sym = $ex_line) - ex_calc.args[k+1] = ex_line - end - =# - - exout = :(($(syms[1]),)) + exout_c = :(($(syms_c[1]),)) for i = 2:N - push!(exout.args, syms[i]) + push!(exout_c.args, syms_c[i]) end + return quote Base.@_inline_meta $ex_calc - return STaylor1{N,T}($exout) + return STaylor1{N,T}($exout_s), STaylor1{N,T}($exout_c) end end @@ -273,73 +248,48 @@ end end end -# Functions for STaylor1 -@generated function sinh(a::STaylor1{N,T}) where {N, T <: Number} +sinh(a::STaylor1{N,T}) where {N, T <: Number} = sinhcosh(a)[1] +cosh(a::STaylor1{N,T}) where {N, T <: Number} = sinhcosh(a)[2] +@generated function sinhcosh(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end - append!(ex_calc.args, Any[nothing for i in 1:N]) - syms = Symbol[Symbol("c$i") for i in 1:N] + append!(ex_calc.args, Any[nothing for i in 1:(2*N)]) - sym = syms[1] - ex_line = :($(sym) = sinh(a[0])) - ex_calc.args[1] = ex_line + syms_s = Symbol[Symbol("c$i") for i in 1:N] + syms_c = Symbol[Symbol("c2$i") for i in 1:N] - #= - for k in 1:(N-1) - kT = convert(T,k) - sym = syms[k+1] - ex_line = :($kT * a[$k] * $(syms[1])) - @inbounds for i = 1:k-1 - ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + ex_line_s = :($(syms_s[1]) = sinh(a[0])) + ex_line_c = :($(syms_c[1]) = cosh(a[0])) + ex_calc.args[1] = ex_line_s + ex_calc.args[2] = ex_line_c + + for k = 1:(N - 1) + ex_line_s = :(a[1]*$(syms_c[k])) + ex_line_c = :(a[1]*$(syms_s[k])) + for i = 2:k + ex_line_s = :($ex_line_s + $i*a[$i]*$(syms_c[(k - i + 1)])) + ex_line_c = :($ex_line_c + $i*a[$i]*$(syms_s[(k - i + 1)])) end - ex_line = :(($ex_line)/$kT) - ex_line = :($sym = $ex_line) - ex_calc.args[k+1] = ex_line + ex_line_s = :($(syms_s[k + 1]) = ($ex_line_s)/$k) + ex_line_c = :($(syms_c[k + 1]) = ($ex_line_c)/$k) + ex_calc.args[2*k + 1] = ex_line_s + ex_calc.args[2*k + 2] = ex_line_c end - =# - exout = :(($(syms[1]),)) + exout_s = :(($(syms_s[1]),)) for i = 2:N - push!(exout.args, syms[i]) + push!(exout_s.args, syms_s[i]) end - return quote - Base.@_inline_meta - $ex_calc - return STaylor1{N,T}($exout) - end -end -# Functions for STaylor1 -@generated function cosh(a::STaylor1{N,T}) where {N, T <: Number} - ex_calc = quote end - append!(ex_calc.args, Any[nothing for i in 1:N]) - syms = Symbol[Symbol("c$i") for i in 1:N] - - sym = syms[1] - ex_line = :($(sym) = cosh(a[0])) - ex_calc.args[1] = ex_line - - #= - for k in 1:(N-1) - kT = convert(T,k) - sym = syms[k+1] - ex_line = :($kT * a[$k] * $(syms[1])) - @inbounds for i = 1:k-1 - ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) - end - ex_line = :(($ex_line)/$kT) - ex_line = :($sym = $ex_line) - ex_calc.args[k+1] = ex_line - end - =# - - exout = :(($(syms[1]),)) + exout_c = :(($(syms_c[1]),)) for i = 2:N - push!(exout.args, syms[i]) + push!(exout_c.args, syms_c[i]) end + return quote Base.@_inline_meta $ex_calc - return STaylor1{N,T}($exout) + return STaylor1{N,T}($exout_s), STaylor1{N,T}($exout_c) end end diff --git a/test/runtests.jl b/test/runtests.jl index 87476df..aa81ecb 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -58,7 +58,7 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") # check that STaylor1 and Taylor yeild same result t1 = STaylor1([1.1, 2.1, 3.1]) t2 = Taylor1([1.1, 2.1, 3.1]) - for f in (exp, abs, log, mod2pi) + for f in (exp, abs, log, sin, cos, sinh, cosh, mod2pi) @test test_vs_Taylor1(f(t1), f(t2)) end @@ -103,5 +103,16 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") @test conj(a) == STaylor1([5.0, 1.2, 2.3, 4.5, 0.0]) @test a == abs(a) @test a == abs(-a) + + @test convert(STaylor1{3,Float64}, STaylor1{3,Float64}((1.1, 2.2, 3.3))) == STaylor1{3,Float64}((1.1, 2.2, 3.3)) + @test convert(STaylor1{3,Float64}, 1) == STaylor1(1.0, Val(3)) + @test convert(STaylor1{3,Float64}, 1.2) == STaylor1(1.2, Val(3)) + + #ta(a) = STaylor1(1.0, Val(15)) + @test promote(1.0, STaylor1(1.0, Val(15)))[1] == STaylor1(1.0, Val(16)) + @test promote(0, STaylor1(1.0, Val(15)))[1] == STaylor1(0.0, Val(16)) + @test eltype(promote(STaylor1(1, Val(15)),2)[2]) == Int + @test eltype(promote(STaylor1(1.0, Val(15)), 1.1)[2]) == Float64 + @test eltype(promote(0, STaylor1(1.0, Val(15)))[1]) == Float64 end end From d249f01fa19bf3f2c7f7e13a0770da21c0cd7249 Mon Sep 17 00:00:00 2001 From: Matthew Wilhelm Date: Fri, 18 Sep 2020 12:51:09 -0400 Subject: [PATCH 07/13] Preliminary Work on Tan/Tanh --- src/functions.jl | 96 +++++++++++++++++++++++++++++++++++------------- test/runtests.jl | 2 +- 2 files changed, 71 insertions(+), 27 deletions(-) diff --git a/src/functions.jl b/src/functions.jl index 2be6647..12658f1 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -112,30 +112,55 @@ end # Functions for STaylor1 @generated function tan(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end - append!(ex_calc.args, Any[nothing for i in 1:(N+1)]) + append!(ex_calc.args, Any[nothing for i in 1:(3*N)]) syms = Symbol[Symbol("c$i") for i in 1:N] syms2 = Symbol[Symbol("c2$i") for i in 1:N] - ex_calc.args[1] = :($(syms[1]) = tan(a[0])) - ex_calc.args[2] = :($(syms2[1]) = ($(syms[1]))^2) + for i = 1:N + ex_calc.args[i] = :($(syms2[i]) = 0.0) + end + + ex_line_c = :($(syms[1]) = tan(a[0])) + ex_line_c2 = :($(syms2[1]) = ($(syms[1]))^2) + ex_calc.args[1 + N] = ex_line_c + ex_calc.args[2 + N] = ex_line_c2 for k = 1:(N - 1) - kT = convert(T, k) - ex_line = :($(kT - 1)*a[$(k - 1)]*$(syms2[1])) - @inbounds for i = 1:(k - 1) - ex_line = :($ex_line + $(kT - i) * a[$(k - i)] * $(syms2[i + 1])) + + kodd = k%2 + kend = div(k - 2 + kodd, 2) + kdiv2 = div(k, 2) + + ex_line_c = :($(k-1)*a[$(k-1)]*$(syms2[2])) + for i = 1:(k - 1) + q = k - i + ex_line_c = :($ex_line_c + ($q)*a[$q]*$(syms2[i + 1])) end - ex_line = :(($ex_line)/$kT) - ex_line = :($(syms[k + 1]) = $ex_line) - ex_calc.args[k + 2] = ex_line - #c2 = sqr(c)... + ex_line_c = :(a[$k] - ($ex_line_c)/$k) + ex_line_c = :($(syms[k + 1]) = $ex_line_c) + ex_calc.args[2*k + 1 + N] = ex_line_c + + ex_line_c2 = :(a[0]*a[$k]) + for i = 1:kend + ex_line_c2 = :($ex_line_c2 + a[$i]*a[$k - $i]) + end + ex_line_c2 = :(2*$ex_line_c2) + + if kodd != 1 + ex_line_c2 = :($ex_line_c2 + a[$kdiv2]^2) + end + + ex_line_c2 = :($(syms2[k + 1]) = $ex_line_c2) + ex_calc.args[2*k + 2 + N] = ex_line_c2 end exout = :(($(syms[1]),)) for i = 2:N push!(exout.args, syms[i]) end + return quote Base.@_inline_meta $ex_calc @@ -295,32 +320,51 @@ end # Functions for STaylor1 @generated function tanh(a::STaylor1{N,T}) where {N, T <: Number} + ex_calc = quote end - append!(ex_calc.args, Any[nothing for i in 1:N]) + append!(ex_calc.args, Any[nothing for i in 1:(2*N)]) syms = Symbol[Symbol("c$i") for i in 1:N] + syms2 = Symbol[Symbol("c2$i") for i in 1:N] - sym = syms[1] - ex_line = :($(sym) = tanh(a[0])) - ex_calc.args[1] = ex_line + ex_line_c = :($(syms[1]) = tanh(a[0])) + ex_line_c2 = :($(syms2[1]) = ($(syms[1]))^2) + ex_calc.args[1] = ex_line_c + ex_calc.args[2] = ex_line_c2 - #= - for k in 1:(N-1) - kT = convert(T,k) - sym = syms[k+1] - ex_line = :($kT * a[$k] * $(syms[1])) - @inbounds for i = 1:k-1 - ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + for k = 1:(N - 1) + + kodd = k%2 + kend = div(k - 2 + kodd, 2) + kdiv2 = div(k, 2) + + ex_line_c = :($k*a[$k]*$(syms2[1])) + for i = 1:(k - 1) + q = k - i + ex_line_c = :($ex_line_c + ($q)*a[$q]*$(syms2[i+1])) end - ex_line = :(($ex_line)/$kT) - ex_line = :($sym = $ex_line) - ex_calc.args[k+1] = ex_line + ex_line_c = :(a[$k] - $ex_line_c/$k) + ex_line_c = :($(syms[k + 1]) = $ex_line_c) + ex_calc.args[2*k + 1] = ex_line_c + + ex_line_c2 = :(a[1]*a[$k]) + @inbounds for i = 1:kend + ex_line_c2 = :($ex_line_c2 + a[$i + 1]*a[$k - $i]) + end + ex_line_c2 = :(2*$ex_line_c2) + + if kodd != 1 + ex_line_c2 = :($ex_line_c2 + a[$kdiv2 + 1]^2) + end + + ex_line_c2 = :($(syms2[k + 1]) = $ex_line_c2) + ex_calc.args[2*k + 2] = ex_line_c2 end - =# exout = :(($(syms[1]),)) for i = 2:N push!(exout.args, syms[i]) end + return quote Base.@_inline_meta $ex_calc diff --git a/test/runtests.jl b/test/runtests.jl index aa81ecb..6f895ea 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -58,7 +58,7 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") # check that STaylor1 and Taylor yeild same result t1 = STaylor1([1.1, 2.1, 3.1]) t2 = Taylor1([1.1, 2.1, 3.1]) - for f in (exp, abs, log, sin, cos, sinh, cosh, mod2pi) + for f in (exp, abs, log, sin, cos, tan, sinh, cosh, tanh, mod2pi) @test test_vs_Taylor1(f(t1), f(t2)) end From b999bf488226b480f1d638d9d991885fbd5bc503 Mon Sep 17 00:00:00 2001 From: Matt W Date: Mon, 21 Sep 2020 09:34:32 -0400 Subject: [PATCH 08/13] Add to readme, update tests --- README.md | 34 +++++++++++++++++++++++++++++++--- src/functions.jl | 23 +++++++++++++++++------ test/runtests.jl | 27 ++++++++++++++++++++------- 3 files changed, 68 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1faa401..2fca59f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,33 @@ # StaticTaylorSeries.jl -A Static 1-D Taylor Series Library +A [Julia](http://julialang.org) package used to compute static 1D Taylor +polynomial expansions -This package contains a static implementation of the Taylor1 structure in TaylorSeries.jl. We intend to -eventually merge this functionality into the TaylorSeries.jl package. +[![Build Status](https://api.travis-ci.org/dpsanders/TaylorSeries.jl.svg?branch=master)](https://travis-ci.org/JuliaDiff/TaylorSeries.jl) +[![Coverage Status](https://coveralls.io/repos/dpsanders/TaylorSeries.jl/badge.svg?branch=master)](https://coveralls.io/github/JuliaDiff/TaylorSeries.jl?branch=master) + +[![](https://img.shields.io/badge/docs-stable-blue.svg)](https://github.com/dpsanders/TaylorSeries.jl/stable) +[![](https://img.shields.io/badge/docs-latest-blue.svg)](https://github.com/dpsanders/TaylorSeries.jl/latest) + +This package contains a static implementation of the Taylor1 structure in TaylorSeries.jl. + +## Key Differences + +This package introduces the `STaylor1{N,T}` structure which can be used as a +replacement for the `Taylor1{T}` structure of TaylorSeries.jl. Arithmetic operators are defined via generated functions and do not allocate. + +Key differences are: +- Arithmetic with `STaylor1{N,T}` structures use entirely immutable storage types +for calculations whereas `Taylor1{T}` calculations make use of a number of +arrays for intermediate storage. As a consequence, the `STaylor1{N,T}` implementation +will be significant faster for calculations involving low-order Taylor series. +- The `STaylor1{N,T}` structure stores coefficients as an `NTuple` rather than +an array. +- Constructors: Most constructors are similar to those used for Taylor1. The +constructor `STaylor1(x::T, v::Val{N})` is used in place of `Taylor1(x::T, order::Int)`. +- In place functions (e.g. `tan!(a,c,k)`) are not supported. +- Currently, **tan**, **tanh**, **sqrt**, **asin**, **acos**, **atan** are +unsupported. + +#### License + +`StaticTaylorSeries` is licensed under the [MIT "Expat" license](./LICENSE.md). diff --git a/src/functions.jl b/src/functions.jl index 12658f1..9a2d385 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -111,10 +111,11 @@ cos(a::STaylor1{N,T}) where {N, T <: Number} = sincos(a)[2] end # Functions for STaylor1 +#= @generated function tan(a::STaylor1{N,T}) where {N, T <: Number} ex_calc = quote end - append!(ex_calc.args, Any[nothing for i in 1:(3*N)]) + append!(ex_calc.args, Any[nothing for i in 1:(4*N)]) syms = Symbol[Symbol("c$i") for i in 1:N] syms2 = Symbol[Symbol("c2$i") for i in 1:N] @@ -124,8 +125,8 @@ end ex_line_c = :($(syms[1]) = tan(a[0])) ex_line_c2 = :($(syms2[1]) = ($(syms[1]))^2) - ex_calc.args[1 + N] = ex_line_c - ex_calc.args[2 + N] = ex_line_c2 + ex_calc.args[N + 1] = ex_line_c + ex_calc.args[N + 2] = ex_line_c2 for k = 1:(N - 1) @@ -138,9 +139,9 @@ end q = k - i ex_line_c = :($ex_line_c + ($q)*a[$q]*$(syms2[i + 1])) end - ex_line_c = :(a[$k] - ($ex_line_c)/$k) + ex_line_c = :(a[$k] + ($ex_line_c)/$k) ex_line_c = :($(syms[k + 1]) = $ex_line_c) - ex_calc.args[2*k + 1 + N] = ex_line_c + ex_calc.args[(3*k-2) + N + 2] = ex_line_c ex_line_c2 = :(a[0]*a[$k]) for i = 1:kend @@ -153,7 +154,10 @@ end end ex_line_c2 = :($(syms2[k + 1]) = $ex_line_c2) - ex_calc.args[2*k + 2 + N] = ex_line_c2 + ex_calc.args[(3*k-1) + 2 + N] = ex_line_c2 + blar = k + 1 + blar_str = "c$(blar) " + ex_calc.args[3*k + N + 2] = :(println($blar_str*string($(syms[k + 1])))) end exout = :(($(syms[1]),)) @@ -167,8 +171,10 @@ end return STaylor1{N,T}($exout) end end +=# # Functions for STaylor1 +#= @generated function asin(a::STaylor1{N,T}) where {N, T <: Number} ex_calc = quote end append!(ex_calc.args, Any[nothing for i in 1:N]) @@ -202,8 +208,10 @@ end return STaylor1{N,T}($exout) end end +=# # Functions for STaylor1 +#= @generated function acos(a::STaylor1{N,T}) where {N, T <: Number} ex_calc = quote end append!(ex_calc.args, Any[nothing for i in 1:N]) @@ -237,8 +245,10 @@ end return STaylor1{N,T}($exout) end end +=# # Functions for STaylor1 +#= @generated function atan(a::STaylor1{N,T}) where {N, T <: Number} ex_calc = quote end append!(ex_calc.args, Any[nothing for i in 1:N]) @@ -272,6 +282,7 @@ end return STaylor1{N,T}($exout) end end +=# sinh(a::STaylor1{N,T}) where {N, T <: Number} = sinhcosh(a)[1] cosh(a::STaylor1{N,T}) where {N, T <: Number} = sinhcosh(a)[2] diff --git a/test/runtests.jl b/test/runtests.jl index 6f895ea..6448539 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -58,7 +58,7 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") # check that STaylor1 and Taylor yeild same result t1 = STaylor1([1.1, 2.1, 3.1]) t2 = Taylor1([1.1, 2.1, 3.1]) - for f in (exp, abs, log, sin, cos, tan, sinh, cosh, tanh, mod2pi) + for f in (exp, abs, log, sin, cos, sinh, cosh, mod2pi) @test test_vs_Taylor1(f(t1), f(t2)) end @@ -76,13 +76,26 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") t1a = STaylor1([2.1, 2.1, 3.1]) t2a = Taylor1([2.1, 2.1, 3.1]) - @test isapprox((t1/t1a)[0], (t2/t2a)[0], atol=1E-10) - @test isapprox((t1/t1a)[1], (t2/t2a)[1], atol=1E-10) - @test isapprox((t1/t1a)[2], (t2/t2a)[2], atol=1E-10) - @test isapprox((t1*t1a)[0], (t2*t2a)[0], atol=1E-10) - @test isapprox((t1*t1a)[1], (t2*t2a)[1], atol=1E-10) - @test isapprox((t1*t1a)[2], (t2*t2a)[2], atol=1E-10) + for test_tup in ((/, t1, t1a, t2, t2a), (*, t1, t1a, t2, t2a), + (/, t1, 1.3, t2, 1.3), (*, t1, 1.3, t2, 1.3), + (+, t1, 1.3, t2, 1.3), (-, t1, 1.3, t2, 1.3), + (*, 1.3, t1, 1.3, t2), (+, 1.3, t1, 1.3, t2), + (-, 1.3, t1, 1.3, t2), (*, 1.3, t1, 1.3, t2), + #(^, t1, -1, t2, -1), (^, t1, -3, t2, -3), + (^, t1, 0, t2, 0), (^, t1, 1, t2, 1), + (^, t1, 3, t2, 3), (^, t1, 4, t2, 4)) + + temp1 = test_tup[1](test_tup[2], test_tup[3]) + temp2 = test_tup[1](test_tup[4], test_tup[5]) + @test isapprox(temp1[0], temp2[0], atol=1E-10) + @test isapprox(temp1[1], temp2[1], atol=1E-10) + @test isapprox(temp1[2], temp2[2], atol=1E-10) + end + + #@test isapprox((1.3/t1)[0], (1.3/t2)[0], atol=1E-10) + #@test isapprox((1.3/t1)[1], (1.3/t2)[1], atol=1E-10) + #@test isapprox((1.3/t1)[2], (1.3/t2)[2], atol=1E-10) @test isapprox(StaticTaylorSeries.square(t1)[0], (t2^2)[0], atol=1E-10) @test isapprox(StaticTaylorSeries.square(t1)[1], (t2^2)[1], atol=1E-10) From 38f4355240ecb83e79d47438ec26049cd54940a3 Mon Sep 17 00:00:00 2001 From: Matt W Date: Mon, 21 Sep 2020 22:28:46 -0400 Subject: [PATCH 09/13] Add sqrt definition --- README.md | 2 +- src/StaticTaylorSeries.jl | 3 +- src/arithmetic.jl | 15 ++- src/constructors.jl | 8 +- src/power.jl | 188 +++++++++++++++++++++++++++++--------- test/runtests.jl | 8 +- 6 files changed, 172 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 2fca59f..a086e31 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ an array. - Constructors: Most constructors are similar to those used for Taylor1. The constructor `STaylor1(x::T, v::Val{N})` is used in place of `Taylor1(x::T, order::Int)`. - In place functions (e.g. `tan!(a,c,k)`) are not supported. -- Currently, **tan**, **tanh**, **sqrt**, **asin**, **acos**, **atan** are +- Currently, **tan**, **tanh**, **asin**, **acos**, **atan** are unsupported. #### License diff --git a/src/StaticTaylorSeries.jl b/src/StaticTaylorSeries.jl index 19fe572..061dea9 100644 --- a/src/StaticTaylorSeries.jl +++ b/src/StaticTaylorSeries.jl @@ -1,6 +1,7 @@ module StaticTaylorSeries -using Requires, TaylorSeries +using Requires +using TaylorSeries: AbstractSeries, NumberNotSeries import Base: ==, +, -, *, /, ^ diff --git a/src/arithmetic.jl b/src/arithmetic.jl index 19fd305..069b971 100644 --- a/src/arithmetic.jl +++ b/src/arithmetic.jl @@ -2,8 +2,15 @@ iszero(a::STaylor1) = all(iszero, a.coeffs) -zero(::STaylor1{N,T}) where {N, T<:Number} = STaylor1(zero(T), Val(N-1)) -one(::STaylor1{N,T}) where {N, T<:Number} = STaylor1(one(T), Val(N-1)) +function zero(::Type{STaylor1{N,T}}) where {N, T<:Number} + return STaylor1(zero(T), Val{N-1}()) +end +zero(a::STaylor1{N,T}) where {N, T<:Number} = zero(STaylor1{N,T}) + +function one(::Type{STaylor1{N,T}}) where {N, T<:Number} + return STaylor1(one(T), Val{N-1}()) +end +one(a::STaylor1{N,T}) where {N, T<:Number} = one(STaylor1{N,T}) @inline +(a::STaylor1{N,T}, b::STaylor1{N,T}) where {N, T<:Number} = STaylor1(a.coeffs .+ b.coeffs) @inline -(a::STaylor1{N,T}, b::STaylor1{N,T}) where {N, T<:Number} = STaylor1(a.coeffs .- b.coeffs) @@ -66,6 +73,10 @@ function /(a::STaylor1{N,T}, b::T) where {N, T<:Number} STaylor1{N,T}(a.coeffs ./ b) end +function /(b::T, a::STaylor1{N,T}) where {N, T<:Number} + return (b*one(STaylor1{N,T}))/a +end + @generated function /(a::STaylor1{N,T}, b::STaylor1{N,T}) where {N,T<:Number} ex_calc = quote end diff --git a/src/constructors.jl b/src/constructors.jl index ccbffff..2bebffc 100644 --- a/src/constructors.jl +++ b/src/constructors.jl @@ -12,8 +12,14 @@ DataType for polynomial expansions in one independent variable. Note that `STaylor1` variables are callable. For more information, see [`evaluate`](@ref). """ -struct STaylor1{N,T<:Number} <: TaylorSeries.AbstractSeries{T} +struct STaylor1{N,T<:Number} <: AbstractSeries{T} coeffs::NTuple{N,T} + function STaylor1{N,T}(coeffs::NTuple{N,T}) where {N, T <: Number} + new(coeffs) + end +end +function STaylor1(coeffs::NTuple{N,T}) where {N, T <: Number} + STaylor1{N,T}(coeffs) end ## Outer constructors ## diff --git a/src/power.jl b/src/power.jl index a588594..6327481 100644 --- a/src/power.jl +++ b/src/power.jl @@ -1,7 +1,7 @@ function ^(a::STaylor1{N,T}, n::Integer) where {N,T<:Real} n == 0 && return one(a) n == 1 && return a - n == 2 && return TaylorSeries.square(a) + n == 2 && return square(a) #n < 0 && return a^float(n) return power_by_squaring(a, n) end @@ -32,6 +32,77 @@ function power_by_squaring(x::STaylor1{N,T}, p::Integer) where {N,T<:Number} return y end +@generated function ^(a::STaylor1{N,T}, r::S) where {N, T<:Number, S<:Real} + + start_quote = quote + (iszero(r)) && return one(T) + (isone(r)) && return a + (r == 2) && return square(a) + (r == 0.5) && return sqrt(a) + end + + #exout = :(($(syms[1]),)) + #for i = 2:N + # push!(exout.args, syms[i]) + #end + + return quote + iszero(r) && return one(a) + r == 1 && return a + r == 2 && return square(a) + r == 1/2 && return sqrt(a) + + c = STaylor1(zero(T), Val{N}()) + for k = 0:(N - 1) + $continue_quote + + # First non-zero coefficient + l0 = findfirst(a) + if l0 < 0 + c = STaylor1{N,T}(ntuple(i -> (i == k) ? zero(T) : c[i], N)) + continue + end + + # The first non-zero coefficient of the result; must be integer + !isinteger(r*l0) && throw(ArgumentError( + """The 0th order Taylor1 coefficient must be non-zero + to raise the Taylor1 polynomial to a non-integer exponent.""")) + lnull = trunc(Int, r*l0 ) + kprime = k - lnull + if (kprime < 0) || (lnull > a.order) + c = STaylor1{N,T}(ntuple(i -> (i == k) ? zero(T) : c[i], N)) + continue + end + + # Relevant for positive integer r, to avoid round-off errors + if isinteger(r) && (k > r*findlast(a)) + c = STaylor1{N,T}(ntuple(i -> (i == k) ? zero(T) : c[i], N)) + continue + end + + if k == lnull + c = STaylor1{N,T}(ntuple(i -> (i == k) ? a[l0]^r : c[i], N)) + continue + end + + # The recursion formula + if l0 + kprime ≤ (N - 1) + c = STaylor1{N,T}(ntuple(i -> (i == k) ? (r*kprime*c[lnull]*a[l0 + kprime]) : c[i], N)) + else + c = STaylor1{N,T}(ntuple(i -> (i == k) ? zero(T) : c[i], N)) + end + for i = 1:(k - lnull - 1) + ((i + lnull) > (N - 1) || (l0 + kprime - i > (N - 1))) && continue + aux = r*(kprime - i) - i + c = STaylor1{N,T}(ntuple(i -> (i == k) ? c[k] + aux*c[i+lnull]*a[l0+kprime-i] : c[i], N)) + end + c = STaylor1{N,T}(ntuple(i -> (i == k) ? c[k]/kprime*a[l0] : c[i], N)) + end + + return c + end +end + @generated function square(a::STaylor1{N,T}) where {N, T<:Number} ex_calc = quote end append!(ex_calc.args, Any[nothing for i in 1:N]) @@ -102,66 +173,95 @@ end end end +function tup_sel(i, vargs) + return vargs[i+1] +end + +@generated function sqrt(a::STaylor1{N,T}) where {N,T<:Number} -@generated function sqrt(a::STaylor1{N,T}) where {N, T <: Number} ex_calc = quote end append!(ex_calc.args, Any[nothing for i in 1:N]) syms = Symbol[Symbol("c$i") for i in 1:N] + ctuple = Expr(:tuple) + for i = 1:N + push!(ctuple.args, syms[i]) + end - start_expr = quote - Base.@_inline_meta - l0nz = findfirst(a) - aux = zero(T) - if l0nz < 0 - return - elseif l0nz%2 == 1 - throw(ArgumentError( - """First non-vanishing Taylor1 coefficient must correspond - to an **even power** in order to expand `sqrt` around 0.""")) - end - lnull = div(l0nz, 2) - end + # First non-zero coefficient + expr_quote = quote + l0nz = findfirst(a) + aux = zero(T) + if l0nz < 0 + return zero(STaylor1{N,T}) + elseif l0nz%2 == 1 # l0nz must be pair + throw(ArgumentError( + """First non-vanishing Taylor1 coefficient must correspond + to an **even power** in order to expand `sqrt` around 0.""")) + end - for k = (lnull + 1):(N-1) + # The last l0nz coefficients are set to zero. + lnull = div(l0nz, 2) + end - pre_loop = quote - if k == lnull - @inbounds $(syms[k]) = sqrt(aa[2*lnull]) - return continue - end - kodd = (k - lnull)%2 - kend = div(k - lnull - 2 + kodd, 2) - imax = min(lnull + kend, N - 1) - imin = max(lnull + 1, k + lnull - N + 1) - imin ≤ imax && (@inbounds $(syms[k]) = $(syms[imin])*$(syms[k + lnull - imin])) - end + for i = 1:N + push!(ex_calc.args, :($(syms[i]) = zero(T))) + end - loop = quote - end - post_loop = quote + for i = 1:N + switch_expr = :((lnull == $(i-1)) && ($(syms[i]) = sqrt(a[l0nz]))) + expr_quote = quote + $expr_quote + $switch_expr end + end - - kT = convert(T,k) - sym = syms[k+1] - ex_line = :($kT * a[$k] * $(syms[1])) - @inbounds for i = 1:k-1 - ex_line = :($ex_line + $(kT-i) * a[$(k-i)] * $(syms[i+1])) + for k = 0:(N - 1) + symk = syms[k + 1] + temp_expr = quote + if $k >= lnull + 1 + if $k == lnull + $symk = sqrt(a[2*lnull]) + else + kodd = ($k - lnull)%2 + kend = div($k - lnull - 2 + kodd, 2) + imax = min(lnull + kend, N - 1) + imin = max(lnull + 1, $k + lnull - (N - 1)) + if imin ≤ imax + tup_in = $ctuple + $symk = tup_sel(imin, tup_in)*tup_sel($k + lnull - imin, tup_in) + end + for i = (imin + 1):imax + tup_in = $ctuple + $symk += tup_sel(i, tup_in)*tup_sel($k + lnull - i, tup_in) + end + if $k + lnull ≤ (N - 1) + aux = a[$k + lnull] - 2*$symk + else + aux = -2*$symk + end + tup_in = $ctuple + if kodd == 0 + aux -= tup_sel(kend + lnull + 1, tup_in)^2 + end + $symk = aux/(2*tup_sel(lnull, tup_in)) + end + end + end + expr_quote = quote + $expr_quote + $temp_expr end - ex_line = :(($ex_line)/$kT) - ex_line = :($sym = $ex_line) - ex_calc.args[k+1] = ex_line end exout = :(($(syms[1]),)) for i = 2:N push!(exout.args, syms[i]) end - return quote - $start_expr - $compute_expr - return STaylor1{N,T}($exout) - end + Base.@_inline_meta + $ex_calc + $expr_quote + return STaylor1{N,T}($exout) + end end diff --git a/test/runtests.jl b/test/runtests.jl index 6448539..9d99750 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -58,7 +58,7 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") # check that STaylor1 and Taylor yeild same result t1 = STaylor1([1.1, 2.1, 3.1]) t2 = Taylor1([1.1, 2.1, 3.1]) - for f in (exp, abs, log, sin, cos, sinh, cosh, mod2pi) + for f in (exp, abs, log, sin, cos, sinh, cosh, mod2pi, sqrt) @test test_vs_Taylor1(f(t1), f(t2)) end @@ -82,9 +82,11 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") (+, t1, 1.3, t2, 1.3), (-, t1, 1.3, t2, 1.3), (*, 1.3, t1, 1.3, t2), (+, 1.3, t1, 1.3, t2), (-, 1.3, t1, 1.3, t2), (*, 1.3, t1, 1.3, t2), - #(^, t1, -1, t2, -1), (^, t1, -3, t2, -3), (^, t1, 0, t2, 0), (^, t1, 1, t2, 1), - (^, t1, 3, t2, 3), (^, t1, 4, t2, 4)) + (^, t1, 2, t2, 2), (^, t1, 3, t2, 3), + (^, t1, 4, t2, 4), (/, 1.3, t1, 1.3, t2)) #(^, t1,-2, t2, -2)) + + #(^, t1, -1, t2, -1), (^, t1,-2, t2, -2), (^, t1, -3, t2, -3), temp1 = test_tup[1](test_tup[2], test_tup[3]) temp2 = test_tup[1](test_tup[4], test_tup[5]) From a8913b1b5d2df856f1d51cd072f0d5de0c1b9554 Mon Sep 17 00:00:00 2001 From: Matt W Date: Mon, 21 Sep 2020 23:26:26 -0400 Subject: [PATCH 10/13] Preliminary work on real valued powers --- src/power.jl | 124 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 74 insertions(+), 50 deletions(-) diff --git a/src/power.jl b/src/power.jl index 6327481..48a9b73 100644 --- a/src/power.jl +++ b/src/power.jl @@ -34,73 +34,97 @@ end @generated function ^(a::STaylor1{N,T}, r::S) where {N, T<:Number, S<:Real} - start_quote = quote - (iszero(r)) && return one(T) - (isone(r)) && return a - (r == 2) && return square(a) - (r == 0.5) && return sqrt(a) + ex_calc = quote end + append!(ex_calc.args, Any[nothing for i in 1:N]) + syms = Symbol[Symbol("c$i") for i in 1:N] + ctuple = Expr(:tuple) + for i = 1:N + push!(ctuple.args, syms[i]) end - #exout = :(($(syms[1]),)) - #for i = 2:N - # push!(exout.args, syms[i]) - #end + for i = 1:N + push!(ex_calc.args, :($(syms[i]) = zero(T))) + end - return quote + expr_quote = quote iszero(r) && return one(a) r == 1 && return a r == 2 && return square(a) r == 1/2 && return sqrt(a) + $ex_calc + end - c = STaylor1(zero(T), Val{N}()) - for k = 0:(N - 1) - $continue_quote - + c = STaylor1(zero(T), Val{N}()) + for k = 0:(N - 1) + symk = syms[k + 1] + temp_quote = quote # First non-zero coefficient l0 = findfirst(a) if l0 < 0 - c = STaylor1{N,T}(ntuple(i -> (i == k) ? zero(T) : c[i], N)) - continue - end - - # The first non-zero coefficient of the result; must be integer - !isinteger(r*l0) && throw(ArgumentError( - """The 0th order Taylor1 coefficient must be non-zero - to raise the Taylor1 polynomial to a non-integer exponent.""")) - lnull = trunc(Int, r*l0 ) - kprime = k - lnull - if (kprime < 0) || (lnull > a.order) - c = STaylor1{N,T}(ntuple(i -> (i == k) ? zero(T) : c[i], N)) - continue - end - - # Relevant for positive integer r, to avoid round-off errors - if isinteger(r) && (k > r*findlast(a)) - c = STaylor1{N,T}(ntuple(i -> (i == k) ? zero(T) : c[i], N)) - continue - end - - if k == lnull - c = STaylor1{N,T}(ntuple(i -> (i == k) ? a[l0]^r : c[i], N)) - continue - end - - # The recursion formula - if l0 + kprime ≤ (N - 1) - c = STaylor1{N,T}(ntuple(i -> (i == k) ? (r*kprime*c[lnull]*a[l0 + kprime]) : c[i], N)) + $symk = zero(T) else - c = STaylor1{N,T}(ntuple(i -> (i == k) ? zero(T) : c[i], N)) + # The first non-zero coefficient of the result; must be integer + !isinteger(r*l0) && throw(ArgumentError( + """The 0th order Taylor1 coefficient must be non-zero + to raise the Taylor1 polynomial to a non-integer exponent.""")) + lnull = trunc(Int, r*l0) + kprime = k - lnull + if (kprime < 0) || (lnull > a.order) + $symk = zero(T) + else + # Relevant for positive integer r, to avoid round-off errors + if isinteger(r) && (k > r*findlast(a)) + $symk = zero(T) + else + if k == lnull + $symk = a[l0]^r + else + # The recursion formula + if l0 + kprime ≤ (N - 1) + tup_in = $ctuple + $symk = r*kprime*tup_sel(lnull, tup_in)*a[l0 + kprime] + else + $symk = zero(T) + end + for i = 1:(k - lnull - 1) + if !((i + lnull) > (N - 1) || (l0 + kprime - i > (N - 1))) + aux = r*(kprime - i) - i + tup_in = $ctuple + $symk += aux*tup_sel(i + lnull, tup_in)*a[l0 + kprime - i] + end + end + $symk /= kprime*a[l0] + end + end + end end - for i = 1:(k - lnull - 1) - ((i + lnull) > (N - 1) || (l0 + kprime - i > (N - 1))) && continue - aux = r*(kprime - i) - i - c = STaylor1{N,T}(ntuple(i -> (i == k) ? c[k] + aux*c[i+lnull]*a[l0+kprime-i] : c[i], N)) + end + expr_quote = quote + $expr_quote + if r == 0 + # one(c) + elseif r == 1 + # DO NOTHING + elseif r == 2 + # square(c) + elseif r == 0.5 + # sqrt(c) + else + $temp_quote end - c = STaylor1{N,T}(ntuple(i -> (i == k) ? c[k]/kprime*a[l0] : c[i], N)) end + end - return c + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) end + + return quote + Base.@_inline_meta + $expr_quote + return STaylor1{N,T}($exout) + end end @generated function square(a::STaylor1{N,T}) where {N, T<:Number} From 043b1c5ad58557f9c0df4698e72f1b530e098ac8 Mon Sep 17 00:00:00 2001 From: Matt W Date: Tue, 22 Sep 2020 09:04:16 -0400 Subject: [PATCH 11/13] Finish pow(x, Float64) definition --- src/power.jl | 22 +++++++++++----------- test/runtests.jl | 11 ++++------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/power.jl b/src/power.jl index 48a9b73..9add5f4 100644 --- a/src/power.jl +++ b/src/power.jl @@ -2,7 +2,7 @@ function ^(a::STaylor1{N,T}, n::Integer) where {N,T<:Real} n == 0 && return one(a) n == 1 && return a n == 2 && return square(a) - #n < 0 && return a^float(n) + n < 0 && return a^float(n) return power_by_squaring(a, n) end @@ -68,15 +68,15 @@ end """The 0th order Taylor1 coefficient must be non-zero to raise the Taylor1 polynomial to a non-integer exponent.""")) lnull = trunc(Int, r*l0) - kprime = k - lnull - if (kprime < 0) || (lnull > a.order) + kprime = $k - lnull + if (kprime < 0) || (lnull > N-1) $symk = zero(T) else # Relevant for positive integer r, to avoid round-off errors - if isinteger(r) && (k > r*findlast(a)) + if isinteger(r) && ($k > r*findlast(a)) $symk = zero(T) else - if k == lnull + if $k == lnull $symk = a[l0]^r else # The recursion formula @@ -86,7 +86,7 @@ end else $symk = zero(T) end - for i = 1:(k - lnull - 1) + for i = 1:($k - lnull - 1) if !((i + lnull) > (N - 1) || (l0 + kprime - i > (N - 1))) aux = r*(kprime - i) - i tup_in = $ctuple @@ -99,16 +99,16 @@ end end end end + one_tup = ntuple(i -> i == 1 ? one(T) : zero(T), Val{N}()) expr_quote = quote $expr_quote if r == 0 - # one(c) - elseif r == 1 - # DO NOTHING + $ctuple = $one_tup + elseif r == 1 # DO NOTHING elseif r == 2 - # square(c) + temp_st1 = square(STaylor1{N,T}($ctuple)) elseif r == 0.5 - # sqrt(c) + temp_st2 = sqrt(STaylor1{N,T}($ctuple)) else $temp_quote end diff --git a/test/runtests.jl b/test/runtests.jl index 9d99750..35b3a91 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -84,9 +84,10 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") (-, 1.3, t1, 1.3, t2), (*, 1.3, t1, 1.3, t2), (^, t1, 0, t2, 0), (^, t1, 1, t2, 1), (^, t1, 2, t2, 2), (^, t1, 3, t2, 3), - (^, t1, 4, t2, 4), (/, 1.3, t1, 1.3, t2)) #(^, t1,-2, t2, -2)) - - #(^, t1, -1, t2, -1), (^, t1,-2, t2, -2), (^, t1, -3, t2, -3), + (^, t1, 4, t2, 4), (/, 1.3, t1, 1.3, t2), + (^, t1, -1, t2, -1), (^, t1,-2, t2, -2), + (^, t1, -3, t2, -3), (^, t1, 0.6, t2, 0.6), + (^, t1, 1/2, t2, 1/2)) temp1 = test_tup[1](test_tup[2], test_tup[3]) temp2 = test_tup[1](test_tup[4], test_tup[5]) @@ -95,10 +96,6 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") @test isapprox(temp1[2], temp2[2], atol=1E-10) end - #@test isapprox((1.3/t1)[0], (1.3/t2)[0], atol=1E-10) - #@test isapprox((1.3/t1)[1], (1.3/t2)[1], atol=1E-10) - #@test isapprox((1.3/t1)[2], (1.3/t2)[2], atol=1E-10) - @test isapprox(StaticTaylorSeries.square(t1)[0], (t2^2)[0], atol=1E-10) @test isapprox(StaticTaylorSeries.square(t1)[1], (t2^2)[1], atol=1E-10) @test isapprox(StaticTaylorSeries.square(t1)[2], (t2^2)[2], atol=1E-10) From 7de23837dbbf71368be3c977426392ef22a7efb0 Mon Sep 17 00:00:00 2001 From: Matt W Date: Tue, 22 Sep 2020 15:49:47 -0400 Subject: [PATCH 12/13] A method for evaluating interval --- src/StaticTaylorSeries.jl | 2 +- src/intervals.jl | 36 ++++++++++++++++++++++++++++++++++++ test/runtests.jl | 6 +++++- 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 src/intervals.jl diff --git a/src/StaticTaylorSeries.jl b/src/StaticTaylorSeries.jl index 061dea9..a445f39 100644 --- a/src/StaticTaylorSeries.jl +++ b/src/StaticTaylorSeries.jl @@ -25,7 +25,7 @@ export getcoeff, derivative, integrate, differentiate, get_order, get_numvars, set_variables, get_variables, get_variable_names, get_variable_symbols, taylor_expand, update!, constant_term, linear_polynomial, - normalize_taylor + normalize_taylor, evaluate include("constructors.jl") include("conversion.jl") diff --git a/src/intervals.jl b/src/intervals.jl new file mode 100644 index 0000000..c482fe8 --- /dev/null +++ b/src/intervals.jl @@ -0,0 +1,36 @@ +using .IntervalArithmetic + +function evaluate(a::STaylor1{N,T}, dx::Interval) where {N, T <: Number} + order = N - 1 + uno = one(dx) + dx2 = dx^2 + if iseven(order) + kend = order-2 + @inbounds sum_even = a[end]*uno + @inbounds sum_odd = a[end-1]*zero(dx) + else + kend = order-3 + @inbounds sum_odd = a[end]*uno + @inbounds sum_even = a[end-1]*uno + end + @inbounds for k = kend:-2:0 + sum_odd = sum_odd*dx2 + a[k + 1] + sum_even = sum_even*dx2 + a[k] + end + return sum_even + sum_odd*dx +end + +normalize_taylor(a::Taylor1{N,S}, I::Interval{T}, symI::Bool=true) where {N, T, S <: Number} = + _normalize(a, I, Val(symI)) + +function _normalize(a::Taylor1, I::Interval{T}, ::Val{true}) where {T} + t = STaylor1(zero(T), Val{N}()) + tnew = mid(I) + t*radius(I) + return a(tnew) +end + +function _normalize(a::Taylor1, I::Interval{T}, ::Val{false}) where {T} + t = STaylor1(zero(T), Val{N}()) + tnew = inf(I) + t*diam(I) + return a(tnew) +end diff --git a/test/runtests.jl b/test/runtests.jl index 35b3a91..b77dbc3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,7 +2,7 @@ using Test # Skipping tests with intervals, since IntervalArithmetic.jl requires Julia v1.1+ if !(VERSION < v"1.1" && testfile == "intervals.jl") - using TaylorSeries, StaticTaylorSeries + using TaylorSeries, StaticTaylorSeries, IntervalArithmetic function test_vs_Taylor1(x, y) flag = true @@ -104,6 +104,10 @@ if !(VERSION < v"1.1" && testfile == "intervals.jl") @test findfirst(a) == 1 @test findlast(a) == 3 + eval_staylor = StaticTaylorSeries.evaluate(a, Interval(1.0,2.0)) + @test isapprox(eval_staylor.lo, 7.99999, atol = 1E-4) + @test isapprox(eval_staylor.hi, 47.6001, atol = 1E-4) + a = STaylor1([5.0, 1.2, 2.3, 4.5, 0.0]) @test isapprox(deg2rad(a)[0], 0.087266, atol=1E-5) @test isapprox(deg2rad(a)[2], 0.040142, atol=1E-5) From 962b75010152ce076e8f5831279edc783a5a8361 Mon Sep 17 00:00:00 2001 From: Matt W Date: Wed, 23 Sep 2020 08:38:50 -0400 Subject: [PATCH 13/13] Talyor1 to STaylor1 in interval.jl --- src/intervals.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/intervals.jl b/src/intervals.jl index c482fe8..caf6298 100644 --- a/src/intervals.jl +++ b/src/intervals.jl @@ -20,17 +20,17 @@ function evaluate(a::STaylor1{N,T}, dx::Interval) where {N, T <: Number} return sum_even + sum_odd*dx end -normalize_taylor(a::Taylor1{N,S}, I::Interval{T}, symI::Bool=true) where {N, T, S <: Number} = +normalize_taylor(a::STaylor1{N,T}, I::Interval{T}, symI::Bool=true) where {N, T <: Number} = _normalize(a, I, Val(symI)) -function _normalize(a::Taylor1, I::Interval{T}, ::Val{true}) where {T} +function _normalize(a::STaylor1{N,T}, I::Interval{T}, ::Val{true}) where {N, T <: Number} t = STaylor1(zero(T), Val{N}()) tnew = mid(I) + t*radius(I) return a(tnew) end -function _normalize(a::Taylor1, I::Interval{T}, ::Val{false}) where {T} - t = STaylor1(zero(T), Val{N}()) +function _normalize(a::STaylor1{N,T}, I::Interval{T}, ::Val{false}) where {N, T <: Number} + t = STaylor1(zero(promote_type(S,T)), Val{N}()) tnew = inf(I) + t*diam(I) return a(tnew) end