123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- // David Eberly, Geometric Tools, Redmond WA 98052
- // Copyright (c) 1998-2020
- // Distributed under the Boost Software License, Version 1.0.
- // https://www.boost.org/LICENSE_1_0.txt
- // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
- // Version: 4.0.2019.08.13
- #pragma once
- #include <Mathematics/RootsPolynomial.h>
- #include <array>
- #include <functional>
- namespace WwiseGTE
- {
- template <typename Real>
- class Integration
- {
- public:
- // A simple algorithm, but slow to converge as the number of samples
- // is increased. The 'numSamples' needs to be two or larger.
- static Real TrapezoidRule(int numSamples, Real a, Real b,
- std::function<Real(Real)> const& integrand)
- {
- Real h = (b - a) / (Real)(numSamples - 1);
- Real result = (Real)0.5 * (integrand(a) + integrand(b));
- for (int i = 1; i <= numSamples - 2; ++i)
- {
- result += integrand(a + i * h);
- }
- result *= h;
- return result;
- }
- // The trapezoid rule is used to generate initial estimates, but then
- // Richardson extrapolation is used to improve the estimates. This is
- // preferred over TrapezoidRule. The 'order' must be positive.
- static Real Romberg(int order, Real a, Real b,
- std::function<Real(Real)> const& integrand)
- {
- Real const half = (Real)0.5;
- std::vector<std::array<Real, 2>> rom(order);
- Real h = b - a;
- rom = half * h * (integrand(a) + integrand(b));
- for (int i0 = 2, p0 = 1; i0 <= order; ++i0, p0 *= 2, h *= half)
- {
- // Approximations via the trapezoid rule.
- Real sum = (Real)0;
- int i1;
- for (i1 = 1; i1 <= p0; ++i1)
- {
- sum += integrand(a + h * (i1 - half));
- }
- // Richardson extrapolation.
- rom = half * (rom + h * sum);
- for (int i2 = 1, p2 = 4; i2 < i0; ++i2, p2 *= 4)
- {
- rom = (p2 * rom - rom) / (p2 - 1);
- }
- for (i1 = 0; i1 < i0; ++i1)
- {
- rom = rom;
- }
- }
- Real result = rom;
- return result;
- }
- // Gaussian quadrature estimates the integral of a function f(x)
- // defined on using
- // integral_{-1}^{1} f(t) dt = sum_{i=0}^{n-1} c*f(r)
- // where r are the roots to the Legendre polynomial p(t) of degree
- // n and
- // c = integral_{-1}^{1} prod_{j=0,j!=i} (t-r/(r-r) dt
- // To integrate over , a transformation to is applied
- // internally: x - ((b-a)*t + (b+a))/2. The Legendre polynomials are
- // generated by
- // P(x) = 1, P(x) = x,
- // P(x) = ((2*k-1)*x*P(x) - (k-1)*P(x))/k, k >= 2
- // Implementing the polynomial generation is simple, and computing the
- // roots requires a numerical method for finding polynomial roots.
- // The challenging task is to develop an efficient algorithm for
- // computing the coefficients c for a specified degree. The
- // 'degree' must be two or larger.
- static void ComputeQuadratureInfo(int degree, std::vector<Real>& roots,
- std::vector<Real>& coefficients)
- {
- Real const zero = (Real)0;
- Real const one = (Real)1;
- Real const half = (Real)0.5;
- std::vector<std::vector<Real>> poly(degree + 1);
- poly.resize(1);
- poly = one;
- poly.resize(2);
- poly = zero;
- poly = one;
- for (int n = 2; n <= degree; ++n)
- {
- Real mult0 = (Real)(n - 1) / (Real)n;
- Real mult1 = (Real)(2 * n - 1) / (Real)n;
- poly.resize(n + 1);
- poly = -mult0 * poly;
- for (int i = 1; i <= n - 2; ++i)
- {
- poly = mult1 * poly - mult0 * poly;
- }
- poly = mult1 * poly;
- poly = mult1 * poly;
- }
- roots.resize(degree);
- RootsPolynomial<Real>::Find(degree, &poly, 2048, &roots);
- coefficients.resize(roots.size());
- size_t n = roots.size() - 1;
- std::vector<Real> subroots(n);
- for (size_t i = 0; i < roots.size(); ++i)
- {
- Real denominator = (Real)1;
- for (size_t j = 0, k = 0; j < roots.size(); ++j)
- {
- if (j != i)
- {
- subroots = roots;
- denominator *= roots - roots;
- }
- }
- std::array<Real, 2> delta =
- {
- -one - subroots.back(),
- +one - subroots.back()
- };
- std::vector<std::array<Real, 2>> weights(n);
- weights = half * delta * delta;
- weights = half * delta * delta;
- for (size_t k = 1; k < n; ++k)
- {
- Real dk = (Real)k;
- Real mult = -dk / (dk + (Real)2);
- weights = mult * delta * weights;
- weights = mult * delta * weights;
- }
- struct Info
- {
- int numBits;
- std::array<Real, 2> product;
- };
- int numElements = (1 << static_cast<unsigned int>(n - 1));
- std::vector<Info> info(numElements);
- info.numBits = 0;
- info.product = one;
- info.product = one;
- for (int ipow = 1, r = 0; ipow < numElements; ipow <<= 1, ++r)
- {
- info.numBits = 1;
- info.product = -one - subroots;
- info.product = +one - subroots;
- for (int m = 1, j = ipow + 1; m < ipow; ++m, ++j)
- {
- info.numBits = info.numBits + 1;
- info.product =
- info.product * info.product;
- info.product =
- info.product * info.product;
- }
- }
- std::vector<std::array<Real, 2>> sum(n);
- std::array<Real, 2> zero2 = { zero, zero };
- std::fill(sum.begin(), sum.end(), zero2);
- for (size_t k = 0; k < info.size(); ++k)
- {
- sum += info.product;
- sum += info.product;
- }
- std::array<Real, 2> total = zero2;
- for (size_t k = 0; k < n; ++k)
- {
- total += weights * sum;
- total += weights * sum;
- }
- coefficients = (total - total) / denominator;
- }
- }
- static Real GaussianQuadrature(std::vector<Real> const& roots,
- std::vector<Real>const& coefficients, Real a, Real b,
- std::function<Real(Real)> const& integrand)
- {
- Real const half = (Real)0.5;
- Real radius = half * (b - a);
- Real center = half * (b + a);
- Real result = (Real)0;
- for (size_t i = 0; i < roots.size(); ++i)
- {
- result += coefficients * integrand(radius * roots + center);
- }
- result *= radius;
- return result;
- }
- };
- }
|