added simple fft

This commit is contained in:
toni
2018-01-24 11:44:08 +01:00
parent 3591106b38
commit b207e5e0f2
10 changed files with 1657 additions and 1 deletions

View File

@@ -0,0 +1,564 @@
#ifndef __SIMPLE_FFT__CHECK_FFT_HPP__
#define __SIMPLE_FFT__CHECK_FFT_HPP__
#include "fft_settings.h"
#include "error_handling.hpp"
#include "copy_array.hpp"
#include <cstddef>
#include <cmath>
#include <numeric>
using std::size_t;
namespace simple_fft {
namespace check_fft_private {
enum CheckMode
{
CHECK_FFT_PARSEVAL,
CHECK_FFT_ENERGY,
CHECK_FFT_EQUALITY
};
template <class TArray1D, class TComplexArray1D>
void getMaxAbsoluteAndRelativeErrorNorms(const TArray1D & array1,
const TComplexArray1D & array2, const size_t size,
real_type & max_absolute_error_norm,
real_type & max_relative_error_norm)
{
using std::abs;
real_type current_error;
// NOTE: no parallelization here, it is a completely sequential loop!
for(size_t i = 0; i < size; ++i) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
current_error = abs(array1[i] - array2[i]);
#else
current_error = abs(array1(i) - array2(i));
#endif
if (current_error > max_absolute_error_norm) {
max_absolute_error_norm = current_error;
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
if (abs(array1[i]) > abs(array2[i])) {
max_relative_error_norm = (abs(array1[i]) > 1e-20
? max_absolute_error_norm / abs(array1[i])
: 0.0);
}
else {
max_relative_error_norm = (abs(array2[i]) > 1e-20
? max_absolute_error_norm / abs(array2[i])
: 0.0);
}
#else
if (abs(array1(i)) > abs(array2(i))) {
max_relative_error_norm = (abs(array1(i)) > 1e-20
? max_absolute_error_norm / abs(array1(i))
: 0.0);
}
else {
max_relative_error_norm = (abs(array2(i)) > 1e-20
? max_absolute_error_norm / abs(array2(i))
: 0.0);
}
#endif
}
}
}
template <class TArray2D, class TComplexArray2D>
void getMaxAbsoluteAndRelativeErrorNorms(const TArray2D & array1,
const TComplexArray2D & array2,
const size_t size1, const size_t size2,
real_type & max_absolute_error_norm,
real_type & max_relative_error_norm)
{
using std::abs;
real_type current_error;
// NOTE: no parallelization here, it is a completely sequential loop!
for(int i = 0; i < static_cast<int>(size1); ++i) {
for(int j = 0; j < static_cast<int>(size2); ++j) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
current_error = abs(array1[i][j] - array2[i][j]);
#else
current_error = abs(array1(i,j) - array2(i,j));
#endif
if (current_error > max_absolute_error_norm) {
max_absolute_error_norm = current_error;
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
if (abs(array1[i][j]) > abs(array2[i][j])) {
max_relative_error_norm = (abs(array1[i][j]) > 1e-20
? max_absolute_error_norm / abs(array1[i][j])
: 0.0);
}
else {
max_relative_error_norm = (abs(array2[i][j]) > 1e-20
? max_absolute_error_norm / abs(array2[i][j])
: 0.0);
}
#else
if (abs(array1(i,j)) > abs(array2(i,j))) {
max_relative_error_norm = (abs(array1(i,j)) > 1e-20
? max_absolute_error_norm / abs(array1(i,j))
: 0.0);
}
else {
max_relative_error_norm = (abs(array2(i,j)) > 1e-20
? max_absolute_error_norm / abs(array2(i,j))
: 0.0);
}
#endif
}
}
}
}
template <class TArray3D, class TComplexArray3D>
void getMaxAbsoluteAndRelativeErrorNorms(const TArray3D & array1, const TComplexArray3D & array2,
const size_t size1, const size_t size2,
const size_t size3, real_type & max_absolute_error_norm,
real_type & max_relative_error_norm)
{
using std::abs;
real_type current_error;
// NOTE: no parallelization here, it is a completely sequential loop!
for(int i = 0; i < static_cast<int>(size1); ++i) {
for(int j = 0; j < static_cast<int>(size2); ++j) {
for(int k = 0; k < static_cast<int>(size3); ++k) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
current_error = abs(array1[i][j][k] - array2[i][j][k]);
#else
current_error = abs(array1(i,j,k) - array2(i,j,k));
#endif
if (current_error > max_absolute_error_norm) {
max_absolute_error_norm = current_error;
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
if (abs(array1[i][j][k]) > abs(array2[i][j][k])) {
max_relative_error_norm = (abs(array1[i][j][k]) > 1e-20
? max_absolute_error_norm / abs(array1[i][j][k])
: 0.0);
}
else {
max_relative_error_norm = (abs(array2[i][j][k]) > 1e-20
? max_absolute_error_norm / abs(array2[i][j][k])
: 0.0);
}
#else
if (abs(array1(i,j,k)) > abs(array2(i,j,k))) {
max_relative_error_norm = (abs(array1(i,j,k)) > 1e-20
? max_absolute_error_norm / abs(array1(i,j,k))
: 0.0);
}
else {
max_relative_error_norm = (abs(array2(i,j,k)) > 1e-20
? max_absolute_error_norm / abs(array2(i,j,k))
: 0.0);
}
#endif
}
}
}
}
}
template <class TArray1D>
real_type squareAbsAccumulate(const TArray1D & array, const size_t size,
const real_type init)
{
int size_signed = static_cast<int>(size);
real_type sum = init;
using std::abs;
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for reduction(+:sum)
#endif
#endif
for(int i = 0; i < size_signed; ++i) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
sum += abs(array[i] * array[i]);
#else
sum += abs(array(i) * array(i));
#endif
}
return sum;
}
template <class TArray2D>
real_type squareAbsAccumulate(const TArray2D & array, const size_t size1,
const size_t size2, const real_type init)
{
int size1_signed = static_cast<int>(size1);
int size2_signed = static_cast<int>(size2);
real_type sum = init;
using std::abs;
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for reduction(+:sum)
#endif
#endif
for(int i = 0; i < size1_signed; ++i) {
for(int j = 0; j < size2_signed; ++j) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
sum += abs(array[i][j] * array[i][j]);
#else
sum += abs(array(i,j) * array(i,j));
#endif
}
}
return sum;
}
template <class TArray3D>
real_type squareAbsAccumulate(const TArray3D & array, const size_t size1,
const size_t size2, const size_t size3,
const real_type init)
{
int size1_signed = static_cast<int>(size1);
int size2_signed = static_cast<int>(size2);
int size3_signed = static_cast<int>(size3);
real_type sum = init;
using std::abs;
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for reduction(+:sum)
#endif
#endif
for(int i = 0; i < size1_signed; ++i) {
for(int j = 0; j < size2_signed; ++j) {
for(int k = 0; k < size3_signed; ++k) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
sum += abs(array[i][j][k] * array[i][j][k]);
#else
sum += abs(array(i,j,k) * array(i,j,k));
#endif
}
}
}
return sum;
}
// Generic template for CCheckFFT struct followed by its explicit specializations
// for certain numbers of dimensions. TArray can be either of real or complex type.
// The technique is similar to the one applied for CFFT struct.
template <class TArray, class TComplexArray, int NumDims>
struct CCheckFFT
{};
template <class TArray1D, class TComplexArray1D>
struct CCheckFFT<TArray1D,TComplexArray1D,1>
{
static bool check_fft(const TArray1D & data_before,
const TComplexArray1D & data_after,
const size_t size, const real_type relative_tolerance,
real_type & discrepancy, const CheckMode check_mode,
const char *& error_description)
{
using namespace error_handling;
if(0 == size) {
GetErrorDescription(EC_NUM_OF_ELEMS_IS_ZERO, error_description);
return false;
}
if ( (CHECK_FFT_PARSEVAL != check_mode) &&
(CHECK_FFT_ENERGY != check_mode) &&
(CHECK_FFT_EQUALITY != check_mode) )
{
GetErrorDescription(EC_WRONG_CHECK_FFT_MODE, error_description);
return false;
}
if (CHECK_FFT_EQUALITY != check_mode)
{
real_type sum_before = squareAbsAccumulate<TArray1D>(data_before, size, 0.0);
real_type sum_after = squareAbsAccumulate<TComplexArray1D>(data_after, size, 0.0);
if (CHECK_FFT_PARSEVAL == check_mode) {
sum_after /= size;
}
using std::abs;
discrepancy = abs(sum_before - sum_after);
if (discrepancy / ((sum_before < 1e-20) ? (sum_before + 1e-20) : sum_before) > relative_tolerance) {
GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
return false;
}
else {
return true;
}
}
else {
real_type relative_error;
getMaxAbsoluteAndRelativeErrorNorms(data_before, data_after, size,
discrepancy, relative_error);
if (relative_error < relative_tolerance) {
return true;
}
else {
GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
return false;
}
}
}
};
template <class TArray2D, class TComplexArray2D>
struct CCheckFFT<TArray2D,TComplexArray2D,2>
{
static bool check_fft(const TArray2D & data_before,
const TComplexArray2D & data_after,
const size_t size1, const size_t size2,
const real_type relative_tolerance, real_type & discrepancy,
const CheckMode check_mode, const char *& error_description)
{
using namespace error_handling;
if( (0 == size1) || (0 == size2) ) {
GetErrorDescription(EC_NUM_OF_ELEMS_IS_ZERO, error_description);
return false;
}
if ( (CHECK_FFT_PARSEVAL != check_mode) &&
(CHECK_FFT_ENERGY != check_mode) &&
(CHECK_FFT_EQUALITY != check_mode) )
{
GetErrorDescription(EC_WRONG_CHECK_FFT_MODE, error_description);
return false;
}
if (CHECK_FFT_EQUALITY != check_mode)
{
real_type sum_before = squareAbsAccumulate<TArray2D>(data_before, size1, size2, 0.0);
real_type sum_after = squareAbsAccumulate<TComplexArray2D>(data_after, size1, size2, 0.0);
if (CHECK_FFT_PARSEVAL == check_mode) {
sum_after /= size1 * size2;
}
using std::abs;
discrepancy = abs(sum_before - sum_after);
if (discrepancy / ((sum_before < 1e-20) ? (sum_before + 1e-20) : sum_before) > relative_tolerance) {
GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
return false;
}
else {
return true;
}
}
else {
real_type relative_error;
getMaxAbsoluteAndRelativeErrorNorms(data_before, data_after, size1,
size2, discrepancy, relative_error);
if (relative_error < relative_tolerance) {
return true;
}
else {
GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
return false;
}
}
}
};
template <class TArray3D, class TComplexArray3D>
struct CCheckFFT<TArray3D,TComplexArray3D,3>
{
static bool check_fft(const TArray3D & data_before,
const TComplexArray3D & data_after,
const size_t size1, const size_t size2, const size_t size3,
const real_type relative_tolerance, real_type & discrepancy,
const CheckMode check_mode, const char *& error_description)
{
using namespace error_handling;
if( (0 == size1) || (0 == size2) || (0 == size3) ) {
GetErrorDescription(EC_NUM_OF_ELEMS_IS_ZERO, error_description);
return false;
}
if ( (CHECK_FFT_PARSEVAL != check_mode) &&
(CHECK_FFT_ENERGY != check_mode) &&
(CHECK_FFT_EQUALITY != check_mode) )
{
GetErrorDescription(EC_WRONG_CHECK_FFT_MODE, error_description);
return false;
}
if (CHECK_FFT_EQUALITY != check_mode)
{
real_type sum_before = squareAbsAccumulate<TArray3D>(data_before, size1, size2, size3, 0.0);
real_type sum_after = squareAbsAccumulate<TComplexArray3D>(data_after, size1, size2, size3, 0.0);
if (CHECK_FFT_PARSEVAL == check_mode) {
sum_after /= size1 * size2 * size3;
}
using std::abs;
discrepancy = abs(sum_before - sum_after);
if (discrepancy / ((sum_before < 1e-20) ? (sum_before + 1e-20) : sum_before) > relative_tolerance) {
GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
return false;
}
else {
return true;
}
}
else {
real_type relative_error;
getMaxAbsoluteAndRelativeErrorNorms(data_before, data_after, size1,
size2, size3, discrepancy, relative_error);
if (relative_error < relative_tolerance) {
return true;
}
else {
GetErrorDescription(EC_RELATIVE_ERROR_TOO_LARGE, error_description);
return false;
}
}
}
};
} // namespace check_fft_private
namespace check_fft {
template <class TArray1D, class TComplexArray1D>
bool checkParsevalTheorem(const TArray1D & data_before_FFT,
const TComplexArray1D & data_after_FFT,
const size_t size, const real_type relative_tolerance,
real_type & discrepancy, const char *& error_description)
{
return check_fft_private::CCheckFFT<TArray1D,TComplexArray1D,1>::check_fft(data_before_FFT,
data_after_FFT, size, relative_tolerance,
discrepancy, check_fft_private::CHECK_FFT_PARSEVAL,
error_description);
}
template <class TArray2D, class TComplexArray2D>
bool checkParsevalTheorem(const TArray2D & data_before_FFT,
const TComplexArray2D & data_after_FFT,
const size_t size1, const size_t size2,
const real_type relative_tolerance,
real_type & discrepancy, const char *& error_description)
{
return check_fft_private::CCheckFFT<TArray2D,TComplexArray2D,2>::check_fft(data_before_FFT,
data_after_FFT, size1, size2, relative_tolerance,
discrepancy, check_fft_private::CHECK_FFT_PARSEVAL,
error_description);
}
template <class TArray3D, class TComplexArray3D>
bool checkParsevalTheorem(const TArray3D & data_before_FFT,
const TComplexArray3D & data_after_FFT,
const size_t size1, const size_t size2, const size_t size3,
const real_type relative_tolerance, real_type & discrepancy,
const char *& error_description)
{
return check_fft_private::CCheckFFT<TArray3D,TComplexArray3D,3>::check_fft(data_before_FFT,
data_after_FFT, size1, size2, size3,
relative_tolerance, discrepancy,
check_fft_private::CHECK_FFT_PARSEVAL,
error_description);
}
template <class TArray1D, class TComplexArray1D>
bool checkEnergyConservation(const TArray1D & data_before_FFT,
const TComplexArray1D & data_after_FFT_and_IFFT,
const size_t size, const real_type relative_tolerance,
real_type & discrepancy, const char *& error_description)
{
return check_fft_private::CCheckFFT<TArray1D,TComplexArray1D,1>::check_fft(data_before_FFT,
data_after_FFT_and_IFFT, size, relative_tolerance,
discrepancy, check_fft_private::CHECK_FFT_ENERGY,
error_description);
}
template <class TArray2D, class TComplexArray2D>
bool checkEnergyConservation(const TArray2D & data_before_FFT,
const TComplexArray2D & data_after_FFT_and_IFFT,
const size_t size1, const size_t size2,
const real_type relative_tolerance,
real_type & discrepancy, const char *& error_description)
{
return check_fft_private::CCheckFFT<TArray2D,TComplexArray2D,2>::check_fft(data_before_FFT,
data_after_FFT_and_IFFT, size1, size2,
relative_tolerance, discrepancy,
check_fft_private::CHECK_FFT_ENERGY,
error_description);
}
template <class TArray3D, class TComplexArray3D>
bool checkEnergyConservation(const TArray3D & data_before_FFT,
const TComplexArray3D & data_after_FFT_and_IFFT,
const size_t size1, const size_t size2, const size_t size3,
const real_type relative_tolerance, real_type & discrepancy,
const char *& error_description)
{
return check_fft_private::CCheckFFT<TArray3D,TComplexArray3D,3>::check_fft(data_before_FFT,
data_after_FFT_and_IFFT, size1, size2,
size3, relative_tolerance, discrepancy,
check_fft_private::CHECK_FFT_ENERGY,
error_description);
}
template <class TArray1D, class TComplexArray1D>
bool checkEquality(const TArray1D & data_before_FFT,
const TComplexArray1D & data_after_FFT_and_IFFT,
const size_t size, const real_type relative_tolerance,
real_type & discrepancy, const char *& error_description)
{
return check_fft_private::CCheckFFT<TArray1D,TComplexArray1D,1>::check_fft(data_before_FFT,
data_after_FFT_and_IFFT, size, relative_tolerance,
discrepancy, check_fft_private::CHECK_FFT_EQUALITY,
error_description);
}
template <class TArray2D, class TComplexArray2D>
bool checkEquality(const TArray2D & data_before_FFT,
const TComplexArray2D & data_after_FFT_and_IFFT, const size_t size1,
const size_t size2, const real_type relative_tolerance,
real_type & discrepancy, const char *& error_description)
{
return check_fft_private::CCheckFFT<TArray2D,TComplexArray2D,2>::check_fft(data_before_FFT,
data_after_FFT_and_IFFT, size1, size2,
relative_tolerance, discrepancy,
check_fft_private::CHECK_FFT_EQUALITY,
error_description);
}
template <class TArray3D, class TComplexArray3D>
bool checkEquality(const TArray3D & data_before_FFT,
const TComplexArray3D & data_after_FFT_and_IFFT, const size_t size1,
const size_t size2, const size_t size3, const real_type relative_tolerance,
real_type & discrepancy, const char *& error_description)
{
return check_fft_private::CCheckFFT<TArray3D,TComplexArray3D,3>::check_fft(data_before_FFT,
data_after_FFT_and_IFFT, size1, size2,
size3, relative_tolerance, discrepancy,
check_fft_private::CHECK_FFT_EQUALITY,
error_description);
}
} // namespace check_fft
} // namespace simple_fft
#endif // __SIMPLE_FFT__CHECK_FFT_HPP__

