ModelSpace
All Classes Namespaces Functions Variables Enumerations Pages
Matrix.hpp
1/******************************************************************************
2* Copyright (c) ATTX LLC 2024. All Rights Reserved.
3*
4* This software and associated documentation (the "Software") are the
5* proprietary and confidential information of ATTX, LLC. The Software is
6* furnished under a license agreement between ATTX and the user organization
7* and may be used or copied only in accordance with the terms of the agreement.
8* Refer to 'license/attx_license.adoc' for standard license terms.
9*
10* EXPORT CONTROL NOTICE: THIS SOFTWARE MAY INCLUDE CONTENT CONTROLLED UNDER THE
11* INTERNATIONAL TRAFFIC IN ARMS REGULATIONS (ITAR) OR THE EXPORT ADMINISTRATION
12* REGULATIONS (EAR99). No part of the Software may be used, reproduced, or
13* transmitted in any form or by any means, for any purpose, without the express
14* written permission of ATTX, LLC.
15******************************************************************************/
16/*
17Matrix math header file
18
19Author: Alex Reynolds
20*/
21#ifndef MATRIX_HPP
22#define MATRIX_HPP
23
24#include <array>
25#include <iostream>
26
28#include "safemath.hpp"
29
30namespace clockwerk {
31
32 /// @brief Matrix math implementation
33 ///
34 /// This file defines a base class for computational matrix math.
35 /// It is designed for use in real-time, embedded mode or with
36 /// simulation. As such, it contains certain limitations on dynamic
37 /// allocation to make it real-time compliant.
38 ///
39 /// Note: The naming convention observed for matrix math is as follows:
40 /// - Capital letters represent matrices
41 /// - Lowercase letters represent scalars
42 /// - All vectors are 1-dimensional matrices for the purpose of math
43 /// - Pass by reference return values are all named "result"
44 /// - The letter used in math indicates the order of operations - A/a
45 /// comes first, then B/b. A/a or B/b always represent the "other"
46 /// value in an operation
47 ///
48 /// Important: The matrix class is templated such that it can be used
49 /// with a generic type T, but should only be used on native types,
50 /// as other implementations could result in undefined behavior.
51 ///
52 /// TODO: Flattening and flattened constructor
53 template <typename T, unsigned int R, unsigned int C>
54 class Matrix {
55 public:
56 /// Default constructor to initialize a matrix to all zeroes for ease of use
57 Matrix<T, R, C>();
58
59 /// Constructor to initialize a matrix with all the same element
60 Matrix<T, R, C>(T elements);
61
62 /// Constructor for Matrix class with initialization.
63 /// Initializes matrix to values passed in via array
64 Matrix<T, R, C>(const T(&initial)[R][C]);
65
66 /// Copy constructor for Matrix class. Copies data from matrix
67 /// object to the current instance
68 Matrix<T, R, C>(const Matrix<T, R, C> &initial);
69
70 /// Array constructor for the matrix class. Initializes from std::array
71 Matrix<T, R, C>(const std::array<std::array<T, C>, R> &initial);
72
73 /// Destructor -- doesn't do anything because we don't dynamically
74 /// allocate
75 ~Matrix<T, R, C>() {}
76
77 /// @brief Function to dump information on matrix
78 void dump() const;
79
80 /// @brief Function to dump information on matrix to string
81 std::string str() const;
82
83 /// ----------------------------------------------------------
84 /// Getters and setters
85 /// ----------------------------------------------------------
86 /// @brief Function to set a single value in the matrix
87 /// @param row The row index
88 /// @param col The column index
89 /// @param value The value to set the matrix to
90 /// @return Integer value corresponding to error codes in clockwerkerrrors.h
91 int set(const unsigned int &row, const unsigned int &col, const T &value);
92
93 /// @brief Function to get a single value in the matrix
94 /// @param row The row index
95 /// @param col The column index
96 /// @param value PBR return of the value in the matrix
97 /// @return Integer value corresponding to error codes in clockwerkerrrors.h
98 int get(const unsigned int &row, const unsigned int &col, T &result) const;
99
100 /// @brief Function to set the values of the matrix row-wise
101 /// @param start_ptr The data address at which read should begin.
102 /// @note start_ptr must represent an array or similar type of at least R*C values
103 /// @note Not protected. May segfault if not used properly
104 /// @note The array [1 2 3 4] would read into a matrix as [1 2; 3 4]
105 void setFromArray(const T* start_ptr);
106
107 /// @brief Function to get the values of the matrix row-wise
108 /// @param start_ptr The data address at which write should begin.
109 /// @note start_ptr must represent an array or similar type of at least R*C values
110 /// @note Not protected. May segfault if not used properly
111 /// @note The matrix [1 2; 3 4] would output as an array [1 2 3 4]
112 void getAsArray(T* start_ptr) const;
113
114 /// @brief Function to get a single value in the matrix
115 /// @param row The row index
116 /// @param col The column index
117 /// @return Matrix value at index
118 /// @note Does not bounds check and is unsafe for embedded operations
119 T get(const unsigned int &row, const unsigned int &col) const;
120
121 /// @brief Function to get a copy of the matrix
122 /// @param result PBR return of a copy of this matrix
123 void getCopy(Matrix<T, R, C> &result) const;
124
125 /// @brief Equals operator overload for matrix
126 Matrix<T, R, C>& operator=(const Matrix<T, R, C>& other);
127
128 /// @brief Function to return a matrix row or vector value
129 /// @param idx The row index to return
130 /// @return A pointer to the row element of the matrix. Nullptr if out of range
131 T* operator [](unsigned int idx);
132
133 /// ----------------------------------------------------------
134 /// Operations to get matrix info
135 /// ----------------------------------------------------------
136
137 /// Function to get the size of the matrix
138 std::pair<unsigned int, unsigned int> size() {return std::pair<unsigned int, unsigned int>(R, C);}
139
140 /// @brief Function to return the maximum value in the matrix
141 /// @param result PBR return of maximum value
142 void max(T &result, std::pair<unsigned int, unsigned int> &index) const;
143
144 /// @brief Function to return the minimum value in the matrix
145 /// @param result PBR return of minimum value
146 void min(T &result, std::pair<unsigned int, unsigned int> &index) const;
147
148 /// ----------------------------------------------------------
149 /// Operations to return important matrix information
150 /// ----------------------------------------------------------
151
152 /// @brief Function to return the determinant of the matrix
153 /// @param result PBR return of determinant
154 /// @return Integer value corresponding to error codes in clockwerkerrrors.h
155 int det(T &result) const;
156
157 /// @brief Function to return the inverse of the matrix
158 /// @param result PBR return of matrix inverse
159 /// @return Integer value corresponding to error codes in clockwerkerrrors.h
160 int inverse(Matrix<T, R, C> &result) const;
161 Matrix<T, R, C> inverse() const {Matrix<T, R, C> tmp; inverse(tmp); return tmp;}
162
163 /// @brief Function to return the transpose of the matrix
164 /// @param result PBR return of matrix transpose
165 /// @note Overloaded for return as well
166 void transpose(Matrix<T, C, R> &result) const;
167 Matrix<T, C, R> transpose() const;
168
169 /// @brief Function to return the trace of the matrix
170 /// @param result PBR return of matrix trace
171 /// @return Integer value corresponding to error codes in clockwerkerrrors.h
172 int trace(T &result) const;
173
174 /// @brief Function to set all elements of the matrix to zero
175 void setToZeros();
176
177 /// @brief Function to set matrix to identity, if it is a square matrix
178 /// @return Error code corresponding to success/failure
179 int identity();
180 int eye() {return identity();}
181
182 /// The actual values held by the matrix -- a two dimensional
183 /// array of values indexed as (row, column).
184 /// NOTE: Public for ease of access and speed (no need to use setter/getter),
185 /// and functions in Matrix and Safemath libraries are tested safe. Behavior
186 /// using matrices outside of clockwerk libraries should use setter/getter, rather
187 /// than direct access, for safety.
188 std::array<std::array<T, C>, R> values;
189
190 protected:
191 /// Function to check, given a set of submatrix boundaries, that those boundaries are
192 /// valid for the current matrix
193 int _checkLookupBoundaries(const unsigned int &start_r, const unsigned int &end_r,
194 const unsigned int &start_c, const unsigned int &end_c) const;
195
196 /// @brief Function to take a 2-d matrix represented by A and decompose it into LU form
197 /// @param A A 2-d matrix represented as a double pointer
198 /// After ops A contains a copy of both matrices L-E and U as A=(L-E)+U such that P*A=L*U.
199 /// @param P The permutation matrix to hold info on matrix changes
200 /// @return Error code corresponding to codes in clockwerkerrors.h
201 /// @note Code borrowed/updated from sample C code on wikipedia here:
202 /// https://en.wikipedia.org/wiki/LU_decomposition
203 int _LUPDecompose(T *A[R], unsigned int P[R+1]) const;
204
205 };
206
207 template <typename T, unsigned int R, unsigned int C>
208 Matrix<T, R, C>::Matrix() {
209 unsigned int i, j;
210 /// Explicitly set initialized values to element
211 for(i = 0; i < R; i++) {
212 for(j = 0; j < C; j++) {
213 values[i][j] = T();
214 }
215 }
216 }
217
218 template <typename T, unsigned int R, unsigned int C>
219 Matrix<T, R, C>::Matrix(T elements) {
220 unsigned int i, j;
221 /// Explicitly set initialized values to element
222 for(i = 0; i < R; i++) {
223 for(j = 0; j < C; j++) {
224 values[i][j] = elements;
225 }
226 }
227 }
228
229 template <typename T, unsigned int R, unsigned int C>
230 Matrix<T, R, C>::Matrix(const T(&initial)[R][C]) {
231 /// Note -- one absolutely essential check that's occurring here is that the size
232 /// of the initializer list matches the size of the matrix. This is occurring implicitly
233 /// at compile time via templating and is the reason why an array is used rather than
234 /// a std::initializer_list
235
236 /// Initialize to values from initializer list
237 unsigned int i, j;
238 for(i = 0; i < R; i++) {
239 for(j = 0; j < C; j++) {
240 values[i][j] = initial[i][j];
241 }
242 }
243 }
244
245 template <typename T, unsigned int R, unsigned int C>
246 Matrix<T, R, C>::Matrix(const Matrix<T, R, C> &initial) {
247 unsigned int i, j;
248 /// Explicitly set initialized values to zero
249 for(i = 0; i < R; i++) {
250 for(j = 0; j < C; j++) {
251 values[i][j] = initial.values[i][j];
252 }
253 }
254 }
255
256 template <typename T, unsigned int R, unsigned int C>
257 Matrix<T, R, C>::Matrix(const std::array<std::array<T, C>, R> &initial) {
258 unsigned int i, j;
259 /// Loop through matrix and copy our values
260 for(i = 0; i < R; i++) {
261 for(j = 0; j < C; j++) {
262 values[i][j] = initial[i][j];
263 }
264 }
265 }
266
267 template <typename T, unsigned int R, unsigned int C>
268 T* Matrix<T, R, C>::operator [](unsigned int idx) {
269 // Return pointer to our array element
270 if(idx < R) {
271 return &values[idx][0];
272 } else {
273 return nullptr;
274 }
275 }
276
277 template <typename T, unsigned int R, unsigned int C>
278 void Matrix<T, R, C>::dump() const {
279 unsigned int i, j;
280 /// Loop through matrix and write out values
281 for(i = 0; i < R; i++) {
282 std::cout<<"[";
283 for(j = 0; j < C; j++) {
284 if(j > 0) {
285 std::cout<<", ";
286 }
287 std::cout<<values[i][j];
288 }
289 std::cout<<"]"<<std::endl;
290 }
291 }
292
293 template <typename T, unsigned int R, unsigned int C>
294 std::string Matrix<T, R, C>::str() const {
295 unsigned int i, j;
296 std::string out = "[";
297 /// Loop through matrix and write out values
298 for(i = 0; i < R; i++) {
299 out += "[";
300 for(j = 0; j < C; j++) {
301 if(j > 0) {
302 out += ",";
303 }
304 out += std::to_string(values[i][j]);
305 }
306 out += "]";
307 }
308
309 out += "]";
310 return out;
311 }
312
313 template <typename T, unsigned int R, unsigned int C>
314 int Matrix<T, R, C>::set(const unsigned int &row, const unsigned int &col, const T &value){
315 /// Check boundaries on lookup values to ensure validity
316 if(_checkLookupBoundaries(row, row, col, col)) {
317 /// If we returned true, we had a dimension mismatch. Return error
318 return ERROR_DIMENSIONS;
319 }
320
321 /// Set value
322 values[row][col] = value;
323 return NO_ERROR;
324 }
325
326 template <typename T, unsigned int R, unsigned int C>
327 int Matrix<T, R, C>::get(const unsigned int &row, const unsigned int &col, T &result) const{
328 /// Check boundaries on lookup values to ensure validity
329 if(_checkLookupBoundaries(row, row, col, col)) {
330 /// If we returned true, we had a dimension mismatch. Return error
331 return ERROR_DIMENSIONS;
332 }
333
334 /// Set value
335 result = values[row][col];
336 return NO_ERROR;
337 }
338
339 template <typename T, unsigned int R, unsigned int C>
340 T Matrix<T, R, C>::get(const unsigned int &row, const unsigned int &col) const{
341 return values[row][col];
342 }
343
344 template <typename T, unsigned int R, unsigned int C>
345 void Matrix<T, R, C>::setFromArray(const T* start_ptr) {
346 const T* ptr = start_ptr;
347 unsigned int i, j;
348 for(i = 0; i < R; i++) {
349 for(j = 0; j < C; j++) {
350 values[i][j] = *ptr;
351 ptr++;
352 }
353 }
354 }
355
356 template <typename T, unsigned int R, unsigned int C>
357 void Matrix<T, R, C>::getAsArray(T* start_ptr) const {
358 T* ptr = start_ptr;
359 unsigned int i, j;
360 for(i = 0; i < R; i++) {
361 for(j = 0; j < C; j++) {
362 *ptr = values[i][j];
363 ptr++;
364 }
365 }
366 }
367
368 template <typename T, unsigned int R, unsigned int C>
369 void Matrix<T, R, C>::getCopy(Matrix<T, R, C> &result) const {
370 /// NOTE: This function does not need to check lookup boundaries because
371 /// templating will enforce. Code will not compile if dimensions are incorrect
372
373 unsigned int i, j;
374 /// Loop through matrix and search for maximum value
375 for(i = 0; i < R; i++) {
376 for(j = 0; j < C; j++) {
377 result.values[i][j] = values[i][j];
378 }
379 }
380 }
381
382 template <typename T, unsigned int R, unsigned int C>
383 Matrix<T, R, C>& Matrix<T, R, C>::operator=(const Matrix<T, R, C>& other)
384 {
385 /// Guard self assignment
386 if (this == &other)
387 return *this;
388
389 /// Copy values from current matrix to return
390 unsigned int i, j;
391 for(i = 0; i < R; i++) {
392 for(j = 0; j < C; j++) {
393 this->values[i][j] = other.values[i][j];
394 }
395 }
396 return *this;
397 }
398
399 template <typename T, unsigned int R, unsigned int C>
400 void Matrix<T, R, C>::max(T &result, std::pair<unsigned int, unsigned int> &index) const {
401 unsigned int i, j;
402 index.first = 0;
403 index.second = 0;
404 result = values[0][0]; /// Set our initial index to first value
405 /// Loop through matrix and search for maximum value
406 for(i = 0; i < R; i++) {
407 for(j = 0; j < C; j++) {
408 if(values[i][j] > result) {
409 result = values[i][j];
410 index.first = i;
411 index.second = j;
412 }
413 }
414 }
415 }
416
417 template <typename T, unsigned int R, unsigned int C>
418 void Matrix<T, R, C>::min(T &result, std::pair<unsigned int, unsigned int> &index) const {
419 unsigned int i, j;
420 index.first = 0;
421 index.second = 0;
422 result = values[0][0]; /// Set our initial index to first value
423 /// Loop through matrix and search for minimum value
424 for(i = 0; i < R; i++) {
425 for(j = 0; j < C; j++) {
426 if(values[i][j] < result) {
427 result = values[i][j];
428 index.first = i;
429 index.second = j;
430 }
431 }
432 }
433 }
434
435 template <typename T, unsigned int R, unsigned int C>
436 int Matrix<T, R, C>::det(T &result) const {
437 /// Note: This function needs to check that we have a square matrix to calculate
438 /// determinant. That check is perfomed in the function _LUPDecompose, and therefore
439 /// not performed here.
440
441 /// Initialize some tracking variables for this function
442 unsigned int i, j, err;
443
444 /// Now that we know we have a square matrix, initialize a copy of our matrix as a
445 /// 2-d array. We need to do this because we're going to decompose the matrix into
446 /// upper triangular form in order to take its determinant, but we don't want to
447 /// actually modify the base matrix. Thus, the copy.
448 T A[R][C];
449 T *decomposed[R];
450 for(i = 0; i < R; i++) {
451 decomposed[i] = A[i];
452 for(j = 0; j < C; j++) {
453 A[i][j] = values[i][j];
454 }
455 }
456
457 /// Define our permutation matrix, which exists purely to track changes to the base
458 /// matrix. This is tracked as a single array.
459 /// Note: from this point forward using R to indicate size. R and C are equivalent
460 /// or there will be an error thrown, so which used doesn't matter.
461 unsigned int P[R+1];
462
463 /// Now decompose our matrix into its L*U form for easy determinant calculation.
464 /// If our function returns that there was a poorly conditioned matrix (rank deficient),
465 /// we know that the determinant is zero and didn't actually have an error. Otherwise,
466 /// if our function returns that our matrix is not square, return error
467 err = _LUPDecompose(decomposed, P);
468 if(err == ERROR_POORLY_CONDITIONED) {
469 result = 0;
470 return NO_ERROR;
471 } else if(err) {
472 return err;
473 }
474
475 /// Now, calculate our determinant simply using known relationship for upper triangular matrices
476 result = decomposed[0][0];
477 for(i = 1; i < R; i++) {
478 result *= decomposed[i][i];
479 }
480 if((P[R] - R)%2) {
481 result = -result;
482 }
483
484 return NO_ERROR;
485 }
486
487 template <typename T, unsigned int R, unsigned int C>
488 int Matrix<T, R, C>::inverse(Matrix<T, R, C> &result) const {
489 /// Note: This function needs to check that we have a square matrix to calculate
490 /// determinant. That check is perfomed in the function _LUPDecompose, and therefore
491 /// not performed here.
492
493 /// Note: Need to check that we have matching sizes between matrix and output -- this
494 /// is handled automatically by compiler and doesn't need to be checked here
495
496 /// Initialize some tracking variables for this function
497 unsigned int i, j, k, idx, err;
498
499 /// Now that we know we have a square matrix, initialize a copy of our matrix as a
500 /// 2-d array. We need to do this because we're going to decompose the matrix into
501 /// upper triangular form in order to take its determinant, but we don't want to
502 /// actually modify the base matrix. Thus, the copy.
503 T A[R][C];
504 T *decomposed[R];
505 for(i = 0; i < R; i++) {
506 decomposed[i] = A[i];
507 for(j = 0; j < C; j++) {
508 A[i][j] = values[i][j];
509 }
510 }
511
512 /// Define our permutation matrix, which exists purely to track changes to the base
513 /// matrix. This is tracked as a single array.
514 /// Note: from this point forward using R to indicate size. R and C are equivalent
515 /// or there will be an error thrown, so which used doesn't matter.
516 unsigned int P[R+1];
517
518 /// Now decompose our matrix into its L*U form for easy determinant calculation. Check
519 /// that there was not an error returned due to poorly conditioned matrix
520 err = _LUPDecompose(decomposed, P);
521 if(err) {
522 return err;
523 }
524
525 /// Now, calculate our inverse and assign to our return value
526 for (j = 0; j < R; j++) {
527 for (i = 0; i < R; i++) {
528 result.values[i][j] = P[i] == j ? 1.0 : 0.0;
529 for (k = 0; k < i; k++) {
530 result.values[i][j] -= decomposed[i][k]*result.values[k][j];
531 }
532 }
533
534
535 for(idx = 1; idx < R+1; idx++) {
536 i = R - idx;
537 for (k = i+1; k < R; k++) {
538 result.values[i][j] -= decomposed[i][k]*result.values[k][j];
539 }
540 err = safeDivide(result.values[i][j], decomposed[i][i], result.values[i][j]);
541 if(err) {;
542 return err;
543 }
544 }
545 }
546
547 return NO_ERROR;
548 }
549
550 template <typename T, unsigned int R, unsigned int C>
551 void Matrix<T, R, C>::transpose(Matrix<T, C, R> &result) const {
552 /// NOTE: No need to enforce size constraints on matrix here because
553 /// they will be enforced by the compiler. Code will not compile
554 /// if matrix of the wrong size is fed to this function
555
556 unsigned int i, j;
557 /// Loop through matrix and write out values in transposed form
558 for(i = 0; i < R; i++) {
559 for(j = 0; j < C; j++) {
560 result.values[j][i] = values[i][j];
561 }
562 }
563 }
564 template <typename T, unsigned int R, unsigned int C>
565 Matrix<T, C, R> Matrix<T, R, C>::transpose() const {
566 /// NOTE: No need to enforce size constraints on matrix here because
567 /// they will be enforced by the compiler. Code will not compile
568 /// if matrix of the wrong size is fed to this function
569 Matrix<T, C, R> tmp;
570 transpose(tmp);
571 return tmp;
572 }
573
574 template <typename T, unsigned int R, unsigned int C>
575 int Matrix<T, R, C>::trace(T &result) const {
576 /// Ensure we have a square matrix -- error if not
577 /// TODO: See if this should be checked at compile time for templating speed
578 if(R != C) {
579 return ERROR_DIMENSIONS;
580 }
581
582 result = T();
583 unsigned int i;
584 /// Now calculate and return trace
585 for(i = 0; i < R; i++) {
586 result += values[i][i];
587 }
588 return NO_ERROR;
589 }
590
591 template <typename T, unsigned int R, unsigned int C>
592 void Matrix<T, R, C>::setToZeros() {
593 unsigned int i, j;
594 for(i = 0; i < R; i++) {
595 for(j = 0; j < C; j++) {
596 values[j][i] = 0;
597 }
598 }
599 }
600
601 template <typename T, unsigned int R, unsigned int C>
602 int Matrix<T, R, C>::identity() {
603 if(C != R) {return ERROR_DIMENSIONS;}
604 unsigned int i, j;
605 for(i = 0; i < R; i++) {
606 for(j = 0; j < C; j++) {
607 if(i == j) {
608 values[i][i] = 1.0;
609 } else {
610 values[i][j] = 0.0;
611 }
612 }
613 }
614
615 return NO_ERROR;
616 }
617
618 template <typename T, unsigned int R, unsigned int C>
619 int Matrix<T, R, C>::_checkLookupBoundaries(const unsigned int &start_r, const unsigned int &end_r,
620 const unsigned int &start_c, const unsigned int &end_c) const{
621 /// Check that start is not greater than end
622 if(start_r > end_r || start_c > end_c) {
623 return ERROR_DIMENSIONS;
624 }
625 /// Check that end does not exceed matrix size. Note we do not need to check start
626 /// because it is an unsigned int where we're checking start !> end
627 if(end_r >= R || end_c >= C) {
628 return ERROR_DIMENSIONS;
629 }
630 /// We passed our checks - return OK
631 return NO_ERROR;
632 }
633
634 template<typename T, unsigned int R, unsigned int C>
635 int Matrix<T, R, C>::_LUPDecompose(T *A[R], unsigned int P[R+1]) const {
636 /// Check to confirm that our matrix is square. If it is not, return with error.
637 /// Note: afterwards we know R and C are equivalent and interchangeable. We'll use
638 /// just R moving forward
639 /// TODO: Figure out if this should be done via macro at compile time due to templating
640 if(R != C) {
641 return ERROR_DIMENSIONS;
642 }
643
644 /// Initialize our iterators, etc. for the function
645 unsigned int i, j, k, imax;
646 T max_A, *ptr, abs_A;
647
648 /// Initialize our unit permutation matrix. P[N] is initialized with N
649 for (k = 0; k <= R; k++) {
650 P[k] = k;
651 }
652
653 /// Loop through matrix columnwise
654 for (i = 0; i < R; i++) {
655 /// Reset max_A and imax to default to start loop. Explicitly invoke
656 /// default constructor on max_A for consistency
657 max_A = T();
658 imax = i;
659
660 /// Loop through elements of column N to identify row with maximum value. This
661 /// will be identified by imax and used in a later pivot operation. The pivot is
662 /// not necessary to take the determinant, but provides better numerical stability
663 for (k = i; k < R; k++) {
664 abs_A = std::abs(A[k][i]); /// Get abs value of individual element
665 /// If absolute value is greater than previous max, set max and index
666 if (abs_A > max_A) {
667 max_A = abs_A;
668 imax = k;
669 }
670 }
671
672 /// If it was not possible to find a value greater than zero (within tolerance)
673 /// in the current column, we know our matrix is rank deficient and should return
674 /// as such. This ensures a zero determinant as well.
675 if (max_A < TOLERANCE_CONDITIONING)
676 return ERROR_POORLY_CONDITIONED; //failure, matrix is degenerate
677
678 /// If the first value of the current row is not the highest value, we will pivot
679 /// for numerical stability in the operation. Swap pointers to current and pivot
680 /// row and record change in Permutation matrix
681 if (imax != i) {
682 /// Count pivots that occurred. This tracking is for determinant purposes --
683 /// Each pivot flips the sign of the determinant and must be tracked
684 P[R]++;
685
686 /// Track pivot in P "matrix"
687 j = P[i];
688 P[i] = P[imax];
689 P[imax] = j;
690
691 /// Pivot the rows of A by rearranging pointers for efficiency
692 ptr = A[i];
693 A[i] = A[imax];
694 A[imax] = ptr;
695 }
696
697 /// Now loop through our matrix and perform the actual gaussian elimination
698 for (j = i+1; j < R; j++) {
699 safeDivide(A[j][i], A[i][i], A[j][i]);
700 for (k = i+1; k < R; k++) {
701 A[j][k] -= A[j][i] * A[i][k];
702 }
703 }
704 }
705
706 /// We reached the end and did not hit an error -- return good job
707 return NO_ERROR;
708 }
709
710}
711
712#endif
Matrix math implementation.
Definition Matrix.hpp:54
void setToZeros()
Function to set all elements of the matrix to zero.
Definition Matrix.hpp:592
std::array< std::array< T, C >, R > values
The actual values held by the matrix – a two dimensional array of values indexed as (row,...
Definition Matrix.hpp:188
void getAsArray(T *start_ptr) const
Function to get the values of the matrix row-wise.
Definition Matrix.hpp:357
int inverse(Matrix< T, R, C > &result) const
Function to return the inverse of the matrix.
Definition Matrix.hpp:488
T * operator[](unsigned int idx)
Function to return a matrix row or vector value.
Definition Matrix.hpp:268
int set(const unsigned int &row, const unsigned int &col, const T &value)
Function to set a single value in the matrix.
Definition Matrix.hpp:314
std::string str() const
Function to dump information on matrix to string.
Definition Matrix.hpp:294
void setFromArray(const T *start_ptr)
Function to set the values of the matrix row-wise.
Definition Matrix.hpp:345
std::pair< unsigned int, unsigned int > size()
Function to get the size of the matrix.
Definition Matrix.hpp:138
T get(const unsigned int &row, const unsigned int &col) const
Function to get a single value in the matrix.
Definition Matrix.hpp:340
int trace(T &result) const
Function to return the trace of the matrix.
Definition Matrix.hpp:575
int identity()
Function to set matrix to identity, if it is a square matrix.
Definition Matrix.hpp:602
Matrix< T, R, C > & operator=(const Matrix< T, R, C > &other)
Equals operator overload for matrix.
Definition Matrix.hpp:383
int _LUPDecompose(T *A[R], unsigned int P[R+1]) const
Function to take a 2-d matrix represented by A and decompose it into LU form.
Definition Matrix.hpp:635
int det(T &result) const
Function to return the determinant of the matrix.
Definition Matrix.hpp:436
void transpose(Matrix< T, C, R > &result) const
Function to return the transpose of the matrix.
Definition Matrix.hpp:551
int get(const unsigned int &row, const unsigned int &col, T &result) const
Function to get a single value in the matrix.
Definition Matrix.hpp:327
void max(T &result, std::pair< unsigned int, unsigned int > &index) const
Function to return the maximum value in the matrix.
Definition Matrix.hpp:400
void getCopy(Matrix< T, R, C > &result) const
Function to get a copy of the matrix.
Definition Matrix.hpp:369
void min(T &result, std::pair< unsigned int, unsigned int > &index) const
Function to return the minimum value in the matrix.
Definition Matrix.hpp:418
void dump() const
Function to dump information on matrix.
Definition Matrix.hpp:278
int _checkLookupBoundaries(const unsigned int &start_r, const unsigned int &end_r, const unsigned int &start_c, const unsigned int &end_c) const
Function to check, given a set of submatrix boundaries, that those boundaries are valid for the curre...
Definition Matrix.hpp:619
#define NO_ERROR
Definition clockwerkerrors.h:31
#define TOLERANCE_CONDITIONING
Definition clockwerkerrors.h:43
#define ERROR_POORLY_CONDITIONED
Definition clockwerkerrors.h:42
#define ERROR_DIMENSIONS
Definition clockwerkerrors.h:38