|
| 1 | +# Transforms between different bases |
| 2 | + |
| 3 | +Sometimes, we have a function ``f`` expanded over the basis functions |
| 4 | +of one basis ``A``, |
| 5 | +```math |
| 6 | +f_A = A\vec{c} |
| 7 | +``` |
| 8 | +and wish to re-express as an expansion over the |
| 9 | +basis functions of another basis ``B``, |
| 10 | +```math |
| 11 | +f_B = B\vec{d}. |
| 12 | +``` |
| 13 | +We thus want to solve ``f_A = f_B`` for ``\vec{d}``; this is very |
| 14 | +easy, and follows the same reasoning as in [Solving |
| 15 | +equations](@ref). We begin by projecting into the space ``B`` by using |
| 16 | +the projector ``BB^H``: |
| 17 | +```math |
| 18 | +BB^HA\vec{c} = |
| 19 | +BB^HB\vec{d}. |
| 20 | +``` |
| 21 | +This has to hold for any ``B``, as long as the basis functions of |
| 22 | +``B`` are linearly independent, and is then equivalent to |
| 23 | +```math |
| 24 | +B^HA\vec{c} = |
| 25 | +B^HB\vec{d}, |
| 26 | +``` |
| 27 | +the solution of which is |
| 28 | +```math |
| 29 | +\vec{d} = |
| 30 | +(B^HB)^{-1} |
| 31 | +B^HA |
| 32 | +\vec{c} |
| 33 | +\defd |
| 34 | +S_{BB}^{-1} |
| 35 | +S_{BA} |
| 36 | +\vec{c}. |
| 37 | +``` |
| 38 | + |
| 39 | +[`basis_overlap`](@ref) computes ``S_{BA}=B^HA`` and |
| 40 | +[`basis_transform`](@ref) the full transformation matrix |
| 41 | +``(B^HB)^{-1}B^HA``; sometimes it may be desirable to perform the |
| 42 | +transformation manually in two steps, since the combined matrix may be |
| 43 | +dense whereas the constituents may be relatively sparse. For |
| 44 | +orthogonal bases, ``B^HB`` is naturally diagonal. |
| 45 | + |
| 46 | +There is no requirement that the two bases span the same intervals, |
| 47 | +i.e. `axes(A,1)` and `axes(B,1)` need not be fully overlapping. Of |
| 48 | +course, if they are fully disjoint, the transform matrix will be |
| 49 | +zero. Additionally, some functions may not be well represented in a |
| 50 | +particular basis, e.g. the step function is not well approximated by |
| 51 | +B-splines (of order ``>1``), but poses no problem for |
| 52 | +finite-differences, except at the discontinuity. To judge if a |
| 53 | +transform is faithful thus amount to comparing the transformed |
| 54 | +expansion coefficients with those obtained by expanding the function |
| 55 | +in the target basis directly, instead of comparing reconstruction |
| 56 | +errors. |
| 57 | + |
| 58 | +## Examples |
| 59 | + |
| 60 | +We will now illustrate transformation between B-splines, FEDVR, and |
| 61 | +various finite-differences: |
| 62 | +```julia |
| 63 | +julia> A = BSpline(LinearKnotSet(7, 0, 2, 11)) |
| 64 | +BSpline{Float64} basis with typename(LinearKnotSet)(Float64) of order k = 7 on 0.0..2.0 (11 intervals) |
| 65 | + |
| 66 | +julia> B = FEDVR(range(0.0, 2.5, 5), 6)[:,2:end-1] |
| 67 | +FEDVR{Float64} basis with 4 elements on 0.0..2.5, restricted to elements 1:4, basis functions 2..20 ⊂ 1..21 |
| 68 | + |
| 69 | +julia> C = StaggeredFiniteDifferences(0.03, 0.3, 0.01, 3.0) |
| 70 | +Staggered finite differences basis {Float64} on 0.0..3.066973760326056 with 90 points with spacing varying from 0.030040496962651875 to 0.037956283408020486 |
| 71 | + |
| 72 | +julia> D = FiniteDifferences(range(0.25,1.75,length=31)) |
| 73 | +Finite differences basis {Float64} on 0.2..1.8 with 31 points spaced by Δx = 0.05 |
| 74 | + |
| 75 | +julia> E = BSpline(LinearKnotSet(9, -1, 3, 15))[:,2:end] |
| 76 | +BSpline{Float64} basis with typename(LinearKnotSet)(Float64) of order k = 9 on -1.0..3.0 (15 intervals), restricted to basis functions 2..23 ⊂ 1..23 |
| 77 | +``` |
| 78 | + |
| 79 | +We use these bases to expand two test functions: |
| 80 | +```math |
| 81 | +f_1(x) = \exp\left[ |
| 82 | +-\frac{(x-1)^2}{2\times0.2^2} |
| 83 | +\right] |
| 84 | +``` |
| 85 | +and |
| 86 | +```math |
| 87 | +f_2(x) = \theta(x-0.5) - \theta(x-1.5), |
| 88 | +``` |
| 89 | +where ``\theta(x)`` is the [Heaviside step |
| 90 | +function](https://en.wikipedia.org/wiki/Heaviside_step_function). |
| 91 | + |
| 92 | +```julia |
| 93 | +julia> gauss(x) = exp(-(x-1.0).^2/(2*0.2^2)) |
| 94 | +gauss (generic function with 1 method) |
| 95 | + |
| 96 | +julia> u(x) = (0.5 < x < 1.5)*one(x) |
| 97 | +u (generic function with 1 method) |
| 98 | +``` |
| 99 | + |
| 100 | +If for example, we first expand ``f_1`` in B-splines (``A``), and then wish to |
| 101 | +project onto the staggered finite-differences (``C``), we do the |
| 102 | +following: |
| 103 | + |
| 104 | +```julia |
| 105 | +julia> c = A \ gauss.(axes(A, 1)) |
| 106 | +17-element Vector{Float64}: |
| 107 | + -0.0003757237742430358 |
| 108 | + 0.001655433792947186 |
| 109 | + -0.003950561899347059 |
| 110 | + 0.00714774775995981 |
| 111 | + -0.011051108016208545 |
| 112 | + 0.0173792655408227 |
| 113 | + 0.04090160980771093 |
| 114 | + 0.6336420285181446 |
| 115 | + 1.3884690430556483 |
| 116 | + 0.6336420285181449 |
| 117 | + 0.04090160980771065 |
| 118 | + 0.01737926554082302 |
| 119 | + -0.011051108016208311 |
| 120 | + 0.007147747759958865 |
| 121 | + -0.00395056189934631 |
| 122 | + 0.0016554337929467365 |
| 123 | + -0.00037572377424269145 |
| 124 | + |
| 125 | +julia> S_CA = basis_transform(C, A) |
| 126 | +90×17 SparseArrays.SparseMatrixCSC{Float64, Int64} with 1530 stored entries: |
| 127 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 128 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 129 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 130 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 131 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 132 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 133 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 134 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 135 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 136 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 137 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 138 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 139 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 140 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 141 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 142 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 143 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 144 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 145 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 146 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 147 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 148 | +⣿⣿⣿⣿⣿⣿⣿⣿⡇ |
| 149 | +⠛⠛⠛⠛⠛⠛⠛⠛⠃ |
| 150 | + |
| 151 | +julia> S_CA*c |
| 152 | +90-element Vector{Float64}: |
| 153 | + 3.805531055898412e-5 |
| 154 | + 1.6890846360940058e-5 |
| 155 | + -4.5403814886370545e-5 |
| 156 | + -4.121425310424971e-5 |
| 157 | + 1.2245471085512564e-5 |
| 158 | + 6.482348697494439e-5 |
| 159 | + 8.896755347867799e-5 |
| 160 | + 9.703404969825733e-5 |
| 161 | + 0.0001290654548866452 |
| 162 | + 0.00023543152355538763 |
| 163 | + 0.0004663267687150117 |
| 164 | + ⋮ |
| 165 | + 0.0 |
| 166 | + 0.0 |
| 167 | + 0.0 |
| 168 | + 0.0 |
| 169 | + 0.0 |
| 170 | + 0.0 |
| 171 | + 0.0 |
| 172 | + 0.0 |
| 173 | + 0.0 |
| 174 | + 0.0 |
| 175 | + |
| 176 | +julia> basis_transform(C, A, c) # Or transform without forming matrix |
| 177 | +90-element Vector{Float64}: |
| 178 | + 3.80553105589841e-5 |
| 179 | + 1.6890846360940065e-5 |
| 180 | + -4.540381488637052e-5 |
| 181 | + -4.121425310424972e-5 |
| 182 | + 1.2245471085512601e-5 |
| 183 | + 6.482348697494439e-5 |
| 184 | + 8.896755347867799e-5 |
| 185 | + 9.703404969825729e-5 |
| 186 | + 0.00012906545488664526 |
| 187 | + 0.0002354315235553877 |
| 188 | + 0.00046632676871501176 |
| 189 | + ⋮ |
| 190 | + 0.0 |
| 191 | + 0.0 |
| 192 | + 0.0 |
| 193 | + 0.0 |
| 194 | + 0.0 |
| 195 | + 0.0 |
| 196 | + 0.0 |
| 197 | + 0.0 |
| 198 | + 0.0 |
| 199 | + 0.0 |
| 200 | +``` |
| 201 | + |
| 202 | +In the plots below, we show each basis (labelled ``A-E``), and their |
| 203 | +respective reconstructions of ``f_1`` and ``f_2`` as solid red lines, |
| 204 | +with the true functions shown as dashed black lines. For each |
| 205 | +basis–function combination, shown are also the expansion coefficients |
| 206 | +as red points joined by solid lines, as well as the expansion |
| 207 | +coefficients transformed from the other bases, as labelled in the |
| 208 | +legends. As an example, we see that first expanding in |
| 209 | +finite-differences ``D`` and then transforming to B-splines ``A`` is |
| 210 | +numerically unstable, leading to very large coefficients at the edges |
| 211 | +of ``D``. Going _to_ finite-differences is always stable, even though |
| 212 | +the results may not be accurate. |
| 213 | + |
| 214 | + |
| 215 | + |
| 216 | +## Reference |
| 217 | + |
| 218 | +```@docs |
| 219 | +basis_overlap |
| 220 | +basis_transform |
| 221 | +``` |
0 commit comments