daubechies_scaling.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. /*
  2. * Copyright Nick Thompson, John Maddock 2020
  3. * Use, modification and distribution are subject to the
  4. * Boost Software License, Version 1.0. (See accompanying file
  5. * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. */
  7. #ifndef BOOST_MATH_SPECIAL_DAUBECHIES_SCALING_HPP
  8. #define BOOST_MATH_SPECIAL_DAUBECHIES_SCALING_HPP
  9. #include <cstdint>
  10. #include <cstring>
  11. #include <cmath>
  12. #include <vector>
  13. #include <array>
  14. #include <thread>
  15. #include <future>
  16. #include <iostream>
  17. #include <memory>
  18. #include <boost/math/special_functions/detail/daubechies_scaling_integer_grid.hpp>
  19. #include <boost/math/filters/daubechies.hpp>
  20. #include <boost/math/interpolators/detail/cubic_hermite_detail.hpp>
  21. #include <boost/math/interpolators/detail/quintic_hermite_detail.hpp>
  22. #include <boost/math/interpolators/detail/septic_hermite_detail.hpp>
  23. #include <boost/math/tools/is_standalone.hpp>
  24. #ifndef BOOST_MATH_STANDALONE
  25. # include <boost/config.hpp>
  26. # ifdef BOOST_MATH_NO_CXX17_IF_CONSTEXPR
  27. # error "The header <boost/special_functions/daubechies_scaling.hpp> can only be used in C++17 and later."
  28. # endif
  29. #endif
  30. namespace boost::math {
  31. template<class Real, int p, int order>
  32. std::vector<Real> daubechies_scaling_dyadic_grid(int64_t j_max)
  33. {
  34. using std::isnan;
  35. using std::sqrt;
  36. auto c = boost::math::filters::daubechies_scaling_filter<Real, p>();
  37. Real scale = sqrt(static_cast<Real>(2))*(1 << order);
  38. for (auto & x : c)
  39. {
  40. x *= scale;
  41. }
  42. auto phik = detail::daubechies_scaling_integer_grid<Real, p, order>();
  43. // Maximum sensible j for 32 bit floats is j_max = 22:
  44. if constexpr (std::is_same_v<Real, float>)
  45. {
  46. if (j_max > 23)
  47. {
  48. throw std::logic_error("Requested dyadic grid more dense than number of representables on the interval.");
  49. }
  50. }
  51. std::vector<Real> v(2*p + (2*p-1)*((1<<j_max) -1), std::numeric_limits<Real>::quiet_NaN());
  52. v[0] = 0;
  53. v[v.size()-1] = 0;
  54. for (int64_t i = 0; i < static_cast<int64_t>(phik.size()); ++i) {
  55. v[i*(1uLL<<j_max)] = phik[i];
  56. }
  57. for (int64_t j = 1; j <= j_max; ++j)
  58. {
  59. int64_t k_max = v.size()/(int64_t(1) << (j_max-j));
  60. for (int64_t k = 1; k < k_max; k += 2)
  61. {
  62. // Where this value will go:
  63. int64_t delivery_idx = k*(1uLL << (j_max-j));
  64. // This is a nice check, but we've tested this exhaustively, and it's an expensive check:
  65. //if (delivery_idx >= static_cast<int64_t>(v.size())) {
  66. // std::cerr << "Delivery index out of range!\n";
  67. // continue;
  68. //}
  69. Real term = 0;
  70. for (int64_t l = 0; l < static_cast<int64_t>(c.size()); ++l)
  71. {
  72. int64_t idx = k*(int64_t(1) << (j_max - j + 1)) - l*(int64_t(1) << j_max);
  73. if (idx < 0)
  74. {
  75. break;
  76. }
  77. if (idx < static_cast<int64_t>(v.size()))
  78. {
  79. term += c[l]*v[idx];
  80. }
  81. }
  82. // Again, another nice check:
  83. //if (!isnan(v[delivery_idx])) {
  84. // std::cerr << "Delivery index already populated!, = " << v[delivery_idx] << "\n";
  85. // std::cerr << "would overwrite with " << term << "\n";
  86. //}
  87. v[delivery_idx] = term;
  88. }
  89. }
  90. return v;
  91. }
  92. namespace detail {
  93. template<class RandomAccessContainer>
  94. class matched_holder {
  95. public:
  96. using Real = typename RandomAccessContainer::value_type;
  97. matched_holder(RandomAccessContainer && y, RandomAccessContainer && dydx, int grid_refinements, Real x0) : x0_{x0}, y_{std::move(y)}, dy_{std::move(dydx)}
  98. {
  99. inv_h_ = (1 << grid_refinements);
  100. Real h = 1/inv_h_;
  101. for (auto & dy : dy_)
  102. {
  103. dy *= h;
  104. }
  105. }
  106. inline Real operator()(Real x) const
  107. {
  108. using std::floor;
  109. using std::sqrt;
  110. // This is the exact Holder exponent, but it's pessimistic almost everywhere!
  111. // It's only exactly right at dyadic rationals.
  112. //Real const alpha = 2 - log(1+sqrt(Real(3)))/log(Real(2));
  113. // We're gonna use alpha = 1/2, rather than 0.5500...
  114. Real s = (x-x0_)*inv_h_;
  115. Real ii = floor(s);
  116. auto i = static_cast<decltype(y_.size())>(ii);
  117. Real t = s - ii;
  118. Real dphi = dy_[i+1];
  119. Real diff = y_[i+1] - y_[i];
  120. return y_[i] + (2*dphi - diff)*t + 2*sqrt(t)*(diff-dphi);
  121. }
  122. int64_t bytes() const
  123. {
  124. return 2*y_.size()*sizeof(Real) + sizeof(*this);
  125. }
  126. private:
  127. Real x0_;
  128. Real inv_h_;
  129. RandomAccessContainer y_;
  130. RandomAccessContainer dy_;
  131. };
  132. template<class RandomAccessContainer>
  133. class matched_holder_aos {
  134. public:
  135. using Point = typename RandomAccessContainer::value_type;
  136. using Real = typename Point::value_type;
  137. matched_holder_aos(RandomAccessContainer && data, int grid_refinements, Real x0) : x0_{x0}, data_{std::move(data)}
  138. {
  139. inv_h_ = Real(1uLL << grid_refinements);
  140. Real h = 1/inv_h_;
  141. for (auto & datum : data_)
  142. {
  143. datum[1] *= h;
  144. }
  145. }
  146. inline Real operator()(Real x) const
  147. {
  148. using std::floor;
  149. using std::sqrt;
  150. Real s = (x-x0_)*inv_h_;
  151. Real ii = floor(s);
  152. auto i = static_cast<decltype(data_.size())>(ii);
  153. Real t = s - ii;
  154. Real y0 = data_[i][0];
  155. Real y1 = data_[i+1][0];
  156. Real dphi = data_[i+1][1];
  157. Real diff = y1 - y0;
  158. return y0 + (2*dphi - diff)*t + 2*sqrt(t)*(diff-dphi);
  159. }
  160. int64_t bytes() const
  161. {
  162. return data_.size()*data_[0].size()*sizeof(Real) + sizeof(*this);
  163. }
  164. private:
  165. Real x0_;
  166. Real inv_h_;
  167. RandomAccessContainer data_;
  168. };
  169. template<class RandomAccessContainer>
  170. class linear_interpolation {
  171. public:
  172. using Real = typename RandomAccessContainer::value_type;
  173. linear_interpolation(RandomAccessContainer && y, RandomAccessContainer && dydx, int grid_refinements) : y_{std::move(y)}, dydx_{std::move(dydx)}
  174. {
  175. s_ = (1 << grid_refinements);
  176. }
  177. inline Real operator()(Real x) const
  178. {
  179. using std::floor;
  180. Real y = x*s_;
  181. Real k = floor(y);
  182. int64_t kk = static_cast<int64_t>(k);
  183. Real t = y - k;
  184. return (1-t)*y_[kk] + t*y_[kk+1];
  185. }
  186. inline Real prime(Real x) const
  187. {
  188. using std::floor;
  189. Real y = x*s_;
  190. Real k = floor(y);
  191. int64_t kk = static_cast<int64_t>(k);
  192. Real t = y - k;
  193. return static_cast<Real>((Real(1)-t)*dydx_[kk] + t*dydx_[kk+1]);
  194. }
  195. int64_t bytes() const
  196. {
  197. return (1 + y_.size() + dydx_.size())*sizeof(Real) + sizeof(y_) + sizeof(dydx_);
  198. }
  199. private:
  200. Real s_;
  201. RandomAccessContainer y_;
  202. RandomAccessContainer dydx_;
  203. };
  204. template<class RandomAccessContainer>
  205. class linear_interpolation_aos {
  206. public:
  207. using Point = typename RandomAccessContainer::value_type;
  208. using Real = typename Point::value_type;
  209. linear_interpolation_aos(RandomAccessContainer && data, int grid_refinements, Real x0) : x0_{x0}, data_{std::move(data)}
  210. {
  211. s_ = Real(1uLL << grid_refinements);
  212. }
  213. inline Real operator()(Real x) const
  214. {
  215. using std::floor;
  216. Real y = (x-x0_)*s_;
  217. Real k = floor(y);
  218. int64_t kk = static_cast<int64_t>(k);
  219. Real t = y - k;
  220. return (t != 0) ? (1-t)*data_[kk][0] + t*data_[kk+1][0] : data_[kk][0];
  221. }
  222. inline Real prime(Real x) const
  223. {
  224. using std::floor;
  225. Real y = (x-x0_)*s_;
  226. Real k = floor(y);
  227. int64_t kk = static_cast<int64_t>(k);
  228. Real t = y - k;
  229. return t != 0 ? (1-t)*data_[kk][1] + t*data_[kk+1][1] : data_[kk][1];
  230. }
  231. int64_t bytes() const
  232. {
  233. return sizeof(*this) + data_.size()*data_[0].size()*sizeof(Real);
  234. }
  235. private:
  236. Real x0_;
  237. Real s_;
  238. RandomAccessContainer data_;
  239. };
  240. template <class T>
  241. struct daubechies_eval_type
  242. {
  243. using type = T;
  244. static const std::vector<T>& vector_cast(const std::vector<T>& v) { return v; }
  245. };
  246. template <>
  247. struct daubechies_eval_type<float>
  248. {
  249. using type = double;
  250. inline static std::vector<float> vector_cast(const std::vector<double>& v)
  251. {
  252. std::vector<float> result(v.size());
  253. for (unsigned i = 0; i < v.size(); ++i)
  254. result[i] = static_cast<float>(v[i]);
  255. return result;
  256. }
  257. };
  258. template <>
  259. struct daubechies_eval_type<double>
  260. {
  261. using type = long double;
  262. inline static std::vector<double> vector_cast(const std::vector<long double>& v)
  263. {
  264. std::vector<double> result(v.size());
  265. for (unsigned i = 0; i < v.size(); ++i)
  266. result[i] = static_cast<double>(v[i]);
  267. return result;
  268. }
  269. };
  270. struct null_interpolator
  271. {
  272. template <class T>
  273. T operator()(const T&)
  274. {
  275. return 1;
  276. }
  277. };
  278. } // namespace detail
  279. template<class Real, int p>
  280. class daubechies_scaling {
  281. //
  282. // Some type manipulation so we know the type of the interpolator, and the vector type it requires:
  283. //
  284. using vector_type = std::vector<std::array<Real, p < 6 ? 2 : p < 10 ? 3 : 4>>;
  285. //
  286. // List our interpolators:
  287. //
  288. using interpolator_list = std::tuple<
  289. detail::null_interpolator, detail::matched_holder_aos<vector_type>, detail::linear_interpolation_aos<vector_type>,
  290. interpolators::detail::cardinal_cubic_hermite_detail_aos<vector_type>, interpolators::detail::cardinal_quintic_hermite_detail_aos<vector_type>,
  291. interpolators::detail::cardinal_septic_hermite_detail_aos<vector_type> >;
  292. //
  293. // Select the one we need:
  294. //
  295. using interpolator_type = std::tuple_element_t<
  296. p == 1 ? 0 :
  297. p == 2 ? 1 :
  298. p == 3 ? 2 :
  299. p <= 5 ? 3 :
  300. p <= 9 ? 4 : 5, interpolator_list>;
  301. public:
  302. daubechies_scaling(int grid_refinements = -1)
  303. {
  304. static_assert(p < 20, "Daubechies scaling functions are only implemented for p < 20.");
  305. static_assert(p > 0, "Daubechies scaling functions must have at least 1 vanishing moment.");
  306. if constexpr (p == 1)
  307. {
  308. return;
  309. }
  310. else {
  311. if (grid_refinements < 0)
  312. {
  313. if constexpr (std::is_same_v<Real, float>)
  314. {
  315. if (grid_refinements == -2)
  316. {
  317. // Control absolute error:
  318. // p= 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
  319. constexpr 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 };
  320. grid_refinements = r[p];
  321. }
  322. else
  323. {
  324. // Control relative error:
  325. // p= 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
  326. constexpr 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 };
  327. grid_refinements = r[p];
  328. }
  329. }
  330. else if constexpr (std::is_same_v<Real, double>)
  331. {
  332. // p= 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
  333. constexpr std::array<int, 20> r{ -1, -1, 21, 21, 21, 21, 21, 21, 21, 21, 20, 20, 19, 19, 18, 18, 18, 18, 18, 18 };
  334. grid_refinements = r[p];
  335. }
  336. else
  337. {
  338. grid_refinements = 21;
  339. }
  340. }
  341. // Compute the refined grid:
  342. // In fact for float precision I know the grid must be computed in double precision and then cast back down, or else parts of the support are systematically inaccurate.
  343. std::future<std::vector<Real>> t0 = std::async(std::launch::async, [&grid_refinements]() {
  344. // Computing in higher precision and downcasting is essential for 1ULP evaluation in float precision:
  345. auto v = daubechies_scaling_dyadic_grid<typename detail::daubechies_eval_type<Real>::type, p, 0>(grid_refinements);
  346. return detail::daubechies_eval_type<Real>::vector_cast(v);
  347. });
  348. // Compute the derivative of the refined grid:
  349. std::future<std::vector<Real>> t1 = std::async(std::launch::async, [&grid_refinements]() {
  350. auto v = daubechies_scaling_dyadic_grid<typename detail::daubechies_eval_type<Real>::type, p, 1>(grid_refinements);
  351. return detail::daubechies_eval_type<Real>::vector_cast(v);
  352. });
  353. // if necessary, compute the second and third derivative:
  354. std::vector<Real> d2ydx2;
  355. std::vector<Real> d3ydx3;
  356. if constexpr (p >= 6) {
  357. std::future<std::vector<Real>> t3 = std::async(std::launch::async, [&grid_refinements]() {
  358. auto v = daubechies_scaling_dyadic_grid<typename detail::daubechies_eval_type<Real>::type, p, 2>(grid_refinements);
  359. return detail::daubechies_eval_type<Real>::vector_cast(v);
  360. });
  361. if constexpr (p >= 10) {
  362. std::future<std::vector<Real>> t4 = std::async(std::launch::async, [&grid_refinements]() {
  363. auto v = daubechies_scaling_dyadic_grid<typename detail::daubechies_eval_type<Real>::type, p, 3>(grid_refinements);
  364. return detail::daubechies_eval_type<Real>::vector_cast(v);
  365. });
  366. d3ydx3 = t4.get();
  367. }
  368. d2ydx2 = t3.get();
  369. }
  370. auto y = t0.get();
  371. auto dydx = t1.get();
  372. if constexpr (p >= 2)
  373. {
  374. vector_type data(y.size());
  375. for (size_t i = 0; i < y.size(); ++i)
  376. {
  377. data[i][0] = y[i];
  378. data[i][1] = dydx[i];
  379. if constexpr (p >= 6)
  380. data[i][2] = d2ydx2[i];
  381. if constexpr (p >= 10)
  382. data[i][3] = d3ydx3[i];
  383. }
  384. if constexpr (p <= 3)
  385. m_interpolator = std::make_shared<interpolator_type>(std::move(data), grid_refinements, Real(0));
  386. else
  387. m_interpolator = std::make_shared<interpolator_type>(std::move(data), Real(0), Real(1) / (1 << grid_refinements));
  388. }
  389. else
  390. m_interpolator = std::make_shared<detail::null_interpolator>();
  391. }
  392. }
  393. inline Real operator()(Real x) const
  394. {
  395. if (x <= 0 || x >= 2*p-1)
  396. {
  397. return 0;
  398. }
  399. return (*m_interpolator)(x);
  400. }
  401. inline Real prime(Real x) const
  402. {
  403. static_assert(p > 2, "The 3-vanishing moment Daubechies scaling function is the first which is continuously differentiable.");
  404. if (x <= Real(0) || x >= 2*p-1)
  405. {
  406. return 0;
  407. }
  408. return m_interpolator->prime(x);
  409. }
  410. inline Real double_prime(Real x) const
  411. {
  412. static_assert(p >= 6, "Second derivatives require at least 6 vanishing moments.");
  413. if (x <= 0 || x >= 2*p - 1)
  414. {
  415. return Real(0);
  416. }
  417. return m_interpolator->double_prime(x);
  418. }
  419. std::pair<Real, Real> support() const
  420. {
  421. return {Real(0), Real(2*p-1)};
  422. }
  423. int64_t bytes() const
  424. {
  425. return m_interpolator->bytes() + sizeof(m_interpolator);
  426. }
  427. private:
  428. std::shared_ptr<interpolator_type> m_interpolator;
  429. };
  430. }
  431. #endif