View File

@@ -0,0 +1,166 @@
#ifndef __SIMPLE_FFT__COPY_ARRAY_HPP
#define __SIMPLE_FFT__COPY_ARRAY_HPP
#include "fft_settings.h"
#include "error_handling.hpp"
#include <cstddef>
using std::size_t;
namespace simple_fft {
namespace copy_array {
template <class TComplexArray1D>
void copyArray(const TComplexArray1D & data_from, TComplexArray1D & data_to,
const size_t size)
{
int size_signed = static_cast<int>(size);
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int i = 0; i < size_signed; ++i) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
data_to[i] = data_from[i];
#else
data_to(i) = data_from(i);
#endif
}
}
template <class TComplexArray1D, class TRealArray1D>
void copyArray(const TRealArray1D & data_from, TComplexArray1D & data_to,
const size_t size)
{
int size_signed = static_cast<int>(size);
// NOTE: user's complex type should have constructor like
// "complex(real, imag)", where each of real and imag has
// real type.
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int i = 0; i < size_signed; ++i) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
data_to[i] = complex_type(data_from[i], 0.0);
#else
data_to(i) = complex_type(data_from(i), 0.0);
#endif
}
}
template <class TComplexArray2D>
void copyArray(const TComplexArray2D & data_from, TComplexArray2D & data_to,
const size_t size1, const size_t size2)
{
int size1_signed = static_cast<int>(size1);
int size2_signed = static_cast<int>(size2);
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int i = 0; i < size1_signed; ++i) {
for(int j = 0; j < size2_signed; ++j) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
data_to[i][j] = data_from[i][j];
#else
data_to(i,j) = data_from(i,j);
#endif
}
}
}
template <class TComplexArray2D, class TRealArray2D>
void copyArray(const TRealArray2D & data_from, TComplexArray2D & data_to,
const size_t size1, const size_t size2)
{
int size1_signed = static_cast<int>(size1);
int size2_signed = static_cast<int>(size2);
// NOTE: user's complex type should have constructor like
// "complex(real, imag)", where each of real and imag has
// real type.
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int i = 0; i < size1_signed; ++i) {
for(int j = 0; j < size2_signed; ++j) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
data_to[i][j] = complex_type(data_from[i][j], 0.0);
#else
data_to(i,j) = complex_type(data_from(i,j), 0.0);
#endif
}
}
}
template <class TComplexArray3D>
void copyArray(const TComplexArray3D & data_from, TComplexArray3D & data_to,
const size_t size1, const size_t size2, const size_t size3)
{
int size1_signed = static_cast<int>(size1);
int size2_signed = static_cast<int>(size2);
int size3_signed = static_cast<int>(size3);
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int i = 0; i < size1_signed; ++i) {
for(int j = 0; j < size2_signed; ++j) {
for(int k = 0; k < size3_signed; ++k) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
data_to[i][j][k] = data_from[i][j][k];
#else
data_to(i,j,k) = data_from(i,j,k);
#endif
}
}
}
}
template <class TComplexArray3D, class TRealArray3D>
void copyArray(const TRealArray3D & data_from, TComplexArray3D & data_to,
const size_t size1, const size_t size2, const size_t size3)
{
int size1_signed = static_cast<int>(size1);
int size2_signed = static_cast<int>(size2);
int size3_signed = static_cast<int>(size3);
// NOTE: user's complex type should have constructor like
// "complex(real, imag)", where each of real and imag has
// real type.
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int i = 0; i < size1_signed; ++i) {
for(int j = 0; j < size2_signed; ++j) {
for(int k = 0; k < size3_signed; ++k) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
data_to[i][j][k] = complex_type(data_from[i][j][k], 0.0);
#else
data_to(i,j,k) = complex_type(data_from(i,j,k), 0.0);
#endif
}
}
}
}
} // namespace copy_array
} // namespace simple_fft
#endif // __SIMPLE_FFT__COPY_ARRAY_HPP

