lib/maths/common/unittest/CLinearAlgebraTest.cc (1,455 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/CJsonStatePersistInserter.h>
#include <core/CJsonStateRestoreTraverser.h>
#include <core/CLogger.h>
#include <maths/common/CBasicStatistics.h>
#include <maths/common/CBasicStatisticsCovariances.h>
#include <maths/common/CLinearAlgebra.h>
#include <maths/common/CLinearAlgebraEigen.h>
#include <maths/common/CLinearAlgebraPersist.h>
#include <maths/common/CLinearAlgebraTools.h>
#include <test/BoostTestCloseAbsolute.h>
#include <boost/test/unit_test.hpp>
#include <vector>
BOOST_AUTO_TEST_SUITE(CLinearAlgebraTest)
using namespace ml;
using TDoubleVec = std::vector<double>;
using TDoubleVecVec = std::vector<TDoubleVec>;
BOOST_AUTO_TEST_CASE(testSymmetricMatrixNxN) {
// Construction.
{
maths::common::CSymmetricMatrixNxN<double, 3> matrix;
LOG_DEBUG(<< "matrix = " << matrix);
for (std::size_t i = 0; i < 3; ++i) {
for (std::size_t j = 0; j < 3; ++j) {
BOOST_REQUIRE_EQUAL(0.0, matrix(i, j));
}
}
BOOST_REQUIRE_EQUAL(0.0, matrix.trace());
}
{
maths::common::CSymmetricMatrixNxN<double, 3> matrix(3.0);
LOG_DEBUG(<< "matrix = " << matrix);
for (std::size_t i = 0; i < 3; ++i) {
for (std::size_t j = 0; j < 3; ++j) {
BOOST_REQUIRE_EQUAL(3.0, matrix(i, j));
}
}
BOOST_REQUIRE_EQUAL(9.0, matrix.trace());
}
{
double m[][5]{{1.1, 2.4, 1.4, 3.7, 4.0},
{2.4, 3.2, 1.8, 0.7, 1.0},
{1.4, 1.8, 0.8, 4.7, 3.1},
{3.7, 0.7, 4.7, 4.7, 1.1},
{4.0, 1.0, 3.1, 1.1, 1.0}};
maths::common::CSymmetricMatrixNxN<double, 5> matrix(m);
LOG_DEBUG(<< "matrix = " << matrix);
for (std::size_t i = 0; i < 5; ++i) {
for (std::size_t j = 0; j < 5; ++j) {
BOOST_REQUIRE_EQUAL(m[i][j], matrix(i, j));
}
}
BOOST_REQUIRE_EQUAL(10.8, matrix.trace());
}
{
maths::common::CVectorNx1<double, 4> vector{{1.0, 2.0, 3.0, 4.0}};
maths::common::CSymmetricMatrixNxN<double, 4> matrix(
maths::common::E_OuterProduct, vector);
LOG_DEBUG(<< "matrix = " << matrix);
for (std::size_t i = 0; i < 4; ++i) {
for (std::size_t j = 0; j < 4; ++j) {
BOOST_REQUIRE_EQUAL(static_cast<double>((i + 1) * (j + 1)), matrix(i, j));
}
}
BOOST_REQUIRE_EQUAL(30.0, matrix.trace());
}
{
maths::common::CVectorNx1<double, 4> vector{{1.0, 2.0, 3.0, 4.0}};
maths::common::CSymmetricMatrixNxN<double, 4> matrix(maths::common::E_Diagonal, vector);
LOG_DEBUG(<< "matrix = " << matrix);
for (std::size_t i = 0; i < 4; ++i) {
for (std::size_t j = 0; j < 4; ++j) {
BOOST_REQUIRE_EQUAL(i == j ? vector(i) : 0.0, matrix(i, j));
}
}
}
// Operators
{
LOG_DEBUG(<< "Sum");
double m[][5]{{1.1, 2.4, 1.4, 3.7, 4.0},
{2.4, 3.2, 1.8, 0.7, 1.0},
{1.4, 1.8, 0.8, 4.7, 3.1},
{3.7, 0.7, 4.7, 4.7, 1.1},
{4.0, 1.0, 3.1, 1.1, 1.0}};
maths::common::CSymmetricMatrixNxN<double, 5> matrix(m);
maths::common::CSymmetricMatrixNxN<double, 5> sum = matrix + matrix;
LOG_DEBUG(<< "sum = " << sum);
for (std::size_t i = 0; i < 5; ++i) {
for (std::size_t j = 0; j < 5; ++j) {
BOOST_REQUIRE_EQUAL(2.0 * m[i][j], sum(i, j));
}
}
}
{
LOG_DEBUG(<< "Difference");
double m1[][3]{{1.1, 0.4, 1.4}, {0.4, 1.2, 1.8}, {1.4, 1.8, 0.8}};
double m2[][3]{{2.1, 0.3, 0.4}, {0.3, 1.2, 3.8}, {0.4, 3.8, 0.2}};
maths::common::CSymmetricMatrixNxN<double, 3> matrix1(m1);
maths::common::CSymmetricMatrixNxN<double, 3> matrix2(m2);
maths::common::CSymmetricMatrixNxN<double, 3> difference = matrix1 - matrix2;
LOG_DEBUG(<< "difference = " << difference);
for (std::size_t i = 0; i < 3; ++i) {
for (std::size_t j = 0; j < 3; ++j) {
BOOST_REQUIRE_EQUAL(m1[i][j] - m2[i][j], difference(i, j));
}
}
}
{
LOG_DEBUG(<< "Matrix Scalar Multiplication");
maths::common::CVectorNx1<double, 5> vector{{1.0, 2.0, 3.0, 4.0, 5.0}};
maths::common::CSymmetricMatrixNxN<double, 5> m(maths::common::E_OuterProduct, vector);
maths::common::CSymmetricMatrixNxN<double, 5> ms = m * 3.0;
LOG_DEBUG(<< "3 * m = " << ms);
for (std::size_t i = 0; i < 5; ++i) {
for (std::size_t j = 0; j < 5; ++j) {
BOOST_REQUIRE_EQUAL(3.0 * static_cast<double>((i + 1) * (j + 1)),
ms(i, j));
}
}
}
{
LOG_DEBUG(<< "Matrix Scalar Division");
maths::common::CVectorNx1<double, 5> vector{{1.0, 2.0, 3.0, 4.0, 5.0}};
maths::common::CSymmetricMatrixNxN<double, 5> m(maths::common::E_OuterProduct, vector);
maths::common::CSymmetricMatrixNxN<double, 5> ms = m / 4.0;
LOG_DEBUG(<< "m / 4.0 = " << ms);
for (std::size_t i = 0; i < 5; ++i) {
for (std::size_t j = 0; j < 5; ++j) {
BOOST_REQUIRE_EQUAL(static_cast<double>((i + 1) * (j + 1)) / 4.0,
ms(i, j));
}
}
}
{
LOG_DEBUG(<< "Mean");
double m[][5]{{1.1, 2.4, 1.4, 3.7, 4.0},
{2.4, 3.2, 1.8, 0.7, 1.0},
{1.4, 1.8, 0.8, 4.7, 3.1},
{3.7, 0.7, 4.7, 4.7, 1.1},
{4.0, 1.0, 3.1, 1.1, 1.0}};
maths::common::CSymmetricMatrixNxN<double, 5> matrix(m);
double expectedMean{
(5.0 * maths::common::CBasicStatistics::mean({1.1, 3.2, 0.8, 4.7, 1.0}) +
20.0 * maths::common::CBasicStatistics::mean(
{2.4, 1.4, 3.7, 4.0, 1.8, 0.7, 1.0, 4.7, 3.1, 1.1})) /
25.0};
BOOST_REQUIRE_CLOSE(expectedMean, matrix.mean(), 1e-6);
}
}
BOOST_AUTO_TEST_CASE(testVectorNx1) {
// Construction.
{
maths::common::CVectorNx1<double, 3> vector;
LOG_DEBUG(<< "vector = " << vector);
for (std::size_t i = 0; i < 3; ++i) {
BOOST_REQUIRE_EQUAL(0.0, vector(i));
}
}
{
maths::common::CVectorNx1<double, 3> vector(3.0);
LOG_DEBUG(<< "vector = " << vector);
for (std::size_t i = 0; i < 3; ++i) {
BOOST_REQUIRE_EQUAL(3.0, vector(i));
}
}
{
double v[]{1.1, 2.4, 1.4, 3.7, 4.0};
maths::common::CVectorNx1<double, 5> vector(v);
LOG_DEBUG(<< "vector = " << vector);
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL(v[i], vector(i));
}
}
// Operators
{
LOG_DEBUG(<< "Sum");
maths::common::CVectorNx1<double, 5> vector{{1.1, 2.4, 1.4, 3.7, 4.0}};
maths::common::CVectorNx1<double, 5> sum = vector + vector;
LOG_DEBUG(<< "vector = " << sum);
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL(2.0 * vector(i), sum(i));
}
}
{
LOG_DEBUG(<< "Difference");
maths::common::CVectorNx1<double, 3> vector1{{1.1, 0.4, 1.4}};
maths::common::CVectorNx1<double, 3> vector2{{2.1, 0.3, 0.4}};
maths::common::CVectorNx1<double, 3> difference = vector1 - vector2;
LOG_DEBUG(<< "vector = " << difference);
for (std::size_t i = 0; i < 3; ++i) {
BOOST_REQUIRE_EQUAL(vector1(i) - vector2(i), difference(i));
}
}
{
LOG_DEBUG(<< "Matrix Vector Multiplication");
Eigen::Matrix4d randomMatrix;
Eigen::Vector4d randomVector;
for (std::size_t t = 0; t < 20; ++t) {
randomMatrix.setRandom();
Eigen::Matrix4d a = randomMatrix.selfadjointView<Eigen::Lower>();
LOG_DEBUG(<< "A =\n" << a);
randomVector.setRandom();
LOG_DEBUG(<< "x =\n" << randomVector);
Eigen::Vector4d expected = a * randomVector;
LOG_DEBUG(<< "Ax =\n" << expected);
maths::common::CSymmetricMatrixNxN<double, 4> s(
maths::common::fromDenseMatrix(randomMatrix));
LOG_DEBUG(<< "S = " << s);
maths::common::CVectorNx1<double, 4> y(maths::common::fromDenseVector(randomVector));
LOG_DEBUG(<< "y =\n" << y);
maths::common::CVectorNx1<double, 4> sy = s * y;
LOG_DEBUG(<< "Sy = " << sy);
for (std::size_t i = 0; i < 4; ++i) {
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected(i), sy(i), 1e-14);
}
}
}
{
LOG_DEBUG(<< "Vector Scalar Multiplication");
maths::common::CVectorNx1<double, 5> vector{{1.0, 2.0, 3.0, 4.0, 5.0}};
maths::common::CVectorNx1<double, 5> vs = vector * 3.0;
LOG_DEBUG(<< "3 * v = " << vs);
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL(3.0 * static_cast<double>((i + 1)), vs(i));
}
}
{
LOG_DEBUG(<< "Matrix Scalar Division");
maths::common::CVectorNx1<double, 5> vector({1.0, 2.0, 3.0, 4.0, 5.0});
maths::common::CVectorNx1<double, 5> vs = vector / 4.0;
LOG_DEBUG(<< "v / 4.0 = " << vs);
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL(static_cast<double>((i + 1)) / 4.0, vs(i));
}
}
{
LOG_DEBUG(<< "Mean");
maths::common::CVectorNx1<double, 5> vector{{1.0, 2.0, 3.0, 4.0, 5.0}};
double expectedMean{maths::common::CBasicStatistics::mean({1.0, 2.0, 3.0, 4.0, 5.0})};
BOOST_REQUIRE_EQUAL(expectedMean, vector.mean());
}
}
BOOST_AUTO_TEST_CASE(testSymmetricMatrix) {
// Construction.
{
maths::common::CSymmetricMatrix<double> matrix(3);
LOG_DEBUG(<< "matrix = " << matrix);
BOOST_REQUIRE_EQUAL(3, matrix.rows());
BOOST_REQUIRE_EQUAL(3, matrix.columns());
for (std::size_t i = 0; i < 3; ++i) {
for (std::size_t j = 0; j < 3; ++j) {
BOOST_REQUIRE_EQUAL(0.0, matrix(i, j));
}
}
}
{
maths::common::CSymmetricMatrix<double> matrix(4, 3.0);
LOG_DEBUG(<< "matrix = " << matrix);
BOOST_REQUIRE_EQUAL(4, matrix.rows());
BOOST_REQUIRE_EQUAL(4, matrix.columns());
for (std::size_t i = 0; i < 4; ++i) {
for (std::size_t j = 0; j < 4; ++j) {
BOOST_REQUIRE_EQUAL(3.0, matrix(i, j));
}
}
}
{
TDoubleVecVec m{{1.1, 2.4, 1.4, 3.7, 4.0},
{2.4, 3.2, 1.8, 0.7, 1.0},
{1.4, 1.8, 0.8, 4.7, 3.1},
{3.7, 0.7, 4.7, 4.7, 1.1},
{4.0, 1.0, 3.1, 1.1, 1.0}};
maths::common::CSymmetricMatrix<double> matrix(m);
LOG_DEBUG(<< "matrix = " << matrix);
BOOST_REQUIRE_EQUAL(5, matrix.rows());
BOOST_REQUIRE_EQUAL(5, matrix.columns());
for (std::size_t i = 0; i < 5; ++i) {
for (std::size_t j = 0; j < 5; ++j) {
BOOST_REQUIRE_EQUAL(m[i][j], matrix(i, j));
}
}
BOOST_REQUIRE_EQUAL(10.8, matrix.trace());
}
{
double m[]{1.1, 2.4, 3.2, 1.4, 1.8, 0.8, 3.7, 0.7,
4.7, 4.7, 4.0, 1.0, 3.1, 1.1, 1.0};
maths::common::CSymmetricMatrix<double> matrix(std::begin(m), std::end(m));
LOG_DEBUG(<< "matrix = " << matrix);
BOOST_REQUIRE_EQUAL(5, matrix.rows());
BOOST_REQUIRE_EQUAL(5, matrix.columns());
for (std::size_t i = 0, k = 0; i < 5; ++i) {
for (std::size_t j = 0; j <= i; ++j, ++k) {
BOOST_REQUIRE_EQUAL(m[k], matrix(i, j));
BOOST_REQUIRE_EQUAL(m[k], matrix(j, i));
}
}
BOOST_REQUIRE_EQUAL(10.8, matrix.trace());
}
{
TDoubleVec v{1.0, 2.0, 3.0, 4.0};
maths::common::CVector<double> vector{v};
maths::common::CSymmetricMatrix<double> matrix(maths::common::E_OuterProduct, vector);
LOG_DEBUG(<< "matrix = " << matrix);
BOOST_REQUIRE_EQUAL(4, matrix.rows());
BOOST_REQUIRE_EQUAL(4, matrix.columns());
for (std::size_t i = 0; i < 4; ++i) {
for (std::size_t j = 0; j < 4; ++j) {
BOOST_REQUIRE_EQUAL(static_cast<double>((i + 1) * (j + 1)), matrix(i, j));
}
}
BOOST_REQUIRE_EQUAL(30.0, matrix.trace());
}
{
TDoubleVec v{1.0, 2.0, 3.0, 4.0};
maths::common::CVector<double> vector{v};
maths::common::CSymmetricMatrix<double> matrix(maths::common::E_Diagonal, vector);
LOG_DEBUG(<< "matrix = " << matrix);
BOOST_REQUIRE_EQUAL(4, matrix.rows());
BOOST_REQUIRE_EQUAL(4, matrix.columns());
for (std::size_t i = 0; i < 4; ++i) {
for (std::size_t j = 0; j < 4; ++j) {
BOOST_REQUIRE_EQUAL(i == j ? vector(i) : 0.0, matrix(i, j));
}
}
}
// Operators
{
LOG_DEBUG(<< "Sum");
double m[]{1.1, 2.4, 3.2, 1.4, 1.8, 0.8, 3.7, 0.7,
4.7, 4.7, 4.0, 1.0, 3.1, 1.1, 1.0};
maths::common::CSymmetricMatrix<double> matrix(std::begin(m), std::end(m));
maths::common::CSymmetricMatrix<double> sum = matrix + matrix;
LOG_DEBUG(<< "sum = " << sum);
for (std::size_t i = 0, k = 0; i < 5; ++i) {
for (std::size_t j = 0; j <= i; ++j, ++k) {
BOOST_REQUIRE_EQUAL(2.0 * m[k], sum(i, j));
BOOST_REQUIRE_EQUAL(2.0 * m[k], sum(j, i));
}
}
}
{
LOG_DEBUG(<< "Difference");
double m1[]{1.1, 0.4, 1.2, 1.4, 1.8, 0.8};
double m2[]{2.1, 0.3, 1.2, 0.4, 3.8, 0.2};
maths::common::CSymmetricMatrix<double> matrix1(std::begin(m1), std::end(m1));
maths::common::CSymmetricMatrix<double> matrix2(std::begin(m2), std::end(m2));
maths::common::CSymmetricMatrix<double> difference = matrix1 - matrix2;
LOG_DEBUG(<< "difference = " << difference);
for (std::size_t i = 0u, k = 0; i < 3; ++i) {
for (std::size_t j = 0; j <= i; ++j, ++k) {
BOOST_REQUIRE_EQUAL(m1[k] - m2[k], difference(i, j));
BOOST_REQUIRE_EQUAL(m1[k] - m2[k], difference(j, i));
}
}
}
{
LOG_DEBUG(<< "Matrix Scalar Multiplication");
TDoubleVec v{1.0, 2.0, 3.0, 4.0, 5.0};
maths::common::CVector<double> vector{v};
maths::common::CSymmetricMatrix<double> m(maths::common::E_OuterProduct, vector);
maths::common::CSymmetricMatrix<double> ms = m * 3.0;
LOG_DEBUG(<< "3 * m = " << ms);
for (std::size_t i = 0; i < 5; ++i) {
for (std::size_t j = 0; j < 5; ++j) {
BOOST_REQUIRE_EQUAL(3.0 * static_cast<double>((i + 1) * (j + 1)),
ms(i, j));
}
}
}
{
LOG_DEBUG(<< "Matrix Scalar Division");
TDoubleVec v{1.0, 2.0, 3.0, 4.0, 5.0};
maths::common::CVector<double> vector{v};
maths::common::CSymmetricMatrix<double> m(maths::common::E_OuterProduct, vector);
maths::common::CSymmetricMatrix<double> ms = m / 4.0;
LOG_DEBUG(<< "m / 4.0 = " << ms);
for (std::size_t i = 0; i < 5; ++i) {
for (std::size_t j = 0; j < 5; ++j) {
BOOST_REQUIRE_EQUAL(static_cast<double>((i + 1) * (j + 1)) / 4.0,
ms(i, j));
}
}
}
{
LOG_DEBUG(<< "Mean");
TDoubleVecVec m{{1.1, 2.4, 1.4, 3.7, 4.0},
{2.4, 3.2, 1.8, 0.7, 1.0},
{1.4, 1.8, 0.8, 4.7, 3.1},
{3.7, 0.7, 4.7, 4.7, 1.1},
{4.0, 1.0, 3.1, 1.1, 1.0}};
maths::common::CSymmetricMatrix<double> matrix(m);
double expectedMean{
(5.0 * maths::common::CBasicStatistics::mean({1.1, 3.2, 0.8, 4.7, 1.0}) +
20.0 * maths::common::CBasicStatistics::mean(
{2.4, 1.4, 3.7, 4.0, 1.8, 0.7, 1.0, 4.7, 3.1, 1.1})) /
25.0};
BOOST_REQUIRE_CLOSE(expectedMean, matrix.mean(), 1e-6);
}
}
BOOST_AUTO_TEST_CASE(testVector) {
// Construction.
{
maths::common::CVector<double> vector(3);
LOG_DEBUG(<< "vector = " << vector);
BOOST_REQUIRE_EQUAL(3, vector.dimension());
for (std::size_t i = 0; i < 3; ++i) {
BOOST_REQUIRE_EQUAL(0.0, vector(i));
}
}
{
maths::common::CVector<double> vector(4, 3.0);
LOG_DEBUG(<< "vector = " << vector);
BOOST_REQUIRE_EQUAL(4, vector.dimension());
for (std::size_t i = 0; i < 4; ++i) {
BOOST_REQUIRE_EQUAL(3.0, vector(i));
}
}
{
TDoubleVec v{1.1, 2.4, 1.4, 3.7, 4.0};
maths::common::CVector<double> vector(v);
BOOST_REQUIRE_EQUAL(5, vector.dimension());
LOG_DEBUG(<< "vector = " << vector);
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL(v[i], vector(i));
}
}
{
TDoubleVec v{1.1, 2.4, 1.4, 3.7, 4.0};
maths::common::CVector<double> vector{v.begin(), v.end()};
BOOST_REQUIRE_EQUAL(5, vector.dimension());
LOG_DEBUG(<< "vector = " << vector);
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL(v[i], vector(i));
}
}
// Operators
{
LOG_DEBUG(<< "Sum");
TDoubleVec v{1.1, 2.4, 1.4, 3.7, 4.0};
maths::common::CVector<double> vector{v};
maths::common::CVector<double> sum = vector + vector;
LOG_DEBUG(<< "vector = " << sum);
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL(2.0 * v[i], sum(i));
}
}
{
LOG_DEBUG(<< "Difference");
TDoubleVec v1{1.1, 0.4, 1.4};
TDoubleVec v2{2.1, 0.3, 0.4};
maths::common::CVector<double> vector1{v1};
maths::common::CVector<double> vector2{v2};
maths::common::CVector<double> difference = vector1 - vector2;
LOG_DEBUG(<< "vector = " << difference);
for (std::size_t i = 0; i < 3; ++i) {
BOOST_REQUIRE_EQUAL(v1[i] - v2[i], difference(i));
}
}
{
LOG_DEBUG(<< "Matrix Vector Multiplication");
Eigen::MatrixXd randomMatrix(4, 4);
Eigen::VectorXd randomVector(4);
for (std::size_t t = 0; t < 20; ++t) {
randomMatrix.setRandom();
Eigen::MatrixXd a = randomMatrix.selfadjointView<Eigen::Lower>();
LOG_DEBUG(<< "A =\n" << a);
randomVector.setRandom();
LOG_DEBUG(<< "x =\n" << randomVector);
Eigen::VectorXd expected = a * randomVector;
LOG_DEBUG(<< "Ax =\n" << expected);
maths::common::CSymmetricMatrix<double> s(
maths::common::fromDenseMatrix(randomMatrix));
LOG_DEBUG(<< "S = " << s);
maths::common::CVector<double> y(maths::common::fromDenseVector(randomVector));
LOG_DEBUG(<< "y =\n" << y);
maths::common::CVector<double> sy = s * y;
LOG_DEBUG(<< "Sy = " << sy);
for (std::size_t i = 0; i < 4; ++i) {
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected(i), sy(i), 1e-10);
}
}
}
{
LOG_DEBUG(<< "Vector Scalar Multiplication");
TDoubleVec v{1.0, 2.0, 3.0, 4.0, 5.0};
maths::common::CVector<double> vector{v};
maths::common::CVector<double> vs = vector * 3.0;
LOG_DEBUG(<< "3 * v = " << vs);
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL(3.0 * static_cast<double>((i + 1)), vs(i));
}
}
{
LOG_DEBUG(<< "Matrix Scalar Division");
TDoubleVec v{1.0, 2.0, 3.0, 4.0, 5.0};
maths::common::CVector<double> vector{v};
maths::common::CVector<double> vs = vector / 4.0;
LOG_DEBUG(<< "v / 4.0 = " << vs);
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL(static_cast<double>((i + 1)) / 4.0, vs(i));
}
}
{
LOG_DEBUG(<< "Mean");
TDoubleVec v{1.0, 2.0, 3.0, 4.0, 5.0};
maths::common::CVector<double> vector{v};
double expectedMean{maths::common::CBasicStatistics::mean({1.0, 2.0, 3.0, 4.0, 5.0})};
BOOST_REQUIRE_EQUAL(expectedMean, vector.mean());
}
}
BOOST_AUTO_TEST_CASE(testNorms) {
double v[][5]{{1.0, 2.1, 3.2, 1.7, 0.1},
{0.0, -2.1, 1.2, 1.9, 4.1},
{-1.0, 7.1, 5.2, 1.7, -0.1},
{-3.0, 1.1, -3.3, 1.8, 6.1}};
double expectedEuclidean[]{4.30697, 5.12543, 9.01942, 7.84538};
for (std::size_t i = 0; i < std::size(v); ++i) {
maths::common::CVectorNx1<double, 5> v_(v[i]);
BOOST_REQUIRE_CLOSE_ABSOLUTE(expectedEuclidean[i], v_.euclidean(), 5e-6);
}
double m[][15]{
{1.0, 2.1, 3.2, 1.7, 0.1, 4.2, 0.3, 2.8, 4.1, 0.1, 0.4, 1.2, 5.2, 0.2, 6.3},
{0.0, -2.1, 1.2, 1.9, 4.1, 4.5, -3.1, 0.0, 1.3, 7.5, 0.2, 1.0, 4.5, 8.1, 0.3},
{-1.0, 7.1, 5.2, 1.7, -0.1, 3.2, 1.8, -3.2, 4.2, 9.1, 0.2, 0.4, 4.1, 7.2, 1.3},
{-3.0, 1.1, -3.3, 1.8, 6.1, -1.3, 1.3, 4.2, 3.1, 1.9, -2.3, 3.1, 2.4, 2.3, 1.0}};
double expectedFrobenius[]{13.78550, 18.00250, 20.72052, 14.80844};
for (std::size_t i = 0; i < std::size(m); ++i) {
maths::common::CSymmetricMatrixNxN<double, 5> m_(std::begin(m[i]),
std::end(m[i]));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expectedFrobenius[i], m_.frobenius(), 5e-6);
}
}
BOOST_AUTO_TEST_CASE(testUtils) {
// Test component min, max, sqrt and fabs.
{
LOG_DEBUG(<< "Vector min, max, fabs, sqrt");
const double v1_[]{1.0, 3.1, 2.2, 4.9, 12.0};
maths::common::CVectorNx1<double, 5> v1(v1_);
const double v2_[]{1.5, 3.0, 2.7, 5.2, 8.0};
maths::common::CVectorNx1<double, 5> v2(v2_);
const double v3_[]{-1.0, 3.1, -2.2, -4.9, 12.0};
maths::common::CVectorNx1<double, 5> v3(v3_);
{
double expected[]{1.0, 3.1, 2.2, 4.0, 4.0};
LOG_DEBUG(<< "min(v1, 4.0) = " << maths::common::min(v1, 4.0));
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL(expected[i], (maths::common::min(v1, 4.0))(i));
}
}
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL((maths::common::min(v1, 4.0))(i),
(maths::common::min(4.0, v1))(i));
}
{
double expected[]{1.0, 3.0, 2.2, 4.9, 8.0};
LOG_DEBUG(<< "min(v1, v2) = " << maths::common::min(v1, v2));
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL(expected[i], (maths::common::min(v1, v2))(i));
}
}
{
double expected[]{3.0, 3.1, 3.0, 4.9, 12.0};
LOG_DEBUG(<< "max(v1, 3.0) = " << maths::common::max(v1, 3.0));
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL(expected[i], (maths::common::max(v1, 3.0))(i));
}
}
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL((maths::common::max(v1, 3.0))(i),
(maths::common::max(3.0, v1))(i));
}
{
double expected[]{1.5, 3.1, 2.7, 5.2, 12.0};
LOG_DEBUG(<< "max(v1, v2) = " << maths::common::max(v1, v2));
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL(expected[i], (maths::common::max(v1, v2))(i));
}
}
{
double expected[]{1.0, std::sqrt(3.1), std::sqrt(2.2),
std::sqrt(4.9), std::sqrt(12.0)};
LOG_DEBUG(<< "sqrt(v1) = " << maths::common::sqrt(v1));
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL(expected[i], (maths::common::sqrt(v1))(i));
}
}
{
const double expected[]{1.0, 3.1, 2.2, 4.9, 12.0};
LOG_DEBUG(<< "fabs(v3) = " << maths::common::fabs(v3));
for (std::size_t i = 0; i < 5; ++i) {
BOOST_REQUIRE_EQUAL(expected[i], (maths::common::fabs(v3))(i));
}
}
}
{
LOG_DEBUG(<< "Matrix min, max, fabs, sqrt");
double m1_[][3]{{2.1, 0.3, 0.4}, {0.3, 1.2, 3.8}, {0.4, 3.8, 0.2}};
maths::common::CSymmetricMatrixNxN<double, 3> m1(m1_);
double m2_[][3]{{1.1, 0.4, 1.4}, {0.4, 1.2, 1.8}, {1.4, 1.8, 0.8}};
maths::common::CSymmetricMatrixNxN<double, 3> m2(m2_);
double m3_[][3]{{-2.1, 0.3, 0.4}, {0.3, -1.2, -3.8}, {0.4, -3.8, 0.2}};
maths::common::CSymmetricMatrixNxN<double, 3> m3(m3_);
{
double expected[][3]{{2.1, 0.3, 0.4}, {0.3, 1.2, 3.0}, {0.4, 3.0, 0.2}};
LOG_DEBUG(<< "min(m1, 3.0) = " << maths::common::min(m1, 3.0));
for (std::size_t i = 0; i < 3; ++i) {
for (std::size_t j = 0; j < 3; ++j) {
BOOST_REQUIRE_EQUAL(expected[i][j],
(maths::common::min(m1, 3.0))(i, j));
}
}
}
for (std::size_t i = 0; i < 3; ++i) {
for (std::size_t j = 0; j < 3; ++j) {
BOOST_REQUIRE_EQUAL((maths::common::min(m1, 3.0))(i, j),
(maths::common::min(3.0, m1))(i, j));
}
}
{
double expected[][3]{{1.1, 0.3, 0.4}, {0.3, 1.2, 1.8}, {0.4, 1.8, 0.2}};
LOG_DEBUG(<< "min(m1, m2) = " << maths::common::min(m1, m2));
for (std::size_t i = 0; i < 3; ++i) {
for (std::size_t j = 0; j < 3; ++j) {
BOOST_REQUIRE_EQUAL(expected[i][j],
(maths::common::min(m1, m2))(i, j));
}
}
}
{
double expected[][3]{{2.1, 2.0, 2.0}, {2.0, 2.0, 3.8}, {2.0, 3.8, 2.0}};
LOG_DEBUG(<< "max(m1, 2.0) = " << maths::common::max(m1, 2.0));
for (std::size_t i = 0; i < 3; ++i) {
for (std::size_t j = 0; j < 3; ++j) {
BOOST_REQUIRE_EQUAL(expected[i][j],
(maths::common::max(m1, 2.0))(i, j));
}
}
}
for (std::size_t i = 0; i < 3; ++i) {
for (std::size_t j = 0; j < 3; ++j) {
BOOST_REQUIRE_EQUAL((maths::common::max(m1, 2.0))(i, j),
(maths::common::max(2.0, m1))(i, j));
}
}
{
double expected[][3]{{2.1, 0.4, 1.4}, {0.4, 1.2, 3.8}, {1.4, 3.8, 0.8}};
LOG_DEBUG(<< "max(m1, m2) = " << maths::common::max(m1, m2));
for (std::size_t i = 0; i < 3; ++i) {
for (std::size_t j = 0; j < 3; ++j) {
BOOST_REQUIRE_EQUAL(expected[i][j],
(maths::common::max(m1, m2))(i, j));
}
}
}
{
double expected[][3]{{std::sqrt(2.1), std::sqrt(0.3), std::sqrt(0.4)},
{std::sqrt(0.3), std::sqrt(1.2), std::sqrt(3.8)},
{std::sqrt(0.4), std::sqrt(3.8), std::sqrt(0.2)}};
LOG_DEBUG(<< "sqrt(m1) = " << maths::common::sqrt(m1));
for (std::size_t i = 0; i < 3; ++i) {
for (std::size_t j = 0; j < 3; ++j) {
BOOST_REQUIRE_EQUAL(expected[i][j], (maths::common::sqrt(m1))(i, j));
}
}
}
{
double expected[][3]{{2.1, 0.3, 0.4}, {0.3, 1.2, 3.8}, {0.4, 3.8, 0.2}};
LOG_DEBUG(<< "fabs(m3) = " << maths::common::fabs(m3));
for (std::size_t i = 0; i < 3; ++i) {
for (std::size_t j = 0; j < 3; ++j) {
BOOST_REQUIRE_EQUAL(expected[i][j], (maths::common::fabs(m3))(i, j));
}
}
}
}
}
BOOST_AUTO_TEST_CASE(testScaleCovariances) {
const double scale_[]{0.8, 1.5, 2.0, 0.5};
const double covariance_[][4]{{10.0, 0.1, 1.5, 2.0},
{0.1, 11.0, 2.5, 1.9},
{1.5, 2.5, 12.0, 2.4},
{2.0, 1.9, 2.4, 11.5}};
LOG_DEBUG(<< "CVectorNx1");
{
maths::common::CVectorNx1<double, 4> scale(scale_);
maths::common::CSymmetricMatrixNxN<double, 4> covariance(covariance_);
maths::common::scaleCovariances(scale, covariance);
LOG_DEBUG(<< "scaled covariances =" << covariance);
for (std::size_t i = 0; i < 4; ++i) {
BOOST_REQUIRE_CLOSE_ABSOLUTE(scale_[i] * covariance_[i][i],
covariance(i, i), 1e-10);
for (std::size_t j = i + 1; j < 4; ++j) {
double expected = ::sqrt(scale_[i] * scale_[j]) * covariance_[i][j];
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, covariance(i, j), 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, covariance(j, i), 1e-10);
}
}
}
LOG_DEBUG(<< "CDenseVector");
{
maths::common::CDenseVector<double> scale(4);
scale << scale_[0], scale_[1], scale_[2], scale_[3];
maths::common::CDenseMatrix<double> covariance(4, 4);
covariance << covariance_[0][0], covariance_[0][1], covariance_[0][2],
covariance_[0][3], covariance_[1][0], covariance_[1][1],
covariance_[1][2], covariance_[1][3], covariance_[2][0],
covariance_[2][1], covariance_[2][2], covariance_[2][3],
covariance_[3][0], covariance_[3][1], covariance_[3][2],
covariance_[3][3];
maths::common::scaleCovariances(scale, covariance);
LOG_DEBUG(<< "scaled covariances =\n" << covariance);
for (std::size_t i = 0; i < 4; ++i) {
BOOST_REQUIRE_CLOSE_ABSOLUTE(scale_[i] * covariance_[i][i],
covariance(i, i), 1e-10);
for (std::size_t j = i + 1; j < 4; ++j) {
double expected = ::sqrt(scale_[i] * scale_[j]) * covariance_[i][j];
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, covariance(i, j), 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected, covariance(j, i), 1e-10);
}
}
}
}
BOOST_AUTO_TEST_CASE(testGaussianLogLikelihood) {
// Test the log likelihood (expected from octave).
{
const double covariance_[][4]{{10.70779, 0.14869, 1.44263, 2.26889},
{0.14869, 10.70919, 2.56363, 1.87805},
{1.44263, 2.56363, 11.90966, 2.44121},
{2.26889, 1.87805, 2.44121, 11.53904}};
maths::common::CSymmetricMatrixNxN<double, 4> covariance(covariance_);
const double x_[][4]{{-1.335028, -0.222988, -0.174935, -0.480772},
{0.137550, 1.286252, 0.027043, 1.349709},
{-0.445561, 2.390953, 0.302770, 0.084871},
{0.275802, 0.408910, -2.247157, 0.196043},
{0.179101, 0.177340, -0.456634, 5.314863},
{0.260426, 0.325159, 1.214650, -1.267697},
{-0.363917, -0.422225, 0.360000, 0.401383},
{1.492814, 3.257986, 0.065441, -0.187108},
{1.214063, 0.067988, -0.241846, -0.425730},
{-0.306693, -0.188497, -1.092719, 1.288093}};
const double expected[]{-8.512128, -8.569778, -8.706920, -8.700537,
-9.794163, -8.602336, -8.462027, -9.096402,
-8.521042, -8.590054};
for (std::size_t i = 0; i < std::size(x_); ++i) {
maths::common::CVectorNx1<double, 4> x(x_[i]);
double likelihood;
BOOST_REQUIRE_EQUAL(maths_t::E_FpNoErrors,
maths::common::gaussianLogLikelihood(covariance, x, likelihood));
LOG_DEBUG(<< "expected log(L(x)) = " << expected[i]);
LOG_DEBUG(<< "got log(L(x)) = " << likelihood);
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected[i], likelihood, 1e-6);
}
}
// Test log likelihood singular matrix.
{
double e1_[]{1.0, 1.0, 1.0, 1.0};
double e2_[]{-1.0, 1.0, 0.0, 0.0};
double e3_[]{-1.0, -1.0, 2.0, 0.0};
double e4_[]{-1.0, -1.0, -1.0, 3.0};
maths::common::CVectorNx1<double, 4> e1(e1_);
maths::common::CVectorNx1<double, 4> e2(e2_);
maths::common::CVectorNx1<double, 4> e3(e3_);
maths::common::CVectorNx1<double, 4> e4(e4_);
maths::common::CSymmetricMatrixNxN<double, 4> covariance(
10.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e1 / e1.euclidean()) +
5.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e2 / e2.euclidean()) +
5.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e3 / e3.euclidean()));
double likelihood;
BOOST_REQUIRE_EQUAL(maths_t::E_FpNoErrors,
maths::common::gaussianLogLikelihood(covariance, e1, likelihood));
BOOST_REQUIRE_CLOSE_ABSOLUTE(
-0.5 * (3.0 * std::log(boost::math::double_constants::two_pi) +
std::log(10.0 * 5.0 * 5.0) + 4.0 / 10.0),
likelihood, 1e-10);
BOOST_REQUIRE_EQUAL(maths_t::E_FpNoErrors,
maths::common::gaussianLogLikelihood(covariance, e2, likelihood));
BOOST_REQUIRE_CLOSE_ABSOLUTE(
-0.5 * (3.0 * std::log(boost::math::double_constants::two_pi) +
std::log(10.0 * 5.0 * 5.0) + 2.0 / 5.0),
likelihood, 1e-10);
BOOST_REQUIRE_EQUAL(maths_t::E_FpNoErrors,
maths::common::gaussianLogLikelihood(covariance, e3, likelihood));
BOOST_REQUIRE_CLOSE_ABSOLUTE(
-0.5 * (3.0 * std::log(boost::math::double_constants::two_pi) +
std::log(10.0 * 5.0 * 5.0) + 6.0 / 5.0),
likelihood, 1e-10);
BOOST_REQUIRE_EQUAL(
maths_t::E_FpOverflowed,
maths::common::gaussianLogLikelihood(covariance, e1, likelihood, false));
BOOST_TEST_REQUIRE(likelihood > 0.0);
BOOST_REQUIRE_EQUAL(
maths_t::E_FpOverflowed,
maths::common::gaussianLogLikelihood(covariance, e4, likelihood, false));
BOOST_TEST_REQUIRE(likelihood < 0.0);
}
// Construct a matrix whose eigenvalues and vectors are known.
{
double e1_[]{1.0, 1.0, 1.0, 1.0};
double e2_[]{-1.0, 1.0, 0.0, 0.0};
double e3_[]{-1.0, -1.0, 2.0, 0.0};
double e4_[]{-1.0, -1.0, -1.0, 3.0};
maths::common::CVectorNx1<double, 4> e1(e1_);
maths::common::CVectorNx1<double, 4> e2(e2_);
maths::common::CVectorNx1<double, 4> e3(e3_);
maths::common::CVectorNx1<double, 4> e4(e4_);
maths::common::CSymmetricMatrixNxN<double, 4> covariance(
10.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e1 / e1.euclidean()) +
5.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e2 / e2.euclidean()) +
5.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e3 / e3.euclidean()) +
2.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e4 / e4.euclidean()));
double likelihood;
BOOST_REQUIRE_EQUAL(maths_t::E_FpNoErrors,
maths::common::gaussianLogLikelihood(covariance, e1, likelihood));
BOOST_REQUIRE_CLOSE_ABSOLUTE(
-0.5 * (4.0 * std::log(boost::math::double_constants::two_pi) +
std::log(10.0 * 5.0 * 5.0 * 2.0) + 4.0 / 10.0),
likelihood, 1e-10);
BOOST_REQUIRE_EQUAL(maths_t::E_FpNoErrors,
maths::common::gaussianLogLikelihood(covariance, e2, likelihood));
BOOST_REQUIRE_CLOSE_ABSOLUTE(
-0.5 * (4.0 * std::log(boost::math::double_constants::two_pi) +
std::log(10.0 * 5.0 * 5.0 * 2.0) + 2.0 / 5.0),
likelihood, 1e-10);
BOOST_REQUIRE_EQUAL(maths_t::E_FpNoErrors,
maths::common::gaussianLogLikelihood(covariance, e3, likelihood));
BOOST_REQUIRE_CLOSE_ABSOLUTE(
-0.5 * (4.0 * std::log(boost::math::double_constants::two_pi) +
std::log(10.0 * 5.0 * 5.0 * 2.0) + 6.0 / 5.0),
likelihood, 1e-10);
BOOST_REQUIRE_EQUAL(maths_t::E_FpNoErrors,
maths::common::gaussianLogLikelihood(covariance, e4, likelihood));
BOOST_REQUIRE_CLOSE_ABSOLUTE(
-0.5 * (4.0 * std::log(boost::math::double_constants::two_pi) +
std::log(10.0 * 5.0 * 5.0 * 2.0) + 12.0 / 2.0),
likelihood, 1e-10);
}
}
BOOST_AUTO_TEST_CASE(testSampleGaussian) {
// Test singular matrix.
{
double m[]{1.0, 2.0, 3.0, 4.0};
maths::common::CVectorNx1<double, 4> mean(m);
double e1_[]{1.0, 1.0, 1.0, 1.0};
double e2_[]{-1.0, 1.0, 0.0, 0.0};
double e3_[]{-1.0, -1.0, 2.0, 0.0};
double e4_[]{-1.0, -1.0, -1.0, 3.0};
maths::common::CVectorNx1<double, 4> e1(e1_);
maths::common::CVectorNx1<double, 4> e2(e2_);
maths::common::CVectorNx1<double, 4> e3(e3_);
maths::common::CVectorNx1<double, 4> e4(e4_);
maths::common::CSymmetricMatrixNxN<double, 4> covariance(
10.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e1 / e1.euclidean()) +
5.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e2 / e2.euclidean()) +
5.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e3 / e3.euclidean()));
std::vector<maths::common::CVectorNx1<double, 4>> samples;
maths::common::sampleGaussian(100, mean, covariance, samples);
BOOST_REQUIRE_EQUAL(99, samples.size());
maths::common::CBasicStatistics::SSampleCovariances<maths::common::CVectorNx1<double, 4>> covariances(
4);
for (std::size_t i = 0; i < samples.size(); ++i) {
covariances.add(samples[i]);
}
LOG_DEBUG(<< "mean = " << mean);
LOG_DEBUG(<< "covariance = " << covariance);
LOG_DEBUG(<< "sample mean = "
<< maths::common::CBasicStatistics::mean(covariances));
LOG_DEBUG(<< "sample covariance = "
<< maths::common::CBasicStatistics::maximumLikelihoodCovariances(covariances));
maths::common::CVectorNx1<double, 4> meanError =
maths::common::CVectorNx1<double, 4>(mean) -
maths::common::CBasicStatistics::mean(covariances);
maths::common::CSymmetricMatrixNxN<double, 4> covarianceError =
maths::common::CSymmetricMatrixNxN<double, 4>(covariance) -
maths::common::CBasicStatistics::maximumLikelihoodCovariances(covariances);
LOG_DEBUG(<< "|error| / |mean| = " << meanError.euclidean() / mean.euclidean());
LOG_DEBUG(<< "|error| / |covariance| = "
<< covarianceError.frobenius() / covariance.frobenius());
BOOST_REQUIRE_CLOSE_ABSOLUTE(0.0, meanError.euclidean() / mean.euclidean(), 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(
0.0, covarianceError.frobenius() / covariance.frobenius(), 0.01);
}
// Construct a matrix whose eigenvalues and vectors are known.
{
double m[]{15.0, 0.0, 1.0, 5.0};
maths::common::CVectorNx1<double, 4> mean(m);
double e1_[]{1.0, 1.0, 1.0, 1.0};
double e2_[]{-1.0, 1.0, 0.0, 0.0};
double e3_[]{-1.0, -1.0, 2.0, 0.0};
double e4_[]{-1.0, -1.0, -1.0, 3.0};
maths::common::CVectorNx1<double, 4> e1(e1_);
maths::common::CVectorNx1<double, 4> e2(e2_);
maths::common::CVectorNx1<double, 4> e3(e3_);
maths::common::CVectorNx1<double, 4> e4(e4_);
maths::common::CSymmetricMatrixNxN<double, 4> covariance(
10.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e1 / e1.euclidean()) +
5.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e2 / e2.euclidean()) +
5.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e3 / e3.euclidean()) +
2.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e4 / e4.euclidean()));
std::vector<maths::common::CVectorNx1<double, 4>> samples;
maths::common::sampleGaussian(100, mean, covariance, samples);
BOOST_REQUIRE_EQUAL(100, samples.size());
maths::common::CBasicStatistics::SSampleCovariances<maths::common::CVectorNx1<double, 4>> covariances(
4);
for (std::size_t i = 0; i < samples.size(); ++i) {
covariances.add(samples[i]);
}
LOG_DEBUG(<< "mean = " << mean);
LOG_DEBUG(<< "covariance = " << covariance);
LOG_DEBUG(<< "sample mean = "
<< maths::common::CBasicStatistics::mean(covariances));
LOG_DEBUG(<< "sample covariance = "
<< maths::common::CBasicStatistics::maximumLikelihoodCovariances(covariances));
maths::common::CVectorNx1<double, 4> meanError =
maths::common::CVectorNx1<double, 4>(mean) -
maths::common::CBasicStatistics::mean(covariances);
maths::common::CSymmetricMatrixNxN<double, 4> covarianceError =
maths::common::CSymmetricMatrixNxN<double, 4>(covariance) -
maths::common::CBasicStatistics::maximumLikelihoodCovariances(covariances);
LOG_DEBUG(<< "|error| / |mean| = " << meanError.euclidean() / mean.euclidean());
LOG_DEBUG(<< "|error| / |covariance| = "
<< covarianceError.frobenius() / covariance.frobenius());
BOOST_REQUIRE_CLOSE_ABSOLUTE(0.0, meanError.euclidean() / mean.euclidean(), 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(
0.0, covarianceError.frobenius() / covariance.frobenius(), 0.02);
}
}
BOOST_AUTO_TEST_CASE(testLogDeterminant) {
// Test the determinant (expected from octave).
{
const double matrices[][3][3]{
{{0.25451, 0.52345, 0.61308}, {0.52345, 1.19825, 1.12804}, {0.61308, 1.12804, 1.78833}},
{{0.83654, 0.24520, 0.80310}, {0.24520, 0.38368, 0.30554}, {0.80310, 0.30554, 0.78936}},
{{0.73063, 0.87818, 0.85836}, {0.87818, 1.50305, 1.17931}, {0.85836, 1.17931, 1.05850}},
{{0.38947, 0.61062, 0.34423}, {0.61062, 1.60437, 0.91664}, {0.34423, 0.91664, 0.52448}},
{{1.79563, 1.78751, 2.17200}, {1.78751, 1.83443, 2.17340}, {2.17200, 2.17340, 2.62958}},
{{0.57023, 0.47992, 0.71581}, {0.47992, 1.09182, 0.97989}, {0.71581, 0.97989, 1.32316}},
{{2.31264, 0.72098, 2.38050}, {0.72098, 0.28103, 0.78025}, {2.38050, 0.78025, 2.49219}},
{{0.83678, 0.45230, 0.74564}, {0.45230, 0.26482, 0.33491}, {0.74564, 0.33491, 1.29216}},
{{0.84991, 0.85443, 0.36922}, {0.85443, 1.12737, 0.83074}, {0.36922, 0.83074, 1.01195}},
{{0.27156, 0.26441, 0.29726}, {0.26441, 0.32388, 0.18895}, {0.29726, 0.18895, 0.47884}}};
const double expected[]{5.1523e-03, 6.7423e-04, 4.5641e-04, 1.5880e-04,
3.1654e-06, 8.5319e-02, 2.0840e-03, 6.8008e-03,
1.4755e-02, 2.6315e-05};
for (std::size_t i = 0; i < std::size(matrices); ++i) {
maths::common::CSymmetricMatrixNxN<double, 3> M(matrices[i]);
double logDeterminant;
maths::common::logDeterminant(M, logDeterminant);
LOG_DEBUG(<< "expected |M| = " << expected[i]);
LOG_DEBUG(<< "got |M| = " << std::exp(logDeterminant));
BOOST_REQUIRE_CLOSE_ABSOLUTE(expected[i], std::exp(logDeterminant),
1e-4 * expected[i]);
}
}
// Construct a matrix whose eigenvalues and vectors are known.
{
double e1_[]{1.0, 1.0, 1.0, 1.0};
double e2_[]{-1.0, 1.0, 0.0, 0.0};
double e3_[]{-1.0, -1.0, 2.0, 0.0};
double e4_[]{-1.0, -1.0, -1.0, 3.0};
maths::common::CVectorNx1<double, 4> e1(e1_);
maths::common::CVectorNx1<double, 4> e2(e2_);
maths::common::CVectorNx1<double, 4> e3(e3_);
maths::common::CVectorNx1<double, 4> e4(e4_);
maths::common::CSymmetricMatrixNxN<double, 4> M(
10.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e1 / e1.euclidean()) +
5.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e2 / e2.euclidean()) +
5.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e3 / e3.euclidean()) +
2.0 * maths::common::CSymmetricMatrixNxN<double, 4>(
maths::common::E_OuterProduct, e4 / e4.euclidean()));
double logDeterminant;
maths::common::logDeterminant(M, logDeterminant);
BOOST_REQUIRE_CLOSE_ABSOLUTE(std::log(10.0 * 5.0 * 5.0 * 2.0), logDeterminant, 1e-10);
}
}
namespace {
template<typename MATRIX>
std::string print(const MATRIX& m) {
std::ostringstream result;
result << m;
return result.str();
}
}
BOOST_AUTO_TEST_CASE(testProjected) {
using TSizeVec = std::vector<std::size_t>;
const double m[][5]{{1.2, 2.4, 1.9, 3.8, 8.3},
{2.4, 1.0, 0.2, 1.6, 3.1},
{1.9, 0.2, 8.1, 1.1, 0.1},
{3.8, 1.6, 1.1, 3.7, 7.3},
{8.3, 3.1, 0.1, 7.3, 0.9}};
const double v[]{0.3, 3.4, 10.6, 0.9, 5.7};
maths::common::CSymmetricMatrixNxN<double, 5> matrix(m);
maths::common::CVectorNx1<double, 5> vector(v);
{
std::size_t ss[]{0, 1};
TSizeVec subspace(std::begin(ss), std::end(ss));
Eigen::MatrixXd projectedMatrix = maths::common::projectedMatrix(subspace, matrix);
Eigen::MatrixXd projectedVector = maths::common::projectedVector(subspace, vector);
LOG_DEBUG(<< "projectedMatrix =\n" << projectedMatrix);
LOG_DEBUG(<< "projectedVector =\n" << projectedVector);
BOOST_REQUIRE_EQUAL(std::string("1.2 2.4\n2.4 1"), print(projectedMatrix));
BOOST_REQUIRE_EQUAL(std::string("0.3\n3.4"), print(projectedVector));
}
{
std::size_t ss[]{1, 0};
TSizeVec subspace(std::begin(ss), std::end(ss));
Eigen::MatrixXd projectedMatrix = maths::common::projectedMatrix(subspace, matrix);
Eigen::MatrixXd projectedVector = maths::common::projectedVector(subspace, vector);
LOG_DEBUG(<< "projectedMatrix =\n" << projectedMatrix);
LOG_DEBUG(<< "projectedVector =\n" << projectedVector);
BOOST_REQUIRE_EQUAL(std::string(" 1 2.4\n2.4 1.2"), print(projectedMatrix));
BOOST_REQUIRE_EQUAL(std::string("3.4\n0.3"), print(projectedVector));
}
{
std::size_t ss[]{1, 0, 4};
TSizeVec subspace(std::begin(ss), std::end(ss));
Eigen::MatrixXd projectedMatrix = maths::common::projectedMatrix(subspace, matrix);
Eigen::MatrixXd projectedVector = maths::common::projectedVector(subspace, vector);
LOG_DEBUG(<< "projectedMatrix =\n" << projectedMatrix);
LOG_DEBUG(<< "projectedVector =\n" << projectedVector);
BOOST_REQUIRE_EQUAL(std::string(" 1 2.4 3.1\n2.4 1.2 8.3\n3.1 8.3 0.9"),
print(projectedMatrix));
BOOST_REQUIRE_EQUAL(std::string("3.4\n0.3\n5.7"), print(projectedVector));
}
}
BOOST_AUTO_TEST_CASE(testShims) {
using TVector4 = maths::common::CVectorNx1<double, 4>;
using TMatrix4 = maths::common::CSymmetricMatrixNxN<double, 4>;
using TVector = maths::common::CVector<double>;
using TMatrix = maths::common::CSymmetricMatrix<double>;
using TDenseVector = maths::common::CDenseVector<double>;
using TDenseMatrix = maths::common::CDenseMatrix<double>;
using TMappedVector = maths::common::CMemoryMappedDenseVector<double>;
using TMappedMatrix = maths::common::CMemoryMappedDenseMatrix<double>;
double components[][4]{{1.0, 3.1, 4.0, 1.5},
{0.9, 3.2, 2.1, 1.7},
{1.3, 1.6, 8.9, 0.2},
{-1.3, 2.7, -4.7, 3.1}};
TVector4 vector1(components[0]);
TVector vector2(std::begin(components[0]), std::end(components[0]));
TDenseVector vector3(4);
vector3 << components[0][0], components[0][1], components[0][2], components[0][3];
TMappedVector vector4(components[0], 4);
LOG_DEBUG(<< "Test dimension");
{
BOOST_REQUIRE_EQUAL(4, maths::common::las::dimension(vector1));
BOOST_REQUIRE_EQUAL(4, maths::common::las::dimension(vector2));
BOOST_REQUIRE_EQUAL(4, maths::common::las::dimension(vector3));
BOOST_REQUIRE_EQUAL(4, maths::common::las::dimension(vector4));
}
LOG_DEBUG(<< "Test zero");
{
BOOST_TEST_REQUIRE(TVector4(0.0) == maths::common::las::zero(vector1));
BOOST_TEST_REQUIRE(TVector(4, 0.0) == maths::common::las::zero(vector2));
BOOST_TEST_REQUIRE(TDenseVector::Zero(4) == maths::common::las::zero(vector3));
BOOST_TEST_REQUIRE(TDenseVector::Zero(4) == maths::common::las::zero(vector4));
}
LOG_DEBUG(<< "Test conformableZeroMatrix");
{
BOOST_TEST_REQUIRE(TMatrix4(0.0) ==
maths::common::las::conformableZeroMatrix(vector1));
BOOST_TEST_REQUIRE((TMatrix(4, 0.0) ==
maths::common::las::conformableZeroMatrix(vector2)));
BOOST_TEST_REQUIRE((TDenseMatrix::Zero(4, 4) ==
maths::common::las::conformableZeroMatrix(vector3)));
BOOST_TEST_REQUIRE((TDenseMatrix::Zero(4, 4) ==
maths::common::las::conformableZeroMatrix(vector4)));
}
LOG_DEBUG(<< "Test isZero");
{
BOOST_TEST_REQUIRE(maths::common::las::isZero(vector1) == false);
BOOST_TEST_REQUIRE(maths::common::las::isZero(vector2) == false);
BOOST_TEST_REQUIRE(maths::common::las::isZero(vector3) == false);
BOOST_TEST_REQUIRE(maths::common::las::isZero(vector4) == false);
BOOST_TEST_REQUIRE(maths::common::las::isZero(maths::common::las::zero(vector1)));
BOOST_TEST_REQUIRE(maths::common::las::isZero(maths::common::las::zero(vector2)));
BOOST_TEST_REQUIRE(maths::common::las::isZero(maths::common::las::zero(vector3)));
BOOST_TEST_REQUIRE(maths::common::las::isZero(maths::common::las::zero(vector4)));
}
LOG_DEBUG(<< "Test ones");
{
BOOST_TEST_REQUIRE(TVector4(1.0) == maths::common::las::ones(vector1));
BOOST_TEST_REQUIRE(TVector(4, 1.0) == maths::common::las::ones(vector2));
BOOST_TEST_REQUIRE(TDenseVector::Ones(4) == maths::common::las::ones(vector3));
BOOST_TEST_REQUIRE(TDenseVector::Ones(4) == maths::common::las::ones(vector4));
}
LOG_DEBUG(<< "Test constant");
{
BOOST_TEST_REQUIRE(TVector4(5.1) == maths::common::las::constant(vector1, 5.1));
BOOST_TEST_REQUIRE(TVector(4, 5.1) == maths::common::las::constant(vector2, 5.1));
const auto expected = 5.1 * TDenseVector::Ones(4);
BOOST_TEST_REQUIRE(expected == maths::common::las::constant(vector3, 5.1));
BOOST_TEST_REQUIRE(expected == maths::common::las::constant(vector4, 5.1));
}
LOG_DEBUG(<< "Test min");
{
double placeholder[2][4];
std::copy(std::begin(components[1]), std::end(components[1]),
std::begin(placeholder[0]));
std::copy(std::begin(components[2]), std::end(components[2]),
std::begin(placeholder[1]));
TVector4 vector5(components[1]);
TVector vector6(std::begin(components[1]), std::end(components[1]));
TDenseVector vector7(4);
vector7 << components[1][0], components[1][1], components[1][2],
components[1][3];
TMappedVector vector8(placeholder[0], 4);
TVector4 vector9(std::begin(components[2]), std::end(components[2]));
TVector vector10(std::begin(components[2]), std::end(components[2]));
TDenseVector vector11(4);
vector11 << components[2][0], components[2][1], components[2][2],
components[2][3];
TMappedVector vector12(placeholder[1], 4);
maths::common::las::min(vector1, vector5);
maths::common::las::min(vector2, vector6);
maths::common::las::min(vector3, vector7);
maths::common::las::min(vector4, vector8);
maths::common::las::min(vector1, vector9);
maths::common::las::min(vector2, vector10);
maths::common::las::min(vector3, vector11);
maths::common::las::min(vector4, vector12);
LOG_DEBUG(<< " min1 = " << vector5);
LOG_DEBUG(<< " min1 = " << vector6);
LOG_DEBUG(<< " min1 = " << vector7.transpose());
LOG_DEBUG(<< " min1 = " << vector8.transpose());
LOG_DEBUG(<< " min2 = " << vector9);
LOG_DEBUG(<< " min2 = " << vector10);
LOG_DEBUG(<< " min2 = " << vector11.transpose());
LOG_DEBUG(<< " min2 = " << vector12.transpose());
std::ostringstream o7;
o7 << vector7.transpose();
std::ostringstream o8;
o8 << vector8.transpose();
std::ostringstream o11;
o11 << vector11.transpose();
std::ostringstream o12;
o12 << vector12.transpose();
BOOST_REQUIRE_EQUAL(std::string("[0.9, 3.1, 2.1, 1.5]"),
core::CContainerPrinter::print(vector5));
BOOST_REQUIRE_EQUAL(std::string("[0.9, 3.1, 2.1, 1.5]"),
core::CContainerPrinter::print(vector6));
BOOST_REQUIRE_EQUAL(std::string("0.9 3.1 2.1 1.5"), o7.str());
BOOST_REQUIRE_EQUAL(std::string("0.9 3.1 2.1 1.5"), o8.str());
BOOST_REQUIRE_EQUAL(std::string("[1, 1.6, 4, 0.2]"),
core::CContainerPrinter::print(vector9));
BOOST_REQUIRE_EQUAL(std::string("[1, 1.6, 4, 0.2]"),
core::CContainerPrinter::print(vector10));
BOOST_REQUIRE_EQUAL(std::string(" 1 1.6 4 0.2"), o11.str());
BOOST_REQUIRE_EQUAL(std::string(" 1 1.6 4 0.2"), o12.str());
}
LOG_DEBUG(<< "Test max");
{
double placeholder[2][4];
std::copy(std::begin(components[1]), std::end(components[1]),
std::begin(placeholder[0]));
std::copy(std::begin(components[2]), std::end(components[2]),
std::begin(placeholder[1]));
TVector4 vector5(components[1]);
TVector vector6(std::begin(components[1]), std::end(components[1]));
TDenseVector vector7(4);
vector7 << components[1][0], components[1][1], components[1][2],
components[1][3];
TMappedVector vector8(placeholder[0], 4);
TVector4 vector9(components[2]);
TVector vector10(std::begin(components[2]), std::end(components[2]));
TDenseVector vector11(4);
vector11 << components[2][0], components[2][1], components[2][2],
components[2][3];
TMappedVector vector12(placeholder[1], 4);
maths::common::las::max(vector1, vector5);
maths::common::las::max(vector2, vector6);
maths::common::las::max(vector3, vector7);
maths::common::las::max(vector4, vector8);
maths::common::las::max(vector1, vector9);
maths::common::las::max(vector2, vector10);
maths::common::las::max(vector3, vector11);
maths::common::las::max(vector4, vector12);
LOG_DEBUG(<< " max1 = " << vector5);
LOG_DEBUG(<< " max1 = " << vector6);
LOG_DEBUG(<< " max1 = " << vector7.transpose());
LOG_DEBUG(<< " max1 = " << vector8.transpose());
LOG_DEBUG(<< " max2 = " << vector9);
LOG_DEBUG(<< " max2 = " << vector10);
LOG_DEBUG(<< " max2 = " << vector11.transpose());
LOG_DEBUG(<< " max2 = " << vector12.transpose());
std::ostringstream o7;
o7 << vector7.transpose();
std::ostringstream o8;
o8 << vector8.transpose();
std::ostringstream o11;
o11 << vector11.transpose();
std::ostringstream o12;
o12 << vector12.transpose();
BOOST_REQUIRE_EQUAL(std::string("[1, 3.2, 4, 1.7]"),
core::CContainerPrinter::print(vector5));
BOOST_REQUIRE_EQUAL(std::string("[1, 3.2, 4, 1.7]"),
core::CContainerPrinter::print(vector6));
BOOST_REQUIRE_EQUAL(std::string(" 1 3.2 4 1.7"), o7.str());
BOOST_REQUIRE_EQUAL(std::string(" 1 3.2 4 1.7"), o8.str());
BOOST_REQUIRE_EQUAL(std::string("[1.3, 3.1, 8.9, 1.5]"),
core::CContainerPrinter::print(vector9));
BOOST_REQUIRE_EQUAL(std::string("[1.3, 3.1, 8.9, 1.5]"),
core::CContainerPrinter::print(vector10));
BOOST_REQUIRE_EQUAL(std::string("1.3 3.1 8.9 1.5"), o11.str());
BOOST_REQUIRE_EQUAL(std::string("1.3 3.1 8.9 1.5"), o12.str());
}
LOG_DEBUG(<< "Test componentwise divide");
{
TVector4 vector5(components[1]);
TVector vector6(std::begin(components[1]), std::end(components[1]));
TDenseVector vector7(4);
vector7 << components[1][0], components[1][1], components[1][2],
components[1][3];
TMappedVector vector8(components[1], 4);
TVector4 d1 = maths::common::las::componentwise(vector1) /
maths::common::las::componentwise(vector5);
TVector d2 = maths::common::las::componentwise(vector2) /
maths::common::las::componentwise(vector6);
TDenseVector d3 = maths::common::las::componentwise(vector3) /
maths::common::las::componentwise(vector7);
TDenseVector d4 = maths::common::las::componentwise(vector4) /
maths::common::las::componentwise(vector8);
LOG_DEBUG(<< " v1 / v2 = " << d1);
LOG_DEBUG(<< " v1 / v2 = " << d2);
LOG_DEBUG(<< " v1 / v2 = " << d3.transpose());
LOG_DEBUG(<< " v1 / v2 = " << d4.transpose());
std::ostringstream o3;
o3 << d3.transpose();
std::ostringstream o4;
o4 << d4.transpose();
BOOST_REQUIRE_EQUAL(std::string("[1.111111, 0.96875, 1.904762, 0.8823529]"),
core::CContainerPrinter::print(d1));
BOOST_REQUIRE_EQUAL(std::string("[1.111111, 0.96875, 1.904762, 0.8823529]"),
core::CContainerPrinter::print(d2));
BOOST_REQUIRE_EQUAL(std::string(" 1.11111 0.96875 1.90476 0.882353"), o3.str());
BOOST_REQUIRE_EQUAL(std::string(" 1.11111 0.96875 1.90476 0.882353"), o4.str());
}
LOG_DEBUG(<< "Test distance");
{
TVector4 vector5(components[2]);
TVector vector6(std::begin(components[2]), std::end(components[2]));
TDenseVector vector7(4);
vector7 << components[2][0], components[2][1], components[2][2],
components[2][3];
TMappedVector vector8(components[2], 4);
double d1 = maths::common::las::distance(vector1, vector5);
double d2 = maths::common::las::distance(vector2, vector6);
double d3 = maths::common::las::distance(vector3, vector7);
double d4 = maths::common::las::distance(vector4, vector8);
LOG_DEBUG(<< " d1 = " << d1);
LOG_DEBUG(<< " d1 = " << d2);
LOG_DEBUG(<< " d1 = " << d3);
LOG_DEBUG(<< " d1 = " << d4);
BOOST_REQUIRE_CLOSE_ABSOLUTE(5.29528091794949, d1, 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(5.29528091794949, d2, 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(5.29528091794949, d3, 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(5.29528091794949, d4, 1e-10);
}
LOG_DEBUG(<< "Test Euclidean norm");
{
double n1 = maths::common::las::norm(vector1);
double n2 = maths::common::las::norm(vector2);
double n3 = maths::common::las::norm(vector3);
double n4 = maths::common::las::norm(vector4);
LOG_DEBUG(<< " n1 = " << n1);
LOG_DEBUG(<< " n1 = " << n2);
LOG_DEBUG(<< " n1 = " << n3);
LOG_DEBUG(<< " n1 = " << n4);
BOOST_REQUIRE_CLOSE_ABSOLUTE(5.37215040742532, n1, 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(5.37215040742532, n2, 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(5.37215040742532, n3, 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(5.37215040742532, n4, 1e-10);
}
LOG_DEBUG(<< "Test L1");
{
TVector4 vector5(components[3]);
TVector vector6(std::begin(components[3]), std::end(components[3]));
TDenseVector vector7(4);
vector7 << components[3][0], components[3][1], components[3][2],
components[3][3];
TMappedVector vector8(components[3], 4);
double l1 = maths::common::las::L1(vector5);
double l2 = maths::common::las::L1(vector6);
double l3 = maths::common::las::L1(vector7);
double l4 = maths::common::las::L1(vector8);
LOG_DEBUG(<< " l1 = " << l1);
LOG_DEBUG(<< " l1 = " << l2);
LOG_DEBUG(<< " l1 = " << l3);
LOG_DEBUG(<< " l1 = " << l4);
BOOST_REQUIRE_CLOSE_ABSOLUTE(11.8, l1, 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(11.8, l2, 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(11.8, l3, 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(11.8, l4, 1e-10);
}
LOG_DEBUG(<< "Test Frobenius");
{
double elements[][4]{{1.0, 2.3, 2.1, -1.3},
{2.3, 5.3, 0.1, -0.8},
{2.1, 0.1, 3.1, 0.0},
{-1.3, -0.8, 0.0, 0.3}};
double flat[16];
for (std::size_t i = 0; i < 4; ++i) {
for (std::size_t j = 0; j < 4; ++j) {
flat[4 * j + i] = elements[i][j];
}
}
TDoubleVecVec elements_;
elements_.emplace_back(std::begin(elements[0]), std::end(elements[0]));
elements_.emplace_back(std::begin(elements[1]), std::end(elements[1]));
elements_.emplace_back(std::begin(elements[2]), std::end(elements[2]));
elements_.emplace_back(std::begin(elements[3]), std::end(elements[3]));
TMatrix4 matrix1(elements);
TMatrix matrix2(elements_);
TDenseMatrix matrix3(4, 4);
matrix3 << elements[0][0], elements[0][1], elements[0][2], elements[0][3],
elements[1][0], elements[1][1], elements[1][2], elements[1][3],
elements[2][0], elements[2][1], elements[2][2], elements[2][3],
elements[3][0], elements[3][1], elements[3][2], elements[3][3];
TMappedMatrix matrix4(flat, 4, 4);
LOG_DEBUG(<< matrix4);
double f1 = maths::common::las::frobenius(matrix1);
double f2 = maths::common::las::frobenius(matrix2);
double f3 = maths::common::las::frobenius(matrix3);
double f4 = maths::common::las::frobenius(matrix4);
LOG_DEBUG(<< " f1 = " << f1);
LOG_DEBUG(<< " f1 = " << f2);
LOG_DEBUG(<< " f1 = " << f3);
LOG_DEBUG(<< " f1 = " << f4);
BOOST_REQUIRE_CLOSE_ABSOLUTE(7.92906047397799, f1, 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(7.92906047397799, f2, 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(7.92906047397799, f3, 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(7.92906047397799, f4, 1e-10);
}
LOG_DEBUG(<< "Test inner");
{
TVector4 vector5(components[1]);
TVector vector6(std::begin(components[1]), std::end(components[1]));
TDenseVector vector7(4);
vector7 << components[1][0], components[1][1], components[1][2],
components[1][3];
TMappedVector vector8(components[1], 4);
double i1 = maths::common::las::inner(vector1, vector5);
double i2 = maths::common::las::inner(vector2, vector6);
double i3 = maths::common::las::inner(vector3, vector7);
double i4 = maths::common::las::inner(vector4, vector8);
LOG_DEBUG(<< "i1 = " << i1);
LOG_DEBUG(<< "i1 = " << i2);
LOG_DEBUG(<< "i1 = " << i3);
LOG_DEBUG(<< "i1 = " << i4);
BOOST_REQUIRE_CLOSE_ABSOLUTE(21.77, i1, 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(21.77, i2, 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(21.77, i3, 1e-10);
BOOST_REQUIRE_CLOSE_ABSOLUTE(21.77, i4, 1e-10);
}
LOG_DEBUG(<< "Test outer");
{
TMatrix4 outer1 = maths::common::las::outer(vector1);
TMatrix outer2 = maths::common::las::outer(vector2);
TDenseMatrix outer3 = maths::common::las::outer(vector3);
TDenseMatrix outer4 = maths::common::las::outer(vector4);
LOG_DEBUG(<< "outer = " << outer1);
LOG_DEBUG(<< "outer = " << outer2);
LOG_DEBUG(<< "outer =\n" << outer3);
for (std::size_t i = 0; i < 4; ++i) {
for (std::size_t j = 0; j < 4; ++j) {
BOOST_REQUIRE_EQUAL(vector1(i) * vector1(j), outer1(i, j));
BOOST_REQUIRE_EQUAL(vector2(i) * vector2(j), outer2(i, j));
BOOST_REQUIRE_EQUAL(vector3(i) * vector3(j), outer3(i, j));
BOOST_REQUIRE_EQUAL(vector4(i) * vector4(j), outer4(i, j));
}
}
}
}
BOOST_AUTO_TEST_CASE(testMemoryMapped) {
using TDenseVector = maths::common::CDenseVector<double>;
using TDenseMatrix = maths::common::CDenseMatrix<double>;
using TMappedFloatVector = maths::common::CMemoryMappedDenseVector<float>;
using TMappedFloatMatrix = maths::common::CMemoryMappedDenseMatrix<float>;
{
float components[4];
TMappedFloatVector mappedVector(components, 4);
TDenseVector vector{4};
vector << 1.1, 2.7, 0.1, -3.0;
mappedVector = vector;
for (int i = 0; i < 4; ++i) {
BOOST_REQUIRE_CLOSE(vector(i), mappedVector(i), 1e-4);
}
}
{
float components[9];
TMappedFloatMatrix mappedMatrix(components, 3, 3);
TDenseMatrix matrix{3, 3};
matrix << 1.1, 2.7, 0.1, 3.0, 1.2, 0.4, 5.1, 0.2, 9.3;
mappedMatrix = matrix;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
BOOST_REQUIRE_CLOSE(matrix(i, j), mappedMatrix(i, j), 1e-4);
}
}
}
}
BOOST_AUTO_TEST_CASE(testPersist) {
// Check conversion to and from delimited is idempotent and parsing
// bad input produces an error.
{
double matrix_[][4]{{1.0, 2.1, 1.5, 0.1},
{2.1, 2.2, 3.7, 0.6},
{1.5, 3.7, 0.4, 8.1},
{0.1, 0.6, 8.1, 4.3}};
maths::common::CSymmetricMatrixNxN<double, 4> matrix(matrix_);
std::string str = matrix.toDelimited();
maths::common::CSymmetricMatrixNxN<double, 4> restoredMatrix;
BOOST_TEST_REQUIRE(restoredMatrix.fromDelimited(str));
LOG_DEBUG(<< "delimited = " << str);
for (std::size_t i = 0; i < 4; ++i) {
for (std::size_t j = 0; j < 4; ++j) {
BOOST_REQUIRE_EQUAL(matrix(i, j), restoredMatrix(i, j));
}
}
BOOST_TEST_REQUIRE(!restoredMatrix.fromDelimited(std::string()));
std::string bad("0.1,0.3,0.5,3");
BOOST_TEST_REQUIRE(!restoredMatrix.fromDelimited(bad));
bad = "0.1,0.3,a,0.1,0.3,0.1,0.3,0.1,1.0,3";
BOOST_TEST_REQUIRE(!restoredMatrix.fromDelimited(bad));
}
{
double vector_[]{11.2, 2.1, 1.5};
maths::common::CVectorNx1<double, 3> vector(vector_);
std::string str = vector.toDelimited();
maths::common::CVectorNx1<double, 3> restoredVector;
BOOST_TEST_REQUIRE(restoredVector.fromDelimited(str));
LOG_DEBUG(<< "delimited = " << str);
for (std::size_t i = 0; i < 3; ++i) {
BOOST_REQUIRE_EQUAL(vector(i), restoredVector(i));
}
BOOST_TEST_REQUIRE(!restoredVector.fromDelimited(std::string()));
std::string bad("0.1,0.3,0.5,3");
BOOST_TEST_REQUIRE(!restoredVector.fromDelimited(bad));
bad = "0.1,0.3,a";
BOOST_TEST_REQUIRE(!restoredVector.fromDelimited(bad));
}
{
maths::common::CDenseVector<double> origVector(4);
origVector << 1.3, 2.4, 3.1, 5.1;
std::ostringstream origJson;
core::CJsonStatePersistInserter::persist(
origJson, std::bind_front(&maths::common::CDenseVector<double>::acceptPersistInserter,
&origVector));
LOG_DEBUG(<< "vector JSON representation:\n" << origJson.str());
// Restore the JSON into a new vector.
std::istringstream origJsonStrm{"{\"topLevel\" : " + origJson.str() + "}"};
core::CJsonStateRestoreTraverser traverser(origJsonStrm);
maths::common::CDenseVector<double> restoredVector;
BOOST_TEST_REQUIRE(traverser.traverseSubLevel(std::bind_front(
&maths::common::CDenseVector<double>::acceptRestoreTraverser, &restoredVector)));
BOOST_REQUIRE_EQUAL(origVector.checksum(), restoredVector.checksum());
}
}
BOOST_AUTO_TEST_SUITE_END()