|
| 1 | +""" |
| 2 | + struct Trigonometric <: AbstractMonomialIndexed end |
| 3 | +
|
| 4 | +Univariate trigonometric basis is |
| 5 | +``` |
| 6 | +a0 + a1 cos(ωt) + a2 sin(ωt) + a3 cos(2ωt) + a4 sin(2ωt) |
| 7 | +``` |
| 8 | +""" |
| 9 | +struct Trigonometric <: AbstractMultipleOrthogonal end |
| 10 | + |
| 11 | +_cos_id(d) = iszero(d) ? 0 : 2d - 1 |
| 12 | +_sin_id(d) = 2d |
| 13 | +_is_cos(d) = isodd(d) |
| 14 | +_is_sin(d) = d > 0 && iseven(d) |
| 15 | +_id(d) = div(d + 1, 2) |
| 16 | + |
| 17 | +# https://en.wikipedia.org/wiki/Chebyshev_polynomials#Properties |
| 18 | +# Using |
| 19 | +# sin(a + b) = sin(a) cos(b) + cos(a) sin(b) |
| 20 | +# sin(a - b) = sin(a) cos(b) - cos(a) sin(b) |
| 21 | +# If a > b |
| 22 | +# sin(a) cos(b) = sin(a + b) + sin(a - b) |
| 23 | +# sin(a) cos(b) = sin(a + b) + sin(a - b) |
| 24 | +function univariate_mul!(::Mul{Trigonometric}, terms, var, a, b) |
| 25 | + @assert !iszero(a) |
| 26 | + @assert !iszero(b) |
| 27 | + I = eachindex(terms) |
| 28 | + da = _id(a) |
| 29 | + db = _id(b) |
| 30 | + for i in I |
| 31 | + if _is_cos(a) == _is_cos(b) |
| 32 | + # Chebyshev first kind |
| 33 | + mono = MP.monomial(terms[i]) * var^(_cos_id(da + db)) |
| 34 | + terms[i] = MA.mul!!(terms[i], var^_cos_id(abs(da - db))) |
| 35 | + terms[i] = MA.operate!!(/, terms[i], 2) |
| 36 | + α = MA.copy_if_mutable(MP.coefficient(terms[i])) |
| 37 | + push!(terms, MP.term(α, mono)) |
| 38 | + # cos(a + b) = cos(a) cos(b) - sin(a) sin(b) |
| 39 | + # cos(a - b) = cos(a) cos(b) + sin(a) sin(b) |
| 40 | + # cos(a - b) - cos(a + b) |
| 41 | + if _is_sin(a) |
| 42 | + terms[end] = MA.operate!!(*, terms[end], -1) |
| 43 | + end |
| 44 | + else |
| 45 | + if _is_cos(a) |
| 46 | + da, db = db, da |
| 47 | + end |
| 48 | + # sin(da) * cos(db) |
| 49 | + if da == db |
| 50 | + # sin(da) * cos(da) = sin(2da) / 2 |
| 51 | + terms[i] = MA.mul!!(terms[i], var^_cos_id(da + db)) |
| 52 | + terms[i] = MA.operate!!(/, terms[i], 2) |
| 53 | + else |
| 54 | + # Using |
| 55 | + # sin(a + b) = sin(a) cos(b) + cos(a) sin(b) |
| 56 | + # sin(a - b) = sin(a) cos(b) - cos(a) sin(b) |
| 57 | + # If a > b |
| 58 | + # sin(a) cos(b) = (sin(a + b) + sin(a - b)) / 2 |
| 59 | + # If a < b |
| 60 | + # sin(a) cos(b) = (sin(b + a) - sin(b - a)) / 2 |
| 61 | + mono = MP.monomial(terms[i]) * var^(_sin_id(da + db)) |
| 62 | + terms[i] = MA.mul!!(terms[i], var^_sin_id(abs(da - db))) |
| 63 | + terms[i] = MA.operate!!(/, terms[i], 2) |
| 64 | + α = MA.copy_if_mutable(MP.coefficient(terms[i])) |
| 65 | + push!(terms, MP.term(α, mono)) |
| 66 | + if da < db |
| 67 | + terms[i] = MA.operate!!(*, terms[i], -1) |
| 68 | + end |
| 69 | + end |
| 70 | + end |
| 71 | + end |
| 72 | + return |
| 73 | +end |
| 74 | + |
| 75 | +function degree_one_univariate_polynomial(::Type{Trigonometric}, variable) |
| 76 | + MA.@rewrite(variable + 0) |
| 77 | +end |
| 78 | + |
| 79 | +function recurrence_eval( |
| 80 | + ::Type{Trigonometric}, |
| 81 | + previous::AbstractVector, |
| 82 | + value, |
| 83 | + degree, |
| 84 | +) |
| 85 | + d = _id(degree) |
| 86 | + if _is_cos(degree) |
| 87 | + # Chebyshev first order |
| 88 | + return 2 * value * previous[_cos_id(d - 1)+1] - |
| 89 | + previous[_cos_id(d - 2)+1] |
| 90 | + else |
| 91 | + return sqrt(1 - previous[degree]^2) |
| 92 | + end |
| 93 | +end |
| 94 | + |
| 95 | +function _promote_coef(::Type{T}, ::Type{Trigonometric}) where {T} |
| 96 | + return _promote_div(T) |
| 97 | +end |
| 98 | + |
| 99 | +# FIXME The cos part is, like Chebysev, maybe the sin part too ? We should do better here, this is just a stopgap |
| 100 | +even_odd_separated(::Type{Trigonometric}) = false |
0 commit comments