daubechies_scaling.hpp 15 KB

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