|
| 1 | + |
| 2 | +export Chebyshev, NormalizedChebyshev |
| 3 | + |
| 4 | +""" |
| 5 | +`Chebyshev()` is the space spanned by the Chebyshev polynomials |
| 6 | +``` |
| 7 | + T_0(x),T_1(x),T_2(x),… |
| 8 | +``` |
| 9 | +where `T_k(x) = cos(k*acos(x))`. This is the default space |
| 10 | +as there exists a fast transform and general smooth functions on `[-1,1]` |
| 11 | +can be easily resolved. |
| 12 | +""" |
| 13 | +struct Chebyshev{D<:Domain,R} <: PolynomialSpace{D,R} |
| 14 | + domain::D |
| 15 | + function Chebyshev{D,R}(d) where {D,R} |
| 16 | + isempty(d) && throw(ArgumentError("Domain cannot be empty")) |
| 17 | + new(d) |
| 18 | + end |
| 19 | + Chebyshev{D,R}() where {D,R} = new(strictconvert(D, ChebyshevInterval())) |
| 20 | +end |
| 21 | + |
| 22 | +Chebyshev(d::Domain) = Chebyshev{typeof(d),real(prectype(d))}(d) |
| 23 | +Chebyshev() = Chebyshev(ChebyshevInterval()) |
| 24 | +Chebyshev(d) = Chebyshev(Domain(d)) |
| 25 | +const NormalizedChebyshev{D<:Domain,R} = NormalizedPolynomialSpace{Chebyshev{D, R}, D, R} |
| 26 | +NormalizedChebyshev() = NormalizedPolynomialSpace(Chebyshev()) |
| 27 | +NormalizedChebyshev(d) = NormalizedPolynomialSpace(Chebyshev(d)) |
| 28 | + |
| 29 | +function Base.getproperty(S::Chebyshev{<:Any,<:Any},v::Symbol) |
| 30 | + if v==:b || v==:a |
| 31 | + -0.5 |
| 32 | + else |
| 33 | + getfield(S,v) |
| 34 | + end |
| 35 | +end |
| 36 | + |
| 37 | +normalization(::Type{T}, sp::Chebyshev, k::Int) where T = T(π)/(2-FastTransforms.δ(k,0)) |
| 38 | + |
| 39 | +Space(d::SegmentDomain) = Chebyshev(d) |
| 40 | +_norm(x) = norm(x) |
| 41 | +_norm(x::Real) = abs(x) # this preserves integers, whereas norm returns a float |
| 42 | +function Space(d::AbstractInterval) |
| 43 | + a,b = endpoints(d) |
| 44 | + d2 = if isinf(_norm(a)) && isinf(_norm(b)) |
| 45 | + Line(d) |
| 46 | + elseif isinf(_norm(a)) || isinf(_norm(b)) |
| 47 | + Ray(d) |
| 48 | + else |
| 49 | + d |
| 50 | + end |
| 51 | + Chebyshev(d2) |
| 52 | +end |
| 53 | + |
| 54 | + |
| 55 | +setdomain(S::Chebyshev, d::Domain) = Chebyshev(d) |
| 56 | + |
| 57 | +ones(::Type{T}, S::Chebyshev) where {T<:Number} = Fun(S,fill(one(T),1)) |
| 58 | +ones(S::Chebyshev) = Fun(S,fill(1.0,1)) |
| 59 | + |
| 60 | +function first(f::Fun{<:Chebyshev}) |
| 61 | + n = ncoefficients(f) |
| 62 | + n == 0 && return zero(cfstype(f)) |
| 63 | + n == 1 && return f.coefficients[1] |
| 64 | + foldr(-,coefficients(f)) |
| 65 | +end |
| 66 | + |
| 67 | +last(f::Fun{<:Chebyshev}) = reduce(+,coefficients(f)) |
| 68 | + |
| 69 | +spacescompatible(a::Chebyshev,b::Chebyshev) = domainscompatible(a,b) |
| 70 | +hasfasttransform(::Chebyshev) = true |
| 71 | +supportsinplacetransform(::Chebyshev{<:Domain{T},T}) where {T<:AbstractFloat} = true |
| 72 | + |
| 73 | + |
| 74 | +## Transform |
| 75 | + |
| 76 | +transform(::Chebyshev,vals::AbstractVector,plan) = plan*vals |
| 77 | +itransform(::Chebyshev,cfs::AbstractVector,plan) = plan*cfs |
| 78 | +plan_transform(::Chebyshev,vals::AbstractVector) = plan_chebyshevtransform(vals) |
| 79 | +plan_itransform(::Chebyshev,cfs::AbstractVector) = plan_ichebyshevtransform(cfs) |
| 80 | +plan_transform!(::Chebyshev, vals::AbstractVector) = plan_chebyshevtransform!(vals) |
| 81 | +plan_itransform!(::Chebyshev, cfs::AbstractVector) = plan_ichebyshevtransform!(cfs) |
| 82 | + |
| 83 | +## Evaluation |
| 84 | + |
| 85 | +clenshaw(sp::Chebyshev, c::AbstractVector, x::AbstractArray) = |
| 86 | + clenshaw(c,x,ClenshawPlan(promote_type(eltype(c),eltype(x)),sp,length(c),length(x))) |
| 87 | + |
| 88 | +clenshaw(::Chebyshev,c::AbstractVector,x) = chebyshev_clenshaw(c,x) |
| 89 | + |
| 90 | +clenshaw(c::AbstractVector,x::AbstractVector,plan::ClenshawPlan{S,V}) where {S<:Chebyshev,V}= |
| 91 | + clenshaw(c,collect(x),plan) |
| 92 | + |
| 93 | +#TODO: This modifies x, which is not threadsafe |
| 94 | +clenshaw(c::AbstractVector,x::Vector,plan::ClenshawPlan{<:Chebyshev}) = chebyshev_clenshaw(c,x,plan) |
| 95 | + |
| 96 | +function clenshaw(c::AbstractMatrix{T},x::T,plan::ClenshawPlan{S,T}) where {S<:Chebyshev,T<:Number} |
| 97 | + bk=plan.bk |
| 98 | + bk1=plan.bk1 |
| 99 | + bk2=plan.bk2 |
| 100 | + |
| 101 | + m,n=size(c) # m is # of coefficients, n is # of funs |
| 102 | + |
| 103 | + @inbounds for i = 1:n |
| 104 | + bk1[i] = zero(T) |
| 105 | + bk2[i] = zero(T) |
| 106 | + end |
| 107 | + x = 2x |
| 108 | + |
| 109 | + @inbounds for k=m:-1:2 |
| 110 | + for j=1:n |
| 111 | + ck = c[k,j] |
| 112 | + bk[j] = muladd(x,bk1[j],ck - bk2[j]) |
| 113 | + end |
| 114 | + bk2, bk1, bk = bk1, bk, bk2 |
| 115 | + end |
| 116 | + |
| 117 | + x = x/2 |
| 118 | + @inbounds for i = 1:n |
| 119 | + ce = c[1,i] |
| 120 | + bk[i] = muladd(x,bk1[i],ce - bk2[i]) |
| 121 | + end |
| 122 | + |
| 123 | + bk |
| 124 | +end |
| 125 | + |
| 126 | +function clenshaw(c::AbstractMatrix{T},x::AbstractVector{T},plan::ClenshawPlan{S,T}) where {S<:Chebyshev,T<:Number} |
| 127 | + bk=plan.bk |
| 128 | + bk1=plan.bk1 |
| 129 | + bk2=plan.bk2 |
| 130 | + |
| 131 | + m,n=size(c) # m is # of coefficients, n is # of funs |
| 132 | + |
| 133 | + @inbounds for i = 1:n |
| 134 | + x[i] = 2x[i] |
| 135 | + bk1[i] = zero(T) |
| 136 | + bk2[i] = zero(T) |
| 137 | + end |
| 138 | + |
| 139 | + @inbounds for k=m:-1:2 |
| 140 | + for j=1:n |
| 141 | + ck = c[k,j] |
| 142 | + bk[j] = muladd(x[j],bk1[j],ck - bk2[j]) |
| 143 | + end |
| 144 | + bk2, bk1, bk = bk1, bk, bk2 |
| 145 | + end |
| 146 | + |
| 147 | + |
| 148 | + @inbounds for i = 1:n |
| 149 | + x[i] = x[i]/2 |
| 150 | + ce = c[1,i] |
| 151 | + bk[i] = muladd(x[i],bk1[i],ce - bk2[i]) |
| 152 | + end |
| 153 | + |
| 154 | + bk |
| 155 | +end |
| 156 | + |
| 157 | +# overwrite x |
| 158 | + |
| 159 | +function clenshaw!(c::AbstractVector,x::AbstractVector,plan::ClenshawPlan{S,V}) where {S<:Chebyshev,V} |
| 160 | + N,n = length(c),length(x) |
| 161 | + |
| 162 | + if isempty(c) |
| 163 | + fill!(x,0) |
| 164 | + return x |
| 165 | + end |
| 166 | + |
| 167 | + bk=plan.bk |
| 168 | + bk1=plan.bk1 |
| 169 | + bk2=plan.bk2 |
| 170 | + |
| 171 | + @inbounds for i = 1:n |
| 172 | + x[i] = 2x[i] |
| 173 | + bk1[i] = zero(V) |
| 174 | + bk2[i] = zero(V) |
| 175 | + end |
| 176 | + |
| 177 | + @inbounds for k = N:-1:2 |
| 178 | + ck = c[k] |
| 179 | + for i = 1:n |
| 180 | + bk[i] = muladd(x[i],bk1[i],ck - bk2[i]) |
| 181 | + end |
| 182 | + bk2, bk1, bk = bk1, bk, bk2 |
| 183 | + end |
| 184 | + |
| 185 | + ce = c[1] |
| 186 | + @inbounds for i = 1:n |
| 187 | + x[i] = x[i]/2 |
| 188 | + x[i] = muladd(x[i],bk1[i],ce-bk2[i]) |
| 189 | + end |
| 190 | + |
| 191 | + x |
| 192 | +end |
| 193 | + |
| 194 | +## Calculus |
| 195 | + |
| 196 | + |
| 197 | +# diff T -> U, then convert U -> T |
| 198 | +integrate(f::Fun{Chebyshev{D,R}}) where {D<:IntervalOrSegment,R} = |
| 199 | + Fun(f.space,fromcanonicalD(f,0)*ultraint!(ultraconversion(f.coefficients))) |
| 200 | +differentiate(f::Fun{Chebyshev{D,R}}) where {D<:IntervalOrSegment,R} = |
| 201 | + Fun(f.space,1/fromcanonicalD(f,0)*ultraiconversion(ultradiff(f.coefficients))) |
| 202 | + |
| 203 | + |
| 204 | + |
| 205 | + |
| 206 | + |
| 207 | +## Multivariate |
| 208 | + |
| 209 | +# determine correct parameter to have at least |
| 210 | +# N point |
| 211 | +_padua_length(N) = Int(cld(-3+sqrt(1+8N),2)) |
| 212 | + |
| 213 | +function squarepoints(::Type{T}, N) where T |
| 214 | + pts = paduapoints(T, _padua_length(N)) |
| 215 | + n = size(pts,1) |
| 216 | + ret = Array{SVector{2,T}}(undef, n) |
| 217 | + @inbounds for k=1:n |
| 218 | + ret[k] = SVector{2,T}(pts[k,1],pts[k,2]) |
| 219 | + end |
| 220 | + ret |
| 221 | +end |
| 222 | + |
| 223 | +points(S::TensorSpace{<:Tuple{<:Chebyshev{<:ChebyshevInterval},<:Chebyshev{<:ChebyshevInterval}}}, N) = |
| 224 | + squarepoints(real(prectype(S)), N) |
| 225 | + |
| 226 | +function points(S::TensorSpace{<:Tuple{<:Chebyshev,<:Chebyshev}},N) |
| 227 | + T = real(prectype(S)) |
| 228 | + pts = squarepoints(T, N) |
| 229 | + pts .= fromcanonical.(Ref(domain(S)), pts) |
| 230 | + pts |
| 231 | +end |
| 232 | + |
| 233 | +plan_transform(S::TensorSpace{<:Tuple{<:Chebyshev,<:Chebyshev}},v::AbstractVector) = |
| 234 | + plan_paduatransform!(v,Val{false}) |
| 235 | + |
| 236 | +transform(S::TensorSpace{<:Tuple{<:Chebyshev,<:Chebyshev}},v::AbstractVector, |
| 237 | + plan=plan_transform(S,v)) = plan*copy(v) |
| 238 | + |
| 239 | +plan_itransform(S::TensorSpace{<:Tuple{<:Chebyshev,<:Chebyshev}},v::AbstractVector) = |
| 240 | + plan_ipaduatransform!(eltype(v),sum(1:Int(block(S,length(v)))),Val{false}) |
| 241 | + |
| 242 | +itransform(S::TensorSpace{<:Tuple{<:Chebyshev,<:Chebyshev}},v::AbstractVector, |
| 243 | + plan=plan_itransform(S,v)) = plan*pad(v,sum(1:Int(block(S,length(v))))) |
| 244 | + |
| 245 | + |
| 246 | +#TODO: adaptive |
| 247 | +for op in (:(Base.sin),:(Base.cos)) |
| 248 | + @eval ($op)(f::ProductFun{<:Chebyshev,<:Chebyshev}) = |
| 249 | + ProductFun(chebyshevtransform($op.(values(f))),space(f)) |
| 250 | +end |
| 251 | + |
| 252 | + |
| 253 | + |
| 254 | +reverseorientation(f::Fun{<:Chebyshev}) = |
| 255 | + Fun(Chebyshev(reverseorientation(domain(f))),alternatesign!(copy(f.coefficients))) |
| 256 | + |
| 257 | + |
| 258 | +include("ChebyshevOperators.jl") |
0 commit comments