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