Skip to content

Commit 8462dd4

Browse files
authored
Switch to TridiagonalConjugation (#232)
* Switch to TridiagonalConjugation * fix tests * Update test_choleskyQR.jl * Update ClassicalOrthogonalPolynomials.jl * resizedata! before getindex * always call resizedata! * tests pass
1 parent 01eecab commit 8462dd4

File tree

5 files changed

+63
-268
lines changed

5 files changed

+63
-268
lines changed

Project.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ClassicalOrthogonalPolynomials"
22
uuid = "b30e2e7b-c4ee-47da-9d5f-2c5c27239acd"
33
authors = ["Sheehan Olver <solver@mac.com>"]
4-
version = "0.15"
4+
version = "0.15.1"
55

66

77
[deps]
@@ -47,9 +47,9 @@ FastTransforms = "0.16.6, 0.17"
4747
FillArrays = "1"
4848
HypergeometricFunctions = "0.3.4"
4949
InfiniteArrays = " 0.15"
50-
InfiniteLinearAlgebra = "0.9"
50+
InfiniteLinearAlgebra = "0.10"
5151
IntervalSets = "0.7"
52-
LazyArrays = "2.2"
52+
LazyArrays = "2.5.1"
5353
LazyBandedMatrices = "0.11"
5454
MutableArithmetics = "1"
5555
QuasiArrays = "0.12"

src/ClassicalOrthogonalPolynomials.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ import QuasiArrays: cardinality, checkindex, QuasiAdjoint, QuasiTranspose, Inclu
2929
QuasiDiagonal, MulQuasiArray, MulQuasiMatrix, MulQuasiVector, QuasiMatMulMat,
3030
ApplyQuasiArray, ApplyQuasiMatrix, LazyQuasiArrayApplyStyle, AbstractQuasiArrayApplyStyle,
3131
LazyQuasiArray, LazyQuasiVector, LazyQuasiMatrix, LazyLayout, LazyQuasiArrayStyle,
32-
_getindex, layout_getindex, _factorize, AbstractQuasiArray, AbstractQuasiMatrix, AbstractQuasiVector,
32+
_getindex, layout_getindex, AbstractQuasiArray, AbstractQuasiMatrix, AbstractQuasiVector,
3333
AbstractQuasiFill, equals_layout, QuasiArrayLayout, PolynomialLayout, diff_layout
3434

3535
import InfiniteArrays: OneToInf, InfAxes, Infinity, AbstractInfUnitRange, InfiniteCardinal, InfRanges
36-
import InfiniteLinearAlgebra: chop!, chop, pad, choplength, compatible_resize!, partialcholesky!
36+
import InfiniteLinearAlgebra: chop!, chop, pad, choplength, compatible_resize!, partialcholesky!, SymTridiagonalConjugation, TridiagonalConjugation
3737
import ContinuumArrays: Basis, Weight, basis_axes, @simplify, AbstractAffineQuasiVector, ProjectionFactorization,
3838
grid, plotgrid, plotgrid_layout, plotvalues_layout, grid_layout, transform_ldiv, TransformFactorization, QInfAxes, broadcastbasis, ExpansionLayout, basismap,
3939
AffineQuasiVector, AffineMap, AbstractWeightLayout, AbstractWeightedBasisLayout, WeightedBasisLayout, WeightedBasisLayouts, demap, AbstractBasisLayout, BasisLayout,

src/choleskyQR.jl

Lines changed: 14 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ arguments(::ApplyLayout{typeof(*)}, Q::ConvertedOrthogonalPolynomial) = Q.P, App
2929
OrthogonalPolynomial(w::AbstractQuasiVector) = OrthogonalPolynomial(w, orthogonalpolynomial(singularities(w)))
3030
function OrthogonalPolynomial(w::AbstractQuasiVector, P::AbstractQuasiMatrix)
3131
Q = normalized(P)
32-
X = cholesky_jacobimatrix(w, Q)
33-
ConvertedOrthogonalPolynomial(w, X, parent(X.dv).U, Q)
32+
X,U = cholesky_jacobimatrix(w, Q)
33+
ConvertedOrthogonalPolynomial(w, X, U, Q)
3434
end
3535

3636
orthogonalpolynomial(wP...) = OrthogonalPolynomial(wP...)
@@ -39,6 +39,8 @@ orthogonalpolynomial(w::SubQuasiArray) = orthogonalpolynomial(parent(w))[parenti
3939
OrthogonalPolynomial(w::Function, P::AbstractQuasiMatrix) = OrthogonalPolynomial(w.(axes(P,1)), P)
4040
orthogonalpolynomial(w::Function, P::AbstractQuasiMatrix) = orthogonalpolynomial(w.(axes(P,1)), P)
4141

42+
resizedata!(P::ConvertedOrthogonalPolynomial, ::Colon, n::Int) = resizedata!(P.X.dv, n)
43+
4244

4345
"""
4446
cholesky_jacobimatrix(w, P)
@@ -50,86 +52,15 @@ The resulting polynomials are orthonormal on the same domain as `P`. The supplie
5052
"""
5153
cholesky_jacobimatrix(w::Function, P) = cholesky_jacobimatrix(w.(axes(P,1)), P)
5254

53-
function cholesky_jacobimatrix(w::AbstractQuasiVector, P)
55+
function cholesky_jacobimatrix(w::AbstractQuasiVector, P::AbstractQuasiMatrix)
5456
Q = normalized(P)
5557
w_P = orthogonalityweight(P)
5658
W = Symmetric(Q \ ((w ./ w_P) .* Q)) # Compute weight multiplication via Clenshaw
57-
return cholesky_jacobimatrix(W, Q)
59+
return cholesky_jacobimatrix(W, jacobimatrix(Q))
5860
end
59-
function cholesky_jacobimatrix(W::AbstractMatrix, Q)
60-
isnormalized(Q) || error("Polynomials must be orthonormal")
61+
function cholesky_jacobimatrix(W::AbstractMatrix, X::AbstractMatrix)
6162
U = cholesky(W).U
62-
CJD = CholeskyJacobiData(U,Q)
63-
return SymTridiagonal(view(CJD,:,1),view(CJD,:,2))
64-
end
65-
66-
# The generated Jacobi operators are symmetric tridiagonal, so we store their data in two cached bands which are generated in tandem but can be accessed separately.
67-
mutable struct CholeskyJacobiData{T} <: LazyMatrix{T}
68-
dv::AbstractVector{T} # store diagonal band entries in adaptively sized vector
69-
ev::AbstractVector{T} # store off-diagonal band entries in adaptively sized vector
70-
U::UpperTriangular{T} # store upper triangular conversion matrix (needed to extend available entries)
71-
X::AbstractMatrix{T} # store Jacobi matrix of original polynomials
72-
P # store original polynomials
73-
datasize::Int # size of so-far computed block
74-
end
75-
76-
# Computes the initial data for the Jacobi operator bands
77-
function CholeskyJacobiData(U::AbstractMatrix{T}, P) where T
78-
# compute a length 2 vector on first go and circumvent BigFloat issue
79-
dv = zeros(T,2)
80-
ev = zeros(T,2)
81-
X = jacobimatrix(P)
82-
partialcholesky!(U.data, 3)
83-
UX = view(U,1:3,1:3)*X[1:3,1:3]
84-
dv[1] = UX[1,1]/U[1,1] # this is dot(view(UX,1,1), U[1,1] \ [one(T)])
85-
dv[2] = -U[1,2]*UX[2,1]/(U[1,1]*U[2,2])+UX[2,2]/U[2,2] # this is dot(view(UX,2,1:2), U[1:2,1:2] \ [zero(T); one(T)])
86-
ev[1] = -UX[1,1]*U[1,2]/(U[1,1]*U[2,2])+UX[1,2]/U[2,2] # this is dot(view(UX,1,1:2), U[1:2,1:2] \ [zero(T); one(T)])
87-
ev[2] = UX[2,1]/U[3,3]*(-U[1,3]/U[1,1]+U[1,2]*U[2,3]/(U[1,1]*U[2,2]))+UX[2,2]/U[3,3]*(-U[2,3]/U[2,2])+UX[2,3]/U[3,3] # this is dot(view(UX,2,1:3), U[1:3,1:3] \ [zeros(T,2); one(T)])[1:3,1:3] \ [zeros(T,2); one(T)])
88-
return CholeskyJacobiData{T}(dv, ev, U, X, P, 2)
89-
end
90-
91-
size(::CholeskyJacobiData) = (ℵ₀,2) # Stored as two infinite cached bands
92-
93-
function getindex(C::SymTridiagonal{<:Any, <:SubArray{<:Any, 1, <:CholeskyJacobiData, <:Tuple, false}, <:SubArray{<:Any, 1, <:CholeskyJacobiData, <:Tuple, false}}, kr::UnitRange, jr::UnitRange)
94-
m = 1+max(kr.stop,jr.stop)
95-
resizedata!(C.dv.parent,m,1)
96-
resizedata!(C.ev.parent,m,2)
97-
return copy(view(C,kr,jr))
98-
end
99-
100-
function getindex(K::CholeskyJacobiData, n::Integer, m::Integer)
101-
@assert (m==1) || (m==2)
102-
resizedata!(K,n,m)
103-
m == 1 && return K.dv[n]
104-
m == 2 && return K.ev[n]
105-
end
106-
107-
# Resize and filling functions for cached implementation
108-
function resizedata!(K::CholeskyJacobiData, n::Integer, m::Integer)
109-
nm = 1+max(n,m)
110-
νμ = K.datasize
111-
if nm > νμ
112-
resize!(K.dv,nm)
113-
resize!(K.ev,nm)
114-
_fillcholeskybanddata!(K, νμ:nm)
115-
K.datasize = nm
116-
end
117-
K
118-
end
119-
120-
function _fillcholeskybanddata!(J::CholeskyJacobiData{T}, inds::UnitRange{Int}) where T
121-
# pre-fill U to prevent expensive step-by-step filling in
122-
m = inds[end]+1
123-
partialcholesky!(J.U.data, m)
124-
dv, ev, U, X = J.dv, J.ev, J.U, J.X
125-
126-
UX = view(U,1:m,1:m)*X[1:m,1:m]
127-
128-
@inbounds for k = inds
129-
# this is dot(view(UX,k,k-1:k), U[k-1:k,k-1:k] \ ek)
130-
dv[k] = -U[k-1,k]*UX[k,k-1]/(U[k-1,k-1]*U[k,k])+UX[k,k]/U[k,k]
131-
ev[k] = UX[k,k-1]/U[k+1,k+1]*(-U[k-1,k+1]/U[k-1,k-1]+U[k-1,k]*U[k,k+1]/(U[k-1,k-1]*U[k,k]))+UX[k,k]/U[k+1,k+1]*(-U[k,k+1]/U[k,k])+UX[k,k+1]/U[k+1,k+1]
132-
end
63+
return SymTridiagonalConjugation(U, X), U
13364
end
13465

13566

@@ -143,141 +74,14 @@ The resulting polynomials are orthonormal on the same domain as `P`. The supplie
14374
14475
The underlying QR approach allows two methods, one which uses the Q matrix and one which uses the R matrix. To change between methods, an optional argument :Q or :R may be supplied. The default is to use the Q method.
14576
"""
146-
qr_jacobimatrix(w::Function, P, method = :Q) = qr_jacobimatrix(w.(axes(P,1)), P, Symbol(method))
147-
function qr_jacobimatrix(w::AbstractQuasiVector, P, method = :Q)
77+
qr_jacobimatrix(w::Function, P) = qr_jacobimatrix(w.(axes(P,1)), P)
78+
function qr_jacobimatrix(w::AbstractQuasiVector, P)
14879
Q = normalized(P)
14980
w_P = orthogonalityweight(P)
15081
sqrtW = Symmetric(Q \ (sqrt.(w ./ w_P) .* Q)) # Compute weight multiplication via Clenshaw
151-
return qr_jacobimatrix(sqrtW, Q, Symbol(method))
152-
end
153-
function qr_jacobimatrix(sqrtW::AbstractMatrix{T}, Q, method = :Q) where T
154-
isnormalized(Q) || error("Polynomials must be orthonormal")
155-
F = qr(sqrtW)
156-
QRJD = QRJacobiData{method,T}(F,Q)
157-
SymTridiagonal(view(QRJD,:,1),view(QRJD,:,2))
82+
return qr_jacobimatrix(sqrtW, jacobimatrix(Q))
15883
end
159-
160-
# The generated Jacobi operators are symmetric tridiagonal, so we store their data in two cached bands which are generated in tandem but can be accessed separately.
161-
mutable struct QRJacobiData{method,T} <: LazyMatrix{T}
162-
dv::AbstractVector{T} # store diagonal band entries in adaptively sized vector
163-
ev::AbstractVector{T} # store off-diagonal band entries in adaptively sized vector
164-
U # store conversion, Q method: stores QR object. R method: only stores R.
165-
UX # Auxilliary matrix. Q method: stores in-progress incomplete modification. R method: stores Jacobi matrix of original polynomials
166-
P # Remember original polynomials
167-
datasize::Int # size of so-far computed block
168-
end
169-
170-
# Computes the initial data for the Jacobi operator bands
171-
function QRJacobiData{:Q,T}(F, P) where T
172-
b = 3+bandwidths(F.R)[2]÷2
173-
X = jacobimatrix(P)
174-
# we fill 1 entry on the first run and circumvent BigFloat issue
175-
dv = zeros(T,2)
176-
ev = zeros(T,1)
177-
# fill first entry (special case)
178-
M = Matrix(X[1:b,1:b])
179-
resizedata!(F.factors,b,b)
180-
# special case for first entry double Householder product
181-
v = view(F.factors,1:b,1)
182-
reflectorApply!(v, F.τ[1], M)
183-
reflectorApply!(v, F.τ[1], M')
184-
dv[1] = M[1,1]
185-
# fill second entry
186-
# computes H*M*H in-place, overwriting M
187-
v = view(F.factors,2:b,2)
188-
reflectorApply!(v, F.τ[2], view(M,1,2:b))
189-
M[1,2:b] .= view(M,1,2:b) # symmetric matrix, avoid recomputation
190-
reflectorApply!(v, F.τ[2], view(M,2:b,2:b))
191-
reflectorApply!(v, F.τ[2], view(M,2:b,2:b)')
192-
ev[1] = M[1,2]*sign(F.R[1,1]*F.R[2,2]) # includes possible correction for sign (only needed in off-diagonal case), since the QR decomposition does not guarantee positive diagonal on R
193-
K = Matrix(X[2:b+1,2:b+1])
194-
K[1:end-1,1:end-1] .= view(M,2:b,2:b)
195-
return QRJacobiData{:Q,T}(dv, ev, F, K, P, 1)
196-
end
197-
function QRJacobiData{:R,T}(F, P) where T
198-
U = F.R
199-
U = ApplyArray(*,Diagonal(sign.(view(U,band(0)))),U) # QR decomposition does not force positive diagonals on R by default
200-
X = jacobimatrix(P)
201-
UX = view(U,1:3,1:3)*X[1:3,1:3]
202-
# compute a length 2 vector on first go and circumvent BigFloat issue
203-
dv = zeros(T,2)
204-
ev = zeros(T,2)
205-
dv[1] = UX[1,1]/U[1,1] # this is dot(view(UX,1,1), U[1,1] \ [one(T)])
206-
dv[2] = -U[1,2]*UX[2,1]/(U[1,1]*U[2,2])+UX[2,2]/U[2,2] # this is dot(view(UX,2,1:2), U[1:2,1:2] \ [zero(T); one(T)])
207-
ev[1] = -UX[1,1]*U[1,2]/(U[1,1]*U[2,2])+UX[1,2]/U[2,2] # this is dot(view(UX,1,1:2), U[1:2,1:2] \ [zero(T); one(T)])
208-
ev[2] = UX[2,1]/U[3,3]*(-U[1,3]/U[1,1]+U[1,2]*U[2,3]/(U[1,1]*U[2,2]))+UX[2,2]/U[3,3]*(-U[2,3]/U[2,2])+UX[2,3]/U[3,3] # this is dot(view(UX,2,1:3), U[1:3,1:3] \ [zeros(T,2); one(T)])
209-
return QRJacobiData{:R,T}(dv, ev, U, X, P, 2)
210-
end
211-
212-
213-
size(::QRJacobiData) = (ℵ₀,2) # Stored as two infinite cached bands
214-
215-
function getindex(K::QRJacobiData, n::Integer, m::Integer)
216-
@assert (m==1) || (m==2)
217-
resizedata!(K,n,m)
218-
m == 1 && return K.dv[n]
219-
m == 2 && return K.ev[n]
220-
end
221-
222-
function getindex(C::SymTridiagonal{<:Any, <:SubArray{<:Any, 1, <:QRJacobiData, <:Tuple, false}, <:SubArray{<:Any, 1, <:QRJacobiData, <:Tuple, false}}, kr::UnitRange, jr::UnitRange)
223-
m = maximum(max(kr,jr))+1
224-
resizedata!(C.dv.parent,m,2)
225-
resizedata!(C.ev.parent,m,2)
226-
return copy(view(C,kr,jr))
227-
end
228-
229-
# Resize and filling functions for cached implementation
230-
function resizedata!(K::QRJacobiData, n::Integer, m::Integer)
231-
nm = 1+max(n,m)
232-
νμ = K.datasize
233-
if nm > νμ
234-
resize!(K.dv,nm)
235-
resize!(K.ev,nm)
236-
_fillqrbanddata!(K, νμ:nm)
237-
K.datasize = nm
238-
end
239-
K
240-
end
241-
function _fillqrbanddata!(J::QRJacobiData{:Q,T}, inds::UnitRange{Int}) where T
242-
b = bandwidths(J.U.factors)[2]÷2
243-
# pre-fill cached arrays to avoid excessive cost from expansion in loop
244-
m, jj = 1+inds[end], inds[2:end]
245-
X = jacobimatrix(J.P)[1:m+b+2,1:m+b+2]
246-
resizedata!(J.U.factors,m+b,m+b)
247-
resizedata!(J.U.τ,m)
248-
K, τ, F, dv, ev = J.UX, J.U.τ, J.U.factors, J.dv, J.ev
249-
D = sign.(view(J.U.R,band(0)).*view(J.U.R,band(0))[2:end])
250-
M = zeros(T,b+3,b+3)
251-
if isprimitivetype(T)
252-
M = Matrix{T}(undef,b+3,b+3)
253-
else
254-
M = zeros(T,b+3,b+3)
255-
end
256-
@inbounds for n in jj
257-
dv[n] = K[1,1] # no sign correction needed on diagonal entry due to cancellation
258-
# doublehouseholderapply!(K,τ[n+1],view(F,n+2:n+b+2,n+1),w)
259-
v = view(F,n+1:n+b+2,n+1)
260-
reflectorApply!(v, τ[n+1], view(K,1,2:b+3))
261-
M[1,2:b+3] .= view(M,1,2:b+3) # symmetric matrix, avoid recomputation
262-
reflectorApply!(v, τ[n+1], view(K,2:b+3,2:b+3))
263-
reflectorApply!(v, τ[n+1], view(K,2:b+3,2:b+3)')
264-
ev[n] = K[1,2]*D[n] # contains sign correction from QR not forcing positive diagonals
265-
M .= view(X,n+1:n+b+3,n+1:n+b+3)
266-
M[1:end-1,1:end-1] .= view(K,2:b+3,2:b+3)
267-
K .= M
268-
end
269-
end
270-
271-
function _fillqrbanddata!(J::QRJacobiData{:R,T}, inds::UnitRange{Int}) where T
272-
# pre-fill U and UX to prevent expensive step-by-step filling in of cached U and UX in the loop
273-
m = inds[end]+1
274-
resizedata!(J.U,m,m)
275-
dv, ev, X, U = J.dv, J.ev, J.UX, J.U
276-
277-
UX = view(U,1:m,1:m)*X[1:m,1:m]
278-
279-
@inbounds for k in inds
280-
dv[k] = -U[k-1,k]*UX[k,k-1]/(U[k-1,k-1]*U[k,k])+UX[k,k]./U[k,k] # this is dot(view(UX,k,k-1:k), U[k-1:k,k-1:k] \ ek)
281-
ev[k] = UX[k,k-1]/U[k+1,k+1]*(-U[k-1,k+1]/U[k-1,k-1]+U[k-1,k]*U[k,k+1]/(U[k-1,k-1]*U[k,k]))+UX[k,k]/U[k+1,k+1]*(-U[k,k+1]/U[k,k])+UX[k,k+1]/U[k+1,k+1] # this is dot(view(UX,k,k-1:k+1), U[k-1:k+1,k-1:k+1] \ ek)
282-
end
84+
function qr_jacobimatrix(sqrtW::AbstractMatrix{T}, X::AbstractMatrix) where T
85+
R = qr(sqrtW).R
86+
return SymTridiagonalConjugation(R, X), R
28387
end

src/clenshaw.jl

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ end
1616
function copyto!(dest::AbstractVector, V::SubArray{<:Any,1,<:OrthogonalPolynomial,<:Tuple{<:Number,<:OneTo}})
1717
P = parent(V)
1818
x,n = parentindices(V)
19+
resizedata!(P, :, last(n))
1920
A,B,C = recurrencecoefficients(P)
2021
forwardrecurrence!(dest, A, B, C, x, _p0(P))
2122
end
@@ -24,6 +25,7 @@ function forwardrecurrence_copyto!(dest::AbstractMatrix, V)
2425
checkbounds(dest, axes(V)...)
2526
P = parent(V)
2627
xr,jr = parentindices(V)
28+
resizedata!(P, :, maximum(jr; init=0))
2729
A,B,C = recurrencecoefficients(P)
2830
shift = first(jr)
2931
Ã,B̃,C̃ = A[shift:∞],B[shift:∞],C[shift:∞]
@@ -40,6 +42,7 @@ function copyto!(dest::AbstractVector, V::SubArray{<:Any,1,<:OrthogonalPolynomia
4042
checkbounds(dest, axes(V)...)
4143
P = parent(V)
4244
x,jr = parentindices(V)
45+
resizedata!(P, :, last(jr))
4346
A,B,C = recurrencecoefficients(P)
4447
shift = first(jr)
4548
Ã,B̃,C̃ = A[shift:∞],B[shift:∞],C[shift:∞]
@@ -61,11 +64,14 @@ unsafe_layout_getindex(A...) = sub_materialize(Base.unsafe_view(A...))
6164

6265
Base.unsafe_getindex(P::OrthogonalPolynomial, x::Number, n::AbstractUnitRange) = unsafe_layout_getindex(P, x, n)
6366
Base.unsafe_getindex(P::OrthogonalPolynomial, x::AbstractVector, n::AbstractUnitRange) = unsafe_layout_getindex(P, x, n)
64-
Base.unsafe_getindex(P::OrthogonalPolynomial, x::Number, n::AbstractVector) = Base.unsafe_getindex(P,x,oneto(maximum(n)))[n]
65-
Base.unsafe_getindex(P::OrthogonalPolynomial, x::AbstractVector, n::AbstractVector) = Base.unsafe_getindex(P,x,oneto(maximum(n)))[:,n]
67+
Base.unsafe_getindex(P::OrthogonalPolynomial, x::Number, n::AbstractVector) = Base.unsafe_getindex(P,x,oneto(maximum(n; init=0)))[n]
68+
Base.unsafe_getindex(P::OrthogonalPolynomial, x::AbstractVector, n::AbstractVector) = Base.unsafe_getindex(P,x,oneto(maximum(n; init=0)))[:,n]
6669
Base.unsafe_getindex(P::OrthogonalPolynomial, x::AbstractVector, n::Number) = Base.unsafe_getindex(P, x, 1:n)[:,end]
6770
Base.unsafe_getindex(P::OrthogonalPolynomial, x::Number, ::Colon) = Base.unsafe_getindex(P, x, axes(P,2))
68-
Base.unsafe_getindex(P::OrthogonalPolynomial, x::Number, n::Number) = initiateforwardrecurrence(n-1, recurrencecoefficients(P)..., x, _p0(P))[end]
71+
function Base.unsafe_getindex(P::OrthogonalPolynomial, x::Number, n::Number)
72+
resizedata!(P, :, n)
73+
initiateforwardrecurrence(n-1, recurrencecoefficients(P)..., x, _p0(P))[end]
74+
end
6975

7076
getindex(P::OrthogonalPolynomial, x::Number, jr::AbstractInfUnitRange{Int}) = view(P, x, jr)
7177
getindex(P::OrthogonalPolynomial, x::AbstractVector, jr::AbstractInfUnitRange{Int}) = view(P, x, jr)
@@ -83,11 +89,15 @@ end
8389

8490
function unsafe_getindex(f::Mul{<:AbstractOPLayout,<:AbstractPaddedLayout}, x::Number)
8591
P,c = f.A,f.B
86-
_p0(P)*clenshaw(paddeddata(c), recurrencecoefficients(P)..., x)
92+
data = paddeddata(c)
93+
resizedata!(P, :, length(data))
94+
_p0(P)*clenshaw(data, recurrencecoefficients(P)..., x)
8795
end
8896

8997
function unsafe_getindex(f::Mul{<:AbstractOPLayout,<:AbstractPaddedLayout}, x::Number, jr)
9098
P,c = f.A,f.B
99+
data = paddeddata(c)
100+
resizedata!(P, :, maximum(jr; init=0))
91101
_p0(P)*clenshaw(view(paddeddata(c),:,jr), recurrencecoefficients(P)..., x)
92102
end
93103

0 commit comments

Comments
 (0)