123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684 |
- // (C) Copyright Nick Thompson 2019.
- // Use, modification and distribution are subject to the
- // Boost Software License, Version 1.0. (See accompanying file
- // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- #ifndef BOOST_MATH_INTERPOLATORS_DETAIL_CARDINAL_TRIGONOMETRIC_HPP
- #define BOOST_MATH_INTERPOLATORS_DETAIL_CARDINAL_TRIGONOMETRIC_HPP
- #include <cstddef>
- #include <cmath>
- #include <stdexcept>
- #include <boost/math/constants/constants.hpp>
- #ifdef BOOST_HAS_FLOAT128
- #include <quadmath.h>
- #endif
- #ifdef __has_include
- # if __has_include(<fftw3.h>)
- # include <fftw3.h>
- # else
- # error "This feature is unavailable without fftw3 installed"
- #endif
- #endif
- namespace boost { namespace math { namespace interpolators { namespace detail {
- template<typename Real>
- class cardinal_trigonometric_detail {
- public:
- cardinal_trigonometric_detail(const Real* data, size_t length, Real t0, Real h)
- {
- m_data = data;
- m_length = length;
- m_t0 = t0;
- m_h = h;
- throw std::domain_error("Not implemented.");
- }
- private:
- size_t m_length;
- Real m_t0;
- Real m_h;
- Real* m_data;
- };
- template<>
- class cardinal_trigonometric_detail<float> {
- public:
- cardinal_trigonometric_detail(const float* data, size_t length, float t0, float h) : m_t0{t0}, m_h{h}
- {
- if (length == 0)
- {
- throw std::logic_error("At least one sample is required.");
- }
- if (h <= 0)
- {
- throw std::logic_error("The step size must be > 0");
- }
- // The period sadly must be stored, since the complex vector has length that cannot be used to recover the period:
- m_T = m_h*length;
- m_complex_vector_size = length/2 + 1;
- m_gamma = fftwf_alloc_complex(m_complex_vector_size);
- // The const_cast is legitimate: FFTW does not change the data as long as FFTW_ESTIMATE is provided.
- fftwf_plan plan = fftwf_plan_dft_r2c_1d(length, const_cast<float*>(data), m_gamma, FFTW_ESTIMATE);
- // FFTW says a null plan is impossible with the basic interface we are using, and I have no reason to doubt them.
- // But it just feels weird not to check this:
- if (!plan)
- {
- throw std::logic_error("A null fftw plan was created.");
- }
- fftwf_execute(plan);
- fftwf_destroy_plan(plan);
- float denom = length;
- for (size_t k = 0; k < m_complex_vector_size; ++k)
- {
- m_gamma[k][0] /= denom;
- m_gamma[k][1] /= denom;
- }
- if (length % 2 == 0)
- {
- m_gamma[m_complex_vector_size -1][0] /= 2;
- // numerically, m_gamma[m_complex_vector_size -1][1] should be zero . . .
- // I believe, but need to check, that FFTW guarantees that it is identically zero.
- }
- }
- cardinal_trigonometric_detail(const cardinal_trigonometric_detail& old) = delete;
- cardinal_trigonometric_detail& operator=(const cardinal_trigonometric_detail&) = delete;
- cardinal_trigonometric_detail(cardinal_trigonometric_detail &&) = delete;
- float operator()(float t) const
- {
- using std::sin;
- using std::cos;
- using boost::math::constants::two_pi;
- using std::exp;
- float s = m_gamma[0][0];
- float x = two_pi<float>()*(t - m_t0)/m_T;
- fftwf_complex z;
- // boost::math::cos_pi with a redefinition of x? Not now . . .
- z[0] = cos(x);
- z[1] = sin(x);
- fftwf_complex b{0, 0};
- // u = b*z
- fftwf_complex u;
- for (size_t k = m_complex_vector_size - 1; k >= 1; --k) {
- u[0] = b[0]*z[0] - b[1]*z[1];
- u[1] = b[0]*z[1] + b[1]*z[0];
- b[0] = m_gamma[k][0] + u[0];
- b[1] = m_gamma[k][1] + u[1];
- }
- s += 2*(b[0]*z[0] - b[1]*z[1]);
- return s;
- }
- float prime(float t) const
- {
- using std::sin;
- using std::cos;
- using boost::math::constants::two_pi;
- using std::exp;
- float x = two_pi<float>()*(t - m_t0)/m_T;
- fftwf_complex z;
- z[0] = cos(x);
- z[1] = sin(x);
- fftwf_complex b{0, 0};
- // u = b*z
- fftwf_complex u;
- for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
- {
- u[0] = b[0]*z[0] - b[1]*z[1];
- u[1] = b[0]*z[1] + b[1]*z[0];
- b[0] = k*m_gamma[k][0] + u[0];
- b[1] = k*m_gamma[k][1] + u[1];
- }
- // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
- return -2*two_pi<float>()*(b[1]*z[0] + b[0]*z[1])/m_T;
- }
- float double_prime(float t) const
- {
- using std::sin;
- using std::cos;
- using boost::math::constants::two_pi;
- using std::exp;
- float x = two_pi<float>()*(t - m_t0)/m_T;
- fftwf_complex z;
- z[0] = cos(x);
- z[1] = sin(x);
- fftwf_complex b{0, 0};
- // u = b*z
- fftwf_complex u;
- for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
- {
- u[0] = b[0]*z[0] - b[1]*z[1];
- u[1] = b[0]*z[1] + b[1]*z[0];
- b[0] = k*k*m_gamma[k][0] + u[0];
- b[1] = k*k*m_gamma[k][1] + u[1];
- }
- // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
- return -2*two_pi<float>()*two_pi<float>()*(b[0]*z[0] - b[1]*z[1])/(m_T*m_T);
- }
- float period() const
- {
- return m_T;
- }
- float integrate() const
- {
- return m_T*m_gamma[0][0];
- }
- float squared_l2() const
- {
- float s = 0;
- // Always add smallest to largest for accuracy.
- for (size_t i = m_complex_vector_size - 1; i >= 1; --i)
- {
- s += (m_gamma[i][0]*m_gamma[i][0] + m_gamma[i][1]*m_gamma[i][1]);
- }
- s *= 2;
- s += m_gamma[0][0]*m_gamma[0][0];
- return s*m_T;
- }
- ~cardinal_trigonometric_detail()
- {
- if (m_gamma)
- {
- fftwf_free(m_gamma);
- m_gamma = nullptr;
- }
- }
- private:
- float m_t0;
- float m_h;
- float m_T;
- fftwf_complex* m_gamma;
- size_t m_complex_vector_size;
- };
- template<>
- class cardinal_trigonometric_detail<double> {
- public:
- cardinal_trigonometric_detail(const double* data, size_t length, double t0, double h) : m_t0{t0}, m_h{h}
- {
- if (length == 0)
- {
- throw std::logic_error("At least one sample is required.");
- }
- if (h <= 0)
- {
- throw std::logic_error("The step size must be > 0");
- }
- m_T = m_h*length;
- m_complex_vector_size = length/2 + 1;
- m_gamma = fftw_alloc_complex(m_complex_vector_size);
- fftw_plan plan = fftw_plan_dft_r2c_1d(length, const_cast<double*>(data), m_gamma, FFTW_ESTIMATE);
- if (!plan)
- {
- throw std::logic_error("A null fftw plan was created.");
- }
- fftw_execute(plan);
- fftw_destroy_plan(plan);
- double denom = length;
- for (size_t k = 0; k < m_complex_vector_size; ++k)
- {
- m_gamma[k][0] /= denom;
- m_gamma[k][1] /= denom;
- }
- if (length % 2 == 0)
- {
- m_gamma[m_complex_vector_size -1][0] /= 2;
- }
- }
- cardinal_trigonometric_detail(const cardinal_trigonometric_detail& old) = delete;
- cardinal_trigonometric_detail& operator=(const cardinal_trigonometric_detail&) = delete;
- cardinal_trigonometric_detail(cardinal_trigonometric_detail &&) = delete;
- double operator()(double t) const
- {
- using std::sin;
- using std::cos;
- using boost::math::constants::two_pi;
- using std::exp;
- double s = m_gamma[0][0];
- double x = two_pi<double>()*(t - m_t0)/m_T;
- fftw_complex z;
- z[0] = cos(x);
- z[1] = sin(x);
- fftw_complex b{0, 0};
- // u = b*z
- fftw_complex u;
- for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
- {
- u[0] = b[0]*z[0] - b[1]*z[1];
- u[1] = b[0]*z[1] + b[1]*z[0];
- b[0] = m_gamma[k][0] + u[0];
- b[1] = m_gamma[k][1] + u[1];
- }
- s += 2*(b[0]*z[0] - b[1]*z[1]);
- return s;
- }
- double prime(double t) const
- {
- using std::sin;
- using std::cos;
- using boost::math::constants::two_pi;
- using std::exp;
- double x = two_pi<double>()*(t - m_t0)/m_T;
- fftw_complex z;
- z[0] = cos(x);
- z[1] = sin(x);
- fftw_complex b{0, 0};
- // u = b*z
- fftw_complex u;
- for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
- {
- u[0] = b[0]*z[0] - b[1]*z[1];
- u[1] = b[0]*z[1] + b[1]*z[0];
- b[0] = k*m_gamma[k][0] + u[0];
- b[1] = k*m_gamma[k][1] + u[1];
- }
- // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
- return -2*two_pi<double>()*(b[1]*z[0] + b[0]*z[1])/m_T;
- }
- double double_prime(double t) const
- {
- using std::sin;
- using std::cos;
- using boost::math::constants::two_pi;
- using std::exp;
- double x = two_pi<double>()*(t - m_t0)/m_T;
- fftw_complex z;
- z[0] = cos(x);
- z[1] = sin(x);
- fftw_complex b{0, 0};
- // u = b*z
- fftw_complex u;
- for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
- {
- u[0] = b[0]*z[0] - b[1]*z[1];
- u[1] = b[0]*z[1] + b[1]*z[0];
- b[0] = k*k*m_gamma[k][0] + u[0];
- b[1] = k*k*m_gamma[k][1] + u[1];
- }
- // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
- return -2*two_pi<double>()*two_pi<double>()*(b[0]*z[0] - b[1]*z[1])/(m_T*m_T);
- }
- double period() const
- {
- return m_T;
- }
- double integrate() const
- {
- return m_T*m_gamma[0][0];
- }
- double squared_l2() const
- {
- double s = 0;
- for (size_t i = m_complex_vector_size - 1; i >= 1; --i)
- {
- s += (m_gamma[i][0]*m_gamma[i][0] + m_gamma[i][1]*m_gamma[i][1]);
- }
- s *= 2;
- s += m_gamma[0][0]*m_gamma[0][0];
- return s*m_T;
- }
- ~cardinal_trigonometric_detail()
- {
- if (m_gamma)
- {
- fftw_free(m_gamma);
- m_gamma = nullptr;
- }
- }
- private:
- double m_t0;
- double m_h;
- double m_T;
- fftw_complex* m_gamma;
- size_t m_complex_vector_size;
- };
- template<>
- class cardinal_trigonometric_detail<long double> {
- public:
- cardinal_trigonometric_detail(const long double* data, size_t length, long double t0, long double h) : m_t0{t0}, m_h{h}
- {
- if (length == 0)
- {
- throw std::logic_error("At least one sample is required.");
- }
- if (h <= 0)
- {
- throw std::logic_error("The step size must be > 0");
- }
- m_T = m_h*length;
- m_complex_vector_size = length/2 + 1;
- m_gamma = fftwl_alloc_complex(m_complex_vector_size);
- fftwl_plan plan = fftwl_plan_dft_r2c_1d(length, const_cast<long double*>(data), m_gamma, FFTW_ESTIMATE);
- if (!plan)
- {
- throw std::logic_error("A null fftw plan was created.");
- }
- fftwl_execute(plan);
- fftwl_destroy_plan(plan);
- long double denom = length;
- for (size_t k = 0; k < m_complex_vector_size; ++k)
- {
- m_gamma[k][0] /= denom;
- m_gamma[k][1] /= denom;
- }
- if (length % 2 == 0) {
- m_gamma[m_complex_vector_size -1][0] /= 2;
- }
- }
- cardinal_trigonometric_detail(const cardinal_trigonometric_detail& old) = delete;
- cardinal_trigonometric_detail& operator=(const cardinal_trigonometric_detail&) = delete;
- cardinal_trigonometric_detail(cardinal_trigonometric_detail &&) = delete;
- long double operator()(long double t) const
- {
- using std::sin;
- using std::cos;
- using boost::math::constants::two_pi;
- using std::exp;
- long double s = m_gamma[0][0];
- long double x = two_pi<long double>()*(t - m_t0)/m_T;
- fftwl_complex z;
- z[0] = cos(x);
- z[1] = sin(x);
- fftwl_complex b{0, 0};
- fftwl_complex u;
- for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
- {
- u[0] = b[0]*z[0] - b[1]*z[1];
- u[1] = b[0]*z[1] + b[1]*z[0];
- b[0] = m_gamma[k][0] + u[0];
- b[1] = m_gamma[k][1] + u[1];
- }
- s += 2*(b[0]*z[0] - b[1]*z[1]);
- return s;
- }
- long double prime(long double t) const
- {
- using std::sin;
- using std::cos;
- using boost::math::constants::two_pi;
- using std::exp;
- long double x = two_pi<long double>()*(t - m_t0)/m_T;
- fftwl_complex z;
- z[0] = cos(x);
- z[1] = sin(x);
- fftwl_complex b{0, 0};
- // u = b*z
- fftwl_complex u;
- for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
- {
- u[0] = b[0]*z[0] - b[1]*z[1];
- u[1] = b[0]*z[1] + b[1]*z[0];
- b[0] = k*m_gamma[k][0] + u[0];
- b[1] = k*m_gamma[k][1] + u[1];
- }
- // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
- return -2*two_pi<long double>()*(b[1]*z[0] + b[0]*z[1])/m_T;
- }
- long double double_prime(long double t) const
- {
- using std::sin;
- using std::cos;
- using boost::math::constants::two_pi;
- using std::exp;
- long double x = two_pi<long double>()*(t - m_t0)/m_T;
- fftwl_complex z;
- z[0] = cos(x);
- z[1] = sin(x);
- fftwl_complex b{0, 0};
- // u = b*z
- fftwl_complex u;
- for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
- {
- u[0] = b[0]*z[0] - b[1]*z[1];
- u[1] = b[0]*z[1] + b[1]*z[0];
- b[0] = k*k*m_gamma[k][0] + u[0];
- b[1] = k*k*m_gamma[k][1] + u[1];
- }
- // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
- return -2*two_pi<long double>()*two_pi<long double>()*(b[0]*z[0] - b[1]*z[1])/(m_T*m_T);
- }
- long double period() const
- {
- return m_T;
- }
- long double integrate() const
- {
- return m_T*m_gamma[0][0];
- }
- long double squared_l2() const
- {
- long double s = 0;
- for (size_t i = m_complex_vector_size - 1; i >= 1; --i)
- {
- s += (m_gamma[i][0]*m_gamma[i][0] + m_gamma[i][1]*m_gamma[i][1]);
- }
- s *= 2;
- s += m_gamma[0][0]*m_gamma[0][0];
- return s*m_T;
- }
- ~cardinal_trigonometric_detail()
- {
- if (m_gamma)
- {
- fftwl_free(m_gamma);
- m_gamma = nullptr;
- }
- }
- private:
- long double m_t0;
- long double m_h;
- long double m_T;
- fftwl_complex* m_gamma;
- size_t m_complex_vector_size;
- };
- #ifdef BOOST_HAS_FLOAT128
- template<>
- class cardinal_trigonometric_detail<__float128> {
- public:
- cardinal_trigonometric_detail(const __float128* data, size_t length, __float128 t0, __float128 h) : m_t0{t0}, m_h{h}
- {
- if (length == 0)
- {
- throw std::logic_error("At least one sample is required.");
- }
- if (h <= 0)
- {
- throw std::logic_error("The step size must be > 0");
- }
- m_T = m_h*length;
- m_complex_vector_size = length/2 + 1;
- m_gamma = fftwq_alloc_complex(m_complex_vector_size);
- fftwq_plan plan = fftwq_plan_dft_r2c_1d(length, reinterpret_cast<__float128*>(const_cast<__float128*>(data)), m_gamma, FFTW_ESTIMATE);
- if (!plan)
- {
- throw std::logic_error("A null fftw plan was created.");
- }
- fftwq_execute(plan);
- fftwq_destroy_plan(plan);
- __float128 denom = length;
- for (size_t k = 0; k < m_complex_vector_size; ++k)
- {
- m_gamma[k][0] /= denom;
- m_gamma[k][1] /= denom;
- }
- if (length % 2 == 0)
- {
- m_gamma[m_complex_vector_size -1][0] /= 2;
- }
- }
- cardinal_trigonometric_detail(const cardinal_trigonometric_detail& old) = delete;
- cardinal_trigonometric_detail& operator=(const cardinal_trigonometric_detail&) = delete;
- cardinal_trigonometric_detail(cardinal_trigonometric_detail &&) = delete;
- __float128 operator()(__float128 t) const
- {
- using std::sin;
- using std::cos;
- using boost::math::constants::two_pi;
- using std::exp;
- __float128 s = m_gamma[0][0];
- __float128 x = two_pi<__float128>()*(t - m_t0)/m_T;
- fftwq_complex z;
- z[0] = cosq(x);
- z[1] = sinq(x);
- fftwq_complex b{0, 0};
- fftwq_complex u;
- for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
- {
- u[0] = b[0]*z[0] - b[1]*z[1];
- u[1] = b[0]*z[1] + b[1]*z[0];
- b[0] = m_gamma[k][0] + u[0];
- b[1] = m_gamma[k][1] + u[1];
- }
- s += 2*(b[0]*z[0] - b[1]*z[1]);
- return s;
- }
- __float128 prime(__float128 t) const
- {
- using std::sin;
- using std::cos;
- using boost::math::constants::two_pi;
- using std::exp;
- __float128 x = two_pi<__float128>()*(t - m_t0)/m_T;
- fftwq_complex z;
- z[0] = cosq(x);
- z[1] = sinq(x);
- fftwq_complex b{0, 0};
- // u = b*z
- fftwq_complex u;
- for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
- {
- u[0] = b[0]*z[0] - b[1]*z[1];
- u[1] = b[0]*z[1] + b[1]*z[0];
- b[0] = k*m_gamma[k][0] + u[0];
- b[1] = k*m_gamma[k][1] + u[1];
- }
- // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
- return -2*two_pi<__float128>()*(b[1]*z[0] + b[0]*z[1])/m_T;
- }
- __float128 double_prime(__float128 t) const
- {
- using std::sin;
- using std::cos;
- using boost::math::constants::two_pi;
- using std::exp;
- __float128 x = two_pi<__float128>()*(t - m_t0)/m_T;
- fftwq_complex z;
- z[0] = cosq(x);
- z[1] = sinq(x);
- fftwq_complex b{0, 0};
- // u = b*z
- fftwq_complex u;
- for (size_t k = m_complex_vector_size - 1; k >= 1; --k)
- {
- u[0] = b[0]*z[0] - b[1]*z[1];
- u[1] = b[0]*z[1] + b[1]*z[0];
- b[0] = k*k*m_gamma[k][0] + u[0];
- b[1] = k*k*m_gamma[k][1] + u[1];
- }
- // b*z = (b[0]*z[0] - b[1]*z[1]) + i(b[1]*z[0] + b[0]*z[1])
- return -2*two_pi<__float128>()*two_pi<__float128>()*(b[0]*z[0] - b[1]*z[1])/(m_T*m_T);
- }
- __float128 period() const
- {
- return m_T;
- }
- __float128 integrate() const
- {
- return m_T*m_gamma[0][0];
- }
- __float128 squared_l2() const
- {
- __float128 s = 0;
- for (size_t i = m_complex_vector_size - 1; i >= 1; --i)
- {
- s += (m_gamma[i][0]*m_gamma[i][0] + m_gamma[i][1]*m_gamma[i][1]);
- }
- s *= 2;
- s += m_gamma[0][0]*m_gamma[0][0];
- return s*m_T;
- }
- ~cardinal_trigonometric_detail()
- {
- if (m_gamma)
- {
- fftwq_free(m_gamma);
- m_gamma = nullptr;
- }
- }
- private:
- __float128 m_t0;
- __float128 m_h;
- __float128 m_T;
- fftwq_complex* m_gamma;
- size_t m_complex_vector_size;
- };
- #endif
- }}}}
- #endif
|