poulpy/poulpy-hal/src/reference/fft64/reim/table_ifft.rs
poulpy-hal/src/reference/fft64/reim/table_ifft.rsRaw
1use std::fmt::Debug;
2
3use rand_distr::num_traits::{Float, FloatConst};
4
5use crate::{
6 alloc_aligned,
7 reference::fft64::reim::{ReimDFTExecute, frac_rev_bits, ifft_ref::ifft_ref},
8};
9
10pub struct ReimIFFTRef;
11
12impl ReimDFTExecute<ReimIFFTTable<f64>, f64> for ReimIFFTRef {
13 fn reim_dft_execute(table: &ReimIFFTTable<f64>, data: &mut [f64]) {
14 ifft_ref(table.m, &table.omg, data);
15 }
16}
17
18pub struct ReimIFFTTable<R: Float + FloatConst + Debug> {
19 m: usize,
20 omg: Vec<R>,
21}
22
23impl<R: Float + FloatConst + Debug> ReimIFFTTable<R> {
24 pub fn new(m: usize) -> Self {
25 assert!(m & (m - 1) == 0, "m must be a power of two but is {}", m);
26 let mut omg: Vec<R> = alloc_aligned::<R>(2 * m);
27
28 let quarter: R = R::exp2(R::from(-2).unwrap());
29
30 if m <= 16 {
31 match m {
32 1 => {}
33 2 => {
34 fill_ifft2_omegas::<R>(quarter, &mut omg, 0);
35 }
36 4 => {
37 fill_ifft4_omegas(quarter, &mut omg, 0);
38 }
39 8 => {
40 fill_ifft8_omegas(quarter, &mut omg, 0);
41 }
42 16 => {
43 fill_ifft16_omegas(quarter, &mut omg, 0);
44 }
45 _ => {}
46 }
47 } else if m <= 2048 {
48 fill_ifft_bfs_16_omegas(m, quarter, &mut omg, 0);
49 } else {
50 fill_ifft_rec_16_omegas(m, quarter, &mut omg, 0);
51 }
52
53 Self { m, omg }
54 }
55
56 pub fn execute(&self, data: &mut [R]) {
57 ifft_ref(self.m, &self.omg, data);
58 }
59
60 pub fn m(&self) -> usize {
61 self.m
62 }
63
64 pub fn omg(&self) -> &[R] {
65 &self.omg
66 }
67}
68
69#[inline(always)]
70fn fill_ifft2_omegas<R: Float + FloatConst>(j: R, omg: &mut [R], pos: usize) -> usize {
71 let omg_pos: &mut [R] = &mut omg[pos..];
72 assert!(omg_pos.len() >= 2);
73 let angle: R = j / R::exp2(R::from(2).unwrap());
74 let two_pi: R = R::exp2(R::from(2).unwrap()) * R::PI();
75 omg_pos[0] = R::cos(two_pi * angle);
76 omg_pos[1] = -R::sin(two_pi * angle);
77 pos + 2
78}
79
80#[inline(always)]
81fn fill_ifft4_omegas<R: Float + FloatConst>(j: R, omg: &mut [R], pos: usize) -> usize {
82 let omg_pos: &mut [R] = &mut omg[pos..];
83 assert!(omg_pos.len() >= 4);
84 let angle_1: R = j / R::from(2).unwrap();
85 let angle_2: R = j / R::from(4).unwrap();
86 let two_pi: R = R::from(2).unwrap() * R::PI();
87 omg_pos[0] = R::cos(two_pi * angle_2);
88 omg_pos[1] = -R::sin(two_pi * angle_2);
89 omg_pos[2] = R::cos(two_pi * angle_1);
90 omg_pos[3] = -R::sin(two_pi * angle_1);
91 pos + 4
92}
93
94#[inline(always)]
95fn fill_ifft8_omegas<R: Float + FloatConst>(j: R, omg: &mut [R], pos: usize) -> usize {
96 let omg_pos: &mut [R] = &mut omg[pos..];
97 assert!(omg_pos.len() >= 8);
98 let _8th: R = R::from(1. / 8.).unwrap();
99 let angle_1: R = j / R::from(2).unwrap();
100 let angle_2: R = j / R::from(4).unwrap();
101 let angle_4: R = j / R::from(2).unwrap();
102 let two_pi: R = R::from(2).unwrap() * R::PI();
103 omg_pos[0] = R::cos(two_pi * angle_4);
104 omg_pos[1] = R::cos(two_pi * (angle_4 + _8th));
105 omg_pos[2] = -R::sin(two_pi * angle_4);
106 omg_pos[3] = -R::sin(two_pi * (angle_4 + _8th));
107 omg_pos[4] = R::cos(two_pi * angle_2);
108 omg_pos[5] = -R::sin(two_pi * angle_2);
109 omg_pos[6] = R::cos(two_pi * angle_1);
110 omg_pos[7] = -R::sin(two_pi * angle_1);
111 pos + 8
112}
113
114#[inline(always)]
115fn fill_ifft16_omegas<R: Float + FloatConst>(j: R, omg: &mut [R], pos: usize) -> usize {
116 let omg_pos: &mut [R] = &mut omg[pos..];
117 assert!(omg_pos.len() >= 16);
118 let _8th: R = R::from(1. / 8.).unwrap();
119 let _16th: R = R::from(1. / 16.).unwrap();
120 let angle_1: R = j / R::from(2).unwrap();
121 let angle_2: R = j / R::from(4).unwrap();
122 let angle_4: R = j / R::from(8).unwrap();
123 let angle_8: R = j / R::from(16).unwrap();
124 let two_pi: R = R::from(2).unwrap() * R::PI();
125 omg_pos[0] = R::cos(two_pi * angle_8);
126 omg_pos[1] = R::cos(two_pi * (angle_8 + _8th));
127 omg_pos[2] = R::cos(two_pi * (angle_8 + _16th));
128 omg_pos[3] = R::cos(two_pi * (angle_8 + _8th + _16th));
129 omg_pos[4] = -R::sin(two_pi * angle_8);
130 omg_pos[5] = -R::sin(two_pi * (angle_8 + _8th));
131 omg_pos[6] = -R::sin(two_pi * (angle_8 + _16th));
132 omg_pos[7] = -R::sin(two_pi * (angle_8 + _8th + _16th));
133 omg_pos[8] = R::cos(two_pi * angle_4);
134 omg_pos[9] = -R::sin(two_pi * angle_4);
135 omg_pos[10] = R::cos(two_pi * (angle_4 + _8th));
136 omg_pos[11] = -R::sin(two_pi * (angle_4 + _8th));
137 omg_pos[12] = R::cos(two_pi * angle_2);
138 omg_pos[13] = -R::sin(two_pi * angle_2);
139 omg_pos[14] = R::cos(two_pi * angle_1);
140 omg_pos[15] = -R::sin(two_pi * angle_1);
141 pos + 16
142}
143
144#[inline(always)]
145fn fill_ifft_bfs_16_omegas<R: Float + FloatConst + Debug>(m: usize, j: R, omg: &mut [R], mut pos: usize) -> usize {
146 let log_m: usize = (usize::BITS - (m - 1).leading_zeros()) as usize;
147 let mut jj: R = j * R::from(16).unwrap() / R::from(m).unwrap();
148
149 for i in (0..m).step_by(16) {
150 let j = jj + frac_rev_bits(i >> 4);
151 fill_ifft16_omegas(j, omg, pos);
152 pos += 16
153 }
154
155 let mut h: usize = 16;
156 let m_half: usize = m >> 1;
157
158 let two_pi: R = R::from(2).unwrap() * R::PI();
159
160 while h < m_half {
161 let mm: usize = h << 2;
162 for i in (0..m).step_by(mm) {
163 let rs_0 = jj + frac_rev_bits::<R>(i / mm) / R::from(4).unwrap();
164 let rs_1 = R::from(2).unwrap() * rs_0;
165 omg[pos] = R::cos(two_pi * rs_0);
166 omg[pos + 1] = -R::sin(two_pi * rs_0);
167 omg[pos + 2] = R::cos(two_pi * rs_1);
168 omg[pos + 3] = -R::sin(two_pi * rs_1);
169 pos += 4;
170 }
171 h = mm;
172 jj = jj * R::from(4).unwrap();
173 }
174
175 if !log_m.is_multiple_of(2) {
176 omg[pos] = R::cos(two_pi * jj);
177 omg[pos + 1] = -R::sin(two_pi * jj);
178 pos += 2;
179 jj = jj * R::from(2).unwrap();
180 }
181
182 assert_eq!(jj, j);
183
184 pos
185}
186
187#[inline(always)]
188fn fill_ifft_rec_16_omegas<R: Float + FloatConst + Debug>(m: usize, j: R, omg: &mut [R], mut pos: usize) -> usize {
189 if m <= 2048 {
190 return fill_ifft_bfs_16_omegas(m, j, omg, pos);
191 }
192 let h: usize = m >> 1;
193 let s: R = j / R::from(2).unwrap();
194 pos = fill_ifft_rec_16_omegas(h, s, omg, pos);
195 pos = fill_ifft_rec_16_omegas(h, s + R::from(0.5).unwrap(), omg, pos);
196 let _2pi = R::from(2).unwrap() * R::PI();
197 omg[pos] = R::cos(_2pi * s);
198 omg[pos + 1] = -R::sin(_2pi * s);
199 pos += 2;
200 pos
201}