From b8bb48bc75a8196d0ada82ea4cc5debeb006e323 Mon Sep 17 00:00:00 2001 From: Justin Muncaster Date: Tue, 27 Oct 2020 10:55:29 -0700 Subject: [PATCH 01/20] Adjust dependency finding so lib works better as submodule - Check for xtensor and xtl targets, similar to how xtensor-python does it. - Adjust path to FindFFTW.cmake so that it uses CMAKE_CURRENT_SOURCE_DIR instead of CMAKE_SOURCE_DIR --- CMakeLists.txt | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 277343f..ac51fa1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,8 +31,8 @@ message(STATUS "Building xtensor-fftw v${${PROJECT_NAME}_VERSION}") #--------------------------------------- cmake modules -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/findFFTW/") +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/") +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/findFFTW/") #--------------------------------------- user options set(FFTW_ROOT "" CACHE STRING "The FFTW prefix, i.e. the base directory under which FFTW is installed (see README.md).") @@ -67,15 +67,33 @@ include_directories(${XTENSOR_FFTW_INCLUDE_DIR}) # .. xtensor set(xtensor_REQUIRED_VERSION 0.20.9) -find_package(xtensor ${xtensor_REQUIRED_VERSION} REQUIRED) -message(STATUS "Found xtensor: ${xtensor_INCLUDE_DIRS}/xtensor") -include_directories(${xtensor_INCLUDE_DIRS}) +if(TARGET xtensor) + set(xtensor_VERSION ${XTENSOR_VERSION_MAJOR}.${XTENSOR_VERSION_MINOR}.${XTENSOR_VERSION_PATCH}) + # Note: This is not SEMVER compatible comparison + if( NOT ${xtensor_VERSION} VERSION_GREATER_EQUAL ${xtensor_REQUIRED_VERSION}) + message(ERROR "Mismatch xtensor versions. Found '${xtensor_VERSION}' but requires: '${xtensor_REQUIRED_VERSION}'") + else() + message(STATUS "Found xtensor v${xtensor_VERSION}") + endif() +else() + find_package(xtensor ${xtensor_REQUIRED_VERSION} REQUIRED) + message(STATUS "Found xtensor: ${xtensor_INCLUDE_DIRS}/xtensor") +endif() # .. xtl set(xtl_REQUIRED_VERSION 0.6.9) -find_package(xtl ${xtl_REQUIRED_VERSION} REQUIRED) -message(STATUS "Found xtl: ${xtl_INCLUDE_DIRS}/xtl") -include_directories(${xtl_INCLUDE_DIRS}) +if(TARGET xtl) + set(xtl_VERSION ${XTL_VERSION_MAJOR}.${XTL_VERSION_MINOR}.${XTL_VERSION_PATCH}) + # Note: This is not SEMVER compatible comparison + if( NOT ${xtl_VERSION} VERSION_GREATER_EQUAL ${xtl_REQUIRED_VERSION}) + message(ERROR "Mismatch xtl versions. Found '${xtl_VERSION}' but requires: '${xtl_REQUIRED_VERSION}'") + else() + message(STATUS "Found xtl v${xtl_VERSION}") + endif() +else() + find_package(xtl ${xtl_REQUIRED_VERSION} REQUIRED) + message(STATUS "Found xtl: ${xtl_INCLUDE_DIRS}/xtl") +endif() # .. fftw if(MSVC) From add75eac5b17e9b7a8d72b26caaeaa847092d6ab Mon Sep 17 00:00:00 2001 From: Sylvain Corlay Date: Tue, 13 Apr 2021 20:37:02 +0200 Subject: [PATCH 02/20] Update installation instructions --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c23cf09..d4c37b2 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,10 @@ Syntax and functionality are inspired by `numpy.fft`, the FFT module in the Pyth ## Installation -Using `conda`: +Using `mamba` (or conda): ```bash -conda install xtensor-fftw -c conda-forge +mamba install xtensor-fftw -c conda-forge ``` This automatically installs dependencies as well (see [list of dependencies](#dependencies) below). From 9f257d562564a0d09f4c70774395f916ec204179 Mon Sep 17 00:00:00 2001 From: michael bacci Date: Sat, 15 May 2021 22:23:31 +0200 Subject: [PATCH 03/20] Remove all FFTW precision dependencies --- .gitignore | 6 + CMakeLists.txt | 100 ++- README.md | 84 +- bench/CMakeLists.txt | 2 +- bench/basic_interface.cpp | 78 +- include/xtensor-fftw/basic.hpp | 907 +-------------------- include/xtensor-fftw/basic_double.hpp | 196 +++++ include/xtensor-fftw/basic_float.hpp | 196 +++++ include/xtensor-fftw/basic_long_double.hpp | 196 +++++ include/xtensor-fftw/basic_option.hpp | 35 + include/xtensor-fftw/common.hpp | 519 ++++++++++++ test/CMakeLists.txt | 2 +- test/basic_interface.hpp | 31 +- test/basic_interface_fft.cpp | 2 +- test/basic_interface_hfft.cpp | 2 +- test/basic_interface_rfft.cpp | 2 +- test/examples.cpp | 6 +- 17 files changed, 1408 insertions(+), 956 deletions(-) create mode 100644 include/xtensor-fftw/basic_double.hpp create mode 100644 include/xtensor-fftw/basic_float.hpp create mode 100644 include/xtensor-fftw/basic_long_double.hpp create mode 100644 include/xtensor-fftw/basic_option.hpp create mode 100644 include/xtensor-fftw/common.hpp diff --git a/.gitignore b/.gitignore index 6850e60..22b8af1 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,9 @@ *.exe *.out *.app + +# cmake +build + +# vscode +.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt index ac51fa1..495dfd5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,8 @@ cmake_minimum_required(VERSION 3.1.3) # 3.1.3 for set(CMAKE_CXX_STANDARD 14) project(xtensor-fftw) set(XTENSOR_FFTW_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) - +set(FFTW_INCLUDE_CUSTOM_DIRS "" CACHE STRING "Set the FFTW include dir without the requirement of FFTW installation.") +set(FFTW_LINK_FLAGS "" CACHE STRING "Set the CXX library to link, e.g.: -L/usr/local -lfftw3") #--------------------------------------- versioning file(STRINGS "${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/xtensor-fftw_config.hpp" xtensor-fftw_version_defines @@ -46,6 +47,35 @@ OPTION(FIX_RPATH "Correctly set rpath for the linker" OFF) OPTION(DEFAULT_COLUMN_MAJOR "Set xtensor default layout to column major. This is currently not supported, since FFTW demands row major layout." OFF) OPTION(COVERAGE "Enable coverage compile flags (gcc only!)" OFF) OPTION(DISABLE_EXCEPTIONS "Disable C++ exceptions" OFF) +OPTION(FFTW_USE_FLOAT "Enable FFTW Float type" ON) +OPTION(FFTW_USE_DOUBLE "Enable FFTW Double type" ON) +OPTION(FFTW_USE_LONG_DOUBLE "Enable FFTW Long Double type" ON) + +if(FFTW_USE_FLOAT) + add_definitions(-DXTENSOR_FFTW_USE_FLOAT) + set(REQUIRE_FLOAT_LIB "FLOAT_LIB") +else() + set(REQUIRE_FLOAT_LIB "") +endif() + +if(FFTW_USE_DOUBLE) + add_definitions(-DXTENSOR_FFTW_USE_DOUBLE) + set(REQUIRE_DOUBLE_LIB "DOUBLE_LIB") +else() + set(REQUIRE_DOUBLE_LIB "") +endif() + +if(FFTW_USE_LONG_DOUBLE AND NOT MSVC) + add_definitions(-DXTENSOR_FFTW_USE_LONG_DOUBLE) + set(REQUIRE_LONG_DOUBLE_LIB "LONGDOUBLE_LIB") +else() + set(REQUIRE_LONG_DOUBLE_LIB "") + set(FFTW_USE_LONG_DOUBLE OFF) +endif() + +if(NOT FFTW_USE_FLOAT AND NOT FFTW_USE_DOUBLE AND NOT FFTW_USE_LONG_DOUBLE) + message(FATAL_ERROR "Please, select at least one of the available FFTW type libraries") +endif() if (COVERAGE) if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU") @@ -65,21 +95,6 @@ set(CMAKE_CXX_EXTENSIONS NO) # .. our own include_directories(${XTENSOR_FFTW_INCLUDE_DIR}) -# .. xtensor -set(xtensor_REQUIRED_VERSION 0.20.9) -if(TARGET xtensor) - set(xtensor_VERSION ${XTENSOR_VERSION_MAJOR}.${XTENSOR_VERSION_MINOR}.${XTENSOR_VERSION_PATCH}) - # Note: This is not SEMVER compatible comparison - if( NOT ${xtensor_VERSION} VERSION_GREATER_EQUAL ${xtensor_REQUIRED_VERSION}) - message(ERROR "Mismatch xtensor versions. Found '${xtensor_VERSION}' but requires: '${xtensor_REQUIRED_VERSION}'") - else() - message(STATUS "Found xtensor v${xtensor_VERSION}") - endif() -else() - find_package(xtensor ${xtensor_REQUIRED_VERSION} REQUIRED) - message(STATUS "Found xtensor: ${xtensor_INCLUDE_DIRS}/xtensor") -endif() - # .. xtl set(xtl_REQUIRED_VERSION 0.6.9) if(TARGET xtl) @@ -95,18 +110,51 @@ else() message(STATUS "Found xtl: ${xtl_INCLUDE_DIRS}/xtl") endif() +# .. xtensor +set(xtensor_REQUIRED_VERSION 0.20.9) +if(TARGET xtensor) + set(xtensor_VERSION ${XTENSOR_VERSION_MAJOR}.${XTENSOR_VERSION_MINOR}.${XTENSOR_VERSION_PATCH}) + # Note: This is not SEMVER compatible comparison + if( NOT ${xtensor_VERSION} VERSION_GREATER_EQUAL ${xtensor_REQUIRED_VERSION}) + message(ERROR "Mismatch xtensor versions. Found '${xtensor_VERSION}' but requires: '${xtensor_REQUIRED_VERSION}'") + else() + message(STATUS "Found xtensor v${xtensor_VERSION}") + endif() +else() + find_package(xtensor ${xtensor_REQUIRED_VERSION} REQUIRED) + message(STATUS "Found xtensor: ${xtensor_INCLUDE_DIRS}/xtensor") +endif() + # .. fftw -if(MSVC) - # no long double component, since in the Windows conda-forge build it is not available - # and the "official" prebuilt long double library can only be used from MinGW - find_package(FFTW REQUIRED - COMPONENTS FLOAT_LIB DOUBLE_LIB) - add_definitions(-DFFTW_NO_LONGDOUBLE) +if(NOT "${FFTW_INCLUDE_CUSTOM_DIRS}" STREQUAL "") + include_directories(${FFTW_INCLUDE_CUSTOM_DIRS}) else() - find_package(FFTW REQUIRED - COMPONENTS FLOAT_LIB DOUBLE_LIB LONGDOUBLE_LIB) -endif(MSVC) -include_directories(${FFTW_INCLUDE_DIRS}) + if(MSVC) + # no long double component, since in the Windows conda-forge build it is not available + # and the "official" prebuilt long double library can only be used from MinGW + find_package(FFTW REQUIRED + COMPONENTS ${REQUIRE_FLOAT_LIB} ${REQUIRE_DOUBLE_LIB}) + add_definitions(-DFFTW_NO_LONGDOUBLE) + else(MSVC) + find_package(FFTW REQUIRED + COMPONENTS ${REQUIRE_FLOAT_LIB} ${REQUIRE_DOUBLE_LIB} ${REQUIRE_LONG_DOUBLE_LIB}) + endif() + include_directories(${FFTW_INCLUDE_DIRS}) + + # link only with selected libraries + set(FFTW_LINK_FLAGS "") + if(FFTW_USE_FLOAT) + set(FFTW_LINK_FLAGS ${FFTW_FLOAT_LIB}) + endif() + + if(FFTW_USE_DOUBLE) + set(FFTW_LINK_FLAGS ${FFTW_LINK_FLAGS} ${FFTW_DOUBLE_LIB}) + endif() + + if(FFTW_USE_LONG_DOUBLE) + set(FFTW_LINK_FLAGS ${FFTW_LINK_FLAGS} ${FFTW_LONGDOUBLE_LIB}) + endif() +endif() # warnings (gcc and clang) if (COMPILE_WARNINGS) diff --git a/README.md b/README.md index d4c37b2..c5c3394 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,25 @@ _xtensor-fftw_ is a header-only library. To use, include one of the header files in the `include` directory, e.g. `xtensor-fftw/basic.hpp`, in your c++ code. To compile, one should also include the paths to the FFTW header and libraries and link to the appropriate FFTW library. +FFTW allows three modes of calculus : `float`, `double` and `long double`. +The impact of the precision type can be see below in the benchmark results. +Use the following matrix to include, compile and link the right target: + +| `#include` | `precision types` | `xtensor-fftw compile options` | `FFTW compile options` | +|------------------------------------|----------------------------|-----------------------------------|-------------------------| +| xtensor-fftw/basic_float.hpp | float | -DXTENSOR_FFTW_USE_FLOAT=ON | -DENABLE_FLOAT=ON | +| xtensor-fftw/basic_double.hpp | double | -DXTENSOR_FFTW_USE_DOUBLE=ON | -DENABLE_DOUBLE=ON | +| xtensor-fftw/basic_long_double.hpp | long double | -DXTENSOR_FFTW_USE_LONG_DOUBLE=ON | -DENABLE_LONGDOUBLE=ON | +| xtensor-fftw/basic_option.hpp | depends by compile options | subset of above options | subset of above options | +| xtensor-fftw/basic.hpp | all types | no option | all above options | + +Specify only the required precision type to reduce the dependencies size of your application (for example for a Mobile App it matters), in fact FFTW needs to compile a specific library for each precision thus creating: +* `libfftw3f` for float precision +* `libfftw3` for double precision +* `libfftw3l` for long double precision + +>__*Notes*__: FFTW allow SIMD instructions (SSE,SSE2,AVX,AVX2), OpenMP and Threads optimizations. Take a look to the availables options before compile it. + The functions in `xtensor-fftw/basic.hpp` mimic the behavior of `numpy.fft` as much as possible. In most cases transforms on identical input data should produce identical results within reasonable machine precision error bounds. However, there are a few differences that one should keep in mind: @@ -126,11 +145,10 @@ What follows are instructions for compiling and running the _xtensor-fftw_ tests These also serve as an example of how to do build your own code using _xtensor-fftw_ (excluding the GoogleTest specific parts). ### Dependencies for building tests + The main dependency is a version of FFTW 3. -For the tests, we need the floating point version which is enabled in the FFTW configuration step using: -```bash -./configure --enable-float -``` +To enable all the precision types, FFTW must be compiled with the related flags: +`cmake -DENABLE_FLOAT:BOOL=ON -DENABLE_LONGDOUBLE:BOOL=ON /path/of/fftw3-src` CMake and _xtensor_ must also be installed in order to compile the _xtensor-fftw_ tests. Both can either be installed through Conda or built/installed manually. @@ -179,6 +197,64 @@ cd test ./test_xtensor-fftw ``` +## Advanced Setting + +This section shows how to configure `cmake` in order to exploit advanced settings. + +### Use only Double precision + +After a standard installation of FFTW library without specify a particular options, this command allow to run Test and Benchmarks using only `double` precision: + +```cmake +cmake -DBUILD_BENCHMARK=ON -DDOWNLOAD_GBENCH=ON -DBUILD_TESTS=ON -DDOWNLOAD_GTEST=ON -DFFTW_USE_FLOAT=OFF -DFFTW_USE_LONG_DOUBLE=OFF -DFFTW_USE_DOUBLE=ON -DCMAKE_BUILD_TYPE=Release .. +``` + +Let's see what `./bench/benchmark_xtensor-fftw` produce: + +``` +Run on (16 X 2300 MHz CPU s) +------------------------------------------------------------------------------- +Benchmark Time CPU Iterations +------------------------------------------------------------------------------- +rfft1Dxarray_double/TransformAndInvert 66375 ns 66354 ns 10149 +rfft1Dxarray_double/TransformAndInvert_nD 70856 ns 70829 ns 10128 +rfft2Dxarray_double/TransformAndInvert 61264 ns 61256 ns 11456 +rfft2Dxarray_double/TransformAndInvert_nD 62297 ns 62269 ns 10851 +``` + +### Manually specify FFTW headers and link flags + +This can be very useful: in this case FFTW is not required to be installed, just compiled. +The following command produce the same results as before: + +```cmake +cmake -DBUILD_BENCHMARK=ON -DDOWNLOAD_GBENCH=ON -DBUILD_TESTS=ON -DDOWNLOAD_GTEST=ON -DFFTW_USE_FLOAT=OFF -DFFTW_USE_LONG_DOUBLE=OFF -DFFTW_USE_DOUBLE=ON -DFFTW_INCLUDE_CUSTOM_DIRS=/path/to/fftw3/api -DFFTW_LINK_FLAGS="-L/path/to/fftw3/build -lfftw3" .. +``` + +### Use Intel MKL + +Since 2018 Intel has release a version of his famous MKL (Math Kernel Library) with a C++ and Fortran wrapper of FFTW. +Once MKL (or oneAPI MKL) installed on the system enter the following command with adjusted path to your system: + +```cmake +cmake -DBUILD_BENCHMARK=ON -DDOWNLOAD_GBENCH=ON -DBUILD_TESTS=ON -DDOWNLOAD_GTEST=ON -DFFTW_USE_FLOAT=OFF -DFFTW_USE_LONG_DOUBLE=OFF -DFFTW_USE_DOUBLE=ON -DFFTW_INCLUDE_CUSTOM_DIRS=/opt/intel/oneapi/mkl/2021.2.0/include/fftw -DFFTW_LINK_FLAGS="-L/opt/intel/oneapi/mkl/2021.2.0/lib -L/opt/intel/oneapi/compiler/2021.2.0/mac/compiler/lib -lmkl_core -lmkl_intel_thread -lmkl_intel_lp64 -liomp5" -DRUN_HAVE_STD_REGEX=0 -DCMAKE_BUILD_TYPE=Release .. +``` + +Let's see what `./bench/benchmark_xtensor-fftw` now produce: + +``` +Run on (16 X 2300 MHz CPU s) +------------------------------------------------------------------------------- +Benchmark Time CPU Iterations +------------------------------------------------------------------------------- +rfft1Dxarray_double/TransformAndInvert 9265 ns 9258 ns 58371 +rfft1Dxarray_double/TransformAndInvert_nD 9636 ns 9602 ns 73961 +rfft2Dxarray_double/TransformAndInvert 34428 ns 34427 ns 20216 +rfft2Dxarray_double/TransformAndInvert_nD 37401 ns 37393 ns 19480 +``` + +>__*Note*__: Before running test or benchmark remember to export the intel library path, e.g. on OS X: `export DYLD_LIBRARY_PATH=/opt/intel/oneapi/mkl/2021.2.0/lib/:/opt/intel/oneapi/compiler/2021.2.0/mac/compiler/lib/` + ## License We use a shared copyright model that enables all contributors to maintain the diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index ae63039..b84cd80 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -101,6 +101,6 @@ add_executable(${XTENSOR_FFTW_TARGET} ${XTENSOR_FFTW_BENCHMARKS} ${XTENSOR_HEADE if(DOWNLOAD_GBENCH OR GBENCH_SRC_DIR) add_dependencies(${XTENSOR_FFTW_TARGET} benchmark) endif() -target_link_libraries(${XTENSOR_FFTW_TARGET} ${benchmark_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${FFTW_FLOAT_LIB} ${FFTW_DOUBLE_LIB} ${FFTW_LONGDOUBLE_LIB}) +target_link_libraries(${XTENSOR_FFTW_TARGET} ${benchmark_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${FFTW_LINK_FLAGS}) add_custom_target(xbench COMMAND benchmark_xtensor-fftw DEPENDS ${XTENSOR_FFTW_TARGET}) diff --git a/bench/basic_interface.cpp b/bench/basic_interface.cpp index 899fb56..407b1f5 100644 --- a/bench/basic_interface.cpp +++ b/bench/basic_interface.cpp @@ -14,7 +14,7 @@ #include // pow #include -#include "xtensor-fftw/basic.hpp" +#include "xtensor-fftw/basic_option.hpp" #include "benchmark/benchmark.h" @@ -49,29 +49,58 @@ class rfft1Dxarray : public ::benchmark::Fixture { xt::xarray a; }; -using rfft1Dxarray_float = rfft1Dxarray; - -BENCHMARK_F(rfft1Dxarray_float, TransformAndInvert)(::benchmark::State& st) { +auto TransformAndInvert = [](auto& a, ::benchmark::State& st) { while (st.KeepRunning()) { auto a_fourier = xt::fftw::rfft(a); ::benchmark::DoNotOptimize(a_fourier); auto should_be_a = xt::fftw::irfft(a_fourier); ::benchmark::DoNotOptimize(should_be_a); } -} +}; //// // Real FFT: nD with n = 1 //// -BENCHMARK_F(rfft1Dxarray_float, TransformAndInvert_nD)(::benchmark::State& st) { +auto TransformAndInvert_nD = [](auto& a, ::benchmark::State& st) { while (st.KeepRunning()) { auto a_fourier = xt::fftw::rfftn<1>(a); ::benchmark::DoNotOptimize(a_fourier); auto should_be_a = xt::fftw::irfftn<1>(a_fourier); ::benchmark::DoNotOptimize(should_be_a); } +}; + +#ifdef XTENSOR_FFTW_USE_FLOAT +using rfft1Dxarray_float = rfft1Dxarray; +BENCHMARK_F(rfft1Dxarray_float, TransformAndInvert)(::benchmark::State& st) { + TransformAndInvert(a, st); +} +BENCHMARK_F(rfft1Dxarray_float, TransformAndInvert_nD)(::benchmark::State& st) { + TransformAndInvert_nD(a, st); +} +#endif + +#ifdef XTENSOR_FFTW_USE_DOUBLE +using rfft1Dxarray_double = rfft1Dxarray; +BENCHMARK_F(rfft1Dxarray_double, TransformAndInvert)(::benchmark::State& st) { + TransformAndInvert(a, st); +} +BENCHMARK_F(rfft1Dxarray_double, TransformAndInvert_nD)(::benchmark::State& st) { + TransformAndInvert_nD(a, st); +} +#endif + +#ifdef XTENSOR_FFTW_USE_LONG_DOUBLE +using rfft1Dxarray_longdouble = rfft1Dxarray; +BENCHMARK_F(rfft1Dxarray_longdouble, TransformAndInvert)(::benchmark::State& st) { + TransformAndInvert(a, st); +} +BENCHMARK_F(rfft1Dxarray_longdouble, TransformAndInvert_nD)(::benchmark::State& st) { + TransformAndInvert_nD(a, st); } +#endif + //// // Real FFT: 2D @@ -95,30 +124,59 @@ class rfft2Dxarray : public ::benchmark::Fixture { xt::xarray a; }; -using rfft2Dxarray_float = rfft2Dxarray; -BENCHMARK_F(rfft2Dxarray_float, TransformAndInvert)(::benchmark::State& st) { +auto TransformAndInvert2 = [](auto& a, ::benchmark::State& st) { while (st.KeepRunning()) { auto a_fourier = xt::fftw::rfft2(a); ::benchmark::DoNotOptimize(a_fourier); auto should_be_a = xt::fftw::irfft2(a_fourier); ::benchmark::DoNotOptimize(should_be_a); } -} +}; + //// // Real FFT: nD with n = 2 //// -BENCHMARK_F(rfft2Dxarray_float, TransformAndInvert_nD)(::benchmark::State& st) { +auto TransformAndInvert_nD2 = [](auto& a, ::benchmark::State& st) { while (st.KeepRunning()) { auto a_fourier = xt::fftw::rfftn<2>(a); ::benchmark::DoNotOptimize(a_fourier); auto should_be_a = xt::fftw::irfftn<2>(a_fourier); ::benchmark::DoNotOptimize(should_be_a); } +}; + +#ifdef XTENSOR_FFTW_USE_FLOAT +using rfft2Dxarray_float = rfft2Dxarray; +BENCHMARK_F(rfft2Dxarray_float, TransformAndInvert)(::benchmark::State& st) { + TransformAndInvert2(a, st); } +BENCHMARK_F(rfft2Dxarray_float, TransformAndInvert_nD)(::benchmark::State& st) { + TransformAndInvert_nD2(a, st); +} +#endif +#ifdef XTENSOR_FFTW_USE_DOUBLE +using rfft2Dxarray_double = rfft2Dxarray; +BENCHMARK_F(rfft2Dxarray_double, TransformAndInvert)(::benchmark::State& st) { + TransformAndInvert2(a, st); +} +BENCHMARK_F(rfft2Dxarray_double, TransformAndInvert_nD)(::benchmark::State& st) { + TransformAndInvert_nD2(a, st); +} +#endif + +#ifdef XTENSOR_FFTW_USE_LONG_DOUBLE +using rfft2Dxarray_longdouble = rfft2Dxarray; +BENCHMARK_F(rfft2Dxarray_longdouble, TransformAndInvert)(::benchmark::State& st) { + TransformAndInvert2(a, st); +} +BENCHMARK_F(rfft2Dxarray_longdouble, TransformAndInvert_nD)(::benchmark::State& st) { + TransformAndInvert_nD2(a, st); +} +#endif //BENCHMARK_TEMPLATE_F(rfft1Dxarray, TransformAndInvert, double)(::benchmark::State& st) { // for (auto _ : st) { diff --git a/include/xtensor-fftw/basic.hpp b/include/xtensor-fftw/basic.hpp index 5412bbe..d58d70b 100644 --- a/include/xtensor-fftw/basic.hpp +++ b/include/xtensor-fftw/basic.hpp @@ -7,914 +7,15 @@ * The full license is in the file LICENSE, distributed with this software. * * basic.hpp: - * Contains the basic functions needed to do FFTs and inverse FFTs on real and - * complex arrays. The behavior of these functions mimics that of the numpy.fft - * module, see https://github.com/xtensor-stack/xtensor-fftw/issues/6. + * Union of all xtensor-fftw functionalities with all precision types. * */ #ifndef XTENSOR_FFTW_BASIC_HPP #define XTENSOR_FFTW_BASIC_HPP -#include -#include "xtensor/xcomplex.hpp" -#include "xtensor/xeval.hpp" -#include -#include -#include -#include -#include -#include - -// for product accumulate: -#include -#include - -#include - -#include "xtensor-fftw_config.hpp" - -#ifdef __CLING__ - #pragma cling load("fftw3") -#endif - -namespace xt { - namespace fftw { - // The implementations must be inline to avoid multiple definition errors due to multiple compilations (e.g. when - // including this header multiple times in a project, or when it is explicitly compiled itself and included too). - - // Note: multidimensional complex-to-real transforms by default destroy the input data! See: - // http://www.fftw.org/fftw3_doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data - - // reinterpret_casts below suggested by http://www.fftw.org/fftw3_doc/Complex-numbers.html - - // We use the convention that the inverse fft divides by N, like numpy does. - - // FFTW is not thread-safe, so we need to guard around its functions (except fftw_execute). - namespace detail { - inline std::mutex& fftw_global_mutex() { - static std::mutex m; - return m; - } - } - - /////////////////////////////////////////////////////////////////////////////// - // General: templates defining the basic interaction logic with fftw. These - // will be specialized for all fft families, precisions and - // dimensionalities. - /////////////////////////////////////////////////////////////////////////////// - - // aliases for the fftw precision-dependent types: - template struct fftw_t { - static_assert(sizeof(T) == 0, "Only specializations of fftw_t can be used"); - }; - template <> struct fftw_t { - using plan = fftwf_plan; - using complex = fftwf_complex; - constexpr static void (&execute)(plan) = fftwf_execute; - constexpr static void (&destroy_plan)(plan) = fftwf_destroy_plan; - }; - template <> struct fftw_t { - using plan = fftw_plan; - using complex = fftw_complex; - constexpr static void (&execute)(plan) = fftw_execute; - constexpr static void (&destroy_plan)(plan) = fftw_destroy_plan; - }; - template <> struct fftw_t { - using plan = fftwl_plan; - using complex = fftwl_complex; - constexpr static void (&execute)(plan) = fftwl_execute; - constexpr static void (&destroy_plan)(plan) = fftwl_destroy_plan; - }; - // and subclass alias for when calling with a complex type: - template struct fftw_t< std::complex > : public fftw_t {}; - - // convert std::complex to fftwX_complex with right precision X; non-complex floats stay themselves: - template - using fftw_number_t = std::conditional_t< - xtl::is_complex::value, - typename fftw_t< xtl::complex_value_type_t >::complex, - xtl::complex_value_type_t - >; - - // short-hand for precision for template arguments - template - using prec_t = xtl::complex_value_type_t; - - // dimension-dependent function signatures of fftw planning functions - template - struct fftw_plan_dft_signature {}; - - template - struct fftw_plan_dft_signature { - using type = typename fftw_t::plan (&)(int rank, const int *n, fftw_number_t *, fftw_number_t *, unsigned int); - }; - template - struct fftw_plan_dft_signature { - using type = typename fftw_t::plan (&)(int n1, fftw_number_t *, fftw_number_t *, unsigned int); - }; - template - struct fftw_plan_dft_signature { - using type = typename fftw_t::plan (&)(int n1, int n2, fftw_number_t *, fftw_number_t *, unsigned int); - }; - template - struct fftw_plan_dft_signature { - using type = typename fftw_t::plan (&)(int n1, int n2, int n3, fftw_number_t *, fftw_number_t *, unsigned int); - }; - - template - struct fftw_plan_dft_signature { - using type = typename fftw_t::plan (&)(int rank, const int *n, fftw_number_t *, fftw_number_t *, int, unsigned int); - }; - template - struct fftw_plan_dft_signature { - using type = typename fftw_t::plan (&)(int n1, fftw_number_t *, fftw_number_t *, int, unsigned int); - }; - template - struct fftw_plan_dft_signature { - using type = typename fftw_t::plan (&)(int n1, int n2, fftw_number_t *, fftw_number_t *, int, unsigned int); - }; - template - struct fftw_plan_dft_signature { - using type = typename fftw_t::plan (&)(int n1, int n2, int n3, fftw_number_t *, fftw_number_t *, int, unsigned int); - }; - - - // all_true, from https://stackoverflow.com/a/28253503/1199693 - template struct bool_pack; - - template - using all_true = std::is_same< bool_pack, bool_pack >; - - // conditionals for correct combinations of dimensionality parameters - namespace dimensional { - template - struct is_1 : public std::false_type {}; - template <> - struct is_1<1, true> : public std::true_type {}; - - template - struct is_2 : public std::false_type {}; - template <> - struct is_2<2, true> : public std::true_type {}; - - template - struct is_3 : public std::false_type {}; - template <> - struct is_3<3, true> : public std::true_type {}; - - template - struct is_123 : public std::conditional_t< - is_1::value || is_2::value || is_3::value, - std::true_type, - std::false_type - > {}; - - template - struct is_n : public std::false_type {}; - template - struct is_n : public std::true_type {}; - } - - - // input vs output shape conversion - template - inline auto output_shape_from_input(const xt::xarray& input, bool half_plus_one_out, bool half_plus_one_in, bool odd_last_dim = false) { - auto output_shape = input.shape(); - if (half_plus_one_out) { // r2c - auto n = output_shape.size(); - output_shape[n-1] = output_shape[n-1]/2 + 1; - } else if (half_plus_one_in) { // c2r - auto n = output_shape.size(); - if (!odd_last_dim) { - output_shape[n - 1] = (output_shape[n - 1] - 1) * 2; - } else { - output_shape[n - 1] = (output_shape[n - 1] - 1) * 2 + 1; - } - } - return output_shape; - } - - // output to DFT-dimensions conversion - template - inline auto dft_dimensions_from_output(const xt::xarray& output, bool half_plus_one_out, bool odd_last_dim = false) { - auto dft_dimensions = output.shape(); - - if (half_plus_one_out) { // r2c - auto n = dft_dimensions.size(); - if (!odd_last_dim) { - dft_dimensions[n - 1] = (dft_dimensions[n - 1] - 1) * 2; - } else { - dft_dimensions[n - 1] = (dft_dimensions[n - 1] - 1) * 2 + 1; - } - } - - return dft_dimensions; - } - - - // Callers for fftw_plan_dft, since they have different call signatures and the - // way shape information is extracted from xtensor differs for different dimensionalities. - - // REGULAR FFT N-dim - template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> - inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool /*odd_last_dim*/ = false) - -> std::enable_if_t::value && (fftw_direction != 0), typename fftw_t::plan> { - using fftw_input_t = fftw_number_t; - using fftw_output_t = fftw_number_t; - - auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out); - std::vector dft_dimensions; - dft_dimensions.reserve(dft_dimensions_unsigned.size()); - std::transform(dft_dimensions_unsigned.begin(), dft_dimensions_unsigned.end(), std::back_inserter(dft_dimensions), [&](std::size_t d) { return static_cast(d); }); - - std::lock_guard guard(detail::fftw_global_mutex()); - return fftw_plan_dft(static_cast(dim), dft_dimensions.data(), - const_cast(reinterpret_cast(input.data())), - reinterpret_cast(output.data()), - fftw_direction, - flags); - }; - - // REGULAR FFT 1D - template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> - inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool /*odd_last_dim*/ = false) - -> std::enable_if_t::value && (fftw_direction != 0), typename fftw_t::plan> { - using fftw_input_t = fftw_number_t; - using fftw_output_t = fftw_number_t; - - auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out); - - std::lock_guard guard(detail::fftw_global_mutex()); - return fftw_plan_dft(static_cast(dft_dimensions_unsigned[0]), - const_cast(reinterpret_cast(input.data())), - reinterpret_cast(output.data()), - fftw_direction, - flags); - }; - - // REGULAR FFT 2D - template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> - inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool /*odd_last_dim*/ = false) - -> std::enable_if_t::value && (fftw_direction != 0), typename fftw_t::plan> { - using fftw_input_t = fftw_number_t; - using fftw_output_t = fftw_number_t; - - auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out); - - std::lock_guard guard(detail::fftw_global_mutex()); - return fftw_plan_dft(static_cast(dft_dimensions_unsigned[0]), static_cast(dft_dimensions_unsigned[1]), - const_cast(reinterpret_cast(input.data())), - reinterpret_cast(output.data()), - fftw_direction, - flags); - }; - - // REGULAR FFT 3D - template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> - inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool /*odd_last_dim*/ = false) - -> std::enable_if_t::value && (fftw_direction != 0), typename fftw_t::plan> { - using fftw_input_t = fftw_number_t; - using fftw_output_t = fftw_number_t; - - auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out); - - std::lock_guard guard(detail::fftw_global_mutex()); - return fftw_plan_dft(static_cast(dft_dimensions_unsigned[0]), static_cast(dft_dimensions_unsigned[1]), static_cast(dft_dimensions_unsigned[2]), - const_cast(reinterpret_cast(input.data())), - reinterpret_cast(output.data()), - fftw_direction, - flags); - }; - - // REAL FFT N-dim - template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> - inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool odd_last_dim = false) - -> std::enable_if_t::value && (fftw_direction == 0), typename fftw_t::plan> { - using fftw_input_t = fftw_number_t; - using fftw_output_t = fftw_number_t; - - auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out, odd_last_dim); - std::vector dft_dimensions; - dft_dimensions.reserve(dft_dimensions_unsigned.size()); - std::transform(dft_dimensions_unsigned.begin(), dft_dimensions_unsigned.end(), std::back_inserter(dft_dimensions), [&](std::size_t d) { return static_cast(d); }); - - std::lock_guard guard(detail::fftw_global_mutex()); - return fftw_plan_dft(static_cast(dim), dft_dimensions.data(), - const_cast(reinterpret_cast(input.data())), - reinterpret_cast(output.data()), - flags); - }; - - // REAL FFT 1D - template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> - inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool odd_last_dim = false) - -> std::enable_if_t::value && (fftw_direction == 0), typename fftw_t::plan> { - using fftw_input_t = fftw_number_t; - using fftw_output_t = fftw_number_t; - - auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out, odd_last_dim); - - std::lock_guard guard(detail::fftw_global_mutex()); - return fftw_plan_dft(static_cast(dft_dimensions_unsigned[0]), - const_cast(reinterpret_cast(input.data())), - reinterpret_cast(output.data()), - flags); - }; - - // REAL FFT 2D - template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> - inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool odd_last_dim = false) - -> std::enable_if_t::value && (fftw_direction == 0), typename fftw_t::plan> { - using fftw_input_t = fftw_number_t; - using fftw_output_t = fftw_number_t; - - auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out, odd_last_dim); - - std::lock_guard guard(detail::fftw_global_mutex()); - return fftw_plan_dft(static_cast(dft_dimensions_unsigned[0]), static_cast(dft_dimensions_unsigned[1]), - const_cast(reinterpret_cast(input.data())), - reinterpret_cast(output.data()), - flags); - }; - - // REAL FFT 3D - template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> - inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool odd_last_dim = false) - -> std::enable_if_t::value && (fftw_direction == 0), typename fftw_t::plan> { - using fftw_input_t = fftw_number_t; - using fftw_output_t = fftw_number_t; - - auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out, odd_last_dim); - - std::lock_guard guard(detail::fftw_global_mutex()); - return fftw_plan_dft(static_cast(dft_dimensions_unsigned[0]), static_cast(dft_dimensions_unsigned[1]), static_cast(dft_dimensions_unsigned[2]), - const_cast(reinterpret_cast(input.data())), - reinterpret_cast(output.data()), - flags); - }; - - - //// - // General: xarray templates - //// - -// template -// inline xt::xarray _fft_ (const xt::xarray &input) { -// static_assert(sizeof(prec_t) == 0, "Only specializations of _fft_ can be used"); -// } -// -// template -// inline xt::xarray _ifft_ (const xt::xarray &input) { -// static_assert(sizeof(prec_t) == 0, "Only specializations of _ifft_ can be used"); -// } - - template < - typename input_t, typename output_t, std::size_t dim, int fftw_direction, bool fftw_123dim, bool half_plus_one_out, bool half_plus_one_in, - typename fftw_plan_dft_signature::type fftw_plan_dft, - void (&fftw_execute)(typename fftw_t::plan), void (&fftw_destroy_plan)(typename fftw_t::plan), - typename = std::enable_if_t< - std::is_same< prec_t, prec_t >::value // input and output precision must be the same - && std::is_floating_point< prec_t >::value // numbers must be float, double or long double - && (dimensional::is_123::value // dimensionality must match fftw_123dim - || dimensional::is_n::value) - > - > - inline xt::xarray _fft_(const xt::xarray &input) { - auto output_shape = output_shape_from_input(input, half_plus_one_out, half_plus_one_in, false); - xt::xarray output(output_shape); - - bool odd_last_dim = (input.shape()[input.shape().size()-1] % 2 != 0); - - auto plan = fftw_plan_dft_caller(input, output, FFTW_ESTIMATE, odd_last_dim); - if (plan == nullptr) { - XTENSOR_FFTW_THROW(std::runtime_error, - "Plan creation returned nullptr. This usually means FFTW cannot create a plan for the given arguments (e.g. a non-destructive multi-dimensional real FFT is impossible in FFTW)."); - } - - fftw_execute(plan); - { - std::lock_guard guard(detail::fftw_global_mutex()); - fftw_destroy_plan(plan); - } - return output; - }; - - template < - typename input_t, typename output_t, std::size_t dim, int fftw_direction, bool fftw_123dim, bool half_plus_one_out, bool half_plus_one_in, - typename fftw_plan_dft_signature::type fftw_plan_dft, - void (&fftw_execute)(typename fftw_t::plan), void (&fftw_destroy_plan)(typename fftw_t::plan), - typename = std::enable_if_t< - std::is_same< prec_t, prec_t >::value // input and output precision must be the same - && std::is_floating_point< prec_t >::value // numbers must be float, double or long double - && (dimensional::is_123::value // dimensionality must match fftw_123dim - || dimensional::is_n::value) - > - > - inline xt::xarray _ifft_(const xt::xarray &input, bool odd_last_dim = false) { - auto output_shape = output_shape_from_input(input, half_plus_one_out, half_plus_one_in, odd_last_dim); - xt::xarray output(output_shape); - - auto plan = fftw_plan_dft_caller(input, output, FFTW_ESTIMATE, odd_last_dim); - if (plan == nullptr) { - XTENSOR_FFTW_THROW(std::runtime_error, - "Plan creation returned nullptr. This usually means FFTW cannot create a plan for the given arguments (e.g. a non-destructive multi-dimensional real FFT is impossible in FFTW)."); - } - - fftw_execute(plan); - { - std::lock_guard guard(detail::fftw_global_mutex()); - fftw_destroy_plan(plan); - } - auto dft_dimensions = dft_dimensions_from_output(output, half_plus_one_out, odd_last_dim); - auto N_dft = static_cast >(std::accumulate(dft_dimensions.begin(), dft_dimensions.end(), static_cast(1u), std::multiplies())); - return output / N_dft; - }; - - template < - typename input_t, typename output_t, std::size_t dim, int fftw_direction, bool fftw_123dim, bool half_plus_one_out, bool half_plus_one_in, - typename fftw_plan_dft_signature::type fftw_plan_dft, - void (&fftw_execute)(typename fftw_t::plan), void (&fftw_destroy_plan)(typename fftw_t::plan), - typename = std::enable_if_t< - std::is_same< prec_t, prec_t >::value // input and output precision must be the same - && std::is_floating_point< prec_t >::value // numbers must be float, double or long double - && (dimensional::is_123::value // dimensionality must match fftw_123dim - || dimensional::is_n::value) - > - > - inline xt::xarray _hfft_(const xt::xarray &input) { - auto output_shape = output_shape_from_input(input, half_plus_one_out, half_plus_one_in); - xt::xarray output(output_shape); - - xt::xarray input_conj = xt::conj(input); - - auto plan = fftw_plan_dft_caller(input_conj, output, FFTW_ESTIMATE); - if (plan == nullptr) { - XTENSOR_FFTW_THROW(std::runtime_error, - "Plan creation returned nullptr. This usually means FFTW cannot create a plan for the given arguments (e.g. a non-destructive multi-dimensional real FFT is impossible in FFTW)."); - } - - fftw_execute(plan); - { - std::lock_guard guard(detail::fftw_global_mutex()); - fftw_destroy_plan(plan); - } - return output; - }; - - template < - typename input_t, typename output_t, std::size_t dim, int fftw_direction, bool fftw_123dim, bool half_plus_one_out, bool half_plus_one_in, - typename fftw_plan_dft_signature::type fftw_plan_dft, - void (&fftw_execute)(typename fftw_t::plan), void (&fftw_destroy_plan)(typename fftw_t::plan), - typename = std::enable_if_t< - std::is_same< prec_t, prec_t >::value // input and output precision must be the same - && std::is_floating_point< prec_t >::value // numbers must be float, double or long double - && (dimensional::is_123::value // dimensionality must match fftw_123dim - || dimensional::is_n::value) - > - > - inline xt::xarray _ihfft_(const xt::xarray &input) { - auto output_shape = output_shape_from_input(input, half_plus_one_out, half_plus_one_in); - xt::xarray output(output_shape); - - auto plan = fftw_plan_dft_caller(input, output, FFTW_ESTIMATE); - if (plan == nullptr) { - XTENSOR_FFTW_THROW(std::runtime_error, - "Plan creation returned nullptr. This usually means FFTW cannot create a plan for the given arguments (e.g. a non-destructive multi-dimensional real FFT is impossible in FFTW)."); - } - - fftw_execute(plan); - { - std::lock_guard guard(detail::fftw_global_mutex()); - fftw_destroy_plan(plan); - } - output = xt::conj(output); - - auto dft_dimensions = dft_dimensions_from_output(output, half_plus_one_out); - auto N_dft = static_cast >(std::accumulate(dft_dimensions.begin(), dft_dimensions.end(), static_cast(1u), std::multiplies())); - return output / N_dft; - }; - - - //// - // General: xtensor templates - //// - -// template -// xt::xtensor< std::complex, dim > _fft_(const xt::xtensor &input) { -// static_assert(sizeof(real_t) == 0, "Only specializations of fft can be used"); -// -// xt::xtensor, dim> output(input.shape(), input.strides()); -// -// fftw_plan_t plan = fftwXXXXX_plan_dft_r2c_1d(static_cast(input.size()), -// const_cast(input.data()), -// reinterpret_cast(output.data()), -// FFTW_ESTIMATE); -// -// fftwXXXXX_execute(plan); -// fftwXXXXX_destroy_plan(plan); -// return output; -// }; -// -// template -// xt::xtensor _ifft_(const xt::xtensor< std::complex, dim > &input) { -// static_assert(sizeof(real_t) == 0, "Only specializations of ifft can be used"); -// -// xt::xtensor output(input.shape(), input.strides()); -// -// fftw_plan_t plan = fftwXXXXX_plan_dft_c2r_1d(static_cast(input.size()), -// const_cast(reinterpret_cast(input.data())), -// output.data(), -// FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); -// -// fftwXXXXX_execute(plan); -// fftwXXXXX_destroy_plan(plan); -// return output / output.size(); -// }; - - - /////////////////////////////////////////////////////////////////////////////// - // Regular FFT (complex to complex) - /////////////////////////////////////////////////////////////////////////////// - - //// - // Regular FFT: 1D - //// - - inline xt::xarray > fft (const xt::xarray > &input) { - return _fft_, std::complex, 1, FFTW_FORWARD, true, false, false, fftwf_plan_dft_1d, fftwf_execute, fftwf_destroy_plan> (input); - } - - inline xt::xarray > ifft (const xt::xarray > &input) { - return _ifft_, std::complex, 1, FFTW_BACKWARD, true, false, false, fftwf_plan_dft_1d, fftwf_execute, fftwf_destroy_plan> (input); - } - - inline xt::xarray > fft (const xt::xarray > &input) { - return _fft_, std::complex, 1, FFTW_FORWARD, true, false, false, fftw_plan_dft_1d, fftw_execute, fftw_destroy_plan> (input); - } - - inline xt::xarray > ifft (const xt::xarray > &input) { - return _ifft_, std::complex, 1, FFTW_BACKWARD, true, false, false, fftw_plan_dft_1d, fftw_execute, fftw_destroy_plan> (input); - } - - inline xt::xarray > fft (const xt::xarray > &input) { - return _fft_, std::complex, 1, FFTW_FORWARD, true, false, false, fftwl_plan_dft_1d, fftwl_execute, fftwl_destroy_plan> (input); - } - - inline xt::xarray > ifft (const xt::xarray > &input) { - return _ifft_, std::complex, 1, FFTW_BACKWARD, true, false, false, fftwl_plan_dft_1d, fftwl_execute, fftwl_destroy_plan> (input); - } - - - //// - // Regular FFT: 2D - //// - - inline xt::xarray > fft2 (const xt::xarray > &input) { - return _fft_, std::complex, 2, FFTW_FORWARD, true, false, false, fftwf_plan_dft_2d, fftwf_execute, fftwf_destroy_plan> (input); - } - - inline xt::xarray > ifft2 (const xt::xarray > &input) { - return _ifft_, std::complex, 2, FFTW_BACKWARD, true, false, false, fftwf_plan_dft_2d, fftwf_execute, fftwf_destroy_plan> (input); - } - - inline xt::xarray > fft2 (const xt::xarray > &input) { - return _fft_, std::complex, 2, FFTW_FORWARD, true, false, false, fftw_plan_dft_2d, fftw_execute, fftw_destroy_plan> (input); - } - - inline xt::xarray > ifft2 (const xt::xarray > &input) { - return _ifft_, std::complex, 2, FFTW_BACKWARD, true, false, false, fftw_plan_dft_2d, fftw_execute, fftw_destroy_plan> (input); - } - - inline xt::xarray > fft2 (const xt::xarray > &input) { - return _fft_, std::complex, 2, FFTW_FORWARD, true, false, false, fftwl_plan_dft_2d, fftwl_execute, fftwl_destroy_plan> (input); - } - - inline xt::xarray > ifft2 (const xt::xarray > &input) { - return _ifft_, std::complex, 2, FFTW_BACKWARD, true, false, false, fftwl_plan_dft_2d, fftwl_execute, fftwl_destroy_plan> (input); - } - - - //// - // Regular FFT: 3D - //// - - inline xt::xarray > fft3 (const xt::xarray > &input) { - return _fft_, std::complex, 3, FFTW_FORWARD, true, false, false, fftwf_plan_dft_3d, fftwf_execute, fftwf_destroy_plan> (input); - } - - inline xt::xarray > ifft3 (const xt::xarray > &input) { - return _ifft_, std::complex, 3, FFTW_BACKWARD, true, false, false, fftwf_plan_dft_3d, fftwf_execute, fftwf_destroy_plan> (input); - } - - inline xt::xarray > fft3 (const xt::xarray > &input) { - return _fft_, std::complex, 3, FFTW_FORWARD, true, false, false, fftw_plan_dft_3d, fftw_execute, fftw_destroy_plan> (input); - } - - inline xt::xarray > ifft3 (const xt::xarray > &input) { - return _ifft_, std::complex, 3, FFTW_BACKWARD, true, false, false, fftw_plan_dft_3d, fftw_execute, fftw_destroy_plan> (input); - } - - inline xt::xarray > fft3 (const xt::xarray > &input) { - return _fft_, std::complex, 3, FFTW_FORWARD, true, false, false, fftwl_plan_dft_3d, fftwl_execute, fftwl_destroy_plan> (input); - } - - inline xt::xarray > ifft3 (const xt::xarray > &input) { - return _ifft_, std::complex, 3, FFTW_BACKWARD, true, false, false, fftwl_plan_dft_3d, fftwl_execute, fftwl_destroy_plan> (input); - } - - - //// - // Regular FFT: nD - //// - - template - inline xt::xarray > fftn (const xt::xarray > &input) { - return _fft_, std::complex, dim, FFTW_FORWARD, false, false, false, fftwf_plan_dft, fftwf_execute, fftwf_destroy_plan> (input); - } - - template - inline xt::xarray > ifftn (const xt::xarray > &input) { - return _ifft_, std::complex, dim, FFTW_BACKWARD, false, false, false, fftwf_plan_dft, fftwf_execute, fftwf_destroy_plan> (input); - } - - template - inline xt::xarray > fftn (const xt::xarray > &input) { - return _fft_, std::complex, dim, FFTW_FORWARD, false, false, false, fftw_plan_dft, fftw_execute, fftw_destroy_plan> (input); - } - - template - inline xt::xarray > ifftn (const xt::xarray > &input) { - return _ifft_, std::complex, dim, FFTW_BACKWARD, false, false, false, fftw_plan_dft, fftw_execute, fftw_destroy_plan> (input); - } - - template - inline xt::xarray > fftn (const xt::xarray > &input) { - return _fft_, std::complex, dim, FFTW_FORWARD, false, false, false, fftwl_plan_dft, fftwl_execute, fftwl_destroy_plan> (input); - } - - template - inline xt::xarray > ifftn (const xt::xarray > &input) { - return _ifft_, std::complex, dim, FFTW_BACKWARD, false, false, false, fftwl_plan_dft, fftwl_execute, fftwl_destroy_plan> (input); - } - - - /////////////////////////////////////////////////////////////////////////////// - // Real FFT (real input) - /////////////////////////////////////////////////////////////////////////////// - - //// - // Real FFT: 1D - //// - - inline xt::xarray > rfft (const xt::xarray &input) { - return _fft_, 1, 0, true, true, false, fftwf_plan_dft_r2c_1d, fftwf_execute, fftwf_destroy_plan> (input); - } - - inline xt::xarray irfft (const xt::xarray > &input, bool odd_last_dim = false) { - return _ifft_, float, 1, 0, true, false, true, fftwf_plan_dft_c2r_1d, fftwf_execute, fftwf_destroy_plan> (input, odd_last_dim); - } - - inline xt::xarray > rfft (const xt::xarray &input) { - return _fft_, 1, 0, true, true, false, fftw_plan_dft_r2c_1d, fftw_execute, fftw_destroy_plan> (input); - } - - inline xt::xarray irfft (const xt::xarray > &input, bool odd_last_dim = false) { - return _ifft_, double, 1, 0, true, false, true, fftw_plan_dft_c2r_1d, fftw_execute, fftw_destroy_plan> (input, odd_last_dim); - } - - inline xt::xarray > rfft (const xt::xarray &input) { - return _fft_, 1, 0, true, true, false, fftwl_plan_dft_r2c_1d, fftwl_execute, fftwl_destroy_plan> (input); - } - - inline xt::xarray irfft (const xt::xarray > &input, bool odd_last_dim = false) { - return _ifft_, long double, 1, 0, true, false, true, fftwl_plan_dft_c2r_1d, fftwl_execute, fftwl_destroy_plan> (input, odd_last_dim); - } - - - //// - // Real FFT: 2D - //// - - inline xt::xarray > rfft2 (const xt::xarray &input) { - return _fft_, 2, 0, true, true, false, fftwf_plan_dft_r2c_2d, fftwf_execute, fftwf_destroy_plan> (input); - } - - inline xt::xarray irfft2 (const xt::xarray > &input, bool odd_last_dim = false) { - return _ifft_, float, 2, 0, true, false, true, fftwf_plan_dft_c2r_2d, fftwf_execute, fftwf_destroy_plan> (input, odd_last_dim); - } - - inline xt::xarray > rfft2 (const xt::xarray &input) { - return _fft_, 2, 0, true, true, false, fftw_plan_dft_r2c_2d, fftw_execute, fftw_destroy_plan> (input); - } - - inline xt::xarray irfft2 (const xt::xarray > &input, bool odd_last_dim = false) { - return _ifft_, double, 2, 0, true, false, true, fftw_plan_dft_c2r_2d, fftw_execute, fftw_destroy_plan> (input, odd_last_dim); - } - - inline xt::xarray > rfft2 (const xt::xarray &input) { - return _fft_, 2, 0, true, true, false, fftwl_plan_dft_r2c_2d, fftwl_execute, fftwl_destroy_plan> (input); - } - - inline xt::xarray irfft2 (const xt::xarray > &input, bool odd_last_dim = false) { - return _ifft_, long double, 2, 0, true, false, true, fftwl_plan_dft_c2r_2d, fftwl_execute, fftwl_destroy_plan> (input, odd_last_dim); - } - - - //// - // Real FFT: 3D - //// - - // this doesn't work because the plan function is also precision dependent and it would be madness to include them all in fftw_t... -// template -// constexpr auto rfft3 = _fft_, 3, true, false, fftwl_plan_dft_r2c_3d, int, int, int>; -// template -// constexpr auto irfft3 = _ifft_, 3, true, false, fftwl_plan_dft_r2c_3d, int, int, int>; - - inline xt::xarray > rfft3 (const xt::xarray &input) { - return _fft_, 3, 0, true, true, false, fftwf_plan_dft_r2c_3d, fftwf_execute, fftwf_destroy_plan> (input); - } - - inline xt::xarray irfft3 (const xt::xarray > &input, bool odd_last_dim = false) { - return _ifft_, float, 3, 0, true, false, true, fftwf_plan_dft_c2r_3d, fftwf_execute, fftwf_destroy_plan> (input, odd_last_dim); - } - - inline xt::xarray > rfft3 (const xt::xarray &input) { - return _fft_, 3, 0, true, true, false, fftw_plan_dft_r2c_3d, fftw_execute, fftw_destroy_plan> (input); - } - - inline xt::xarray irfft3 (const xt::xarray > &input, bool odd_last_dim = false) { - return _ifft_, double, 3, 0, true, false, true, fftw_plan_dft_c2r_3d, fftw_execute, fftw_destroy_plan> (input, odd_last_dim); - } - - inline xt::xarray > rfft3 (const xt::xarray &input) { - return _fft_, 3, 0, true, true, false, fftwl_plan_dft_r2c_3d, fftwl_execute, fftwl_destroy_plan> (input); - } - - inline xt::xarray irfft3 (const xt::xarray > &input, bool odd_last_dim = false) { - return _ifft_, long double, 3, 0, true, false, true, fftwl_plan_dft_c2r_3d, fftwl_execute, fftwl_destroy_plan> (input, odd_last_dim); - } - - //// - // Real FFT: nD - //// - - template - inline xt::xarray > rfftn (const xt::xarray &input) { - return _fft_, dim, 0, false, true, false, fftwf_plan_dft_r2c, fftwf_execute, fftwf_destroy_plan> (input); - } - - template - inline xt::xarray irfftn (const xt::xarray > &input, bool odd_last_dim = false) { - return _ifft_, float, dim, 0, false, false, true, fftwf_plan_dft_c2r, fftwf_execute, fftwf_destroy_plan> (input, odd_last_dim); - } - - template - inline xt::xarray > rfftn (const xt::xarray &input) { - return _fft_, dim, 0, false, true, false, fftw_plan_dft_r2c, fftw_execute, fftw_destroy_plan> (input); - } - - template - inline xt::xarray irfftn (const xt::xarray > &input, bool odd_last_dim = false) { - return _ifft_, double, dim, 0, false, false, true, fftw_plan_dft_c2r, fftw_execute, fftw_destroy_plan> (input, odd_last_dim); - } - - template - inline xt::xarray > rfftn (const xt::xarray &input) { - return _fft_, dim, 0, false, true, false, fftwl_plan_dft_r2c, fftwl_execute, fftwl_destroy_plan> (input); - } - - template - inline xt::xarray irfftn (const xt::xarray > &input, bool odd_last_dim = false) { - return _ifft_, long double, dim, 0, false, false, true, fftwl_plan_dft_c2r, fftwl_execute, fftwl_destroy_plan> (input, odd_last_dim); - } - - - /////////////////////////////////////////////////////////////////////////////// - // Hermitian FFT (real spectrum) - /////////////////////////////////////////////////////////////////////////////// - - //// - // Hermitian FFT: 1D - //// - - inline xt::xarray hfft (const xt::xarray > &input) { - return _hfft_, float, 1, 0, true, false, true, fftwf_plan_dft_c2r_1d, fftwf_execute, fftwf_destroy_plan> (input); - } - - inline xt::xarray > ihfft (const xt::xarray &input) { - return _ihfft_, 1, 0, true, true, false, fftwf_plan_dft_r2c_1d, fftwf_execute, fftwf_destroy_plan> (input); - } - - inline xt::xarray hfft (const xt::xarray > &input) { - return _hfft_, double, 1, 0, true, false, true, fftw_plan_dft_c2r_1d, fftw_execute, fftw_destroy_plan> (input); - } - - inline xt::xarray > ihfft (const xt::xarray &input) { - return _ihfft_, 1, 0, true, true, false, fftw_plan_dft_r2c_1d, fftw_execute, fftw_destroy_plan> (input); - } - - inline xt::xarray hfft (const xt::xarray > &input) { - return _hfft_, long double, 1, 0, true, false, true, fftwl_plan_dft_c2r_1d, fftwl_execute, fftwl_destroy_plan> (input); - } - - inline xt::xarray > ihfft (const xt::xarray &input) { - return _ihfft_, 1, 0, true, true, false, fftwl_plan_dft_r2c_1d, fftwl_execute, fftwl_destroy_plan> (input); - } - - //// - // Hermitian FFT: 2D - //// - - inline xt::xarray hfft2 (const xt::xarray > &input) { - return _hfft_, float, 2, 0, true, false, true, fftwf_plan_dft_c2r_2d, fftwf_execute, fftwf_destroy_plan> (input); - } - - inline xt::xarray > ihfft2 (const xt::xarray &input) { - return _ihfft_, 2, 0, true, true, false, fftwf_plan_dft_r2c_2d, fftwf_execute, fftwf_destroy_plan> (input); - } - - inline xt::xarray hfft2 (const xt::xarray > &input) { - return _hfft_, double, 2, 0, true, false, true, fftw_plan_dft_c2r_2d, fftw_execute, fftw_destroy_plan> (input); - } - - inline xt::xarray > ihfft2 (const xt::xarray &input) { - return _ihfft_, 2, 0, true, true, false, fftw_plan_dft_r2c_2d, fftw_execute, fftw_destroy_plan> (input); - } - - inline xt::xarray hfft2 (const xt::xarray > &input) { - return _hfft_, long double, 2, 0, true, false, true, fftwl_plan_dft_c2r_2d, fftwl_execute, fftwl_destroy_plan> (input); - } - - inline xt::xarray > ihfft2 (const xt::xarray &input) { - return _ihfft_, 2, 0, true, true, false, fftwl_plan_dft_r2c_2d, fftwl_execute, fftwl_destroy_plan> (input); - } - - - //// - // Hermitian FFT: 3D - //// - - inline xt::xarray hfft3 (const xt::xarray > &input) { - return _hfft_, float, 3, 0, true, false, true, fftwf_plan_dft_c2r_3d, fftwf_execute, fftwf_destroy_plan> (input); - } - - inline xt::xarray > ihfft3 (const xt::xarray &input) { - return _ihfft_, 3, 0, true, true, false, fftwf_plan_dft_r2c_3d, fftwf_execute, fftwf_destroy_plan> (input); - } - - inline xt::xarray hfft3 (const xt::xarray > &input) { - return _hfft_, double, 3, 0, true, false, true, fftw_plan_dft_c2r_3d, fftw_execute, fftw_destroy_plan> (input); - } - - inline xt::xarray > ihfft3 (const xt::xarray &input) { - return _ihfft_, 3, 0, true, true, false, fftw_plan_dft_r2c_3d, fftw_execute, fftw_destroy_plan> (input); - } - - inline xt::xarray hfft3 (const xt::xarray > &input) { - return _hfft_, long double, 3, 0, true, false, true, fftwl_plan_dft_c2r_3d, fftwl_execute, fftwl_destroy_plan> (input); - } - - inline xt::xarray > ihfft3 (const xt::xarray &input) { - return _ihfft_, 3, 0, true, true, false, fftwl_plan_dft_r2c_3d, fftwl_execute, fftwl_destroy_plan> (input); - } - - - //// - // Hermitian FFT: nD - //// - - template - inline xt::xarray hfftn (const xt::xarray > &input) { - return _hfft_, float, dim, 0, false, false, true, fftwf_plan_dft_c2r, fftwf_execute, fftwf_destroy_plan> (input); - } - - template - inline xt::xarray > ihfftn (const xt::xarray &input) { - return _ihfft_, dim, 0, false, true, false, fftwf_plan_dft_r2c, fftwf_execute, fftwf_destroy_plan> (input); - } - - template - inline xt::xarray hfftn (const xt::xarray > &input) { - return _hfft_, double, dim, 0, false, false, true, fftw_plan_dft_c2r, fftw_execute, fftw_destroy_plan> (input); - } - - template - inline xt::xarray > ihfftn (const xt::xarray &input) { - return _ihfft_, dim, 0, false, true, false, fftw_plan_dft_r2c, fftw_execute, fftw_destroy_plan> (input); - } - - template - inline xt::xarray hfftn (const xt::xarray > &input) { - return _hfft_, long double, dim, 0, false, false, true, fftwl_plan_dft_c2r, fftwl_execute, fftwl_destroy_plan> (input); - } - - template - inline xt::xarray > ihfftn (const xt::xarray &input) { - return _ihfft_, dim, 0, false, true, false, fftwl_plan_dft_r2c, fftwl_execute, fftwl_destroy_plan> (input); - } - - } -} +#include "basic_float.hpp" +#include "basic_double.hpp" +#include "basic_long_double.hpp" #endif //XTENSOR_FFTW_BASIC_HPP diff --git a/include/xtensor-fftw/basic_double.hpp b/include/xtensor-fftw/basic_double.hpp new file mode 100644 index 0000000..4b3a3dd --- /dev/null +++ b/include/xtensor-fftw/basic_double.hpp @@ -0,0 +1,196 @@ +/* + * xtensor-fftw + * Copyright (c) 2017, Patrick Bos + * + * Distributed under the terms of the BSD 3-Clause License. + * + * The full license is in the file LICENSE, distributed with this software. + * + * basic_double.hpp: + * Contains the basic functions needed to do FFTs and inverse FFTs on double + * and complex array. The behavior of these functions mimics that of + * the numpy.fft module, see https://github.com/xtensor-stack/xtensor-fftw/issues/6. + * + */ + +#ifndef XTENSOR_FFTW_BASIC_DOUBLE_HPP +#define XTENSOR_FFTW_BASIC_DOUBLE_HPP + +#include "common.hpp" + +namespace xt { + namespace fftw { + + template <> struct fftw_t { + using plan = fftw_plan; + using complex = fftw_complex; + constexpr static void (&execute)(plan) = fftw_execute; + constexpr static void (&destroy_plan)(plan) = fftw_destroy_plan; + }; + + /////////////////////////////////////////////////////////////////////////////// + // Regular FFT (complex to complex) + /////////////////////////////////////////////////////////////////////////////// + + //// + // Regular FFT: 1D + //// + + inline xt::xarray > fft (const xt::xarray > &input) { + return _fft_, std::complex, 1, FFTW_FORWARD, true, false, false, fftw_plan_dft_1d, fftw_execute, fftw_destroy_plan> (input); + } + + inline xt::xarray > ifft (const xt::xarray > &input) { + return _ifft_, std::complex, 1, FFTW_BACKWARD, true, false, false, fftw_plan_dft_1d, fftw_execute, fftw_destroy_plan> (input); + } + + //// + // Regular FFT: 2D + //// + + inline xt::xarray > fft2 (const xt::xarray > &input) { + return _fft_, std::complex, 2, FFTW_FORWARD, true, false, false, fftw_plan_dft_2d, fftw_execute, fftw_destroy_plan> (input); + } + + inline xt::xarray > ifft2 (const xt::xarray > &input) { + return _ifft_, std::complex, 2, FFTW_BACKWARD, true, false, false, fftw_plan_dft_2d, fftw_execute, fftw_destroy_plan> (input); + } + + //// + // Regular FFT: 3D + //// + + inline xt::xarray > fft3 (const xt::xarray > &input) { + return _fft_, std::complex, 3, FFTW_FORWARD, true, false, false, fftw_plan_dft_3d, fftw_execute, fftw_destroy_plan> (input); + } + + inline xt::xarray > ifft3 (const xt::xarray > &input) { + return _ifft_, std::complex, 3, FFTW_BACKWARD, true, false, false, fftw_plan_dft_3d, fftw_execute, fftw_destroy_plan> (input); + } + + //// + // Regular FFT: nD + //// + + template + inline xt::xarray > fftn (const xt::xarray > &input) { + return _fft_, std::complex, dim, FFTW_FORWARD, false, false, false, fftw_plan_dft, fftw_execute, fftw_destroy_plan> (input); + } + + template + inline xt::xarray > ifftn (const xt::xarray > &input) { + return _ifft_, std::complex, dim, FFTW_BACKWARD, false, false, false, fftw_plan_dft, fftw_execute, fftw_destroy_plan> (input); + } + + /////////////////////////////////////////////////////////////////////////////// + // Real FFT (real input) + /////////////////////////////////////////////////////////////////////////////// + + //// + // Real FFT: 1D + //// + + inline xt::xarray > rfft (const xt::xarray &input) { + return _fft_, 1, 0, true, true, false, fftw_plan_dft_r2c_1d, fftw_execute, fftw_destroy_plan> (input); + } + + inline xt::xarray irfft (const xt::xarray > &input, bool odd_last_dim = false) { + return _ifft_, double, 1, 0, true, false, true, fftw_plan_dft_c2r_1d, fftw_execute, fftw_destroy_plan> (input, odd_last_dim); + } + + //// + // Real FFT: 2D + //// + + inline xt::xarray > rfft2 (const xt::xarray &input) { + return _fft_, 2, 0, true, true, false, fftw_plan_dft_r2c_2d, fftw_execute, fftw_destroy_plan> (input); + } + + inline xt::xarray irfft2 (const xt::xarray > &input, bool odd_last_dim = false) { + return _ifft_, double, 2, 0, true, false, true, fftw_plan_dft_c2r_2d, fftw_execute, fftw_destroy_plan> (input, odd_last_dim); + } + + //// + // Real FFT: 3D + //// + + inline xt::xarray > rfft3 (const xt::xarray &input) { + return _fft_, 3, 0, true, true, false, fftw_plan_dft_r2c_3d, fftw_execute, fftw_destroy_plan> (input); + } + + inline xt::xarray irfft3 (const xt::xarray > &input, bool odd_last_dim = false) { + return _ifft_, double, 3, 0, true, false, true, fftw_plan_dft_c2r_3d, fftw_execute, fftw_destroy_plan> (input, odd_last_dim); + } + + //// + // Real FFT: nD + //// + + template + inline xt::xarray > rfftn (const xt::xarray &input) { + return _fft_, dim, 0, false, true, false, fftw_plan_dft_r2c, fftw_execute, fftw_destroy_plan> (input); + } + + template + inline xt::xarray irfftn (const xt::xarray > &input, bool odd_last_dim = false) { + return _ifft_, double, dim, 0, false, false, true, fftw_plan_dft_c2r, fftw_execute, fftw_destroy_plan> (input, odd_last_dim); + } + + /////////////////////////////////////////////////////////////////////////////// + // Hermitian FFT (real spectrum) + /////////////////////////////////////////////////////////////////////////////// + + //// + // Hermitian FFT: 1D + //// + + inline xt::xarray hfft (const xt::xarray > &input) { + return _hfft_, double, 1, 0, true, false, true, fftw_plan_dft_c2r_1d, fftw_execute, fftw_destroy_plan> (input); + } + + inline xt::xarray > ihfft (const xt::xarray &input) { + return _ihfft_, 1, 0, true, true, false, fftw_plan_dft_r2c_1d, fftw_execute, fftw_destroy_plan> (input); + } + + //// + // Hermitian FFT: 2D + //// + + inline xt::xarray hfft2 (const xt::xarray > &input) { + return _hfft_, double, 2, 0, true, false, true, fftw_plan_dft_c2r_2d, fftw_execute, fftw_destroy_plan> (input); + } + + inline xt::xarray > ihfft2 (const xt::xarray &input) { + return _ihfft_, 2, 0, true, true, false, fftw_plan_dft_r2c_2d, fftw_execute, fftw_destroy_plan> (input); + } + + //// + // Hermitian FFT: 3D + //// + + inline xt::xarray hfft3 (const xt::xarray > &input) { + return _hfft_, double, 3, 0, true, false, true, fftw_plan_dft_c2r_3d, fftw_execute, fftw_destroy_plan> (input); + } + + inline xt::xarray > ihfft3 (const xt::xarray &input) { + return _ihfft_, 3, 0, true, true, false, fftw_plan_dft_r2c_3d, fftw_execute, fftw_destroy_plan> (input); + } + + //// + // Hermitian FFT: nD + //// + + template + inline xt::xarray hfftn (const xt::xarray > &input) { + return _hfft_, double, dim, 0, false, false, true, fftw_plan_dft_c2r, fftw_execute, fftw_destroy_plan> (input); + } + + template + inline xt::xarray > ihfftn (const xt::xarray &input) { + return _ihfft_, dim, 0, false, true, false, fftw_plan_dft_r2c, fftw_execute, fftw_destroy_plan> (input); + } + + } +} + +#endif //XTENSOR_FFTW_BASIC_DOUBLE_HPP diff --git a/include/xtensor-fftw/basic_float.hpp b/include/xtensor-fftw/basic_float.hpp new file mode 100644 index 0000000..886c138 --- /dev/null +++ b/include/xtensor-fftw/basic_float.hpp @@ -0,0 +1,196 @@ +/* + * xtensor-fftw + * Copyright (c) 2017, Patrick Bos + * + * Distributed under the terms of the BSD 3-Clause License. + * + * The full license is in the file LICENSE, distributed with this software. + * + * basic_float.hpp: + * Contains the basic functions needed to do FFTs and inverse FFTs on float + * and complex arrays. The behavior of these functions mimics that of + * the numpy.fft module, see https://github.com/xtensor-stack/xtensor-fftw/issues/6. + * + */ + +#ifndef XTENSOR_FFTW_BASIC_FLOAT_HPP +#define XTENSOR_FFTW_BASIC_FLOAT_HPP + +#include "common.hpp" + +namespace xt { + namespace fftw { + + template <> struct fftw_t { + using plan = fftwf_plan; + using complex = fftwf_complex; + constexpr static void (&execute)(plan) = fftwf_execute; + constexpr static void (&destroy_plan)(plan) = fftwf_destroy_plan; + }; + + /////////////////////////////////////////////////////////////////////////////// + // Regular FFT (complex to complex) + /////////////////////////////////////////////////////////////////////////////// + + //// + // Regular FFT: 1D + //// + + inline xt::xarray > fft (const xt::xarray > &input) { + return _fft_, std::complex, 1, FFTW_FORWARD, true, false, false, fftwf_plan_dft_1d, fftwf_execute, fftwf_destroy_plan> (input); + } + + inline xt::xarray > ifft (const xt::xarray > &input) { + return _ifft_, std::complex, 1, FFTW_BACKWARD, true, false, false, fftwf_plan_dft_1d, fftwf_execute, fftwf_destroy_plan> (input); + } + + //// + // Regular FFT: 2D + //// + + inline xt::xarray > fft2 (const xt::xarray > &input) { + return _fft_, std::complex, 2, FFTW_FORWARD, true, false, false, fftwf_plan_dft_2d, fftwf_execute, fftwf_destroy_plan> (input); + } + + inline xt::xarray > ifft2 (const xt::xarray > &input) { + return _ifft_, std::complex, 2, FFTW_BACKWARD, true, false, false, fftwf_plan_dft_2d, fftwf_execute, fftwf_destroy_plan> (input); + } + + //// + // Regular FFT: 3D + //// + + inline xt::xarray > fft3 (const xt::xarray > &input) { + return _fft_, std::complex, 3, FFTW_FORWARD, true, false, false, fftwf_plan_dft_3d, fftwf_execute, fftwf_destroy_plan> (input); + } + + inline xt::xarray > ifft3 (const xt::xarray > &input) { + return _ifft_, std::complex, 3, FFTW_BACKWARD, true, false, false, fftwf_plan_dft_3d, fftwf_execute, fftwf_destroy_plan> (input); + } + + //// + // Regular FFT: nD + //// + + template + inline xt::xarray > fftn (const xt::xarray > &input) { + return _fft_, std::complex, dim, FFTW_FORWARD, false, false, false, fftwf_plan_dft, fftwf_execute, fftwf_destroy_plan> (input); + } + + template + inline xt::xarray > ifftn (const xt::xarray > &input) { + return _ifft_, std::complex, dim, FFTW_BACKWARD, false, false, false, fftwf_plan_dft, fftwf_execute, fftwf_destroy_plan> (input); + } + + /////////////////////////////////////////////////////////////////////////////// + // Real FFT (real input) + /////////////////////////////////////////////////////////////////////////////// + + //// + // Real FFT: 1D + //// + + inline xt::xarray > rfft (const xt::xarray &input) { + return _fft_, 1, 0, true, true, false, fftwf_plan_dft_r2c_1d, fftwf_execute, fftwf_destroy_plan> (input); + } + + inline xt::xarray irfft (const xt::xarray > &input, bool odd_last_dim = false) { + return _ifft_, float, 1, 0, true, false, true, fftwf_plan_dft_c2r_1d, fftwf_execute, fftwf_destroy_plan> (input, odd_last_dim); + } + + //// + // Real FFT: 2D + //// + + inline xt::xarray > rfft2 (const xt::xarray &input) { + return _fft_, 2, 0, true, true, false, fftwf_plan_dft_r2c_2d, fftwf_execute, fftwf_destroy_plan> (input); + } + + inline xt::xarray irfft2 (const xt::xarray > &input, bool odd_last_dim = false) { + return _ifft_, float, 2, 0, true, false, true, fftwf_plan_dft_c2r_2d, fftwf_execute, fftwf_destroy_plan> (input, odd_last_dim); + } + + //// + // Real FFT: 3D + //// + + inline xt::xarray > rfft3 (const xt::xarray &input) { + return _fft_, 3, 0, true, true, false, fftwf_plan_dft_r2c_3d, fftwf_execute, fftwf_destroy_plan> (input); + } + + inline xt::xarray irfft3 (const xt::xarray > &input, bool odd_last_dim = false) { + return _ifft_, float, 3, 0, true, false, true, fftwf_plan_dft_c2r_3d, fftwf_execute, fftwf_destroy_plan> (input, odd_last_dim); + } + + //// + // Real FFT: nD + //// + + template + inline xt::xarray > rfftn (const xt::xarray &input) { + return _fft_, dim, 0, false, true, false, fftwf_plan_dft_r2c, fftwf_execute, fftwf_destroy_plan> (input); + } + + template + inline xt::xarray irfftn (const xt::xarray > &input, bool odd_last_dim = false) { + return _ifft_, float, dim, 0, false, false, true, fftwf_plan_dft_c2r, fftwf_execute, fftwf_destroy_plan> (input, odd_last_dim); + } + + /////////////////////////////////////////////////////////////////////////////// + // Hermitian FFT (real spectrum) + /////////////////////////////////////////////////////////////////////////////// + + //// + // Hermitian FFT: 1D + //// + + inline xt::xarray hfft (const xt::xarray > &input) { + return _hfft_, float, 1, 0, true, false, true, fftwf_plan_dft_c2r_1d, fftwf_execute, fftwf_destroy_plan> (input); + } + + inline xt::xarray > ihfft (const xt::xarray &input) { + return _ihfft_, 1, 0, true, true, false, fftwf_plan_dft_r2c_1d, fftwf_execute, fftwf_destroy_plan> (input); + } + + //// + // Hermitian FFT: 2D + //// + + inline xt::xarray hfft2 (const xt::xarray > &input) { + return _hfft_, float, 2, 0, true, false, true, fftwf_plan_dft_c2r_2d, fftwf_execute, fftwf_destroy_plan> (input); + } + + inline xt::xarray > ihfft2 (const xt::xarray &input) { + return _ihfft_, 2, 0, true, true, false, fftwf_plan_dft_r2c_2d, fftwf_execute, fftwf_destroy_plan> (input); + } + + //// + // Hermitian FFT: 3D + //// + + inline xt::xarray hfft3 (const xt::xarray > &input) { + return _hfft_, float, 3, 0, true, false, true, fftwf_plan_dft_c2r_3d, fftwf_execute, fftwf_destroy_plan> (input); + } + + inline xt::xarray > ihfft3 (const xt::xarray &input) { + return _ihfft_, 3, 0, true, true, false, fftwf_plan_dft_r2c_3d, fftwf_execute, fftwf_destroy_plan> (input); + } + + //// + // Hermitian FFT: nD + //// + + template + inline xt::xarray hfftn (const xt::xarray > &input) { + return _hfft_, float, dim, 0, false, false, true, fftwf_plan_dft_c2r, fftwf_execute, fftwf_destroy_plan> (input); + } + + template + inline xt::xarray > ihfftn (const xt::xarray &input) { + return _ihfft_, dim, 0, false, true, false, fftwf_plan_dft_r2c, fftwf_execute, fftwf_destroy_plan> (input); + } + + } +} + +#endif //XTENSOR_FFTW_BASIC_FLOAT_HPP diff --git a/include/xtensor-fftw/basic_long_double.hpp b/include/xtensor-fftw/basic_long_double.hpp new file mode 100644 index 0000000..5604395 --- /dev/null +++ b/include/xtensor-fftw/basic_long_double.hpp @@ -0,0 +1,196 @@ +/* + * xtensor-fftw + * Copyright (c) 2017, Patrick Bos + * + * Distributed under the terms of the BSD 3-Clause License. + * + * The full license is in the file LICENSE, distributed with this software. + * + * basic_long_double.hpp: + * Contains the basic functions needed to do FFTs and inverse FFTs on long double + * and complex arrays. The behavior of these functions mimics that of + * the numpy.fft module, see https://github.com/xtensor-stack/xtensor-fftw/issues/6. + * + */ + +#ifndef XTENSOR_FFTW_BASIC_LONG_DOUBLE_HPP +#define XTENSOR_FFTW_BASIC_LONG_DOUBLE_HPP + +#include "common.hpp" + +namespace xt { + namespace fftw { + + template <> struct fftw_t { + using plan = fftwl_plan; + using complex = fftwl_complex; + constexpr static void (&execute)(plan) = fftwl_execute; + constexpr static void (&destroy_plan)(plan) = fftwl_destroy_plan; + }; + + /////////////////////////////////////////////////////////////////////////////// + // Regular FFT (complex to complex) + /////////////////////////////////////////////////////////////////////////////// + + //// + // Regular FFT: 1D + //// + + inline xt::xarray > fft (const xt::xarray > &input) { + return _fft_, std::complex, 1, FFTW_FORWARD, true, false, false, fftwl_plan_dft_1d, fftwl_execute, fftwl_destroy_plan> (input); + } + + inline xt::xarray > ifft (const xt::xarray > &input) { + return _ifft_, std::complex, 1, FFTW_BACKWARD, true, false, false, fftwl_plan_dft_1d, fftwl_execute, fftwl_destroy_plan> (input); + } + + //// + // Regular FFT: 2D + //// + + inline xt::xarray > fft2 (const xt::xarray > &input) { + return _fft_, std::complex, 2, FFTW_FORWARD, true, false, false, fftwl_plan_dft_2d, fftwl_execute, fftwl_destroy_plan> (input); + } + + inline xt::xarray > ifft2 (const xt::xarray > &input) { + return _ifft_, std::complex, 2, FFTW_BACKWARD, true, false, false, fftwl_plan_dft_2d, fftwl_execute, fftwl_destroy_plan> (input); + } + + //// + // Regular FFT: 3D + //// + + inline xt::xarray > fft3 (const xt::xarray > &input) { + return _fft_, std::complex, 3, FFTW_FORWARD, true, false, false, fftwl_plan_dft_3d, fftwl_execute, fftwl_destroy_plan> (input); + } + + inline xt::xarray > ifft3 (const xt::xarray > &input) { + return _ifft_, std::complex, 3, FFTW_BACKWARD, true, false, false, fftwl_plan_dft_3d, fftwl_execute, fftwl_destroy_plan> (input); + } + + //// + // Regular FFT: nD + //// + + template + inline xt::xarray > fftn (const xt::xarray > &input) { + return _fft_, std::complex, dim, FFTW_FORWARD, false, false, false, fftwl_plan_dft, fftwl_execute, fftwl_destroy_plan> (input); + } + + template + inline xt::xarray > ifftn (const xt::xarray > &input) { + return _ifft_, std::complex, dim, FFTW_BACKWARD, false, false, false, fftwl_plan_dft, fftwl_execute, fftwl_destroy_plan> (input); + } + + /////////////////////////////////////////////////////////////////////////////// + // Real FFT (real input) + /////////////////////////////////////////////////////////////////////////////// + + //// + // Real FFT: 1D + //// + + inline xt::xarray > rfft (const xt::xarray &input) { + return _fft_, 1, 0, true, true, false, fftwl_plan_dft_r2c_1d, fftwl_execute, fftwl_destroy_plan> (input); + } + + inline xt::xarray irfft (const xt::xarray > &input, bool odd_last_dim = false) { + return _ifft_, long double, 1, 0, true, false, true, fftwl_plan_dft_c2r_1d, fftwl_execute, fftwl_destroy_plan> (input, odd_last_dim); + } + + //// + // Real FFT: 2D + //// + + inline xt::xarray > rfft2 (const xt::xarray &input) { + return _fft_, 2, 0, true, true, false, fftwl_plan_dft_r2c_2d, fftwl_execute, fftwl_destroy_plan> (input); + } + + inline xt::xarray irfft2 (const xt::xarray > &input, bool odd_last_dim = false) { + return _ifft_, long double, 2, 0, true, false, true, fftwl_plan_dft_c2r_2d, fftwl_execute, fftwl_destroy_plan> (input, odd_last_dim); + } + + //// + // Real FFT: 3D + //// + + inline xt::xarray > rfft3 (const xt::xarray &input) { + return _fft_, 3, 0, true, true, false, fftwl_plan_dft_r2c_3d, fftwl_execute, fftwl_destroy_plan> (input); + } + + inline xt::xarray irfft3 (const xt::xarray > &input, bool odd_last_dim = false) { + return _ifft_, long double, 3, 0, true, false, true, fftwl_plan_dft_c2r_3d, fftwl_execute, fftwl_destroy_plan> (input, odd_last_dim); + } + + //// + // Real FFT: nD + //// + + template + inline xt::xarray > rfftn (const xt::xarray &input) { + return _fft_, dim, 0, false, true, false, fftwl_plan_dft_r2c, fftwl_execute, fftwl_destroy_plan> (input); + } + + template + inline xt::xarray irfftn (const xt::xarray > &input, bool odd_last_dim = false) { + return _ifft_, long double, dim, 0, false, false, true, fftwl_plan_dft_c2r, fftwl_execute, fftwl_destroy_plan> (input, odd_last_dim); + } + + /////////////////////////////////////////////////////////////////////////////// + // Hermitian FFT (real spectrum) + /////////////////////////////////////////////////////////////////////////////// + + //// + // Hermitian FFT: 1D + //// + + inline xt::xarray hfft (const xt::xarray > &input) { + return _hfft_, long double, 1, 0, true, false, true, fftwl_plan_dft_c2r_1d, fftwl_execute, fftwl_destroy_plan> (input); + } + + inline xt::xarray > ihfft (const xt::xarray &input) { + return _ihfft_, 1, 0, true, true, false, fftwl_plan_dft_r2c_1d, fftwl_execute, fftwl_destroy_plan> (input); + } + + //// + // Hermitian FFT: 2D + //// + + inline xt::xarray hfft2 (const xt::xarray > &input) { + return _hfft_, long double, 2, 0, true, false, true, fftwl_plan_dft_c2r_2d, fftwl_execute, fftwl_destroy_plan> (input); + } + + inline xt::xarray > ihfft2 (const xt::xarray &input) { + return _ihfft_, 2, 0, true, true, false, fftwl_plan_dft_r2c_2d, fftwl_execute, fftwl_destroy_plan> (input); + } + + //// + // Hermitian FFT: 3D + //// + + inline xt::xarray hfft3 (const xt::xarray > &input) { + return _hfft_, long double, 3, 0, true, false, true, fftwl_plan_dft_c2r_3d, fftwl_execute, fftwl_destroy_plan> (input); + } + + inline xt::xarray > ihfft3 (const xt::xarray &input) { + return _ihfft_, 3, 0, true, true, false, fftwl_plan_dft_r2c_3d, fftwl_execute, fftwl_destroy_plan> (input); + } + + //// + // Hermitian FFT: nD + //// + + template + inline xt::xarray hfftn (const xt::xarray > &input) { + return _hfft_, long double, dim, 0, false, false, true, fftwl_plan_dft_c2r, fftwl_execute, fftwl_destroy_plan> (input); + } + + template + inline xt::xarray > ihfftn (const xt::xarray &input) { + return _ihfft_, dim, 0, false, true, false, fftwl_plan_dft_r2c, fftwl_execute, fftwl_destroy_plan> (input); + } + + } +} + +#endif //XTENSOR_FFTW_BASIC_LONG_DOUBLE_HPP diff --git a/include/xtensor-fftw/basic_option.hpp b/include/xtensor-fftw/basic_option.hpp new file mode 100644 index 0000000..6166286 --- /dev/null +++ b/include/xtensor-fftw/basic_option.hpp @@ -0,0 +1,35 @@ +/* + * xtensor-fftw + * Copyright (c) 2017, Patrick Bos + * + * Distributed under the terms of the BSD 3-Clause License. + * + * The full license is in the file LICENSE, distributed with this software. + * + * basic_option.hpp: + * Include xtensor-fftw functionalities with explicit defined precision types. + * + */ + +#ifndef XTENSOR_FFTW_BASIC_OPTION_HPP +#define XTENSOR_FFTW_BASIC_OPTION_HPP + +#if !defined(XTENSOR_FFTW_USE_FLOAT) \ + && !defined(XTENSOR_FFTW_USE_DOUBLE) \ + && !defined(XTENSOR_FFTW_USE_LONG_DOUBLE) +#error Missing definition of FFTW type library. Please #define at least once before include xtensor-fftw library. +#endif + +#ifdef XTENSOR_FFTW_USE_FLOAT +#include "xtensor-fftw/basic_float.hpp" +#endif + +#ifdef XTENSOR_FFTW_USE_DOUBLE +#include "xtensor-fftw/basic_double.hpp" +#endif + +#ifdef XTENSOR_FFTW_USE_LONG_DOUBLE +#include "xtensor-fftw/basic_long_double.hpp" +#endif + +#endif //XTENSOR_FFTW_BASIC_OPTION_HPP diff --git a/include/xtensor-fftw/common.hpp b/include/xtensor-fftw/common.hpp new file mode 100644 index 0000000..77256b1 --- /dev/null +++ b/include/xtensor-fftw/common.hpp @@ -0,0 +1,519 @@ +/* + * xtensor-fftw + * Copyright (c) 2017, Patrick Bos + * + * Distributed under the terms of the BSD 3-Clause License. + * + * The full license is in the file LICENSE, distributed with this software. + * + * common.hpp: + * Defines the commons datas and functions to wrap original FFTW library. + * + */ + +#ifndef XTENSOR_FFTW_COMMON_HPP +#define XTENSOR_FFTW_COMMON_HPP + +#include +#include "xtensor/xcomplex.hpp" +#include "xtensor/xeval.hpp" +#include +#include +#include +#include +#include +#include + +// for product accumulate: +#include +#include + +#include + +#include "xtensor-fftw_config.hpp" + +#ifdef __CLING__ + #pragma cling load("fftw3") +#endif + +namespace xt { + namespace fftw { + // The implementations must be inline to avoid multiple definition errors due to multiple compilations (e.g. when + // including this header multiple times in a project, or when it is explicitly compiled itself and included too). + + // Note: multidimensional complex-to-real transforms by default destroy the input data! See: + // http://www.fftw.org/fftw3_doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data + + // reinterpret_casts below suggested by http://www.fftw.org/fftw3_doc/Complex-numbers.html + + // We use the convention that the inverse fft divides by N, like numpy does. + + // FFTW is not thread-safe, so we need to guard around its functions (except fftw_execute). + namespace detail { + inline std::mutex& fftw_global_mutex() { + static std::mutex m; + return m; + } + } + + /////////////////////////////////////////////////////////////////////////////// + // General: templates defining the basic interaction logic with fftw. These + // will be specialized for all fft families, precisions and + // dimensionalities. + /////////////////////////////////////////////////////////////////////////////// + + // aliases for the fftw precision-dependent types: + template struct fftw_t { + static_assert(sizeof(T) == 0, "Only specializations of fftw_t can be used"); + }; + + // and subclass alias for when calling with a complex type: + template struct fftw_t< std::complex > : public fftw_t {}; + + // convert std::complex to fftwX_complex with right precision X; non-complex floats stay themselves: + template + using fftw_number_t = std::conditional_t< + xtl::is_complex::value, + typename fftw_t< xtl::complex_value_type_t >::complex, + xtl::complex_value_type_t + >; + + // short-hand for precision for template arguments + template + using prec_t = xtl::complex_value_type_t; + + // dimension-dependent function signatures of fftw planning functions + template + struct fftw_plan_dft_signature {}; + + template + struct fftw_plan_dft_signature { + using type = typename fftw_t::plan (&)(int rank, const int *n, fftw_number_t *, fftw_number_t *, unsigned int); + }; + template + struct fftw_plan_dft_signature { + using type = typename fftw_t::plan (&)(int n1, fftw_number_t *, fftw_number_t *, unsigned int); + }; + template + struct fftw_plan_dft_signature { + using type = typename fftw_t::plan (&)(int n1, int n2, fftw_number_t *, fftw_number_t *, unsigned int); + }; + template + struct fftw_plan_dft_signature { + using type = typename fftw_t::plan (&)(int n1, int n2, int n3, fftw_number_t *, fftw_number_t *, unsigned int); + }; + + template + struct fftw_plan_dft_signature { + using type = typename fftw_t::plan (&)(int rank, const int *n, fftw_number_t *, fftw_number_t *, int, unsigned int); + }; + template + struct fftw_plan_dft_signature { + using type = typename fftw_t::plan (&)(int n1, fftw_number_t *, fftw_number_t *, int, unsigned int); + }; + template + struct fftw_plan_dft_signature { + using type = typename fftw_t::plan (&)(int n1, int n2, fftw_number_t *, fftw_number_t *, int, unsigned int); + }; + template + struct fftw_plan_dft_signature { + using type = typename fftw_t::plan (&)(int n1, int n2, int n3, fftw_number_t *, fftw_number_t *, int, unsigned int); + }; + + + // all_true, from https://stackoverflow.com/a/28253503/1199693 + template struct bool_pack; + + template + using all_true = std::is_same< bool_pack, bool_pack >; + + // conditionals for correct combinations of dimensionality parameters + namespace dimensional { + template + struct is_1 : public std::false_type {}; + template <> + struct is_1<1, true> : public std::true_type {}; + + template + struct is_2 : public std::false_type {}; + template <> + struct is_2<2, true> : public std::true_type {}; + + template + struct is_3 : public std::false_type {}; + template <> + struct is_3<3, true> : public std::true_type {}; + + template + struct is_123 : public std::conditional_t< + is_1::value || is_2::value || is_3::value, + std::true_type, + std::false_type + > {}; + + template + struct is_n : public std::false_type {}; + template + struct is_n : public std::true_type {}; + } + + + // input vs output shape conversion + template + inline auto output_shape_from_input(const xt::xarray& input, bool half_plus_one_out, bool half_plus_one_in, bool odd_last_dim = false) { + auto output_shape = input.shape(); + if (half_plus_one_out) { // r2c + auto n = output_shape.size(); + output_shape[n-1] = output_shape[n-1]/2 + 1; + } else if (half_plus_one_in) { // c2r + auto n = output_shape.size(); + if (!odd_last_dim) { + output_shape[n - 1] = (output_shape[n - 1] - 1) * 2; + } else { + output_shape[n - 1] = (output_shape[n - 1] - 1) * 2 + 1; + } + } + return output_shape; + } + + // output to DFT-dimensions conversion + template + inline auto dft_dimensions_from_output(const xt::xarray& output, bool half_plus_one_out, bool odd_last_dim = false) { + auto dft_dimensions = output.shape(); + + if (half_plus_one_out) { // r2c + auto n = dft_dimensions.size(); + if (!odd_last_dim) { + dft_dimensions[n - 1] = (dft_dimensions[n - 1] - 1) * 2; + } else { + dft_dimensions[n - 1] = (dft_dimensions[n - 1] - 1) * 2 + 1; + } + } + + return dft_dimensions; + } + + + // Callers for fftw_plan_dft, since they have different call signatures and the + // way shape information is extracted from xtensor differs for different dimensionalities. + + // REGULAR FFT N-dim + template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> + inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool /*odd_last_dim*/ = false) + -> std::enable_if_t::value && (fftw_direction != 0), typename fftw_t::plan> { + using fftw_input_t = fftw_number_t; + using fftw_output_t = fftw_number_t; + + auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out); + std::vector dft_dimensions; + dft_dimensions.reserve(dft_dimensions_unsigned.size()); + std::transform(dft_dimensions_unsigned.begin(), dft_dimensions_unsigned.end(), std::back_inserter(dft_dimensions), [&](std::size_t d) { return static_cast(d); }); + + std::lock_guard guard(detail::fftw_global_mutex()); + return fftw_plan_dft(static_cast(dim), dft_dimensions.data(), + const_cast(reinterpret_cast(input.data())), + reinterpret_cast(output.data()), + fftw_direction, + flags); + }; + + // REGULAR FFT 1D + template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> + inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool /*odd_last_dim*/ = false) + -> std::enable_if_t::value && (fftw_direction != 0), typename fftw_t::plan> { + using fftw_input_t = fftw_number_t; + using fftw_output_t = fftw_number_t; + + auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out); + + std::lock_guard guard(detail::fftw_global_mutex()); + return fftw_plan_dft(static_cast(dft_dimensions_unsigned[0]), + const_cast(reinterpret_cast(input.data())), + reinterpret_cast(output.data()), + fftw_direction, + flags); + }; + + // REGULAR FFT 2D + template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> + inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool /*odd_last_dim*/ = false) + -> std::enable_if_t::value && (fftw_direction != 0), typename fftw_t::plan> { + using fftw_input_t = fftw_number_t; + using fftw_output_t = fftw_number_t; + + auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out); + + std::lock_guard guard(detail::fftw_global_mutex()); + return fftw_plan_dft(static_cast(dft_dimensions_unsigned[0]), static_cast(dft_dimensions_unsigned[1]), + const_cast(reinterpret_cast(input.data())), + reinterpret_cast(output.data()), + fftw_direction, + flags); + }; + + // REGULAR FFT 3D + template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> + inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool /*odd_last_dim*/ = false) + -> std::enable_if_t::value && (fftw_direction != 0), typename fftw_t::plan> { + using fftw_input_t = fftw_number_t; + using fftw_output_t = fftw_number_t; + + auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out); + + std::lock_guard guard(detail::fftw_global_mutex()); + return fftw_plan_dft(static_cast(dft_dimensions_unsigned[0]), static_cast(dft_dimensions_unsigned[1]), static_cast(dft_dimensions_unsigned[2]), + const_cast(reinterpret_cast(input.data())), + reinterpret_cast(output.data()), + fftw_direction, + flags); + }; + + // REAL FFT N-dim + template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> + inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool odd_last_dim = false) + -> std::enable_if_t::value && (fftw_direction == 0), typename fftw_t::plan> { + using fftw_input_t = fftw_number_t; + using fftw_output_t = fftw_number_t; + + auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out, odd_last_dim); + std::vector dft_dimensions; + dft_dimensions.reserve(dft_dimensions_unsigned.size()); + std::transform(dft_dimensions_unsigned.begin(), dft_dimensions_unsigned.end(), std::back_inserter(dft_dimensions), [&](std::size_t d) { return static_cast(d); }); + + std::lock_guard guard(detail::fftw_global_mutex()); + return fftw_plan_dft(static_cast(dim), dft_dimensions.data(), + const_cast(reinterpret_cast(input.data())), + reinterpret_cast(output.data()), + flags); + }; + + // REAL FFT 1D + template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> + inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool odd_last_dim = false) + -> std::enable_if_t::value && (fftw_direction == 0), typename fftw_t::plan> { + using fftw_input_t = fftw_number_t; + using fftw_output_t = fftw_number_t; + + auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out, odd_last_dim); + + std::lock_guard guard(detail::fftw_global_mutex()); + return fftw_plan_dft(static_cast(dft_dimensions_unsigned[0]), + const_cast(reinterpret_cast(input.data())), + reinterpret_cast(output.data()), + flags); + }; + + // REAL FFT 2D + template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> + inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool odd_last_dim = false) + -> std::enable_if_t::value && (fftw_direction == 0), typename fftw_t::plan> { + using fftw_input_t = fftw_number_t; + using fftw_output_t = fftw_number_t; + + auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out, odd_last_dim); + + std::lock_guard guard(detail::fftw_global_mutex()); + return fftw_plan_dft(static_cast(dft_dimensions_unsigned[0]), static_cast(dft_dimensions_unsigned[1]), + const_cast(reinterpret_cast(input.data())), + reinterpret_cast(output.data()), + flags); + }; + + // REAL FFT 3D + template ::type fftw_plan_dft, bool half_plus_one_out, bool half_plus_one_in> + inline auto fftw_plan_dft_caller(const xt::xarray &input, xt::xarray &output, unsigned int flags, bool odd_last_dim = false) + -> std::enable_if_t::value && (fftw_direction == 0), typename fftw_t::plan> { + using fftw_input_t = fftw_number_t; + using fftw_output_t = fftw_number_t; + + auto dft_dimensions_unsigned = dft_dimensions_from_output(output, half_plus_one_out, odd_last_dim); + + std::lock_guard guard(detail::fftw_global_mutex()); + return fftw_plan_dft(static_cast(dft_dimensions_unsigned[0]), static_cast(dft_dimensions_unsigned[1]), static_cast(dft_dimensions_unsigned[2]), + const_cast(reinterpret_cast(input.data())), + reinterpret_cast(output.data()), + flags); + }; + + + //// + // General: xarray templates + //// + +// template +// inline xt::xarray _fft_ (const xt::xarray &input) { +// static_assert(sizeof(prec_t) == 0, "Only specializations of _fft_ can be used"); +// } +// +// template +// inline xt::xarray _ifft_ (const xt::xarray &input) { +// static_assert(sizeof(prec_t) == 0, "Only specializations of _ifft_ can be used"); +// } + + template < + typename input_t, typename output_t, std::size_t dim, int fftw_direction, bool fftw_123dim, bool half_plus_one_out, bool half_plus_one_in, + typename fftw_plan_dft_signature::type fftw_plan_dft, + void (&fftw_execute)(typename fftw_t::plan), void (&fftw_destroy_plan)(typename fftw_t::plan), + typename = std::enable_if_t< + std::is_same< prec_t, prec_t >::value // input and output precision must be the same + && std::is_floating_point< prec_t >::value // numbers must be float, double or long double + && (dimensional::is_123::value // dimensionality must match fftw_123dim + || dimensional::is_n::value) + > + > + inline xt::xarray _fft_(const xt::xarray &input) { + auto output_shape = output_shape_from_input(input, half_plus_one_out, half_plus_one_in, false); + xt::xarray output(output_shape); + + bool odd_last_dim = (input.shape()[input.shape().size()-1] % 2 != 0); + + auto plan = fftw_plan_dft_caller(input, output, FFTW_ESTIMATE, odd_last_dim); + if (plan == nullptr) { + XTENSOR_FFTW_THROW(std::runtime_error, + "Plan creation returned nullptr. This usually means FFTW cannot create a plan for the given arguments (e.g. a non-destructive multi-dimensional real FFT is impossible in FFTW)."); + } + + fftw_execute(plan); + { + std::lock_guard guard(detail::fftw_global_mutex()); + fftw_destroy_plan(plan); + } + return output; + }; + + template < + typename input_t, typename output_t, std::size_t dim, int fftw_direction, bool fftw_123dim, bool half_plus_one_out, bool half_plus_one_in, + typename fftw_plan_dft_signature::type fftw_plan_dft, + void (&fftw_execute)(typename fftw_t::plan), void (&fftw_destroy_plan)(typename fftw_t::plan), + typename = std::enable_if_t< + std::is_same< prec_t, prec_t >::value // input and output precision must be the same + && std::is_floating_point< prec_t >::value // numbers must be float, double or long double + && (dimensional::is_123::value // dimensionality must match fftw_123dim + || dimensional::is_n::value) + > + > + inline xt::xarray _ifft_(const xt::xarray &input, bool odd_last_dim = false) { + auto output_shape = output_shape_from_input(input, half_plus_one_out, half_plus_one_in, odd_last_dim); + xt::xarray output(output_shape); + + auto plan = fftw_plan_dft_caller(input, output, FFTW_ESTIMATE, odd_last_dim); + if (plan == nullptr) { + XTENSOR_FFTW_THROW(std::runtime_error, + "Plan creation returned nullptr. This usually means FFTW cannot create a plan for the given arguments (e.g. a non-destructive multi-dimensional real FFT is impossible in FFTW)."); + } + + fftw_execute(plan); + { + std::lock_guard guard(detail::fftw_global_mutex()); + fftw_destroy_plan(plan); + } + auto dft_dimensions = dft_dimensions_from_output(output, half_plus_one_out, odd_last_dim); + auto N_dft = static_cast >(std::accumulate(dft_dimensions.begin(), dft_dimensions.end(), static_cast(1u), std::multiplies())); + return output / N_dft; + }; + + template < + typename input_t, typename output_t, std::size_t dim, int fftw_direction, bool fftw_123dim, bool half_plus_one_out, bool half_plus_one_in, + typename fftw_plan_dft_signature::type fftw_plan_dft, + void (&fftw_execute)(typename fftw_t::plan), void (&fftw_destroy_plan)(typename fftw_t::plan), + typename = std::enable_if_t< + std::is_same< prec_t, prec_t >::value // input and output precision must be the same + && std::is_floating_point< prec_t >::value // numbers must be float, double or long double + && (dimensional::is_123::value // dimensionality must match fftw_123dim + || dimensional::is_n::value) + > + > + inline xt::xarray _hfft_(const xt::xarray &input) { + auto output_shape = output_shape_from_input(input, half_plus_one_out, half_plus_one_in); + xt::xarray output(output_shape); + + xt::xarray input_conj = xt::conj(input); + + auto plan = fftw_plan_dft_caller(input_conj, output, FFTW_ESTIMATE); + if (plan == nullptr) { + XTENSOR_FFTW_THROW(std::runtime_error, + "Plan creation returned nullptr. This usually means FFTW cannot create a plan for the given arguments (e.g. a non-destructive multi-dimensional real FFT is impossible in FFTW)."); + } + + fftw_execute(plan); + { + std::lock_guard guard(detail::fftw_global_mutex()); + fftw_destroy_plan(plan); + } + return output; + }; + + template < + typename input_t, typename output_t, std::size_t dim, int fftw_direction, bool fftw_123dim, bool half_plus_one_out, bool half_plus_one_in, + typename fftw_plan_dft_signature::type fftw_plan_dft, + void (&fftw_execute)(typename fftw_t::plan), void (&fftw_destroy_plan)(typename fftw_t::plan), + typename = std::enable_if_t< + std::is_same< prec_t, prec_t >::value // input and output precision must be the same + && std::is_floating_point< prec_t >::value // numbers must be float, double or long double + && (dimensional::is_123::value // dimensionality must match fftw_123dim + || dimensional::is_n::value) + > + > + inline xt::xarray _ihfft_(const xt::xarray &input) { + auto output_shape = output_shape_from_input(input, half_plus_one_out, half_plus_one_in); + xt::xarray output(output_shape); + + auto plan = fftw_plan_dft_caller(input, output, FFTW_ESTIMATE); + if (plan == nullptr) { + XTENSOR_FFTW_THROW(std::runtime_error, + "Plan creation returned nullptr. This usually means FFTW cannot create a plan for the given arguments (e.g. a non-destructive multi-dimensional real FFT is impossible in FFTW)."); + } + + fftw_execute(plan); + { + std::lock_guard guard(detail::fftw_global_mutex()); + fftw_destroy_plan(plan); + } + output = xt::conj(output); + + auto dft_dimensions = dft_dimensions_from_output(output, half_plus_one_out); + auto N_dft = static_cast >(std::accumulate(dft_dimensions.begin(), dft_dimensions.end(), static_cast(1u), std::multiplies())); + return output / N_dft; + }; + + + //// + // General: xtensor templates + //// + +// template +// xt::xtensor< std::complex, dim > _fft_(const xt::xtensor &input) { +// static_assert(sizeof(real_t) == 0, "Only specializations of fft can be used"); +// +// xt::xtensor, dim> output(input.shape(), input.strides()); +// +// fftw_plan_t plan = fftwXXXXX_plan_dft_r2c_1d(static_cast(input.size()), +// const_cast(input.data()), +// reinterpret_cast(output.data()), +// FFTW_ESTIMATE); +// +// fftwXXXXX_execute(plan); +// fftwXXXXX_destroy_plan(plan); +// return output; +// }; +// +// template +// xt::xtensor _ifft_(const xt::xtensor< std::complex, dim > &input) { +// static_assert(sizeof(real_t) == 0, "Only specializations of ifft can be used"); +// +// xt::xtensor output(input.shape(), input.strides()); +// +// fftw_plan_t plan = fftwXXXXX_plan_dft_c2r_1d(static_cast(input.size()), +// const_cast(reinterpret_cast(input.data())), +// output.data(), +// FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); +// +// fftwXXXXX_execute(plan); +// fftwXXXXX_destroy_plan(plan); +// return output / output.size(); +// }; + + } //namespace fftw +} //namespace xt + +#endif //XTENSOR_FFTW_COMMON_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index eb01d31..08653be 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -117,7 +117,7 @@ add_executable(${XTENSOR_FFTW_TARGET} ${XTENSOR_FFTW_TESTS} ${XTENSOR_HEADERS} $ if(DOWNLOAD_GTEST OR GTEST_SRC_DIR) add_dependencies(${XTENSOR_FFTW_TARGET} gtest_main) endif() -target_link_libraries(${XTENSOR_FFTW_TARGET} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${FFTW_LIBRARIES}) +target_link_libraries(${XTENSOR_FFTW_TARGET} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${FFTW_LINK_FLAGS}) add_custom_target(xtest COMMAND test_xtensor-fftw DEPENDS ${XTENSOR_FFTW_TARGET}) diff --git a/test/basic_interface.hpp b/test/basic_interface.hpp index ad8e417..1b218d9 100644 --- a/test/basic_interface.hpp +++ b/test/basic_interface.hpp @@ -22,12 +22,33 @@ #include "gtest/gtest.h" -// the GoogleTest list of typed test cases -#ifdef FFTW_NO_LONGDOUBLE -typedef ::testing::Types MyTypes; +#ifndef XTENSOR_FFTW_USE_FLOAT + #define Add_Float #else -typedef ::testing::Types MyTypes; -#endif // FFTW_NO_LONGDOUBLE + #if defined(XTENSOR_FFTW_USE_DOUBLE) || defined(XTENSOR_FFTW_USE_LONG_DOUBLE) + #define Add_Float float, + #else + #define Add_Float float + #endif +#endif + +#ifndef XTENSOR_FFTW_USE_DOUBLE + #define Add_Double +#else + #if defined(XTENSOR_FFTW_USE_LONG_DOUBLE) + #define Add_Double double, + #else + #define Add_Double double + #endif +#endif + +#ifndef XTENSOR_FFTW_USE_LONG_DOUBLE + #define Add_Long_Double +#else + #define Add_Long_Double long double +#endif + +typedef ::testing::Types MyTypes; // Generates a dim-dimensional array of size n in each dimension, filled with random numbers between 0 and the numeric // limit of type T divided by pow(n, dim) (the latter to keep the FFTs from generating infs and nans). diff --git a/test/basic_interface_fft.cpp b/test/basic_interface_fft.cpp index 0bb6779..e4d1cb7 100644 --- a/test/basic_interface_fft.cpp +++ b/test/basic_interface_fft.cpp @@ -7,7 +7,7 @@ * The full license is in the file LICENSE, distributed with this software. */ -#include +#include #include "basic_interface.hpp" /////////////////////////////////////////////////////////////////////////////// diff --git a/test/basic_interface_hfft.cpp b/test/basic_interface_hfft.cpp index b967280..bcf9e4e 100644 --- a/test/basic_interface_hfft.cpp +++ b/test/basic_interface_hfft.cpp @@ -7,7 +7,7 @@ * The full license is in the file LICENSE, distributed with this software. */ -#include +#include #include "basic_interface.hpp" /////////////////////////////////////////////////////////////////////////////// diff --git a/test/basic_interface_rfft.cpp b/test/basic_interface_rfft.cpp index 0b50f27..b45e6ec 100644 --- a/test/basic_interface_rfft.cpp +++ b/test/basic_interface_rfft.cpp @@ -7,7 +7,7 @@ * The full license is in the file LICENSE, distributed with this software. */ -#include +#include #include "basic_interface.hpp" /////////////////////////////////////////////////////////////////////////////// diff --git a/test/examples.cpp b/test/examples.cpp index 46e3c3c..a405ee0 100644 --- a/test/examples.cpp +++ b/test/examples.cpp @@ -7,12 +7,12 @@ */ // real life examples - +#ifdef XTENSOR_FFTW_USE_DOUBLE #define _USE_MATH_DEFINES // for MSVC ("Math Constants are not defined in Standard C/C++") #include // M_PI #include -#include +#include #include #include #include // xt::arange @@ -21,7 +21,6 @@ #include "gtest/gtest.h" - TEST(examples, sin_derivative) { // generate a sinusoid field double dx = M_PI/100; @@ -45,3 +44,4 @@ TEST(examples, sin_derivative) { // std::cout << "cos: " << xt::cos(x) << std::endl; // std::cout << "sin_derivative: " << sin_derivative << std::endl; } +#endif From 0d36bd71aa57f7285f022d92389273795a9aa122 Mon Sep 17 00:00:00 2001 From: michael bacci Date: Thu, 24 Jun 2021 11:59:07 +0200 Subject: [PATCH 04/20] fix: Install new headers --- CMakeLists.txt | 7 ++++++- xtensor-fftwConfig.cmake.in | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 495dfd5..5461841 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,7 @@ else() set(FFTW_USE_LONG_DOUBLE OFF) endif() -if(NOT FFTW_USE_FLOAT AND NOT FFTW_USE_DOUBLE AND NOT FFTW_USE_LONG_DOUBLE) +if(NOT REQUIRE_FLOAT_LIB AND NOT REQUIRE_DOUBLE_LIB AND NOT REQUIRE_LONG_DOUBLE_LIB) message(FATAL_ERROR "Please, select at least one of the available FFTW type libraries") endif() @@ -177,7 +177,12 @@ endif(FIX_RPATH) #--------------------------------------- library contents set(XTENSOR_FFTW_HEADERS + ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/basic_double.hpp + ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/basic_float.hpp + ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/basic_long_double.hpp + ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/basic_option.hpp ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/basic.hpp + ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/common.hpp ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/helper.hpp ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/xtensor-fftw_config.hpp ) diff --git a/xtensor-fftwConfig.cmake.in b/xtensor-fftwConfig.cmake.in index 36d7ff5..c38aa7d 100644 --- a/xtensor-fftwConfig.cmake.in +++ b/xtensor-fftwConfig.cmake.in @@ -16,6 +16,22 @@ @PACKAGE_INIT@ +if(FFTW_USE_FLOAT) + add_definitions(-DXTENSOR_FFTW_USE_FLOAT) +endif() + +if(FFTW_USE_DOUBLE) + add_definitions(-DXTENSOR_FFTW_USE_DOUBLE)s +endif() + +if(FFTW_USE_LONG_DOUBLE AND NOT MSVC) + add_definitions(-DXTENSOR_FFTW_USE_LONG_DOUBLE) +endif() + +if(NOT FFTW_USE_FLOAT AND NOT FFTW_USE_DOUBLE AND NOT FFTW_USE_LONG_DOUBLE) + message(FATAL_ERROR "Please, select at least one of the available FFTW type libraries") +endif() + set(PN xtensor-fftw) set_and_check(${PN}_INCLUDE_DIRS "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@") set(${PN}_LIBRARY "") From 1d1176052c084370b497182c8bd7652bc6f89360 Mon Sep 17 00:00:00 2001 From: michael bacci Date: Thu, 24 Jun 2021 11:59:07 +0200 Subject: [PATCH 05/20] fix: Install new headers --- CMakeLists.txt | 7 ++++++- xtensor-fftwConfig.cmake.in | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 495dfd5..5461841 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,7 @@ else() set(FFTW_USE_LONG_DOUBLE OFF) endif() -if(NOT FFTW_USE_FLOAT AND NOT FFTW_USE_DOUBLE AND NOT FFTW_USE_LONG_DOUBLE) +if(NOT REQUIRE_FLOAT_LIB AND NOT REQUIRE_DOUBLE_LIB AND NOT REQUIRE_LONG_DOUBLE_LIB) message(FATAL_ERROR "Please, select at least one of the available FFTW type libraries") endif() @@ -177,7 +177,12 @@ endif(FIX_RPATH) #--------------------------------------- library contents set(XTENSOR_FFTW_HEADERS + ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/basic_double.hpp + ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/basic_float.hpp + ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/basic_long_double.hpp + ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/basic_option.hpp ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/basic.hpp + ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/common.hpp ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/helper.hpp ${XTENSOR_FFTW_INCLUDE_DIR}/xtensor-fftw/xtensor-fftw_config.hpp ) diff --git a/xtensor-fftwConfig.cmake.in b/xtensor-fftwConfig.cmake.in index 36d7ff5..c46b524 100644 --- a/xtensor-fftwConfig.cmake.in +++ b/xtensor-fftwConfig.cmake.in @@ -16,6 +16,22 @@ @PACKAGE_INIT@ +if(FFTW_USE_FLOAT) + add_definitions(-DXTENSOR_FFTW_USE_FLOAT) +endif() + +if(FFTW_USE_DOUBLE) + add_definitions(-DXTENSOR_FFTW_USE_DOUBLE) +endif() + +if(FFTW_USE_LONG_DOUBLE AND NOT MSVC) + add_definitions(-DXTENSOR_FFTW_USE_LONG_DOUBLE) +endif() + +if(NOT FFTW_USE_FLOAT AND NOT FFTW_USE_DOUBLE AND NOT FFTW_USE_LONG_DOUBLE) + message(FATAL_ERROR "Please, select at least one of the available FFTW type libraries") +endif() + set(PN xtensor-fftw) set_and_check(${PN}_INCLUDE_DIRS "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@") set(${PN}_LIBRARY "") From 936c74fbbfec2853fd0dc807dfbc9867c8638c51 Mon Sep 17 00:00:00 2001 From: Roy Kid Date: Mon, 28 Apr 2025 19:48:50 +0000 Subject: [PATCH 06/20] [dev] default c++20, cmake==3.29.0; gtest==v1.16.0 --- CMakeLists.txt | 4 ++-- bench/CMakeLists.txt | 4 ++-- bench/copyGBench.cmake.in | 2 +- bench/downloadGBench.cmake.in | 2 +- include/xtensor-fftw/common.hpp | 7 +++---- include/xtensor-fftw/helper.hpp | 2 +- test/CMakeLists.txt | 4 ++-- test/basic_interface.hpp | 8 ++++---- test/copyGTest.cmake.in | 2 +- test/downloadGTest.cmake.in | 8 ++++++-- test/examples.cpp | 8 ++++---- test/helper.cpp | 2 +- 12 files changed, 28 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5461841..c63d16d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ # - Copyright 2017 Johan Mabille (rpath fix) # -cmake_minimum_required(VERSION 3.1.3) # 3.1.3 for set(CMAKE_CXX_STANDARD 14) +cmake_minimum_required(VERSION 3.29) # 3.1.3 for set(CMAKE_CXX_STANDARD 14) project(xtensor-fftw) @@ -86,7 +86,7 @@ endif() #--------------------------------------- build parameters for all targets # c++ standard build options # N.B.: these have to be set before defining targets! -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED YES) set(CMAKE_CXX_EXTENSIONS NO) diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index b84cd80..b0c61b7 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -15,7 +15,7 @@ # ############################################################################ -cmake_minimum_required(VERSION 3.1.3) # 3.1.3 for set(CMAKE_CXX_STANDARD 14) +cmake_minimum_required(VERSION 3.29) # 3.1.3 for set(CMAKE_CXX_STANDARD 14) if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) project(xtensor-fftw-bench) @@ -32,7 +32,7 @@ include(CheckCXXCompilerFlag) #string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 20) #if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Intel") # add_compile_options(-march=native) #-Wunused-parameter -Wextra -Wreorder -Wconversion) diff --git a/bench/copyGBench.cmake.in b/bench/copyGBench.cmake.in index ec66cb0..17629e8 100644 --- a/bench/copyGBench.cmake.in +++ b/bench/copyGBench.cmake.in @@ -11,7 +11,7 @@ # ############################################################################ -cmake_minimum_required(VERSION 2.8.2) +cmake_minimum_required(VERSION 3.29) project(googlebench-download NONE) diff --git a/bench/downloadGBench.cmake.in b/bench/downloadGBench.cmake.in index 7c057df..44c02e3 100644 --- a/bench/downloadGBench.cmake.in +++ b/bench/downloadGBench.cmake.in @@ -11,7 +11,7 @@ # ############################################################################ -cmake_minimum_required(VERSION 2.8.2) +cmake_minimum_required(VERSION 3.29) project(googlebench-download NONE) diff --git a/include/xtensor-fftw/common.hpp b/include/xtensor-fftw/common.hpp index 77256b1..eae1993 100644 --- a/include/xtensor-fftw/common.hpp +++ b/include/xtensor-fftw/common.hpp @@ -14,10 +14,9 @@ #ifndef XTENSOR_FFTW_COMMON_HPP #define XTENSOR_FFTW_COMMON_HPP -#include -#include "xtensor/xcomplex.hpp" -#include "xtensor/xeval.hpp" -#include +#include +#include +#include #include #include #include diff --git a/include/xtensor-fftw/helper.hpp b/include/xtensor-fftw/helper.hpp index 00bc13e..9a5410d 100644 --- a/include/xtensor-fftw/helper.hpp +++ b/include/xtensor-fftw/helper.hpp @@ -12,7 +12,7 @@ #define _USE_MATH_DEFINES // for MSVC ("Math Constants are not defined in Standard C/C++") #include // M_PI -#include +#include namespace xt { namespace fftw { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 08653be..d700e5c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,7 +15,7 @@ # ############################################################################ -cmake_minimum_required(VERSION 3.1.3) # 3.1.3 for set(CMAKE_CXX_STANDARD 14) +cmake_minimum_required(VERSION 3.29) # 3.1.3 for set(CMAKE_CXX_STANDARD 14) if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) project(xtensor-fftw-test) @@ -31,7 +31,7 @@ include(CheckCXXCompilerFlag) string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 20) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR (CMAKE_CXX_COMPILER_ID MATCHES "Intel" AND NOT WIN32)) if (DISABLE_EXCEPTIONS) diff --git a/test/basic_interface.hpp b/test/basic_interface.hpp index 1b218d9..0dccd55 100644 --- a/test/basic_interface.hpp +++ b/test/basic_interface.hpp @@ -15,10 +15,10 @@ #include #include // workaround for xt bug, where only including xarray does not include stdexcept; TODO: remove this include when bug is fixed! -#include -#include -#include -#include +#include +#include +#include +#include #include "gtest/gtest.h" diff --git a/test/copyGTest.cmake.in b/test/copyGTest.cmake.in index 1e5d3ed..a7ce34c 100644 --- a/test/copyGTest.cmake.in +++ b/test/copyGTest.cmake.in @@ -6,7 +6,7 @@ # The full license is in the file LICENSE, distributed with this software. # ############################################################################ -cmake_minimum_required(VERSION 2.8.2) +cmake_minimum_required(VERSION 3.29) project(googletest-download NONE) diff --git a/test/downloadGTest.cmake.in b/test/downloadGTest.cmake.in index a4a0817..24a1e7c 100644 --- a/test/downloadGTest.cmake.in +++ b/test/downloadGTest.cmake.in @@ -6,14 +6,16 @@ # The full license is in the file LICENSE, distributed with this software. # ############################################################################ -cmake_minimum_required(VERSION 2.8.2) +cmake_minimum_required(VERSION 3.29) project(googletest-download NONE) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) include(ExternalProject) ExternalProject_Add(googletest GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-1.8.0 + GIT_TAG v1.16.0 SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src" BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build" CONFIGURE_COMMAND "" @@ -22,3 +24,5 @@ ExternalProject_Add(googletest TEST_COMMAND "" ) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) \ No newline at end of file diff --git a/test/examples.cpp b/test/examples.cpp index a405ee0..6b6dcb3 100644 --- a/test/examples.cpp +++ b/test/examples.cpp @@ -14,10 +14,10 @@ #include #include -#include -#include // xt::arange -#include // xt::sin, cos -#include +#include +#include // xt::arange +#include // xt::sin, cos +#include #include "gtest/gtest.h" diff --git a/test/helper.cpp b/test/helper.cpp index 63a4d0c..e4d0b86 100644 --- a/test/helper.cpp +++ b/test/helper.cpp @@ -9,7 +9,7 @@ #define _USE_MATH_DEFINES // for MSVC ("Math Constants are not defined in Standard C/C++") #include // M_PI -#include +#include #include #include "gtest/gtest.h" From 4f0516e9c4f880aa485bfdfdc0b25ee843121c2f Mon Sep 17 00:00:00 2001 From: Roy Kid Date: Mon, 28 Apr 2025 19:49:40 +0000 Subject: [PATCH 07/20] [dev] default c++20, cmake==3.29.0; gtest==v1.16.0 --- CMakeLists.txt | 2 +- bench/CMakeLists.txt | 2 +- test/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c63d16d..5a054cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ # - Copyright 2017 Johan Mabille (rpath fix) # -cmake_minimum_required(VERSION 3.29) # 3.1.3 for set(CMAKE_CXX_STANDARD 14) +cmake_minimum_required(VERSION 3.29) project(xtensor-fftw) diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index b0c61b7..d2509f1 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -15,7 +15,7 @@ # ############################################################################ -cmake_minimum_required(VERSION 3.29) # 3.1.3 for set(CMAKE_CXX_STANDARD 14) +cmake_minimum_required(VERSION 3.29) if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) project(xtensor-fftw-bench) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d700e5c..e51d694 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,7 +15,7 @@ # ############################################################################ -cmake_minimum_required(VERSION 3.29) # 3.1.3 for set(CMAKE_CXX_STANDARD 14) +cmake_minimum_required(VERSION 3.29) if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) project(xtensor-fftw-test) From 1124526e9519fd3e54096575ed021e3874e78d0f Mon Sep 17 00:00:00 2001 From: Roy Kid Date: Mon, 28 Apr 2025 19:51:03 +0000 Subject: [PATCH 08/20] [dev] update xtensor>=0.26, xtl>=0.8 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a054cd..3fc47f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,7 +96,7 @@ set(CMAKE_CXX_EXTENSIONS NO) include_directories(${XTENSOR_FFTW_INCLUDE_DIR}) # .. xtl -set(xtl_REQUIRED_VERSION 0.6.9) +set(xtl_REQUIRED_VERSION 0.8.0) if(TARGET xtl) set(xtl_VERSION ${XTL_VERSION_MAJOR}.${XTL_VERSION_MINOR}.${XTL_VERSION_PATCH}) # Note: This is not SEMVER compatible comparison @@ -111,7 +111,7 @@ else() endif() # .. xtensor -set(xtensor_REQUIRED_VERSION 0.20.9) +set(xtensor_REQUIRED_VERSION 0.26.0) if(TARGET xtensor) set(xtensor_VERSION ${XTENSOR_VERSION_MAJOR}.${XTENSOR_VERSION_MINOR}.${XTENSOR_VERSION_PATCH}) # Note: This is not SEMVER compatible comparison From 413fab5e3a38aaa62bf1e4b6bd9b652a3fc77103 Mon Sep 17 00:00:00 2001 From: Roy Kid Date: Mon, 28 Apr 2025 19:52:56 +0000 Subject: [PATCH 09/20] [gtest] replace TYPED_TEST_CASE with TYPED_TEST_SUITE due to `TypedTestCaseIsDeprecated is deprecated` --- test/basic_interface_fft.cpp | 2 +- test/basic_interface_hfft.cpp | 2 +- test/basic_interface_rfft.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/basic_interface_fft.cpp b/test/basic_interface_fft.cpp index e4d1cb7..9655865 100644 --- a/test/basic_interface_fft.cpp +++ b/test/basic_interface_fft.cpp @@ -18,7 +18,7 @@ template class TransformAndInvert_FFT : public ::testing::Test {}; -TYPED_TEST_CASE(TransformAndInvert_FFT, MyTypes); +TYPED_TEST_SUITE(TransformAndInvert_FFT, MyTypes); /////////////////////////////////////////////////////////////////////////////// // Regular FFT (complex to complex) diff --git a/test/basic_interface_hfft.cpp b/test/basic_interface_hfft.cpp index bcf9e4e..55d2ab5 100644 --- a/test/basic_interface_hfft.cpp +++ b/test/basic_interface_hfft.cpp @@ -18,7 +18,7 @@ template class TransformAndInvert_hermFFT : public ::testing::Test {}; -TYPED_TEST_CASE(TransformAndInvert_hermFFT, MyTypes); +TYPED_TEST_SUITE(TransformAndInvert_hermFFT, MyTypes); /////////////////////////////////////////////////////////////////////////////// diff --git a/test/basic_interface_rfft.cpp b/test/basic_interface_rfft.cpp index b45e6ec..97ec40d 100644 --- a/test/basic_interface_rfft.cpp +++ b/test/basic_interface_rfft.cpp @@ -18,7 +18,7 @@ template class TransformAndInvert_realFFT : public ::testing::Test {}; -TYPED_TEST_CASE(TransformAndInvert_realFFT, MyTypes); +TYPED_TEST_SUITE(TransformAndInvert_realFFT, MyTypes); /////////////////////////////////////////////////////////////////////////////// From d436986a481fc8ceae2fae4bcee1524f0533272c Mon Sep 17 00:00:00 2001 From: Roy Kid Date: Mon, 28 Apr 2025 19:57:11 +0000 Subject: [PATCH 10/20] [dev] update notebooks(w/o xtensor-io); update travis --- .travis.yml | 2 +- notebooks/intensely_edgy_cat.ipynb | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index b1cb501..6f32960 100644 --- a/.travis.yml +++ b/.travis.yml @@ -157,7 +157,7 @@ install: - export PATH="$HOME/miniconda/bin:$PATH" - hash -r - conda update -yq conda - - conda install -y cmake xtl==0.6.9 xtensor=0.21.2 nlohmann_json=3.7.1 -c conda-forge + - conda install -y cmake xtl=>0.8.0 xtensor=>0.26.0 nlohmann_json=3.7.1 -c conda-forge - | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then conda install -y fftw -c conda-forge diff --git a/notebooks/intensely_edgy_cat.ipynb b/notebooks/intensely_edgy_cat.ipynb index 7c54367..faf5ff8 100644 --- a/notebooks/intensely_edgy_cat.ipynb +++ b/notebooks/intensely_edgy_cat.ipynb @@ -9,13 +9,13 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "#include // xarrays!\n", - "#include // slice the image arrays into color channels\n", - "#include // xt::stack\n", + "#include // xarrays!\n", + "#include // slice the image arrays into color channels\n", + "#include // xt::stack\n", "\n", "#include // for loading images\n", "\n", From bf240cc6f898cd754333e5996df2159fdc6b2e80 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Jun 2026 19:40:43 +0000 Subject: [PATCH 11/20] Initial plan From a215776c287fcee6775f9b4283cd9abf8556b62e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Jun 2026 19:44:26 +0000 Subject: [PATCH 12/20] ci: migrate Travis and AppVeyor to GitHub Actions --- .appveyor.yml | 51 ---------- .github/workflows/main.yml | 101 ++++++++++++++++++++ .travis.yml | 185 ------------------------------------- README.md | 3 +- appveyor_install_fftw.ps1 | 29 ------ 5 files changed, 102 insertions(+), 267 deletions(-) delete mode 100644 .appveyor.yml create mode 100644 .github/workflows/main.yml delete mode 100644 .travis.yml delete mode 100644 appveyor_install_fftw.ps1 diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 4c4b48b..0000000 --- a/.appveyor.yml +++ /dev/null @@ -1,51 +0,0 @@ -build: false - -image: Visual Studio 2015 - -configuration: Release - -platform: - - x64 - - x86 - -environment: - matrix: - # fftw from conda-forge - - MINICONDA: C:\Miniconda36-x64 - FFTW_INSTALL_MODE: conda-forge - FFTW_ROOT: - # fftw from the "official" fftw website build - - MINICONDA: C:\Miniconda36-x64 - FFTW_INSTALL_MODE: official - FFTW_ROOT: C:\fftw_download - -matrix: - exclude: - - platform: x86 - MINICONDA: C:\Miniconda36-x64 - - platform: x64 - MINICONDA: C:\Miniconda36 - fast_finish: true - -init: - - C:\"Program Files (x86)"\"Microsoft Visual Studio 14.0"\VC\vcvarsall.bat %PLATFORM% - - "set PATH=%FFTW_ROOT%;%MINICONDA%;%MINICONDA%\\Scripts;%MINICONDA%\\Library\\bin;%PATH%" - -install: - - conda config --set always_yes yes --set changeps1 no - - conda config --add channels conda-forge - - conda update -q conda - - conda info -a - - conda install xtensor=0.21.2 -c conda-forge - - IF "%FFTW_INSTALL_MODE%"=="official" ( Powershell.exe -File appveyor_install_fftw.ps1 ) ELSE IF "%FFTW_INSTALL_MODE%"=="conda-forge" ( conda install fftw ) - - cd %APPVEYOR_BUILD_FOLDER% - - mkdir build - - cd build - - cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DFFTW_ROOT=%FFTW_ROOT% -DCMAKE_INSTALL_PREFIX=%MINICONDA%\\Library -DDOWNLOAD_GTEST=ON .. - - nmake test_xtensor-fftw - -before_build: - - cd test - -build_script: - - test_xtensor-fftw.exe diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..2e82ebe --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,101 @@ +name: CI +on: + workflow_dispatch: + pull_request: + push: + branches: [master] +concurrency: + group: ${{ github.workflow }}-${{ github.job }}-${{ github.ref }} + cancel-in-progress: true +defaults: + run: + shell: bash -e -l {0} +jobs: + unix: + runs-on: ${{ matrix.os }} + name: ${{ matrix.name }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-24.04 + name: Linux + extra-cmake-args: "" + - os: ubuntu-24.04 + name: Linux (no exceptions) + extra-cmake-args: "-DDISABLE_EXCEPTIONS=ON" + - os: macos-13 + name: macOS + extra-cmake-args: "" + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set conda environment + uses: mamba-org/setup-micromamba@main + with: + environment-name: xtensor-fftw + init-shell: bash + cache-downloads: true + condarc: | + channels: + - conda-forge + channel_priority: strict + create-args: >- + cmake + ninja + xtl + xtensor + fftw + gtest + + - name: Configure using CMake + run: cmake -G Ninja -Bbuild -DBUILD_TESTS=ON -DCMAKE_PREFIX_PATH=$CONDA_PREFIX ${{ matrix.extra-cmake-args }} + + - name: Build + working-directory: build + run: cmake --build . --target test_xtensor-fftw --parallel 2 + + - name: Run tests + working-directory: build/test + run: ./test_xtensor-fftw + + windows: + runs-on: windows-latest + + steps: + - name: Setup MSVC + uses: ilammy/msvc-dev-cmd@v1 + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set conda environment + uses: mamba-org/setup-micromamba@main + with: + environment-name: xtensor-fftw + init-shell: bash + cache-downloads: true + condarc: | + channels: + - conda-forge + channel_priority: strict + create-args: >- + cmake + ninja + xtl + xtensor + fftw + gtest + + - name: Configure using CMake + run: cmake -G Ninja -Bbuild -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DCMAKE_PREFIX_PATH=$CONDA_PREFIX + + - name: Build + working-directory: build + run: cmake --build . --target test_xtensor-fftw --parallel 2 + + - name: Run tests + working-directory: build/test + run: ./test_xtensor-fftw.exe diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6f32960..0000000 --- a/.travis.yml +++ /dev/null @@ -1,185 +0,0 @@ -language: cpp -dist: trusty -env: -matrix: - include: - # code coverage: - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-7 - - libfftw3-dev - - lcov - env: COMPILER=gcc GCC=7 COVERAGE=ON - # normal tests: - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-4.9 - - libfftw3-dev - env: COMPILER=gcc GCC=4.9 - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-5 - - libfftw3-dev - env: COMPILER=gcc GCC=5 - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-6 - - libfftw3-dev - env: COMPILER=gcc GCC=6 - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-7 - - libfftw3-dev - env: COMPILER=gcc GCC=7 - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-7 - - libfftw3-dev - env: COMPILER=gcc GCC=7 DISABLE_EXCEPTION=1 - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-trusty-3.6 - packages: - - g++-4.9 - - clang-3.6 - - libfftw3-dev - env: COMPILER=clang CLANG=3.6 - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-trusty-3.9 - packages: - - g++-4.9 - - clang-3.9 - - libfftw3-dev - env: COMPILER=clang CLANG=3.9 - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-trusty-4.0 - packages: - - g++-4.9 - - clang-4.0 - - libfftw3-dev - env: COMPILER=clang CLANG=4.0 - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-trusty-5.0 - packages: - - g++-4.9 - - clang-5.0 - - libfftw3-dev - env: COMPILER=clang CLANG=5.0 - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-trusty-6.0 - packages: - - clang-6.0 - - libfftw3-dev - env: COMPILER=clang CLANG=6.0 - # osx 10.11 - - os: osx - osx_image: xcode8 - compiler: clang - # osx 10.12 - - os: osx - osx_image: xcode9.2 - compiler: clang - # osx 10.13 - - os: osx - osx_image: xcode10 - compiler: clang - -env: - global: - - MINCONDA_VERSION="latest" - - MINCONDA_LINUX="Linux-x86_64" - - MINCONDA_OSX="MacOSX-x86_64" -before_install: - - | - # Configure build variables - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - if [[ "$COMPILER" == "gcc" ]]; then - export CXX=g++-$GCC CC=gcc-$GCC; - fi - if [[ "$COMPILER" == "clang" ]]; then - export CXX=clang++-$CLANG CC=clang-$CLANG; - fi - elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - export CXX=clang++ CC=clang; - fi -install: - # Define the version of miniconda to download - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - MINCONDA_OS=$MINCONDA_LINUX; - elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - MINCONDA_OS=$MINCONDA_OSX; - fi - - wget --tries=10 "http://repo.continuum.io/miniconda/Miniconda3-$MINCONDA_VERSION-$MINCONDA_OS.sh" -O miniconda.sh; - - bash miniconda.sh -b -p $HOME/miniconda - - export PATH="$HOME/miniconda/bin:$PATH" - - hash -r - - conda update -yq conda - - conda install -y cmake xtl=>0.8.0 xtensor=>0.26.0 nlohmann_json=3.7.1 -c conda-forge - - | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - conda install -y fftw -c conda-forge - fi - # Testing - - mkdir $TRAVIS_BUILD_DIR/build - - cd $TRAVIS_BUILD_DIR/build - - cmake -DDOWNLOAD_GTEST=ON -DCOVERAGE=$COVERAGE .. - - make -j2 test_xtensor-fftw VERBOSE=1 - - if [[ "$COVERAGE" == "ON" ]]; then gem install coveralls-lcov; fi -script: - - cd test - - ./test_xtensor-fftw - -after_success: -- | - if [[ "${COVERAGE}" == "ON" ]]; then - cd ${TRAVIS_BUILD_DIR}/build - ls - ls test - lcov --directory . --capture --output-file coverage.info # capture coverage info - lcov --remove coverage.info 'tests/*' '/usr/*' 'miniconda/*' --output-file coverage.info # filter out system and test code and external includes - lcov --list coverage.info # debug before upload - coveralls-lcov coverage.info # uploads to coveralls - fi diff --git a/README.md b/README.md index c5c3394..14bd02b 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/xtensor-stack/xtensor-fftw/stable?filepath=notebooks%2Fintensely_edgy_cat.ipynb) [![Join the Gitter Chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/QuantStack/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Travis](https://travis-ci.org/xtensor-stack/xtensor-fftw.svg?branch=master)](https://travis-ci.org/xtensor-stack/xtensor-fftw) -[![Appveyor](https://ci.appveyor.com/api/projects/status/6h369haechmjeofj/branch/master?svg=true)](https://ci.appveyor.com/project/egpbos/xtensor-fftw-ivn9w/branch/master) +[![CI](https://github.com/xtensor-stack/xtensor-fftw/actions/workflows/main.yml/badge.svg)](https://github.com/xtensor-stack/xtensor-fftw/actions/workflows/main.yml) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/18861a283cf84b2e95886ba79c66e028)](https://www.codacy.com/app/egpbos/xtensor-fftw?utm_source=github.com&utm_medium=referral&utm_content=egpbos/xtensor-fftw&utm_campaign=Badge_Grade) [![Coverage Status](https://coveralls.io/repos/github/egpbos/xtensor-fftw/badge.svg)](https://coveralls.io/github/egpbos/xtensor-fftw) diff --git a/appveyor_install_fftw.ps1 b/appveyor_install_fftw.ps1 deleted file mode 100644 index 0796522..0000000 --- a/appveyor_install_fftw.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -mkdir $env:FFTW_ROOT -if ($env:Platform -eq "x86") { - $source = "ftp://ftp.fftw.org/pub/fftw/fftw-3.3.5-dll32.zip" - $destination = "C:\fftw-3.3.5.zip" - Invoke-WebRequest $source -OutFile $destination - echo "x86 fftw download complete" -} -if ($env:Platform -eq "x64") { - $source = "ftp://ftp.fftw.org/pub/fftw/fftw-3.3.5-dll64.zip" - $destination = "C:\fftw-3.3.5.zip" - Invoke-WebRequest $source -OutFile $destination - echo "x64 fftw download complete" -} - -7z e C:\fftw-3.3.5.zip -o"${env:FFTW_ROOT}" -cd $env:FFTW_ROOT - -if ($env:Platform -eq "x86") { - lib.exe /def:libfftw3-3.def - lib.exe /def:libfftw3f-3.def - lib.exe /def:libfftw3l-3.def -} -if ($env:Platform -eq "x64") { - lib.exe /machine:x64 /def:libfftw3-3.def - lib.exe /machine:x64 /def:libfftw3f-3.def - lib.exe /machine:x64 /def:libfftw3l-3.def -} - -$env:PATH="${env:FFTW_ROOT};${env:PATH}" From 98cd229858e7da33f5f844e1151c883a97cf01ea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Jun 2026 19:45:07 +0000 Subject: [PATCH 13/20] ci: harden GitHub Actions workflow --- .github/workflows/main.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2e82ebe..1bea188 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,6 +7,8 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.job }}-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read defaults: run: shell: bash -e -l {0} @@ -33,7 +35,7 @@ jobs: uses: actions/checkout@v4 - name: Set conda environment - uses: mamba-org/setup-micromamba@main + uses: mamba-org/setup-micromamba@v2 with: environment-name: xtensor-fftw init-shell: bash @@ -72,7 +74,7 @@ jobs: uses: actions/checkout@v4 - name: Set conda environment - uses: mamba-org/setup-micromamba@main + uses: mamba-org/setup-micromamba@v2 with: environment-name: xtensor-fftw init-shell: bash From d1a827ee6ea622ce8e3b952dcde357e37d36ed22 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Jun 2026 19:45:44 +0000 Subject: [PATCH 14/20] ci: pin micromamba action SHA --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1bea188..d9c1cec 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,7 +35,7 @@ jobs: uses: actions/checkout@v4 - name: Set conda environment - uses: mamba-org/setup-micromamba@v2 + uses: mamba-org/setup-micromamba@add3a49764cedee8ee24e82dfde87f5bc2914462 with: environment-name: xtensor-fftw init-shell: bash @@ -74,7 +74,7 @@ jobs: uses: actions/checkout@v4 - name: Set conda environment - uses: mamba-org/setup-micromamba@v2 + uses: mamba-org/setup-micromamba@add3a49764cedee8ee24e82dfde87f5bc2914462 with: environment-name: xtensor-fftw init-shell: bash From a720f34ad2bb7bcc3c7c1c050a3667db49882a81 Mon Sep 17 00:00:00 2001 From: "E. G. Patrick Bos" Date: Thu, 18 Jun 2026 22:07:13 +0200 Subject: [PATCH 15/20] fix AI mistakes + add macOS arm - Use newest action versions - Use an actually still alive macOS runner version - Add Windows to the single job matrix, because almost all steps are identical --- .github/workflows/main.yml | 65 ++++++++++++-------------------------- 1 file changed, 21 insertions(+), 44 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d9c1cec..8a8d0c7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,29 +13,39 @@ defaults: run: shell: bash -e -l {0} jobs: - unix: + build-and-test: runs-on: ${{ matrix.os }} name: ${{ matrix.name }} strategy: fail-fast: false matrix: include: - - os: ubuntu-24.04 + - os: ubuntu-latest name: Linux extra-cmake-args: "" - - os: ubuntu-24.04 + - os: ubuntu-latest name: Linux (no exceptions) extra-cmake-args: "-DDISABLE_EXCEPTIONS=ON" - - os: macos-13 - name: macOS + - os: macos-latest + name: macOS arm extra-cmake-args: "" + - os: macos-latest-intel + name: macOS intel + extra-cmake-args: "" + - os: windows-latest + name: Windows + extra-cmake-args: "-DCMAKE_BUILD_TYPE=Release" steps: + - name: Setup MSVC + if: matrix.os == 'windows-latest' + uses: ilammy/msvc-dev-cmd@v1 + - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set conda environment - uses: mamba-org/setup-micromamba@add3a49764cedee8ee24e82dfde87f5bc2914462 + uses: mamba-org/setup-micromamba@v3 with: environment-name: xtensor-fftw init-shell: bash @@ -59,45 +69,12 @@ jobs: working-directory: build run: cmake --build . --target test_xtensor-fftw --parallel 2 - - name: Run tests + - name: Run tests (Unix) + if: matrix.os != 'windows-latest' working-directory: build/test run: ./test_xtensor-fftw - windows: - runs-on: windows-latest - - steps: - - name: Setup MSVC - uses: ilammy/msvc-dev-cmd@v1 - - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set conda environment - uses: mamba-org/setup-micromamba@add3a49764cedee8ee24e82dfde87f5bc2914462 - with: - environment-name: xtensor-fftw - init-shell: bash - cache-downloads: true - condarc: | - channels: - - conda-forge - channel_priority: strict - create-args: >- - cmake - ninja - xtl - xtensor - fftw - gtest - - - name: Configure using CMake - run: cmake -G Ninja -Bbuild -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DCMAKE_PREFIX_PATH=$CONDA_PREFIX - - - name: Build - working-directory: build - run: cmake --build . --target test_xtensor-fftw --parallel 2 - - - name: Run tests + - name: Run tests (Windows) + if: matrix.os == 'windows-latest' working-directory: build/test run: ./test_xtensor-fftw.exe From e1c703d91c96188722c40cdc5eb549c19b83ba92 Mon Sep 17 00:00:00 2001 From: "E. G. Patrick Bos" Date: Thu, 18 Jun 2026 22:23:53 +0200 Subject: [PATCH 16/20] try GHA build on macos-26-intel The macos-latest-intel runner takes ages to queue. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8a8d0c7..601d82e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,7 +29,7 @@ jobs: - os: macos-latest name: macOS arm extra-cmake-args: "" - - os: macos-latest-intel + - os: macos-26-intel name: macOS intel extra-cmake-args: "" - os: windows-latest From 83bb76ba53fe3a982d9bdb57a07568a6ab061eb4 Mon Sep 17 00:00:00 2001 From: Johan Mabille Date: Fri, 26 Jun 2026 09:42:03 +0200 Subject: [PATCH 17/20] Do not require long double lib on OSX arm64 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fc47f5..872265f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,7 +65,7 @@ else() set(REQUIRE_DOUBLE_LIB "") endif() -if(FFTW_USE_LONG_DOUBLE AND NOT MSVC) +if(FFTW_USE_LONG_DOUBLE AND NOT MSVC AND NOT (APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")) add_definitions(-DXTENSOR_FFTW_USE_LONG_DOUBLE) set(REQUIRE_LONG_DOUBLE_LIB "LONGDOUBLE_LIB") else() From 1d76d969c328d888901771d75b4a0996c5cdaab1 Mon Sep 17 00:00:00 2001 From: Johan Mabille Date: Fri, 26 Jun 2026 09:55:09 +0200 Subject: [PATCH 18/20] Added bounds to xtensor dependency --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 601d82e..de64d40 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -58,7 +58,7 @@ jobs: cmake ninja xtl - xtensor + xtensor>=0.27,<0.28 fftw gtest From efe99cacfe3bac265b57c77627cf5c989245ea7d Mon Sep 17 00:00:00 2001 From: Johan Mabille Date: Fri, 26 Jun 2026 10:07:26 +0200 Subject: [PATCH 19/20] Requires xtnsor 0.27 in CMakeLists.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 872265f..7908186 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,7 +111,7 @@ else() endif() # .. xtensor -set(xtensor_REQUIRED_VERSION 0.26.0) +set(xtensor_REQUIRED_VERSION 0.27.0) if(TARGET xtensor) set(xtensor_VERSION ${XTENSOR_VERSION_MAJOR}.${XTENSOR_VERSION_MINOR}.${XTENSOR_VERSION_PATCH}) # Note: This is not SEMVER compatible comparison From e6c4636b4e92cf0ce286332af78c8d01718d61f9 Mon Sep 17 00:00:00 2001 From: Johan Mabille Date: Fri, 26 Jun 2026 10:24:06 +0200 Subject: [PATCH 20/20] Release 0.4.0 --- README.md | 7 +------ include/xtensor-fftw/xtensor-fftw_config.hpp | 4 ++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 14bd02b..4b86bb1 100644 --- a/README.md +++ b/README.md @@ -44,12 +44,7 @@ make install * [xtensor](https://github.com/xtensor-stack/xtensor) * [xtl](https://github.com/xtensor-stack/xtl) * [FFTW](http://www.fftw.org/) version 3 -* A compiler supporting C++14 - -| `xtensor-fftw` | `xtensor` | `xtl` | `fftw` | -|----------------|------------------|---------|---------| -| master | >=0.20.9,<0.22 | ^0.6.9 | ^3.3.8 | -| 0.2.6 | >=0.20.9,<0.22 | ^0.6.9 | ^3.3.8 | +* A compiler supporting C++20 ## Usage diff --git a/include/xtensor-fftw/xtensor-fftw_config.hpp b/include/xtensor-fftw/xtensor-fftw_config.hpp index 02081c2..fd3bb71 100644 --- a/include/xtensor-fftw/xtensor-fftw_config.hpp +++ b/include/xtensor-fftw/xtensor-fftw_config.hpp @@ -10,8 +10,8 @@ #define XTENSOR_FFTW_CONFIG_HPP #define XTENSOR_FFTW_VERSION_MAJOR 0 -#define XTENSOR_FFTW_VERSION_MINOR 2 -#define XTENSOR_FFTW_VERSION_PATCH 6 +#define XTENSOR_FFTW_VERSION_MINOR 4 +#define XTENSOR_FFTW_VERSION_PATCH 0 // Define if the library is going to be using exceptions. #if (!defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND))