Chi-Tech
chi_math_02_matrix_operations.cc
Go to the documentation of this file.
1#include "chi_math.h"
2#include <assert.h>
3
4//######################################################### Print
5/** Prints the contents of a matrix.*/
7{
8 size_t AR = A.size();
9 size_t AC = 0;
10 if (AR)
11 AC = A[0].size();
12 else
13 std::cout << "A has no rows" << std::endl;
14
15 for(size_t i = 0; i < AR; i++)
16 {
17 for(size_t j = 0; j < AC; j++)
18 {
19 std::cout << A[i][j] << ' ';
20 }
21 std::cout << std::endl;
22 }
23}
24
25//######################################################### Scale
26/** Scales the matrix by a constant value.*/
27void chi_math::Scale(MatDbl &A, const double &val)
28{
29 for (std::vector<double>& Ai : A)
30 for (double& Aij : Ai)
31 Aij *= val;
32}
33
34//######################################################### Scale
35/** Sets all the entries of the matrix to a constant value.*/
36void chi_math::Set(MatDbl &A, const double &val)
37{
38 for (std::vector<double>& Ai : A)
39 for (double& Aij : Ai)
40 Aij = val;
41}
42
43//######################################################### Transpose
44/** Returns the transpose of a matrix.*/
46{
47 assert(A.size());
48 assert(A[0].size());
49 size_t AR = A.size();
50 size_t AC = 0;
51 if (AR)
52 AC = A[0].size();
53
54 MatDbl T(AC, VecDbl(AR));
55 for (size_t i = 0; i < AR; i++)
56 for (size_t j = 0; j < AC; j++)
57 T[j][i] = A[i][j];
58 return T;
59}
60
61//######################################################### Swap Row
62/** Swaps two rows of a matrix.*/
63void chi_math::SwapRow(size_t r1, size_t r2, MatDbl &A)
64{
65 assert(A.size());
66 assert(A[0].size());
67 size_t AR = A.size();
68 size_t AC = 0;
69 if (AR)
70 AC = A[0].size();
71
72 assert(r1 >= 0 && r1 < AR && r2 >= 0 && r2 < AR);
73
74 for (size_t j = 0; j < AC; j++)
75 std::swap(A[r1][j], A[r2][j]);
76
77}
78
79//######################################################### Swap Columns
80/** Swaps two columns of a matrix.*/
81void chi_math::SwapColumn(size_t c1, size_t c2, MatDbl &A)
82{
83 assert(A.size());
84 assert(A[0].size());
85 size_t AR = A.size();
86
87 if (A.size())
88 assert(c1 >= 0 && c1 < A[0].size() && c2 >= 0 && c2 < A[0].size());
89
90 for (size_t i = 0; i < AR; i++)
91 std::swap(A[i][c1], A[i][c2]);
92}
93
94//######################################################### Matrix-multiply
95/** Multiply matrix with a constant and return result.*/
96MatDbl chi_math::MatMul(const MatDbl &A, const double c)
97{
98 size_t R = A.size();
99 size_t C = 0;
100 if(R)
101 C = A[0].size();
102
103 MatDbl B(R, VecDbl(C,0.));
104
105 for(size_t i = 0; i < R; i++)
106 for(size_t j = 0; j < C; j++)
107 B[i][j] = A[i][j] * c;
108
109 return B;
110}
111
112/** Multiply matrix with a vector and return resulting vector*/
114{
115 size_t R = A.size();
116 size_t C = x.size();
117
118 assert(R>0);
119 assert(C == A[0].size());
120
121 VecDbl b(R,0.);
122
123 for(size_t i = 0; i < R; i++)
124 {
125 for(size_t j = 0; j < C; j++)
126 b[i] += A[i][j] * x[j];
127 }
128
129 return b;
130}
131
132/** Mutliply two matrices and return result.*/
134{
135 size_t AR = A.size();
136
137 assert(AR != 0 && B.size() != 0);
138
139 size_t AC = A[0].size();
140 size_t BC = B[0].size();
141
142 assert(AC != 0 && BC != 0 && AC == B.size());
143
144 size_t CR = AR;
145 size_t CC = BC;
146 size_t Cs = AC;
147
148 MatDbl C(CR, VecDbl(CC,0.));
149
150 for(size_t i = 0; i < CR; i++)
151 for(size_t j = 0; j < CC; j++)
152 for(size_t k = 0; k < Cs; k++)
153 C[i][j] += A[i][k] * B[k][j];
154
155 return C;
156}
157
158//######################################################### Addition
159/** Adds two matrices and returns the result.*/
161{
162 size_t AR = A.size();
163 size_t BR = A.size();
164
165 assert(AR != 0 && B.size() != 0);
166 assert(AR == BR);
167
168 size_t AC = A[0].size();
169 size_t BC = B[0].size();
170
171 assert(AC != 0 && BC != 0);
172 assert(AC == BC);
173
174 MatDbl C(AR, VecDbl(AC,0.0));
175
176 for(size_t i = 0; i < AR; i++)
177 for(size_t j = 0; j < AC; j++)
178 C[i][j] = A[i][j] + B[i][j];
179
180 return C;
181}
182
183//######################################################### Addition
184/** Subtracts matrix A from B and returns the result.*/
186{
187 size_t AR = A.size();
188 size_t BR = A.size();
189
190 assert(AR != 0 && B.size() != 0);
191 assert(AR == BR);
192
193 size_t AC = A[0].size();
194 size_t BC = B[0].size();
195
196 assert(AC != 0 && BC != 0);
197 assert(AC == BC);
198
199 MatDbl C(AR, VecDbl(AC,0.0));
200
201 for(size_t i = 0; i < AR; i++)
202 for(size_t j = 0; j < AC; j++)
203 C[i][j] = B[i][j] - A[i][j];
204
205 return C;
206}
207
208
209//######################################################### Determinant
210/** Computes the determinant of a matrix.*/
212{
213 size_t R = A.size();
214
215 if ( R == 1 )
216 return A[0][0];
217 else if ( R == 2 )
218 {
219 return A[0][0]*A[1][1] - A[0][1]*A[1][0];
220 }
221 else if ( R == 3 )
222 {
223 return A[0][0]*A[1][1]*A[2][2] + A[0][1]*A[1][2]*A[2][0] +
224 A[0][2]*A[1][0]*A[2][1] - A[0][0]*A[1][2]*A[2][1] -
225 A[0][1]*A[1][0]*A[2][2] - A[0][2]*A[1][1]*A[2][0];
226 }
227 // http://www.cvl.iis.u-tokyo.ac.jp/~Aiyazaki/tech/teche23.htAl
228 else if ( R == 4 )
229 {
230 return A[0][0]*A[1][1]*A[2][2]*A[3][3] + A[0][0]*A[1][2]*A[2][3]*A[3][1] + A[0][0]*A[1][3]*A[2][1]*A[3][2]
231 + A[0][1]*A[1][0]*A[2][3]*A[3][2] + A[0][1]*A[1][2]*A[2][0]*A[3][3] + A[0][1]*A[1][3]*A[2][2]*A[3][0]
232 + A[0][2]*A[1][0]*A[2][1]*A[3][3] + A[0][2]*A[1][1]*A[2][3]*A[3][0] + A[0][2]*A[1][3]*A[2][0]*A[3][1]
233 + A[0][3]*A[1][0]*A[2][2]*A[3][1] + A[0][3]*A[1][1]*A[2][0]*A[3][2] + A[0][3]*A[1][2]*A[2][1]*A[3][0]
234 - A[0][0]*A[1][1]*A[2][3]*A[3][2] - A[0][0]*A[1][2]*A[2][1]*A[3][3] - A[0][0]*A[1][3]*A[2][2]*A[3][1]
235 - A[0][1]*A[1][0]*A[2][2]*A[3][3] - A[0][1]*A[1][2]*A[2][3]*A[3][0] - A[0][1]*A[1][3]*A[2][0]*A[3][2]
236 - A[0][2]*A[1][0]*A[2][3]*A[3][1] - A[0][2]*A[1][1]*A[2][0]*A[3][3] - A[0][2]*A[1][3]*A[2][1]*A[3][0]
237 - A[0][3]*A[1][0]*A[2][1]*A[3][2] - A[0][3]*A[1][1]*A[2][2]*A[3][0] - A[0][3]*A[1][2]*A[2][0]*A[3][1];
238 }
239 else
240 {
241 double det = 0;
242 for (size_t n = 0; n < R; n++)
243 {
244 std::vector<std::vector<double> > M = SubMatrix(0, n, A);
245 double pm = ((n+1)%2)*2. - 1.;
246 det += pm * A[0][n] * Determinant(M);
247 }
248 return det;
249 }
250}
251
252//######################################################### Submatrix
253/** Returns a sub-matrix.*/
255 const size_t c,
256 const MatDbl &A)
257{
258 size_t R = A.size();
259 size_t C = 0;
260 if (R)
261 C = A[0].size();
262
263 assert( (r >= 0) && (r < R) && (c >= 0) && (c < C) );
264
265 MatDbl B(R-1,VecDbl(C-1));
266 for (size_t i = 0, ii = 0; i < R; ++i)
267 {
268 if ( i != r )
269 {
270 for (size_t j = 0, jj = 0; j < C; ++j)
271 {
272 if ( j != c )
273 {
274 B[ii][jj] = A[i][j];
275 ++jj;
276 }
277 }
278 ++ii;
279 }
280 }
281 return B;
282}
283
284//######################################################### Gauss Elimination
285/** Gauss Elimination without pivoting.*/
287 VecDbl &b, int n)
288{
289 // Forward elimination
290 for(int i = 0;i < n-1;++i)
291 {
292 const std::vector<double>& ai = A[i];
293 double bi = b[i];
294 double factor = 1.0/A[i][i];
295 for(int j = i+1;j < n;++j)
296 {
297 std::vector<double>& aj = A[j];
298 double val = aj[i] * factor;
299 b[j] -= val * bi;
300 for(int k = i+1;k < n;++k)
301 aj[k] -= val * ai[k];
302 }
303 }
304
305 // Back substitution
306 for(int i = n-1;i >= 0;--i)
307 {
308 const std::vector<double>& ai = A[i];
309 double bi = b[i];
310 for(int j = i+1;j < n;++j)
311 bi -= ai[j] * b[j];
312 b[i] = bi/ai[i];
313 }
314}
315
316//#########################################################
317/** Computes the inverse of a matrix using Gauss-Elimination with pivoting.*/
319{
320 assert(A.size());
321 assert(A.size() == A[0].size());
322
323 const unsigned int R = A.size();
324
325 std::vector<std::vector<double> > M(R,std::vector<double>(R,0.));
326
327 for(unsigned int i = 0; i < R; i++)
328 M[i][i] = 1.;
329
330 std::vector<std::vector<double> > B = A;
331
332 for (unsigned int c = 0; c < R; c++)
333 {
334 // Find a row with the largest pivot value
335 unsigned int max_row=c; //nzr = non-zero row
336 for (unsigned int r = c; r < R; ++r)
337 if (std::fabs(B[r][c]) > std::fabs(B[max_row][c]))
338 max_row = r;
339
340 if (max_row != c )
341 {
342 SwapRow(max_row , c, B );
343 SwapRow(max_row , c, M );
344 }
345
346 // Eliminate non-zero values
347 for (unsigned int r = 0; r < R; r++)
348 {
349 if ( r != c )
350 {
351 double g = B[r][c]/B[c][c];
352 if ( B[r][c] != 0 )
353 {
354 for(unsigned int k=0; k < R; k++)
355 {
356 B[r][k] -= B[c][k]*g;
357 M[r][k] -= M[c][k]*g;
358 }
359 B[r][c] = 0;
360 }
361 }
362 else
363 {
364 double g = 1/B[c][c];
365 for (unsigned int k = 0; k < R; k++)
366 {
367 B[r][k] *= g;
368 M[r][k] *= g;
369 }
370 }
371 }
372 }
373 return M;
374}
375
376//######################################################### Matrix inverse
377/** Computes the inverse of a matrix.*/
378MatDbl
380{
381 size_t R = A.size();
382 std::vector<std::vector<double>> M(R,std::vector<double>(R,0.));
383 double f(0.);
384
385 // Only calculate the determinant if matrix size is less than 5 since
386 // the inverse is directly calculated for larger matrices. Otherwise,
387 // the inverse routine spends all of its time sitting in the determinant
388 // function which is unnecessary.
389 if (R < 5)
390 {
391 f = Determinant(A);
392 assert(f != 0.);
393 f = 1./f;
394 }
395
396 if (R==1)
397 M[0][0] = f;
398 else if (R==2)
399 {
400 M[0][0] = A[1][1];
401 M[0][1] = -A[0][1];
402 M[1][0] = -A[1][0];
403 M[1][1] = A[0][0];
404 Scale(M,f);
405 }
406 else if (R==3)
407 {
408 M[0][0] = A[2][2]*A[1][1]-A[2][1]*A[1][2];
409 M[0][1] = -(A[2][2]*A[0][1]-A[2][1]*A[0][2]);
410 M[0][2] = A[1][2]*A[0][1]-A[1][1]*A[0][2];
411 M[1][0] = -(A[2][2]*A[1][0]-A[2][0]*A[1][2]);
412 M[1][1] = A[2][2]*A[0][0]-A[2][0]*A[0][2];
413 M[1][2] = -(A[1][2]*A[0][0]-A[1][0]*A[0][2]);
414 M[2][0] = A[2][1]*A[1][0]-A[2][0]*A[1][1];
415 M[2][1] = -(A[2][1]*A[0][0]-A[2][0]*A[0][1]);
416 M[2][2] = A[1][1]*A[0][0]-A[1][0]*A[0][1];
417 Scale(M,f);
418 }
419 else if (R==4)
420 {
421 // http://www.cvl.iis.u-tokyo.ac.jp/~Aiyazaki/tech/teche23.htAl
422 M[0][0] = A[1][1]*A[2][2]*A[3][3] + A[1][2]*A[2][3]*A[3][1] + A[1][3]*A[2][1]*A[3][2] - A[1][1]*A[2][3]*A[3][2] - A[1][2]*A[2][1]*A[3][3] - A[1][3]*A[2][2]*A[3][1];
423 M[0][1] = A[0][1]*A[2][3]*A[3][2] + A[0][2]*A[2][1]*A[3][3] + A[0][3]*A[2][2]*A[3][1] - A[0][1]*A[2][2]*A[3][3] - A[0][2]*A[2][3]*A[3][1] - A[0][3]*A[2][1]*A[3][2];
424 M[0][2] = A[0][1]*A[1][2]*A[3][3] + A[0][2]*A[1][3]*A[3][1] + A[0][3]*A[1][1]*A[3][2] - A[0][1]*A[1][3]*A[3][2] - A[0][2]*A[1][1]*A[3][3] - A[0][3]*A[1][2]*A[3][1];
425 M[0][3] = A[0][1]*A[1][3]*A[2][2] + A[0][2]*A[1][1]*A[2][3] + A[0][3]*A[1][2]*A[2][1] - A[0][1]*A[1][2]*A[2][3] - A[0][2]*A[1][3]*A[2][1] - A[0][3]*A[1][1]*A[2][2];
426
427 M[1][0] = A[1][0]*A[2][3]*A[3][2] + A[1][2]*A[2][0]*A[3][3] + A[1][3]*A[2][2]*A[3][0] - A[1][0]*A[2][2]*A[3][3] - A[1][2]*A[2][3]*A[3][0] - A[1][3]*A[2][0]*A[3][2];
428 M[1][1] = A[0][0]*A[2][2]*A[3][3] + A[0][2]*A[2][3]*A[3][0] + A[0][3]*A[2][0]*A[3][2] - A[0][0]*A[2][3]*A[3][2] - A[0][2]*A[2][0]*A[3][3] - A[0][3]*A[2][2]*A[3][0];
429 M[1][2] = A[0][0]*A[1][3]*A[3][2] + A[0][2]*A[1][0]*A[3][3] + A[0][3]*A[1][2]*A[3][0] - A[0][0]*A[1][2]*A[3][3] - A[0][2]*A[1][3]*A[3][0] - A[0][3]*A[1][0]*A[3][2];
430 M[1][3] = A[0][0]*A[1][2]*A[2][3] + A[0][2]*A[1][3]*A[2][0] + A[0][3]*A[1][0]*A[2][2] - A[0][0]*A[1][3]*A[2][2] - A[0][2]*A[1][0]*A[2][3] - A[0][3]*A[1][2]*A[2][0];
431
432 M[2][0] = A[1][0]*A[2][1]*A[3][3] + A[1][1]*A[2][3]*A[3][0] + A[1][3]*A[2][0]*A[3][1] - A[1][0]*A[2][3]*A[3][1] - A[1][1]*A[2][0]*A[3][3] - A[1][3]*A[2][1]*A[3][0];
433 M[2][1] = A[0][0]*A[2][3]*A[3][1] + A[0][1]*A[2][0]*A[3][3] + A[0][3]*A[2][1]*A[3][0] - A[0][0]*A[2][1]*A[3][3] - A[0][1]*A[2][3]*A[3][0] - A[0][3]*A[2][0]*A[3][1];
434 M[2][2] = A[0][0]*A[1][1]*A[3][3] + A[0][1]*A[1][3]*A[3][0] + A[0][3]*A[1][0]*A[3][1] - A[0][0]*A[1][3]*A[3][1] - A[0][1]*A[1][0]*A[3][3] - A[0][3]*A[1][1]*A[3][0];
435 M[2][3] = A[0][0]*A[1][3]*A[2][1] + A[0][1]*A[1][0]*A[2][3] + A[0][3]*A[1][1]*A[2][0] - A[0][0]*A[1][1]*A[2][3] - A[0][1]*A[1][3]*A[2][0] - A[0][3]*A[1][0]*A[2][1];
436
437 M[3][0] = A[1][0]*A[2][2]*A[3][1] + A[1][1]*A[2][0]*A[3][2] + A[1][2]*A[2][1]*A[3][0] - A[1][0]*A[2][1]*A[3][2] - A[1][1]*A[2][2]*A[3][0] - A[1][2]*A[2][0]*A[3][1];
438 M[3][1] = A[0][0]*A[2][1]*A[3][2] + A[0][1]*A[2][2]*A[3][0] + A[0][2]*A[2][0]*A[3][1] - A[0][0]*A[2][2]*A[3][1] - A[0][1]*A[2][0]*A[3][2] - A[0][2]*A[2][1]*A[3][0];
439 M[3][2] = A[0][0]*A[1][2]*A[3][1] + A[0][1]*A[1][0]*A[3][2] + A[0][2]*A[1][1]*A[3][0] - A[0][0]*A[1][1]*A[3][2] - A[0][1]*A[1][2]*A[3][0] - A[0][2]*A[1][0]*A[3][1];
440 M[3][3] = A[0][0]*A[1][1]*A[2][2] + A[0][1]*A[1][2]*A[2][0] + A[0][2]*A[1][0]*A[2][1] - A[0][0]*A[1][2]*A[2][1] - A[0][1]*A[1][0]*A[2][2] - A[0][2]*A[1][1]*A[2][0];
441 Scale(M,f);
442 }
443 else
444 M = InverseGEPivoting(A);
445
446 return M;
447}
448
449
450//######################################################### Power iteration
451/** Performs power iteration to obtain the fundamental eigen mode. The
452 * eigen-value of the fundamental mode is return whilst the eigen-vector
453 * is return via reference.*/
455 const MatDbl &A, VecDbl &e_vec, int max_it, double tol)
456{
457 // Local Variables
458 unsigned int n = A.size();
459 int it_counter = 0;
460 VecDbl y(n,1.);
461 double lambda0 = 0.;
462
463 // Perform initial iteration outside of loop
464 VecDbl Ay = MatMul(A, y);
465 double lambda = Dot( y, Ay );
466 y = VecMul( Ay, 1./Vec2Norm(Ay) );
467 if (lambda < 0.)
468 Scale(y, -1.0);
469
470 // Perform convergence loop
471 bool converged = false;
472 while(!converged && it_counter < max_it)
473 {
474 // Update old eigenvalue
475 lambda0 = std::fabs(lambda);
476 // Calculate new eigenvalue/eigenvector
477 Ay = MatMul(A, y);
478 lambda = Dot( y, Ay );
479 y = VecMul( Ay, 1./Vec2Norm(Ay) );
480
481 // Check if converged or not
482 if (std::fabs(std::fabs(lambda) - lambda0) <= tol)
483 converged = true;
484 // Update counter
485 ++it_counter;
486 }
487
488 if (lambda < 0.)
489 Scale(y, -1.0);
490
491 // Renormalize eigenvector for the last time
492 y = VecMul(Ay, 1./lambda);
493
494 // Set eigenvector, return the eigenvalue
495 e_vec = std::move(y);
496
497 return lambda;
498}
499
500
501
502
503
504
505
506
507
508
509
510
511
512
std::vector< VecDbl > MatDbl
Definition: chi_math.h:13
std::vector< double > VecDbl
Definition: chi_math.h:12
void PrintMatrix(const MatDbl &A)
double PowerIteration(const MatDbl &A, VecDbl &e_vec, int max_it=2000, double tol=1.0e-13)
void Scale(VecDbl &x, const double &val)
VecDbl VecMul(const VecDbl &x, const double &val)
void Set(VecDbl &x, const double &val)
MatDbl Inverse(const MatDbl &A)
MatDbl SubMatrix(const size_t r, const size_t c, const MatDbl &A)
double Determinant(const MatDbl &A)
void SwapColumn(size_t c1, size_t c2, MatDbl &A)
MatDbl Transpose(const MatDbl &A)
MatDbl MatAdd(const MatDbl &A, const MatDbl &B)
void GaussElimination(MatDbl &A, VecDbl &b, int n)
MatDbl MatMul(const MatDbl &A, const double c)
double Dot(const VecDbl &x, const VecDbl &y)
MatDbl InverseGEPivoting(const MatDbl &A)
MatDbl MatSubtract(const MatDbl &A, const MatDbl &B)
double Vec2Norm(const VecDbl &x)
void SwapRow(size_t r1, size_t r2, MatDbl &A)