View File

@@ -0,0 +1,57 @@
#ifndef __SIMPLE_FFT__ERROR_HANDLING_HPP
#define __SIMPLE_FFT__ERROR_HANDLING_HPP
namespace simple_fft {
namespace error_handling {
enum EC_SimpleFFT
{
EC_SUCCESS = 0,
EC_UNSUPPORTED_DIMENSIONALITY,
EC_WRONG_FFT_DIRECTION,
EC_ONE_OF_DIMS_ISNT_POWER_OF_TWO,
EC_NUM_OF_ELEMS_IS_ZERO,
EC_WRONG_CHECK_FFT_MODE,
EC_RELATIVE_ERROR_TOO_LARGE
};
inline void GetErrorDescription(const EC_SimpleFFT error_code,
const char *& error_description)
{
switch(error_code)
{
case EC_SUCCESS:
error_description = "Calculation was successful!";
break;
case EC_UNSUPPORTED_DIMENSIONALITY:
error_description = "Unsupported dimensionality: currently only 1D, 2D "
"and 3D arrays are supported";
break;
case EC_WRONG_FFT_DIRECTION:
error_description = "Wrong direction for FFT was specified";
break;
case EC_ONE_OF_DIMS_ISNT_POWER_OF_TWO:
error_description = "Unsupported dimensionality: one of dimensions is not "
"a power of 2";
break;
case EC_NUM_OF_ELEMS_IS_ZERO:
error_description = "Number of elements for FFT or IFFT is zero!";
break;
case EC_WRONG_CHECK_FFT_MODE:
error_description = "Wrong check FFT mode was specified (should be either "
"Parseval theorem or energy conservation check";
break;
case EC_RELATIVE_ERROR_TOO_LARGE:
error_description = "Relative error returned by FFT test exceeds specified "
"relative tolerance";
break;
default:
error_description = "Unknown error";
break;
}
}
} // namespace error_handling
} // namespace simple_fft
#endif // __SIMPLE_FFT__ERROR_HANDLING_HPP

