Skip to content

Commit bc1735c

Browse files
authored
Avoid some allocations in mutating methods (#347)
* WIP: proof of concept: avoid allocations in inplace methods * Move zero! methods from arithmetic.jl to functions.jl * Substitute zero! methods where appropriate * Substitute 0:a.order -> eachindex(a) * Add one-order zero! methods * Try allocation-free zero! for non-mixtures * WIP: debugging zero stuff * WIP: workaround TaylorIntegration bug * Add tests * Fix typo * Small fix in div! * Allocate less in div!(::Taylor1,::NumberNotSeries,::Taylor1,Int) * Add missing div! method * Add /(::NumberNotSeries,::Taylor1{TaylorN{<:NumberNotSeries}} * Update comments * Address review by @lbenet * WIP: try less allocations in div! * Fixes in division methods * Cleanup and add test * Cleanup * More cleanup * Remove comments * Update Project.toml: bump minor version and use hyphen instead of commas in IntervalArithmetic compat entry
1 parent f5587a7 commit bc1735c

File tree

6 files changed

+279
-69
lines changed

6 files changed

+279
-69
lines changed

Project.toml

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

66
[deps]
77
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
88
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
99
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
1010
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
1111

12+
[weakdeps]
13+
IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"
14+
15+
[extensions]
16+
TaylorSeriesIAExt = "IntervalArithmetic"
17+
1218
[compat]
1319
Aqua = "0.8"
14-
IntervalArithmetic = "0.15, 0.16, 0.17, 0.18, 0.19, 0.20"
20+
IntervalArithmetic = "0.15 - 0.20"
1521
LinearAlgebra = "<0.0.1, 1"
1622
Markdown = "<0.0.1, 1"
1723
Requires = "0.5.2, 1"
1824
SparseArrays = "<0.0.1, 1"
1925
Test = "<0.0.1, 1"
2026
julia = "1"
2127

22-
[extensions]
23-
TaylorSeriesIAExt = "IntervalArithmetic"
24-
2528
[extras]
2629
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
2730
IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"
@@ -31,6 +34,3 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
3134

3235
[targets]
3336
test = ["IntervalArithmetic", "LinearAlgebra", "SparseArrays", "Test", "Aqua"]
34-
35-
[weakdeps]
36-
IntervalArithmetic = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253"

src/arithmetic.jl

Lines changed: 128 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ end
482482
function mul!(res::Taylor1{TaylorN{T}}, a::Taylor1{TaylorN{T}}, b::Taylor1{TaylorN{T}},
483483
ordT::Int) where {T<:NumberNotSeries}
484484
# Sanity
485-
zero!(res, a, ordT)
485+
zero!(res, ordT)
486486
for k in 0:ordT
487487
@inbounds for ordQ in eachindex(a[ordT])
488488
mul!(res[ordT], a[k], b[ordT-k], ordQ)
@@ -491,6 +491,27 @@ function mul!(res::Taylor1{TaylorN{T}}, a::Taylor1{TaylorN{T}}, b::Taylor1{Taylo
491491
return nothing
492492
end
493493

494+
@inline function mul!(res::Taylor1{TaylorN{T}}, a::NumberNotSeries,
495+
b::Taylor1{TaylorN{T}}, k::Int) where {T<:NumberNotSeries}
496+
for l in eachindex(b[k])
497+
for m in eachindex(b[k][l])
498+
res[k][l][m] = a*b[k][l][m]
499+
end
500+
end
501+
return nothing
502+
end
503+
504+
mul!(res::Taylor1{TaylorN{T}}, a::Taylor1{TaylorN{T}},
505+
b::NumberNotSeries, k::Int) where {T<:NumberNotSeries} = mul!(res, b, a, k)
506+
507+
# in-place division (assumes equal order among TaylorNs)
508+
function mul!(c::TaylorN, a::TaylorN, b::TaylorN)
509+
for k in eachindex(c)
510+
mul!(c, a, b, k)
511+
end
512+
end
513+
514+
494515

495516
@doc doc"""
496517
mul!(c, a, b, k::Int) --> nothing
@@ -665,6 +686,27 @@ function /(a::Taylor1{TaylorN{T}}, b::Taylor1{TaylorN{T}}) where {T<:NumberNotSe
665686
return res
666687
end
667688

689+
function /(a::S, b::Taylor1{TaylorN{T}}) where {S<:NumberNotSeries, T<:NumberNotSeries}
690+
R = promote_type(TaylorN{S}, TaylorN{T})
691+
res = convert(Taylor1{R}, zero(b))
692+
iszero(a) && !iszero(b) && return res
693+
694+
for ordT in eachindex(res)
695+
div!(res, a, b, ordT)
696+
end
697+
return res
698+
end
699+
700+
function /(a::TaylorN{T}, b::Taylor1{TaylorN{T}}) where {T<:NumberNotSeries}
701+
res = zero(b)
702+
iszero(a) && !iszero(b) && return res
703+
704+
aa = Taylor1(a, b.order)
705+
for ordT in eachindex(res)
706+
div!(res, aa, b, ordT)
707+
end
708+
return res
709+
end
668710

669711
@inline function divfactorization(a1::Taylor1, b1::Taylor1)
670712
# order of first factorized term; a1 and b1 assumed to be of the same order
@@ -731,23 +773,94 @@ end
731773
return nothing
732774
end
733775

734-
div!(v::Taylor1, b::NumberNotSeries, a::Taylor1, k::Int) =
735-
div!(v::Taylor1, Taylor1(b, a.order), a, k)
776+
@inline function div!(c::Taylor1{T}, a::NumberNotSeries,
777+
b::Taylor1{T}, k::Int) where {T<:Number}
778+
iszero(a) && !iszero(b) && zero!(c, k)
779+
# order and coefficient of first factorized term
780+
# In this case, since a[k]=0 for k>0, we can simplify to:
781+
# ordfact, cdivfact = 0, a/b[0]
782+
if k == 0
783+
@inbounds c[0] = a/b[0]
784+
return nothing
785+
end
786+
787+
@inbounds c[k] = c[0] * b[k]
788+
@inbounds for i = 1:k-1
789+
c[k] += c[i] * b[k-i]
790+
end
791+
@inbounds c[k] = -c[k]/b[0]
792+
return nothing
793+
end
794+
795+
# TODO: get rid of remaining allocations here.
796+
# This can either be achieved via pre-allocating auxiliary variables
797+
# with can be passed here as arguments, or adding allocation-free in-place
798+
# methods for operations such as `a += a*b` and `a = -a/b`, where a and b are
799+
# TaylorN variables. See #347 for further discussion.
800+
@inline function div!(c::Taylor1{TaylorN{T}}, a::NumberNotSeries,
801+
b::Taylor1{TaylorN{T}}, k::Int) where {T<:NumberNotSeries}
802+
iszero(a) && !iszero(b) && zero!(c, k)
803+
# order and coefficient of first factorized term
804+
# In this case, since a[k]=0 for k>0, we can simplify to:
805+
# ordfact, cdivfact = 0, a/b[0]
806+
if k == 0
807+
@inbounds div!(c[0], a, b[0])
808+
return nothing
809+
end
810+
811+
@inbounds mul!(c[k], c[0], b[k])
812+
@inbounds for i = 1:k-1
813+
c[k] += c[i] * b[k-i]
814+
end
815+
@inbounds c[k] = -c[k]/b[0]
816+
return nothing
817+
end
736818

737819
# NOTE: Here `div!` *accumulates* the result of a / b in c[k] (k > 0)
738820
@inline function div!(c::TaylorN, a::TaylorN, b::TaylorN, k::Int)
739821
if k==0
740-
@inbounds c[0] = a[0] / constant_term(b)
822+
@inbounds c[0][1] = constant_term(a) / constant_term(b)
741823
return nothing
742824
end
743825

744826
@inbounds for i = 0:k-1
745827
mul!(c[k], c[i], b[k-i])
746828
end
747-
@inbounds c[k] = (a[k] - c[k]) / constant_term(b)
829+
@inbounds for i in eachindex(c[k])
830+
c[k][i] = (a[k][i] - c[k][i]) / constant_term(b)
831+
end
748832
return nothing
749833
end
750834

835+
# NOTE: Here `div!` *accumulates* the result of a / b in c[k] (k > 0)
836+
@inline function div!(c::TaylorN, a::NumberNotSeries, b::TaylorN, k::Int)
837+
if k==0
838+
@inbounds c[0][1] = a / constant_term(b)
839+
return nothing
840+
end
841+
842+
@inbounds for i = 0:k-1
843+
mul!(c[k], c[i], b[k-i])
844+
end
845+
@inbounds for i in eachindex(c[k])
846+
c[k][i] = ( -c[k][i] ) / constant_term(b)
847+
end
848+
return nothing
849+
end
850+
851+
# in-place division (assumes equal order among TaylorNs)
852+
function div!(c::TaylorN, a::TaylorN, b::TaylorN)
853+
for k in eachindex(c)
854+
div!(c, a, b, k)
855+
end
856+
end
857+
858+
function div!(c::TaylorN, a::NumberNotSeries, b::TaylorN)
859+
for k in eachindex(c)
860+
div!(c, a, b, k)
861+
end
862+
end
863+
751864
# NOTE: Here `div!` *accumulates* the result of a / b in res[k] (k > 0)
752865
@inline function div!(res::Taylor1{TaylorN{T}}, a::Taylor1{TaylorN{T}},
753866
b::Taylor1{TaylorN{T}}, ordT::Int) where {T<:NumberNotSeriesN}
@@ -758,7 +871,7 @@ end
758871
@inbounds res[0] = cdivfact
759872
return nothing
760873
end
761-
zero!(res, a, ordT)
874+
zero!(res, ordT)
762875
imin = max(0, ordT+ordfact-b.order)
763876
aux = TaylorN(zero(a[ordT][0][1]), a[ordT].order)
764877
for k in imin:ordT-1
@@ -783,6 +896,15 @@ end
783896
return nothing
784897
end
785898

899+
@inline function div!(res::Taylor1{TaylorN{T}}, a::Taylor1{TaylorN{T}},
900+
b::NumberNotSeries, k::Int) where {T<:NumberNotSeries}
901+
for l in eachindex(a[k])
902+
for m in eachindex(a[k][l])
903+
res[k][l][m] = a[k][l][m]/b
904+
end
905+
end
906+
return nothing
907+
end
786908

787909

788910

0 commit comments

Comments
 (0)