/*************************************************************************** * test_GMatrixSparse.cpp - Test sparse matrix class * * ----------------------------------------------------------------------- * * copyright (C) 2006-2017 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program 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. * * * * This program 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 this program. If not, see . * * * ***************************************************************************/ /** * @file test_GMatrixSparse.cpp * @brief Implementation of unit tests for sparse matrices * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include #include "test_GMatrixSparse.hpp" /* __ Globals ____________________________________________________________ */ double g_matrix[] = {1.0, 7.0, 3.0, 2.0, 4.0, 8.0, 5.0, 6.0, 9.0}; int g_row[] = { 0, 0, 1, 2, 2, 2, 3, 3, 3}; int g_col[] = { 0, 4, 1, 0, 2, 4, 2, 3, 4}; double g_vector[] = {1.0, 2.0, 3.0, 4.0, 5.0}; int g_elements = 9; int g_rows = 4; int g_cols = 5; /*************************************************************************** * @brief Set test matrix ***************************************************************************/ GMatrixSparse TestGMatrixSparse::set_matrix(void) const { // Allocate matrix GMatrixSparse matrix(g_rows, g_cols); // Set matrix values for (int i = 0; i < g_elements; ++i) { matrix(g_row[i],g_col[i]) = g_matrix[i]; } // Return matrix return matrix; } /*************************************************************************** * @brief Set test matrix with zero lines ***************************************************************************/ /* GMatrixSparse TestGMatrixSparse::set_matrix_zero(void) const { // Allocate matrix GMatrixSparse matrix(g_rows+1,g_cols+1); // Set matrix values int i = 0; for (int row = 0; row < g_rows+1; ++row) { if (row == 2) continue; int j = 0; for (int col = 0; col < g_cols+1; ++col) { if (col == 2) continue; matrix(row,col) = g_matrix[j+i*g_cols]; j++; } i++; } // Return matrix return matrix; } */ /*************************************************************************** * @brief Set test vector ***************************************************************************/ GVector TestGMatrixSparse::set_vector(void) const { // Allocate vector GVector vector(g_cols); // Set vector values for (int col = 0; col < g_cols; ++col) { vector[col] = g_vector[col]; } // Return vector return vector; } /*************************************************************************** * @brief Check if matrix corresponds to test matrix * * @param[in] matrix Matrix. * @param[in] scale scale factor (default: 1.0). * @param[in] offset offset value (default: 0.0). * * Checks if a matrix corresponds to the test matrix. Optionally, the test * matrix can be scaled and an offset can be added. ***************************************************************************/ bool TestGMatrixSparse::check_matrix(const GMatrixSparse& matrix, const double& scale, const double& offset) const { // Initialise check with true bool result = true; // Compare all matrix elements for (int row = 0; row < g_rows; ++row) { for (int col = 0; col < g_cols; ++col) { // Set reference value double ref_value = offset; for (int i = 0; i < g_elements; ++i) { if (g_row[i] == row && g_col[i] == col) { ref_value = g_matrix[i] * scale + offset; break; } } // Check matrix if (std::abs(matrix(row,col)-ref_value) > 1.0e-15) { result = false; break; } } // endfor: looped over columns } // endfor: looped over rows // Return result return result; } /*************************************************************************** * @brief Check if transposed matrix corresponds to test matrix * * @param[in] matrix Trasposed matrix. * @param[in] scale scale factor (default: 1.0). * @param[in] offset offset value (default: 0.0). * * Checks if a matrix corresponds to the test matrix. Optionally, the test * matrix can be scaled and an offset can be added. ***************************************************************************/ bool TestGMatrixSparse::check_matrix_trans(const GMatrixSparse& matrix, const double& scale, const double& offset) const { // Initialise check with true bool result = true; // Compare all matrix elements for (int row = 0; row < g_rows; ++row) { for (int col = 0; col < g_cols; ++col) { // Set reference value double ref_value = offset; for (int i = 0; i < g_elements; ++i) { if (g_row[i] == row && g_col[i] == col) { ref_value = g_matrix[i] * scale + offset; break; } } // Check matrix if (std::abs(matrix(col,row)-ref_value) > 1.0e-15) { result = false; break; } } // endfor: looped over columns } // endfor: looped over rows // Return result return result; } /*************************************************************************** * @brief Check lower triangle general matrix * * @param[in] matrix Matrix. * @param[in] ref Reference matrix. ***************************************************************************/ bool TestGMatrixSparse::check_matrix_lt(const GMatrixSparse& matrix, const GMatrixSparse& ref) const { // Initialise check with true bool result = true; // Compare all matrix elements for (int row = 0; row < g_rows; ++row) { for (int col = 0; col < g_cols; ++col) { // Set reference value double ref_value = 0.0; if (col <= row) { for (int i = 0; i < g_elements; ++i) { if (g_row[i] == row && g_col[i] == col) { ref_value = g_matrix[i]; break; } } } // Check matrix if (std::abs(matrix(row,col)-ref_value) > 1.0e-15) { result = false; break; } } // endfor: looped over columns } // endfor: looped over rows // Return result return result; } /*************************************************************************** * @brief Check upper triangle general matrix * * @param[in] matrix Matrix. * @param[in] ref Reference matrix. ***************************************************************************/ bool TestGMatrixSparse::check_matrix_ut(const GMatrixSparse& matrix, const GMatrixSparse& ref) const { // Initialise check with true bool result = true; // Compare all matrix elements for (int row = 0; row < g_rows; ++row) { for (int col = 0; col < g_cols; ++col) { // Set reference value double ref_value = 0.0; if (col >= row) { for (int i = 0; i < g_elements; ++i) { if (g_row[i] == row && g_col[i] == col) { ref_value = g_matrix[i]; break; } } } // Check matrix if (std::abs(matrix(row,col)-ref_value) > 1.0e-15) { result = false; break; } } // endfor: looped over columns } // endfor: looped over rows // Return result return result; } /***********************************************************************//** * @brief Set parameters and tests **************************************************************************/ void TestGMatrixSparse::set(void) { // Set test name name("GMatrixSparse"); // Append tests append(static_cast(&TestGMatrixSparse::alloc_matrix), "Test matrix allocation"); append(static_cast(&TestGMatrixSparse::assign_values), "Test value assignment"); append(static_cast(&TestGMatrixSparse::copy_matrix), "Test matrix copying"); append(static_cast(&TestGMatrixSparse::matrix_operations), "Test matrix operations"); append(static_cast(&TestGMatrixSparse::matrix_arithmetics), "Test matrix arithmetics"); append(static_cast(&TestGMatrixSparse::matrix_functions), "Test matrix functions"); append(static_cast(&TestGMatrixSparse::matrix_compare), "Test matrix comparisons"); append(static_cast(&TestGMatrixSparse::matrix_cholesky), "Test matrix Cholesky decomposition"); append(static_cast(&TestGMatrixSparse::matrix_print), "Test matrix printing"); // Set members m_test = set_matrix(); v_test = set_vector(); m_bigger = GMatrixSparse(g_rows+1, g_cols+1); // Return return; } /***********************************************************************//** * @brief Clone test suite * * @return Pointer to deep copy of test suite. ***************************************************************************/ TestGMatrixSparse* TestGMatrixSparse::clone(void) const { // Clone test suite return new TestGMatrixSparse(*this); } /***********************************************************************//** * @brief Test matrix allocation ***************************************************************************/ void TestGMatrixSparse::alloc_matrix(void) { // Allocate zero matrix. The allocation should fail. test_try("Allocate zero matrix"); try { GMatrixSparse test(0,0); test_try_failure("Expected GException::empty exception."); } catch (GException::empty &e) { test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Setup a symmetric sparse matrix int size = 30; GMatrixSparse symmetric(size,size); for (int i = 0; i < size; i+=2) { for (int j = 0; j < size; j+=2) { symmetric(i,j) = 1.0+i+j; } } // Convert to GMatrix test_try("Test symmetric GMatrix conversion"); try { GMatrix cnv_matrix = GMatrix(symmetric); GMatrixSparse back_matrix = GMatrixSparse(cnv_matrix); test_assert((symmetric == back_matrix), "Test symmetric GMatrixSparse - GMatrix conversion", "Found:\n"+back_matrix.print()+"\nExpected:\n"+symmetric.print()); test_try_success(); } catch (GException::empty &e) { test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test GMatrix conversion test_try("Test GMatrix conversion"); try { GMatrix cnv_matrix = GMatrix(m_test); GMatrixSparse back_matrix = GMatrixSparse(cnv_matrix); test_assert((m_test == back_matrix), "Test GMatrixSparse - GMatrix conversion", "Found:\n"+back_matrix.print()+"\nExpected:\n"+m_test.print()); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test GMatrixSparse <-> GMatrixSymmetric conversion test_try("Test GMatrixSymmetric conversion"); try { GMatrixSymmetric cnv_sym = GMatrixSymmetric(symmetric); GMatrixSparse back_sym = GMatrixSparse(cnv_sym); test_assert((symmetric == back_sym), "Test GMatrixSparse - GMatrixSymmetric conversion", "Found:\n"+back_sym.print()+"\nExpected:\n"+symmetric.print()); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test invalid GMatrixSparse <-> GMatrixSymmetric conversion test_try("Test invalid GMatrixSymmetric conversion"); try { GMatrixSymmetric bad_sym = GMatrixSymmetric(m_test); test_try_failure("Expected GException::matrix_not_symmetric exception."); } catch (GException::matrix_not_symmetric &e) { test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Return return; } /***********************************************************************//** * @brief Test value assignment ***************************************************************************/ void TestGMatrixSparse::assign_values(void) { // Setup 3x3 matrix GMatrixSparse test(3,3); // Assignment individual values for (int i = 0; i < 3; ++i) { for (int k = 0; k < 3; ++k) { double value = i*2.0 + k*2.0; test(i,k) = value; } } // Check assignment of individual values for (int i = 0; i < 3; ++i) { for (int k = 0; k < 3; ++k) { double value = i*2.0 + k*2.0; test_value(test(i,k), value, 1.0e-10, "Test matrix element assignment"); } } // Check value assignment const double ref = 37.89; test = ref; for (int i = 0; i < 3; ++i) { for (int k = 0; k < 3; ++k) { test_value(test(i,k), ref, 1.0e-10, "Test matrix element assignment"); } } test = 0.0; for (int i = 0; i < 3; ++i) { for (int k = 0; k < 3; ++k) { test_value(test(i,k), 0.0, 1.0e-10, "Test matrix element assignment"); } } // Verify range checking #ifdef G_RANGE_CHECK test_try("Verify range checking"); try { test.at(3,3) = 1.0; test_try_failure("Expected GException::out_of_range exception."); } catch (GException::out_of_range &e) { test_try_success(); } catch (std::exception &e) { test_try_failure(e); } #endif // Setup 10x10 matrix and keep for reference GMatrixSparse sparse(10,10); for (int i = 3; i < 5; ++i) { sparse(i,i) = 5.0; } GMatrixSparse initial = sparse; GMatrixSparse reference = sparse; // Insert column into 10 x 10 matrix using large matrix stack and the // add_col(GVector) method sparse.stack_init(100,50); GVector column(10); for (int j = 0; j < 10; ++j) { int col = int(0.8 * j + 0.5); // This allows that some columns are twice if (col > 9) col -= 10; // This avoids overflow int i_min = (j < 2) ? 0 : j-2; int i_max = (j > 8) ? 10 : j+2; column = 0.0; for (int i = i_min; i < i_max; ++i) { column[i] = (i+1)*1; reference(i,col) += column[i]; } sparse.add_to_column(col, column); } sparse.stack_destroy(); test_assert((sparse == reference), "Test stack fill with large stack using add_col(GVector) method", "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print()); // Insert column into 10 x 10 matrix using small matrix stack and the // add_col(GVector) method sparse = initial; sparse.stack_init(100,3); for (int j = 0; j < 10; ++j) { int col = int(0.8 * j + 0.5); // This allows that some columns are twice if (col > 9) col -= 10; // This avoids overflow int i_min = (j < 2) ? 0 : j-2; int i_max = (j > 8) ? 10 : j+2; column = 0.0; for (int i = i_min; i < i_max; ++i) { column[i] = (i+1)*1; } sparse.add_to_column(col, column); } sparse.stack_destroy(); test_assert((sparse == reference), "Test stack fill with small stack using add_col(GVector) method", "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print()); // Insert column into 10 x 10 matrix using tiny matrix stack and the // add_col(GVector) method sparse = initial; sparse.stack_init(8,3); for (int j = 0; j < 10; ++j) { int col = int(0.8 * j + 0.5); // This allows that some columns are twice if (col > 9) col -= 10; // This avoids overflow int i_min = (j < 2) ? 0 : j-2; int i_max = (j > 8) ? 10 : j+2; column = 0.0; for (int i = i_min; i < i_max; ++i) { column[i] = (i+1)*1; } sparse.add_to_column(col, column); } sparse.stack_destroy(); test_assert((sparse == reference), "Test stack fill with tiny stack using add_col(GVector) method", "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print()); // Insert column into 10 x 10 matrix using no matrix stack and the // add_col(GVector) method sparse = initial; for (int j = 0; j < 10; ++j) { int col = int(0.8 * j + 0.5); // This allows that some columns are twice if (col > 9) col -= 10; // This avoids overflow int i_min = (j < 2) ? 0 : j-2; int i_max = (j > 8) ? 10 : j+2; column = 0.0; for (int i = i_min; i < i_max; ++i) { column[i] = (i+1)*1; } sparse.add_to_column(col, column); } sparse.stack_destroy(); test_assert((sparse == reference), "Test fill using add_col(GVector) method", "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print()); // Set-up workspace for compressed column adding double* wrk_data = new double[10]; int* wrk_row = new int[10]; // Compressed tests test_try("Verify compressed column add_col() method"); try { // Insert column into 10 x 10 matrix using large matrix stack and the // compressed column add_col() method sparse = initial; reference = initial; sparse.stack_init(100,50); for (int j = 0; j < 10; ++j) { int col = int(0.8 * j + 0.5); // This allows that some columns are twice if (col > 9) col -= 10; // This avoids overflow int inx = 0; int i_min = (j < 3) ? 0 : j-3; int i_max = (j > 8) ? 10 : j+2; for (int i = i_min; i < i_max; ++i) { wrk_data[inx] = (i+1)*3.7; wrk_row[inx] = i; reference(i,col) += wrk_data[inx]; inx++; } sparse.add_to_column(col, wrk_data, wrk_row, inx); } sparse.stack_destroy(); test_assert((sparse == reference), "Test stack fill with large stack using compressed add_col() method", "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print()); // Insert column into 10 x 10 matrix using small matrix stack and the // compressed column add_col() method sparse = initial; sparse.stack_init(100,2); for (int j = 0; j < 10; ++j) { int col = int(0.8 * j + 0.5); // This allows that some columns are twice if (col > 9) col -= 10; // This avoids overflow int inx = 0; int i_min = (j < 3) ? 0 : j-3; int i_max = (j > 8) ? 10 : j+2; for (int i = i_min; i < i_max; ++i) { wrk_data[inx] = (i+1)*3.7; wrk_row[inx] = i; inx++; } sparse.add_to_column(col, wrk_data, wrk_row, inx); } sparse.stack_destroy(); test_assert((sparse == reference), "Test stack fill with small stack using compressed add_col() method", "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print()); // Insert column into 10 x 10 matrix using tiny matrix stack and the // compressed column add_col() method sparse = initial; sparse.stack_init(3,2); for (int j = 0; j < 10; ++j) { int col = int(0.8 * j + 0.5); // This allows that some columns are twice if (col > 9) col -= 10; // This avoids overflow int inx = 0; int i_min = (j < 3) ? 0 : j-3; int i_max = (j > 8) ? 10 : j+2; for (int i = i_min; i < i_max; ++i) { wrk_data[inx] = (i+1)*3.7; wrk_row[inx] = i; inx++; } sparse.add_to_column(col, wrk_data, wrk_row, inx); } sparse.stack_destroy(); test_assert((sparse == reference), "Test stack fill with tiny stack using compressed add_col() method", "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print()); // Insert column into 10 x 10 matrix using no matrix stack and the // compressed column add_col() method sparse = initial; for (int j = 0; j < 10; ++j) { int col = int(0.8 * j + 0.5); // This allows that some columns are twice if (col > 9) col -= 10; // This avoids overflow int inx = 0; int i_min = (j < 3) ? 0 : j-3; int i_max = (j > 8) ? 10 : j+2; for (int i = i_min; i < i_max; ++i) { wrk_data[inx] = (i+1)*3.7; wrk_row[inx] = i; inx++; } sparse.add_to_column(col, wrk_data, wrk_row, inx); } sparse.stack_destroy(); test_assert((sparse == reference), "Test fill using compressed add_col() method", "Found:\n"+sparse.print()+"\nExpected:\n"+reference.print()); // Signal success test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Free workspace delete [] wrk_data; delete [] wrk_row; // Return return; } /***********************************************************************//** * @brief Test matrix copy ***************************************************************************/ void TestGMatrixSparse::copy_matrix(void) { // Copy matrix GMatrixSparse test = m_test; // Test if original and compied matrices are correct test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test), "Test matrix copy operator", "Found:\n"+test.print()+"\nExpected:\n"+m_test.print()); // Return return; } /***********************************************************************//** * @brief Test matrix operations * * Tests matrix*vector and matrix*matrix multiplication operations. ***************************************************************************/ void TestGMatrixSparse::matrix_operations(void) { // Perform vector multiplication GVector test1 = m_test * v_test; // Check result GVector ref1(g_rows); for (int i = 0; i < g_elements; ++i) { ref1[g_row[i]] += g_matrix[i] * v_test[g_col[i]]; } bool result = true; for (int i = 0; i < g_rows; ++i) { if (ref1[i] != test1[i]) { result = false; break; } } // Test if original matrix and result vector are correct test_assert(check_matrix(m_test), "Test source matrix"); test_assert(result, "Test matrix*vector multiplication", "Found:\n"+test1.print()+"\nExpected:\n"+ref1.print()); // Test incompatible vector multiplication test_try("Test incompatible matrix*vector multiplication"); try { GVector test2 = m_bigger * v_test; test_try_failure("Expected GException::matrix_vector_mismatch exception."); } catch (GException::matrix_vector_mismatch &e) { test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test matrix multiplication GMatrixSparse test3 = m_test * m_test.transpose(); // Check if the result matrix is as expected GMatrixSparse ref3(g_rows, g_rows); for (int row = 0; row < g_rows; ++row) { for (int col = 0; col < g_rows; ++col) { double value = 0.0; for (int i = 0; i < g_cols; ++i) { double ref_value_1 = 0.0; double ref_value_2 = 0.0; for (int k = 0; k < g_elements; ++k) { if (g_row[k] == row && g_col[k] == i) { ref_value_1 = g_matrix[k]; break; } } for (int k = 0; k < g_elements; ++k) { if (g_row[k] == col && g_col[k] == i) { ref_value_2 = g_matrix[k]; break; } } value += ref_value_1 * ref_value_2; } ref3(row,col) = value; if (test3(row,col) != value) { result = false; break; } } } // Test if original matrix and result matrix are correct test_assert(check_matrix(m_test), "Test source matrix"); test_assert(result, "Test matrix multiplication", "Found:\n"+test3.print()+"\nExpected:\n"+ref3.print()); test_value(test3.rows(), g_rows, "Test number of rows of result matrix"); test_value(test3.columns(), g_rows, "Test number of columns of result matrix"); // Test incompatible matrix multiplication test_try("Test incompatible matrix multiplication"); try { GMatrixSparse test4 = m_bigger * m_test; test_try_failure("Expected GException::matrix_mismatch exception."); } catch (GException::matrix_mismatch &e) { test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test another incompatible matrix multiplication test_try("Test incompatible matrix multiplication"); try { GMatrixSparse test5 = m_bigger * m_test; test_try_failure("Expected GException::matrix_mismatch exception."); } catch (GException::matrix_mismatch &e) { test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Return return; } /***********************************************************************//** * @brief Test matrix arithmetics * * Tests matrix arithmetics. ***************************************************************************/ void TestGMatrixSparse::matrix_arithmetics(void) { // -GMatrixSparse GMatrixSparse test = -m_test; test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test, -1.0, 0.0), "Test -GMatrixSparse", "Unexpected result matrix:\n"+test.print()); // GMatrixSparse += GMatrixSparse test = m_test; test += m_test; test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test, 2.0, 0.0), "Test GMatrixSparse += GMatrixSparse", "Unexpected result matrix:\n"+test.print()); // GMatrixSparse -= GMatrixSparse test = m_test; test -= m_test; test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test, 0.0, 0.0), "Test GMatrixSparse -= GMatrixSparse", "Unexpected result matrix:\n"+test.print()); // GMatrixSparse *= 3.0 test = m_test; test *= 3.0; test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test, 3.0, 0.0), "Test GMatrixSparse *= 3.0", "Unexpected result matrix:\n"+test.print()); // GMatrixSparse /= 3.0 test = m_test; test /= 3.0; test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test, 1.0/3.0, 0.0), "Test GMatrixSparse /= 3.0", "Unexpected result matrix:\n"+test.print()); // GMatrixSparse + GMatrixSparse test = m_test + m_test; test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test, 2.0, 0.0), "Test GMatrixSparse + GMatrixSparse", "Unexpected result matrix:\n"+test.print()); // GMatrixSparse - GMatrixSparse test = m_test - m_test; test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test, 0.0, 0.0), "Test GMatrixSparse - GMatrixSparse", "Unexpected result matrix:\n"+test.print()); // GMatrixSparse * 3.0 test = m_test * 3.0; test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test, 3.0, 0.0), "Test GMatrixSparse * 3.0", "Unexpected result matrix:\n"+test.print()); // 3.0 * GMatrixSparse test = 3.0 * m_test; test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test, 3.0, 0.0), "Test 3.0 * GMatrixSparse", "Unexpected result matrix:\n"+test.print()); // GMatrixSparse / 3.0 test = m_test / 3.0; test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test, 1.0/3.0, 0.0), "Test GMatrixSparse / 3.0", "Unexpected result matrix:\n"+test.print()); // GMatrixSparse + 3.0 test = m_test + 3.0; test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test, 1.0, 3.0), "Test GMatrixSparse + 3.0", "Unexpected result matrix:\n"+test.print()); // GMatrixSparse += 3.0 test = m_test; test += 3.0; test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test, 1.0, 3.0), "Test GMatrixSparse + 3.0", "Unexpected result matrix:\n"+test.print()); // GMatrixSparse - 5.0 test = m_test - 5.0; test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test, 1.0, -5.0), "Test GMatrixSparse -= 5.0", "Unexpected result matrix:\n"+test.print()); // GMatrixSparse -= 3.0 test = m_test; test -= 3.0; test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test, 1.0, -3.0), "Test GMatrixSparse -= 3.0", "Unexpected result matrix:\n"+test.print()); // Test invalid matrix addition test_try("Test invalid matrix addition"); try { test = m_test; test += m_bigger; test_try_failure("Expected GException::matrix_mismatch exception."); } catch (GException::matrix_mismatch &e) { test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Return return; } /***********************************************************************//** * @brief Test matrix functions * * Tests matrix functions. ***************************************************************************/ void TestGMatrixSparse::matrix_functions(void) { // Minimum double min = m_test.min(); // Check mimimum double value = 0.0; test_value(min, value, 0.0, "Test minimum function"); // Maximum double max = m_test.max(); // Check maximum value = g_matrix[0]; for (int i = 1; i < g_elements; ++i) { if (g_matrix[i] > value) { value = g_matrix[i]; } } test_value(max, value, 0.0, "Test maximum function"); // Sum double sum = m_test.sum(); // Check sum value = 0.0; for (int i = 0; i < g_elements; ++i) { value += g_matrix[i]; } test_value(sum, value, 1.0e-20, "Test sum function"); // Transpose method GMatrixSparse test1 = m_test.transpose(); test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix_trans(test1, 1.0, 0.0), "Test transpose(GMatrixSparse) function", "Unexpected transposed matrix:\n"+test1.print()); // Convert to general matrix GMatrixSparse test2 = GMatrixSparse(m_test); test_assert(check_matrix(m_test), "Test source matrix"); test_assert(check_matrix(test2, 1.0, 0.0), "Test GMatrixSparse(GMatrixSparse) constructor", "Unexpected GMatrixSparse:\n"+test2.print()); // Return return; } /***********************************************************************//** * @brief Test matrix comparisons ***************************************************************************/ void TestGMatrixSparse::matrix_compare(void) { // Allocate an empty matrix GMatrixSparse empty(g_rows, g_cols); // Allocate a full matrix GMatrixSparse full(g_rows, g_cols); for (int i = 0; i < g_rows; ++i) { for (int j = 0; j < g_rows; ++j) { full(i,j) = 1.0 + i*1000.0 + j*10.0; } } // Test == operator test_assert((m_test == m_test), "Test == operator", "Identity test faileded on:\n"+m_test.print()); test_assert(!(m_test == empty), "Test == operator", "Matrix\n"+m_test.print()+"\nshould not be equal to\n"+empty.print()); test_assert(!(m_test == full), "Test == operator", "Matrix\n"+m_test.print()+"\nshould not be equal to\n"+full.print()); test_assert(!(m_test == m_bigger), "Test == operator", "Matrix\n"+m_test.print()+"\nshould not be equal to\n"+m_bigger.print()); // Test != operator test_assert(!(m_test != m_test), "Test != operator", "Unequality test faileded on:\n"+m_test.print()); test_assert((m_test != empty), "Test != operator", "Matrix\n"+m_test.print()+"\nshould differ from\n"+empty.print()); test_assert((m_test != full), "Test != operator", "Matrix\n"+m_test.print()+"\nshould differ from\n"+full.print()); test_assert((m_test != m_bigger), "Test != operator", "Matrix\n"+m_test.print()+"\nshould differ from\n"+m_bigger.print()); // Return return; } /***********************************************************************//** * @brief Test Cholesky decomposition ***************************************************************************/ void TestGMatrixSparse::matrix_cholesky(void) { // Setup matrix for Cholesky decomposition GMatrixSparse chol_test(5,5); chol_test(0,0) = 1.0; chol_test(0,1) = 0.2; chol_test(0,2) = 0.2; chol_test(0,3) = 0.2; chol_test(0,4) = 0.2; chol_test(1,0) = 0.2; chol_test(2,0) = 0.2; chol_test(3,0) = 0.2; chol_test(4,0) = 0.2; chol_test(1,1) = 1.0; chol_test(2,2) = 1.0; chol_test(3,3) = 1.0; chol_test(4,4) = 1.0; // Try to solve now (should not work) test_try("Try Cholesky solver without factorisation"); try { GVector vector(5); vector = chol_test.cholesky_solver(vector); test_try_failure("Expected GException::matrix_not_factorised exception."); } catch (GException::matrix_not_factorised &e) { test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Perform Cholesky decomposition GMatrixSparse cd = chol_test.cholesky_decompose(); // Test Cholesky solver (first test) GVector e0(5); GVector a0(5); e0[0] = 1.0; a0[0] = 1.0; a0[1] = 0.2; a0[2] = 0.2; a0[3] = 0.2; a0[4] = 0.2; GVector s0 = cd.cholesky_solver(a0); double res = max(abs(s0-e0)); test_value(res, 0.0, 1.0e-15, "Test cholesky_solver() method - 1"); // Test Cholesky solver (second test) e0[0] = 0.0; e0[1] = 1.0; a0[0] = 0.2; a0[1] = 1.0; a0[2] = 0.0; a0[3] = 0.0; a0[4] = 0.0; s0 = cd.cholesky_solver(a0); res = max(abs(s0-e0)); test_value(res, 0.0, 1.0e-15, "Test cholesky_solver() method - 2"); // Test Cholesky solver (third test) e0[1] = 0.0; e0[2] = 1.0; a0[1] = 0.0; a0[2] = 1.0; s0 = cd.cholesky_solver(a0); res = max(abs(s0-e0)); test_value(res, 0.0, 1.0e-15, "Test cholesky_solver() method - 3"); // Test Cholesky solver (forth test) e0[2] = 0.0; e0[3] = 1.0; a0[2] = 0.0; a0[3] = 1.0; s0 = cd.cholesky_solver(a0); res = max(abs(s0-e0)); test_value(res, 0.0, 1.0e-15, "Test cholesky_solver() method - 4"); // Test Cholesky solver (fifth test) e0[3] = 0.0; e0[4] = 1.0; a0[3] = 0.0; a0[4] = 1.0; s0 = cd.cholesky_solver(a0); res = max(abs(s0-e0)); test_value(res, 0.0, 1.0e-15, "Test cholesky_solver() method - 5"); // Setup matrix for Cholesky decomposition with zero row/col GMatrixSparse chol_test_zero(6,6); chol_test_zero(0,0) = 1.0; chol_test_zero(0,1) = 0.2; chol_test_zero(0,2) = 0.2; chol_test_zero(0,4) = 0.2; chol_test_zero(0,5) = 0.2; chol_test_zero(1,0) = 0.2; chol_test_zero(2,0) = 0.2; chol_test_zero(4,0) = 0.2; chol_test_zero(5,0) = 0.2; chol_test_zero(1,1) = 1.0; chol_test_zero(2,2) = 1.0; chol_test_zero(4,4) = 1.0; chol_test_zero(5,5) = 1.0; // Test compressed Cholesky decomposition GMatrixSparse cd_zero = chol_test_zero.cholesky_decompose(); // Test compressed Cholesky solver (first test) e0 = GVector(6); a0 = GVector(6); e0[0] = 1.0; a0[0] = 1.0; a0[1] = 0.2; a0[2] = 0.2; a0[4] = 0.2; a0[5] = 0.2; s0 = cd_zero.cholesky_solver(a0); res = max(abs(s0-e0)); test_value(res, 0.0, 1.0e-15, "Test compressed cholesky_solver() method - 1"); // Test compressed Cholesky solver (second test) e0[0] = 0.0; e0[1] = 1.0; a0[0] = 0.2; a0[1] = 1.0; a0[2] = 0.0; a0[4] = 0.0; a0[5] = 0.0; s0 = cd_zero.cholesky_solver(a0); res = max(abs(s0-e0)); test_value(res, 0.0, 1.0e-15, "Test compressed cholesky_solver() method - 2"); // Test compressed Cholesky solver (third test) e0[1] = 0.0; e0[2] = 1.0; a0[1] = 0.0; a0[2] = 1.0; s0 = cd_zero.cholesky_solver(a0); res = max(abs(s0-e0)); test_value(res, 0.0, 1.0e-15, "Test compressed cholesky_solver() method - 3"); // Test compressed Cholesky solver (forth test) e0[2] = 0.0; e0[4] = 1.0; a0[2] = 0.0; a0[4] = 1.0; s0 = cd_zero.cholesky_solver(a0); res = max(abs(s0-e0)); test_value(res, 0.0, 1.0e-15, "Test compressed cholesky_solver() method - 4"); // Test compressed Cholesky solver (fifth test) e0[4] = 0.0; e0[5] = 1.0; a0[4] = 0.0; a0[5] = 1.0; s0 = cd_zero.cholesky_solver(a0); res = max(abs(s0-e0)); test_value(res, 0.0, 1.0e-15, "Test compressed cholesky_solver() method - 5"); // Setup matrix for Cholesky decomposition with zero row/col (unsymmetric case) GMatrixSparse chol_test_zero2(6,5); chol_test_zero2(0,0) = 1.0; chol_test_zero2(0,1) = 0.2; chol_test_zero2(0,2) = 0.2; chol_test_zero2(0,3) = 0.2; chol_test_zero2(0,4) = 0.2; chol_test_zero2(1,0) = 0.2; chol_test_zero2(2,0) = 0.2; chol_test_zero2(4,0) = 0.2; chol_test_zero2(5,0) = 0.2; chol_test_zero2(1,1) = 1.0; chol_test_zero2(2,2) = 1.0; chol_test_zero2(4,3) = 1.0; chol_test_zero2(5,4) = 1.0; // Test compressed Cholesky decomposition (unsymmetric case) GMatrixSparse cd_zero2 = chol_test_zero2.cholesky_decompose(); // Test compressed Cholesky solver (unsymmetric case) e0 = GVector(5); a0 = GVector(6); e0[0] = 1.0; a0[0] = 1.0; a0[1] = 0.2; a0[2] = 0.2; a0[4] = 0.2; a0[5] = 0.2; s0 = cd_zero2.cholesky_solver(a0); res = max(abs(s0-e0)); test_value(res, 0.0, 1.0e-15, "Test unsymmetric compressed cholesky_solver() method - 1"); // Test compressed Cholesky solver (unsymmetric case) e0[0] = 0.0; e0[1] = 1.0; a0[0] = 0.2; a0[1] = 1.0; a0[2] = 0.0; a0[4] = 0.0; a0[5] = 0.0; s0 = cd_zero2.cholesky_solver(a0); res = max(abs(s0-e0)); test_value(res, 0.0, 1.0e-15, "Test unsymmetric compressed cholesky_solver() method - 2"); // Test compressed Cholesky solver (unsymmetric case) e0[1] = 0.0; e0[2] = 1.0; a0[1] = 0.0; a0[2] = 1.0; s0 = cd_zero2.cholesky_solver(a0); res = max(abs(s0-e0)); test_value(res, 0.0, 1.0e-15, "Test unsymmetric compressed cholesky_solver() method - 3"); // Test compressed Cholesky solver (unsymmetric case) e0[2] = 0.0; e0[3] = 1.0; a0[2] = 0.0; a0[4] = 1.0; s0 = cd_zero2.cholesky_solver(a0); res = max(abs(s0-e0)); test_value(res, 0.0, 1.0e-15, "Test unsymmetric compressed cholesky_solver() method - 4"); // Test compressed Cholesky solver (unsymmetric case) e0[3] = 0.0; e0[4] = 1.0; a0[4] = 0.0; a0[5] = 1.0; s0 = cd_zero2.cholesky_solver(a0); res = max(abs(s0-e0)); test_value(res, 0.0, 1.0e-15, "Test unsymmetric compressed cholesky_solver() method - 5"); // Test Cholesky inverter (inplace) GMatrixSparse unit(5,5); unit(0,0) = 1.0; unit(1,1) = 1.0; unit(2,2) = 1.0; unit(3,3) = 1.0; unit(4,4) = 1.0; GMatrixSparse chol_test_inv = chol_test.cholesky_invert(); GMatrixSparse ci_product = chol_test * chol_test_inv; GMatrixSparse ci_residuals = ci_product - unit; res = (ci_residuals.abs()).max(); test_value(res, 0.0, 1.0e-15, "Test Cholesky inverter"); // Test Cholesky inverter /* chol_test_inv = chol_test.cholesky_invert(); ci_product = chol_test * chol_test_inv; ci_residuals = ci_product - unit; res = (ci_residuals.abs()).max(); test_value(res, 0.0, 1.0e-15, "Test Cholesky inverter"); */ // Test Cholesky inverter for compressed matrix unit = GMatrixSparse(6,6); unit(0,0) = 1.0; unit(1,1) = 1.0; unit(2,2) = 1.0; unit(4,4) = 1.0; unit(5,5) = 1.0; GMatrixSparse chol_test_zero_inv = chol_test_zero.cholesky_invert(); GMatrixSparse ciz_product = chol_test_zero * chol_test_zero_inv; GMatrixSparse ciz_residuals = ciz_product - unit; res = (ciz_residuals.abs()).max(); test_value(res, 0.0, 1.0e-15, "Test compressed matrix Cholesky inverter"); // Return return; } /*************************************************************************** * @brief Test matrix printing ***************************************************************************/ void TestGMatrixSparse::matrix_print(void) { // Set reference std::string reference; reference.append("=== GMatrixSparse ===\n"); reference.append(" Number of rows ............: 4\n"); reference.append(" Number of columns .........: 5\n"); reference.append(" Number of nonzero elements : 9\n"); reference.append(" Pending element ...........: (3,4)=9\n"); reference.append(" Number of allocated cells .: 512\n"); reference.append(" Memory block size .........: 512\n"); reference.append(" Sparse matrix fill ........: 0.45\n"); reference.append(" Pending element ...........: 9\n"); reference.append(" Fill stack size ...........: 0 (none)\n"); reference.append(" 1, 0, 0, 0, 7\n"); reference.append(" 0, 3, 0, 0, 0\n"); reference.append(" 2, 0, 4, 0, 8\n"); reference.append(" 0, 0, 5, 6, 9"); // Test the print method std::string output = m_test.print(); test_value(output, reference); // Allocate big matrix GMatrixSparse big(30, 40); for (int i = 0; i < 30; ++i) { for (int j = 0; j < 40; ++j) { big(i,j) = i+j; } } // Set reference for big matrix std::string ref_big; ref_big.append("=== GMatrixSparse ===\n"); ref_big.append(" Number of rows ............: 30\n"); ref_big.append(" Number of columns .........: 40\n"); ref_big.append(" Number of nonzero elements : 1199\n"); ref_big.append(" Pending element ...........: (29,39)=68\n"); ref_big.append(" Number of allocated cells .: 1536\n"); ref_big.append(" Memory block size .........: 512\n"); ref_big.append(" Sparse matrix fill ........: 0.999166666666667\n"); ref_big.append(" Pending element ...........: 68\n"); ref_big.append(" Fill stack size ...........: 0 (none)\n"); ref_big.append(" 0, 1, 2, 3, 4, 5, 6, ... 33, 34, 35, 36, 37, 38, 39\n"); ref_big.append(" 1, 2, 3, 4, 5, 6, 7, ... 34, 35, 36, 37, 38, 39, 40\n"); ref_big.append(" 2, 3, 4, 5, 6, 7, 8, ... 35, 36, 37, 38, 39, 40, 41\n"); ref_big.append(" 3, 4, 5, 6, 7, 8, 9, ... 36, 37, 38, 39, 40, 41, 42\n"); ref_big.append(" 4, 5, 6, 7, 8, 9, 10, ... 37, 38, 39, 40, 41, 42, 43\n"); ref_big.append(" 5, 6, 7, 8, 9, 10, 11, ... 38, 39, 40, 41, 42, 43, 44\n"); ref_big.append(" 6, 7, 8, 9, 10, 11, 12, ... 39, 40, 41, 42, 43, 44, 45\n"); ref_big.append(" ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... \n"); ref_big.append(" 23, 24, 25, 26, 27, 28, 29, ... 56, 57, 58, 59, 60, 61, 62\n"); ref_big.append(" 24, 25, 26, 27, 28, 29, 30, ... 57, 58, 59, 60, 61, 62, 63\n"); ref_big.append(" 25, 26, 27, 28, 29, 30, 31, ... 58, 59, 60, 61, 62, 63, 64\n"); ref_big.append(" 26, 27, 28, 29, 30, 31, 32, ... 59, 60, 61, 62, 63, 64, 65\n"); ref_big.append(" 27, 28, 29, 30, 31, 32, 33, ... 60, 61, 62, 63, 64, 65, 66\n"); ref_big.append(" 28, 29, 30, 31, 32, 33, 34, ... 61, 62, 63, 64, 65, 66, 67\n"); ref_big.append(" 29, 30, 31, 32, 33, 34, 35, ... 62, 63, 64, 65, 66, 67, 68"); // Test the print method output = big.print(); test_value(output, ref_big); // Return return; } /*************************************************************************** * @brief Main entry point for test executable ***************************************************************************/ int main(void) { // Allocate test suit container GTestSuites testsuites("GMatrixSparse class testing"); // Initially assume that we pass all tests bool success = true; // Create a test suite TestGMatrixSparse test; // Append test suite to the container testsuites.append(test); // Run the testsuites success = testsuites.run(); // Save test report testsuites.save("reports/GMatrixSparse.xml"); // Return success status return (success ? 0 : 1); }