In [1]:
import Graphics.Dynamic.Plot.R2
import Data.Complex
import Data.Monoid
We want to express the following series transformation $\operatorname{BFT}: \mathbb{C}^\mathbb{N} \to \mathbb{R}^\mathbb{R} , x\mapsto y$: $$ y(t) = \sum_{k=0}^\infty \left( \Re{x_k} \cdot \cos ((k+\tfrac12)\cdot \pi \cdot t)
- \Im{x_k} \cdot \sin ((k+\tfrac12)\cdot \pi \cdot t) \right).
$$
In [2]:
plotWindow $ [ plot $ continFnPlot . (.((k+1/2)*pi*))<$>[cos, negate . sin]
| k <- [0..3] ]
++ [forceXRange (-0.5,0.5)]
In [3]:
myFourier :: [Complex Double] -> Double->Double
myFourier xs t = sum [ rx * cos ((k+1/2)*pi*t) + ix * sin ((k+1/2)*pi*t)
| (k, rx:+ix) <- zip [0..] xs ]
Using $(\cos,i\sin) \varphi = \tfrac12 (e^{i\cdot\varphi} \pm e^{-i\cdot\varphi})$, $$\begin{align} y(t) =& \tfrac12 \cdot \sum_{k=0}^\infty \left( \Re{x_k} \cdot (e^{i\pi\cdot(k+\tfrac12)\cdot t} + e^{-i\pi\cdot(k+\tfrac12)\cdot t})
- i\Im{x_k} \cdot (e^{i\pi\cdot(k+\tfrac12)\cdot t} - e^{-i\pi\cdot(k+\tfrac12)\cdot t}) \right)
\=& \tfrac12 \cdot \sum_{k=0}^\infty \left( (\Re{x_k} - i\Im{x_i}) \cdot e^{i\pi\cdot(k+\tfrac12)\cdot t}
+ (\Re{x_k} + i\Im{x_i}) \cdot e^{-i\pi\cdot(k+\tfrac12)\cdot t}
\right)
\=& \sum_{k=0}^\infty \Re\left( xk \cdot (e^{i\pi\cdot(k+\tfrac12)\cdot t})^\ast \right) \=& \Re\left( e^{-i\frac{\pi\cdot t}2} \sum{k=0}^\infty x_k \cdot e^{-i\pi\cdot k\cdot t} \right) \end{align}$$
In [4]:
-- myFourier xs t = realPart $ cis (-pi*t/2) * sum [x * cis (-pi*k*t) | (k,x) <- zip [0..] xs]
Discretised over points $-1+\tfrac1n , -1 + \tfrac3n \ldots 1 - \tfrac1{n}$ (midpoints of $n$ subdivisions of the interval $[-1,1]$), i.e. $$\begin{align} t_j = -1 + \tfrac{1+2\cdot j}{n} = \tfrac{1-n}{n} + \tfrac{2\cdot j}{n} \end{align}$$ we get $$\begin{align} y_j =& \Re\left( e^{-\tfrac{i\pi}2\cdot t_j} \sum_{k=0}^{n/2 - 1} x_k \cdot e^{-i\pi\cdot k\cdot \left(\tfrac{1-n}{n} + \tfrac{2\cdot j}{n}\right)} \right) \\=& \Re\left( e^{-\tfrac{i\pi}2\cdot t_j} \sum_{k=0}^{n/2 - 1} x_k \cdot e^{-i\pi\cdot k\cdot\tfrac{1-n}{n}} \cdot e^{-2i\pi\cdot\tfrac{k\cdot j}{n}} \right) \\\equiv& \Re\left( \mu_j \sum_{k=0}^{n - 1} \xi_k \cdot e^{-2i\pi\cdot\tfrac{k\cdot j}{\tilde n}} \right) = \Re\left(\mu_j \cdot \operatorname{DFT}(\xi)_j\right) \end{align}$$
with $$ \xi_k = \begin{cases} x_k \cdot e^{-i\pi\cdot k\cdot\tfrac{1-n}{n}} & \text{for }k<\tfrac{n}2 \\ 0 & \text{else} \end{cases} $$ and $$ \mu_j = e^{-\tfrac{i\pi}2\cdot t_j}. $$ Thus, the transform can be implemented using FFT standards:
In [5]:
import Numeric.FFT.Vector.Invertible
import qualified Data.Vector.Storable as SArr
import qualified Data.Vector.Generic as Arr
myFFourier :: [Complex Double] -> [(Double,Double)]
myFFourier xs = [ (t, realPart $ μ*υ)
| (j,υ) <- zip [0..] $ SArr.toList (run dft ξs)
, let μ = cis $ -pi * t/2
t = ((1-n)/n + 2*j/n)]
where ξs = SArr.fromList $
[x * cis (-pi*k*(1-n)/n) | (k,x) <- zip [0..] xs]
++ (const 0<$>xs)
n = fromIntegral $ length xs * 2
In [6]:
someCoefs :: [Complex Double]
someCoefs = [0.4:+0.0, 0.6:+1.0, (-0.7):+(-0.6), 0.6:+(-0.9), 0.0:+1.0, 0.1:+0.1, (-0.6):+(-0.2), 0.7:+(-1.0)
, 0, 0, 0, 0 ]
In [7]:
plotWindow [ continFnPlot $ myFourier someCoefs
, lineSegPlot $ myFFourier someCoefs ]
To perform the inverse transform, we first need to reconstruct the discarded imaginary parts of $\mu_j \cdot \operatorname{DFT}(\xi)_j$, since that DFT actually operates on $\mathbb{C}^{n}$ rather than $\mathbb{R}^n$. This is possible because only $$ \partial_t \operatorname{BFT}(\rho)(t) = \operatorname{BFT}((i\pi\cdot (k+\tfrac12)\cdot \rho_k)_{(k)})(t) $$ while for real $\iota$ $$ \partial_t \operatorname{BFT}(i\cdot \iota)(t) = -\operatorname{BFT}((\pi\cdot (k+\tfrac12)\cdot \iota_k)_{(k)})(t). $$ We can sort out the real/imaginary parts since they correspond to even/odd components of the signal: for $x = \rho + i\cdot \iota$, $$\begin{align} \operatorname{BFT}(\rho)(t) = \tfrac12 (\operatorname{BFT}(x)(t) + \operatorname{BFT}(x)(-t) \\ \operatorname{BFT}(i\cdot\iota)(t) = \tfrac12 (\operatorname{BFT}(x)(t) - \operatorname{BFT}(x)(-t). \end{align}$$ Furthermore, it is $$\begin{align} \operatorname{BFT}(\rho)(t+1) = \tfrac12 (\operatorname{BFT}(x)(t) + \operatorname{BFT}(x)(-t) \\ \operatorname{BFT}(i\cdot\iota)(t) = \tfrac12 (\operatorname{BFT}(x)(t) - \operatorname{BFT}(x)(-t). \end{align}$$ The imaginary part of the (linear!) DFT must correspond to the real part of the complex-rotated coefficients;
In [8]:
myInvFFourier :: Int -> (Double -> Double) -> [Complex Double]
myInvFFourier n' f = [ 2 * cis (pi*k*(1-n)/n) * ξ
| (k,ξ) <- zip [0..n/2-1] $ SArr.toList ξs ]
where ts = SArr.generate n' $ \j' -> (1-n)/n + 2*fromIntegral j'/n
ys = SArr.map f ts
μinvs = SArr.map (\t -> cis $ pi*t/2) ts
ξs = run idft μinvys
μinvys = SArr.zipWith (*) μinvs $ SArr.map (:+0) ys
n = fromIntegral n'
In [9]:
f x = sin (exp $ 3*x) - x^2
--let ((μinvys, μinvðt'ys),_) = myInvFFourier 8 f
-- in plotWindow [ legendName (pn++"("++vn++")") . lineSegPlot . zip [0..] $ p<$>vs
-- | (vn,vs) <- [("μ⁻¹·y",μinvys), ("μ⁻¹·∂y/∂t",(2/pi*)<$>μinvðt'ys)]
-- , (pn,p) <- [("Re",realPart), ("Im",imagPart)] ]
plotWindow $ continFnPlot f
: [ legendName ("n="++show n) . lineSegPlot . myFFourier $ myInvFFourier n f
| n<-[4,8,16,32] ]
In [ ]: