cardinal_cubic_b_spline_example.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. // Copyright Nicholas Thompson 2017.
  2. // Copyright Paul A. Bristow 2017.
  3. // Copyright John Maddock 2017.
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE_1_0.txt or
  6. // copy at http://www.boost.org/LICENSE_1_0.txt).
  7. #include <iostream>
  8. #include <limits>
  9. #include <vector>
  10. #include <algorithm>
  11. #include <iomanip>
  12. #include <iterator>
  13. #include <cmath>
  14. #include <random>
  15. #include <cstdint>
  16. #include <boost/random/uniform_real_distribution.hpp>
  17. #include <boost/math/tools/roots.hpp>
  18. //[cubic_b_spline_example
  19. /*`This example demonstrates how to use the cubic b spline interpolator for regularly spaced data.
  20. */
  21. #include <boost/math/interpolators/cardinal_cubic_b_spline.hpp>
  22. int main()
  23. {
  24. // We begin with an array of samples:
  25. std::vector<double> v(500);
  26. // And decide on a stepsize:
  27. double step = 0.01;
  28. // Initialize the vector with a function we'd like to interpolate:
  29. for (size_t i = 0; i < v.size(); ++i)
  30. {
  31. v[i] = sin(i*step);
  32. }
  33. // We could define an arbitrary start time, but for now we'll just use 0:
  34. boost::math::interpolators::cardinal_cubic_b_spline<double> spline(v.data(), v.size(), 0 /* start time */, step);
  35. // Now we can evaluate the spline wherever we please.
  36. std::mt19937 gen;
  37. boost::random::uniform_real_distribution<double> absissa(0, v.size()*step);
  38. for (size_t i = 0; i < 10; ++i)
  39. {
  40. double x = absissa(gen);
  41. std::cout << "sin(" << x << ") = " << sin(x) << ", spline interpolation gives " << spline(x) << std::endl;
  42. std::cout << "cos(" << x << ") = " << cos(x) << ", spline derivative interpolation gives " << spline.prime(x) << std::endl;
  43. }
  44. // The next example is less trivial:
  45. // We will try to figure out when the population of the United States crossed 100 million.
  46. // Since the census is taken every 10 years, the data is equally spaced, so we can use the cubic b spline.
  47. // Data taken from https://en.wikipedia.org/wiki/United_States_Census
  48. // We'll start at the year 1860:
  49. double t0 = 1860;
  50. double time_step = 10;
  51. std::vector<double> population{31443321, /* 1860 */
  52. 39818449, /* 1870 */
  53. 50189209, /* 1880 */
  54. 62947714, /* 1890 */
  55. 76212168, /* 1900 */
  56. 92228496, /* 1910 */
  57. 106021537, /* 1920 */
  58. 122775046, /* 1930 */
  59. 132164569, /* 1940 */
  60. 150697361, /* 1950 */
  61. 179323175};/* 1960 */
  62. // An eyeball estimate indicates that the population crossed 100 million around 1915.
  63. // Let's see what interpolation says:
  64. boost::math::interpolators::cardinal_cubic_b_spline<double> p(population.data(), population.size(), t0, time_step);
  65. // Now create a function which has a zero at p = 100,000,000:
  66. auto f = [=](double t){ return p(t) - 100000000; };
  67. // Boost includes a bisection algorithm, which is robust, though not as fast as some others
  68. // we provide, but let's try that first. We need a termination condition for it, which
  69. // takes the two endpoints of the range and returns either true (stop) or false (keep going),
  70. // we could use a predefined one such as boost::math::tools::eps_tolerance<double>, but that
  71. // won't stop until we have full double precision which is overkill, since we just need the
  72. // endpoint to yield the same month. While we're at it, we'll keep track of the number of
  73. // iterations required too, though this is strictly optional:
  74. auto termination = [](double left, double right)
  75. {
  76. double left_month = std::round((left - std::floor(left)) * 12 + 1);
  77. double right_month = std::round((right - std::floor(right)) * 12 + 1);
  78. return (left_month == right_month) && (std::floor(left) == std::floor(right));
  79. };
  80. std::uintmax_t iterations = 1000;
  81. auto result = boost::math::tools::bisect(f, 1910.0, 1920.0, termination, iterations);
  82. auto time = result.first; // termination condition ensures that both endpoints yield the same result
  83. auto month = std::round((time - std::floor(time))*12 + 1);
  84. auto year = std::floor(time);
  85. std::cout << "The population of the United States surpassed 100 million on the ";
  86. std::cout << month << "th month of " << year << std::endl;
  87. std::cout << "Found in " << iterations << " iterations" << std::endl;
  88. // Since the cubic B spline offers the first derivative, we could equally have used Newton iterations,
  89. // this takes "number of bits correct" as a termination condition - 20 should be plenty for what we need,
  90. // and once again, we track how many iterations are taken:
  91. auto f_n = [=](double t) { return std::make_pair(p(t) - 100000000, p.prime(t)); };
  92. iterations = 1000;
  93. time = boost::math::tools::newton_raphson_iterate(f_n, 1910.0, 1900.0, 2000.0, 20, iterations);
  94. month = std::round((time - std::floor(time))*12 + 1);
  95. year = std::floor(time);
  96. std::cout << "The population of the United States surpassed 100 million on the ";
  97. std::cout << month << "th month of " << year << std::endl;
  98. std::cout << "Found in " << iterations << " iterations" << std::endl;
  99. }
  100. //] [/cubic_b_spline_example]
  101. //[cubic_b_spline_example_out
  102. /*` Program output is:
  103. [pre
  104. sin(4.07362) = -0.802829, spline interpolation gives - 0.802829
  105. cos(4.07362) = -0.596209, spline derivative interpolation gives - 0.596209
  106. sin(0.677385) = 0.626758, spline interpolation gives 0.626758
  107. cos(0.677385) = 0.779214, spline derivative interpolation gives 0.779214
  108. sin(4.52896) = -0.983224, spline interpolation gives - 0.983224
  109. cos(4.52896) = -0.182402, spline derivative interpolation gives - 0.182402
  110. sin(4.17504) = -0.85907, spline interpolation gives - 0.85907
  111. cos(4.17504) = -0.511858, spline derivative interpolation gives - 0.511858
  112. sin(0.634934) = 0.593124, spline interpolation gives 0.593124
  113. cos(0.634934) = 0.805111, spline derivative interpolation gives 0.805111
  114. sin(4.84434) = -0.991307, spline interpolation gives - 0.991307
  115. cos(4.84434) = 0.131567, spline derivative interpolation gives 0.131567
  116. sin(4.56688) = -0.989432, spline interpolation gives - 0.989432
  117. cos(4.56688) = -0.144997, spline derivative interpolation gives - 0.144997
  118. sin(1.10517) = 0.893541, spline interpolation gives 0.893541
  119. cos(1.10517) = 0.448982, spline derivative interpolation gives 0.448982
  120. sin(3.1618) = -0.0202022, spline interpolation gives - 0.0202022
  121. cos(3.1618) = -0.999796, spline derivative interpolation gives - 0.999796
  122. sin(1.54084) = 0.999551, spline interpolation gives 0.999551
  123. cos(1.54084) = 0.0299566, spline derivative interpolation gives 0.0299566
  124. The population of the United States surpassed 100 million on the 11th month of 1915
  125. Found in 12 iterations
  126. The population of the United States surpassed 100 million on the 11th month of 1915
  127. Found in 3 iterations
  128. ]
  129. */
  130. //]