yum/vfft
git clone https://git.yummers.dev/yum/vfft
7441799
master
1use crate :: util:: * ; 2use num:: complex:: Complex ; 3use num:: traits::{ Float , FloatConst }; 4 5fn fft_butterfly_radix_2_unsafe < T : Float +FloatConst >( 6input : & mut [ Complex < T >], 7output : & mut [ Complex < T >], 8stride : usize , 9big_n : usize , 10twiddles : & [ Complex < T >], 11 _: & ExecutionStage < T >, 12) { 13let input_ptr = input. as_ptr (); 14let output_ptr = output. as_mut_ptr (); 15for start_idxin 0 ..stride{ 16for kin 0 ..big_n /2 { 17unsafe { 18// Collect inputs. 19let p =* input_ptr. add ( start_idx +2 * k* stride); 20let q =* input_ptr. add ( start_idx +( 2 * k +1 ) * stride); 21// Collect relevant twiddles. 22let ot = twiddles. get_unchecked ( k* stride); 23// Combine. 24let r = q* ot; 25* output_ptr. add ( start_idx + k* stride) = p + r; 26* output_ptr. add ( start_idx +( k + big_n /2 ) * stride) = p - r; 27} 28} 29} 30} 31 32fn fft_butterfly_radix_2_s0_unsafe < T : Float +FloatConst >( 33input : & mut [ Complex < T >], 34output : & mut [ Complex < T >], 35 _: & Matrix < Complex < T >>, 36) { 37let input_ptr = input. as_ptr (); 38let output_ptr = output. as_mut_ptr (); 39let stride = input. len () /2 ; 40let big_n =2 ; 41for start_idxin 0 ..stride{ 42for kin 0 ..big_n /2 { 43unsafe { 44// Collect inputs. 45let p =* input_ptr. add ( start_idx +2 * k* stride); 46let q =* input_ptr. add ( start_idx +( 2 * k +1 ) * stride); 47* output_ptr. add ( start_idx + k* stride) = p + q; 48* output_ptr. add ( start_idx +( k + big_n /2 ) * stride) = p - q; 49} 50} 51} 52} 53 54fn radix_3_dft < T : Float +FloatConst >( 55input : & [ Complex < T >; 3 ], 56 _: & Matrix < Complex < T >>, 57) ->[ Complex < T >; 3 ] { 58const N : usize =3 ; 59let zero =Complex :: new ( T :: zero (), T :: zero ()); 60let mut result =[ zero; N ]; 61let half =T :: from ( 0.5 ). unwrap (); 62let sqrt_3_over_2 = 63T :: from ( 0.8660254037844386467637231707529361834714026269051903140279034897 ). unwrap (); 64let sum = input[ 1 ] + input[ 2 ]; 65let diff = input[ 1 ] - input[ 2 ]; 66 result[ 0 ] = input[ 0 ] + input[ 1 ] + input[ 2 ]; 67 result[ 1 ] = input[ 0 ] - sum. scale ( half) -mul_i ( diff. scale ( sqrt_3_over_2)); 68 result[ 2 ] = input[ 0 ] - sum. scale ( half) +mul_i ( diff. scale ( sqrt_3_over_2)); 69 result 70} 71 72fn fft_butterfly_radix_3_unsafe < T : Float +FloatConst >( 73input : & mut [ Complex < T >], 74output : & mut [ Complex < T >], 75stride : usize , 76big_n : usize , 77twiddles : & [ Complex < T >], 78stage : & ExecutionStage < T >, 79) { 80let input_ptr = input. as_ptr (); 81let output_ptr = output. as_mut_ptr (); 82const N : usize =3 ; 83for start_idxin 0 ..stride{ 84for kin 0 ..big_n /N { 85unsafe { 86// Collect inputs. 87let inputs: [ Complex < T >; N ] = 88 std:: array:: from_fn ( |idx|* input_ptr. add ( start_idx +( N * k + idx) * stride)); 89// Collect relevant twiddles. 90let twiddles: [ & Complex < T >; N ] = 91 std:: array:: from_fn ( |idx| twiddles. get_unchecked ( idx* k* stride)); 92// Precompute a term used in the inner loop. 93let it: [ Complex < T >; N ] = std:: array:: from_fn ( |idx|{ 94if idx ==0 { 95 inputs[ idx] 96} else { 97 inputs[ idx] * twiddles[ idx] 98} 99}); 100let result =radix_3_dft ( & it, & stage. outer_terms ); 101for rowin 0 ..N { 102* output_ptr. add ( start_idx +( k + row* big_n /N ) * stride) = result[ row]; 103} 104} 105} 106} 107} 108 109fn fft_butterfly_radix_3_s0_unsafe < T : Float +FloatConst >( 110input : & mut [ Complex < T >], 111output : & mut [ Complex < T >], 112outer_terms : & Matrix < Complex < T >>, 113) { 114const N : usize =3 ; 115let input_ptr = input. as_ptr (); 116let output_ptr = output. as_mut_ptr (); 117let stride = input. len () /N ; 118let big_n =N ; 119for start_idxin 0 ..stride{ 120for kin 0 ..big_n /N { 121unsafe { 122// Collect inputs. 123let inputs: [ Complex < T >; N ] = 124 std:: array:: from_fn ( |idx|* input_ptr. add ( start_idx +( N * k + idx) * stride)); 125let result =radix_3_dft ( & inputs, outer_terms); 126for rowin 0 ..N { 127* output_ptr. add ( start_idx +( k + row* big_n /N ) * stride) = result[ row]; 128} 129} 130} 131} 132} 133 134fn fft_butterfly_radix_9_unsafe < T : Float +FloatConst >( 135input : & mut [ Complex < T >], 136output : & mut [ Complex < T >], 137stride : usize , 138big_n : usize , 139twiddles : & [ Complex < T >], 140stage : & ExecutionStage < T >, 141) { 142let input_ptr = input. as_ptr (); 143let output_ptr = output. as_mut_ptr (); 144const N : usize =9 ; 145for start_idxin 0 ..stride{ 146for kin 0 ..big_n /N { 147unsafe { 148let inputs: [ Complex < T >; N ] = 149 std:: array:: from_fn ( |idx|* input_ptr. add ( start_idx +( N * k + idx) * stride)); 150let twiddles: [ & Complex < T >; N ] = 151 std:: array:: from_fn ( |idx| twiddles. get_unchecked ( idx* k* stride)); 152let it: [ Complex < T >; N ] = std:: array:: from_fn ( |idx|{ 153if idx ==0 { 154 inputs[ idx] 155} else { 156 inputs[ idx] * twiddles[ idx] 157} 158}); 159 160let b00 =[ it[ 0 ], it[ 3 ], it[ 6 ]]; 161let b01 =[ it[ 1 ], it[ 4 ], it[ 7 ]]; 162let b02 =[ it[ 2 ], it[ 5 ], it[ 8 ]]; 163 164let b00_out =radix_3_dft ( & b00, & stage. outer_terms ); 165let b01_out =radix_3_dft ( & b01, & stage. outer_terms ); 166let b02_out =radix_3_dft ( & b02, & stage. outer_terms ); 167 168// Recall the general way that outer terms work: 169// First row is all 1s. 170// Second row rotates 1/r clockwise. 171// Third row rotates 2/r clockwise. 172// outer_terms already takes into account the way rotations 173// accelerate each row, so we just have to index into the right 174// row/col. 175let b10 =[ b00_out[ 0 ], b01_out[ 0 ], b02_out[ 0 ]]; 176let b11 =[ 177 b00_out[ 1 ], 178 b01_out[ 1 ] * stage. outer_terms . data . get_unchecked ( 9 +1 ), 179 b02_out[ 1 ] * stage. outer_terms . data . get_unchecked ( 9 +2 ), 180]; 181let b12 =[ 182 b00_out[ 2 ], 183 b01_out[ 2 ] * stage. outer_terms . data . get_unchecked ( 18 +1 ), 184 b02_out[ 2 ] * stage. outer_terms . data . get_unchecked ( 18 +2 ), 185]; 186 187let r00 =radix_3_dft ( & b10, & stage. outer_terms ); 188let r01 =radix_3_dft ( & b11, & stage. outer_terms ); 189let r02 =radix_3_dft ( & b12, & stage. outer_terms ); 190 191let results =[ 192 r00[ 0 ], r01[ 0 ], r02[ 0 ], r00[ 1 ], r01[ 1 ], r02[ 1 ], r00[ 2 ], r01[ 2 ], r02[ 2 ], 193]; 194for rowin 0 ..N { 195* output_ptr. add ( start_idx +( k + row* big_n /N ) * stride) = results[ row]; 196} 197} 198} 199} 200} 201 202fn fft_butterfly_radix_9_s0_unsafe < T : Float +FloatConst >( 203input : & mut [ Complex < T >], 204output : & mut [ Complex < T >], 205outer_terms : & Matrix < Complex < T >>, 206) { 207let input_ptr = input. as_ptr (); 208let output_ptr = output. as_mut_ptr (); 209const N : usize =9 ; 210let stride = input. len () /N ; 211let big_n =N ; 212for start_idxin 0 ..stride{ 213for kin 0 ..big_n /N { 214unsafe { 215let inputs: [ Complex < T >; N ] = 216 std:: array:: from_fn ( |idx|* input_ptr. add ( start_idx +( N * k + idx) * stride)); 217 218let b00 =[ inputs[ 0 ], inputs[ 3 ], inputs[ 6 ]]; 219let b01 =[ inputs[ 1 ], inputs[ 4 ], inputs[ 7 ]]; 220let b02 =[ inputs[ 2 ], inputs[ 5 ], inputs[ 8 ]]; 221 222let b00_out =radix_3_dft ( & b00, & outer_terms); 223let b01_out =radix_3_dft ( & b01, & outer_terms); 224let b02_out =radix_3_dft ( & b02, & outer_terms); 225 226// Recall the general way that outer terms work: 227// First row is all 1s. 228// Second row rotates 1/r clockwise. 229// Third row rotates 2/r clockwise. 230let b10 =[ b00_out[ 0 ], b01_out[ 0 ], b02_out[ 0 ]]; 231let b11 =[ 232 b00_out[ 1 ], 233 b01_out[ 1 ] * outer_terms. data . get_unchecked ( 9 +1 ), 234 b02_out[ 1 ] * outer_terms. data . get_unchecked ( 9 +2 ), 235]; 236let b12 =[ 237 b00_out[ 2 ], 238 b01_out[ 2 ] * outer_terms. data . get_unchecked ( 18 +1 ), 239 b02_out[ 2 ] * outer_terms. data . get_unchecked ( 18 +2 ), 240]; 241 242let r00 =radix_3_dft ( & b10, & outer_terms); 243let r01 =radix_3_dft ( & b11, & outer_terms); 244let r02 =radix_3_dft ( & b12, & outer_terms); 245 246let results =[ 247 r00[ 0 ], r01[ 0 ], r02[ 0 ], r00[ 1 ], r01[ 1 ], r02[ 1 ], r00[ 2 ], r01[ 2 ], r02[ 2 ], 248]; 249for rowin 0 ..N { 250* output_ptr. add ( start_idx +( k + row* big_n /N ) * stride) = results[ row]; 251} 252} 253} 254} 255} 256 257fn odd_radix_dft < const N : usize , T : Float +FloatConst >( 258input : & [ Complex < T >; N ], 259outer_terms : & Matrix < Complex < T >>, 260) ->[ Complex < T >; N ] { 261let zero =Complex :: new ( T :: zero (), T :: zero ()); 262let mut result =[ zero; N ]; 263// Precompute sums and differences. 264let mut sums =[ zero; N ]; 265let mut diffs =[ zero; N ]; 266for colin 1 ..N /2 +1 { 267 sums[ col] = input[ col] + input[ N - col]; 268 diffs[ col] = input[ col] - input[ N - col]; 269} 270 271// 0,0 term is never indexed, so compute it out of the loop. 272 result[ 0 ] = input[ 0 ]; 273 274// 0th row is never indexed, so compute it out of the loop. 275for colin 1 ..N /2 +1 { 276// outer_terms is always 1 along the first row and column, so we can skip the multiplication. 277 result[ 0 ] = result[ 0 ] + sums[ col]; 278} 279 280for rowin 1 ..N /2 +1 { 281// Column zero is not indexed below, so precompute here. Recall the 282// outer_terms is 1 along the first column. 283// 284// Per our identity, we split this sum into a "real" coefficient and an 285// "imaginary" coefficient. Note that they are in fact both complex-valued. 286let mut re_part = input[ 0 ]; 287let mut im_part = zero; 288 289for colin 1 ..N /2 +1 { 290unsafe { 291let w = outer_terms. data . get_unchecked ( row* N + col); 292 re_part = re_part + sums[ col]. scale ( w. re ); 293 im_part = im_part + diffs[ col]. scale ( w. im ); 294} 295} 296 297// Then apply the identity to get both rows. 298let i_im_part =mul_i ( im_part); 299 result[ row] = re_part + i_im_part; 300 result[ N - row] = re_part - i_im_part; 301} 302 result 303} 304 305fn fft_butterfly_radix_p_unsafe < const N : usize , T : Float +FloatConst >( 306input : & mut [ Complex < T >], 307output : & mut [ Complex < T >], 308stride : usize , 309big_n : usize , 310twiddles : & [ Complex < T >], 311stage : & ExecutionStage < T >, 312) { 313let input_ptr = input. as_ptr (); 314let output_ptr = output. as_mut_ptr (); 315for start_idxin 0 ..stride{ 316for kin 0 ..big_n /N { 317unsafe { 318// Collect inputs. 319let inputs: [ Complex < T >; N ] = 320 std:: array:: from_fn ( |idx|* input_ptr. add ( start_idx +( N * k + idx) * stride)); 321// Collect relevant twiddles. 322let twiddles: [ & Complex < T >; N ] = 323 std:: array:: from_fn ( |idx| twiddles. get_unchecked ( idx* k* stride)); 324// Precompute a term used in the inner loop. 325let it: [ Complex < T >; N ] = std:: array:: from_fn ( |idx|{ 326if idx ==0 { 327 inputs[ idx] 328} else { 329 inputs[ idx] * twiddles[ idx] 330} 331}); 332let result =odd_radix_dft ( & it, & stage. outer_terms ); 333for rowin 0 ..N { 334* output_ptr. add ( start_idx +( k + row* big_n /N ) * stride) = result[ row]; 335} 336} 337} 338} 339} 340 341fn fft_butterfly_radix_p_s0_unsafe < const N : usize , T : Float +FloatConst >( 342input : & mut [ Complex < T >], 343output : & mut [ Complex < T >], 344outer_terms : & Matrix < Complex < T >>, 345) { 346let input_ptr = input. as_ptr (); 347let output_ptr = output. as_mut_ptr (); 348let stride = input. len () /N ; 349let big_n =N ; 350for start_idxin 0 ..stride{ 351for kin 0 ..big_n /N { 352unsafe { 353// Collect inputs. 354let inputs: [ Complex < T >; N ] = 355 std:: array:: from_fn ( |idx|* input_ptr. add ( start_idx +( N * k + idx) * stride)); 356// Precompute a term used in the inner loop. 357let result =odd_radix_dft ( & inputs, outer_terms); 358for rowin 0 ..N { 359* output_ptr. add ( start_idx +( k + row* big_n /N ) * stride) = result[ row]; 360} 361} 362} 363} 364} 365 366# [ inline ( always )] 367fn mul_i < T : Float +FloatConst >( x : Complex < T >) ->Complex < T > { 368Complex :: new ( -x. im , x. re ) 369} 370 371# [ inline ( always )] 372fn mul_ni < T : Float +FloatConst >( x : Complex < T >) ->Complex < T > { 373Complex :: new ( x. im , -x. re ) 374} 375 376fn fft_butterfly_radix_4_unsafe < T : Float +FloatConst >( 377input : & mut [ Complex < T >], 378output : & mut [ Complex < T >], 379stride : usize , 380big_n : usize , 381twiddles : & [ Complex < T >], 382 _: & ExecutionStage < T >, 383) { 384let input_ptr = input. as_ptr (); 385let output_ptr = output. as_mut_ptr (); 386for start_idxin 0 ..stride{ 387for kin 0 ..big_n /4 { 388unsafe { 389// Collect inputs. 390let i0 =* input_ptr. add ( start_idx +4 * k* stride); 391let i1 =* input_ptr. add ( start_idx +( 4 * k +1 ) * stride); 392let i2 =* input_ptr. add ( start_idx +( 4 * k +2 ) * stride); 393let i3 =* input_ptr. add ( start_idx +( 4 * k +3 ) * stride); 394// Collect relevant twiddles. 395let ot1 = twiddles. get_unchecked ( 1 * k* stride); 396let ot2 = twiddles. get_unchecked ( 2 * k* stride); 397let ot3 = twiddles. get_unchecked ( 3 * k* stride); 398 399let a = i0; 400let b = ot1* i1; 401let c = ot2* i2; 402let d = ot3* i3; 403 404// To derive this, write the output assignments in terms of 405// a/b/c/d, then factor out! 406let ac_sum = a + c; 407let ac_diff = a - c; 408let bd_sum = b + d; 409let bd_diff_ni =mul_ni ( b - d); 410 411* output_ptr. add ( start_idx + k* stride) = ac_sum + bd_sum; 412* output_ptr. add ( start_idx +( k + big_n /4 ) * stride) = ac_diff + bd_diff_ni; 413* output_ptr. add ( start_idx +( k + big_n /2 ) * stride) = ac_sum - bd_sum; 414* output_ptr. add ( start_idx +( k +3 * big_n /4 ) * stride) = ac_diff - bd_diff_ni; 415} 416} 417} 418} 419 420fn fft_butterfly_radix_4_s0_unsafe < T : Float +FloatConst >( 421input : & mut [ Complex < T >], 422output : & mut [ Complex < T >], 423 _: & Matrix < Complex < T >>, 424) { 425let stride = input. len () /4 ; 426let big_n =4 ; 427let input_ptr = input. as_ptr (); 428let output_ptr = output. as_mut_ptr (); 429for start_idxin 0 ..stride{ 430for kin 0 ..big_n /4 { 431unsafe { 432// Collect inputs. 433let i0 =* input_ptr. add ( start_idx +4 * k* stride); 434let i1 =* input_ptr. add ( start_idx +( 4 * k +1 ) * stride); 435let i2 =* input_ptr. add ( start_idx +( 4 * k +2 ) * stride); 436let i3 =* input_ptr. add ( start_idx +( 4 * k +3 ) * stride); 437 438let a = i0; 439let b = i1; 440let c = i2; 441let d = i3; 442 443// To derive this, write the output assignments in terms of 444// a/b/c/d, then factor out! 445let ac_sum = a + c; 446let ac_diff = a - c; 447let bd_sum = b + d; 448let bd_diff_ni =mul_ni ( b - d); 449 450* output_ptr. add ( start_idx + k* stride) = ac_sum + bd_sum; 451* output_ptr. add ( start_idx +( k + big_n /4 ) * stride) = ac_diff + bd_diff_ni; 452* output_ptr. add ( start_idx +( k + big_n /2 ) * stride) = ac_sum - bd_sum; 453* output_ptr. add ( start_idx +( k +3 * big_n /4 ) * stride) = ac_diff - bd_diff_ni; 454} 455} 456} 457} 458 459# [ inline ( always )] 460fn rot_45 < T : Float +FloatConst >( c : Complex < T >) ->Complex < T > { 461let s =T :: FRAC_1_SQRT_2 (); 462// The standard 2D rotation matrix gives: 463// [ cos(pi/4) -sin(pi/4)] [ s -s ] 464// [ sin(pi/4) cos(pi/4)] = [ s s ] 465Complex ::< T >:: new ( c. re - c. im , c. re + c. im ) * s 466} 467 468# [ inline ( always )] 469fn rot_90 < T : Float +FloatConst >( c : Complex < T >) ->Complex < T > { 470// The standard 2D rotation matrix gives: 471// [ cos(pi/2) -sin(pi/2)] [ 0 -1 ] 472// [ sin(pi/2) cos(pi/2)] = [ 1 0 ] 473Complex ::< T >:: new ( -c. im , c. re ) 474} 475 476# [ inline ( always )] 477fn rot_180 < T : Float +FloatConst >( c : Complex < T >) ->Complex < T > { 478// The standard 2D rotation matrix gives: 479// [ cos(pi) -sin(pi)] [ -1 0 ] 480// [ sin(pi) cos(pi)] = [ 0 -1 ] 481Complex ::< T >:: new ( -c. re , -c. im ) 482} 483 484# [ inline ( always )] 485fn rot_270 < T : Float +FloatConst >( c : Complex < T >) ->Complex < T > { 486// The standard 2D rotation matrix gives: 487// [ cos(3pi/2) -sin(3pi/2)] [ 0 1 ] 488// [ sin(3pi/2) cos(3pi/2)] = [ -1 0 ] 489Complex ::< T >:: new ( c. im , -c. re ) 490} 491 492fn fft_butterfly_radix_8_unsafe < T : Float +FloatConst >( 493input : & mut [ Complex < T >], 494output : & mut [ Complex < T >], 495stride : usize , 496big_n : usize , 497twiddles : & [ Complex < T >], 498 _: & ExecutionStage < T >, 499) { 500let input_ptr = input. as_ptr (); 501let output_ptr = output. as_mut_ptr (); 502for start_idxin 0 ..stride{ 503for kin 0 ..big_n /8 { 504unsafe { 505// Collect inputs. 506let i0 =* input_ptr. add ( start_idx +8 * k* stride); 507let i1 =* input_ptr. add ( start_idx +( 8 * k +1 ) * stride); 508let i2 =* input_ptr. add ( start_idx +( 8 * k +2 ) * stride); 509let i3 =* input_ptr. add ( start_idx +( 8 * k +3 ) * stride); 510let i4 =* input_ptr. add ( start_idx +( 8 * k +4 ) * stride); 511let i5 =* input_ptr. add ( start_idx +( 8 * k +5 ) * stride); 512let i6 =* input_ptr. add ( start_idx +( 8 * k +6 ) * stride); 513let i7 =* input_ptr. add ( start_idx +( 8 * k +7 ) * stride); 514 515// Collect relevant twiddles. 516let ot1 = twiddles. get_unchecked ( 1 * k* stride); 517let ot2 = twiddles. get_unchecked ( 2 * k* stride); 518let ot3 = twiddles. get_unchecked ( 3 * k* stride); 519let ot4 = twiddles. get_unchecked ( 4 * k* stride); 520let ot5 = twiddles. get_unchecked ( 5 * k* stride); 521let ot6 = twiddles. get_unchecked ( 6 * k* stride); 522let ot7 = twiddles. get_unchecked ( 7 * k* stride); 523 524let a = i0; 525let b = ot1* i1; 526let c = ot2* i2; 527let d = ot3* i3; 528let e = ot4* i4; 529let f = ot5* i5; 530let g = ot6* i6; 531let h = ot7* i7; 532 533let ae_sum = a + e; 534let ae_diff = a - e; 535let bf_sum = b + f; 536let bf_diff = b - f; 537let cg_sum = c + g; 538let cg_diff = c - g; 539let dh_sum = d + h; 540let dh_diff = d - h; 541 542let w00 = ae_sum + cg_sum; 543let w01 = ae_sum - cg_sum; 544let w10 = ae_diff +rot_270 ( cg_diff); 545let w11 = ae_diff -rot_270 ( cg_diff); 546let x00 = bf_sum + dh_sum; 547let x01 =rot_270 ( bf_sum) +rot_90 ( dh_sum); 548let x10 =rot_45 ( rot_270 ( bf_diff) +rot_180 ( dh_diff)); 549let x11 =rot_45 ( rot_180 ( bf_diff) +rot_270 ( dh_diff)); 550 551* output_ptr. add ( start_idx + k* stride) = w00 + x00; 552* output_ptr. add ( start_idx +( k + big_n /8 ) * stride) = w10 + x10; 553* output_ptr. add ( start_idx +( k + big_n /4 ) * stride) = w01 + x01; 554* output_ptr. add ( start_idx +( k +3 * big_n /8 ) * stride) = w11 + x11; 555* output_ptr. add ( start_idx +( k + big_n /2 ) * stride) = w00 - x00; 556* output_ptr. add ( start_idx +( k +5 * big_n /8 ) * stride) = w10 - x10; 557* output_ptr. add ( start_idx +( k +3 * big_n /4 ) * stride) = w01 - x01; 558* output_ptr. add ( start_idx +( k +7 * big_n /8 ) * stride) = w11 - x11; 559} 560} 561} 562} 563 564fn fft_butterfly_radix_8_s0_unsafe < T : Float +FloatConst >( 565input : & mut [ Complex < T >], 566output : & mut [ Complex < T >], 567 _: & Matrix < Complex < T >>, 568) { 569let stride = input. len () /8 ; 570let big_n =8 ; 571let input_ptr = input. as_ptr (); 572let output_ptr = output. as_mut_ptr (); 573for start_idxin 0 ..stride{ 574for kin 0 ..big_n /8 { 575unsafe { 576// Collect inputs. 577let i0 =* input_ptr. add ( start_idx +8 * k* stride); 578let i1 =* input_ptr. add ( start_idx +( 8 * k +1 ) * stride); 579let i2 =* input_ptr. add ( start_idx +( 8 * k +2 ) * stride); 580let i3 =* input_ptr. add ( start_idx +( 8 * k +3 ) * stride); 581let i4 =* input_ptr. add ( start_idx +( 8 * k +4 ) * stride); 582let i5 =* input_ptr. add ( start_idx +( 8 * k +5 ) * stride); 583let i6 =* input_ptr. add ( start_idx +( 8 * k +6 ) * stride); 584let i7 =* input_ptr. add ( start_idx +( 8 * k +7 ) * stride); 585 586let a = i0; 587let b = i1; 588let c = i2; 589let d = i3; 590let e = i4; 591let f = i5; 592let g = i6; 593let h = i7; 594 595let ae_sum = a + e; 596let ae_diff = a - e; 597let bf_sum = b + f; 598let bf_diff = b - f; 599let cg_sum = c + g; 600let cg_diff = c - g; 601let dh_sum = d + h; 602let dh_diff = d - h; 603 604let w00 = ae_sum + cg_sum; 605let w01 = ae_sum - cg_sum; 606let w10 = ae_diff +rot_270 ( cg_diff); 607let w11 = ae_diff -rot_270 ( cg_diff); 608let x00 = bf_sum + dh_sum; 609let x01 =rot_270 ( bf_sum) +rot_90 ( dh_sum); 610let x10 =rot_45 ( rot_270 ( bf_diff) +rot_180 ( dh_diff)); 611let x11 =rot_45 ( rot_180 ( bf_diff) +rot_270 ( dh_diff)); 612 613* output_ptr. add ( start_idx + k* stride) = w00 + x00; 614* output_ptr. add ( start_idx +( k + big_n /8 ) * stride) = w10 + x10; 615* output_ptr. add ( start_idx +( k + big_n /4 ) * stride) = w01 + x01; 616* output_ptr. add ( start_idx +( k +3 * big_n /8 ) * stride) = w11 + x11; 617* output_ptr. add ( start_idx +( k + big_n /2 ) * stride) = w00 - x00; 618* output_ptr. add ( start_idx +( k +5 * big_n /8 ) * stride) = w10 - x10; 619* output_ptr. add ( start_idx +( k +3 * big_n /4 ) * stride) = w01 - x01; 620* output_ptr. add ( start_idx +( k +7 * big_n /8 ) * stride) = w11 - x11; 621} 622} 623} 624} 625 626struct Matrix < T > { 627data : Vec < T >, 628rows : usize , 629cols : usize , 630} 631 632impl < T : Clone +Default > Matrix < T > { 633pub fn new ( rows : usize , cols : usize ) ->Self { 634let data =vec! [ T :: default (); rows* cols]; 635Self { data, rows, cols} 636} 637} 638 639// Reference: https://doc.rust-lang.org/std/ops/trait.Index.html 640impl < T > std:: ops:: Index <( usize , usize )> for Matrix < T > { 641type Output =T ; 642fn index ( & self , ( r, c): ( usize , usize )) ->& T { 643& self . data [ r* self . cols + c] 644} 645} 646 647impl < T > std:: ops:: IndexMut <( usize , usize )> for Matrix < T > { 648fn index_mut ( & mut self , ( r, c): ( usize , usize )) ->& mut T { 649& mut self . data [ r* self . cols + c] 650} 651} 652 653pub struct ExecutionPlan < T : Float +FloatConst > { 654pub twiddles : Vec < Complex < T >>, 655pub stages : Vec < ExecutionStage < T >>, 656pub scratch : Vec < Complex < T >>, 657} 658 659pub struct ExecutionStage < T : Float +FloatConst > { 660radix : usize , 661butterfly : 662fn ( & mut [ Complex < T >], & mut [ Complex < T >], usize , usize , & [ Complex < T >], & ExecutionStage < T >), 663butterfly_s0 : fn ( & mut [ Complex < T >], & mut [ Complex < T >], & Matrix < Complex < T >>), 664// TODO explain wtf this is. Link to article? 665outer_terms : Matrix < Complex < T >>, 666} 667 668impl < T : Clone +Default +Float +FloatConst > ExecutionPlan < T > { 669pub fn new ( data : & [ Complex < T >]) -> anyhow:: Result < Self > { 670let data_len = data. len (); 671 672let available_radices =[ 9 , 7 , 8 , 5 , 4 , 3 , 2 ]; 673let mut stages =Vec :: new (); 674let mut len = data_len; 675while len >1 { 676let mut got_match =false ; 677for radixin & available_radices{ 678if len % radix ==0 { 679let butterflies =Self :: radix_to_butterfly ( * radix) ?; 680 stages. push ( ExecutionStage { 681radix : * radix, 682butterfly : butterflies. 0 , 683butterfly_s0 : butterflies. 1 , 684outer_terms : Self :: generate_radix_outer_terms ( * radix), 685}); 686 len /= radix; 687 got_match =true ; 688break ; 689} 690} 691if !got_match{ 692 anyhow:: bail!( "Could not factor input length {}" , len); 693} 694} 695 696let twiddles =precompute_twiddles ::< T >( data_len); 697let scratch: Vec < Complex < T >> =vec! [ Complex :: new ( T :: zero (), T :: zero ()); data. len ()]; 698Ok ( Self { 699 twiddles, 700 stages, 701 scratch, 702}) 703} 704 705fn generate_radix_outer_terms ( radix : usize ) ->Matrix < Complex < T >> { 706assert! ( radix >1 , "radix must be greater than 1" ); 707let mut data =Matrix ::< Complex < T >>:: new ( radix, radix); 708for rowin 0 ..radix{ 709for colin 0 ..radix{ 710let k =( row* col) % radix; 711let entry =Complex ::< f64 >:: cis ( -f64:: TAU () * ( kas f64 ) /( radixas f64 )); 712 data. data [ row* radix + col] = 713Complex ::< T >:: new ( T :: from ( entry. re ). unwrap (), T :: from ( entry. im ). unwrap ()); 714} 715} 716 data 717} 718 719fn radix_to_butterfly ( 720radix : usize , 721) -> anyhow:: Result <( 722fn ( & mut [ Complex < T >], & mut [ Complex < T >], usize , usize , & [ Complex < T >], & ExecutionStage < T >), 723fn ( & mut [ Complex < T >], & mut [ Complex < T >], & Matrix < Complex < T >>), 724)> { 725match radix{ 7268 =>Ok (( 727 fft_butterfly_radix_8_unsafe, 728 fft_butterfly_radix_8_s0_unsafe, 729)), 7304 =>Ok (( 731 fft_butterfly_radix_4_unsafe, 732 fft_butterfly_radix_4_s0_unsafe, 733)), 7342 =>Ok (( 735 fft_butterfly_radix_2_unsafe, 736 fft_butterfly_radix_2_s0_unsafe, 737)), 7389 =>Ok (( 739 fft_butterfly_radix_9_unsafe, 740 fft_butterfly_radix_9_s0_unsafe, 741)), 7423 =>Ok (( 743 fft_butterfly_radix_3_unsafe, 744 fft_butterfly_radix_3_s0_unsafe, 745)), 7465 =>Ok (( 747fft_butterfly_radix_p_unsafe ::< 5 , T >, 748fft_butterfly_radix_p_s0_unsafe ::< 5 , T >, 749)), 7507 =>Ok (( 751fft_butterfly_radix_p_unsafe ::< 7 , T >, 752fft_butterfly_radix_p_s0_unsafe ::< 7 , T >, 753)), 754 _ => anyhow:: bail!( "Unsupported radix {}" , radix), 755} 756} 757} 758 759pub fn fft < T : Float +FloatConst >( src : & mut [ Complex < T >], plan : & mut ExecutionPlan < T >) { 760let scratch = plan. scratch . as_mut_slice (); 761assert_eq! ( src. len (), scratch. len ()); 762assert_eq! ( plan. twiddles . len (), src. len ()); 763 764let n_iter = plan. stages . len (); 765if n_iter %2 ==1 { 766 scratch. copy_from_slice ( src); 767} 768 769let ( mut input, mut output) =if n_iter %2 ==0 { 770( scratch, src) 771} else { 772( src, scratch) 773}; 774 775let big_n = input. len (); 776let mut stride = big_n; 777let mut big_n =1 ; 778for ( stage_idx, stage) in plan. stages . iter (). enumerate () { 779 stride /= stage. radix ; 780 big_n *= stage. radix ; 781 std:: mem:: swap ( & mut input, & mut output); 782 783if stage_idx ==0 { 784( stage. butterfly_s0 )( input, output, & stage. outer_terms ); 785} else { 786( stage. butterfly )( input, output, stride, big_n, & plan. twiddles , & stage); 787} 788} 789} 790 791# [ cfg ( test )] 792mod tests{ 793use rand:: RngExt ; 794use rand:: SeedableRng ; 795use rand:: rngs:: StdRng ; 796 797use super :: * ; 798use num:: complex:: Complex ; 799use std:: time:: Instant ; 800 801// Cast one float type to another, truncating if needed. 802fn t0_to_t1 < T0 : Float , T1 : Float >( val : T0 ) ->T1 { 803return num:: cast ( val). unwrap (); 804} 805 806// Casts one Complex<T> to another. 807fn complex_to_t < T0 : Float , T1 : Float >( data : & [ Complex < T0 >]) ->Vec < Complex < T1 >> { 808 data. iter () 809. map ( |c|Complex :: new ( t0_to_t1 ( c. re ), t0_to_t1 ( c. im ))) 810. collect () 811} 812 813fn evaluate_results ( 814result_ref_64 : & Vec < Complex < f64 >>, 815result_cur_64 : & Vec < Complex < f64 >>, 816duration_dft : std:: time:: Duration , 817duration_cur : std:: time:: Duration , 818algo_name : & str , 819) { 820assert_eq! ( result_ref_64. len (), result_cur_64. len ()); 821 822println! ( " Algorithm: {}" , algo_name); 823println! ( " Duration: {:?}" , duration_cur); 824println! ( 825" Speedup: {:?}" , 826 duration_dft. as_nanos () as f32 / duration_cur. as_nanos () as f32 827); 828 829let mut max_err: f64 =0.0 ; 830let mut sum_err: f64 =0.0 ; 831// TODO: median, 90p, 99p 832 833let result_64: Vec < Complex < f64 >> =complex_to_t ( & result_cur_64); 834 835for iin 0 ..result_ref_64. len () { 836 max_err = max_err. max (( result_ref_64[ i] - result_64[ i]). norm ()); 837 sum_err +=( result_ref_64[ i] - result_64[ i]). norm (); 838} 839println! ( " Max err: {}" , max_err); 840println! ( " Avg err: {}" , sum_err /( result_ref_64. len () as f64 )); 841} 842 843fn evaluate_size ( size : u32 ) { 844println! ( "size={}" , size); 845 846let mut data: Vec < Complex < f64 >> =Vec :: new (); 847 data. resize ( sizeas usize , Complex :: ZERO ); 848let mut rng =StdRng :: seed_from_u64 ( 42 ); 849for iin 0 ..data. len () { 850 data[ i] =Complex :: new ( rng. random (), rng. random ()); 851} 852 853let mut result_ref: Vec < rustfft:: num_complex:: Complex < f64 >> =complex_to_t ( & data); 854let mut ref_planner = rustfft:: FftPlanner ::< f64 >:: new (); 855let ref_fft = ref_planner. plan_fft_forward ( result_ref. len ()); 856let begin_instant =Instant :: now (); 857 ref_fft. process ( & mut result_ref); 858let duration_ref = begin_instant. elapsed (); 859let result_ref_64: Vec < Complex < f64 >> =complex_to_t ( & result_ref); 860evaluate_results ( 861& result_ref_64, 862& result_ref_64, 863 std:: time:: Duration :: from_secs ( 0 ), 864 duration_ref, 865"REF" , 866); 867 868let mut result_rfft: Vec < rustfft:: num_complex:: Complex < f32 >> =complex_to_t ( & data); 869let mut rfftner = rustfft:: FftPlanner ::< f32 >:: new (); 870let rfft_fft = rfftner. plan_fft_forward ( result_rfft. len ()); 871let mut begin_instant =Instant :: now (); 872 rfft_fft. process ( & mut result_rfft); 873let duration_rfft = begin_instant. elapsed (); 874let result_rfft_64: Vec < Complex < f64 >> =complex_to_t ( & result_rfft); 875evaluate_results ( 876& result_ref_64, 877& result_rfft_64, 878 std:: time:: Duration :: from_secs ( 0 ), 879 duration_rfft, 880"RFFT" , 881); 882 883let mut result_fft =complex_to_t ( & data); 884let mut plan =ExecutionPlan ::< f32 >:: new ( & result_fft). unwrap (); 885 begin_instant =Instant :: now (); 886fft ( & mut result_fft, & mut plan); 887let duration_fft = begin_instant. elapsed (); 888let result_fft_64: Vec < Complex < f64 >> =complex_to_t ( & result_fft); 889evaluate_results ( 890& result_ref_64, 891& result_fft_64, 892 std:: time:: Duration :: from_secs ( 0 ), 893 duration_fft, 894"FFT v8 (plan)" , 895); 896} 897 898# [ test ] 899fn naive_fft_matches_ref () { 900for exponentin 4 ..17 { 901evaluate_size ( 2u32 . pow ( exponent)); 902} 903evaluate_size ( 3u32 . pow ( 3 )); // 27 904evaluate_size ( 3u32 . pow ( 4 )); // 81 905evaluate_size ( 3u32 . pow ( 5 )); // 243 906evaluate_size ( 3u32 . pow ( 6 )); // 729 907evaluate_size ( 3u32 . pow ( 7 )); // 2187 908evaluate_size ( 3u32 . pow ( 8 )); // 6561 909 910evaluate_size ( 5u32 . pow ( 2 )); // 25 911evaluate_size ( 5u32 . pow ( 3 )); // 125 912evaluate_size ( 5u32 . pow ( 4 )); // 625 913evaluate_size ( 5u32 . pow ( 5 )); // 3125 914 915evaluate_size ( 7u32 . pow ( 2 )); // 49 916evaluate_size ( 7u32 . pow ( 3 )); // 343 917evaluate_size ( 7u32 . pow ( 4 )); // 2401 918} 919}