| #pragma once | |
| #include <string> | |
| #include <stdexcept> | |
| #include <sstream> | |
| #include <ATen/native/DispatchStub.h> | |
| namespace at { namespace native { | |
| // Normalization types used in _fft_with_size | |
| enum class fft_norm_mode { | |
| none, // No normalization | |
| by_root_n, // Divide by sqrt(signal_size) | |
| by_n, // Divide by signal_size | |
| }; | |
| // NOTE [ Fourier Transform Conjugate Symmetry ] | |
| // | |
| // Real-to-complex Fourier transform satisfies the conjugate symmetry. That is, | |
| // assuming X is the transformed K-dimensionsal signal, we have | |
| // | |
| // X[i_1, ..., i_K] = X[j_i, ..., j_K]*, | |
| // | |
| // where j_k = (N_k - i_k) mod N_k, N_k being the signal size at dim k, | |
| // * is the conjugate operator. | |
| // | |
| // Therefore, in such cases, FFT libraries return only roughly half of the | |
| // values to avoid redundancy: | |
| // | |
| // X[:, :, ..., :floor(N / 2) + 1] | |
| // | |
| // This is also the assumption in cuFFT and MKL. In ATen SpectralOps, such | |
| // halved signal will also be returned by default (flag onesided=True). | |
| // The following infer_ft_real_to_complex_onesided_size function calculates the | |
| // onesided size from the twosided size. | |
| // | |
| // Note that this loses some information about the size of signal at last | |
| // dimension. E.g., both 11 and 10 maps to 6. Hence, the following | |
| // infer_ft_complex_to_real_onesided_size function takes in optional parameter | |
| // to infer the twosided size from given onesided size. | |
| // | |
| // cuFFT doc: http://docs.nvidia.com/cuda/cufft/index.html#multi-dimensional | |
| // MKL doc: https://software.intel.com/en-us/mkl-developer-reference-c-dfti-complex-storage-dfti-real-storage-dfti-conjugate-even-storage#CONJUGATE_EVEN_STORAGE | |
| inline int64_t infer_ft_real_to_complex_onesided_size(int64_t real_size) { | |
| return (real_size / 2) + 1; | |
| } | |
| inline int64_t infer_ft_complex_to_real_onesided_size(int64_t complex_size, | |
| int64_t expected_size=-1) { | |
| int64_t base = (complex_size - 1) * 2; | |
| if (expected_size < 0) { | |
| return base + 1; | |
| } else if (base == expected_size) { | |
| return base; | |
| } else if (base + 1 == expected_size) { | |
| return base + 1; | |
| } else { | |
| std::ostringstream ss; | |
| ss << "expected real signal size " << expected_size << " is incompatible " | |
| << "with onesided complex frequency size " << complex_size; | |
| AT_ERROR(ss.str()); | |
| } | |
| } | |
| using fft_fill_with_conjugate_symmetry_fn = | |
| void (*)(ScalarType dtype, IntArrayRef mirror_dims, IntArrayRef half_sizes, | |
| IntArrayRef in_strides, const void* in_data, | |
| IntArrayRef out_strides, void* out_data); | |
| DECLARE_DISPATCH(fft_fill_with_conjugate_symmetry_fn, fft_fill_with_conjugate_symmetry_stub); | |
| // In real-to-complex transform, cuFFT and MKL only fill half of the values | |
| // due to conjugate symmetry. This function fills in the other half of the full | |
| // fft by using the Hermitian symmetry in the signal. | |
| // self should be the shape of the full signal and dims.back() should be the | |
| // one-sided dimension. | |
| // See NOTE [ Fourier Transform Conjugate Symmetry ] | |
| TORCH_API void _fft_fill_with_conjugate_symmetry_(const Tensor& self, IntArrayRef dims); | |
| }} // at::native |