#pragma once #include #include #include #include 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