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