100
lib/simple_fft/fft.h Normal file
View File

@@ -0,0 +1,100 @@
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Dmitry Ivanov <dm.vl.ivanov@gmail.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
#ifndef __SIMPLE_FFT__FFT_H__
#define __SIMPLE_FFT__FFT_H__
#include <cstddef>
using std::size_t;
/// The public API
namespace simple_fft {
/// FFT and IFFT functions
// in-place, complex, forward
template <class TComplexArray1D>
bool FFT(TComplexArray1D & data, const size_t size, const char *& error_description);
template <class TComplexArray2D>
bool FFT(TComplexArray2D & data, const size_t size1, const size_t size2,
const char *& error_description);
template <class TComplexArray3D>
bool FFT(TComplexArray3D & data, const size_t size1, const size_t size2, const size_t size3,
const char *& error_description);
// in-place, complex, inverse
template <class TComplexArray1D>
bool IFFT(TComplexArray1D & data, const size_t size, const char *& error_description);
template <class TComplexArray2D>
bool IFFT(TComplexArray2D & data, const size_t size1, const size_t size2,
const char *& error_description);
template <class TComplexArray3D>
bool IFFT(TComplexArray3D & data, const size_t size1, const size_t size2, const size_t size3,
const char *& error_description);
// not-in-place, complex, forward
template <class TComplexArray1D>
bool FFT(const TComplexArray1D & data_in, TComplexArray1D & data_out,
const size_t size, const char *& error_description);
template <class TComplexArray2D>
bool FFT(const TComplexArray2D & data_in, TComplexArray2D & data_out,
const size_t size1, const size_t size2, const char *& error_description);
template <class TComplexArray3D>
bool FFT(const TComplexArray3D & data_in, TComplexArray3D & data_out,
const size_t size1, const size_t size2, const size_t size3,
const char *& error_description);
// not-in-place, complex, inverse
template <class TComplexArray1D>
bool IFFT(const TComplexArray1D & data_in, TComplexArray1D & data_out,
const size_t size, const char *& error_description);
template <class TComplexArray2D>
bool IFFT(const TComplexArray2D & data_in, TComplexArray2D & data_out,
const size_t size1, const size_t size2, const char *& error_description);
template <class TComplexArray3D>
bool IFFT(const TComplexArray3D & data_in, TComplexArray3D & data_out,
const size_t size1, const size_t size2, const size_t size3,
const char *& error_description);
// not-in-place, real, forward
template <class TRealArray1D, class TComplexArray1D>
bool FFT(const TRealArray1D & data_in, TComplexArray1D & data_out,
const size_t size, const char *& error_description);
template <class TRealArray2D, class TComplexArray2D>
bool FFT(const TRealArray2D & data_in, TComplexArray2D & data_out,
const size_t size1, const size_t size2, const char *& error_description);
template <class TRealArray3D, class TComplexArray3D>
bool FFT(const TRealArray3D & data_in, TComplexArray3D & data_out,
const size_t size1, const size_t size2, const size_t size3,
const char *& error_description);
// NOTE: There is no inverse transform from complex spectrum to real signal
// because round-off errors during computation of inverse FFT lead to the appearance
// of signal imaginary components even though they are small by absolute value.
// These can be ignored but the author of this file thinks adding such an function
// would be wrong methodogically: looking at complex result, you can estimate
// the value of spurious imaginary part. Otherwise you may never know that IFFT
// provides too large imaginary values due to too small grid size, for example.
} // namespace simple_fft
#endif // __SIMPLE_FFT__FFT_H__
#include "fft.hpp"

