-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Real to complex #21
Real to complex #21
Changes from all commits
4c3ee6a
95b09d1
ab2fe9e
350a421
f9ecb7a
e02dd98
8dcb6c1
5b1b809
e8a0160
295744e
0f7e425
9cd754e
53e9caf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ | |
#include <boost/math/fft/multiprecision_complex.hpp> | ||
|
||
#include <boost/math/fft/algorithms.hpp> | ||
#include <boost/math/fft/real_algorithms.hpp> | ||
#include <boost/math/fft/dft_api.hpp> | ||
|
||
namespace boost { namespace math { namespace fft { | ||
|
@@ -138,11 +139,101 @@ | |
std::size_t my_size{}; | ||
}; | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the bsl rfft backend needs to be implemented |
||
template<class T, class allocator_t = std::allocator<T> > | ||
class bsl_rfft_backend | ||
{ | ||
public: | ||
using value_type = T; | ||
using allocator_type = allocator_t; | ||
|
||
// the provided root of unity is used instead of exp(-i 2 pi/n) | ||
constexpr bsl_rfft_backend(std::size_t n, const allocator_type& in_alloc = allocator_type{}): | ||
alloc{in_alloc}, | ||
my_size{n} | ||
{ | ||
} | ||
|
||
~bsl_rfft_backend() | ||
{ | ||
} | ||
|
||
void resize(std::size_t new_size) | ||
{ | ||
my_size = new_size; | ||
} | ||
|
||
constexpr std::size_t size() const { return my_size; } | ||
constexpr std::size_t unique_complex_size() const { return my_size/2 + 1;} | ||
|
||
void real_to_halfcomplex(const value_type* in, value_type* out) const | ||
{ | ||
const long N = static_cast<long>(size()); | ||
// select the implementation according to the DFT size | ||
switch(N) | ||
{ | ||
case 0: | ||
return; | ||
case 1: | ||
out[0]=in[0]; | ||
return; | ||
case 2: | ||
detail::real_dft_2(in,out,1); | ||
return; | ||
} | ||
if( detail::is_power2(N)) | ||
{ | ||
detail::real_dft_power2(in,in+N,out,1); | ||
}else | ||
{ | ||
detail::real_dft_composite(in,in+N,out,1,alloc); | ||
} | ||
//if(detail::is_prime(N)) | ||
//{ | ||
// detail::real_dft_prime_rader(in,in+N,out,sign,alloc); | ||
//} | ||
} | ||
void halfcomplex_to_real(const value_type* in, value_type* out) const | ||
{ | ||
const long N = static_cast<long>(size()); | ||
// select the implementation according to the DFT size | ||
switch(N) | ||
{ | ||
case 0: | ||
return; | ||
case 1: | ||
out[0]=in[0]; | ||
return; | ||
case 2: | ||
detail::real_inverse_dft_2(in,out,1); | ||
return; | ||
} | ||
if( detail::is_power2(N)) | ||
{ | ||
detail::real_inverse_dft_power2(in,in+N,out,1); | ||
}else | ||
{ | ||
detail::real_inverse_dft_composite(in,in+N,out,1,alloc); | ||
} | ||
//if(detail::is_prime(N)) | ||
//{ | ||
// detail::real_inverse_dft_prime_rader(in,in+N,out,sign,alloc); | ||
//} | ||
} | ||
|
||
private: | ||
allocator_type alloc; | ||
std::size_t my_size{}; | ||
}; | ||
|
||
} // namespace detail | ||
|
||
template<class RingType = std::complex<double>, class Allocator_t = std::allocator<RingType> > | ||
using bsl_dft = detail::complex_dft<detail::bsl_backend,RingType,Allocator_t>; | ||
|
||
template<class T = double, class Allocator_t = std::allocator<T> > | ||
using bsl_rfft = detail::real_dft<detail::bsl_rfft_backend,T,Allocator_t>; | ||
|
||
template<class RingType = std::complex<double>, class Allocator_t = std::allocator<RingType> > | ||
using bsl_algebraic_dft = detail::algebraic_dft<detail::bsl_backend,RingType,Allocator_t>; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -574,28 +574,14 @@ | |
// -> size(out) >= N | ||
{ | ||
const std::size_t N = size(); | ||
vector_t<real_value_type> tmp(out,out+N); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The algorithm that we use for rfft for powers of 2, comes naturally in-place if we use the halfcomplex representation used by FFTW. |
||
out[0]=tmp[0]; | ||
for(unsigned int i=1,j=1;j<N;++i,j+=2) | ||
{ | ||
out[j] = tmp[i]; | ||
if(j+1<N) | ||
out[j+1] = tmp[N-i]; | ||
} | ||
for(unsigned int i=1,j=N-1;i<j;++i,--j) | ||
out[j] = -out[j]; | ||
} | ||
void unpack_halfcomplex(real_value_type* out) const | ||
// precondition: | ||
// -> size(out) >= N | ||
{ | ||
const std::size_t N = size(); | ||
vector_t<real_value_type> tmp(out,out+N); | ||
out[0]=tmp[0]; | ||
for(unsigned int i=1,j=1;j<N;++i,j+=2) | ||
{ | ||
out[i] = tmp[j]; | ||
if(j+1<N) | ||
out[N-i] = tmp[j+1]; | ||
} | ||
pack_halfcomplex(out); | ||
} | ||
|
||
public: | ||
|
@@ -638,6 +624,11 @@ | |
std::copy(in,in+size(),out); | ||
unpack_halfcomplex(out); | ||
execute(my_hc2r_plan,my_hc2r_unaligned_plan,out,out); | ||
|
||
const std::size_t N = size(); | ||
const real_value_type inv_N = real_value_type{1.0}/N; | ||
for(unsigned int i=0;i<N;++i) | ||
out[i] *= inv_N; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. when transforming halfcomplex-to-real, both GSL and FFTW, return an array which is a 1/N factor of the original real array. This could be in tune with the fact that the reverse FFT is the inverse of the forward FFT up to a 1/N factor. But the actual implementation of the halfcomplex-to-real (at least in BSL) is the reverse graph of the real-to-halfcomplex and the 1/N factor does not appear. I cannot see a good reason why the halfcomplex-to-real cannot be the inverse linear map of the real-to-halfcomplex. In the BSL this comes naturally. With the GSL and FFTW I need to multiply times 1/N. UPD. I realized that the halfcomplex-to-real in BSL has multiplications by inverse factors of N all over. So it doesn't actually comes naturally like I previously said. @cosurgi @ckormanyos : if you agree I will change the halfcomplex-to-real to return the inverse of the real-to-halfcomplex times N, just to save some multiplications and be in tune with the FFTW and GSL conventions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Lagrang3 yes, this is a good idea. Better to make sure that all backends produce the same kind of output. To avoid any kind of frustration for the users who have a nice working code, and only decide to switch to a different backend. Having to put an if somewhere in the user code to multiply by 1/N, depending on the type of backend, would not be good. |
||
} | ||
template<class Complex> | ||
void real_to_complex(const real_value_type* in, Complex *out)const | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -174,6 +174,34 @@ namespace fft { namespace detail { | |
gsl_fft_halfcomplex_wavetable *halfcomplex_wtable; | ||
gsl_fft_real_workspace *real_wspace; | ||
|
||
void pack_halfcomplex(real_value_type* out) const | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The algorithm that we use for rfft for powers of 2, comes naturally in-place if we use the halfcomplex representation used by FFTW. Hence we have to pack and unpack the halfcomplex returned by GSL to make it compatible with our representation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow, I appreciate your comments about what's going on in the code. It's a bit unrelated to this comment, but I noticed that we could replace |
||
// precondition: | ||
// -> size(out) >= N | ||
{ | ||
const std::size_t N = size(); | ||
vector_t<real_value_type> tmp(out,out+N); | ||
out[0]=tmp[0]; | ||
for(unsigned int i=1,j=1;j<N;++i,j+=2) | ||
{ | ||
out[j] = tmp[i]; | ||
if(j+1<N) | ||
out[j+1] = -tmp[N-i]; | ||
} | ||
} | ||
void unpack_halfcomplex(real_value_type* out) const | ||
// precondition: | ||
// -> size(out) >= N | ||
{ | ||
const std::size_t N = size(); | ||
vector_t<real_value_type> tmp(out,out+N); | ||
out[0]=tmp[0]; | ||
for(unsigned int i=1,j=1;j<N;++i,j+=2) | ||
{ | ||
out[i] = tmp[j]; | ||
if(j+1<N) | ||
out[N-i] = -tmp[j+1]; | ||
} | ||
} | ||
template<plan_type p> | ||
void execute(const real_value_type* in, | ||
real_value_type* out, | ||
|
@@ -189,6 +217,7 @@ namespace fft { namespace detail { | |
} | ||
gsl_fft_real_transform( | ||
out,1, N, real_wtable, real_wspace); | ||
unpack_halfcomplex(out); | ||
} | ||
template<plan_type p> | ||
void execute(const real_value_type* in, | ||
|
@@ -203,8 +232,13 @@ namespace fft { namespace detail { | |
// undefined-behavior | ||
std::copy(in,in+N,out); | ||
} | ||
pack_halfcomplex(out); | ||
gsl_fft_halfcomplex_transform( | ||
out,1, N, halfcomplex_wtable, real_wspace); | ||
|
||
const real_value_type inv_N = real_value_type{1.0}/N; | ||
for(unsigned int i=0;i<N;++i) | ||
out[i] *= inv_N; | ||
} | ||
|
||
void free() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the GSL documentation, the rfft can be deduced from the decimation-in-time (DIT) complex fft.
The default complex fft for powers of 2 is implemented as Decimation-in-frequency (DIF), i wrote another complex fft as DIT to test the algorithm before writing the rfft.