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..a086e31 100644 --- a/README.md +++ b/README.md @@ -1 +1,33 @@ -# StaticTaylorSeries +# StaticTaylorSeries.jl +A [Julia](http://julialang.org) package used to compute static 1D Taylor +polynomial expansions + +[![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**, **asin**, **acos**, **atan** are +unsupported. + +#### License + +`StaticTaylorSeries` is licensed under the [MIT "Expat" license](./LICENSE.md). diff --git a/src/StaticTaylorSeries.jl b/src/StaticTaylorSeries.jl index 38f977d..a445f39 100644 --- a/src/StaticTaylorSeries.jl +++ b/src/StaticTaylorSeries.jl @@ -1,192 +1,44 @@ 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 +using TaylorSeries: AbstractSeries, NumberNotSeries + +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, evaluate + +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..069b971 --- /dev/null +++ b/src/arithmetic.jl @@ -0,0 +1,117 @@ +==(a::STaylor1, b::STaylor1) = (a.coeffs == b.coeffs) + +iszero(a::STaylor1) = all(iszero, a.coeffs) + +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) +@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 + +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 + 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..2bebffc --- /dev/null +++ b/src/constructors.jl @@ -0,0 +1,48 @@ +######################### 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} <: 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 ## + +""" + 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..9a2d385 --- /dev/null +++ b/src/functions.jl @@ -0,0 +1,384 @@ +# 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 + +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:(2*N)]) + + syms_s = Symbol[Symbol("c$i") for i in 1:N] + syms_c = Symbol[Symbol("c2$i") for i in 1:N] + + 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_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_s = :(($(syms_s[1]),)) + for i = 2:N + push!(exout_s.args, syms_s[i]) + end + + exout_c = :(($(syms_c[1]),)) + for i = 2:N + push!(exout_c.args, syms_c[i]) + end + + return quote + Base.@_inline_meta + $ex_calc + return STaylor1{N,T}($exout_s), STaylor1{N,T}($exout_c) + 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:(4*N)]) + syms = Symbol[Symbol("c$i") for i in 1:N] + syms2 = Symbol[Symbol("c2$i") for i in 1:N] + + 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[N + 1] = ex_line_c + ex_calc.args[N + 2] = ex_line_c2 + + for k = 1:(N - 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_c = :(a[$k] + ($ex_line_c)/$k) + ex_line_c = :($(syms[k + 1]) = $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 + 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[(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]),)) + 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 +=# + +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:(2*N)]) + + syms_s = Symbol[Symbol("c$i") for i in 1:N] + syms_c = Symbol[Symbol("c2$i") for i in 1:N] + + 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_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_s = :(($(syms_s[1]),)) + for i = 2:N + push!(exout_s.args, syms_s[i]) + end + + exout_c = :(($(syms_c[1]),)) + for i = 2:N + push!(exout_c.args, syms_c[i]) + end + + return quote + Base.@_inline_meta + $ex_calc + return STaylor1{N,T}($exout_s), STaylor1{N,T}($exout_c) + 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:(2*N)]) + syms = Symbol[Symbol("c$i") for i in 1:N] + syms2 = Symbol[Symbol("c2$i") for i in 1:N] + + 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 = 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_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 + return STaylor1{N,T}($exout) + end +end diff --git a/src/intervals.jl b/src/intervals.jl new file mode 100644 index 0000000..caf6298 --- /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::STaylor1{N,T}, I::Interval{T}, symI::Bool=true) where {N, T <: Number} = + _normalize(a, I, Val(symI)) + +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::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 diff --git a/src/other_functions.jl b/src/other_functions.jl new file mode 100644 index 0000000..5fa1faf --- /dev/null +++ b/src/other_functions.jl @@ -0,0 +1,51 @@ +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)) + +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 + +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/src/power.jl b/src/power.jl new file mode 100644 index 0000000..9add5f4 --- /dev/null +++ b/src/power.jl @@ -0,0 +1,291 @@ +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) + 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 ^(a::STaylor1{N,T}, r::S) where {N, T<:Number, S<:Real} + + 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 + + for i = 1:N + push!(ex_calc.args, :($(syms[i]) = zero(T))) + end + + 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) + symk = syms[k + 1] + temp_quote = quote + # First non-zero coefficient + l0 = findfirst(a) + if l0 < 0 + $symk = zero(T) + else + # 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 > N-1) + $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 + end + one_tup = ntuple(i -> i == 1 ? one(T) : zero(T), Val{N}()) + expr_quote = quote + $expr_quote + if r == 0 + $ctuple = $one_tup + elseif r == 1 # DO NOTHING + elseif r == 2 + temp_st1 = square(STaylor1{N,T}($ctuple)) + elseif r == 0.5 + temp_st2 = sqrt(STaylor1{N,T}($ctuple)) + else + $temp_quote + end + end + end + + 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} + 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 + +@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 + +function tup_sel(i, vargs) + return vargs[i+1] +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] + ctuple = Expr(:tuple) + for i = 1:N + push!(ctuple.args, syms[i]) + 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 + + # The last l0nz coefficients are set to zero. + lnull = div(l0nz, 2) + end + + for i = 1:N + push!(ex_calc.args, :($(syms[i]) = zero(T))) + end + + + for i = 1:N + switch_expr = :((lnull == $(i-1)) && ($(syms[i]) = sqrt(a[l0nz]))) + expr_quote = quote + $expr_quote + $switch_expr + end + end + + 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 + end + + exout = :(($(syms[1]),)) + for i = 2:N + push!(exout.args, syms[i]) + end + return quote + Base.@_inline_meta + $ex_calc + $expr_quote + 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..b77dbc3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,15 +1,134 @@ -using StaticTaylorSeries using Test -@testset "StaticTaylorSeries.jl" begin - t = StaticTaylor(1, 2, 3) +# Skipping tests with intervals, since IntervalArithmetic.jl requires Julia v1.1+ +if !(VERSION < v"1.1" && testfile == "intervals.jl") + using TaylorSeries, StaticTaylorSeries, IntervalArithmetic - @test t + 1 == StaticTaylor(2, 2, 3) - @test t - 1 == StaticTaylor(0, 2, 3) - @test t * 2 == StaticTaylor(2, 4, 6) + 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^2 == StaticTaylor(1, 4, 10) + @testset "Tests for STaylor1 expansions" begin - t = StaticTaylor(1.0, 2.0, 3.0) - @test t / 2 == StaticTaylor(0.5, 1.0, 1.5) + @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)) + + @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 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]) + t2 = Taylor1([1.1, 2.1, 3.1]) + for f in (exp, abs, log, sin, cos, sinh, cosh, mod2pi, sqrt) + @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]) + + 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, 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, -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]) + @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(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 + @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) + @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) + + @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