Skip to content

Commit defba13

Browse files
authored
Inplace polynomial transforms (#142)
* inplace polynomial transforms * fix for Ultraspherical * version bump to v0.6.5 [skip ci]
1 parent 51b9aa0 commit defba13

File tree

10 files changed

+114
-66
lines changed

10 files changed

+114
-66
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "ApproxFunBase"
22
uuid = "fbd15aa5-315a-5a7d-a8a4-24992e37be05"
3-
version = "0.6.4"
3+
version = "0.6.5"
44

55
[deps]
66
AbstractFFTs = "621f4979-c628-5d54-868e-fcf4e3e8185c"

src/Caching/banded.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ end
150150

151151
## back substitution
152152
# loop to avoid ambiguity with AbstractTRiangular
153-
for ArrTyp in (:AbstractVector, :AbstractMatrix)
153+
for ArrTyp in (:AbstractVector, :AbstractMatrix, :StridedVector)
154154
@eval function ldiv!(U::UpperTriangular{T, <:SubArray{T, 2, <:BandedMatrix{T}, Tuple{UnitRange{Int}, UnitRange{Int}}, false}},
155155
u::$ArrTyp{T}) where T
156156
n = size(u,1)

src/Caching/matrix.jl

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,8 @@ end
4040

4141
# Apply Householder
4242

43-
4443
function mulpars(Ac::Adjoint{T,<:QROperatorQ{QROperator{RR,Matrix{T},T},T}},
45-
B::AbstractVector{T},tolerance,maxlength) where {RR,T}
44+
B::AbstractVector{T},tolerance,maxlength, inplace::Val = Val(false)) where {RR,T}
4645
A = parent(Ac)
4746
if length(B) > A.QR.ncols
4847
# upper triangularize extra columns to prepare for \
@@ -52,7 +51,7 @@ function mulpars(Ac::Adjoint{T,<:QROperatorQ{QROperator{RR,Matrix{T},T},T}},
5251
H=A.QR.H
5352
M=size(H,1)
5453
m=length(B)
55-
Y=pad(B,m+M+10)
54+
Y=_pad!!(inplace)(B,m+M+10)
5655

5756
k=1
5857
yp=view(Y,1:M)
@@ -86,7 +85,7 @@ end
8685

8786
function mulpars(Ac::Adjoint{T,<:QROperatorQ{QROperator{RR,Matrix{T},T},T}},
8887
B::AbstractVector{T},
89-
tolerance,maxlength) where {RR,T<:BlasFloat}
88+
tolerance,maxlength, inplace::Val = Val(false)) where {RR,T<:BlasFloat}
9089

9190
A = parent(Ac)
9291

