Skip to content

Commit 82710db

Browse files
Add files via upload
1 parent 0de4751 commit 82710db

1 file changed

Lines changed: 333 additions & 0 deletions

File tree

src/detail/bruun_radix4_kernel.hpp

Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
#ifndef BFFT_BRUUN_RADIX4_KERNEL_HPP
2+
#define BFFT_BRUUN_RADIX4_KERNEL_HPP
3+
4+
// Experimental self-contained Bruun radix-4 forward kernel.
5+
//
6+
// Two forward paths:
7+
// forward_residues() – breadth-first, one norm_q per tree node
8+
// forward_residues_radix4() – depth-first, fused 2-level radix-4 nodes (norm2_fused)
9+
//
10+
// Both produce the same N-element residue vector as BFFT's forward_residues.
11+
// No SIMD – scalar reference only.
12+
13+
#include <cassert>
14+
#include <cmath>
15+
#include <cstdlib>
16+
#include <cstring>
17+
18+
namespace bruun_radix4 {
19+
20+
static inline bool is_pow2(int n) { return n > 0 && (n & (n - 1)) == 0; }
21+
static inline int ilog2(int n) { int l = 0; while (n > 1) { n >>= 1; ++l; } return l; }
22+
23+
static inline int gray_decode(int g) {
24+
for (int s = 1; s < 32; s <<= 1) g ^= g >> s;
25+
return g;
26+
}
27+
28+
static inline int bit_rev(int r, int t) {
29+
int out = 0;
30+
for (int i = 0; i < t; ++i) { out = (out << 1) | (r & 1); r >>= 1; }
31+
return out;
32+
}
33+
34+
static inline int bruun_idx(int m, int L) {
35+
int t = 0;
36+
for (int x = m; x > 1; x >>= 1) ++t;
37+
int r = m ^ (1 << t);
38+
return (2 * gray_decode(bit_rev(r, t)) + 1) << ((L - 2) - t);
39+
}
40+
41+
// ---------------------------------------------------------------------------
42+
// Chebyshev radix-4 even/odd split: 14q flops for q coefficient lanes.
43+
//
44+
// f(y) = a0 + a1 y + a2 y^2 + a3 y^3
45+
// E(y^2) = a0 + a2 y^2, O(y^2) = a1 + a3 y^2
46+
// f(±u) = E(u²) ± u O(u²), f(±v) = E(v²) ± v O(v²)
47+
// ---------------------------------------------------------------------------
48+
struct Cheb4Const { double u, u2, v, v2; };
49+
50+
static inline void chebyshev_radix4_split(
51+
const double* a0, const double* a1,
52+
const double* a2, const double* a3,
53+
double* pu, double* mu, double* pv, double* mv,
54+
int q, double u, double u2, double v, double v2)
55+
{
56+
for (int n = 0; n < q; ++n) {
57+
double eu = a0[n] + a2[n] * u2;
58+
double ou = a1[n] + a3[n] * u2;
59+
double sou = u * ou;
60+
double ev = a0[n] + a2[n] * v2;
61+
double ov = a1[n] + a3[n] * v2;
62+
double sov = v * ov;
63+
pu[n] = eu + sou;
64+
mu[n] = eu - sou;
65+
pv[n] = ev + sov;
66+
mv[n] = ev - sov;
67+
}
68+
}
69+
70+
// ---------------------------------------------------------------------------
71+
// RFFT_Radix4 – self-contained forward residue transform
72+
// ---------------------------------------------------------------------------
73+
class RFFT_Radix4 {
74+
public:
75+
RFFT_Radix4() = default;
76+
~RFFT_Radix4() { std::free(C_); }
77+
78+
RFFT_Radix4(const RFFT_Radix4&) = delete;
79+
RFFT_Radix4& operator=(const RFFT_Radix4&) = delete;
80+
81+
bool init(int n) {
82+
if (!is_pow2(n) || n < 16) return false;
83+
N_ = n;
84+
L_ = ilog2(n);
85+
C_ = static_cast<double*>(std::calloc(n / 2, sizeof(double)));
86+
if (!C_) return false;
87+
88+
if (n >= 4) C_[1] = std::sqrt(0.5);
89+
for (int m = 1; 2 * m < n / 2; ++m) {
90+
double c = C_[m], s = stw(m);
91+
double ce = std::sqrt(0.5 * (1.0 + c));
92+
double se = s / (2.0 * ce);
93+
C_[2 * m] = ce;
94+
if (2 * m + 1 < n / 2) C_[2 * m + 1] = se;
95+
}
96+
return true;
97+
}
98+
99+
int size() const { return N_; }
100+
101+
// Breadth-first forward residue transform (single-level norm_q per node).
102+
void forward_residues(const double* input, double* v) const {
103+
std::memcpy(v, input, sizeof(double) * N_);
104+
for (int jj = 0; jj < L_ - 1; ++jj)
105+
fwd_stage(v, jj);
106+
}
107+
108+
// Depth-first forward using fused 2-level radix-4 nodes where possible.
109+
void forward_residues_radix4(const double* input, double* v) const {
110+
if (N_ < 64) { forward_residues(input, v); return; }
111+
112+
const int h = N_ / 2;
113+
for (int i = 0; i < h; ++i) {
114+
v[i] = input[i] + input[h + i];
115+
v[h + i] = input[i] - input[h + i];
116+
}
117+
118+
for (int sp = h; sp >= 32; sp >>= 1) {
119+
subtree_r4(v + sp, sp >> 2, 1);
120+
binomial(v, sp >> 1);
121+
}
122+
spine_tail(v);
123+
}
124+
125+
// Accessor for twiddle constants (for per-node tests).
126+
double cosval(int m) const { return C_[m]; }
127+
double sinval(int m) const { return stw(m); }
128+
129+
Cheb4Const cheb4_const(int m) const {
130+
Cheb4Const k;
131+
k.u = C_[4 * m];
132+
k.u2 = 0.5 * (1.0 + C_[2 * m]);
133+
k.v = C_[4 * m + 1];
134+
k.v2 = 0.5 * (1.0 - C_[2 * m]);
135+
return k;
136+
}
137+
138+
private:
139+
int N_ = 0, L_ = 0;
140+
double* C_ = nullptr;
141+
142+
double stw(int m) const {
143+
return (m <= 1) ? (m == 1 ? C_[1] : 0.0) : C_[m ^ 1];
144+
}
145+
146+
// ----- primitive operations -----
147+
148+
static void binomial(double* v, int h) {
149+
for (int i = 0; i < h; ++i) {
150+
double a = v[i], b = v[h + i];
151+
v[i] = a + b;
152+
v[h + i] = a - b;
153+
}
154+
}
155+
156+
static void norm_q(double* p, int q, double c, double s) {
157+
double* A0 = p, *B0 = p + q, *A1 = p + 2 * q, *B1 = p + 3 * q;
158+
for (int n = 0; n < q; ++n) {
159+
double a0 = A0[n], b0 = B0[n], a1 = A1[n], b1 = B1[n];
160+
double R = c * b0 - s * b1;
161+
double I = s * b0 + c * b1;
162+
A0[n] = a0 + R;
163+
B0[n] = a1 + I;
164+
A1[n] = a0 - R;
165+
B1[n] = -a1 + I;
166+
}
167+
}
168+
169+
static void norm2_fused(double* p, int q,
170+
double c, double s,
171+
double c0, double s0,
172+
double c1, double s1)
173+
{
174+
const int qh = q >> 1;
175+
double* A0 = p, *B0 = p + q, *A1 = p + 2 * q, *B1 = p + 3 * q;
176+
for (int n = 0; n < qh; ++n) {
177+
double a0n = A0[n], a0h = A0[qh + n];
178+
double b0n = B0[n], b0h = B0[qh + n];
179+
double a1n = A1[n], a1h = A1[qh + n];
180+
double b1n = B1[n], b1h = B1[qh + n];
181+
182+
double Rn = c * b0n - s * b1n;
183+
double In = s * b0n + c * b1n;
184+
double Rh = c * b0h - s * b1h;
185+
double Ih = s * b0h + c * b1h;
186+
187+
double u0 = a0n + Rn, uh = a0h + Rh;
188+
double w0 = a1n + In, wh = a1h + Ih;
189+
double v0 = a0n - Rn, vh = a0h - Rh;
190+
double x0 = In - a1n, xh = Ih - a1h;
191+
192+
double R0 = c0 * uh - s0 * wh;
193+
double I0 = s0 * uh + c0 * wh;
194+
double R1 = c1 * vh - s1 * xh;
195+
double I1 = s1 * vh + c1 * xh;
196+
197+
A0[n] = u0 + R0;
198+
A0[qh + n] = w0 + I0;
199+
B0[n] = u0 - R0;
200+
B0[qh + n] = I0 - w0;
201+
A1[n] = v0 + R1;
202+
A1[qh + n] = x0 + I1;
203+
B1[n] = v0 - R1;
204+
B1[qh + n] = I1 - x0;
205+
}
206+
}
207+
208+
// ----- breadth-first stages -----
209+
210+
void fwd_stage(double* v, int jj) const {
211+
const int s = N_ >> jj;
212+
const int h = s >> 1;
213+
const int q = s >> 2;
214+
const int m_end = 1 << jj;
215+
216+
binomial(v, h);
217+
218+
for (int m = 1; m < m_end; ++m)
219+
norm_q(v + m * s, q, C_[m], stw(m));
220+
}
221+
222+
// ----- depth-first radix-4 subtree -----
223+
224+
void subtree_r4(double* v, int q, int m) const {
225+
if (q >= 16) {
226+
norm2_fused(v, q, C_[m], stw(m),
227+
C_[2 * m], stw(2 * m),
228+
C_[2 * m + 1], stw(2 * m + 1));
229+
int qq = q >> 2, cm = 4 * m;
230+
subtree_r4(v, qq, cm);
231+
subtree_r4(v + q, qq, cm + 1);
232+
subtree_r4(v + 2 * q, qq, cm + 2);
233+
subtree_r4(v + 3 * q, qq, cm + 3);
234+
return;
235+
}
236+
if (q == 8) {
237+
norm_q(v, 8, C_[m], stw(m));
238+
leaf_d3(v, 2 * m);
239+
leaf_d3(v + 16, 2 * m + 1);
240+
return;
241+
}
242+
if (q == 4) {
243+
leaf_d3(v, m);
244+
return;
245+
}
246+
if (q == 2) {
247+
norm_q(v, 2, C_[m], stw(m));
248+
norm_q(v, 1, C_[2 * m], stw(2 * m));
249+
norm_q(v + 4, 1, C_[2 * m + 1], stw(2 * m + 1));
250+
return;
251+
}
252+
}
253+
254+
void leaf_d3(double* p, int m) const {
255+
norm_q(p, 4, C_[m], stw(m));
256+
norm_q(p, 2, C_[2 * m], stw(2 * m));
257+
norm_q(p + 8, 2, C_[2 * m + 1], stw(2 * m + 1));
258+
norm_q(p, 1, C_[4 * m], stw(4 * m));
259+
norm_q(p + 4, 1, C_[4 * m + 1], stw(4 * m + 1));
260+
norm_q(p + 8, 1, C_[4 * m + 2], stw(4 * m + 2));
261+
norm_q(p + 12, 1, C_[4 * m + 3], stw(4 * m + 3));
262+
}
263+
264+
void spine_tail(double* v) const {
265+
leaf_d3(v + 16, 1);
266+
binomial(v, 8);
267+
norm_q(v + 8, 2, C_[1], stw(1));
268+
norm_q(v + 8, 1, C_[2], stw(2));
269+
norm_q(v + 12, 1, C_[3], stw(3));
270+
binomial(v, 4);
271+
norm_q(v + 4, 1, C_[1], stw(1));
272+
binomial(v, 2);
273+
}
274+
};
275+
276+
// ---------------------------------------------------------------------------
277+
// Standalone norm_q and norm2_fused for use by external test code.
278+
// ---------------------------------------------------------------------------
279+
static inline void norm_q_standalone(double* p, int q, double c, double s) {
280+
double* A0 = p, *B0 = p + q, *A1 = p + 2 * q, *B1 = p + 3 * q;
281+
for (int n = 0; n < q; ++n) {
282+
double a0 = A0[n], b0 = B0[n], a1 = A1[n], b1 = B1[n];
283+
double R = c * b0 - s * b1;
284+
double I = s * b0 + c * b1;
285+
A0[n] = a0 + R;
286+
B0[n] = a1 + I;
287+
A1[n] = a0 - R;
288+
B1[n] = -a1 + I;
289+
}
290+
}
291+
292+
static inline void norm2_fused_standalone(double* p, int q,
293+
double c, double s,
294+
double c0, double s0,
295+
double c1, double s1)
296+
{
297+
const int qh = q >> 1;
298+
double* A0 = p, *B0 = p + q, *A1 = p + 2 * q, *B1 = p + 3 * q;
299+
for (int n = 0; n < qh; ++n) {
300+
double a0n = A0[n], a0h = A0[qh + n];
301+
double b0n = B0[n], b0h = B0[qh + n];
302+
double a1n = A1[n], a1h = A1[qh + n];
303+
double b1n = B1[n], b1h = B1[qh + n];
304+
305+
double Rn = c * b0n - s * b1n;
306+
double In = s * b0n + c * b1n;
307+
double Rh = c * b0h - s * b1h;
308+
double Ih = s * b0h + c * b1h;
309+
310+
double u0 = a0n + Rn, uh = a0h + Rh;
311+
double w0 = a1n + In, wh = a1h + Ih;
312+
double v0 = a0n - Rn, vh = a0h - Rh;
313+
double x0 = In - a1n, xh = Ih - a1h;
314+
315+
double R0 = c0 * uh - s0 * wh;
316+
double I0 = s0 * uh + c0 * wh;
317+
double R1 = c1 * vh - s1 * xh;
318+
double I1 = s1 * vh + c1 * xh;
319+
320+
A0[n] = u0 + R0;
321+
A0[qh + n] = w0 + I0;
322+
B0[n] = u0 - R0;
323+
B0[qh + n] = I0 - w0;
324+
A1[n] = v0 + R1;
325+
A1[qh + n] = x0 + I1;
326+
B1[n] = v0 - R1;
327+
B1[qh + n] = I1 - x0;
328+
}
329+
}
330+
331+
} // namespace bruun_radix4
332+
333+
#endif // BFFT_BRUUN_RADIX4_KERNEL_HPP

0 commit comments

Comments
 (0)