Skip to content

Commit d43b558

Browse files
authored
Cleanup Cholesky, add ConvertedOrthogonalPolynomial (#134)
* Cleanup Cholesky, add ConvertedOrthogonalPolynomial * p = 10k !!! * Fix normalisation * check conversion operator * Update choleskyQR.jl * remove checks * Update test_choleskyQR.jl * Simplify equals for normalized * Update hermite.jl
1 parent a424701 commit d43b558

File tree

9 files changed

+286
-219
lines changed

9 files changed

+286
-219
lines changed

Project.toml

Lines changed: 2 additions & 2 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 <[email protected]>"]
4-
version = "0.8"
4+
version = "0.8.1"
55

66
[deps]
77
ArrayLayouts = "4c555306-a7a7-4459-81d9-ec55ddd5c99a"
@@ -25,7 +25,7 @@ QuasiArrays = "c4ea9172-b204-11e9-377d-29865faadc5c"
2525
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
2626

2727
[compat]
28-
ArrayLayouts = "1"
28+
ArrayLayouts = "1.0.1"
2929
BandedMatrices = "0.17.17"
3030
BlockArrays = "0.16.9"
3131
BlockBandedMatrices = "0.12"

src/ClassicalOrthogonalPolynomials.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import FastGaussQuadrature: jacobimoment
4747
import BlockArrays: blockedrange, _BlockedUnitRange, unblock, _BlockArray, block, blockindex, BlockSlice, blockvec
4848
import BandedMatrices: bandwidths
4949

50-
export OrthogonalPolynomial, Normalized, orthonormalpolynomial, LanczosPolynomial,
50+
export OrthogonalPolynomial, Normalized, LanczosPolynomial,
5151
Hermite, Jacobi, Legendre, Chebyshev, ChebyshevT, ChebyshevU, ChebyshevInterval, Ultraspherical, Fourier, Laurent, Laguerre,
5252
HermiteWeight, JacobiWeight, ChebyshevWeight, ChebyshevGrid, ChebyshevTWeight, ChebyshevUWeight, UltrasphericalWeight, LegendreWeight, LaguerreWeight,
5353
WeightedUltraspherical, WeightedChebyshev, WeightedChebyshevT, WeightedChebyshevU, WeightedJacobi,
@@ -230,6 +230,7 @@ include("clenshaw.jl")
230230
include("ratios.jl")
231231
include("normalized.jl")
232232
include("lanczos.jl")
233+
include("choleskyQR.jl")
233234

234235
function _tritrunc(_, X, n)
235236
c,a,b = subdiagonaldata(X),diagonaldata(X),supdiagonaldata(X)
@@ -322,7 +323,6 @@ include("classical/chebyshev.jl")
322323
include("classical/ultraspherical.jl")
323324
include("classical/laguerre.jl")
324325
include("classical/fourier.jl")
325-
include("choleskyQR.jl")
326326
include("roots.jl")
327327

328328
end # module

src/choleskyQR.jl

Lines changed: 71 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,85 @@
1+
"""
2+
Represent an Orthogonal polynomial which has a conversion operator from P, that is, Q = P * inv(U).
3+
"""
4+
struct ConvertedOrthogonalPolynomial{T, WW<:AbstractQuasiVector{T}, XX, UU, PP} <: OrthonormalPolynomial{T}
5+
weight::WW
6+
X::XX # jacobimatrix
7+
U::UU # conversion to P
8+
P::PP
9+
end
10+
11+
_p0(Q::ConvertedOrthogonalPolynomial) = _p0(Q.P)
12+
13+
axes(Q::ConvertedOrthogonalPolynomial) = axes(Q.P)
14+
MemoryLayout(::Type{<:ConvertedOrthogonalPolynomial}) = ConvertedOPLayout()
15+
jacobimatrix(Q::ConvertedOrthogonalPolynomial) = Q.X
16+
orthogonalityweight(Q::ConvertedOrthogonalPolynomial) = Q.weight
17+
18+
19+
# transform to P * U if needed for differentiation, etc.
20+
arguments(::ApplyLayout{typeof(*)}, Q::ConvertedOrthogonalPolynomial) = Q.P, ApplyArray(inv, Q.U)
21+
22+
OrthogonalPolynomial(w::AbstractQuasiVector) = OrthogonalPolynomial(w, orthogonalpolynomial(singularities(w)))
23+
function OrthogonalPolynomial(w::AbstractQuasiVector, P::AbstractQuasiMatrix)
24+
Q = normalized(P)
25+
X = cholesky_jacobimatrix(w, Q)
26+
ConvertedOrthogonalPolynomial(w, X, X.dv.U, Q)
27+
end
28+
29+
orthogonalpolynomial(w::AbstractQuasiVector) = OrthogonalPolynomial(w)
30+
orthogonalpolynomial(w::SubQuasiArray) = orthogonalpolynomial(parent(w))[parentindices(w)[1],:]
31+
32+
33+
134
"""
235
cholesky_jacobimatrix(w, P)
336
437
returns the Jacobi matrix `X` associated to a quasi-matrix of polynomials
538
orthogonal with respect to `w(x) w_p(x)` where `w_p(x)` is the weight of the polynomials in `P`.
639
740
The resulting polynomials are orthonormal on the same domain as `P`. The supplied `P` must be normalized. Accepted inputs are `w` as a function or `W` as an infinite matrix representing multiplication with the function `w` on the basis `P`.
8-
9-
An optional bool can be supplied, i.e. `cholesky_jacobimatrix(sqrtw, P, false)` to disable checks of symmetry for the weight multiplication matrix and orthonormality for the basis (use with caution).
1041
"""
11-
function cholesky_jacobimatrix(w::Function, P::OrthogonalPolynomial, checks::Bool = true)
12-
checks && !(P isa Normalized) && error("Polynomials must be orthonormal.")
13-
W = Symmetric(P \ (w.(axes(P,1)) .* P)) # Compute weight multiplication via Clenshaw
14-
return cholesky_jacobimatrix(W, P, false) # At this point checks already passed or were entered as false, no need to recheck
42+
cholesky_jacobimatrix(w::Function, P) = cholesky_jacobimatrix(w.(axes(P,1)), P)
43+
44+
function cholesky_jacobimatrix(w::AbstractQuasiVector, P)
45+
Q = normalized(P)
46+
W = Symmetric(Q \ (w .* Q)) # Compute weight multiplication via Clenshaw
47+
return cholesky_jacobimatrix(W, Q)
1548
end
16-
function cholesky_jacobimatrix(W::AbstractMatrix, P::OrthogonalPolynomial, checks::Bool = true)
17-
checks && !(P isa Normalized) && error("Polynomials must be orthonormal.")
18-
checks && !(W isa Symmetric) && error("Weight modification matrix must be symmetric.")
19-
return SymTridiagonal(CholeskyJacobiBands{:dv}(W,P),CholeskyJacobiBands{:ev}(W,P))
49+
function cholesky_jacobimatrix(W::AbstractMatrix, Q)
50+
isnormalized(Q) || error("Polynomials must be orthonormal")
51+
U = cholesky(W).U
52+
X = jacobimatrix(Q)
53+
UX = ApplyArray(*,U,X)
54+
return SymTridiagonal(CholeskyJacobiBand{:dv}(U, UX),CholeskyJacobiBand{:ev}(U, UX))
2055
end
2156

2257
# The generated Jacobi operators are symmetric tridiagonal, so we store their data in cached bands
23-
mutable struct CholeskyJacobiBands{dv,T} <: AbstractCachedVector{T}
58+
mutable struct CholeskyJacobiBand{dv,T} <: AbstractCachedVector{T}
2459
data::Vector{T} # store band entries, :dv for diagonal, :ev for off-diagonal
2560
U::UpperTriangular{T} # store upper triangular conversion matrix (needed to extend available entries)
2661
UX::ApplyArray{T} # store U*X, where X is the Jacobi matrix of the original P (needed to extend available entries)
2762
datasize::Int # size of so-far computed block
2863
end
2964

3065
# Computes the initial data for the Jacobi operator bands
31-
function CholeskyJacobiBands{:dv}(W, P::OrthogonalPolynomial{T}) where T
32-
U = cholesky(W).U
33-
X = jacobimatrix(P)
34-
UX = ApplyArray(*,U,X)
66+
function CholeskyJacobiBand{:dv}(U::AbstractMatrix{T}, UX) where T
3567
dv = zeros(T,2) # compute a length 2 vector on first go
3668
dv[1] = dot(view(UX,1,1), U[1,1] \ [one(T)])
3769
dv[2] = dot(view(UX,2,1:2), U[1:2,1:2] \ [zero(T); one(T)])
38-
return CholeskyJacobiBands{:dv,T}(dv, U, UX, 2)
70+
return CholeskyJacobiBand{:dv,T}(dv, U, UX, 2)
3971
end
40-
function CholeskyJacobiBands{:ev}(W, P::OrthogonalPolynomial{T}) where T
41-
U = cholesky(W).U
42-
X = jacobimatrix(P)
43-
UX = ApplyArray(*,U,X)
72+
function CholeskyJacobiBand{:ev}(U::AbstractMatrix{T}, UX) where T
4473
ev = zeros(T,2) # compute a length 2 vector on first go
4574
ev[1] = dot(view(UX,1,1:2), U[1:2,1:2] \ [zero(T); one(T)])
4675
ev[2] = dot(view(UX,2,1:3), U[1:3,1:3] \ [zeros(T,2); one(T)])
47-
return CholeskyJacobiBands{:ev,T}(ev, U, UX, 2)
76+
return CholeskyJacobiBand{:ev,T}(ev, U, UX, 2)
4877
end
4978

50-
size(::CholeskyJacobiBands) = (ℵ₀,) # Stored as an infinite cached vector
79+
size(::CholeskyJacobiBand) = (ℵ₀,) # Stored as an infinite cached vector
5180

5281
# Resize and filling functions for cached implementation
53-
function resizedata!(K::CholeskyJacobiBands, nm::Integer)
82+
function resizedata!(K::CholeskyJacobiBand, nm::Integer)
5483
νμ = K.datasize
5584
if nm > νμ
5685
resize!(K.data,nm)
@@ -59,7 +88,7 @@ function resizedata!(K::CholeskyJacobiBands, nm::Integer)
5988
end
6089
K
6190
end
62-
function cache_filldata!(J::CholeskyJacobiBands{:dv,T}, inds::UnitRange{Int}) where T
91+
function cache_filldata!(J::CholeskyJacobiBand{:dv,T}, inds::UnitRange{Int}) where T
6392
# pre-fill U and UX to prevent expensive step-by-step filling in of cached U and UX in the loop
6493
getindex(J.U,inds[end]+1,inds[end]+1)
6594
getindex(J.UX,inds[end]+1,inds[end]+1)
@@ -69,7 +98,7 @@ function cache_filldata!(J::CholeskyJacobiBands{:dv,T}, inds::UnitRange{Int}) wh
6998
J.data[k] = dot(view(J.UX,k,k-1:k), J.U[k-1:k,k-1:k] \ ek)
7099
end
71100
end
72-
function cache_filldata!(J::CholeskyJacobiBands{:ev, T}, inds::UnitRange{Int}) where T
101+
function cache_filldata!(J::CholeskyJacobiBand{:ev, T}, inds::UnitRange{Int}) where T
73102
# pre-fill U and UX to prevent expensive step-by-step filling in of cached U and UX in the loop
74103
getindex(J.U,inds[end]+1,inds[end]+1)
75104
getindex(J.UX,inds[end]+1,inds[end]+1)
@@ -88,55 +117,52 @@ returns the Jacobi matrix `X` associated to a quasi-matrix of polynomials
88117
orthogonal with respect to `w(x) w_p(x)` where `w_p(x)` is the weight of the polynomials in `P`.
89118
90119
The resulting polynomials are orthonormal on the same domain as `P`. The supplied `P` must be normalized. Accepted inputs for `sqrtw` are the square root of the weight modification as a function or `sqrtW` as an infinite matrix representing multiplication with the function `sqrt(w)` on the basis `P`.
91-
92-
An optional bool can be supplied, i.e. `qr_jacobimatrix(sqrtw, P, false)` to disable checks of symmetry for the weight multiplication matrix and orthonormality for the basis (use with caution).
93120
"""
94-
function qr_jacobimatrix(sqrtw::Function, P::OrthogonalPolynomial, checks::Bool = true)
95-
checks && !(P isa Normalized) && error("Polynomials must be orthonormal.")
96-
sqrtW = (P \ (sqrtw.(axes(P,1)) .* P)) # Compute weight multiplication via Clenshaw
97-
return qr_jacobimatrix(sqrtW, P, false) # At this point checks already passed or were entered as false, no need to recheck
121+
function qr_jacobimatrix(sqrtw::Function, P)
122+
Q = normalized(P)
123+
x = axes(P,1)
124+
sqrtW = (Q \ (sqrtw.(x) .* Q)) # Compute weight multiplication via Clenshaw
125+
return qr_jacobimatrix(sqrtW, Q)
98126
end
99-
function qr_jacobimatrix(sqrtW::AbstractMatrix, P::OrthogonalPolynomial, checks::Bool = true)
100-
checks && !(P isa Normalized) && error("Polynomials must be orthonormal.")
101-
checks && !(sqrtW isa Symmetric) && error("Weight modification matrix must be symmetric.")
102-
K = SymTridiagonal(QRJacobiBands{:dv}(sqrtW,P),QRJacobiBands{:ev}(sqrtW,P))
103-
return K
127+
function qr_jacobimatrix(sqrtW::AbstractMatrix, Q)
128+
isnormalized(Q) || error("Polynomials must be orthonormal")
129+
SymTridiagonal(QRJacobiBand{:dv}(sqrtW,Q),QRJacobiBand{:ev}(sqrtW,Q))
104130
end
105131

106132
# The generated Jacobi operators are symmetric tridiagonal, so we store their data in cached bands
107-
mutable struct QRJacobiBands{dv,T} <: AbstractCachedVector{T}
133+
mutable struct QRJacobiBand{dv,T} <: AbstractCachedVector{T}
108134
data::Vector{T} # store band entries, :dv for diagonal, :ev for off-diagonal
109135
U::ApplyArray{T} # store upper triangular conversion matrix (needed to extend available entries)
110136
UX::ApplyArray{T} # store U*X, where X is the Jacobi matrix of the original P (needed to extend available entries)
111137
datasize::Int # size of so-far computed block
112138
end
113139

114140
# Computes the initial data for the Jacobi operator bands
115-
function QRJacobiBands{:dv}(sqrtW, P::OrthogonalPolynomial{T}) where T
141+
function QRJacobiBand{:dv}(sqrtW, P::OrthogonalPolynomial{T}) where T
116142
U = qr(sqrtW).R
117143
U = ApplyArray(*,Diagonal(sign.(view(U,band(0)))),U)
118144
X = jacobimatrix(P)
119145
UX = ApplyArray(*,U,X)
120146
dv = zeros(T,2) # compute a length 2 vector on first go
121147
dv[1] = dot(view(UX,1,1), U[1,1] \ [one(T)])
122148
dv[2] = dot(view(UX,2,1:2), U[1:2,1:2] \ [zero(T); one(T)])
123-
return QRJacobiBands{:dv,T}(dv, U, UX, 2)
149+
return QRJacobiBand{:dv,T}(dv, U, UX, 2)
124150
end
125-
function QRJacobiBands{:ev}(sqrtW, P::OrthogonalPolynomial{T}) where T
151+
function QRJacobiBand{:ev}(sqrtW, P::OrthogonalPolynomial{T}) where T
126152
U = qr(sqrtW).R
127153
U = ApplyArray(*,Diagonal(sign.(view(U,band(0)))),U)
128154
X = jacobimatrix(P)
129155
UX = ApplyArray(*,U,X)
130156
ev = zeros(T,2) # compute a length 2 vector on first go
131157
ev[1] = dot(view(UX,1,1:2), U[1:2,1:2] \ [zero(T); one(T)])
132158
ev[2] = dot(view(UX,2,1:3), U[1:3,1:3] \ [zeros(T,2); one(T)])
133-
return QRJacobiBands{:ev,T}(ev, U, UX, 2)
159+
return QRJacobiBand{:ev,T}(ev, U, UX, 2)
134160
end
135161

136-
size(::QRJacobiBands) = (ℵ₀,) # Stored as an infinite cached vector
162+
size(::QRJacobiBand) = (ℵ₀,) # Stored as an infinite cached vector
137163

138164
# Resize and filling functions for cached implementation
139-
function resizedata!(K::QRJacobiBands, nm::Integer)
165+
function resizedata!(K::QRJacobiBand, nm::Integer)
140166
νμ = K.datasize
141167
if nm > νμ
142168
resize!(K.data,nm)
@@ -145,7 +171,7 @@ function resizedata!(K::QRJacobiBands, nm::Integer)
145171
end
146172
K
147173
end
148-
function cache_filldata!(J::QRJacobiBands{:dv,T}, inds::UnitRange{Int}) where T
174+
function cache_filldata!(J::QRJacobiBand{:dv,T}, inds::UnitRange{Int}) where T
149175
# pre-fill U and UX to prevent expensive step-by-step filling in of cached U and UX in the loop
150176
getindex(J.U,inds[end]+1,inds[end]+1)
151177
getindex(J.UX,inds[end]+1,inds[end]+1)
@@ -155,7 +181,7 @@ function cache_filldata!(J::QRJacobiBands{:dv,T}, inds::UnitRange{Int}) where T
155181
J.data[k] = dot(view(J.UX,k,k-1:k), J.U[k-1:k,k-1:k] \ ek)
156182
end
157183
end
158-
function cache_filldata!(J::QRJacobiBands{:ev, T}, inds::UnitRange{Int}) where T
184+
function cache_filldata!(J::QRJacobiBand{:ev, T}, inds::UnitRange{Int}) where T
159185
# pre-fill U and UX to prevent expensive step-by-step filling in of cached U and UX in the loop
160186
getindex(J.U,inds[end]+1,inds[end]+1)
161187
getindex(J.UX,inds[end]+1,inds[end]+1)

src/classical/hermite.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ struct HermiteWeight{T} <: Weight{T} end
77

88
HermiteWeight() = HermiteWeight{Float64}()
99
axes(::HermiteWeight{T}) where T = (Inclusion{T}(ℝ),)
10+
==(::HermiteWeight, ::HermiteWeight) = true
11+
1012
function getindex(w::HermiteWeight, x::Number)
1113
x axes(w,1) || throw(BoundsError())
1214
exp(-x^2)

src/classical/jacobi.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ abstract type AbstractJacobiWeight{T} <: Weight{T} end
33
axes(::AbstractJacobiWeight{T}) where T = (Inclusion(ChebyshevInterval{T}()),)
44

55
==(w::AbstractJacobiWeight, v::AbstractJacobiWeight) = w.a == v.a && w.b == v.b
6+
function ==(a::AffineQuasiVector, w::AbstractJacobiWeight)
7+
axes(a,1) == axes(w,1) || return false
8+
iszero(w.a) && iszero(w.b) && return iszero(a.A) && return iszero(a.b)
9+
isone(w.a) && iszero(w.b) && return isone(-a.A) && return isone(a.b)
10+
iszero(w.a) && isone(w.b) && return isone(a.A) && return isone(a.b)
11+
return false
12+
end
13+
==(w::AbstractJacobiWeight, a::AffineQuasiVector) = a == w
614

715
broadcasted(::LazyQuasiArrayStyle{1}, ::typeof(*), w::AbstractJacobiWeight, v::AbstractJacobiWeight) =
816
JacobiWeight(w.a + v.a, w.b + v.b)

src/lanczos.jl

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,13 @@ struct LanczosPolynomial{T,Weight,Basis} <: OrthogonalPolynomial{T}
157157
end
158158

159159
function LanczosPolynomial(w_in::AbstractQuasiVector, P::AbstractQuasiMatrix)
160-
Q = normalize(P)
160+
Q = normalized(P)
161161
wQ = weighted(Q)
162162
w = wQ * (wQ \ w_in) # expand weight in basis
163163
LanczosPolynomial(w, Q, LanczosData(w, Q))
164164
end
165165

166-
LanczosPolynomial(w::AbstractQuasiVector) = LanczosPolynomial(w, orthonormalpolynomial(singularities(w)))
166+
LanczosPolynomial(w::AbstractQuasiVector) = LanczosPolynomial(w, normalized(orthogonalpolynomial(singularities(w))))
167167

168168
==(A::LanczosPolynomial, B::LanczosPolynomial) = A.w == B.w
169169
==(::LanczosPolynomial, ::OrthogonalPolynomial) = false # TODO: fix
@@ -172,16 +172,7 @@ LanczosPolynomial(w::AbstractQuasiVector) = LanczosPolynomial(w, orthonormalpoly
172172
==(::LanczosPolynomial, ::SubQuasiArray{<:Any,2,<:OrthogonalPolynomial}) = false # TODO: fix
173173

174174

175-
normalize(Q::LanczosPolynomial) = Q
176-
normalize(Q::AbstractQuasiMatrix) = Normalized(Q)
177-
178-
OrthogonalPolynomial(w::AbstractQuasiVector) = LanczosPolynomial(w)
179-
orthogonalpolynomial(w::AbstractQuasiVector) = OrthogonalPolynomial(w)
180-
orthogonalpolynomial(w::SubQuasiArray) = orthogonalpolynomial(parent(w))[parentindices(w)[1],:]
181-
orthonormalpolynomial(w::AbstractQuasiVector) = normalize(orthogonalpolynomial(w))
182-
183-
184-
175+
normalized(Q::LanczosPolynomial) = Q
185176

186177
orthogonalityweight(Q::LanczosPolynomial) = Q.w
187178

@@ -227,17 +218,11 @@ function ldiv(Qn::SubQuasiArray{<:Any,2,<:LanczosPolynomial,<:Tuple{<:Inclusion,
227218
LanczosConversion(Q.data)[jr,jr] \ (Q.P[:,jr] \ C)
228219
end
229220

230-
struct LanczosLayout <: AbstractOPLayout end
221+
struct LanczosLayout <: AbstractNormalizedOPLayout end
231222

232223
MemoryLayout(::Type{<:LanczosPolynomial}) = LanczosLayout()
233224
arguments(::ApplyLayout{typeof(*)}, Q::LanczosPolynomial) = Q.P, LanczosConversion(Q.data)
234-
copy(L::Ldiv{LanczosLayout,Lay}) where Lay<:AbstractLazyLayout = copy(Ldiv{ApplyLayout{typeof(*)},Lay}(L.A,L.B))
235-
copy(L::Ldiv{LanczosLayout,Lay}) where Lay<:BroadcastLayout = copy(Ldiv{ApplyLayout{typeof(*)},Lay}(L.A,L.B))
236-
copy(L::Ldiv{LanczosLayout,BroadcastLayout{typeof(*)}}) = copy(Ldiv{ApplyLayout{typeof(*)},BroadcastLayout{typeof(*)}}(L.A,L.B))
237-
copy(L::Ldiv{LanczosLayout,BroadcastLayout{typeof(*)},<:Any,<:AbstractQuasiVector}) = copy(Ldiv{ApplyLayout{typeof(*)},BroadcastLayout{typeof(*)}}(L.A,L.B))
238-
copy(L::Ldiv{LanczosLayout,ApplyLayout{typeof(*)}}) = copy(Ldiv{ApplyLayout{typeof(*)},ApplyLayout{typeof(*)}}(L.A,L.B))
239-
copy(L::Ldiv{LanczosLayout,ApplyLayout{typeof(*)},<:Any,<:AbstractQuasiVector}) = copy(Ldiv{ApplyLayout{typeof(*)},ApplyLayout{typeof(*)}}(L.A,L.B))
240-
copy(L::Ldiv{LanczosLayout,<:ExpansionLayout}) = copy(Ldiv{ApplyLayout{typeof(*)},ApplyLayout{typeof(*)}}(L.A, L.B))
225+
241226
LazyArrays._mul_arguments(Q::LanczosPolynomial) = arguments(ApplyLayout{typeof(*)}(), Q)
242227
LazyArrays._mul_arguments(Q::QuasiAdjoint{<:Any,<:LanczosPolynomial}) = arguments(ApplyLayout{typeof(*)}(), Q)
243228

0 commit comments

Comments
 (0)