LCOV - code coverage report
Current view: top level - modules/linalg - linsys.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 122 226 54.0 %
Date: 2024-05-13 05:06:06 Functions: 11 18 61.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*************************************************************************
       2             :  *
       3             :  *  Project
       4             :  *                         _____ _____  __  __ _____
       5             :  *                        / ____|  __ \|  \/  |  __ \
       6             :  *  ___  _ __   ___ _ __ | |  __| |__) | \  / | |__) |
       7             :  * / _ \| '_ \ / _ \ '_ \| | |_ |  ___/| |\/| |  ___/
       8             :  *| (_) | |_) |  __/ | | | |__| | |    | |  | | |
       9             :  * \___/| .__/ \___|_| |_|\_____|_|    |_|  |_|_|
      10             :  *      | |
      11             :  *      |_|
      12             :  *
      13             :  * Copyright (C) Akiel Aries, <akiel@akiel.org>, et al.
      14             :  *
      15             :  * This software is licensed as described in the file LICENSE, which
      16             :  * you should have received as part of this distribution. The terms
      17             :  * among other details are referenced in the official documentation
      18             :  * seen here : https://akielaries.github.io/openGPMP/ along with
      19             :  * important files seen in this project.
      20             :  *
      21             :  * You may opt to use, copy, modify, merge, publish, distribute
      22             :  * and/or sell copies of the Software, and permit persons to whom
      23             :  * the Software is furnished to do so, under the terms of the
      24             :  * LICENSE file. As this is an Open Source effort, all implementations
      25             :  * must be of the same methodology.
      26             :  *
      27             :  *
      28             :  *
      29             :  * This software is distributed on an AS IS basis, WITHOUT
      30             :  * WARRANTY OF ANY KIND, either express or implied.
      31             :  *
      32             :  ************************************************************************/
      33             : #include <cmath>
      34             : #include <cstdint>
      35             : #include <iostream>
      36             : #include <openGPMP/linalg/linsys.hpp>
      37             : #include <utility>
      38             : #include <vector>
      39             : 
      40          20 : gpmp::linalg::LinSys::LinSys(const std::vector<std::vector<double>> &mat)
      41          20 :     : matrix(mat) {
      42          20 :     num_rows = matrix.size();
      43          20 :     num_cols = matrix[0].size();
      44          20 : }
      45             : 
      46             : // display the augmented matrix
      47           0 : void gpmp::linalg::LinSys::display_mtx() const {
      48           0 :     for (int i = 0; i < num_rows; ++i) {
      49           0 :         for (int j = 0; j < num_cols; ++j) {
      50           0 :             std::cout << matrix[i][j] << " ";
      51             :         }
      52           0 :         std::cout << std::endl;
      53             :     }
      54           0 : }
      55             : 
      56           0 : void gpmp::linalg::LinSys::display(
      57             :     const std::vector<std::vector<double>> &mat) const {
      58           0 :     uint32_t rows = mat.size();
      59           0 :     uint32_t cols = mat[0].size();
      60             : 
      61           0 :     for (uint32_t i = 0; i < rows; ++i) {
      62           0 :         for (uint32_t j = 0; j < cols; ++j) {
      63           0 :             std::cout << mat[i][j] << " ";
      64             :         }
      65           0 :         std::cout << std::endl;
      66             :     }
      67           0 : }
      68             : 
      69             : // solve the linear system using Gaussian Elimination
      70           1 : std::vector<double> gpmp::linalg::LinSys::solve_gauss() {
      71             :     // temp matrix because our result is a vector of solutions
      72           1 :     std::vector<std::vector<double>> temp_mtx = matrix;
      73             : 
      74           3 :     for (int i = 0; i < num_rows - 1; ++i) {
      75             :         // perform partial pivot
      76           2 :         int pivotRow = i;
      77           5 :         for (int k = i + 1; k < num_rows; ++k) {
      78           3 :             if (std::abs(temp_mtx[k][i]) > std::abs(temp_mtx[pivotRow][i]))
      79           1 :                 pivotRow = k;
      80             :         }
      81           2 :         std::swap(temp_mtx[i], temp_mtx[pivotRow]);
      82             : 
      83             :         // eliminate entries below pivot
      84           5 :         for (int k = i + 1; k < num_rows; ++k) {
      85           3 :             double factor = temp_mtx[k][i] / temp_mtx[i][i];
      86          14 :             for (int j = i; j < num_cols; ++j) {
      87          11 :                 temp_mtx[k][j] -= factor * temp_mtx[i][j];
      88             :             }
      89             :         }
      90             :     }
      91             : 
      92             :     // back-substitution
      93           1 :     std::vector<double> solutions(num_rows, 0);
      94             : 
      95           4 :     for (int i = num_rows - 1; i >= 0; --i) {
      96           3 :         double sum = 0.0;
      97           6 :         for (int j = i + 1; j < num_cols - 1; ++j) {
      98           3 :             sum += temp_mtx[i][j] * solutions[j];
      99             :         }
     100           3 :         solutions[i] = (temp_mtx[i][num_cols - 1] - sum) / temp_mtx[i][i];
     101             :     }
     102             : 
     103           2 :     return solutions;
     104           1 : }
     105             : 
     106             : // calculate the determinant of the matrix
     107           7 : double gpmp::linalg::LinSys::determinant() const {
     108             :     // assuming square matrix
     109           7 :     if (num_rows != num_cols) {
     110           0 :         std::cerr << "Error: Determinant is undefined for non-square matrices."
     111           0 :                   << std::endl;
     112           0 :         return 0.0;
     113             :     }
     114             : 
     115           7 :     std::vector<std::vector<double>> temp_mtx = matrix;
     116           7 :     double det = 1.0;
     117             : 
     118          15 :     for (int i = 0; i < num_rows - 1; ++i) {
     119          20 :         for (int k = i + 1; k < num_rows; ++k) {
     120          12 :             double factor = temp_mtx[k][i] / temp_mtx[i][i];
     121          46 :             for (int j = i; j < num_cols; ++j) {
     122          34 :                 temp_mtx[k][j] -= factor * temp_mtx[i][j];
     123             :             }
     124             :         }
     125             :     }
     126             : 
     127          22 :     for (int i = 0; i < num_rows; ++i) {
     128          15 :         det *= temp_mtx[i][i];
     129             :     }
     130             : 
     131           7 :     return det;
     132           7 : }
     133             : 
     134             : // invert the matrix
     135           0 : void gpmp::linalg::LinSys::invert_mtx() {
     136             :     // assuming square matrix
     137           0 :     if (num_rows != num_cols) {
     138             :         std::cerr
     139           0 :             << "Error: Matrix inversion is undefined for non-square matrices."
     140           0 :             << std::endl;
     141           0 :         return;
     142             :     }
     143             : 
     144             :     std::vector<std::vector<double>> identity(
     145           0 :         num_rows,
     146           0 :         std::vector<double>(num_cols, 0.0));
     147             : 
     148             :     // augment the matrix with the identity matrix
     149           0 :     for (int i = 0; i < num_rows; ++i) {
     150           0 :         matrix[i].insert(matrix[i].end(),
     151           0 :                          identity[i].begin(),
     152           0 :                          identity[i].end());
     153             :     }
     154             : 
     155             :     // perform Gaussian elimination
     156           0 :     solve_gauss();
     157             : 
     158             :     // separate the inverse matrix
     159           0 :     for (int i = 0; i < num_rows; ++i) {
     160           0 :         matrix[i].erase(matrix[i].begin(), matrix[i].begin() + num_rows);
     161             :     }
     162           0 : }
     163             : 
     164             : std::pair<std::vector<std::vector<double>>, std::vector<std::vector<double>>>
     165           1 : gpmp::linalg::LinSys::lu_decomp() {
     166           1 :     std::vector<std::vector<double>> L(num_rows,
     167           2 :                                        std::vector<double>(num_cols, 0.0));
     168           1 :     std::vector<std::vector<double>> U(num_rows,
     169           2 :                                        std::vector<double>(num_cols, 0.0));
     170             : 
     171           5 :     for (int i = 0; i < num_rows; ++i) {
     172             :         // Set diagonal elements of L to 1
     173           4 :         L[i][i] = 1.0;
     174             : 
     175          14 :         for (int j = i; j < num_cols; ++j) {
     176          10 :             U[i][j] = matrix[i][j];
     177          20 :             for (int k = 0; k < i; ++k) {
     178          10 :                 U[i][j] -= L[i][k] * U[k][j];
     179             :             }
     180             :         }
     181             : 
     182          10 :         for (int j = i + 1; j < num_rows; ++j) {
     183           6 :             L[j][i] = matrix[j][i];
     184          10 :             for (int k = 0; k < i; ++k) {
     185           4 :                 L[j][i] -= L[j][k] * U[k][i];
     186             :             }
     187           6 :             L[j][i] /= U[i][i];
     188             :         }
     189             :     }
     190             : 
     191           2 :     return {L, U};
     192           1 : }
     193             : 
     194             : // solve the linear system using LU decomposition
     195           0 : void gpmp::linalg::LinSys::solve_lu() {
     196           0 :     lu_decomp();
     197             : 
     198             :     // forward substitution
     199           0 :     for (int i = 0; i < num_rows - 1; ++i) {
     200           0 :         for (int j = i + 1; j < num_rows; ++j) {
     201           0 :             matrix[j][num_cols - 1] -= matrix[j][i] * matrix[i][num_cols - 1];
     202             :         }
     203             :     }
     204             : 
     205             :     // back-substitution
     206           0 :     for (int i = num_rows - 1; i >= 0; --i) {
     207           0 :         for (int j = i + 1; j < num_cols - 1; ++j) {
     208           0 :             matrix[i][num_cols - 1] -= matrix[i][j] * matrix[j][num_cols - 1];
     209             :         }
     210           0 :         matrix[i][num_cols - 1] /= matrix[i][i];
     211             :     }
     212           0 : }
     213             : 
     214           0 : void gpmp::linalg::LinSys::solve_cholesky() {
     215             :     // assuming symmetric and positive-definite matrix
     216           0 :     if (!is_symmetric()) {
     217             :         std::cerr << "Error: Cholesky decomposition is only applicable to"
     218           0 :                      "symmetric positive-definite matrices\n";
     219             :     }
     220             : 
     221           0 :     for (int i = 0; i < num_rows; ++i) {
     222           0 :         for (int j = 0; j <= i; ++j) {
     223           0 :             if (i == j) {
     224           0 :                 double sum = 0.0;
     225           0 :                 for (int k = 0; k < j; ++k) {
     226           0 :                     sum += std::pow(matrix[j][k], 2);
     227             :                 }
     228           0 :                 matrix[j][j] = std::sqrt(matrix[j][j] - sum);
     229             :             }
     230             : 
     231             :             else {
     232           0 :                 double sum = 0.0;
     233           0 :                 for (int k = 0; k < j; ++k) {
     234           0 :                     sum += matrix[i][k] * matrix[j][k];
     235             :                 }
     236           0 :                 matrix[i][j] = (matrix[i][j] - sum) / matrix[j][j];
     237             :             }
     238             :         }
     239             :     }
     240             : 
     241             :     // forward substitution
     242           0 :     for (int i = 0; i < num_rows; ++i) {
     243           0 :         double sum = 0.0;
     244           0 :         for (int j = 0; j < i; ++j) {
     245           0 :             sum += matrix[i][j] * matrix[num_cols - 1][j];
     246             :         }
     247           0 :         matrix[num_cols - 1][i] =
     248           0 :             (matrix[num_cols - 1][i] - sum) / matrix[i][i];
     249             :     }
     250             : 
     251             :     // back-substitution
     252           0 :     for (int i = num_rows - 1; i >= 0; --i) {
     253           0 :         double sum = 0.0;
     254           0 :         for (int j = i + 1; j < num_cols - 1; ++j) {
     255           0 :             sum += matrix[j][i] * matrix[i][num_cols - 1];
     256             :         }
     257           0 :         matrix[i][num_cols - 1] =
     258           0 :             (matrix[i][num_cols - 1] - sum) / matrix[i][i];
     259             :     }
     260           0 : }
     261             : 
     262             : // solve the linear system using Jacobi iteration
     263           0 : void gpmp::linalg::LinSys::solve_jacobi(int maxIterations, double tolerance) {
     264           0 :     std::vector<std::vector<double>> x(num_rows, std::vector<double>(1, 0.0));
     265           0 :     std::vector<std::vector<double>> xOld = x;
     266             : 
     267           0 :     for (int iter = 0; iter < maxIterations; ++iter) {
     268           0 :         for (int i = 0; i < num_rows; ++i) {
     269           0 :             double sum = 0.0;
     270           0 :             for (int j = 0; j < num_cols - 1; ++j) {
     271           0 :                 if (j != i) {
     272           0 :                     sum += matrix[i][j] * xOld[j][0];
     273             :                 }
     274             :             }
     275           0 :             x[i][0] = (matrix[i][num_cols - 1] - sum) / matrix[i][i];
     276             :         }
     277             : 
     278             :         // check convergence
     279           0 :         double maxDiff = 0.0;
     280           0 :         for (int i = 0; i < num_rows; ++i) {
     281           0 :             double diff = std::abs(x[i][0] - xOld[i][0]);
     282           0 :             if (diff > maxDiff) {
     283           0 :                 maxDiff = diff;
     284             :             }
     285             :         }
     286             : 
     287             :         // converged
     288           0 :         if (maxDiff < tolerance) {
     289           0 :             break;
     290             :         }
     291             : 
     292           0 :         xOld = x;
     293             :     }
     294             : 
     295             :     // copy the solution back to the augmented matrix
     296           0 :     for (int i = 0; i < num_rows; ++i) {
     297           0 :         matrix[i][num_cols - 1] = x[i][0];
     298             :     }
     299           0 : }
     300             : 
     301             : // check if the matrix is symmetric and positive-definite
     302           2 : bool gpmp::linalg::LinSys::is_symmetric() const {
     303             :     // assuming square matrix
     304           2 :     if (num_rows != num_cols) {
     305           0 :         return false;
     306             :     }
     307             : 
     308             :     // check symmetry
     309           8 :     for (int i = 0; i < num_rows; ++i) {
     310          24 :         for (int j = 0; j < num_cols; ++j) {
     311             :             // if (matrix[i][j] != matrix[j][i]) {
     312          36 :             if (fabs(matrix[i][j] - matrix[j][i]) >
     313          18 :                 std::numeric_limits<double>::epsilon()) {
     314           0 :                 return false;
     315             :             }
     316             :         }
     317             :     }
     318             : 
     319             :     // check positive definiteness
     320           6 :     for (int i = 0; i < num_rows; ++i) {
     321           5 :         std::vector<std::vector<double>> submatrix(i + 1,
     322          10 :                                                    std::vector<double>(i + 1));
     323          14 :         for (int j = 0; j <= i; ++j) {
     324          28 :             for (int k = 0; k <= i; ++k) {
     325          19 :                 submatrix[j][k] = matrix[j][k];
     326             :             }
     327             :         }
     328             : 
     329           5 :         double det = LinSys(submatrix).determinant();
     330           5 :         if (det <= 0.0) {
     331           1 :             return false;
     332             :         }
     333           5 :     }
     334             : 
     335           1 :     return true;
     336             : }
     337             : 
     338           1 : double gpmp::linalg::LinSys::frobenius_norm() const {
     339           1 :     double sum = 0.0;
     340           4 :     for (int i = 0; i < num_rows; ++i) {
     341          12 :         for (int j = 0; j < num_cols; ++j) {
     342           9 :             sum += std::pow(matrix[i][j], 2);
     343             :         }
     344             :     }
     345           1 :     return std::sqrt(sum);
     346             : }
     347             : 
     348             : // calculate the 1-norm of the matrix
     349           1 : double gpmp::linalg::LinSys::one_norm() const {
     350           1 :     double maxColSum = 0.0;
     351           4 :     for (int j = 0; j < num_cols; ++j) {
     352           3 :         double colSum = 0.0;
     353          12 :         for (int i = 0; i < num_rows; ++i) {
     354           9 :             colSum += std::abs(matrix[i][j]);
     355             :         }
     356           3 :         maxColSum = std::max(maxColSum, colSum);
     357             :     }
     358           1 :     return maxColSum;
     359             : }
     360             : 
     361             : // calculate the infinity norm of the matrix
     362           1 : double gpmp::linalg::LinSys::inf_norm() const {
     363           1 :     double maxRowSum = 0.0;
     364           4 :     for (int i = 0; i < num_rows; ++i) {
     365           3 :         double rowSum = 0.0;
     366          12 :         for (int j = 0; j < num_cols; ++j) {
     367           9 :             rowSum += std::abs(matrix[i][j]);
     368             :         }
     369           3 :         maxRowSum = std::max(maxRowSum, rowSum);
     370             :     }
     371           1 :     return maxRowSum;
     372             : }
     373             : 
     374             : // perform Gram-Schmidt orthogonalization
     375           0 : void gpmp::linalg::LinSys::gram_schmidt() {
     376             :     std::vector<std::vector<double>> orthoBasis(
     377           0 :         num_rows,
     378           0 :         std::vector<double>(num_cols, 0.0));
     379             : 
     380           0 :     for (int j = 0; j < num_cols; ++j) {
     381           0 :         for (int i = 0; i < num_rows; ++i) {
     382           0 :             double projection = 0.0;
     383           0 :             for (int k = 0; k < i; ++k) {
     384           0 :                 projection += (matrix[i][j] * orthoBasis[k][j]) /
     385           0 :                               (orthoBasis[k][j] * orthoBasis[k][j]);
     386             :             }
     387           0 :             orthoBasis[i][j] = matrix[i][j] - projection;
     388             :         }
     389             :     }
     390             : 
     391             :     // Replace the matrix with the orthogonalized basis
     392           0 :     matrix = orthoBasis;
     393           0 : }
     394             : 
     395             : // check if the matrix is diagonally dominant
     396           2 : bool gpmp::linalg::LinSys::diagonally_dominant() const {
     397           5 :     for (int i = 0; i < num_rows; ++i) {
     398           4 :         double diagonalValue = std::abs(matrix[i][i]);
     399           4 :         double rowSum = 0.0;
     400          16 :         for (int j = 0; j < num_cols; ++j) {
     401          12 :             if (j != i) {
     402           8 :                 rowSum += std::abs(matrix[i][j]);
     403             :             }
     404             :         }
     405           4 :         if (diagonalValue <= rowSum) {
     406           1 :             return false;
     407             :         }
     408             :     }
     409           1 :     return true;
     410             : }
     411             : 
     412             : // check if the system is consistent
     413           2 : bool gpmp::linalg::LinSys::is_consistent() const {
     414           5 :     for (int i = 0; i < num_rows; ++i) {
     415           4 :         bool allZeros = true;
     416           7 :         for (int j = 0; j < num_cols - 1; ++j) {
     417           6 :             if (fabs(matrix[i][j]) > std::numeric_limits<double>::epsilon()) {
     418             : 
     419           3 :                 allZeros = false;
     420           3 :                 break;
     421             :             }
     422             :         }
     423           5 :         if (allZeros && fabs(matrix[i][num_cols - 1]) >
     424           1 :                             std::numeric_limits<double>::epsilon()) {
     425           1 :             return false;
     426             :         }
     427             :     }
     428           1 :     return true;
     429             : }
     430             : 
     431             : // check if the system is homogeneous
     432           2 : bool gpmp::linalg::LinSys::is_homogeneous() const {
     433           5 :     for (int i = 0; i < num_rows; ++i) {
     434           8 :         if (fabs(matrix[i][num_cols - 1]) >
     435           4 :             std::numeric_limits<double>::epsilon()) {
     436           1 :             return false;
     437             :         }
     438             :     }
     439           1 :     return true;
     440             : }

Generated by: LCOV version 1.14