poulpy/poulpy-backend/src/cpu_spqlios/spqlios-arithmetic/spqlios/cplx/cplx_ifft_ref.c
poulpy-backend/src/cpu_spqlios/spqlios-arithmetic/spqlios/cplx/cplx_ifft_ref.cRaw
1#include <memory.h>
2
3#include "../commons_private.h"
4#include "cplx_fft.h"
5#include "cplx_fft_internal.h"
6#include "cplx_fft_private.h"
7
8/** @brief (a,b) <- (a+b,omegabar.(a-b)) */
9void invctwiddle(CPLX a, CPLX b, const CPLX ombar) {
10 double diffre = a[0] - b[0];
11 double diffim = a[1] - b[1];
12 a[0] = a[0] + b[0];
13 a[1] = a[1] + b[1];
14 b[0] = diffre * ombar[0] - diffim * ombar[1];
15 b[1] = diffre * ombar[1] + diffim * ombar[0];
16}
17
18/** @brief (a,b) <- (a+b,-i.omegabar(a-b)) */
19void invcitwiddle(CPLX a, CPLX b, const CPLX ombar) {
20 double diffre = a[0] - b[0];
21 double diffim = a[1] - b[1];
22 a[0] = a[0] + b[0];
23 a[1] = a[1] + b[1];
24 //-i(x+iy)=-ix+y
25 b[0] = diffre * ombar[1] + diffim * ombar[0];
26 b[1] = -diffre * ombar[0] + diffim * ombar[1];
27}
28
29/** @brief exp(-i.2pi.x) */
30void cplx_set_e2pimx(CPLX res, double x) {
31 res[0] = m_accurate_cos(2 * M_PI * x);
32 res[1] = -m_accurate_sin(2 * M_PI * x);
33}
34/**
35 * @brief this computes the sequence: 0,1/2,1/4,3/4,1/8,5/8,3/8,7/8,...
36 * essentially: the bits of (i+1) in lsb order on the basis (1/2^k) mod 1*/
37double fracrevbits(uint32_t i);
38/** @brief fft modulo X^m-exp(i.2pi.entry+pwr) -- reference code */
39void cplx_ifft_naive(const uint32_t m, const double entry_pwr, CPLX* data) {
40 if (m == 1) return;
41 const double pom = entry_pwr / 2.;
42 const uint32_t h = m / 2;
43 CPLX cpom;
44 cplx_set_e2pimx(cpom, pom);
45 // do the recursive calls
46 cplx_ifft_naive(h, pom, data);
47 cplx_ifft_naive(h, pom + 0.5, data + h);
48 // apply the inverse twiddle factors
49 for (uint64_t i = 0; i < h; ++i) {
50 invctwiddle(data[i], data[i + h], cpom);
51 }
52}
53
54void cplx_ifft16_precomp(const double entry_pwr, CPLX** omg) {
55 static const double j_pow = 1. / 8.;
56 static const double k_pow = 1. / 16.;
57 const double pom = entry_pwr / 2.;
58 const double pom_2 = entry_pwr / 4.;
59 const double pom_4 = entry_pwr / 8.;
60 const double pom_8 = entry_pwr / 16.;
61 cplx_set_e2pimx((*omg)[0], pom_8);
62 cplx_set_e2pimx((*omg)[1], pom_8 + j_pow);
63 cplx_set_e2pimx((*omg)[2], pom_8 + k_pow);
64 cplx_set_e2pimx((*omg)[3], pom_8 + j_pow + k_pow);
65 cplx_set_e2pimx((*omg)[4], pom_4);
66 cplx_set_e2pimx((*omg)[5], pom_4 + j_pow);
67 cplx_set_e2pimx((*omg)[6], pom_2);
68 cplx_set_e2pimx((*omg)[7], pom);
69 *omg += 8;
70}
71
72/**
73 * @brief iFFT modulo X^16-omega^2 (in registers)
74 * @param data contains 16 complexes
75 * @param omegabar 8 complexes in this order:
76 * gammabar,jb.gammabar,kb.gammabar,kbjb.gammabar,
77 * betabar,jb.betabar,alphabar,omegabar
78 * alpha = sqrt(omega), beta = sqrt(alpha), gamma = sqrt(beta)
79 * jb = sqrt(ib), kb=sqrt(jb)
80 */
81void cplx_ifft16_ref(void* data, const void* omegabar) {
82 CPLX* d = data;
83 const CPLX* om = omegabar;
84 // fourth pass inverse
85 invctwiddle(d[0], d[1], om[0]);
86 invcitwiddle(d[2], d[3], om[0]);
87 invctwiddle(d[4], d[5], om[1]);
88 invcitwiddle(d[6], d[7], om[1]);
89 invctwiddle(d[8], d[9], om[2]);
90 invcitwiddle(d[10], d[11], om[2]);
91 invctwiddle(d[12], d[13], om[3]);
92 invcitwiddle(d[14], d[15], om[3]);
93 // third pass inverse
94 invctwiddle(d[0], d[2], om[4]);
95 invctwiddle(d[1], d[3], om[4]);
96 invcitwiddle(d[4], d[6], om[4]);
97 invcitwiddle(d[5], d[7], om[4]);
98 invctwiddle(d[8], d[10], om[5]);
99 invctwiddle(d[9], d[11], om[5]);
100 invcitwiddle(d[12], d[14], om[5]);
101 invcitwiddle(d[13], d[15], om[5]);
102 // second pass inverse
103 invctwiddle(d[0], d[4], om[6]);
104 invctwiddle(d[1], d[5], om[6]);
105 invctwiddle(d[2], d[6], om[6]);
106 invctwiddle(d[3], d[7], om[6]);
107 invcitwiddle(d[8], d[12], om[6]);
108 invcitwiddle(d[9], d[13], om[6]);
109 invcitwiddle(d[10], d[14], om[6]);
110 invcitwiddle(d[11], d[15], om[6]);
111 // first pass
112 for (uint64_t i = 0; i < 8; ++i) {
113 invctwiddle(d[0 + i], d[8 + i], om[7]);
114 }
115}
116
117void cplx_ifft_ref_bfs_2(CPLX* dat, const CPLX** omg, uint32_t m) {
118 CPLX* const dend = dat + m;
119 for (CPLX* d = dat; d < dend; d += 2) {
120 split_fft_last_ref(d, (*omg)[0]);
121 *omg += 1;
122 }
123#if 0
124 printf("after first: ");
125 for (uint64_t ii=0; ii<nn/2; ++ii) {
126 printf("%.6lf %.6lf ",data[ii][0],data[ii][1]);
127 }
128 printf("\n");
129#endif
130 int32_t Ms2 = m / 2;
131 for (int32_t h = 2; h <= Ms2; h <<= 1) {
132 for (CPLX* d = dat; d < dend; d += 2 * h) {
133 if (memcmp((*omg)[0], (*omg)[1], 8) != 0) abort();
134 cplx_split_fft_ref(h, d, **omg);
135 *omg += 2;
136 }
137#if 0
138 printf("after split %d: ", h);
139 for (uint64_t ii=0; ii<nn/2; ++ii) {
140 printf("%.6lf %.6lf ",data[ii][0],data[ii][1]);
141 }
142 printf("\n");
143#endif
144 }
145}
146
147void cplx_ifft_ref_bfs_16(CPLX* dat, const CPLX** omg, uint32_t m) {
148 const uint64_t log2m = log2(m);
149 CPLX* const dend = dat + m;
150 // h=1,2,4,8 use the 16-dim macroblock
151 for (CPLX* d = dat; d < dend; d += 16) {
152 cplx_ifft16_ref(d, *omg);
153 *omg += 8;
154 }
155#if 0
156 printf("after first: ");
157 for (uint64_t ii=0; ii<nn/2; ++ii) {
158 printf("%.6lf %.6lf ",data[ii][0],data[ii][1]);
159 }
160 printf("\n");
161#endif
162 int32_t h = 16;
163 if (log2m % 2 != 0) {
164 // if parity needs it, uses one regular twiddle
165 for (CPLX* d = dat; d < dend; d += 2 * h) {
166 cplx_split_fft_ref(h, d, **omg);
167 *omg += 1;
168 }
169 h = 32;
170 }
171 // h=16,...,2*floor(Ms2/2) use the bitwiddle
172 for (; h < m; h <<= 2) {
173 for (CPLX* d = dat; d < dend; d += 4 * h) {
174 cplx_bisplit_fft_ref(h, d, *omg);
175 *omg += 2;
176 }
177#if 0
178 printf("after split %d: ", h);
179 for (uint64_t ii=0; ii<nn/2; ++ii) {
180 printf("%.6lf %.6lf ",data[ii][0],data[ii][1]);
181 }
182 printf("\n");
183#endif
184 }
185}
186
187void fill_cplx_ifft_omegas_bfs_16(const double entry_pwr, CPLX** omg, uint32_t m) {
188 const uint64_t log2m = log2(m);
189 double pwr = entry_pwr * 16. / m;
190 {
191 // h=8
192 for (uint32_t i = 0; i < m / 16; i++) {
193 cplx_ifft16_precomp(pwr + fracrevbits(i), omg);
194 }
195 }
196 int32_t h = 16;
197 if (log2m % 2 != 0) {
198 // if parity needs it, uses one regular twiddle
199 for (uint32_t i = 0; i < m / (2 * h); i++) {
200 cplx_set_e2pimx(omg[0][0], pwr + fracrevbits(i) / 2.);
201 *omg += 1;
202 }
203 pwr *= 2.;
204 h = 32;
205 }
206 for (; h < m; h <<= 2) {
207 for (uint32_t i = 0; i < m / (2 * h); i += 2) {
208 cplx_set_e2pimx(omg[0][0], pwr + fracrevbits(i) / 2.);
209 cplx_set_e2pimx(omg[0][1], 2. * pwr + fracrevbits(i));
210 *omg += 2;
211 }
212 pwr *= 4.;
213 }
214}
215
216void fill_cplx_ifft_omegas_bfs_2(const double entry_pwr, CPLX** omg, uint32_t m) {
217 double pom = entry_pwr / m;
218 {
219 // h=1
220 for (uint32_t i = 0; i < m / 2; i++) {
221 cplx_set_e2pimx((*omg)[0], pom + fracrevbits(i) / 2.);
222 *omg += 1; // optim function reads by 1
223 }
224 }
225 for (int32_t h = 2; h <= m / 2; h <<= 1) {
226 pom *= 2;
227 for (uint32_t i = 0; i < m / (2 * h); i++) {
228 cplx_set_e2pimx(omg[0][0], pom + fracrevbits(i) / 2.);
229 cplx_set(omg[0][1], omg[0][0]);
230 *omg += 2;
231 }
232 }
233}
234
235void fill_cplx_ifft_omegas_rec_16(const double entry_pwr, CPLX** omg, uint32_t m) {
236 if (m == 1) return;
237 if (m <= 8) return fill_cplx_ifft_omegas_bfs_2(entry_pwr, omg, m);
238 if (m <= 2048) return fill_cplx_ifft_omegas_bfs_16(entry_pwr, omg, m);
239 double pom = entry_pwr / 2.;
240 uint32_t h = m / 2;
241 fill_cplx_ifft_omegas_rec_16(pom, omg, h);
242 fill_cplx_ifft_omegas_rec_16(pom + 0.5, omg, h);
243 cplx_set_e2pimx((*omg)[0], pom);
244 cplx_set((*omg)[1], (*omg)[0]);
245 *omg += 2;
246}
247
248void cplx_ifft_ref_rec_16(CPLX* dat, const CPLX** omg, uint32_t m) {
249 if (m == 1) return;
250 if (m <= 8) return cplx_ifft_ref_bfs_2(dat, omg, m);
251 if (m <= 2048) return cplx_ifft_ref_bfs_16(dat, omg, m);
252 const uint32_t h = m / 2;
253 cplx_ifft_ref_rec_16(dat, omg, h);
254 cplx_ifft_ref_rec_16(dat + h, omg, h);
255 if (memcmp((*omg)[0], (*omg)[1], 8) != 0) abort();
256 cplx_split_fft_ref(h, dat, **omg);
257 *omg += 2;
258}
259
260EXPORT void cplx_ifft_ref(const CPLX_IFFT_PRECOMP* precomp, void* d) {
261 CPLX* data = (CPLX*)d;
262 const int32_t m = precomp->m;
263 const CPLX* omg = (CPLX*)precomp->powomegas;
264 if (m == 1) return;
265 if (m <= 8) return cplx_ifft_ref_bfs_2(data, &omg, m);
266 if (m <= 2048) return cplx_ifft_ref_bfs_16(data, &omg, m);
267 cplx_ifft_ref_rec_16(data, &omg, m);
268}
269
270EXPORT CPLX_IFFT_PRECOMP* new_cplx_ifft_precomp(uint32_t m, uint32_t num_buffers) {
271 const uint64_t OMG_SPACE = ceilto64b(2 * m * sizeof(CPLX));
272 const uint64_t BUF_SIZE = ceilto64b(m * sizeof(CPLX));
273 void* reps = malloc(sizeof(CPLX_IFFT_PRECOMP) + 63 // padding
274 + OMG_SPACE // tables
275 + num_buffers * BUF_SIZE // buffers
276 );
277 uint64_t aligned_addr = ceilto64b((uint64_t)reps + sizeof(CPLX_IFFT_PRECOMP));
278 CPLX_IFFT_PRECOMP* r = (CPLX_IFFT_PRECOMP*)reps;
279 r->m = m;
280 r->buf_size = BUF_SIZE;
281 r->powomegas = (double*)aligned_addr;
282 r->aligned_buffers = (void*)(aligned_addr + OMG_SPACE);
283 // fill in powomegas
284 CPLX* omg = (CPLX*)r->powomegas;
285 fill_cplx_ifft_omegas_rec_16(0.25, &omg, m);
286 if (((uint64_t)omg) - aligned_addr > OMG_SPACE) abort();
287 {
288 if (m <= 4) {
289 // currently, we do not have any acceletated
290 // implementation for m<=4
291 r->function = cplx_ifft_ref;
292 } else if (CPU_SUPPORTS("fma")) {
293 r->function = cplx_ifft_avx2_fma;
294 } else {
295 r->function = cplx_ifft_ref;
296 }
297 }
298 return reps;
299}
300
301EXPORT void* cplx_ifft_precomp_get_buffer(const CPLX_IFFT_PRECOMP* itables, uint32_t buffer_index) {
302 return (uint8_t*)itables->aligned_buffers + buffer_index * itables->buf_size;
303}
304
305EXPORT void cplx_ifft(const CPLX_IFFT_PRECOMP* itables, void* data) { itables->function(itables, data); }
306
307EXPORT void cplx_ifft_simple(uint32_t m, void* data) {
308 static CPLX_IFFT_PRECOMP* p[31] = {0};
309 CPLX_IFFT_PRECOMP** f = p + log2m(m);
310 if (!*f) *f = new_cplx_ifft_precomp(m, 0);
311 (*f)->function(*f, data);
312}