@@ -99,7 +98,7 @@ function mulpars(Ac::Adjoint{T,<:QROperatorQ{QROperator{RR,Matrix{T},T},T}},
9998

10099
if size(A.QR.H,1) == 1 # diagonal scaling, avoid growing
101100
diagv=view(A.QR.H,1,1:length(B))
102-
ret=similar(B)
101+
ret = inplace isa Val{true} ? B : similar(B)
103102
@simd for k=1:length(ret)
104103
@inbounds ret[k]=(1-2A.QR.H[1,k]^2)*B[k]
105104
end
@@ -114,7 +113,7 @@ function mulpars(Ac::Adjoint{T,<:QROperatorQ{QROperator{RR,Matrix{T},T},T}},
114113
sz=sizeof(T)
115114

116115
m=length(B)
117-
Y=pad(B,m+M+10)
116+
Y=_pad!!(inplace)(B,m+M+10)
118117
y=pointer(Y)
119118

120119
k=1

src/LinearAlgebra/helper.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ function pad(v::AbstractVector{T}, ::PosInfinity) where T
301301
end
302302
end
303303

304+
_pad!!(::Val{false}) = pad
305+
_pad!!(::Val{true}) = pad!
304306

305307
#TODO:padleft!
306308

src/Operators/SubOperator.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,3 +351,11 @@ function mul_coefficients(A::SubOperator{T,B,Tuple{UnitRange{Int},UnitRange{Int}
351351
view(A,:,1:length(b))*b
352352
end
353353
end
354+
function mul_coefficients!(A::SubOperator{T,B,Tuple{UnitRange{Int},UnitRange{Int}}},b) where {T,B}
355+
if size(A,2) == length(b)
356+
mul!(b, AbstractMatrix(A), b)
357+
else
358+
mul!(b, view(A,:,1:length(b)), b)
359+
end
360+
return b
361+
end

src/Operators/general/algebra.jl

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -535,18 +535,22 @@ end
535535

536536

537537
## Operations
538-
function mul_coefficients(A::Operator,b)
539-
n=size(b,1)
540-
ret = n>0 ? mul_coefficients(view(A,FiniteRange,1:n),b) : b
541-
end
538+
for mulcoeff in [:mul_coefficients, :mul_coefficients!]
539+
@eval begin
540+
function $mulcoeff(A::Operator,b)
541+
n=size(b,1)
542+
ret = n>0 ? $mulcoeff(view(A,FiniteRange,1:n),b) : b
543+
end
542544

543-
function mul_coefficients(A::TimesOperator,b)
544-
ret = b
545-
for k=length(A.ops):-1:1
546-
ret = mul_coefficients(A.ops[k],ret)
547-
end
545+
function $mulcoeff(A::TimesOperator,b)
546+
ret = b
547+
for k=length(A.ops):-1:1
548+
ret = $mulcoeff(A.ops[k],ret)
549+
end
548550

549-
ret
551+
ret
552+
end
553+
end
550554
end
551555

552556

src/Operators/ldiv.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ end
4343
\(A::Operator,B::MatrixFun;kwds...) = \(A,Array(B);kwds...)
4444

4545
ldiv_coefficients(A::Operator,b;kwds...) = ldiv_coefficients(qr(A),b;kwds...)
46+
ldiv_coefficients!(A::Operator,b;kwds...) = ldiv_coefficients!(qr(A),b;kwds...)
4647

4748
\(A::Operator,B::Operator) = TimesOperator(inv(A),B)
4849

src/Operators/qr.jl

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
2-
3-
4-
51
mutable struct QROperator{CO,MT,T} <: Operator{T}
62
R_cache::CO
73
H::MT # Contains the Householder reflections
@@ -147,9 +143,17 @@ det(A::Operator) = det(qr(A))
147143

148144
mul_coefficients(At::Transpose{T,<:QROperatorQ{T}},B::AbstractVector{T}) where {T<:Real} = parent(At)'*B
149145
mul_coefficients(At::Transpose{T,<:QROperatorQ{T}},B::AbstractMatrix{T}) where {T<:Real} = parent(At)'*B
146+
mul_coefficients!(At::Transpose{T,<:QROperatorQ{T}},B::AbstractVector{T}) where {T<:Real} = mul!(B, parent(At)', B)
147+
mul_coefficients!(At::Transpose{T,<:QROperatorQ{T}},B::AbstractMatrix{T}) where {T<:Real} = mul!(B, parent(At)', B)
150148

151-
mul_coefficients(Ac::Adjoint{T,<:QROperatorQ{QR,T}},B::AbstractVector{T};tolerance=eps(eltype(Ac))/10,maxlength=1000000) where {QR,T} =
152-
mulpars(Ac,B,tolerance,maxlength)
149+
function mul_coefficients(Ac::Adjoint{T,<:QROperatorQ{QR,T}},B::AbstractVector{T};
150+
tolerance=eps(eltype(Ac))/10,maxlength=1000000) where {QR,T}
151+
mulpars(Ac,B,tolerance,maxlength)
152+
end
153+
function mul_coefficients!(Ac::Adjoint{T,<:QROperatorQ{QR,T}},B::AbstractVector{T};
154+
tolerance=eps(eltype(Ac))/10,maxlength=1000000) where {QR,T}
155+
mulpars(Ac,B,tolerance,maxlength,Val(true))
156+
end
153157

154158
mul_coefficients(Ac::Adjoint{T,<:QROperatorQ{QR,T}},B::AbstractVector{V};opts...) where {QR,T,V} =
155159
mul_coefficients(Ac,AbstractVector{T}(B); opts...)
@@ -167,28 +171,39 @@ end
167171

168172

169173
ldiv_coefficients(A::QROperatorQ, B; opts...) = mul_coefficients(A', B; opts...)
174+
ldiv_coefficients!(A::QROperatorQ, B; opts...) = mul_coefficients!(A', B; opts...)
170175
\(A::QROperatorQ, B::Fun; opts...) = *(A', B; opts...)
171176

172177

173178
# R
174-
function ldiv_coefficients(R::QROperatorR, b::AbstractVector)
175-
if length(b) > R.QR.ncols
179+
function uppertriangularview!(R, lenb)
180+
if lenb > R.QR.ncols
176181
# upper triangularize columns
177-
resizedata!(R.QR, :, length(b))
182+
resizedata!(R.QR, :, lenb)
178183
end
179-
UpperTriangular(view(R.QR.R_cache.data, 1:length(b), 1:length(b))) \ b
184+
return UpperTriangular(view(R.QR.R_cache.data, 1:lenb, 1:lenb))
185+
end
186+
function ldiv_coefficients(R::QROperatorR, b::AbstractVector)
187+
U = uppertriangularview!(R, length(b))
188+
U \ b
189+
end
190+
function ldiv_coefficients!(R::QROperatorR, b::AbstractVector)
191+
U = uppertriangularview!(R, length(b))
192+
ldiv!(U, b)
180193
end
181194

182195
\(R::QROperatorR,b::Fun{SequenceSpace};kwds...) =
183196
Fun(domainspace(R),ldiv_coefficients(R,b.coefficients;kwds...))
184-
\(A::QROperatorR,b::Fun;kwds...) = error("\\ not implement for $(typeof(b)) right-hand sides")
197+
\(A::QROperatorR,b::Fun;kwds...) = error(\, " not implement for ", typeof(b), " right-hand sides")
185198

186199

187200
# QR
188201

189202
for TYP in (:Real,:Complex,:Number)
190203
@eval ldiv_coefficients(QR::QROperator{CO,MT,T},b::AbstractVector{T}; kwds...) where {CO,MT,T<:$TYP} =
191204
ldiv_coefficients(QR.R, mul_coefficients(QR.Q',b;kwds...))
205+
@eval ldiv_coefficients!(QR::QROperator{CO,MT,T},b::AbstractVector{T}; kwds...) where {CO,MT,T<:$TYP} =
206+
ldiv_coefficients!(QR.R, mul_coefficients!(QR.Q',b;kwds...))
192207
end
193208

194209

src/Space.jl

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -303,18 +303,22 @@ coefficients(f::AbstractVector,sp1::Space,::Type{T2}) where {T2<:Space} = coeffi
303303

304304
## coefficients defaults to calling Conversion, otherwise it tries to pipe through Chebyshev
305305

306+
_mul_coefficients!!(inplace::Val{true}) = mul_coefficients!
307+
_mul_coefficients!!(inplace::Val{false}) = mul_coefficients
308+
_ldiv_coefficients!!(inplace::Val{true}) = ldiv_coefficients!
309+
_ldiv_coefficients!!(inplace::Val{false}) = ldiv_coefficients
306310

307311
_Fun(v::AbstractVector, sp) = Fun(sp, v)
308312
_Fun(v, sp) = Fun(v, sp)
309-
function defaultcoefficients(f,a,b)
313+
function defaultcoefficients(f,a,b,inplace = Val(false))
310314
ct=conversion_type(a,b) # gives a space that has a banded conversion to both a and b
311315

312316
if spacescompatible(a,b)
313317
f
314318
elseif hasconversion(a,b)
315-
mul_coefficients(Conversion(a,b),f)
319+
_mul_coefficients!!(inplace)(Conversion(a,b),f)
316320
elseif hasconversion(b,a)
317-
ldiv_coefficients(Conversion(b,a),f)
321+
_ldiv_coefficients!!(inplace)(Conversion(b,a),f)
318322
else
319323
csp=canonicalspace(a)
320324

@@ -323,14 +327,15 @@ function defaultcoefficients(f,a,b)
323327
end
324328
if spacescompatible(a,csp) || spacescompatible(b,csp)
325329
# b is csp too, so we are stuck, try Fun constructor
326-
coefficients(default_Fun(_Fun(f,a),b))
330+
_coefficients!!(inplace)(default_Fun(_Fun(f,a),b))
327331
else
328-
coefficients(f,a,csp,b)
332+
_coefficients!!(inplace)(f,a,csp,b)
329333
end
330334
end
331335
end
332336

333337
coefficients(f,a,b) = defaultcoefficients(f,a,b)
338+
coefficients!(f,a,b) = defaultcoefficients(f,a,b,Val(true))
334339

335340

336341

@@ -368,50 +373,43 @@ end
368373

369374
for Typ in (:CanonicalTransformPlan,:ICanonicalTransformPlan)
370375
@eval begin
371-
struct $Typ{T,SP,PL,CSP} <: AbstractTransformPlan{T}
376+
struct $Typ{T,SP,PL,CSP,inplace} <: AbstractTransformPlan{T}
372377
space::SP
373378
plan::PL
374379
canonicalspace::CSP
375380
end
376-
$Typ(space,plan,csp) =
377-
$Typ{eltype(plan),typeof(space),typeof(plan),typeof(csp)}(space,plan,csp)
378-
$Typ(::Type{T},space,plan,csp) where {T} =
379-
$Typ{T,typeof(space),typeof(plan),typeof(csp)}(space,plan,csp)
381+
$Typ(space,plan,csp) = $Typ(space,plan,csp,Val(false))
382+
$Typ(space,plan,csp,ip::Val{inplace}) where {inplace} =
383+
$Typ{eltype(plan),typeof(space),typeof(plan),typeof(csp),inplace}(space,plan,csp)
380384
end
381385
end
382-
386+
inplace(::CanonicalTransformPlan{<:Any,<:Any,<:Any,<:Any,IP}) where {IP} = IP
387+
inplace(::ICanonicalTransformPlan{<:Any,<:Any,<:Any,<:Any,IP}) where {IP} = IP
383388

384389
# Canonical plan uses coefficients
385-
function CanonicalTransformPlan(space,v)
386-
csp = canonicalspace(space)
387-
CanonicalTransformPlan(eltype(v),space,plan_transform(csp,v),csp)
388-
end
389390
function checkcanonicalspace(sp)
390391
csp = canonicalspace(sp)
391392
sp == csp && error("Override for $sp")
392393
csp
393394
end
394-
function plan_transform(sp::Space,vals)
395-
csp = checkcanonicalspace(sp)
396-
CanonicalTransformPlan(sp,plan_transform(csp,vals),csp)
395+
_plan_transform!!(::Val{true}) = plan_transform!
396+
_plan_transform!!(::Val{false}) = plan_transform
397+
function CanonicalTransformPlan(space, v, inplace::Val = Val(false))
398+
csp = checkcanonicalspace(space)
399+
CanonicalTransformPlan(space, _plan_transform!!(inplace)(csp,v), csp, inplace)
397400
end
398-
399-
function ICanonicalTransformPlan(space,v)
400-
csp = canonicalspace(space)
401-
cfs = coefficients(v,space,csp)
402-
ICanonicalTransformPlan(eltype(v),space,plan_itransform(csp,cfs),csp)
401+
plan_transform(sp::Space,vals) = CanonicalTransformPlan(sp, vals, Val(false))
402+
plan_transform!(sp::Space,vals) = CanonicalTransformPlan(sp, vals, Val(true))
403+
404+
_plan_itransform!!(::Val{true}) = plan_itransform!
405+
_plan_itransform!!(::Val{false}) = plan_itransform
406+
function ICanonicalTransformPlan(space, v, ip::Val{inplace} = Val(false)) where {inplace}
407+
csp = checkcanonicalspace(space)
408+
cfs = inplace ? coefficients(v,space,csp) : v
409+
ICanonicalTransformPlan(space, _plan_itransform!!(ip)(csp,cfs), csp, ip)
403410
end
404-
function plan_itransform(sp::Space,v)
405-
csp = checkcanonicalspace(sp)
406-
cfs = coefficients(v,sp,csp)
407-
ICanonicalTransformPlan(sp,plan_itransform(csp,cfs),csp)
408-
end
409-
410-
411-
plan_transform!(sp::Space,vals) = error("Override for $sp")
412-
plan_itransform!(sp::Space,cfs) = error("Override for $sp")
413-
414-
411+
plan_itransform(sp::Space,v) = ICanonicalTransformPlan(sp, v, Val(false))
412+
plan_itransform!(sp::Space,v) = ICanonicalTransformPlan(sp, v, Val(true))
415413

416414
# transform converts from values at points(S,n) to coefficients
417415
# itransform converts from coefficients to values at points(S,n)
@@ -423,9 +421,11 @@ itransform!(S::Space,cfs) = plan_itransform!(S,cfs)*cfs
423421
transform!(S::Space,cfs) = plan_transform!(S,cfs)*cfs
424422

425423

426-
*(P::CanonicalTransformPlan,vals::AbstractVector) = coefficients(P.plan*vals,P.canonicalspace,P.space)
427-
*(P::ICanonicalTransformPlan,cfs::AbstractVector) = P.plan*coefficients(cfs,P.space,P.canonicalspace)
428-
424+
_coefficients!!(::Val{true}) = coefficients!
425+
_coefficients!!(::Val{false}) = coefficients
426+
_mul(P::CanonicalTransformPlan, ip, vals) = _coefficients!!(ip)(P.plan * vals, P.canonicalspace, P.space)
427+
_mul(P::ICanonicalTransformPlan, ip, cfs) = P.plan * _coefficients!!(ip)(cfs, P.space, P.canonicalspace)
428+
*(P::Union{CanonicalTransformPlan, ICanonicalTransformPlan}, vals::AbstractVector) = _mul(P, Val(inplace(P)), vals)
429429

430430

431431
for OP in (:plan_transform,:plan_itransform,:plan_transform!,:plan_itransform!)

test/SpacesTest.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,5 +177,24 @@ using ApproxFunOrthogonalPolynomials
177177
f = Fun(x->x^2, Chebyshev())
178178
v = coefficients(f, Chebyshev(), Legendre())
179179
@test v coefficients(Fun(x->x^2, Legendre()))
180+
181+
@testset "inplace transform" begin
182+
@testset for sp_c in Any[Legendre(), Chebyshev(), Jacobi(1,2), Jacobi(0.3, 2.3),
183+
Ultraspherical(1), Ultraspherical(2)]
184+
@testset for sp in Any[sp_c, NormalizedPolynomialSpace(sp_c)]
185+
v = rand(10)
186+
v2 = copy(v)
187+
@test itransform!(sp, transform!(sp, v)) v
188+
@test transform!(sp, v) transform(sp, v2)
189+
@test itransform(sp, v) v2
190+
@test itransform!(sp, v) v2
191+
192+
# different vector
193+
p_fwd = ApproxFunBase.plan_transform!(sp, v)
194+
p_inv = ApproxFunBase.plan_itransform!(sp, v)
195+
@test p_inv * copy(p_fwd * copy(v)) v
196+
end
197+
end
198+
end
180199
end
181200
end

0 commit comments

Comments
 (0)