|
25 | 25 | //! - `dct3`: [`nddct3`],[`nddct3_par`] |
26 | 26 | //! - `dct4`: [`nddct4`],[`nddct4_par`] |
27 | 27 | //! |
| 28 | +//! `ndrustfft` >= v0.2.2: |
| 29 | +//! |
| 30 | +//! Real-to-complex transforms now behave like numpys rfft. |
| 31 | +//! The first element (for odd and even input) and the last element (for even input) |
| 32 | +//! of the coefficient array should be real due to Hermitian symmetry. |
| 33 | +//! Thus, the solution of the inverse transform is independent of the imaginary |
| 34 | +//! part of the first and last element (for even input). Note, this is different |
| 35 | +//! to the behaviour of the `RealFft` crate. |
| 36 | +//! |
28 | 37 | //! ## Parallel |
29 | 38 | //! The library ships all functions with a parallel version |
30 | 39 | //! which leverages the parallel abilities of ndarray. |
@@ -428,6 +437,12 @@ impl<T: FftNum> R2cFftHandler<T> { |
428 | 437 | a.re = b.re * n64; |
429 | 438 | a.im = b.im * n64; |
430 | 439 | } |
| 440 | + // First element must be real |
| 441 | + indata[0].im = T::zero(); |
| 442 | + // If original vector is even, last element must be real |
| 443 | + if self.n % 2 == 0 { |
| 444 | + indata[self.m - 1].im = T::zero(); |
| 445 | + } |
431 | 446 | self.plan_bwd.process(&mut indata, out).unwrap(); |
432 | 447 | } |
433 | 448 |
|
@@ -873,6 +888,40 @@ mod test { |
873 | 888 | approx_eq(&v, &v_copy); |
874 | 889 | } |
875 | 890 |
|
| 891 | + #[test] |
| 892 | + fn test_ifft_c2r_first_last_element() { |
| 893 | + let n = 6; |
| 894 | + let mut v = Array1::<f64>::zeros(n); |
| 895 | + let mut vhat = Array1::<Complex<f64>>::zeros(n / 2 + 1); |
| 896 | + let solution_numpy_first_elem: Array1<f64> = |
| 897 | + array![0.16667, 0.16667, 0.16667, 0.16667, 0.16667, 0.16667]; |
| 898 | + let solution_numpy_last_elem: Array1<f64> = |
| 899 | + array![0.16667, -0.16667, 0.16667, -0.16667, 0.16667, -0.16667]; |
| 900 | + let mut rfft_handler = R2cFftHandler::<f64>::new(n); |
| 901 | + |
| 902 | + // First element should be purely real, thus the imaginary |
| 903 | + // part should not matter. However, original realfft gives |
| 904 | + // different results for different imaginary parts |
| 905 | + vhat[0].re = 1.; |
| 906 | + vhat[0].im = 100.; |
| 907 | + // backward |
| 908 | + ndifft_r2c(&vhat, &mut v, &mut rfft_handler, 0); |
| 909 | + // assert |
| 910 | + approx_eq(&v, &solution_numpy_first_elem); |
| 911 | + |
| 912 | + // Same for last element, if input is even |
| 913 | + for v in vhat.iter_mut() { |
| 914 | + v.re = 0.; |
| 915 | + v.im = 0.; |
| 916 | + } |
| 917 | + vhat[3].re = 1.; |
| 918 | + vhat[3].im = 100.; |
| 919 | + // backward |
| 920 | + ndifft_r2c(&vhat, &mut v, &mut rfft_handler, 0); |
| 921 | + // assert |
| 922 | + approx_eq(&v, &solution_numpy_last_elem); |
| 923 | + } |
| 924 | + |
876 | 925 | #[test] |
877 | 926 | fn test_dct1() { |
878 | 927 | // Solution from scipy.fft.dct(x, type=1) |
|
0 commit comments