155
lib/simple_fft/fft.hpp Normal file
View File

@@ -0,0 +1,155 @@
#ifndef __SIMPLE_FFT__FFT_HPP__
#define __SIMPLE_FFT__FFT_HPP__
#include "copy_array.hpp"
#include "fft_impl.hpp"
namespace simple_fft {
// in-place, complex, forward
template <class TComplexArray1D>
bool FFT(TComplexArray1D & data, const size_t size, const char *& error_description)
{
return impl::CFFT<TComplexArray1D,1>::FFT_inplace(data, size, impl::FFT_FORWARD,
error_description);
}
template <class TComplexArray2D>
bool FFT(TComplexArray2D & data, const size_t size1, const size_t size2,
const char *& error_description)
{
return impl::CFFT<TComplexArray2D,2>::FFT_inplace(data, size1, size2, impl::FFT_FORWARD,
error_description);
}
template <class TComplexArray3D>
bool FFT(TComplexArray3D & data, const size_t size1, const size_t size2, const size_t size3,
const char *& error_description)
{
return impl::CFFT<TComplexArray3D,3>::FFT_inplace(data, size1, size2, size3,
impl::FFT_FORWARD,
error_description);
}
// in-place, complex, inverse
template <class TComplexArray1D>
bool IFFT(TComplexArray1D & data, const size_t size, const char *& error_description)
{
return impl::CFFT<TComplexArray1D,1>::FFT_inplace(data, size, impl::FFT_BACKWARD,
error_description);
}
template <class TComplexArray2D>
bool IFFT(TComplexArray2D & data, const size_t size1, const size_t size2,
const char *& error_description)
{
return impl::CFFT<TComplexArray2D,2>::FFT_inplace(data, size1, size2, impl::FFT_BACKWARD,
error_description);
}
template <class TComplexArray3D>
bool IFFT(TComplexArray3D & data, const size_t size1, const size_t size2, const size_t size3,
const char *& error_description)
{
return impl::CFFT<TComplexArray3D,3>::FFT_inplace(data, size1, size2, size3,
impl::FFT_BACKWARD,
error_description);
}
// not-in-place, complex, forward
template <class TComplexArray1D>
bool FFT(const TComplexArray1D & data_in, TComplexArray1D & data_out,
const size_t size, const char *& error_description)
{
copy_array::copyArray(data_in, data_out, size);
return impl::CFFT<TComplexArray1D,1>::FFT_inplace(data_out, size, impl::FFT_FORWARD,
error_description);
}
template <class TComplexArray2D>
bool FFT(const TComplexArray2D & data_in, TComplexArray2D & data_out,
const size_t size1, const size_t size2, const char *& error_description)
{
copy_array::copyArray(data_in, data_out, size1, size2);
return impl::CFFT<TComplexArray2D,2>::FFT_inplace(data_out, size1, size2,
impl::FFT_FORWARD,
error_description);
}
template <class TComplexArray3D>
bool FFT(const TComplexArray3D & data_in, TComplexArray3D & data_out,
const size_t size1, const size_t size2, const size_t size3,
const char *& error_description)
{
copy_array::copyArray(data_in, data_out, size1, size2, size3);
return impl::CFFT<TComplexArray3D,3>::FFT_inplace(data_out, size1, size2, size3,
impl::FFT_FORWARD,
error_description);
}
// not-in-place, complex, inverse
template <class TComplexArray1D>
bool IFFT(const TComplexArray1D & data_in, TComplexArray1D & data_out,
const size_t size, const char *& error_description)
{
copy_array::copyArray(data_in, data_out, size);
return impl::CFFT<TComplexArray1D,1>::FFT_inplace(data_out, size, impl::FFT_BACKWARD,
error_description);
}
template <class TComplexArray2D>
bool IFFT(const TComplexArray2D & data_in, TComplexArray2D & data_out,
const size_t size1, const size_t size2, const char *& error_description)
{
copy_array::copyArray(data_in, data_out, size1, size2);
return impl::CFFT<TComplexArray2D,2>::FFT_inplace(data_out, size1, size2,
impl::FFT_BACKWARD,
error_description);
}
template <class TComplexArray3D>
bool IFFT(const TComplexArray3D & data_in, TComplexArray3D & data_out,
const size_t size1, const size_t size2, const size_t size3,
const char *& error_description)
{
copy_array::copyArray(data_in, data_out, size1, size2, size3);
return impl::CFFT<TComplexArray3D,3>::FFT_inplace(data_out, size1, size2, size3,
impl::FFT_BACKWARD,
error_description);
}
// not-in-place, real, forward
template <class TRealArray1D, class TComplexArray1D>
bool FFT(const TRealArray1D & data_in, TComplexArray1D & data_out,
const size_t size, const char *& error_description)
{
copy_array::copyArray(data_in, data_out, size);
return impl::CFFT<TComplexArray1D,1>::FFT_inplace(data_out, size,
impl::FFT_FORWARD,
error_description);
}
template <class TRealArray2D, class TComplexArray2D>
bool FFT(const TRealArray2D & data_in, TComplexArray2D & data_out,
const size_t size1, const size_t size2, const char *& error_description)
{
copy_array::copyArray(data_in, data_out, size1, size2);
return impl::CFFT<TComplexArray2D,2>::FFT_inplace(data_out, size1, size2,
impl::FFT_FORWARD,
error_description);
}
template <class TRealArray3D, class TComplexArray3D>
bool FFT(const TRealArray3D & data_in, TComplexArray3D & data_out,
const size_t size1, const size_t size2, const size_t size3,
const char *& error_description)
{
copy_array::copyArray(data_in, data_out, size1, size2, size3);
return impl::CFFT<TComplexArray3D,3>::FFT_inplace(data_out, size1, size2, size3,
impl::FFT_FORWARD,
error_description);
}
} // simple_fft
#endif // __SIMPLE_FFT__FFT_HPP__

