Integration.h 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/RootsPolynomial.h>
  9. #include <array>
  10. #include <functional>
  11. namespace WwiseGTE
  12. {
  13. template <typename Real>
  14. class Integration
  15. {
  16. public:
  17. // A simple algorithm, but slow to converge as the number of samples
  18. // is increased. The 'numSamples' needs to be two or larger.
  19. static Real TrapezoidRule(int numSamples, Real a, Real b,
  20. std::function<Real(Real)> const& integrand)
  21. {
  22. Real h = (b - a) / (Real)(numSamples - 1);
  23. Real result = (Real)0.5 * (integrand(a) + integrand(b));
  24. for (int i = 1; i <= numSamples - 2; ++i)
  25. {
  26. result += integrand(a + i * h);
  27. }
  28. result *= h;
  29. return result;
  30. }
  31. // The trapezoid rule is used to generate initial estimates, but then
  32. // Richardson extrapolation is used to improve the estimates. This is
  33. // preferred over TrapezoidRule. The 'order' must be positive.
  34. static Real Romberg(int order, Real a, Real b,
  35. std::function<Real(Real)> const& integrand)
  36. {
  37. Real const half = (Real)0.5;
  38. std::vector<std::array<Real, 2>> rom(order);
  39. Real h = b - a;
  40. rom[0][0] = half * h * (integrand(a) + integrand(b));
  41. for (int i0 = 2, p0 = 1; i0 <= order; ++i0, p0 *= 2, h *= half)
  42. {
  43. // Approximations via the trapezoid rule.
  44. Real sum = (Real)0;
  45. int i1;
  46. for (i1 = 1; i1 <= p0; ++i1)
  47. {
  48. sum += integrand(a + h * (i1 - half));
  49. }
  50. // Richardson extrapolation.
  51. rom[0][1] = half * (rom[0][0] + h * sum);
  52. for (int i2 = 1, p2 = 4; i2 < i0; ++i2, p2 *= 4)
  53. {
  54. rom[i2][1] = (p2 * rom[i2 - 1][1] - rom[i2 - 1][0]) / (p2 - 1);
  55. }
  56. for (i1 = 0; i1 < i0; ++i1)
  57. {
  58. rom[i1][0] = rom[i1][1];
  59. }
  60. }
  61. Real result = rom[order - 1][0];
  62. return result;
  63. }
  64. // Gaussian quadrature estimates the integral of a function f(x)
  65. // defined on [-1,1] using
  66. // integral_{-1}^{1} f(t) dt = sum_{i=0}^{n-1} c[i]*f(r[i])
  67. // where r[i] are the roots to the Legendre polynomial p(t) of degree
  68. // n and
  69. // c[i] = integral_{-1}^{1} prod_{j=0,j!=i} (t-r[j]/(r[i]-r[j]) dt
  70. // To integrate over [a,b], a transformation to [-1,1] is applied
  71. // internally: x - ((b-a)*t + (b+a))/2. The Legendre polynomials are
  72. // generated by
  73. // P[0](x) = 1, P[1](x) = x,
  74. // P[k](x) = ((2*k-1)*x*P[k-1](x) - (k-1)*P[k-2](x))/k, k >= 2
  75. // Implementing the polynomial generation is simple, and computing the
  76. // roots requires a numerical method for finding polynomial roots.
  77. // The challenging task is to develop an efficient algorithm for
  78. // computing the coefficients c[i] for a specified degree. The
  79. // 'degree' must be two or larger.
  80. static void ComputeQuadratureInfo(int degree, std::vector<Real>& roots,
  81. std::vector<Real>& coefficients)
  82. {
  83. Real const zero = (Real)0;
  84. Real const one = (Real)1;
  85. Real const half = (Real)0.5;
  86. std::vector<std::vector<Real>> poly(degree + 1);
  87. poly[0].resize(1);
  88. poly[0][0] = one;
  89. poly[1].resize(2);
  90. poly[1][0] = zero;
  91. poly[1][1] = one;
  92. for (int n = 2; n <= degree; ++n)
  93. {
  94. Real mult0 = (Real)(n - 1) / (Real)n;
  95. Real mult1 = (Real)(2 * n - 1) / (Real)n;
  96. poly[n].resize(n + 1);
  97. poly[n][0] = -mult0 * poly[n - 2][0];
  98. for (int i = 1; i <= n - 2; ++i)
  99. {
  100. poly[n][i] = mult1 * poly[n - 1][i - 1] - mult0 * poly[n - 2][i];
  101. }
  102. poly[n][n - 1] = mult1 * poly[n - 1][n - 2];
  103. poly[n][n] = mult1 * poly[n - 1][n - 1];
  104. }
  105. roots.resize(degree);
  106. RootsPolynomial<Real>::Find(degree, &poly[degree][0], 2048, &roots[0]);
  107. coefficients.resize(roots.size());
  108. size_t n = roots.size() - 1;
  109. std::vector<Real> subroots(n);
  110. for (size_t i = 0; i < roots.size(); ++i)
  111. {
  112. Real denominator = (Real)1;
  113. for (size_t j = 0, k = 0; j < roots.size(); ++j)
  114. {
  115. if (j != i)
  116. {
  117. subroots[k++] = roots[j];
  118. denominator *= roots[i] - roots[j];
  119. }
  120. }
  121. std::array<Real, 2> delta =
  122. {
  123. -one - subroots.back(),
  124. +one - subroots.back()
  125. };
  126. std::vector<std::array<Real, 2>> weights(n);
  127. weights[0][0] = half * delta[0] * delta[0];
  128. weights[0][1] = half * delta[1] * delta[1];
  129. for (size_t k = 1; k < n; ++k)
  130. {
  131. Real dk = (Real)k;
  132. Real mult = -dk / (dk + (Real)2);
  133. weights[k][0] = mult * delta[0] * weights[k - 1][0];
  134. weights[k][1] = mult * delta[1] * weights[k - 1][1];
  135. }
  136. struct Info
  137. {
  138. int numBits;
  139. std::array<Real, 2> product;
  140. };
  141. int numElements = (1 << static_cast<unsigned int>(n - 1));
  142. std::vector<Info> info(numElements);
  143. info[0].numBits = 0;
  144. info[0].product[0] = one;
  145. info[0].product[1] = one;
  146. for (int ipow = 1, r = 0; ipow < numElements; ipow <<= 1, ++r)
  147. {
  148. info[ipow].numBits = 1;
  149. info[ipow].product[0] = -one - subroots[r];
  150. info[ipow].product[1] = +one - subroots[r];
  151. for (int m = 1, j = ipow + 1; m < ipow; ++m, ++j)
  152. {
  153. info[j].numBits = info[m].numBits + 1;
  154. info[j].product[0] =
  155. info[ipow].product[0] * info[m].product[0];
  156. info[j].product[1] =
  157. info[ipow].product[1] * info[m].product[1];
  158. }
  159. }
  160. std::vector<std::array<Real, 2>> sum(n);
  161. std::array<Real, 2> zero2 = { zero, zero };
  162. std::fill(sum.begin(), sum.end(), zero2);
  163. for (size_t k = 0; k < info.size(); ++k)
  164. {
  165. sum[info[k].numBits][0] += info[k].product[0];
  166. sum[info[k].numBits][1] += info[k].product[1];
  167. }
  168. std::array<Real, 2> total = zero2;
  169. for (size_t k = 0; k < n; ++k)
  170. {
  171. total[0] += weights[n - 1 - k][0] * sum[k][0];
  172. total[1] += weights[n - 1 - k][1] * sum[k][1];
  173. }
  174. coefficients[i] = (total[1] - total[0]) / denominator;
  175. }
  176. }
  177. static Real GaussianQuadrature(std::vector<Real> const& roots,
  178. std::vector<Real>const& coefficients, Real a, Real b,
  179. std::function<Real(Real)> const& integrand)
  180. {
  181. Real const half = (Real)0.5;
  182. Real radius = half * (b - a);
  183. Real center = half * (b + a);
  184. Real result = (Real)0;
  185. for (size_t i = 0; i < roots.size(); ++i)
  186. {
  187. result += coefficients[i] * integrand(radius * roots[i] + center);
  188. }
  189. result *= radius;
  190. return result;
  191. }
  192. };
  193. }