lib/maths/common/unittest/CIntegrationTest.cc (997 lines of code) (raw):
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the following additional limitation. Functionality enabled by the
* files subject to the Elastic License 2.0 may only be used in production when
* invoked by an Elasticsearch process with a license key installed that permits
* use of machine learning features. You may not use this file except in
* compliance with the Elastic License 2.0 and the foregoing additional
* limitation.
*/
#include <core/CLogger.h>
#include <maths/common/CCompositeFunctions.h>
#include <maths/common/CIntegration.h>
#include <maths/common/CLinearAlgebra.h>
#include <maths/common/CLinearAlgebraTools.h>
#include <test/BoostTestCloseAbsolute.h>
#include <test/CRandomNumbers.h>
#include <boost/math/distributions/normal.hpp>
#include <boost/test/unit_test.hpp>
#include <algorithm>
#include <cmath>
#include <fstream>
#include <numeric>
BOOST_AUTO_TEST_SUITE(CIntegrationTest)
using namespace ml;
using namespace maths;
using namespace common;
namespace {
template<unsigned int ORDER>
class CPolynomialFunction {
public:
explicit CPolynomialFunction(const double (&coefficients)[ORDER + 1]) {
std::copy(coefficients, coefficients + ORDER + 1, m_Coefficients);
}
bool operator()(const double& x, double& result) const {
result = 0.0;
for (unsigned int i = 0; i < ORDER + 1; ++i) {
result += m_Coefficients[i] * std::pow(x, static_cast<double>(i));
}
return true;
}
const double& coefficient(unsigned int i) const {
return m_Coefficients[i];
}
private:
double m_Coefficients[ORDER + 1];
};
std::ostream& operator<<(std::ostream& o, const CPolynomialFunction<0>& f) {
return o << f.coefficient(0);
}
template<unsigned int ORDER>
std::ostream& operator<<(std::ostream& o, const CPolynomialFunction<ORDER>& f) {
o << f.coefficient(0) << " + ";
for (unsigned int i = 1; i < ORDER; ++i) {
o << f.coefficient(i) << "x^" << i << " + ";
}
if (ORDER > 0) {
o << f.coefficient(ORDER) << "x^" << ORDER;
}
return o;
}
template<unsigned int ORDER>
double integrate(const CPolynomialFunction<ORDER>& f, const double& a, const double& b) {
double result = 0.0;
for (unsigned int i = 0; i < ORDER + 1; ++i) {
double n = static_cast<double>(i) + 1.0;
result += f.coefficient(i) / n * (std::pow(b, n) - std::pow(a, n));
}
return result;
}
template<unsigned int DIMENSION>
class CMultivariatePolynomialFunction {
public:
using TVector = CVectorNx1<double, DIMENSION>;
struct SMonomial {
bool operator<(const SMonomial& rhs) const {
return std::accumulate(s_Powers, s_Powers + DIMENSION, 0.0) <
std::accumulate(rhs.s_Powers, rhs.s_Powers + DIMENSION, 0.0);
}
double s_Coefficient;
double s_Powers[DIMENSION];
};
using TMonomialVec = std::vector<SMonomial>;
public:
void add(double coefficient, double powers[DIMENSION]) {
m_Terms.push_back(SMonomial());
m_Terms.back().s_Coefficient = coefficient;
std::copy(powers, powers + DIMENSION, m_Terms.back().s_Powers);
}
void finalize() { std::sort(m_Terms.begin(), m_Terms.end()); }
bool operator()(const TVector& x, double& result) const {
result = 0.0;
for (std::size_t i = 0; i < m_Terms.size(); ++i) {
const SMonomial& monomial = m_Terms[i];
double term = monomial.s_Coefficient;
for (unsigned int j = 0; j < DIMENSION; ++j) {
if (monomial.s_Powers[j] > 0.0) {
term *= std::pow(x(j), monomial.s_Powers[j]);
}
}
result += term;
}
return true;
}
const TMonomialVec& terms() const { return m_Terms; }
private:
TMonomialVec m_Terms;
};
template<unsigned int DIMENSION>
std::ostream&
operator<<(std::ostream& o, const CMultivariatePolynomialFunction<DIMENSION>& f) {
if (!f.terms().empty()) {
o << (f.terms())[0].s_Coefficient;
for (unsigned int j = 0; j < DIMENSION; ++j) {
if ((f.terms())[0].s_Powers[j] > 0.0) {
o << ".x" << j << "^" << (f.terms())[0].s_Powers[j];
}
}
}
for (std::size_t i = 1; i < f.terms().size(); ++i) {
o << " + " << (f.terms())[i].s_Coefficient;
for (unsigned int j = 0; j < DIMENSION; ++j) {
if ((f.terms())[i].s_Powers[j] > 0.0) {
o << ".x" << j << "^" << (f.terms())[i].s_Powers[j];
}
}
}
return o;
}
using TDoubleVec = std::vector<double>;
template<unsigned int DIMENSION>
double integrate(const CMultivariatePolynomialFunction<DIMENSION>& f,
const TDoubleVec& a,
const TDoubleVec& b) {
double result = 0.0;
for (std::size_t i = 0; i < f.terms().size(); ++i) {
double term = (f.terms())[i].s_Coefficient;
for (unsigned int j = 0; j < DIMENSION; ++j) {
double n = (f.terms())[i].s_Powers[j] + 1.0;
term *= (std::pow(b[j], n) - std::pow(a[j], n)) / n;
}
result += term;
}
return result;
}
using TDoubleVecVec = std::vector<TDoubleVec>;
bool readGrid(const std::string& file, TDoubleVec& weights, TDoubleVecVec& points) {
using TStrVec = std::vector<std::string>;
std::ifstream d2_l1;
d2_l1.open(file.c_str());
if (!d2_l1) {
LOG_ERROR(<< "Bad file: " << file);
return false;
}
std::string line;
while (std::getline(d2_l1, line)) {
TStrVec point;
std::string weight;
core::CStringUtils::tokenise(", ", line, point, weight);
core::CStringUtils::trimWhitespace(weight);
points.push_back(TDoubleVec());
for (std::size_t i = 0; i < point.size(); ++i) {
core::CStringUtils::trimWhitespace(point[i]);
double xi;
if (!core::CStringUtils::stringToType(point[i], xi)) {
LOG_ERROR(<< "Bad point: " << point);
return false;
}
points.back().push_back(xi);
}
double w;
if (!core::CStringUtils::stringToType(weight, w)) {
LOG_ERROR(<< "Bad weight: " << weight);
return false;
}
weights.push_back(w);
}
return true;
}
class CSmoothHeavySide {
public:
CSmoothHeavySide(double slope, double offset)
: m_Slope(slope), m_Offset(offset) {}
bool operator()(double x, double& result) const {
result = std::exp(m_Slope * (x - m_Offset)) /
(std::exp(m_Slope * (x - m_Offset)) + 1.0);
return true;
}
private:
double m_Slope;
double m_Offset;
};
class CNormal {
public:
CNormal(double mean, double std) : m_Mean(mean), m_Std(std) {}
bool operator()(double x, double& result) const {
if (m_Std <= 0.0) {
return false;
}
boost::math::normal_distribution<> normal(m_Mean, m_Std);
result = boost::math::pdf(normal, x);
return true;
}
private:
double m_Mean;
double m_Std;
};
}
BOOST_AUTO_TEST_CASE(testAllSingleVariate) {
// Test that "low" order polynomials are integrated exactly
// (as they should be for a higher order quadrature).
using TConstant = CPolynomialFunction<0>;
using TLinear = CPolynomialFunction<1>;
using TQuadratic = CPolynomialFunction<2>;
using TCubic = CPolynomialFunction<3>;
using TQuartic = CPolynomialFunction<4>;
using TQuintic = CPolynomialFunction<5>;
using THexic = CPolynomialFunction<6>;
using THeptic = CPolynomialFunction<7>;
using TOctic = CPolynomialFunction<8>;
using TNonic = CPolynomialFunction<9>;
using TDecic = CPolynomialFunction<10>;
static const double EPS = 1e-6;
double ranges[][2] = {{-3.0, -1.0}, {-1.0, 5.0}, {0.0, 8.0}};
{
double coeffs[][1] = {{-3.2}, {0.0}, {1.0}, {5.0}, {12.1}};
for (unsigned int i = 0; i < sizeof(ranges) / sizeof(ranges[0]); ++i) {
for (unsigned int j = 0; j < sizeof(coeffs) / sizeof(coeffs[0]); ++j) {
TConstant f(coeffs[j]);
LOG_DEBUG(<< "range = [" << ranges[i][0] << "," << ranges[i][1] << "]"
<< ", f(x) = " << f);
double expected = integrate(f, ranges[i][0], ranges[i][1]);
double actual;
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderOne>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderTwo>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderThree>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderFour>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderFive>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderSix>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderSeven>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderEight>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderNine>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderTen>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
}
}
}
{
double coeffs[][2] = {
{-3.2, -1.2}, {0.0, 2.0}, {1.0, -1.0}, {5.0, 6.4}, {12.1, -8.3}};
for (unsigned int i = 0; i < sizeof(ranges) / sizeof(ranges[0]); ++i) {
for (unsigned int j = 0; j < sizeof(coeffs) / sizeof(coeffs[0]); ++j) {
TLinear f(coeffs[j]);
LOG_DEBUG(<< "range = [" << ranges[i][0] << "," << ranges[i][1] << "]"
<< ", f(x) = " << f);
double expected = integrate(f, ranges[i][0], ranges[i][1]);
double actual;
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderOne>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderTwo>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderThree>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderFour>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderFive>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderSix>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderSeven>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderEight>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderNine>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderTen>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
}
}
}
{
double coeffs[][3] = {{-3.2, -1.2, -3.5},
{0.1, 2.0, 4.6},
{1.0, -1.0, 1.0},
{5.0, 6.4, -4.1},
{12.1, -8.3, 10.1}};
for (unsigned int i = 0; i < sizeof(ranges) / sizeof(ranges[0]); ++i) {
for (unsigned int j = 0; j < sizeof(coeffs) / sizeof(coeffs[0]); ++j) {
TQuadratic f(coeffs[j]);
LOG_DEBUG(<< "range = [" << ranges[i][0] << "," << ranges[i][1] << "]"
<< ", f(x) = " << f);
double expected = integrate(f, ranges[i][0], ranges[i][1]);
double actual;
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderTwo>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderThree>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderFour>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderFive>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderSix>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderSeven>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderEight>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderNine>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderTen>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
}
}
}
{
double coeffs[][4] = {{-1.2, -1.9, -3.0, -3.2},
{0.4, 2.0, 4.6, 2.3},
{1.0, -1.0, 1.0, -1.0},
{4.0, 2.4, -8.1, -2.1},
{10.1, -6.3, 1.1, 8.3}};
for (unsigned int i = 0; i < sizeof(ranges) / sizeof(ranges[0]); ++i) {
for (unsigned int j = 0; j < sizeof(coeffs) / sizeof(coeffs[0]); ++j) {
TCubic f(coeffs[j]);
LOG_DEBUG(<< "range = [" << ranges[i][0] << "," << ranges[i][1] << "]"
<< ", f(x) = " << f);
double expected = integrate(f, ranges[i][0], ranges[i][1]);
double actual;
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderThree>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderFour>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderFive>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderSix>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderSeven>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderEight>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderNine>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderTen>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
}
}
}
{
double coeffs[][5] = {{-1.1, -0.9, -4.0, -1.2, -0.2},
{20.4, 2.0, 4.6, 2.3, 0.7},
{1.0, -1.0, 1.0, -1.0, 1.0},
{4.0, 2.4, -8.1, -2.1, 1.4},
{10.1, -6.3, 1.1, 8.3, -5.1}};
for (unsigned int i = 0; i < sizeof(ranges) / sizeof(ranges[0]); ++i) {
for (unsigned int j = 0; j < sizeof(coeffs) / sizeof(coeffs[0]); ++j) {
TQuartic f(coeffs[j]);
LOG_DEBUG(<< "range = [" << ranges[i][0] << "," << ranges[i][1] << "]"
<< ", f(x) = " << f);
double expected = integrate(f, ranges[i][0], ranges[i][1]);
double actual;
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderFour>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderFive>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderSix>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderSeven>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderEight>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderNine>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderTen>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
}
}
}
{
double coeffs[][6] = {{-1.1, -0.9, -4.0, -1.2, -0.2, -1.1},
{20.4, 6.0, 2.6, 0.3, 0.7, 2.3},
{1.0, -1.0, 1.0, -1.0, 1.0, -1.0},
{3.0, 2.4, -8.1, -2.1, 1.4, -3.1},
{10.1, -5.3, 2.1, 4.3, -7.1, 0.4}};
for (unsigned int i = 0; i < sizeof(ranges) / sizeof(ranges[0]); ++i) {
for (unsigned int j = 0; j < sizeof(coeffs) / sizeof(coeffs[0]); ++j) {
TQuintic f(coeffs[j]);
LOG_DEBUG(<< "range = [" << ranges[i][0] << "," << ranges[i][1] << "]"
<< ", f(x) = " << f);
double expected = integrate(f, ranges[i][0], ranges[i][1]);
double actual;
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderFive>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderSix>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderSeven>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderEight>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderNine>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderTen>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
}
}
}
{
double coeffs[][7] = {{-1.1, -0.9, -4.0, -1.2, -0.2, -1.1, -0.1},
{20.4, 6.0, 2.6, 0.3, 0.7, 2.3, 1.0},
{1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0},
{3.0, 2.4, -8.1, -2.1, 1.4, -3.1, 2.1},
{10.1, -5.3, 2.1, 4.3, -7.1, 0.4, -0.5}};
for (unsigned int i = 0; i < sizeof(ranges) / sizeof(ranges[0]); ++i) {
for (unsigned int j = 0; j < sizeof(coeffs) / sizeof(coeffs[0]); ++j) {
THexic f(coeffs[j]);
LOG_DEBUG(<< "range = [" << ranges[i][0] << "," << ranges[i][1] << "]"
<< ", f(x) = " << f);
double expected = integrate(f, ranges[i][0], ranges[i][1]);
double actual;
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderSix>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderSeven>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderEight>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderNine>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderTen>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
}
}
}
{
double coeffs[][8] = {{-1.1, -0.9, -4.0, -1.2, -0.2, -1.1, -0.1, -2.0},
{20.4, 6.0, 2.6, 0.3, 0.7, 2.3, 1.0, 3.0},
{1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0},
{3.0, 2.4, -8.1, -2.1, 1.4, -3.1, 2.1, -2.1},
{10.1, -5.3, 2.1, 4.3, -7.1, 0.4, -0.5, 0.3}};
for (unsigned int i = 0; i < sizeof(ranges) / sizeof(ranges[0]); ++i) {
for (unsigned int j = 0; j < sizeof(coeffs) / sizeof(coeffs[0]); ++j) {
THeptic f(coeffs[j]);
LOG_DEBUG(<< "range = [" << ranges[i][0] << "," << ranges[i][1] << "]"
<< ", f(x) = " << f);
double expected = integrate(f, ranges[i][0], ranges[i][1]);
double actual;
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderSeven>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderEight>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderNine>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderTen>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
}
}
}
{
double coeffs[][9] = {{-1.1, -0.9, -4.0, -1.2, -0.2, -1.1, -0.1, -2.0, -0.1},
{20.4, 6.0, 2.6, 0.3, 0.7, 2.3, 1.0, 3.0, 10.0},
{1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0},
{3.0, 2.4, -8.1, -2.1, 1.4, -3.1, 2.1, -2.1, -1.0},
{10.1, -5.3, 2.1, 4.3, -7.1, 0.4, -0.5, 0.3, 0.3}};
for (unsigned int i = 0; i < sizeof(ranges) / sizeof(ranges[0]); ++i) {
for (unsigned int j = 0; j < sizeof(coeffs) / sizeof(coeffs[0]); ++j) {
TOctic f(coeffs[j]);
LOG_DEBUG(<< "range = [" << ranges[i][0] << "," << ranges[i][1] << "]"
<< ", f(x) = " << f);
double expected = integrate(f, ranges[i][0], ranges[i][1]);
double actual;
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderEight>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderNine>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderTen>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
}
}
}
{
double coeffs[][10] = {
{-1.1, -0.9, -4.0, -1.2, -0.2, -1.1, -0.1, -2.0, -0.1, -3.4},
{20.4, 6.0, 2.6, 0.3, 0.7, 2.3, 1.0, 3.0, 10.0, 1.3},
{1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0},
{3.0, 2.4, -8.1, -2.1, 1.4, -3.1, 2.1, -2.1, -1.0, 1.1},
{10.1, -5.3, 2.1, 4.3, -7.1, 0.4, -0.5, 0.3, 0.3, -5.0}};
for (unsigned int i = 0; i < sizeof(ranges) / sizeof(ranges[0]); ++i) {
for (unsigned int j = 0; j < sizeof(coeffs) / sizeof(coeffs[0]); ++j) {
TNonic f(coeffs[j]);
LOG_DEBUG(<< "range = [" << ranges[i][0] << "," << ranges[i][1] << "]"
<< ", f(x) = " << f);
double expected = integrate(f, ranges[i][0], ranges[i][1]);
double actual;
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderNine>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderTen>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
}
}
}
{
double coeffs[][11] = {
{-1.1, -0.9, -4.0, -1.2, -0.2, -1.1, -0.1, -2.0, -0.1, -3.4, -0.9},
{20.4, 6.0, 2.6, 0.3, 0.7, 2.3, 1.0, 3.0, 10.0, 1.3, 2.0},
{1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0},
{3.0, 2.4, -8.1, -2.1, 1.4, -3.1, 2.1, -2.1, -1.0, 1.1, 3.1},
{10.1, -5.3, 2.1, 4.3, -7.1, 0.4, -0.5, 0.3, 0.3, -5.0, -0.1}};
for (unsigned int i = 0; i < sizeof(ranges) / sizeof(ranges[0]); ++i) {
for (unsigned int j = 0; j < sizeof(coeffs) / sizeof(coeffs[0]); ++j) {
TDecic f(coeffs[j]);
LOG_DEBUG(<< "range = [" << ranges[i][0] << "," << ranges[i][1] << "]"
<< ", f(x) = " << f);
double expected = integrate(f, ranges[i][0], ranges[i][1]);
double actual;
BOOST_TEST_REQUIRE(CIntegration::gaussLegendre<CIntegration::OrderTen>(
f, ranges[i][0], ranges[i][1], actual));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, EPS);
}
}
}
}
BOOST_AUTO_TEST_CASE(testAdaptive) {
using TDoubleDoublePr = std::pair<double, double>;
using TDoubleDoublePrVec = std::vector<TDoubleDoublePr>;
{
LOG_DEBUG(<< "*** Smooth unit step at 20 ***");
CSmoothHeavySide heavySide(10.0, 20.0);
TDoubleDoublePr intervals_[] = {
TDoubleDoublePr(0.0, 10.0), TDoubleDoublePr(10.0, 20.0),
TDoubleDoublePr(20.0, 30.0), TDoubleDoublePr(30.0, 40.0)};
TDoubleDoublePrVec intervals(std::begin(intervals_), std::end(intervals_));
TDoubleVec fIntervals(intervals.size());
for (std::size_t i = 0; i < intervals.size(); ++i) {
CIntegration::gaussLegendre<CIntegration::OrderThree>(
heavySide, intervals[i].first, intervals[i].second, fIntervals[i]);
}
double result = 0.0;
CIntegration::adaptiveGaussLegendre<CIntegration::OrderThree>(
heavySide, intervals, fIntervals, 3, 5, 0.01, result);
LOG_DEBUG(<< "expectedResult = 20.0");
LOG_DEBUG(<< "result = " << result);
BOOST_REQUIRE_CLOSE_ABSOLUTE(20.0, result, 0.01 * 20.0);
}
{
LOG_DEBUG(<< "*** N(21, 3) ***");
CNormal normal(21.0, 3.0);
double expectedResult = 0.0;
for (std::size_t i = 0; i < 400; ++i) {
double fi;
CIntegration::gaussLegendre<CIntegration::OrderThree>(
normal, 0.1 * static_cast<double>(i), 0.1 * static_cast<double>(i + 1), fi);
expectedResult += fi;
}
TDoubleDoublePr intervals_[] = {
TDoubleDoublePr(0.0, 10.0), TDoubleDoublePr(10.0, 20.0),
TDoubleDoublePr(20.0, 30.0), TDoubleDoublePr(30.0, 40.0)};
TDoubleDoublePrVec intervals(std::begin(intervals_), std::end(intervals_));
TDoubleVec fIntervals(intervals.size());
for (std::size_t i = 0; i < intervals.size(); ++i) {
CIntegration::gaussLegendre<CIntegration::OrderThree>(
normal, intervals[i].first, intervals[i].second, fIntervals[i]);
}
double result = 0.0;
CIntegration::adaptiveGaussLegendre<CIntegration::OrderThree>(
normal, intervals, fIntervals, 3, 5, 0.0001, result);
LOG_DEBUG(<< "expectedResult = " << expectedResult);
LOG_DEBUG(<< "result = " << result);
BOOST_REQUIRE_CLOSE_ABSOLUTE(expectedResult, result, 0.0001 * expectedResult);
}
{
LOG_DEBUG(<< "*** \"Smooth unit step at 20\" x N(21, 3) ***");
CSmoothHeavySide heavySide(4.0, 20.0);
CNormal normal(21.0, 4.0);
CCompositeFunctions::CProduct<CSmoothHeavySide, CNormal> f(heavySide, normal);
double expectedResult = 0.0;
for (std::size_t i = 0; i < 400; ++i) {
double fi;
CIntegration::gaussLegendre<CIntegration::OrderThree>(
f, 0.1 * static_cast<double>(i), 0.1 * static_cast<double>(i + 1), fi);
expectedResult += fi;
}
TDoubleDoublePr intervals_[] = {TDoubleDoublePr(0.0, 20.0),
TDoubleDoublePr(20.0, 40.0)};
TDoubleDoublePrVec intervals(std::begin(intervals_), std::end(intervals_));
TDoubleVec fIntervals(intervals.size());
for (std::size_t i = 0; i < intervals.size(); ++i) {
CIntegration::gaussLegendre<CIntegration::OrderThree>(
f, intervals[i].first, intervals[i].second, fIntervals[i]);
}
double result = 0.0;
CIntegration::adaptiveGaussLegendre<CIntegration::OrderThree>(
f, intervals, fIntervals, 3, 5, 0.0001, result);
LOG_DEBUG(<< "expectedResult = " << expectedResult);
LOG_DEBUG(<< "result = " << result);
BOOST_REQUIRE_CLOSE_ABSOLUTE(expectedResult, result, 0.0001 * expectedResult);
}
}
BOOST_AUTO_TEST_CASE(testSparseGrid) {
// Compare against known grid characteristics. These are available
// at http://www.sparse-grids.de/#Nodes.
{
LOG_DEBUG(<< "*** 2D, order 1 ***");
TDoubleVec expectedWeights;
TDoubleVecVec expectedPoints;
BOOST_TEST_REQUIRE(readGrid("testfiles/sparse_guass_quadrature_test_d2_l1",
expectedWeights, expectedPoints));
using TSparse2do1 =
CIntegration::CSparseGaussLegendreQuadrature<CIntegration::OrderOne, CIntegration::TwoDimensions>;
const TSparse2do1& sparse = TSparse2do1::instance();
LOG_DEBUG(<< "# points = " << sparse.weights().size());
BOOST_REQUIRE_EQUAL(expectedWeights.size(), sparse.weights().size());
BOOST_REQUIRE_EQUAL(expectedPoints.size(), sparse.points().size());
for (std::size_t i = 0; i < expectedWeights.size(); ++i) {
LOG_DEBUG(<< "weight = " << (sparse.weights())[i]);
BOOST_REQUIRE_CLOSE_ABSOLUTE(expectedWeights[i],
(sparse.weights())[i] / 4.0, 1e-6);
LOG_DEBUG(<< "point = " << (sparse.points())[i]);
for (std::size_t j = 0; j < expectedPoints[i].size(); ++j) {
BOOST_REQUIRE_CLOSE_ABSOLUTE(expectedPoints[i][j],
0.5 + (sparse.points())[i](j) / 2.0, 1e-6);
}
}
}
{
LOG_DEBUG(<< "*** 2D, order 2 ***");
TDoubleVec expectedWeights;
TDoubleVecVec expectedPoints;
BOOST_TEST_REQUIRE(readGrid("testfiles/sparse_guass_quadrature_test_d2_l2",
expectedWeights, expectedPoints));
using TSparse2do2 =
CIntegration::CSparseGaussLegendreQuadrature<CIntegration::OrderTwo, CIntegration::TwoDimensions>;
const TSparse2do2& sparse = TSparse2do2::instance();
LOG_DEBUG(<< "# points = " << sparse.weights().size());
BOOST_REQUIRE_EQUAL(expectedWeights.size(), sparse.weights().size());
BOOST_REQUIRE_EQUAL(expectedPoints.size(), sparse.points().size());
for (std::size_t i = 0; i < expectedWeights.size(); ++i) {
LOG_DEBUG(<< "weight = " << (sparse.weights())[i]);
BOOST_REQUIRE_CLOSE_ABSOLUTE(expectedWeights[i],
(sparse.weights())[i] / 4.0, 1e-6);
LOG_DEBUG(<< "point = " << (sparse.points())[i]);
for (std::size_t j = 0; j < expectedPoints[i].size(); ++j) {
BOOST_REQUIRE_CLOSE_ABSOLUTE(expectedPoints[i][j],
0.5 + (sparse.points())[i](j) / 2.0, 1e-6);
}
}
}
{
LOG_DEBUG(<< "*** 2D, order 4 ***");
TDoubleVec expectedWeights;
TDoubleVecVec expectedPoints;
BOOST_TEST_REQUIRE(readGrid("testfiles/sparse_guass_quadrature_test_d2_l4",
expectedWeights, expectedPoints));
using TSparse2do4 =
CIntegration::CSparseGaussLegendreQuadrature<CIntegration::OrderFour, CIntegration::TwoDimensions>;
const TSparse2do4& sparse = TSparse2do4::instance();
LOG_DEBUG(<< "# points = " << sparse.weights().size());
BOOST_REQUIRE_EQUAL(expectedWeights.size(), sparse.weights().size());
BOOST_REQUIRE_EQUAL(expectedPoints.size(), sparse.points().size());
for (std::size_t i = 0; i < expectedWeights.size(); ++i) {
LOG_DEBUG(<< "weight = " << (sparse.weights())[i]);
BOOST_REQUIRE_CLOSE_ABSOLUTE(expectedWeights[i],
(sparse.weights())[i] / 4.0, 1e-6);
LOG_DEBUG(<< "point = " << (sparse.points())[i]);
for (std::size_t j = 0; j < expectedPoints[i].size(); ++j) {
BOOST_REQUIRE_CLOSE_ABSOLUTE(expectedPoints[i][j],
0.5 + (sparse.points())[i](j) / 2.0, 1e-6);
}
}
}
{
LOG_DEBUG(<< "*** 7D, order 3 ***");
TDoubleVec expectedWeights;
TDoubleVecVec expectedPoints;
BOOST_TEST_REQUIRE(readGrid("testfiles/sparse_guass_quadrature_test_d7_l3",
expectedWeights, expectedPoints));
using TSparse7do3 =
CIntegration::CSparseGaussLegendreQuadrature<CIntegration::OrderThree, CIntegration::SevenDimensions>;
const TSparse7do3& sparse = TSparse7do3::instance();
LOG_DEBUG(<< "# points = " << sparse.weights().size());
BOOST_REQUIRE_EQUAL(expectedWeights.size(), sparse.weights().size());
BOOST_REQUIRE_EQUAL(expectedPoints.size(), sparse.points().size());
for (std::size_t i = 0; i < expectedWeights.size(); ++i) {
LOG_DEBUG(<< "weight = " << (sparse.weights())[i]);
BOOST_REQUIRE_CLOSE_ABSOLUTE(
expectedWeights[i], (sparse.weights())[i] / std::pow(2.0, 7.0), 1e-6);
LOG_DEBUG(<< "point = " << (sparse.points())[i]);
for (std::size_t j = 0; j < expectedPoints[i].size(); ++j) {
BOOST_REQUIRE_CLOSE_ABSOLUTE(expectedPoints[i][j],
0.5 + (sparse.points())[i](j) / 2.0, 1e-6);
}
}
}
{
LOG_DEBUG(<< "*** 7D, order 5 ***");
TDoubleVec expectedWeights;
TDoubleVecVec expectedPoints;
BOOST_TEST_REQUIRE(readGrid("testfiles/sparse_guass_quadrature_test_d7_l5",
expectedWeights, expectedPoints));
using TSparse7do5 =
CIntegration::CSparseGaussLegendreQuadrature<CIntegration::OrderFive, CIntegration::SevenDimensions>;
const TSparse7do5& sparse = TSparse7do5::instance();
LOG_DEBUG(<< "# points = " << sparse.weights().size());
BOOST_REQUIRE_EQUAL(expectedWeights.size(), sparse.weights().size());
BOOST_REQUIRE_EQUAL(expectedPoints.size(), sparse.points().size());
for (std::size_t i = 0; i < expectedWeights.size(); ++i) {
if (i % 10 == 0) {
LOG_DEBUG(<< "weight = " << (sparse.weights())[i]);
}
BOOST_REQUIRE_CLOSE_ABSOLUTE(
expectedWeights[i], (sparse.weights())[i] / std::pow(2.0, 7.0), 1e-6);
if (i % 10 == 0) {
LOG_DEBUG(<< "point = " << (sparse.points())[i]);
}
for (std::size_t j = 0; j < expectedPoints[i].size(); ++j) {
BOOST_REQUIRE_CLOSE_ABSOLUTE(expectedPoints[i][j],
0.5 + (sparse.points())[i](j) / 2.0, 1e-6);
}
}
}
unsigned int dimensions[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
unsigned int order[] = {1, 2, 3, 4, 5};
std::size_t expectedNumberPoints[][5] = {
{1, 2, 3, 4, 5}, {1, 5, 13, 29, 53},
{1, 7, 25, 69, 165}, {1, 9, 41, 137, 385},
{1, 11, 61, 241, 781}, {1, 13, 85, 389, 1433},
{1, 15, 113, 589, 2437}, {1, 17, 145, 849, 3905},
{1, 19, 181, 1177, 5965}, {1, 21, 221, 1581, 8761}};
for (std::size_t i = 0; i < std::size(dimensions); ++i) {
LOG_DEBUG(<< "DIMENSION = " << dimensions[i]);
#define NUMBER_POINTS(dimension, n) \
switch (order[j]) { \
case 1: \
n = CIntegration::CSparseGaussLegendreQuadrature<CIntegration::OrderOne, dimension>::instance() \
.points() \
.size(); \
break; \
case 2: \
n = CIntegration::CSparseGaussLegendreQuadrature<CIntegration::OrderTwo, dimension>::instance() \
.points() \
.size(); \
break; \
case 3: \
n = CIntegration::CSparseGaussLegendreQuadrature<CIntegration::OrderThree, dimension>::instance() \
.points() \
.size(); \
break; \
case 4: \
n = CIntegration::CSparseGaussLegendreQuadrature<CIntegration::OrderFour, dimension>::instance() \
.points() \
.size(); \
break; \
case 5: \
n = CIntegration::CSparseGaussLegendreQuadrature<CIntegration::OrderFive, dimension>::instance() \
.points() \
.size(); \
break; \
default: \
n = 0; \
break; \
}
for (std::size_t j = 0; j < std::size(order); ++j) {
LOG_DEBUG(<< "ORDER = " << order[j]);
std::size_t numberPoints = 0;
switch (dimensions[i]) {
case 1:
NUMBER_POINTS(CIntegration::OneDimension, numberPoints);
break;
case 2:
NUMBER_POINTS(CIntegration::TwoDimensions, numberPoints);
break;
case 3:
NUMBER_POINTS(CIntegration::ThreeDimensions, numberPoints);
break;
case 4:
NUMBER_POINTS(CIntegration::FourDimensions, numberPoints);
break;
case 5:
NUMBER_POINTS(CIntegration::FiveDimensions, numberPoints);
break;
case 6:
NUMBER_POINTS(CIntegration::SixDimensions, numberPoints);
break;
case 7:
NUMBER_POINTS(CIntegration::SevenDimensions, numberPoints);
break;
case 8:
NUMBER_POINTS(CIntegration::EightDimensions, numberPoints);
break;
case 9:
NUMBER_POINTS(CIntegration::NineDimensions, numberPoints);
break;
case 10:
NUMBER_POINTS(CIntegration::TenDimensions, numberPoints);
break;
default:
numberPoints = 0;
break;
}
#undef NUMBER_POINTS
LOG_DEBUG(<< "number points: actual = " << numberPoints
<< ", expected = " << expectedNumberPoints[i][j]);
BOOST_REQUIRE_EQUAL(expectedNumberPoints[i][j], numberPoints);
}
}
}
BOOST_AUTO_TEST_CASE(testMultivariateSmooth) {
// Test that "low" order polynomials are integrated exactly.
// A sparse grid of order l will integrate a polynomial exactly
// if its order is no more than 2l - 1. A polynomial order is
// the largest sum of exponents in any term.
using TSizeVec = std::vector<std::size_t>;
test::CRandomNumbers rng;
{
LOG_DEBUG(<< "*** Dimension = 2 ***");
static const std::size_t DIMENSION = 2;
for (std::size_t l = 2; l < 5; ++l) {
LOG_DEBUG(<< "ORDER = " << 1 + l);
for (std::size_t t = 0; t < 20; ++t) {
std::size_t n = 3;
TSizeVec coefficients;
rng.generateUniformSamples(1, 50, n, coefficients);
TSizeVec powers;
rng.generateUniformSamples(0, l, DIMENSION * n, powers);
CMultivariatePolynomialFunction<DIMENSION> polynomial;
for (std::size_t i = 0; i < n; ++i) {
double c = static_cast<double>(coefficients[i]);
double p[] = {static_cast<double>(powers[DIMENSION * i + 0]),
static_cast<double>(powers[DIMENSION * i + 1])};
if (std::accumulate(p, p + DIMENSION, 0.0) >
(2.0 * static_cast<double>(l) - 1.0)) {
continue;
}
polynomial.add(c, p);
}
TDoubleVec limits;
rng.generateUniformSamples(-10.0, 10.0, 2 * DIMENSION, limits);
std::sort(limits.begin(), limits.end());
TDoubleVec a(limits.begin(), limits.begin() + DIMENSION);
TDoubleVec b(limits.begin() + DIMENSION, limits.end());
LOG_DEBUG(<< "Testing " << polynomial);
LOG_DEBUG(<< "a = " << a);
LOG_DEBUG(<< "b = " << b);
double expected = integrate(polynomial, a, b);
double actual = 0.0;
bool successful = false;
switch (l) {
case 2:
successful =
CIntegration::sparseGaussLegendre<CIntegration::OrderTwo, CIntegration::TwoDimensions>(
polynomial, a, b, actual);
break;
case 3:
successful =
CIntegration::sparseGaussLegendre<CIntegration::OrderThree, CIntegration::TwoDimensions>(
polynomial, a, b, actual);
break;
case 4:
successful =
CIntegration::sparseGaussLegendre<CIntegration::OrderFour, CIntegration::TwoDimensions>(
polynomial, a, b, actual);
break;
case 5:
successful =
CIntegration::sparseGaussLegendre<CIntegration::OrderFive, CIntegration::TwoDimensions>(
polynomial, a, b, actual);
break;
default:
break;
}
LOG_DEBUG(<< "expected = " << expected);
LOG_DEBUG(<< "actual = " << actual);
BOOST_TEST_REQUIRE(successful);
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, 1e-6);
}
}
}
{
LOG_DEBUG(<< "*** Dimension = 5 ***");
static const std::size_t DIMENSION = 5;
for (std::size_t l = 2; l < 5; ++l) {
LOG_DEBUG(<< "ORDER = " << l);
for (std::size_t t = 0; t < 20; ++t) {
std::size_t n = 10;
TSizeVec coefficients;
rng.generateUniformSamples(1, 50, n, coefficients);
TSizeVec powers;
rng.generateUniformSamples(0, l, DIMENSION * n, powers);
CMultivariatePolynomialFunction<DIMENSION> polynomial;
for (std::size_t i = 0; i < n; ++i) {
double c = static_cast<double>(coefficients[i]);
double p[] = {static_cast<double>(powers[5 * i + 0]),
static_cast<double>(powers[5 * i + 1]),
static_cast<double>(powers[5 * i + 2]),
static_cast<double>(powers[5 * i + 3]),
static_cast<double>(powers[5 * i + 4])};
if (std::accumulate(p, p + DIMENSION, 0.0) >
(2.0 * static_cast<double>(l) - 1.0)) {
continue;
}
polynomial.add(c, p);
}
TDoubleVec limits;
rng.generateUniformSamples(-10.0, 10.0, 2 * DIMENSION, limits);
std::sort(limits.begin(), limits.end());
TDoubleVec a(limits.begin(), limits.begin() + DIMENSION);
TDoubleVec b(limits.begin() + DIMENSION, limits.end());
LOG_DEBUG(<< "Testing " << polynomial);
LOG_DEBUG(<< "a = " << a);
LOG_DEBUG(<< "b = " << b);
double expected = integrate(polynomial, a, b);
double actual = 0.0;
bool successful = false;
switch (l) {
case 2:
successful =
CIntegration::sparseGaussLegendre<CIntegration::OrderTwo, CIntegration::FiveDimensions>(
polynomial, a, b, actual);
break;
case 3:
successful =
CIntegration::sparseGaussLegendre<CIntegration::OrderThree, CIntegration::FiveDimensions>(
polynomial, a, b, actual);
break;
case 4:
successful =
CIntegration::sparseGaussLegendre<CIntegration::OrderFour, CIntegration::FiveDimensions>(
polynomial, a, b, actual);
break;
case 5:
successful =
CIntegration::sparseGaussLegendre<CIntegration::OrderFive, CIntegration::FiveDimensions>(
polynomial, a, b, actual);
break;
default:
break;
}
LOG_DEBUG(<< "expected = " << expected);
LOG_DEBUG(<< "actual = " << actual);
BOOST_TEST_REQUIRE(successful);
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, actual, 1e-2);
}
}
}
}
BOOST_AUTO_TEST_SUITE_END()