/* 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/Random/Random.h" #include "Maths/HermitianMatrix.hh" #include "Maths/Matrix.hh" #include "Maths/SymmetricMatrix.hh" #include "Maths/Vector.hh" using MAUS::Matrix; using MAUS::HermitianMatrix; using MAUS::SymmetricMatrix; using MAUS::Vector; using MAUS::real; using MAUS::imag; using MAUS::conj; using MAUS::operator *; using MAUS::operator -; // Defined in ComplexTest.cc bool equal(double d1, double d2); bool equal(MAUS::complex c1, MAUS::complex c2); class HermitianMatrixTest : public ::testing::Test { public: HermitianMatrixTest() { for (size_t index = 0; index < data_size_; ++index) { complex_data_doubled_[index] = complex_data_[index] * 2.; complex_data_multiple_[index] = complex_data_[index] * complex_scaling_factor_; } } bool elements_equal(const HermitianMatrix& matrix, const MAUS::complex value) { MAUS::complex value_conj = conj(value); MAUS::complex value_real = MAUS::Complex::complex(real(value)); bool are_equal = true; if (matrix.size() != size_) { return false; } for (size_t row = 1; row <= size_; ++row) { for (size_t column = 1; column <= row; ++column) { if (row == column) { are_equal &= ::equal(matrix(row, column), value_real); } else { are_equal &= ::equal(matrix(row, column), value); are_equal &= ::equal(matrix(column, row), value_conj); } } } return are_equal; } bool equal(const HermitianMatrix& matrix, const MAUS::complex* array) { bool are_equal = true; if (matrix.size() != size_) { return false; } for (size_t row = 1; row <= size_; ++row) { for (size_t column = 1; column <= row; ++column) { are_equal &= ::equal(matrix(row, column), array[(row-1)*size_+(column-1)]); if (row != column) { are_equal &= ::equal(matrix(column, row), conj(array[(row-1)*size_+(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; } bool equal(const HermitianMatrix& m1, const HermitianMatrix& m2) { return equal((Matrix&) m1, (Matrix&) m2); } 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 data_size_; static const double real_data_[25]; static const double imag_data_[25]; static const MAUS::complex complex_data_[25]; static const size_t size_; MAUS::complex complex_data_doubled_[25]; static const MAUS::complex complex_scaling_factor_; MAUS::complex complex_data_multiple_[25]; }; template bool HermitianMatrixTest::equal(const Matrix& m1, const Matrix& m2); template bool HermitianMatrixTest::equal(const Matrix& m1, const Matrix& m2); // ************************************************ // HermitianMatrixTest static const initializations // ************************************************ const double HermitianMatrixTest::real_data_[25] = { 0., 1., 2., 3., -5., 1., -13., -21., 34.5, 55.7, 2., -21., -32.5, -57.5, -91.2, 3., 34.5, -57.5, 2.65, -3.58, -5., -55.7, -91.2, -3.58, 3.38 }; const double HermitianMatrixTest::imag_data_[25] = { 0., 3.58, 91.2, 55.7, 5., -3.58, 0., 57.5, -34.5, 4.98, -91.2, -57.5, 0., 21., -2., -55.7, 34.5, -21., 0., 112.7, -5., -4.98, 2., -112.7, 0. }; const MAUS::complex HermitianMatrixTest::complex_data_[25] = { MAUS::Complex::complex(0., 0.), MAUS::Complex::complex(1., 3.58), MAUS::Complex::complex(2., 91.2), MAUS::Complex::complex(3., 55.7), MAUS::Complex::complex(-5., 5.), MAUS::Complex::complex(1., -3.58), MAUS::Complex::complex(-13., 0.), MAUS::Complex::complex(-21., 57.5), MAUS::Complex::complex(34.5, -34.5), MAUS::Complex::complex(55.7, 4.98), MAUS::Complex::complex(2., -91.2), MAUS::Complex::complex(-21., -57.5), MAUS::Complex::complex(-32.5, 0.), MAUS::Complex::complex(-57.5, 21.), MAUS::Complex::complex(-91.2, -2.), MAUS::Complex::complex(3., -55.7), MAUS::Complex::complex(34.5, 34.5), MAUS::Complex::complex(-57.5, -21.), MAUS::Complex::complex(2.65, 0.), MAUS::Complex::complex(-3.58, 112.7), MAUS::Complex::complex(-5., -5.), MAUS::Complex::complex(-55.7, -4.98), MAUS::Complex::complex(-91.2, 2.), MAUS::Complex::complex(-3.58, -112.7), MAUS::Complex::complex(3.38, 0.) }; const size_t HermitianMatrixTest::data_size_ = 25; const size_t HermitianMatrixTest::size_ = 5; const MAUS::complex HermitianMatrixTest::complex_scaling_factor_ = MAUS::Complex::complex(2, -5); // *********** // test cases // *********** TEST_F(HermitianMatrixTest, DefaultConstructor) { HermitianMatrix matrix_h0; ASSERT_EQ(matrix_h0.size(), (size_t) 0); } TEST_F(HermitianMatrixTest, CopyConstructor) { // should work if matrix is const const HermitianMatrix matrix_h0(size_, complex_data_); const HermitianMatrix matrix_h1(matrix_h0); EXPECT_TRUE(equal(matrix_h0, matrix_h1)); // check that handles 0 length okay HermitianMatrix matrix_h2; HermitianMatrix matrix_h3(matrix_h2); EXPECT_TRUE(equal(matrix_h2, matrix_h3)); } TEST_F(HermitianMatrixTest, ConstructorSizeOnly) { HermitianMatrix matrix_h0(size_); EXPECT_TRUE(elements_equal(matrix_h0, MAUS::Complex::complex(0.))); } TEST_F(HermitianMatrixTest, ConstructorFill) { HermitianMatrix matrix_h0(size_, complex_data_[4]); EXPECT_TRUE(elements_equal(matrix_h0, complex_data_[4])); } TEST_F(HermitianMatrixTest, ConstructorFromArray) { HermitianMatrix matrix_h0(size_, complex_data_); EXPECT_TRUE(equal(matrix_h0, complex_data_)); } TEST_F(HermitianMatrixTest, Set) { const HermitianMatrix matrix_h0(size_, complex_data_); HermitianMatrix matrix_h1(size_); for (size_t row = 1; row <= size_; ++row) { for (size_t column = 1; column <= row; ++column) { matrix_h1.set(row, column, complex_data_[(row-1)*size_+(column-1)]); } } EXPECT_TRUE(equal(matrix_h0, matrix_h1)); } TEST_F(HermitianMatrixTest, Assignment) { // plain vanilla assignment const HermitianMatrix matrix_h0(size_, complex_data_); HermitianMatrix matrix_h1; matrix_h1 = matrix_h0; EXPECT_TRUE(equal(matrix_h1, matrix_h0)); } TEST_F(HermitianMatrixTest, Addition) { // add and assign HermitianMatrix matrix_h0(size_, complex_data_); HermitianMatrix matrix_h1(size_); matrix_h1 += matrix_h0; ASSERT_TRUE(equal(matrix_h0, matrix_h1)); // add HermitianMatrix matrix_h2 = matrix_h0 + matrix_h1; EXPECT_TRUE(equal(matrix_h2, complex_data_doubled_)); } TEST_F(HermitianMatrixTest, Subtraction) { // subtract and assign HermitianMatrix matrix_h0(size_, complex_data_); HermitianMatrix matrix_h1(size_, complex_data_doubled_); HermitianMatrix matrix_h2(matrix_h1); matrix_h2 -= matrix_h0; ASSERT_TRUE(equal(matrix_h2, matrix_h0)); // subtract HermitianMatrix matrix_h3 = matrix_h1 - matrix_h0; EXPECT_TRUE(equal(matrix_h3, complex_data_)); } TEST_F(HermitianMatrixTest, Inverse) { HermitianMatrix matrix_h1(size_, complex_data_); HermitianMatrix matrix_h2 = inverse(matrix_h1); // M^-1 * M = Identity Matrix matrix_c0 = (Matrix) matrix_h1 * matrix_h2; MAUS::complex one = MAUS::Complex::complex(1.); MAUS::complex zero = MAUS::Complex::complex(0.); for (size_t row = 1; row <= size_; ++row) { for (size_t column = 1; column <= size_; ++column) { EXPECT_TRUE(::equal(row == column ? one : zero, matrix_c0(row, column))); } } } TEST_F(HermitianMatrixTest, ComplexDecomposition) { const HermitianMatrix matrix_h0(size_, complex_data_); const SymmetricMatrix matrix_s0(size_, real_data_); // the real part of complex_data_ is just real_data_ SymmetricMatrix matrix_real = real(matrix_h0); EXPECT_TRUE(equal(matrix_real, matrix_s0)); } TEST_F(HermitianMatrixTest, Eigen) { // verify the eigensystem equation M*E = e*E HermitianMatrix matrix_h0(size_, complex_data_); std::pair< Vector, Matrix > eigensys = eigensystem(matrix_h0); Vector eigenvals = eigensys.first; Matrix eigenvectors = eigensys.second; ASSERT_EQ(eigenvals.size(), size_); ASSERT_EQ(eigenvectors.number_of_columns(), size_); for (size_t i = 1; i < eigenvals.size(); ++i) { double eigenvalue = eigenvals(i); Vector eigenvector = eigenvectors.column(i); EXPECT_TRUE(equal(matrix_h0 * eigenvector, MAUS::Complex::complex(eigenvalue) * eigenvector)); } // verify that just getting the eigenvalues returns the same set from // eigensystem() (but perhaps in a different order) Vector eigenvals2 = eigenvalues(matrix_h0); ASSERT_EQ(eigenvals2.size(), size_); for (size_t i = 1; i <= size_; ++i) { bool found_match = false; for (size_t j = 1; j <= size_; j++) { if (::equal(eigenvals2(i), eigenvals(j))) { found_match = true; } } EXPECT_TRUE(found_match); } } TEST_F(HermitianMatrixTest, Negation) { HermitianMatrix matrix_h0(size_, complex_data_); HermitianMatrix matrix_h1(-matrix_h0); for (size_t row = 1; row <= size_; ++row) { for (size_t column = 1; column <= row; ++column) { EXPECT_TRUE(::equal(-matrix_h1(row, column), matrix_h0(row, column))); EXPECT_TRUE(::equal(-matrix_h1(column, row), matrix_h0(column, row))); } } }