File indexing completed on 2025-01-18 09:40:10
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef BOOST_MATH_SPECIAL_DAUBECHIES_WAVELET_HPP
0009 #define BOOST_MATH_SPECIAL_DAUBECHIES_WAVELET_HPP
0010 #include <vector>
0011 #include <array>
0012 #include <cmath>
0013 #include <thread>
0014 #include <future>
0015 #include <iostream>
0016 #include <boost/math/constants/constants.hpp>
0017 #include <boost/math/special_functions/detail/daubechies_scaling_integer_grid.hpp>
0018 #include <boost/math/special_functions/daubechies_scaling.hpp>
0019 #include <boost/math/filters/daubechies.hpp>
0020 #include <boost/math/interpolators/detail/cubic_hermite_detail.hpp>
0021 #include <boost/math/interpolators/detail/quintic_hermite_detail.hpp>
0022 #include <boost/math/interpolators/detail/septic_hermite_detail.hpp>
0023
0024 #include <boost/math/tools/is_standalone.hpp>
0025 #ifndef BOOST_MATH_STANDALONE
0026 #include <boost/config.hpp>
0027 #ifdef BOOST_NO_CXX17_IF_CONSTEXPR
0028 #error "The header <boost/math/norms.hpp> can only be used in C++17 and later."
0029 #endif
0030 #endif
0031
0032 namespace boost::math {
0033
0034 template<class Real, int p, int order>
0035 std::vector<Real> daubechies_wavelet_dyadic_grid(int64_t j_max)
0036 {
0037 if (j_max == 0)
0038 {
0039 throw std::domain_error("The wavelet dyadic grid is refined from the scaling integer grid, so its minimum amount of data is half integer widths.");
0040 }
0041 auto phijk = daubechies_scaling_dyadic_grid<Real, p, order>(j_max - 1);
0042
0043
0044 auto d = boost::math::filters::daubechies_scaling_filter<Real, p>();
0045 Real scale = boost::math::constants::root_two<Real>() * (1 << order);
0046 for (size_t i = 0; i < d.size(); ++i)
0047 {
0048 d[i] *= scale;
0049 if (!(i & 1))
0050 {
0051 d[i] = -d[i];
0052 }
0053 }
0054
0055 std::vector<Real> v(2 * p + (2 * p - 1) * ((int64_t(1) << j_max) - 1), std::numeric_limits<Real>::quiet_NaN());
0056 v[0] = 0;
0057 v[v.size() - 1] = 0;
0058
0059 for (int64_t l = 1; l < static_cast<int64_t>(v.size() - 1); ++l)
0060 {
0061 Real term = 0;
0062 for (int64_t k = 0; k < static_cast<int64_t>(d.size()); ++k)
0063 {
0064 int64_t idx = (int64_t(1) << (j_max - 1)) * (1 - 2 * p + k) + l;
0065 if (idx < 0 || idx >= static_cast<int64_t>(phijk.size()))
0066 {
0067 continue;
0068 }
0069 term += d[k] * phijk[idx];
0070 }
0071 v[l] = term;
0072 }
0073
0074 return v;
0075 }
0076
0077
0078 template<class Real, int p>
0079 class daubechies_wavelet {
0080
0081
0082
0083 using vector_type = std::vector < std::array < Real, p < 6 ? 2 : p < 10 ? 3 : 4>>;
0084
0085
0086
0087 using interpolator_list = std::tuple<
0088 detail::null_interpolator, detail::matched_holder_aos<vector_type>, detail::linear_interpolation_aos<vector_type>,
0089 interpolators::detail::cardinal_cubic_hermite_detail_aos<vector_type>, interpolators::detail::cardinal_quintic_hermite_detail_aos<vector_type>,
0090 interpolators::detail::cardinal_septic_hermite_detail_aos<vector_type> > ;
0091
0092
0093
0094 using interpolator_type = std::tuple_element_t<
0095 p == 1 ? 0 :
0096 p == 2 ? 1 :
0097 p == 3 ? 2 :
0098 p <= 5 ? 3 :
0099 p <= 9 ? 4 : 5, interpolator_list>;
0100 public:
0101 explicit daubechies_wavelet(int grid_refinements = -1)
0102 {
0103 static_assert(p < 20, "Daubechies wavelets are only implemented for p < 20.");
0104 static_assert(p > 0, "Daubechies wavelets must have at least 1 vanishing moment.");
0105 if (grid_refinements == 0)
0106 {
0107 throw std::domain_error("The wavelet requires at least 1 grid refinement.");
0108 }
0109 if constexpr (p == 1)
0110 {
0111 return;
0112 }
0113 else
0114 {
0115 if (grid_refinements < 0)
0116 {
0117 if constexpr (std::is_same_v<Real, float>)
0118 {
0119 if (grid_refinements == -2)
0120 {
0121
0122
0123 std::array<int, 20> r{ -1, -1, 18, 19, 16, 11, 8, 7, 7, 7, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3 };
0124 grid_refinements = r[p];
0125 }
0126 else
0127 {
0128
0129
0130 std::array<int, 20> r{ -1, -1, 21, 21, 21, 17, 16, 15, 14, 13, 12, 11, 11, 11, 11, 11, 11, 11, 11, 11 };
0131 grid_refinements = r[p];
0132 }
0133 }
0134 else if constexpr (std::is_same_v<Real, double>)
0135 {
0136
0137 std::array<int, 20> r{ -1, -1, 21, 21, 21, 21, 21, 21, 21, 21, 20, 20, 19, 18, 18, 18, 18, 18, 18, 18 };
0138 grid_refinements = r[p];
0139 }
0140 else
0141 {
0142 grid_refinements = 21;
0143 }
0144 }
0145
0146
0147
0148 std::future<std::vector<Real>> t0 = std::async(std::launch::async, [&grid_refinements]() {
0149
0150 auto v = daubechies_wavelet_dyadic_grid<typename detail::daubechies_eval_type<Real>::type, p, 0>(grid_refinements);
0151 return detail::daubechies_eval_type<Real>::vector_cast(v);
0152 });
0153
0154 std::future<std::vector<Real>> t1 = std::async(std::launch::async, [&grid_refinements]() {
0155 auto v = daubechies_wavelet_dyadic_grid<typename detail::daubechies_eval_type<Real>::type, p, 1>(grid_refinements);
0156 return detail::daubechies_eval_type<Real>::vector_cast(v);
0157 });
0158
0159
0160 std::vector<Real> d2ydx2;
0161 std::vector<Real> d3ydx3;
0162 if constexpr (p >= 6) {
0163 std::future<std::vector<Real>> t3 = std::async(std::launch::async, [&grid_refinements]() {
0164 auto v = daubechies_wavelet_dyadic_grid<typename detail::daubechies_eval_type<Real>::type, p, 2>(grid_refinements);
0165 return detail::daubechies_eval_type<Real>::vector_cast(v);
0166 });
0167
0168 if constexpr (p >= 10) {
0169 std::future<std::vector<Real>> t4 = std::async(std::launch::async, [&grid_refinements]() {
0170 auto v = daubechies_wavelet_dyadic_grid<typename detail::daubechies_eval_type<Real>::type, p, 3>(grid_refinements);
0171 return detail::daubechies_eval_type<Real>::vector_cast(v);
0172 });
0173 d3ydx3 = t4.get();
0174 }
0175 d2ydx2 = t3.get();
0176 }
0177
0178
0179 auto y = t0.get();
0180 auto dydx = t1.get();
0181
0182 if constexpr (p >= 2)
0183 {
0184 vector_type data(y.size());
0185 for (size_t i = 0; i < y.size(); ++i)
0186 {
0187 data[i][0] = y[i];
0188 data[i][1] = dydx[i];
0189 if constexpr (p >= 6)
0190 data[i][2] = d2ydx2[i];
0191 if constexpr (p >= 10)
0192 data[i][3] = d3ydx3[i];
0193 }
0194 if constexpr (p <= 3)
0195 m_interpolator = std::make_shared<interpolator_type>(std::move(data), grid_refinements, Real(-p + 1));
0196 else
0197 m_interpolator = std::make_shared<interpolator_type>(std::move(data), Real(-p + 1), Real(1) / (1 << grid_refinements));
0198 }
0199 else
0200 m_interpolator = std::make_shared<detail::null_interpolator>();
0201 }
0202 }
0203
0204
0205 inline Real operator()(Real x) const
0206 {
0207 if (x <= -p + 1 || x >= p)
0208 {
0209 return 0;
0210 }
0211
0212 if constexpr (p == 1)
0213 {
0214 if (x < Real(1) / Real(2))
0215 {
0216 return 1;
0217 }
0218 else if (x == Real(1) / Real(2))
0219 {
0220 return 0;
0221 }
0222 return -1;
0223 }
0224 else
0225 {
0226 return (*m_interpolator)(x);
0227 }
0228 }
0229
0230 inline Real prime(Real x) const
0231 {
0232 static_assert(p > 2, "The 3-vanishing moment Daubechies wavelet is the first which is continuously differentiable.");
0233 if (x <= -p + 1 || x >= p)
0234 {
0235 return 0;
0236 }
0237 return m_interpolator->prime(x);
0238 }
0239
0240 inline Real double_prime(Real x) const
0241 {
0242 static_assert(p >= 6, "Second derivatives of Daubechies wavelets require at least 6 vanishing moments.");
0243 if (x <= -p + 1 || x >= p)
0244 {
0245 return Real(0);
0246 }
0247 return m_interpolator->double_prime(x);
0248 }
0249
0250 std::pair<Real, Real> support() const
0251 {
0252 return std::make_pair(Real(-p + 1), Real(p));
0253 }
0254
0255 int64_t bytes() const
0256 {
0257 return m_interpolator->bytes() + sizeof(*this);
0258 }
0259
0260 private:
0261 std::shared_ptr<interpolator_type> m_interpolator;
0262 };
0263
0264 }
0265
0266 #endif