Skip to content

Commit e5b4989

Browse files
authored
Add evaluation method for arrays of TaylorN (#364)
* WIP: add `evaluate!` method for `TaylorN`s * Update * Small fix * pow! fix for Julia 1.6 * Remove unused `@inbounds` * Bump minor version * Update README.md * Small fix and add comments * Update tests * Update tests again
1 parent bc87e50 commit e5b4989

File tree

7 files changed

+170
-70
lines changed

7 files changed

+170
-70
lines changed

Project.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "TaylorSeries"
22
uuid = "6aa5eb33-94cf-58f4-a9d0-e4b2c4fc25ea"
33
repo = "https://github.com/JuliaDiff/TaylorSeries.jl.git"
4-
version = "0.17.8"
4+
version = "0.18.0"
55

66
[deps]
77
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
@@ -12,14 +12,14 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
1212
[weakdeps]
1313
IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"
1414
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
15-
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
1615
RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd"
16+
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
1717

1818
[extensions]
1919
TaylorSeriesIAExt = "IntervalArithmetic"
2020
TaylorSeriesJLD2Ext = "JLD2"
21-
TaylorSeriesSAExt = "StaticArrays"
2221
TaylorSeriesRATExt = "RecursiveArrayTools"
22+
TaylorSeriesSAExt = "StaticArrays"
2323

2424
[compat]
2525
Aqua = "0.8"

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ julia> t = Taylor1(Float64, 5)
3030

3131
julia> exp(t)
3232
1.0 + 1.0 t + 0.5+ 0.16666666666666666+ 0.041666666666666664 t⁴ + 0.008333333333333333 t⁵ + 𝒪(t⁶)
33-
33+
3434
julia> log(1 + t)
3535
1.0 t - 0.5+ 0.3333333333333333- 0.25 t⁴ + 0.2 t⁵ + 𝒪(t⁶)
3636
```
@@ -40,7 +40,7 @@ julia> x, y = set_variables("x y", order=2);
4040

4141
julia> exp(x + y)
4242
1.0 + 1.0 x + 1.0 y + 0.5+ 1.0 x y + 0.5+ 𝒪(‖x‖³)
43-
43+
4444
```
4545
Differential and integral calculus on Taylor series:
4646
```julia
@@ -95,6 +95,6 @@ We thank the participants of the course for putting up with the half-baked
9595
material and contributing energy and ideas.
9696

9797
We acknowledge financial support from DGAPA-UNAM PAPIME grants
98-
PE-105911 and PE-107114, and DGAPA-PAPIIT grants IG-101113,
99-
IG-100616, and IG-100819.
98+
PE-105911 and PE-107114, and DGAPA-PAPIIT grants IG-101113,
99+
IG-100616, IG-100819 and IG-101122.
100100
LB acknowledges support through a *Cátedra Moshinsky* (2013).

src/arithmetic.jl

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,7 @@ for T in (:Taylor1, :HomogeneousPolynomial, :TaylorN)
162162
end
163163

164164
for T in (:Taylor1, :TaylorN)
165-
@eval function zero(a::$T)
166-
return $T(zero.(a.coeffs))
167-
end
165+
@eval zero(a::$T) = $T(zero.(a.coeffs))
168166
@eval function one(a::$T)
169167
b = zero(a)
170168
b[0] = one(b[0])
@@ -539,25 +537,50 @@ for T in (:Taylor1, :TaylorN)
539537
return nothing
540538
end
541539

542-
@eval @inline function mul!(v::$T, a::$T, b::NumberNotSeries, k::Int)
540+
@eval begin
543541
if $T == Taylor1
544-
@inbounds v[k] = a[k] * b
545-
else
546-
@inbounds for i in eachindex(v[k])
547-
v[k][i] = a[k][i] * b
542+
@inline function mul!(v::$T, a::$T, b::NumberNotSeries, k::Int)
543+
@inbounds v[k] = a[k] * b
544+
return nothing
545+
end
546+
@inline function mul!(v::$T, a::NumberNotSeries, b::$T, k::Int)
547+
@inbounds v[k] = a * b[k]
548+
return nothing
549+
end
550+
@inline function muladd!(v::$T, a::$T, b::NumberNotSeries, k::Int)
551+
@inbounds v[k] += a[k] * b
552+
return nothing
553+
end
554+
@inline function muladd!(v::$T, a::NumberNotSeries, b::$T, k::Int)
555+
@inbounds v[k] += a * b[k]
556+
return nothing
548557
end
549-
end
550-
return nothing
551-
end
552-
@eval @inline function mul!(v::$T, a::NumberNotSeries, b::$T, k::Int)
553-
if $T == Taylor1
554-
@inbounds v[k] = a * b[k]
555558
else
556-
@inbounds for i in eachindex(v[k])
557-
v[k][i] = a * b[k][i]
559+
@inline function mul!(v::$T, a::$T, b::NumberNotSeries, k::Int)
560+
@inbounds for i in eachindex(v[k])
561+
v[k][i] = a[k][i] * b
562+
end
563+
return nothing
564+
end
565+
@inline function mul!(v::$T, a::NumberNotSeries, b::$T, k::Int)
566+
@inbounds for i in eachindex(v[k])
567+
v[k][i] = a * b[k][i]
568+
end
569+
return nothing
570+
end
571+
@inline function muladd!(v::$T, a::$T, b::NumberNotSeries, k::Int)
572+
@inbounds for i in eachindex(v[k])
573+
v[k][i] += a[k][i] * b
574+
end
575+
return nothing
576+
end
577+
@inline function muladd!(v::$T, a::NumberNotSeries, b::$T, k::Int)
578+
@inbounds for i in eachindex(v[k])
579+
v[k][i] += a * b[k][i]
580+
end
581+
return nothing
558582
end
559583
end
560-
return nothing
561584
end
562585

563586
@eval @inline function mul!(v::$T, a::$T, b::NumberNotSeries)
@@ -951,7 +974,8 @@ end
951974

952975
@inline function div!(c::Taylor1{T}, a::NumberNotSeries,
953976
b::Taylor1{T}, k::Int) where {T<:Number}
954-
iszero(a) && !iszero(b) && zero!(c, k)
977+
zero!(c, k)
978+
iszero(a) && !iszero(b) && return nothing
955979
# order and coefficient of first factorized term
956980
# In this case, since a[k]=0 for k>0, we can simplify to:
957981
# ordfact, cdivfact = 0, a/b[0]
@@ -970,7 +994,8 @@ end
970994

971995
@inline function div!(c::Taylor1{TaylorN{T}}, a::NumberNotSeries,
972996
b::Taylor1{TaylorN{T}}, k::Int) where {T<:NumberNotSeries}
973-
iszero(a) && !iszero(b) && zero!(c, k)
997+
zero!(c, k)
998+
iszero(a) && !iszero(b) && return nothing
974999
# order and coefficient of first factorized term
9751000
# In this case, since a[k]=0 for k>0, we can simplify to:
9761001
# ordfact, cdivfact = 0, a/b[0]
@@ -1141,14 +1166,14 @@ end
11411166
"""Division does not define a Taylor1 polynomial;
11421167
order k=$(ordfact) => coeff[$(ordfact)]=$(cdivfact).""") )
11431168

1169+
zero!(c, k)
1170+
11441171
if k == 0
11451172
# @inbounds c[0] = a[ordfact]/b[ordfact]
11461173
@inbounds div!(c[0], a[ordfact], b[ordfact])
11471174
return nothing
11481175
end
11491176

1150-
@inbounds zero!(c, k)
1151-
11521177
imin = max(0, k+ordfact-b.order)
11531178
@inbounds mul!(c[k], c[imin], b[k+ordfact-imin])
11541179
@inbounds for i = imin+1:k-1

src/evaluate.jl

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -187,43 +187,42 @@ function _evaluate(a::HomogeneousPolynomial{T}, vals::NTuple) where {T}
187187
return suma
188188
end
189189

190-
function _evaluate(a::HomogeneousPolynomial{T}, vals::NTuple{N,<:TaylorN{T}}) where
191-
{N,T<:NumberNotSeries}
192-
# @assert length(vals) == get_numvars()
193-
a.order == 0 && return a[1]*one(vals[1])
190+
function _evaluate!(res::TaylorN{T}, a::HomogeneousPolynomial{T}, vals::NTuple{N,<:TaylorN{T}},
191+
valscache::Vector{TaylorN{T}}, aux::TaylorN{T}) where {N,T<:NumberNotSeries}
194192
ct = coeff_table[a.order+1]
195-
suma = TaylorN(zero(T), vals[1].order)
196-
#
197-
# vv = power_by_squaring.(vals, ct[1])
198-
vv = vals .^ ct[1]
199-
tmp = zero(suma)
200-
aux = one(suma)
193+
for el in eachindex(valscache)
194+
power_by_squaring!(valscache[el], vals[el], aux, ct[1][el])
195+
end
201196
for (i, a_coeff) in enumerate(a.coeffs)
202197
iszero(a_coeff) && continue
203-
# @inbounds vv .= power_by_squaring.(vals, ct[i])
204-
vv .= vals .^ ct[i]
205-
# tmp = prod( vv )
206-
for ord in eachindex(tmp)
207-
@inbounds one!(aux, vv[1], ord)
198+
# valscache .= vals .^ ct[i]
199+
@inbounds for el in eachindex(valscache)
200+
power_by_squaring!(valscache[el], vals[el], aux, ct[i][el])
208201
end
209-
for j in eachindex(vv)
210-
for ord in eachindex(tmp)
211-
zero!(tmp, ord)
212-
@inbounds mul!(tmp, aux, vv[j], ord)
213-
end
214-
for ord in eachindex(tmp)
215-
identity!(aux, tmp, ord)
216-
end
202+
# aux = one(valscache[1])
203+
for ord in eachindex(aux)
204+
@inbounds one!(aux, valscache[1], ord)
217205
end
218-
# suma += a_coeff * tmp
219-
for ord in eachindex(tmp)
220-
for ordQ in eachindex(tmp[ord])
221-
zero!(aux[ord], ordQ)
222-
aux[ord][ordQ] = a_coeff * tmp[ord][ordQ]
223-
suma[ord][ordQ] += aux[ord][ordQ]
224-
end
206+
for j in eachindex(valscache)
207+
# aux *= valscache[j]
208+
mul!(aux, valscache[j])
209+
end
210+
# res += a_coeff * aux
211+
for ord in eachindex(aux)
212+
muladd!(res, a_coeff, aux, ord)
225213
end
226214
end
215+
return nothing
216+
end
217+
218+
function _evaluate(a::HomogeneousPolynomial{T}, vals::NTuple{N,<:TaylorN{T}}) where
219+
{N,T<:NumberNotSeries}
220+
# @assert length(vals) == get_numvars()
221+
a.order == 0 && return a[1]*one(vals[1])
222+
suma = TaylorN(zero(T), vals[1].order)
223+
valscache = [zero(val) for val in vals]
224+
aux = zero(suma)
225+
_evaluate!(suma, a, vals, valscache, aux)
227226
return suma
228227
end
229228

@@ -319,6 +318,17 @@ _evaluate(a::TaylorN{T}, vals::NTuple, ::Val{true}) where {T<:NumberNotSeries} =
319318
_evaluate(a::TaylorN{T}, vals::NTuple, ::Val{false}) where {T<:Number} =
320319
sum( _evaluate(a, vals) )
321320

321+
function _evaluate(a::TaylorN{T}, vals::NTuple{N,<:TaylorN}, ::Val{false}) where {N,T<:Number}
322+
R = promote_type(T, TS.numtype(vals[1]))
323+
res = TaylorN(zero(R), vals[1].order)
324+
valscache = [zero(val) for val in vals]
325+
aux = zero(res)
326+
@inbounds for homPol in eachindex(a)
327+
_evaluate!(res, a[homPol], vals, valscache, aux)
328+
end
329+
return res
330+
end
331+
322332
function _evaluate(a::TaylorN{T}, vals::NTuple{N,<:Number}) where {N,T<:Number}
323333
R = promote_type(T, typeof(vals[1]))
324334
a_length = length(a)
@@ -329,13 +339,20 @@ function _evaluate(a::TaylorN{T}, vals::NTuple{N,<:Number}) where {N,T<:Number}
329339
return suma
330340
end
331341

332-
function _evaluate(a::TaylorN{T}, vals::NTuple{N,<:TaylorN}) where {N,T<:Number}
333-
R = TaylorN{promote_type(T, TS.numtype(vals[1]))}
334-
a_length = length(a)
335-
suma = zeros(R, a_length)
342+
function _evaluate!(res::Vector{TaylorN{T}}, a::TaylorN{T}, vals::NTuple{N,<:TaylorN},
343+
valscache::Vector{TaylorN{T}}, aux::TaylorN{T}) where {N,T<:Number}
336344
@inbounds for homPol in eachindex(a)
337-
suma[homPol+1] = _evaluate(a[homPol], vals)
345+
_evaluate!(res[homPol+1], a[homPol], vals, valscache, aux)
338346
end
347+
return nothing
348+
end
349+
350+
function _evaluate(a::TaylorN{T}, vals::NTuple{N,<:TaylorN}) where {N,T<:Number}
351+
R = promote_type(T, TS.numtype(vals[1]))
352+
suma = [TaylorN(zero(R), vals[1].order) for _ in eachindex(a)]
353+
valscache = [zero(val) for val in vals]
354+
aux = zero(suma[1])
355+
_evaluate!(suma, a, vals, valscache, aux)
339356
return suma
340357
end
341358

@@ -486,6 +503,24 @@ function evaluate!(x::AbstractArray{TaylorN{T}}, δx::Array{TaylorN{T},1},
486503
return nothing
487504
end
488505

506+
function evaluate!(a::TaylorN{T}, vals::NTuple{N,TaylorN{T}}, dest::TaylorN{T}, valscache::Vector{TaylorN{T}}, aux::TaylorN{T}) where {N,T<:Number}
507+
@inbounds for homPol in eachindex(a)
508+
_evaluate!(dest, a[homPol], vals, valscache, aux)
509+
end
510+
return nothing
511+
end
512+
513+
function evaluate!(a::AbstractArray{TaylorN{T}}, vals::NTuple{N,TaylorN{T}}, dest::AbstractArray{TaylorN{T}}) where {N,T<:Number}
514+
# initialize evaluation cache
515+
valscache = [zero(val) for val in vals]
516+
aux = zero(dest[1])
517+
# loop over elements of `a`
518+
for i in eachindex(a)
519+
(!iszero(dest[i])) && zero!(dest[i])
520+
evaluate!(a[i], vals, dest[i], valscache, aux)
521+
end
522+
return nothing
523+
end
489524

490525
# In-place Horner methods, used when the result of an evaluation (substitution)
491526
# is Taylor1{}

src/functions.jl

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -354,12 +354,18 @@ for T in (:Taylor1, :TaylorN)
354354
return nothing
355355
end
356356

357-
@inline function one!(c::$T{T}, a::$T{T}, k::Int) where {T<:Number}
358-
zero!(c, k)
359-
if k == 0
360-
@inbounds c[0] = one(a[0])
357+
if $T == Taylor1
358+
@inline function one!(c::$T{T}, a::$T{T}, k::Int) where {T<:Number}
359+
zero!(c, k)
360+
(k == 0) && (@inbounds c[0] = one(constant_term(a)))
361+
return nothing
362+
end
363+
else
364+
@inline function one!(c::$T{T}, a::$T{T}, k::Int) where {T<:Number}
365+
zero!(c, k)
366+
(k == 0) && (@inbounds c[0][1] = one(constant_term(a)))
367+
return nothing
361368
end
362-
return nothing
363369
end
364370

365371
@inline function abs!(c::$T{T}, a::$T{T}, k::Int) where {T<:Number}

src/power.jl

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,15 @@ end
6262
# this method assumes `y`, `x` and `aux` are of same order
6363
# TODO: add power_by_squaring! method for HomogeneousPolynomial and mixtures
6464
for T in (:Taylor1, :TaylorN)
65-
@eval function power_by_squaring!(y::$T{T}, x::$T{T}, aux::$T{T},
65+
@eval @inline function power_by_squaring_0!(y::$T{T}, x::$T{T}) where {T<:NumberNotSeries}
66+
for k in eachindex(y)
67+
one!(y, x, k)
68+
end
69+
return nothing
70+
end
71+
@eval @inline function power_by_squaring!(y::$T{T}, x::$T{T}, aux::$T{T},
6672
p::Integer) where {T<:NumberNotSeries}
73+
(p == 0) && return power_by_squaring_0!(y, x)
6774
t = trailing_zeros(p) + 1
6875
p >>= t
6976
# aux = x
@@ -272,7 +279,7 @@ exploits `k_0`, the order of the first non-zero coefficient of `a`.
272279
isinteger(r) && r > 0 && (k > r*findlast(a)) && return nothing
273280

274281
if k == lnull
275-
@inbounds c[k] = (a[l0])^r
282+
@inbounds c[k] = (a[l0])^float(r)
276283
return nothing
277284
end
278285

test/manyvariables.jl

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,33 @@ end
821821
@test float(TaylorN{Complex{Rational}}) == float(TaylorN{Complex{Float64}})
822822
end
823823

824+
@testset "Test evaluate! method for arrays of TaylorN" begin
825+
x = set_variables("x", order=2, numvars=4)
826+
function radntn!(y)
827+
for k in eachindex(y)
828+
for l in eachindex(y[k])
829+
y[k][l] = randn()
830+
end
831+
end
832+
nothing
833+
end
834+
y = zero(x[1])
835+
radntn!(y)
836+
n = 10
837+
v = [zero(x[1]) for _ in 1:n]
838+
r = [zero(x[1]) for _ in 1:n] # output vector
839+
radntn!.(v)
840+
x1 = randn(4) .+ x
841+
# warmup
842+
TaylorSeries.evaluate!(v, (x1...,), r)
843+
# call twice to make sure `r` is reset on second call
844+
TaylorSeries.evaluate!(v, (x1...,), r)
845+
r2 = evaluate.(v, Ref(x1))
846+
@test r == r2
847+
@test iszero(norm(r-r2, Inf))
848+
849+
end
850+
824851
end
825852

826853
@testset "Integrate for several variables" begin

0 commit comments

Comments
 (0)