| // This file is part of Eigen, a lightweight C++ template library |
| // for linear algebra. |
| // |
| // Copyright (C) 2009 Mark Borgerding mark a borgerding net |
| // |
| // This Source Code Form is subject to the terms of the Mozilla |
| // Public License v. 2.0. If a copy of the MPL was not distributed |
| // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| #ifndef EIGEN_FFT_FFTW_IMPL_H |
| #define EIGEN_FFT_FFTW_IMPL_H |
| |
| // IWYU pragma: private |
| #include "./InternalHeaderCheck.h" |
| |
| #include <memory> |
| #include <mutex> |
| |
| namespace Eigen { |
| |
| namespace internal { |
| |
| // FFTW uses non-const arguments, |
| // so const_cast is needed for all the args it uses. |
| // |
| // This should be safe as long as |
| // 1. we use FFTW_ESTIMATE for all our planning |
| // see the FFTW docs section 4.3.2 "Planner Flags" |
| // 2. fftw_complex is compatible with std::complex |
| // This assumes std::complex<T> layout is array of size 2 with real,imag |
| template <typename T> |
| inline T *fftw_cast(const T *p) { |
| return const_cast<T *>(p); |
| } |
| |
| inline fftw_complex *fftw_cast(const std::complex<double> *p) { |
| return const_cast<fftw_complex *>(reinterpret_cast<const fftw_complex *>(p)); |
| } |
| |
| inline fftwf_complex *fftw_cast(const std::complex<float> *p) { |
| return const_cast<fftwf_complex *>(reinterpret_cast<const fftwf_complex *>(p)); |
| } |
| |
| inline fftwl_complex *fftw_cast(const std::complex<long double> *p) { |
| return const_cast<fftwl_complex *>(reinterpret_cast<const fftwl_complex *>(p)); |
| } |
| |
| // The FFTW planner is not thread-safe: fftw_execute and its new-array variants, |
| // which is what this backend runs transforms through, are the only entry points |
| // that may be called concurrently (FFTW manual, "Thread safety"), so plan |
| // creation and destruction are serialized through this mutex. A template |
| // static gives the header-only definition; std::mutex is |
| // constexpr-constructible, so the mutex is ready before any thread starts. |
| // The planner state it stands in for is one per process, so the mutex must be |
| // too: the explicit default visibility is what keeps the definition from being |
| // bound locally under -fvisibility=hidden, where each library planning through |
| // Eigen would get a mutex of its own and serialize nothing between them. The |
| // module documentation covers what no header can reach, which needs FFTW's own |
| // fftw_make_planner_thread_safe(). |
| #if EIGEN_HAS_ATTRIBUTE(visibility) && !EIGEN_OS_WIN |
| #define EIGEN_FFTW_PLANNER_MUTEX_VISIBILITY __attribute__((visibility("default"))) |
| #else |
| #define EIGEN_FFTW_PLANNER_MUTEX_VISIBILITY |
| #endif |
| |
| template <typename Dummy = void> |
| struct fftw_planner_lock { |
| static EIGEN_FFTW_PLANNER_MUTEX_VISIBILITY std::mutex mutex; |
| }; |
| template <typename Dummy> |
| EIGEN_FFTW_PLANNER_MUTEX_VISIBILITY std::mutex fftw_planner_lock<Dummy>::mutex; |
| |
| inline std::mutex &fftw_planner_mutex() { return fftw_planner_lock<>::mutex; } |
| |
| template <typename PlanFactory> |
| inline decltype(auto) fftw_make_plan(PlanFactory factory) { |
| std::lock_guard<std::mutex> lock(fftw_planner_mutex()); |
| return factory(); |
| } |
| |
| template <typename T> |
| struct fftw_plan {}; |
| |
| template <> |
| struct fftw_plan<float> { |
| typedef float scalar_type; |
| typedef fftwf_complex complex_type; |
| std::shared_ptr<fftwf_plan_s> m_plan; |
| fftw_plan() = default; |
| |
| void set_plan(fftwf_plan p) { |
| m_plan.reset(p, [](fftwf_plan plan) { |
| std::lock_guard<std::mutex> lock(fftw_planner_mutex()); |
| fftwf_destroy_plan(plan); |
| }); |
| } |
| inline void fwd(complex_type *dst, complex_type *src, int nfft) { |
| if (!m_plan) |
| set_plan(fftw_make_plan( |
| [&] { return fftwf_plan_dft_1d(nfft, src, dst, FFTW_FORWARD, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftwf_execute_dft(m_plan.get(), src, dst); |
| } |
| inline void inv(complex_type *dst, complex_type *src, int nfft) { |
| if (!m_plan) |
| set_plan(fftw_make_plan( |
| [&] { return fftwf_plan_dft_1d(nfft, src, dst, FFTW_BACKWARD, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftwf_execute_dft(m_plan.get(), src, dst); |
| } |
| inline void fwd(complex_type *dst, scalar_type *src, int nfft) { |
| if (!m_plan) |
| set_plan( |
| fftw_make_plan([&] { return fftwf_plan_dft_r2c_1d(nfft, src, dst, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftwf_execute_dft_r2c(m_plan.get(), src, dst); |
| } |
| inline void inv(scalar_type *dst, complex_type *src, int nfft) { |
| if (!m_plan) |
| set_plan( |
| fftw_make_plan([&] { return fftwf_plan_dft_c2r_1d(nfft, src, dst, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftwf_execute_dft_c2r(m_plan.get(), src, dst); |
| } |
| |
| inline void fwd2(complex_type *dst, complex_type *src, int n0, int n1) { |
| if (!m_plan) |
| set_plan(fftw_make_plan( |
| [&] { return fftwf_plan_dft_2d(n0, n1, src, dst, FFTW_FORWARD, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftwf_execute_dft(m_plan.get(), src, dst); |
| } |
| inline void inv2(complex_type *dst, complex_type *src, int n0, int n1) { |
| if (!m_plan) |
| set_plan(fftw_make_plan( |
| [&] { return fftwf_plan_dft_2d(n0, n1, src, dst, FFTW_BACKWARD, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftwf_execute_dft(m_plan.get(), src, dst); |
| } |
| }; |
| template <> |
| struct fftw_plan<double> { |
| typedef double scalar_type; |
| typedef fftw_complex complex_type; |
| std::shared_ptr<fftw_plan_s> m_plan; |
| fftw_plan() = default; |
| |
| void set_plan(::fftw_plan p) { |
| m_plan.reset(p, [](::fftw_plan plan) { |
| std::lock_guard<std::mutex> lock(fftw_planner_mutex()); |
| fftw_destroy_plan(plan); |
| }); |
| } |
| inline void fwd(complex_type *dst, complex_type *src, int nfft) { |
| if (!m_plan) |
| set_plan(fftw_make_plan( |
| [&] { return fftw_plan_dft_1d(nfft, src, dst, FFTW_FORWARD, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftw_execute_dft(m_plan.get(), src, dst); |
| } |
| inline void inv(complex_type *dst, complex_type *src, int nfft) { |
| if (!m_plan) |
| set_plan(fftw_make_plan( |
| [&] { return fftw_plan_dft_1d(nfft, src, dst, FFTW_BACKWARD, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftw_execute_dft(m_plan.get(), src, dst); |
| } |
| inline void fwd(complex_type *dst, scalar_type *src, int nfft) { |
| if (!m_plan) |
| set_plan( |
| fftw_make_plan([&] { return fftw_plan_dft_r2c_1d(nfft, src, dst, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftw_execute_dft_r2c(m_plan.get(), src, dst); |
| } |
| inline void inv(scalar_type *dst, complex_type *src, int nfft) { |
| if (!m_plan) |
| set_plan( |
| fftw_make_plan([&] { return fftw_plan_dft_c2r_1d(nfft, src, dst, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftw_execute_dft_c2r(m_plan.get(), src, dst); |
| } |
| inline void fwd2(complex_type *dst, complex_type *src, int n0, int n1) { |
| if (!m_plan) |
| set_plan(fftw_make_plan( |
| [&] { return fftw_plan_dft_2d(n0, n1, src, dst, FFTW_FORWARD, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftw_execute_dft(m_plan.get(), src, dst); |
| } |
| inline void inv2(complex_type *dst, complex_type *src, int n0, int n1) { |
| if (!m_plan) |
| set_plan(fftw_make_plan( |
| [&] { return fftw_plan_dft_2d(n0, n1, src, dst, FFTW_BACKWARD, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftw_execute_dft(m_plan.get(), src, dst); |
| } |
| }; |
| template <> |
| struct fftw_plan<long double> { |
| typedef long double scalar_type; |
| typedef fftwl_complex complex_type; |
| std::shared_ptr<fftwl_plan_s> m_plan; |
| fftw_plan() = default; |
| |
| void set_plan(fftwl_plan p) { |
| m_plan.reset(p, [](fftwl_plan plan) { |
| std::lock_guard<std::mutex> lock(fftw_planner_mutex()); |
| fftwl_destroy_plan(plan); |
| }); |
| } |
| inline void fwd(complex_type *dst, complex_type *src, int nfft) { |
| if (!m_plan) |
| set_plan(fftw_make_plan( |
| [&] { return fftwl_plan_dft_1d(nfft, src, dst, FFTW_FORWARD, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftwl_execute_dft(m_plan.get(), src, dst); |
| } |
| inline void inv(complex_type *dst, complex_type *src, int nfft) { |
| if (!m_plan) |
| set_plan(fftw_make_plan( |
| [&] { return fftwl_plan_dft_1d(nfft, src, dst, FFTW_BACKWARD, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftwl_execute_dft(m_plan.get(), src, dst); |
| } |
| inline void fwd(complex_type *dst, scalar_type *src, int nfft) { |
| if (!m_plan) |
| set_plan( |
| fftw_make_plan([&] { return fftwl_plan_dft_r2c_1d(nfft, src, dst, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftwl_execute_dft_r2c(m_plan.get(), src, dst); |
| } |
| inline void inv(scalar_type *dst, complex_type *src, int nfft) { |
| if (!m_plan) |
| set_plan( |
| fftw_make_plan([&] { return fftwl_plan_dft_c2r_1d(nfft, src, dst, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftwl_execute_dft_c2r(m_plan.get(), src, dst); |
| } |
| inline void fwd2(complex_type *dst, complex_type *src, int n0, int n1) { |
| if (!m_plan) |
| set_plan(fftw_make_plan( |
| [&] { return fftwl_plan_dft_2d(n0, n1, src, dst, FFTW_FORWARD, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftwl_execute_dft(m_plan.get(), src, dst); |
| } |
| inline void inv2(complex_type *dst, complex_type *src, int n0, int n1) { |
| if (!m_plan) |
| set_plan(fftw_make_plan( |
| [&] { return fftwl_plan_dft_2d(n0, n1, src, dst, FFTW_BACKWARD, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); })); |
| fftwl_execute_dft(m_plan.get(), src, dst); |
| } |
| }; |
| |
| template <typename Scalar_> |
| struct fftw_impl { |
| typedef Scalar_ Scalar; |
| typedef std::complex<Scalar> Complex; |
| |
| inline void clear() { m_plans.clear(); } |
| |
| // complex-to-complex forward FFT |
| inline void fwd(Complex *dst, const Complex *src, int nfft) { |
| get_plan(nfft, false, /*real_io=*/false, dst, src).fwd(fftw_cast(dst), fftw_cast(src), nfft); |
| } |
| |
| // real-to-complex forward FFT |
| inline void fwd(Complex *dst, const Scalar *src, int nfft) { |
| get_plan(nfft, false, /*real_io=*/true, dst, src).fwd(fftw_cast(dst), fftw_cast(src), nfft); |
| } |
| |
| // 2-d complex-to-complex |
| inline void fwd2(Complex *dst, const Complex *src, int n0, int n1) { |
| get_plan(n0, n1, false, /*real_io=*/false, dst, src).fwd2(fftw_cast(dst), fftw_cast(src), n0, n1); |
| } |
| |
| // inverse complex-to-complex |
| inline void inv(Complex *dst, const Complex *src, int nfft) { |
| get_plan(nfft, true, /*real_io=*/false, dst, src).inv(fftw_cast(dst), fftw_cast(src), nfft); |
| } |
| |
| // half-complex to scalar |
| inline void inv(Scalar *dst, const Complex *src, int nfft) { |
| get_plan(nfft, true, /*real_io=*/true, dst, src).inv(fftw_cast(dst), fftw_cast(src), nfft); |
| } |
| |
| // 2-d complex-to-complex |
| inline void inv2(Complex *dst, const Complex *src, int n0, int n1) { |
| get_plan(n0, n1, true, /*real_io=*/false, dst, src).inv2(fftw_cast(dst), fftw_cast(src), n0, n1); |
| } |
| |
| protected: |
| typedef fftw_plan<Scalar> PlanData; |
| |
| typedef Eigen::numext::int64_t int64_t; |
| |
| typedef std::map<int64_t, PlanData> PlanMap; |
| |
| PlanMap m_plans; |
| |
| // Pack (inverse, real_io, inplace, aligned) into 4 contiguous low bits of |
| // the cache key. real_io distinguishes r2c/c2r from c2c so that reusing |
| // the same FFT object across real-input and complex-input transforms |
| // doesn't return a cached plan of the wrong kind. |
| static int64_t plan_flags(bool inverse, bool real_io, void *dst, const void *src) { |
| bool inplace = (dst == src); |
| bool aligned = ((reinterpret_cast<size_t>(src) & 15) | (reinterpret_cast<size_t>(dst) & 15)) == 0; |
| return (inverse << 3) | (real_io << 2) | (inplace << 1) | aligned; |
| } |
| |
| inline PlanData &get_plan(int nfft, bool inverse, bool real_io, void *dst, const void *src) { |
| int64_t key = ((nfft << 4) | plan_flags(inverse, real_io, dst, src)) << 1; |
| return m_plans[key]; |
| } |
| |
| inline PlanData &get_plan(int n0, int n1, bool inverse, bool real_io, void *dst, const void *src) { |
| int64_t key = (((((int64_t)n0) << 31) | (n1 << 4) | plan_flags(inverse, real_io, dst, src)) << 1) + 1; |
| return m_plans[key]; |
| } |
| }; |
| |
| } // end namespace internal |
| |
| } // end namespace Eigen |
| |
| #endif // EIGEN_FFT_FFTW_IMPL_H |