/* This file is part of MAUS: http:// micewww.pp.rl.ac.uk:8080/projects/maus * * MAUS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MAUS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MAUS. If not, see . */ /* Author: Peter Lane */ #include #include #include #include #include #include "gtest/gtest.h" #include "CLHEP/Matrix/Matrix.h" #include "CLHEP/Random/Random.h" #include "Maths/Complex.hh" #include "Maths/Matrix.hh" #include "Maths/Vector.hh" using MAUS::complex; using MAUS::Matrix; using MAUS::Vector; using MAUS::operator *; using MAUS::operator +=; using MAUS::operator -; using MAUS::operator ==; using MAUS::operator <<; using MAUS::real; using MAUS::imag; // Defined in ComplexTest.cc bool equal(const complex c1, const complex c2); bool equal(double c1, double c2); class MatrixTest : public ::testing::Test { public: MatrixTest() { for (size_t index = 0; index < kDataSize; ++index) { kDoubleDatadoubled_[index] = kDoubleData[index] * 2; kComplexDatadoubled_[index] = kComplexData[index] * 2.; kComplexDatamultiple_[index] = kComplexData[index] * kComplexScalingFactor; } } template bool elements_equal(const Matrix& matrix, const Tmplt value) { bool are_equal = true; if ( matrix.number_of_rows() != kRows || matrix.number_of_columns() != kColumns) { return false; } for (size_t row = 1; row <= kRows; ++row) { for (size_t column = 1; column <= kColumns; ++column) { are_equal &= ::equal(matrix(row, column), value); } } return are_equal; } template bool equal(const Matrix& matrix, const Tmplt* array) { bool are_equal = true; if ( matrix.number_of_rows() != kRows || matrix.number_of_columns() != kColumns) { return false; } for (size_t row = 1; row <= kRows; ++row) { for (size_t column = 1; column <= kColumns; ++column) { are_equal &= ::equal(matrix(row, column), array[(row-1)*kColumns+(column-1)]); } } return are_equal; } template bool equal(const Matrix& m1, const Matrix& m2) { bool are_equal = true; if ( m1.number_of_rows() != m2.number_of_rows() || m1.number_of_columns() != m2.number_of_columns()) { return false; } for (size_t i = 1; i <= m1.number_of_rows(); i++) for (size_t j = 1; j <= m1.number_of_columns(); j++) are_equal &= ::equal(m1(i, j), m2(i, j)); return are_equal; } template bool equal(const Vector& v1, const Vector& v2) { bool are_equal = true; if (v1.size() != v2.size()) { return false; } for (size_t i = 1; i <= v1.size(); ++i) are_equal &= ::equal(v1(i), v2(i)); return are_equal; } protected: static const size_t kDataSize; static const double kDoubleData[24]; static const complex kComplexData[24]; static const size_t kSize; // square matrix static const size_t kRows; static const size_t kColumns; static const size_t kSubmatrixStartRow; static const size_t kSubmatrixRows; static const size_t kSubmatrixStartColumn; static const size_t kSubmatrixColumns; double kDoubleDatadoubled_[24]; complex kComplexDatadoubled_[24]; static const complex kComplexScalingFactor; complex kComplexDatamultiple_[24]; }; template bool MatrixTest::elements_equal(const Matrix& matrix, const double value); template bool MatrixTest::elements_equal(const Matrix& matrix, const complex value); template bool MatrixTest::equal(const Matrix& matrix, const double* array); template bool MatrixTest::equal(const Matrix& matrix, const complex* array); template bool MatrixTest::equal(const Matrix& m1, const Matrix& m2); template bool MatrixTest::equal(const Matrix& m1, const Matrix& m2); template bool MatrixTest::equal(const Vector& v1, const Vector& v2); template bool MatrixTest::equal(const Vector& v1, const Vector& v2); // **************************************** // MatrixTest static const initializations // **************************************** const double MatrixTest::kDoubleData[24] = { 0., 1., 2., 3., -5., -8., -13., -21., 34.5, 55.7, 89.3, 144.2, -32.5, -57.5, -91.2, -146.3, 3.14, -1.59, 2.65, -3.58, 9.79, -3.23, 8.46, -2.64 }; const complex MatrixTest::kComplexData[24] = { MAUS::Complex::complex(0., -2.64), MAUS::Complex::complex(1., 8.46), MAUS::Complex::complex(2., -3.23), MAUS::Complex::complex(3., 9.79), MAUS::Complex::complex(-5., -3.58), MAUS::Complex::complex(-8., 2.65), MAUS::Complex::complex(-13., -1.59), MAUS::Complex::complex(-21, 3.14), MAUS::Complex::complex(34.5, -146.3), MAUS::Complex::complex(55.7, -91.2), MAUS::Complex::complex(89.3, -57.5), MAUS::Complex::complex(144.2, -32.5), MAUS::Complex::complex(-32.5, 144.2), MAUS::Complex::complex(-57.5, 89.3), MAUS::Complex::complex(-91.2, 55.7), MAUS::Complex::complex(-146.3, 34.5), MAUS::Complex::complex(3.14, -21.), MAUS::Complex::complex(-1.59, -13.), MAUS::Complex::complex(2.65, -8.), MAUS::Complex::complex(-3.58, -5.), MAUS::Complex::complex(9.79, 3.), MAUS::Complex::complex(-3.23, 2.), MAUS::Complex::complex(8.46, 1.), MAUS::Complex::complex(-2.64, 0.) }; const size_t MatrixTest::kDataSize = 24; const size_t MatrixTest::kSize = 4; const size_t MatrixTest::kRows = 3; const size_t MatrixTest::kColumns = 8; const size_t MatrixTest::kSubmatrixRows = 2; const size_t MatrixTest::kSubmatrixColumns = 4; const size_t MatrixTest::kSubmatrixStartRow = 2; const size_t MatrixTest::kSubmatrixStartColumn = 3; const MAUS::complex MatrixTest::kComplexScalingFactor = MAUS::Complex::complex(2, -5); // *********** // test cases // *********** TEST_F(MatrixTest, DefaultConstructor) { Matrix matrix_d0; ASSERT_EQ(matrix_d0.number_of_rows(), (size_t) 0); ASSERT_EQ(matrix_d0.number_of_columns(), (size_t) 0); Matrix matrix_c0; ASSERT_EQ(matrix_c0.number_of_rows(), (size_t) 0); ASSERT_EQ(matrix_c0.number_of_columns(), (size_t) 0); } TEST_F(MatrixTest, SingleElementValueConstructor) { Matrix matrix_d0(kRows, kColumns, kDoubleData[9]); EXPECT_TRUE(elements_equal(matrix_d0, kDoubleData[9])); Matrix matrix_c0(kRows, kColumns, kComplexData[17]); EXPECT_TRUE(elements_equal(matrix_c0, kComplexData[17])); } TEST_F(MatrixTest, ConstructorFromArray) { Matrix matrix_d0(kRows, kColumns, kDoubleData); EXPECT_TRUE(equal(matrix_d0, kDoubleData)); Matrix matrix_c0(kRows, kColumns, kComplexData); EXPECT_TRUE(equal(matrix_c0, kComplexData)); } TEST_F(MatrixTest, IndexingElements) { Matrix matrix_d1(kRows, kColumns, kDoubleData); const Matrix matrix_d2(kRows, kColumns, kDoubleData); for (size_t i = 0; i < kRows; ++i) { for (size_t j = 0; j < kColumns; ++j) { // check that it returns correct value EXPECT_EQ(matrix_d1(i+1, j+1), kDoubleData[i*kColumns + j]); // check that it works with const matrix EXPECT_EQ(matrix_d2(i+1, j+1), kDoubleData[i*kColumns + j]); // check that it can set the correct value matrix_d1(i+1, j+1) = 4.; EXPECT_EQ(matrix_d1(i+1, j+1), 4.); } } // empty matrix bool caught_exception = false; try { Matrix matrix_d3; matrix_d3(20, 20) = 5.0; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); caught_exception = false; try { Matrix matrix_d3; const double element_d = matrix_d3(20, 20); std::cout << element_d; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); // row out of bounds caught_exception = false; try { matrix_d1(-1, 1) = 5.0; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); caught_exception = false; try { const double element_d = matrix_d1(-1, 1); std::cout << element_d; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); // column out of bonds caught_exception = false; try { matrix_d1(1, -1) = 5.0; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); caught_exception = false; try { const double element_d = matrix_d1(1, -1); std::cout << element_d; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); Matrix matrix_c1(kRows, kColumns, kComplexData); // check that it works for const as well const Matrix matrix_c2(kRows, kColumns, kComplexData); complex new_value1 = MAUS::Complex::complex(4., -2.); complex new_value2 = MAUS::Complex::complex(-6., 15.); for (size_t i = 0; i < kRows; ++i) { for (size_t j = 0; j < kColumns; ++j) { // check that it returns correct value EXPECT_TRUE(::equal(matrix_c1(i+1, j+1), kComplexData[i*kColumns + j])); // check that it works with const matrix EXPECT_TRUE(::equal(matrix_c2(i+1, j+1), kComplexData[i*kColumns + j])); // check that it can set the correct value matrix_c1(i+1, j+1) = new_value1; EXPECT_TRUE(::equal(matrix_c1(i+1, j+1), new_value1)); } } // empty matrix MAUS::complex some_value = MAUS::Complex::complex(5.0, 6.0); caught_exception = false; try { Matrix matrix_c3; matrix_c3(20, 20) = some_value; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); caught_exception = false; try { Matrix matrix_c3; const MAUS::complex element_c = matrix_c3(20, 20); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); // row out of bounds caught_exception = false; try { matrix_c1(-1, 1) = some_value; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); caught_exception = false; try { const MAUS::complex element_c = matrix_c1(-1, 1); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); // column out of bounds caught_exception = false; try { matrix_c1(1, -1) = some_value; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); caught_exception = false; try { const MAUS::complex element_c = matrix_c1(1, -1); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); } TEST_F(MatrixTest, CopyConstructor) { // should work if matrix is const const Matrix matrix_d0(kRows, kColumns, kDoubleData); const Matrix matrix_d1(matrix_d0); EXPECT_TRUE(equal(matrix_d0, matrix_d1)); // check that handles 0 length okay Matrix matrix_d2; Matrix matrix_d3(matrix_d2); EXPECT_TRUE(equal(matrix_d2, matrix_d3)); // should work if matrix is const const Matrix matrix_c0(kRows, kColumns, kComplexData); const Matrix matrix_c1(matrix_c0); EXPECT_TRUE(equal(matrix_c0, matrix_c1)); // check that handles 0 length okay Matrix matrix_c2; Matrix matrix_c3(matrix_c2); EXPECT_TRUE(equal(matrix_c2, matrix_c3)); } TEST_F(MatrixTest, HepMatrixConstructor) { ::CLHEP::HepRandom hep_random; const ::CLHEP::HepMatrix matrix_hep0(kRows, kColumns, hep_random); const Matrix matrix_d0(matrix_hep0); ASSERT_EQ(matrix_d0.number_of_rows(), (size_t) kRows); ASSERT_EQ(matrix_d0.number_of_columns(), (size_t) kColumns); for (size_t i = 1; i <= kRows; ++i) for (size_t j = 1; j <= kColumns; ++j) EXPECT_EQ(matrix_d0(i, j), matrix_hep0(i, j)); // check that handles 0 length okay ::CLHEP::HepMatrix matrix_hep1; Matrix matrix_d1(matrix_hep1); ASSERT_EQ(matrix_d1.number_of_rows(), (size_t) 0); ASSERT_EQ(matrix_d1.number_of_columns(), (size_t) 0); const Matrix matrix_c0(matrix_hep0); ASSERT_EQ(matrix_c0.number_of_rows(), (size_t) kRows); ASSERT_EQ(matrix_c0.number_of_columns(), (size_t) kColumns); for (size_t i = 1; i <= kRows; ++i) for (size_t j = 1; j <= kColumns; ++j) EXPECT_EQ(matrix_c0(i, j), MAUS::Complex::complex(matrix_hep0(i, j))); // check that handles 0 length okay Matrix matrix_c1(matrix_hep1); ASSERT_EQ(matrix_c1.number_of_rows(), (size_t) 0); ASSERT_EQ(matrix_c1.number_of_columns(), (size_t) 0); } TEST_F(MatrixTest, ConstructorSizeOnly) { Matrix matrix_d0(kRows, kColumns); EXPECT_TRUE(elements_equal(matrix_d0, 0.)); Matrix matrix_c0(kRows, kColumns); EXPECT_TRUE(elements_equal(matrix_c0, MAUS::Complex::complex(0.0))); } TEST_F(MatrixTest, ConstructorFill) { Matrix matrix_d0(kRows, kColumns, kDoubleData[4]); EXPECT_TRUE(elements_equal(matrix_d0, kDoubleData[4])); Matrix matrix_c0(kRows, kColumns, kComplexData[4]); EXPECT_TRUE(elements_equal(matrix_c0, kComplexData[4])); } TEST_F(MatrixTest, IndexingRows) { Matrix matrix_d0(kRows, kColumns, kDoubleData); for (size_t row = 0; row < kRows; ++row) { Vector row_vector = matrix_d0.row(row+1); for (size_t column = 0; column < kColumns; ++column) { EXPECT_EQ(row_vector[column], kDoubleData[row*kColumns + column]); } } // empty matrix bool caught_exception = false; try { Matrix matrix_d3; matrix_d3.row(20); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); // row out of bounds caught_exception = false; try { matrix_d0.row(20); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); Matrix matrix_c0(kRows, kColumns, kComplexData); for (size_t row = 0; row < kRows; ++row) { Vector row_vector = matrix_c0.row(row+1); for (size_t column = 0; column < kColumns; ++column) { EXPECT_EQ(row_vector[column], kComplexData[row*kColumns + column]); } } // empty matrix caught_exception = false; try { Matrix matrix_c3; matrix_c3.row(20); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); // row out of bounds caught_exception = false; try { matrix_c0.row(20); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); } TEST_F(MatrixTest, IndexingColumns) { Matrix matrix_d0(kRows, kColumns, kDoubleData); for (size_t column = 0; column < kColumns; ++column) { Vector column_vector = matrix_d0.column(column+1); for (size_t row = 0; row < kRows; ++row) { EXPECT_EQ(column_vector[row], kDoubleData[row*kColumns + column]); } } // empty matrix bool caught_exception = false; try { Matrix matrix_d3; matrix_d3.column(20); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); // column out of bounds caught_exception = false; try { matrix_d0.column(20); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); Matrix matrix_c0(kRows, kColumns, kComplexData); for (size_t column = 0; column < kColumns; ++column) { Vector column_vector = matrix_c0.column(column+1); for (size_t row = 0; row < kRows; ++row) { EXPECT_EQ(column_vector[row], kComplexData[row*kColumns + column]); } } // empty matrix caught_exception = false; try { Matrix matrix_c3; matrix_c3.column(20); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); // column out of bounds caught_exception = false; try { matrix_c0.column(20); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); } TEST_F(MatrixTest, Submatrix) { Matrix matrix_d0(kRows, kColumns, kDoubleData); // union of the last two kRows and middle four kColumns Matrix submatrix_d0 = matrix_d0.submatrix( kSubmatrixStartRow, kSubmatrixRows, kSubmatrixStartColumn, kSubmatrixColumns); ASSERT_EQ((size_t) kSubmatrixRows, submatrix_d0.number_of_rows()); ASSERT_EQ((size_t) kSubmatrixColumns, submatrix_d0.number_of_columns()); int index; for (size_t row = 1; row <= kSubmatrixRows; ++row) { for (size_t column = 1; column <= kSubmatrixColumns; ++column) { index = (kSubmatrixStartRow+row-2)*kColumns + kSubmatrixStartColumn+column-2; EXPECT_EQ(kDoubleData[index], submatrix_d0(row, column)); } } Matrix matrix_c0(kRows, kColumns, kComplexData); // union of the last two kRows and middle four kColumns Matrix submatrix_c0 = matrix_c0.submatrix( kSubmatrixStartRow, kSubmatrixRows, kSubmatrixStartColumn, kSubmatrixColumns); ASSERT_EQ(submatrix_c0.number_of_rows(), (size_t) kSubmatrixRows); ASSERT_EQ(submatrix_c0.number_of_columns(), (size_t) kSubmatrixColumns); for (size_t row = 1; row <= kSubmatrixRows; ++row) { for (size_t column = 1; column <= kSubmatrixColumns; ++column) { index = (kSubmatrixStartRow+row-2)*kColumns + kSubmatrixStartColumn+column-2; EXPECT_TRUE(::equal(submatrix_c0(row, column), kComplexData[index])); } } } TEST_F(MatrixTest, Comparison) { // *** double equality *** // self-equality Matrix matrix_d0(kRows, kColumns, kDoubleData); EXPECT_TRUE(matrix_d0 == matrix_d0); // identically prepared equality Matrix matrix_d1(kRows, kColumns, kDoubleData); EXPECT_TRUE(matrix_d0 == matrix_d1); // empty equality Matrix matrix_d2; Matrix matrix_d3; EXPECT_TRUE(matrix_d2 == matrix_d3); // *** double inequality *** // different sizes Matrix matrix_d4 = matrix_d0.submatrix(1, 1, kRows-1, kColumns-1); EXPECT_TRUE(matrix_d0 != matrix_d4); EXPECT_FALSE(matrix_d0 == matrix_d4); // empty/non-empty EXPECT_TRUE(matrix_d2 != matrix_d0); EXPECT_FALSE(matrix_d2 == matrix_d0); // same size, different elements Matrix matrix_d5(kRows, kColumns, kDoubleData); Matrix matrix_d6(kRows, kColumns); for (size_t row = 1; row <= kSubmatrixRows; ++row) { for (size_t column = 1; column <= kSubmatrixColumns; ++column) { matrix_d6(row, column) = static_cast (row*column); } } EXPECT_TRUE(matrix_d5 != matrix_d6); EXPECT_FALSE(matrix_d5 == matrix_d6); // *** complex equality *** // self-equality Matrix matrix_c0(kRows, kColumns, kComplexData); EXPECT_TRUE(matrix_c0 == matrix_c0); // identically prepared equality Matrix matrix_c1(kRows, kColumns, kComplexData); EXPECT_TRUE(matrix_c0 == matrix_c1); // empty equality Matrix matrix_c2; Matrix matrix_c3; EXPECT_TRUE(matrix_c2 == matrix_c3); // *** complex inequality *** // different sizes Matrix matrix_c4 = matrix_c0.submatrix(1, 1, kRows-1, kColumns-1); EXPECT_TRUE(matrix_c0 != matrix_c4); EXPECT_FALSE(matrix_c0 == matrix_c4); // empty/non-empty EXPECT_TRUE(matrix_c2 != matrix_c0); EXPECT_FALSE(matrix_c2 == matrix_c0); // same size, different elements Matrix matrix_c5(kRows, kColumns, kComplexData); Matrix matrix_c6(kRows, kColumns); for (size_t row = 1; row <= kSubmatrixRows; ++row) { for (size_t column = 1; column <= kSubmatrixColumns; ++column) { matrix_c6(row, column) = MAUS::Complex::complex(row*column); } } EXPECT_TRUE(matrix_c5 != matrix_c6); EXPECT_FALSE(matrix_c5 == matrix_c6); } TEST_F(MatrixTest, Assignment) { // *** double assignment *** // plain vanilla assignment Matrix matrix_d0(kRows, kColumns, kDoubleData); Matrix matrix_d1(kRows, kColumns); matrix_d1 = matrix_d0; EXPECT_TRUE(equal(matrix_d0, matrix_d1)); // check special assignement to null matrix Matrix matrix_d2; matrix_d2 = matrix_d0; EXPECT_TRUE(equal(matrix_d2, matrix_d0)); // check bad assignment to differently sized matrix bool caught_exception = false; try { Matrix matrix_d3(1, 12); matrix_d3 = matrix_d0; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); // *** complex assignment *** // plain vanilla assignment Matrix matrix_c0(kRows, kColumns, kComplexData); Matrix matrix_c1(kRows, kColumns); matrix_c1 = matrix_c0; EXPECT_TRUE(equal(matrix_c0, matrix_c1)); // check special assignement to null matrix Matrix matrix_c2; matrix_c2 = matrix_c0; EXPECT_TRUE(equal(matrix_c2, matrix_c0)); // check bad assignment to differently sized matrix caught_exception = false; try { Matrix matrix_c3(1, 12); matrix_c3 = matrix_c0; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); } TEST_F(MatrixTest, Addition) { // add and assign Matrix matrix_d0(kRows, kColumns, kDoubleData); Matrix matrix_d1(kRows, kColumns); matrix_d1 += matrix_d0; ASSERT_TRUE(equal(matrix_d0, matrix_d1)); // add Matrix matrix_d2 = matrix_d0 + matrix_d1; EXPECT_TRUE(equal(matrix_d2, kDoubleDatadoubled_)); // bad addition of differently sized matrices bool caught_exception = false; try { Matrix matrix_d3(kRows+1, kColumns); matrix_d3 += matrix_d0; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); caught_exception = false; try { Matrix matrix_d3(kRows, kColumns+1); matrix_d3 += matrix_d0; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); // add and assign Matrix matrix_c0(kRows, kColumns, kComplexData); Matrix matrix_c1(kRows, kColumns); matrix_c1 += matrix_c0; ASSERT_TRUE(equal(matrix_c0, matrix_c1)); // add Matrix matrix_c2 = matrix_c0 + matrix_c1; EXPECT_TRUE(equal(matrix_c2, kComplexDatadoubled_)); // bad addition of differently sized matrices caught_exception = false; try { Matrix matrix_c3(kRows+1, kColumns); matrix_c3 += matrix_c0; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); caught_exception = false; try { Matrix matrix_c3(kRows, kColumns+1); matrix_c3 += matrix_c0; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); } TEST_F(MatrixTest, Subtraction) { // subtract and assign Matrix matrix_d0(kRows, kColumns, kDoubleData); Matrix matrix_d1(kRows, kColumns, kDoubleDatadoubled_); Matrix matrix_d2(matrix_d1); matrix_d2 -= matrix_d0; ASSERT_TRUE(equal(matrix_d0, matrix_d2)); // subtract Matrix matrix_d3 = matrix_d1 - matrix_d0; EXPECT_TRUE(equal(matrix_d3, kDoubleData)); // bad subtraction of differently sized matrices bool caught_exception = false; try { Matrix matrix_d3(kRows+1, kColumns); matrix_d3 -= matrix_d0; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); caught_exception = false; try { Matrix matrix_d3(kRows, kColumns+1); matrix_d3 -= matrix_d0; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); // subtract and assign Matrix matrix_c0(kRows, kColumns, kComplexData); Matrix matrix_c1(kRows, kColumns, kComplexDatadoubled_); Matrix matrix_c2(matrix_c1); matrix_c2 -= matrix_c0; ASSERT_TRUE(equal(matrix_c0, matrix_c2)); // subtract Matrix matrix_c3 = matrix_c1 - matrix_c0; EXPECT_TRUE(equal(matrix_c3, kComplexData)); // bad subtraction of differently sized matrices caught_exception = false; try { Matrix matrix_c3(kRows+1, kColumns); matrix_c3 -= matrix_c0; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); caught_exception = false; try { Matrix matrix_c3(kRows, kColumns+1); matrix_c3 -= matrix_c0; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); } TEST_F(MatrixTest, Multiplication) { // multiple and assign double d1[6] = {1, 2, 3, 7, 6, 5}; double d2[6] = {3, 4, 5, 2, 8, 3}; const Matrix matrix_d0(3, 2, d1); const Matrix matrix_d1(2, 3, d2); Matrix matrix_d2(3, 3); for (int i = 1; i <= 3; i++) for (int j = 1; j <= 3; j++) for (int k = 1; k <= 2; k++) matrix_d2(i, j) += matrix_d0(i, k)*matrix_d1(k, j); Matrix matrix_d3(matrix_d0); matrix_d3 *= matrix_d1; ASSERT_TRUE(equal(matrix_d2, matrix_d3)); // multiply Matrix matrix_d4 = matrix_d0 * matrix_d1; EXPECT_TRUE(equal(matrix_d2, matrix_d4)); // bad multiplication of differently sized matrices bool caught_exception = false; try { Matrix matrix_d3(kColumns, kRows+1); matrix_d3 *= matrix_d0; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); // multiply and assign complex c1[6]; complex c2[6]; for (int i = 0; i < 6; i++) { c1[i] = MAUS::Complex::complex(d1[i], d2[i]); } for (int i = 0; i < 6; i++) { c2[i] = MAUS::Complex::complex(-d2[i]*d1[i], d1[i]); } const Matrix matrix_c0(3, 2, c1); const Matrix matrix_c1(2, 3, c2); Matrix matrix_c2(3, 3); for (int i = 1; i <= 3; i++) for (int j = 1; j <= 3; j++) for (int k = 1; k <= 2; k++) matrix_c2(i, j) += matrix_c0(i, k) * matrix_c1(k, j); Matrix matrix_c3(matrix_c0); matrix_c3 *= matrix_c1; ASSERT_TRUE(equal(matrix_c2, matrix_c3)); // multiply Matrix matrix_c4 = matrix_c0 * matrix_c1; EXPECT_TRUE(equal(matrix_c2, matrix_c4)); // bad multiplication of differently sized matrices caught_exception = false; try { Matrix matrix_c3(kColumns, kRows+1); matrix_c3 *= matrix_c0; } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); } TEST_F(MatrixTest, ScalarMultiplication) { // *** double multiplication *** // multiply and assign const Matrix matrix_d0(kRows, kColumns, kDoubleData); Matrix matrix_d1(matrix_d0); matrix_d1 *= 2.; ASSERT_TRUE(equal(matrix_d1, kDoubleDatadoubled_)); // multiplication on the right by a scalar Matrix matrix_d2 = matrix_d0 * 2.; ASSERT_TRUE(equal(matrix_d2, kDoubleDatadoubled_)); // multiplication on the left by a scalar Matrix matrix_d3 = 2. * matrix_d0; ASSERT_TRUE(equal(matrix_d3, kDoubleDatadoubled_)); // *** double multiplication *** // multiply and assign const Matrix matrix_c0(kRows, kColumns, kComplexData); Matrix matrix_c1(matrix_c0); matrix_c1 *= kComplexScalingFactor; ASSERT_TRUE(equal(matrix_c1, kComplexDatamultiple_)); // multiplication on the right by a scalar Matrix matrix_c2 = matrix_c0 * kComplexScalingFactor; ASSERT_TRUE(equal(matrix_c2, kComplexDatamultiple_)); // multiplication on the left by a scalar Matrix matrix_c3 = kComplexScalingFactor * matrix_c0; ASSERT_TRUE(equal(matrix_c3, kComplexDatamultiple_)); } TEST_F(MatrixTest, ScalarDivision) { const Matrix matrix_d0(kRows, kColumns, kDoubleDatadoubled_); Matrix matrix_d1(matrix_d0); matrix_d1 /= 2.; ASSERT_TRUE(equal(matrix_d1, kDoubleData)); Matrix matrix_d2 = matrix_d0 / 2.; ASSERT_TRUE(equal(matrix_d2, kDoubleData)); const Matrix matrix_c0(kRows, kColumns, kComplexDatamultiple_); Matrix matrix_c1(matrix_c0); matrix_c1 /= kComplexScalingFactor; ASSERT_TRUE(equal(matrix_c1, kComplexData)); Matrix matrix_c2 = matrix_c0 / kComplexScalingFactor; ASSERT_TRUE(equal(matrix_c2, kComplexData)); } TEST_F(MatrixTest, ComplexComposition) { // test construction of a complex matrix from one double matrix (real part) const Matrix matrix_c0(kRows, kColumns, kComplexData); const Matrix matrix_real(kRows, kColumns, kDoubleData); Matrix matrix_c1(kRows, kColumns); for (size_t row = 1; row <= kRows; ++row) { for (size_t column = 1; column <= kColumns; ++column) { matrix_c1(row, column) = MAUS::Complex::complex(kDoubleData[(row-1)*kColumns+(column-1)]); } } Matrix matrix_c2 = MAUS::Complex::complex(matrix_real); ASSERT_TRUE(equal(matrix_c2, matrix_c1)); const Matrix matrix_c3(matrix_real); ASSERT_TRUE(equal(matrix_c3, matrix_c1)); // test construction of a complex matrix from two double matrices (real, imag) double kDoubleDatareversed[kDataSize]; for (size_t index = 0; index < kDataSize; ++index) { kDoubleDatareversed[kDataSize-index-1] = kDoubleData[index]; } const Matrix matrix_imag(kRows, kColumns, kDoubleDatareversed); Matrix matrix_c4 = MAUS::Complex::complex(matrix_real, matrix_imag); ASSERT_TRUE(equal(matrix_c4, matrix_c0)); Matrix matrix_c5(matrix_real, matrix_imag); ASSERT_TRUE(equal(matrix_c5, matrix_c0)); // bad use of two differently sized real matrices bool caught_exception = false; Matrix matrix_real_bad_rows(kRows+1, kColumns); try { Matrix matrix_c6 = MAUS::Complex::complex(matrix_real_bad_rows, matrix_imag); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); caught_exception = false; Matrix matrix_real_bad_columns(kRows, kColumns+1); try { Matrix matrix_c6 = MAUS::Complex::complex(matrix_real_bad_columns, matrix_imag); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); } TEST_F(MatrixTest, ComplexDecomposition) { const Matrix matrix_c0(kRows, kColumns, kComplexData); const Matrix matrix_d0(kRows, kColumns, kDoubleData); // the real part of kComplexData is just kDoubleData Matrix matrix_real = real(matrix_c0); EXPECT_TRUE(equal(matrix_real, matrix_d0)); // subtract off the imaginary part of kComplexData // which is again just kDoubleData Matrix matrix_imag = imag(matrix_c0); // create a pure-imaginary matrix Matrix zero_matrix(kRows, kColumns); Matrix matrix_pure_imag = MAUS::Complex::complex(zero_matrix, matrix_imag); Matrix matrix_c1 = matrix_c0 - matrix_pure_imag; Matrix matrix_c2 = MAUS::Complex::complex(matrix_d0); EXPECT_TRUE(equal(matrix_c1, matrix_c2)); } TEST_F(MatrixTest, ComplexConjugation) { const Matrix matrix_c0(kRows, kColumns, kComplexData); const Matrix matrix_d0(kRows, kColumns, kDoubleDatadoubled_); Matrix matrix_d1 = real(matrix_c0 + conj(matrix_c0)); ASSERT_TRUE(equal(matrix_d1, matrix_d0)); } TEST_F(MatrixTest, Determinant) { // double matrix determinant Matrix matrix_d1(kSize, kSize, kDoubleData); for (size_t i = 1; i <= 2; ++i) { for (size_t j = 3; j <= 4; ++j) { matrix_d1(i, j) = 0.; matrix_d1(j, i) = 0.; } } // det is equal to det(upper block diag)*det(lower block diag) double upper_block_diag_det = matrix_d1(1, 1)*matrix_d1(2, 2) - matrix_d1(1, 2)*matrix_d1(2, 1); double lower_block_diag_det = matrix_d1(3, 3)*matrix_d1(4, 4) -matrix_d1(3, 4)*matrix_d1(4, 3); EXPECT_TRUE(::equal(upper_block_diag_det * lower_block_diag_det, determinant(matrix_d1))); // if 2 rows are identical, det is 0 Matrix matrix_d2(kSize, kSize, kDoubleData); for (size_t column = 1; column <= kSize; ++column) { matrix_d2(2, column) = matrix_d2(1, column); } EXPECT_TRUE(::equal(0., determinant(matrix_d2))); // bad non-square matrix determinant bool caught_exception = false; try { Matrix matrix_d3(kRows, kColumns); determinant(matrix_d3); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); // complex matrix determinant Matrix matrix_c1(kSize, kSize, kComplexData); for (size_t i = 1; i <= 2; ++i) { for (size_t j = 3; j <= 4; ++j) { matrix_c1(i, j) = MAUS::Complex::complex(0.0, 0.0); matrix_c1(j, i) = MAUS::Complex::complex(0.0, 0.0); } } // det is equal to det(upper block diag)*det(lower block diag) MAUS::complex upper_block_diag_det_c = matrix_c1(1, 1)*matrix_c1(2, 2) - matrix_c1(1, 2)*matrix_c1(2, 1); MAUS::complex lower_block_diag_det_c = matrix_c1(3, 3)*matrix_c1(4, 4) - matrix_c1(3, 4)*matrix_c1(4, 3); EXPECT_TRUE(::equal(upper_block_diag_det_c * lower_block_diag_det_c, determinant(matrix_c1))); // if 2 rows are identical, det is 0 Matrix matrix_c2(kSize, kSize, kComplexData); for (size_t column = 1; column <= kSize; ++column) { matrix_c2(2, column) = matrix_c2(1, column); } EXPECT_TRUE(::equal(MAUS::Complex::complex(0.0, 0.0), determinant(matrix_c2))); // bad non-square matrix determinant caught_exception = false; try { Matrix matrix_c3(kRows, kColumns); determinant(matrix_c3); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); } TEST_F(MatrixTest, Inverse) { // M^-1 * M = Identity (double) Matrix matrix_d1(kSize, kSize, kDoubleData); Matrix matrix_d2 = inverse(matrix_d1); Matrix matrix_d3 = matrix_d1 * matrix_d2; for (size_t row = 1; row <= kSize; ++row) { for (size_t column = 1; column <= kSize; ++column) { EXPECT_TRUE(::equal(row == column ? 1. : 0., matrix_d3(row, column))); } } // bad non-square matrix inverse bool caught_exception = false; try { Matrix matrix_d4(kRows, kColumns); inverse(matrix_d4); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); // non-invertible matrix (infinite elements) caught_exception = false; try { const double bad_array[] = { std::numeric_limits::infinity(), 1, 1, 1}; Matrix matrix_d4(2, 2, bad_array); inverse(matrix_d4); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); // non-invertible matrix (NaN elements) -- not sure how to do this } TEST_F(MatrixTest, Trace) { Matrix matrix_d1(kSize, kSize, kDoubleData); double tr = trace(matrix_d1); for (size_t i = 1; i <= kSize; i++) { tr -= matrix_d1(i, i); } EXPECT_DOUBLE_EQ(tr, 0.); } TEST_F(MatrixTest, Transpose) { // verify the transpose of a real-valued matrix Matrix matrix_da(kSize, kSize, kDoubleData); Matrix matrix_dt = transpose(matrix_da); ASSERT_EQ(matrix_da.number_of_rows(), matrix_dt.number_of_columns()); ASSERT_EQ(matrix_da.number_of_columns(), matrix_dt.number_of_rows()); for (size_t i = 1; i <= matrix_da.number_of_rows(); ++i) for (size_t j = 1; j <= matrix_da.number_of_columns(); ++j) EXPECT_EQ(matrix_da(i, j), matrix_dt(j, i)); // verify the creation of a real-valued, row-vector matrix Vector column_vector(kDoubleData, 6); Matrix row_vector = transpose(column_vector); for (size_t j = 1; j <= 6; ++j) { EXPECT_EQ(row_vector(1, j), column_vector(j)); } // verify the transpose of a complex-valued matrix Matrix matrix_ca(kSize, kSize, kComplexData); Matrix matrix_ct = transpose(matrix_ca); ASSERT_EQ(matrix_ca.number_of_rows(), matrix_ct.number_of_columns()); ASSERT_EQ(matrix_ca.number_of_columns(), matrix_ct.number_of_rows()); for (size_t i = 1; i <= matrix_ca.number_of_rows(); ++i) for (size_t j = 1; j <= matrix_ca.number_of_columns(); ++j) EXPECT_EQ(matrix_ca(i, j), matrix_ct(j, i)); } TEST_F(MatrixTest, Dagger) { complex herm_data[4] = { MAUS::Complex::complex(12.1, 0.), MAUS::Complex::complex(98.6, 100.0), MAUS::Complex::complex(98.6, -100.0), MAUS::Complex::complex(22.4, 0.) }; Matrix matrix(2, 2, herm_data); Matrix matrix_dagger = dagger(matrix); ASSERT_TRUE(equal(matrix_dagger, matrix)); // verify the creation of a complex-valued, row-vector matrix Vector column_vector(kComplexData, 6); Matrix row_vector = dagger(column_vector); for (size_t j = 1; j <= 6; ++j) { EXPECT_EQ(row_vector(1, j), MAUS::conj(column_vector(j))); } } TEST_F(MatrixTest, HepMatrix) { ::CLHEP::HepRandom hep_random; const Matrix matrix_d0(kRows, kColumns, kDoubleData); ::CLHEP::HepMatrix matrix_hep0 = MAUS::CLHEP::HepMatrix(matrix_d0); ASSERT_EQ((size_t) matrix_hep0.num_row(), matrix_d0.number_of_rows()); ASSERT_EQ((size_t) matrix_hep0.num_col(), matrix_d0.number_of_columns()); for (size_t row = 1; row <= kRows; ++row) { for (size_t column = 1; column <= kColumns; ++column) { EXPECT_TRUE(::equal(matrix_hep0(row, column), matrix_d0(row, column))); } } } TEST_F(MatrixTest, Eigen) { // verify the eigensystem equation M*E = e*E Matrix matrix_d0(kSize, kSize, kDoubleData); std::pair< Vector, Matrix > eigensys = eigensystem(matrix_d0); Vector eigenvals = eigensys.first; Matrix eigenvectors = eigensys.second; ASSERT_EQ(eigenvals.size(), kSize); ASSERT_EQ(eigenvectors.number_of_columns(), kSize); for (size_t i = 1; i < eigenvals.size(); ++i) { complex eigenvalue = eigenvals(i); Vector eigenvector = eigenvectors.column(i); EXPECT_TRUE(equal(MAUS::Complex::complex(matrix_d0) * eigenvector, eigenvalue * eigenvector)); } // verify that just getting the eigenvalues returns the same set from // eigensystem() (but perhaps in a different order) Vector eigenvals2 = eigenvalues(matrix_d0); ASSERT_EQ(eigenvals2.size(), kSize); for (size_t i = 1; i <= kSize; ++i) { bool found_match = false; for (size_t j = 1; j <= kSize; j++) { if (::equal(eigenvals2(i), eigenvals(j))) { found_match = true; } } EXPECT_TRUE(found_match); } // bad non-square matrix eigensystem bool caught_exception = false; try { Matrix matrix_d4(kRows, kColumns); eigenvalues(matrix_d4); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); caught_exception = false; try { Matrix matrix_d4(kRows, kColumns); eigensystem(matrix_d4); } catch (MAUS::Exception exc) { caught_exception = true; } EXPECT_TRUE(caught_exception); } TEST_F(MatrixTest, Negation) { Matrix matrix_d0(kRows, kColumns, kDoubleData); Matrix matrix_d1(-matrix_d0); for (size_t row = 1; row <= kRows; ++row) { for (size_t column = 1; column <= kColumns; ++column) { EXPECT_TRUE(::equal(matrix_d0(row, column), -matrix_d1(row, column))); } } Matrix matrix_c0(kRows, kColumns, kComplexData); Matrix matrix_c1(-matrix_c0); for (size_t row = 1; row <= kRows; ++row) { for (size_t column = 1; column <= kColumns; ++column) { EXPECT_TRUE(::equal(matrix_c0(row, column), -matrix_c1(row, column))); } } } TEST_F(MatrixTest, VectorMultiplication) { // reverse the order of a vector's elements using a matrix Matrix matrix_d0(kSize, kSize, kDoubleData); Matrix matrix_reverse_d(kSize, kSize); for (size_t i = 1; i <= kSize; ++i) { matrix_reverse_d(i, kSize-i+1) = 1.; } Vector vector_d0 = matrix_d0.row(1); Vector vector_product_d = matrix_reverse_d * vector_d0; for (size_t i = 1; i <= kSize; ++i) { EXPECT_TRUE(::equal(vector_product_d(i), vector_d0(kSize-i+1))); } // reverse the order of a vector's elements using a matrix Matrix matrix_c0(kSize, kSize, kComplexData); Matrix matrix_reverse_c(kSize, kSize); for (size_t i = 1; i <= kSize; ++i) { matrix_reverse_c(i, kSize-i+1) = MAUS::Complex::complex(1., 0.); } Vector vector_c0 = matrix_c0.row(1); Vector vector_product_c = matrix_reverse_c * vector_c0; for (size_t i = 1; i <= kSize; ++i) { EXPECT_TRUE(::equal(vector_product_c(i), vector_c0(kSize-i+1))); } } TEST_F(MatrixTest, Streaming) { std::stringstream double_stream; Matrix matrix_da(kRows, kColumns, kDoubleData); Matrix matrix_db; double_stream << matrix_da; double_stream >> matrix_db; EXPECT_TRUE(equal(matrix_da, matrix_db)); std::stringstream complex_stream; Matrix matrix_ca(kRows, kColumns, kComplexData); Matrix matrix_cb; complex_stream << matrix_ca; complex_stream >> matrix_cb; EXPECT_TRUE(equal(matrix_ca, matrix_cb)); }