511
lib/simple_fft/fft_impl.hpp Normal file
View File

@@ -0,0 +1,511 @@
#ifndef __SIMPLE_FFT__FFT_IMPL_HPP__
#define __SIMPLE_FFT__FFT_IMPL_HPP__
#include "fft_settings.h"
#include "error_handling.hpp"
#include <cstddef>
#include <math.h>
#include <vector>
using std::size_t;
#ifndef M_PI
#define M_PI 3.1415926535897932
#endif
namespace simple_fft {
namespace impl {
enum FFT_direction
{
FFT_FORWARD = 0,
FFT_BACKWARD
};
// checking whether the size of array dimension is power of 2
// via "complement and compare" method
inline bool isPowerOfTwo(const size_t num)
{
if ((num == 0) || !(num & (~num + 1)))
return false;
return true;
}
inline bool checkNumElements(const size_t num_elements, const char *& error_description)
{
using namespace error_handling;
if (!isPowerOfTwo(num_elements)) {
GetErrorDescription(EC_ONE_OF_DIMS_ISNT_POWER_OF_TWO, error_description);
return false;
}
return true;
}
template <class TComplexArray1D>
inline void scaleValues(TComplexArray1D & data, const size_t num_elements)
{
real_type mult = 1.0 / num_elements;
int num_elements_signed = static_cast<int>(num_elements);
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int i = 0; i < num_elements_signed; ++i) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
data[i] *= mult;
#else
data(i) *= mult;
#endif
}
}
// NOTE: explicit template specialization for the case of std::vector<complex_type>
// because it is used in 2D and 3D FFT for both array classes with square and round
// brackets of element access operator; I need to guarantee that sub-FFT 1D will
// use square brackets for element access operator anyway. It is pretty ugly
// to duplicate the code but I haven't found more elegant solution.
template <>
inline void scaleValues<std::vector<complex_type> >(std::vector<complex_type> & data,
const size_t num_elements)
{
real_type mult = 1.0 / num_elements;
int num_elements_signed = static_cast<int>(num_elements);
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int i = 0; i < num_elements_signed; ++i) {
data[i] *= mult;
}
}
template <class TComplexArray1D>
inline void bufferExchangeHelper(TComplexArray1D & data, const size_t index_from,
const size_t index_to, complex_type & buf)
{
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
buf = data[index_from];
data[index_from] = data[index_to];
data[index_to]= buf;
#else
buf = data(index_from);
data(index_from) = data(index_to);
data(index_to)= buf;
#endif
}
// NOTE: explicit template specialization for the case of std::vector<complex_type>
// because it is used in 2D and 3D FFT for both array classes with square and round
// brackets of element access operator; I need to guarantee that sub-FFT 1D will
// use square brackets for element access operator anyway. It is pretty ugly
// to duplicate the code but I haven't found more elegant solution.
template <>
inline void bufferExchangeHelper<std::vector<complex_type> >(std::vector<complex_type> & data,
const size_t index_from,
const size_t index_to,
complex_type & buf)
{
buf = data[index_from];
data[index_from] = data[index_to];
data[index_to]= buf;
}
template <class TComplexArray1D>
void rearrangeData(TComplexArray1D & data, const size_t num_elements)
{
complex_type buf;
size_t target_index = 0;
size_t bit_mask;
for (size_t i = 0; i < num_elements; ++i)
{
if (target_index > i)
{
bufferExchangeHelper(data, target_index, i, buf);
}
// Initialize the bit mask
bit_mask = num_elements;
// While bit is 1
while (target_index & (bit_mask >>= 1)) // bit_mask = bit_mask >> 1
{
// Drop bit:
// & is bitwise AND,
// ~ is bitwise NOT
target_index &= ~bit_mask; // target_index = target_index & (~bit_mask)
}
// | is bitwise OR
target_index |= bit_mask; // target_index = target_index | bit_mask
}
}
template <class TComplexArray1D>
inline void fftTransformHelper(TComplexArray1D & data, const size_t match,
const size_t k, complex_type & product,
const complex_type factor)
{
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
product = data[match] * factor;
data[match] = data[k] - product;
data[k] += product;
#else
product = data(match) * factor;
data(match) = data(k) - product;
data(k) += product;
#endif
}
// NOTE: explicit template specialization for the case of std::vector<complex_type>
// because it is used in 2D and 3D FFT for both array classes with square and round
// brackets of element access operator; I need to guarantee that sub-FFT 1D will
// use square brackets for element access operator anyway. It is pretty ugly
// to duplicate the code but I haven't found more elegant solution.
template <>
inline void fftTransformHelper<std::vector<complex_type> >(std::vector<complex_type> & data,
const size_t match,
const size_t k,
complex_type & product,
const complex_type factor)
{
product = data[match] * factor;
data[match] = data[k] - product;
data[k] += product;
}
template <class TComplexArray1D>
bool makeTransform(TComplexArray1D & data, const size_t num_elements,
const FFT_direction fft_direction, const char *& error_description)
{
using namespace error_handling;
using std::sin;
double local_pi;
switch(fft_direction)
{
case(FFT_FORWARD):
local_pi = -M_PI;
break;
case(FFT_BACKWARD):
local_pi = M_PI;
break;
default:
GetErrorDescription(EC_WRONG_FFT_DIRECTION, error_description);
return false;
}
// declare variables to cycle the bits of initial signal
size_t next, match;
real_type sine;
real_type delta;
complex_type mult, factor, product;
// NOTE: user's complex type should have constructor like
// "complex(real, imag)", where each of real and imag has
// real type.
// cycle for all bit positions of initial signal
for (size_t i = 1; i < num_elements; i <<= 1)
{
next = i << 1; // getting the next bit
delta = local_pi / i; // angle increasing
sine = sin(0.5 * delta); // supplementary sin
// multiplier for trigonometric recurrence
mult = complex_type(-2.0 * sine * sine, sin(delta));
factor = 1.0; // start transform factor
for (size_t j = 0; j < i; ++j) // iterations through groups
// with different transform factors
{
for (size_t k = j; k < num_elements; k += next) // iterations through
// pairs within group
{
match = k + i;
fftTransformHelper(data, match, k, product, factor);
}
factor = mult * factor + factor;
}
}
return true;
}
// Generic template for complex FFT followed by its explicit specializations
template <class TComplexArray, int NumDims>
struct CFFT
{};
// 1D FFT:
template <class TComplexArray1D>
struct CFFT<TComplexArray1D,1>
{
// NOTE: passing by pointer is needed to avoid using element access operator
static bool FFT_inplace(TComplexArray1D & data, const size_t size,
const FFT_direction fft_direction,
const char *& error_description)
{
if(!checkNumElements(size, error_description)) {
return false;
}
rearrangeData(data, size);
if(!makeTransform(data, size, fft_direction, error_description)) {
return false;
}
if (FFT_BACKWARD == fft_direction) {
scaleValues(data, size);
}
return true;
}
};
// 2D FFT
template <class TComplexArray2D>
struct CFFT<TComplexArray2D,2>
{
static bool FFT_inplace(TComplexArray2D & data, const size_t size1, const size_t size2,
const FFT_direction fft_direction, const char *& error_description)
{
int n_rows = static_cast<int>(size1);
int n_cols = static_cast<int>(size2);
// fft for columns
std::vector<complex_type> subarray(n_rows); // each column has n_rows elements
for(int j = 0; j < n_cols; ++j)
{
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int i = 0; i < n_rows; ++i) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
subarray[i] = data[i][j];
#else
subarray[i] = data(i,j);
#endif
}
if(!CFFT<std::vector<complex_type>,1>::FFT_inplace(subarray, size1,
fft_direction,
error_description))
{
return false;
}
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int i = 0; i < n_rows; ++i) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
data[i][j] = subarray[i];
#else
data(i,j) = subarray[i];
#endif
}
}
// fft for rows
subarray.resize(n_cols); // each row has n_cols elements
for(int i = 0; i < n_rows; ++i)
{
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int j = 0; j < n_cols; ++j) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
subarray[j] = data[i][j];
#else
subarray[j] = data(i,j);
#endif
}
if(!CFFT<std::vector<complex_type>,1>::FFT_inplace(subarray, size2,
fft_direction,
error_description))
{
return false;
}
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int j = 0; j < n_cols; ++j) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
data[i][j] = subarray[j];
#else
data(i,j) = subarray[j];
#endif
}
}
return true;
}
};
// 3D FFT
template <class TComplexArray3D>
struct CFFT<TComplexArray3D,3>
{
static bool FFT_inplace(TComplexArray3D & data, const size_t size1, const size_t size2,
const size_t size3, const FFT_direction fft_direction,
const char *& error_description)
{
int n_rows = static_cast<int>(size1);
int n_cols = static_cast<int>(size2);
int n_depth = static_cast<int>(size3);
std::vector<complex_type> subarray(n_rows); // for fft for columns: each column has n_rows elements
for(int k = 0; k < n_depth; ++k) // for all depth layers
{
// fft for columns
for(int j = 0; j < n_cols; ++j)
{
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int i = 0; i < n_rows; ++i) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
subarray[i] = data[i][j][k];
#else
subarray[i] = data(i,j,k);
#endif
}
if(!CFFT<std::vector<complex_type>,1>::FFT_inplace(subarray, size1,
fft_direction,
error_description))
{
return false;
}
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int i = 0; i < n_rows; ++i) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
data[i][j][k] = subarray[i];
#else
data(i,j,k) = subarray[i];
#endif
}
}
}
subarray.resize(n_cols); // for fft for rows: each row has n_cols elements
for(int k = 0; k < n_depth; ++k) // for all depth layers
{
// fft for rows
for(int i = 0; i < n_rows; ++i)
{
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int j = 0; j < n_cols; ++j) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
subarray[j] = data[i][j][k];
#else
subarray[j] = data(i,j,k);
#endif
}
if(!CFFT<std::vector<complex_type>,1>::FFT_inplace(subarray, size2,
fft_direction,
error_description))
{
return false;
}
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int j = 0; j < n_cols; ++j) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
data[i][j][k] = subarray[j];
#else
data(i,j,k) = subarray[j];
#endif
}
}
}
// fft for depth
subarray.resize(n_depth); // each depth strip contains n_depth elements
for(int i = 0; i < n_rows; ++i) // for all rows layers
{
for(int j = 0; j < n_cols; ++j) // for all cols layers
{
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int k = 0; k < n_depth; ++k) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
subarray[k] = data[i][j][k];
#else
subarray[k] = data(i,j,k);
#endif
}
if(!CFFT<std::vector<complex_type>,1>::FFT_inplace(subarray, size3,
fft_direction,
error_description))
{
return false;
}
#ifndef __clang__
#ifdef __USE_OPENMP
#pragma omp parallel for
#endif
#endif
for(int k = 0; k < n_depth; ++k) {
#ifdef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
data[i][j][k] = subarray[k];
#else
data(i,j,k) = subarray[k];
#endif
}
}
}
return true;
}
};
} // namespace impl
} // namespace simple_fft
#endif // __SIMPLE_FFT__FFT_IMPL_HPP__

