Eigen-unsupported  5.0.1-dev+284dcc12
 
Loading...
Searching...
No Matches
kissfft_impl.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2009 Mark Borgerding mark a borgerding net
5//
6// This Source Code Form is subject to the terms of the Mozilla
7// Public License v. 2.0. If a copy of the MPL was not distributed
8// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9
10// IWYU pragma: private
11#include "./InternalHeaderCheck.h"
12
13namespace Eigen {
14
15namespace internal {
16
17// This FFT implementation was derived from kissfft http:sourceforge.net/projects/kissfft
18// Copyright 2003-2009 Mark Borgerding
19
20template <typename Scalar_>
21struct kiss_cpx_fft {
22 typedef Scalar_ Scalar;
23 typedef std::complex<Scalar> Complex;
24 std::vector<Complex> m_twiddles;
25 std::vector<int> m_stageRadix;
26 std::vector<int> m_stageRemainder;
27 std::vector<Complex> m_scratchBuf;
28 bool m_inverse;
29
30 static const Scalar m_pi4; // constant pi / 4
31
32 inline void make_twiddles(int nfft, bool inverse) {
33 using numext::cos;
34 using numext::sin;
35 m_inverse = inverse;
36 m_twiddles.resize(nfft);
37 Scalar phinc = m_pi4 / nfft;
38 Scalar flip = inverse ? Scalar(1) : Scalar(-1);
39 m_twiddles[0] = Complex(Scalar(1), Scalar(0));
40 if ((nfft & 1) == 0) m_twiddles[nfft / 2] = Complex(Scalar(-1), Scalar(0));
41 int i = 1;
42 for (; i * 8 < nfft; ++i) {
43 Scalar c = Scalar(cos(i * 8 * phinc));
44 Scalar s = Scalar(sin(i * 8 * phinc));
45 m_twiddles[i] = Complex(c, s * flip);
46 m_twiddles[nfft - i] = Complex(c, -s * flip);
47 }
48 for (; i * 4 < nfft; ++i) {
49 Scalar c = Scalar(cos((2 * nfft - 8 * i) * phinc));
50 Scalar s = Scalar(sin((2 * nfft - 8 * i) * phinc));
51 m_twiddles[i] = Complex(s, c * flip);
52 m_twiddles[nfft - i] = Complex(s, -c * flip);
53 }
54 for (; i * 8 < 3 * nfft; ++i) {
55 Scalar c = Scalar(cos((8 * i - 2 * nfft) * phinc));
56 Scalar s = Scalar(sin((8 * i - 2 * nfft) * phinc));
57 m_twiddles[i] = Complex(-s, c * flip);
58 m_twiddles[nfft - i] = Complex(-s, -c * flip);
59 }
60 for (; i * 2 < nfft; ++i) {
61 Scalar c = Scalar(cos((4 * nfft - 8 * i) * phinc));
62 Scalar s = Scalar(sin((4 * nfft - 8 * i) * phinc));
63 m_twiddles[i] = Complex(-c, s * flip);
64 m_twiddles[nfft - i] = Complex(-c, -s * flip);
65 }
66 }
67
68 void factorize(int nfft) {
69 // start factoring out 4's, then 2's, then 3,5,7,9,...
70 int n = nfft;
71 int p = 4;
72 do {
73 while (n % p) {
74 switch (p) {
75 case 4:
76 p = 2;
77 break;
78 case 2:
79 p = 3;
80 break;
81 default:
82 p += 2;
83 break;
84 }
85 if (p * p > n) p = n; // impossible to have a factor > sqrt(n)
86 }
87 n /= p;
88 m_stageRadix.push_back(p);
89 m_stageRemainder.push_back(n);
90 if (p > 5) m_scratchBuf.resize(p); // scratchbuf will be needed in bfly_generic
91 } while (n > 1);
92 }
93
94 template <typename Src_>
95 inline void work(int stage, Complex *xout, const Src_ *xin, size_t fstride, size_t in_stride) {
96 int p = m_stageRadix[stage];
97 int m = m_stageRemainder[stage];
98 Complex *Fout_beg = xout;
99 Complex *Fout_end = xout + p * m;
100
101 if (m > 1) {
102 do {
103 // recursive call:
104 // DFT of size m*p performed by doing
105 // p instances of smaller DFTs of size m,
106 // each one takes a decimated version of the input
107 work(stage + 1, xout, xin, fstride * p, in_stride);
108 xin += fstride * in_stride;
109 } while ((xout += m) != Fout_end);
110 } else {
111 do {
112 *xout = *xin;
113 xin += fstride * in_stride;
114 } while (++xout != Fout_end);
115 }
116 xout = Fout_beg;
117
118 // recombine the p smaller DFTs
119 switch (p) {
120 case 2:
121 bfly2(xout, fstride, m);
122 break;
123 case 3:
124 bfly3(xout, fstride, m);
125 break;
126 case 4:
127 bfly4(xout, fstride, m);
128 break;
129 case 5:
130 bfly5(xout, fstride, m);
131 break;
132 default:
133 bfly_generic(xout, fstride, m, p);
134 break;
135 }
136 }
137
138 inline void bfly2(Complex *Fout, const size_t fstride, int m) {
139 for (int k = 0; k < m; ++k) {
140 Complex t = Fout[m + k] * m_twiddles[k * fstride];
141 Fout[m + k] = Fout[k] - t;
142 Fout[k] += t;
143 }
144 }
145
146 inline void bfly4(Complex *Fout, const size_t fstride, const size_t m) {
147 Complex scratch[6];
148 int negative_if_inverse = m_inverse * -2 + 1;
149 for (size_t k = 0; k < m; ++k) {
150 scratch[0] = Fout[k + m] * m_twiddles[k * fstride];
151 scratch[1] = Fout[k + 2 * m] * m_twiddles[k * fstride * 2];
152 scratch[2] = Fout[k + 3 * m] * m_twiddles[k * fstride * 3];
153 scratch[5] = Fout[k] - scratch[1];
154
155 Fout[k] += scratch[1];
156 scratch[3] = scratch[0] + scratch[2];
157 scratch[4] = scratch[0] - scratch[2];
158 scratch[4] = Complex(scratch[4].imag() * negative_if_inverse, -scratch[4].real() * negative_if_inverse);
159
160 Fout[k + 2 * m] = Fout[k] - scratch[3];
161 Fout[k] += scratch[3];
162 Fout[k + m] = scratch[5] + scratch[4];
163 Fout[k + 3 * m] = scratch[5] - scratch[4];
164 }
165 }
166
167 inline void bfly3(Complex *Fout, const size_t fstride, const size_t m) {
168 size_t k = m;
169 const size_t m2 = 2 * m;
170 Complex *tw1, *tw2;
171 Complex scratch[5];
172 Complex epi3;
173 epi3 = m_twiddles[fstride * m];
174
175 tw1 = tw2 = &m_twiddles[0];
176
177 do {
178 scratch[1] = Fout[m] * *tw1;
179 scratch[2] = Fout[m2] * *tw2;
180
181 scratch[3] = scratch[1] + scratch[2];
182 scratch[0] = scratch[1] - scratch[2];
183 tw1 += fstride;
184 tw2 += fstride * 2;
185 Fout[m] = Complex(Fout->real() - Scalar(.5) * scratch[3].real(), Fout->imag() - Scalar(.5) * scratch[3].imag());
186 scratch[0] *= epi3.imag();
187 *Fout += scratch[3];
188 Fout[m2] = Complex(Fout[m].real() + scratch[0].imag(), Fout[m].imag() - scratch[0].real());
189 Fout[m] += Complex(-scratch[0].imag(), scratch[0].real());
190 ++Fout;
191 } while (--k);
192 }
193
194 inline void bfly5(Complex *Fout, const size_t fstride, const size_t m) {
195 Complex *Fout0, *Fout1, *Fout2, *Fout3, *Fout4;
196 size_t u;
197 Complex scratch[13];
198 Complex *twiddles = &m_twiddles[0];
199 Complex *tw;
200 Complex ya, yb;
201 ya = twiddles[fstride * m];
202 yb = twiddles[fstride * 2 * m];
203
204 Fout0 = Fout;
205 Fout1 = Fout0 + m;
206 Fout2 = Fout0 + 2 * m;
207 Fout3 = Fout0 + 3 * m;
208 Fout4 = Fout0 + 4 * m;
209
210 tw = twiddles;
211 for (u = 0; u < m; ++u) {
212 scratch[0] = *Fout0;
213
214 scratch[1] = *Fout1 * tw[u * fstride];
215 scratch[2] = *Fout2 * tw[2 * u * fstride];
216 scratch[3] = *Fout3 * tw[3 * u * fstride];
217 scratch[4] = *Fout4 * tw[4 * u * fstride];
218
219 scratch[7] = scratch[1] + scratch[4];
220 scratch[10] = scratch[1] - scratch[4];
221 scratch[8] = scratch[2] + scratch[3];
222 scratch[9] = scratch[2] - scratch[3];
223
224 *Fout0 += scratch[7];
225 *Fout0 += scratch[8];
226
227 scratch[5] = scratch[0] + Complex((scratch[7].real() * ya.real()) + (scratch[8].real() * yb.real()),
228 (scratch[7].imag() * ya.real()) + (scratch[8].imag() * yb.real()));
229
230 scratch[6] = Complex((scratch[10].imag() * ya.imag()) + (scratch[9].imag() * yb.imag()),
231 -(scratch[10].real() * ya.imag()) - (scratch[9].real() * yb.imag()));
232
233 *Fout1 = scratch[5] - scratch[6];
234 *Fout4 = scratch[5] + scratch[6];
235
236 scratch[11] = scratch[0] + Complex((scratch[7].real() * yb.real()) + (scratch[8].real() * ya.real()),
237 (scratch[7].imag() * yb.real()) + (scratch[8].imag() * ya.real()));
238
239 scratch[12] = Complex(-(scratch[10].imag() * yb.imag()) + (scratch[9].imag() * ya.imag()),
240 (scratch[10].real() * yb.imag()) - (scratch[9].real() * ya.imag()));
241
242 *Fout2 = scratch[11] + scratch[12];
243 *Fout3 = scratch[11] - scratch[12];
244
245 ++Fout0;
246 ++Fout1;
247 ++Fout2;
248 ++Fout3;
249 ++Fout4;
250 }
251 }
252
253 /* perform the butterfly for one stage of a mixed radix FFT */
254 inline void bfly_generic(Complex *Fout, const size_t fstride, int m, int p) {
255 int u, k, q1, q;
256 Complex *twiddles = &m_twiddles[0];
257 Complex t;
258 int Norig = static_cast<int>(m_twiddles.size());
259 Complex *scratchbuf = &m_scratchBuf[0];
260
261 for (u = 0; u < m; ++u) {
262 k = u;
263 for (q1 = 0; q1 < p; ++q1) {
264 scratchbuf[q1] = Fout[k];
265 k += m;
266 }
267
268 k = u;
269 for (q1 = 0; q1 < p; ++q1) {
270 int twidx = 0;
271 Fout[k] = scratchbuf[0];
272 for (q = 1; q < p; ++q) {
273 twidx += static_cast<int>(fstride) * k;
274 if (twidx >= Norig) twidx -= Norig;
275 t = scratchbuf[q] * twiddles[twidx];
276 Fout[k] += t;
277 }
278 k += m;
279 }
280 }
281 }
282};
283
284template <typename _Scalar>
285const typename kiss_cpx_fft<_Scalar>::Scalar kiss_cpx_fft<_Scalar>::m_pi4 =
286 numext::atan(kiss_cpx_fft<_Scalar>::Scalar(1));
287
288template <typename Scalar_>
289struct kissfft_impl {
290 typedef Scalar_ Scalar;
291 typedef std::complex<Scalar> Complex;
292
293 void clear() {
294 m_plans.clear();
295 m_realTwiddles.clear();
296 }
297
298 inline void fwd(Complex *dst, const Complex *src, int nfft) { get_plan(nfft, false).work(0, dst, src, 1, 1); }
299
300 inline void fwd2(Complex *dst, const Complex *src, int n0, int n1) {
301 EIGEN_UNUSED_VARIABLE(dst);
302 EIGEN_UNUSED_VARIABLE(src);
303 EIGEN_UNUSED_VARIABLE(n0);
304 EIGEN_UNUSED_VARIABLE(n1);
305 }
306
307 inline void inv2(Complex *dst, const Complex *src, int n0, int n1) {
308 EIGEN_UNUSED_VARIABLE(dst);
309 EIGEN_UNUSED_VARIABLE(src);
310 EIGEN_UNUSED_VARIABLE(n0);
311 EIGEN_UNUSED_VARIABLE(n1);
312 }
313
314 // real-to-complex forward FFT
315 // perform two FFTs of src even and src odd
316 // then twiddle to recombine them into the half-spectrum format
317 // then fill in the conjugate symmetric half
318 inline void fwd(Complex *dst, const Scalar *src, int nfft) {
319 if (nfft & 3) {
320 // use generic mode for odd
321 m_tmpBuf1.resize(nfft);
322 get_plan(nfft, false).work(0, &m_tmpBuf1[0], src, 1, 1);
323 std::copy(m_tmpBuf1.begin(), m_tmpBuf1.begin() + (nfft >> 1) + 1, dst);
324 } else {
325 int ncfft = nfft >> 1;
326 int ncfft2 = nfft >> 2;
327 Complex *rtw = real_twiddles(ncfft2);
328
329 // use optimized mode for even real
330 fwd(dst, reinterpret_cast<const Complex *>(src), ncfft);
331 Complex dc(dst[0].real() + dst[0].imag());
332 Complex nyquist(dst[0].real() - dst[0].imag());
333 int k;
334 for (k = 1; k <= ncfft2; ++k) {
335 Complex fpk = dst[k];
336 Complex fpnk = conj(dst[ncfft - k]);
337 Complex f1k = fpk + fpnk;
338 Complex f2k = fpk - fpnk;
339 Complex tw = f2k * rtw[k - 1];
340 dst[k] = (f1k + tw) * Scalar(.5);
341 dst[ncfft - k] = conj(f1k - tw) * Scalar(.5);
342 }
343 dst[0] = dc;
344 dst[ncfft] = nyquist;
345 }
346 }
347
348 // inverse complex-to-complex
349 inline void inv(Complex *dst, const Complex *src, int nfft) { get_plan(nfft, true).work(0, dst, src, 1, 1); }
350
351 // half-complex to scalar
352 inline void inv(Scalar *dst, const Complex *src, int nfft) {
353 if (nfft & 3) {
354 m_tmpBuf1.resize(nfft);
355 m_tmpBuf2.resize(nfft);
356 std::copy(src, src + (nfft >> 1) + 1, m_tmpBuf1.begin());
357 for (int k = 1; k < (nfft >> 1) + 1; ++k) m_tmpBuf1[nfft - k] = conj(m_tmpBuf1[k]);
358 inv(&m_tmpBuf2[0], &m_tmpBuf1[0], nfft);
359 for (int k = 0; k < nfft; ++k) dst[k] = m_tmpBuf2[k].real();
360 } else {
361 // optimized version for multiple of 4
362 int ncfft = nfft >> 1;
363 int ncfft2 = nfft >> 2;
364 Complex *rtw = real_twiddles(ncfft2);
365 m_tmpBuf1.resize(ncfft);
366 m_tmpBuf1[0] = Complex(src[0].real() + src[ncfft].real(), src[0].real() - src[ncfft].real());
367 for (int k = 1; k <= ncfft / 2; ++k) {
368 Complex fk = src[k];
369 Complex fnkc = conj(src[ncfft - k]);
370 Complex fek = fk + fnkc;
371 Complex tmp = fk - fnkc;
372 Complex fok = tmp * conj(rtw[k - 1]);
373 m_tmpBuf1[k] = fek + fok;
374 m_tmpBuf1[ncfft - k] = conj(fek - fok);
375 }
376 get_plan(ncfft, true).work(0, reinterpret_cast<Complex *>(dst), &m_tmpBuf1[0], 1, 1);
377 }
378 }
379
380 protected:
381 typedef kiss_cpx_fft<Scalar> PlanData;
382 typedef std::map<int, PlanData> PlanMap;
383
384 PlanMap m_plans;
385 std::map<int, std::vector<Complex> > m_realTwiddles;
386 std::vector<Complex> m_tmpBuf1;
387 std::vector<Complex> m_tmpBuf2;
388
389 inline int PlanKey(int nfft, bool isinverse) const { return (nfft << 1) | int(isinverse); }
390
391 inline PlanData &get_plan(int nfft, bool inverse) {
392 // TODO look for PlanKey(nfft, ! inverse) and conjugate the twiddles
393 PlanData &pd = m_plans[PlanKey(nfft, inverse)];
394 if (pd.m_twiddles.size() == 0) {
395 pd.make_twiddles(nfft, inverse);
396 pd.factorize(nfft);
397 }
398 return pd;
399 }
400
401 inline Complex *real_twiddles(int ncfft2) {
402 using std::acos;
403 std::vector<Complex> &twidref = m_realTwiddles[ncfft2]; // creates new if not there
404 if ((int)twidref.size() != ncfft2) {
405 twidref.resize(ncfft2);
406 int ncfft = ncfft2 << 1;
407 Scalar pi = acos(Scalar(-1));
408 for (int k = 1; k <= ncfft2; ++k) twidref[k - 1] = exp(Complex(0, -pi * (Scalar(k) / ncfft + Scalar(.5))));
409 }
410 return &twidref[0];
411 }
412};
413
414} // end namespace internal
415
416} // end namespace Eigen
Namespace containing all symbols from the Eigen library.
const Eigen::CwiseUnaryOp< Eigen::internal::scalar_cos_op< typename Derived::Scalar >, const Derived > cos(const Eigen::ArrayBase< Derived > &x)
const Eigen::CwiseUnaryOp< Eigen::internal::scalar_real_op< typename Derived::Scalar >, const Derived > real(const Eigen::ArrayBase< Derived > &x)
const Eigen::CwiseUnaryOp< Eigen::internal::scalar_inverse_op< typename Derived::Scalar >, const Derived > inverse(const Eigen::ArrayBase< Derived > &x)
const Eigen::CwiseUnaryOp< Eigen::internal::scalar_exp_op< typename Derived::Scalar >, const Derived > exp(const Eigen::ArrayBase< Derived > &x)
const Eigen::CwiseUnaryOp< Eigen::internal::scalar_conjugate_op< typename Derived::Scalar >, const Derived > conj(const Eigen::ArrayBase< Derived > &x)
const Eigen::CwiseUnaryOp< Eigen::internal::scalar_imag_op< typename Derived::Scalar >, const Derived > imag(const Eigen::ArrayBase< Derived > &x)
const Eigen::CwiseUnaryOp< Eigen::internal::scalar_sin_op< typename Derived::Scalar >, const Derived > sin(const Eigen::ArrayBase< Derived > &x)
const Eigen::CwiseUnaryOp< Eigen::internal::scalar_acos_op< typename Derived::Scalar >, const Derived > acos(const Eigen::ArrayBase< Derived > &x)