View File

@@ -0,0 +1,19 @@
// In this file you can alter some settings of the library:
// 1) Specify the desired real and complex types by typedef'ing real_type and complex_type.
// By default real_type is double and complex_type is std::complex<real_type>.
// 2) If the array class uses square brackets for element access operator, define
// the macro __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
#ifndef __SIMPLE_FFT__FFT_SETTINGS_H__
#define __SIMPLE_FFT__FFT_SETTINGS_H__
#include <complex>
typedef double real_type;
typedef std::complex<real_type> complex_type;
//#ifndef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
//#define __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
//#endif
#endif // __SIMPLE_FFT__FFT_SETTINGS_H__

74
math/dsp/Convolution2D.h Normal file
View File

@@ -0,0 +1,74 @@
#ifndef CONVOLUTION2D_H
#define CONVOLUTION2D_H
#ifndef __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
#define __USE_SQUARE_BRACKETS_FOR_ELEMENT_ACCESS_OPERATOR
#endif
#include "../../math/boxkde/Image2D.h"
#include "../../lib/simple_fft/fft.h"
namespace DSP {
template <typename Scalar> class Convolution2D {
public:
static inline Image2D<Scalar> convolve(Image2D<Scalar> signal, Image2D<Scalar> kernel){
RealArray2D signalReal;
ComplexArray2D signalSpectrum;
RealArray2D kernelReal;
ComplexArray2D kernelSpectrum;
signal.data().data
//convert image2d single vector to 2d vector, just use pointers
int signal_x = signal.width;
int signal_y = signal.height;
int kernel_x = kernel.width;
int kernel_y = kernel.height;
//check if power of 2, if not fill with zeros
while(!isPowerOfTwo(signal_x)){
}
if(!isPowerOfTwo(kernel.size)){
}
//calculate FFT for both
simple_fft::FFT();
//multiplay the results pointwise
//IFFT
simple_fft::IFFT();
//profit?
}
private:
typedef std::vector<std::vector<complex_type> > ComplexArray2D;
typedef std::vector<std::vector<real_type> > RealArray2D;
bool isPowerOfTwo (unsigned int x)
{
return ((x != 0) && !(x & (x - 1)));
}
};
}
#endif // CONVOLUTION_H

View File

@@ -36,7 +36,7 @@ namespace NM {
/** algorithm choice */
SamplePartitionType partitionType = SamplePartitionType::SAMPLE_PARTITION_WATERSHED;
const float regionMinSize = 2;//8; // (isolated) regions smaller than this will not be rendered?!
float regionMinSize = 2;//8; // (isolated) regions smaller than this will not be rendered?!
const float regionMergeSize = 20; //??
const int vertsPerPoly = 3;//6.0f;
const float detailSampleDist = 6.0f;

View File

@@ -0,0 +1,10 @@
#ifdef WITH_TESTS
#include "../../Tests.h"
#include "../../../lib/simple_fft/fft.h"
TEST(Convolution, Normal) {
}
#endif