├── LICENSE ├── Test ├── matBasic_testUtil.hpp ├── matBasic_example.cpp ├── matBasic_real_test.cpp └── matBasic_complex_test.cpp ├── README.md ├── matBasic_real.hpp └── matBasic_complex.hpp /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 FerryYoungFan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Test/matBasic_testUtil.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include // Performance test timer 5 | 6 | 7 | class TestTimer 8 | { 9 | public: 10 | TestTimer() = default; 11 | ~TestTimer() = default; 12 | 13 | void tic() 14 | { 15 | m_running = true; 16 | gettimeofday(&m_time_start, NULL); 17 | } 18 | 19 | double toc(const char *printInfo = nullptr) 20 | { 21 | double res = 0.0; 22 | if (m_running) 23 | { 24 | gettimeofday(&m_time_end, NULL); 25 | m_running = false; 26 | res = static_cast(m_time_end.tv_sec - m_time_start.tv_sec) + static_cast(m_time_end.tv_usec - m_time_start.tv_usec) / 1000000.0; 27 | if (nullptr != printInfo) 28 | { 29 | std::cout << "Time elapsed - " << printInfo << ": "; 30 | if (res > 1.0) 31 | { 32 | std::cout << res << " s\n"; 33 | } 34 | else if (res > 0.001) 35 | { 36 | std::cout << res * 1000.0 << " ms\n"; 37 | } 38 | else 39 | { 40 | std::cout << res * 1000000.0 << " us\n"; 41 | } 42 | } 43 | } 44 | return res; 45 | } 46 | 47 | private: 48 | struct timeval m_time_start 49 | { 50 | 0, 0 51 | }; 52 | 53 | struct timeval m_time_end 54 | { 55 | 0, 0 56 | }; 57 | 58 | bool m_running{false}; 59 | }; 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NaiveMatrixLib 帆帆的简易矩阵计算库 2 | A simple C++ stdlib-based complex & real matrix library, with matrix inversion, left division (A\b) and determinant calculation.
3 | 这是一个使用 C++ 标准库实现的简易复数及实数矩阵库,包含求逆矩阵、反斜杠除法 (A\b) 及行列式计算。 4 | 5 | ## Features 特点 6 | * Designed for users who don't want to use large linear algebra libs. 7 | * Only used C++ standard library, easy to learn and modify (Each file less than 600 lines). 8 | * Header files only, separated complex and real matrix library. 9 | * No recursive algorithm (using LU and Cholesky decomposition). Reliable for 1000 x 1000 and larger matrices. 10 | - 如果你不想使用大型线性代数库来计算这些,那你来对地方了。(什么,你只是想交作业?) 11 | - 仅使用C++标准库,无论是学习思维还是修改都很简单(每份代码都少于600行)。 12 | - 仅使用头文件即可,复数和实数矩阵库是分开的。 13 | - 没有递归运算(基于 LU 和 Cholesky 分解)。可对1000x1000及更大的矩阵使用。 14 | 15 | 16 | ## Available Functions 可用函数 17 | * rank: Matrix rank (Cholesky decomposition) 18 | * det: Matrix determinant calculation 19 | * inv: LU decomposition-based matrix inversion 20 | * pinv: pinv(G) = inv(G' * G) * G' (WARNING: full-rank matrix only!) 21 | * pinv2: Moore-Penrose pseudoinversion (same as pinv(G) in MATLAB) 22 | * leftDiv: x = A \ b, using Moore-Penrose pinv, NOT same as MATLAB for a singular matrix 23 | - rank: 计算矩阵秩 (Cholesky 分解) 24 | - det: 计算矩阵行列式 25 | - inv: 求逆矩阵,基于 LU 分解 26 | - pinv: 经典伪逆,pinv(G) = inv(G' * G) * G' (警告:只能用于满秩矩阵!) 27 | - pinv2: Moore-Penrose 伪逆 (与 MATLAB 中的 pinv(G) 相同) 28 | - leftDiv: 反斜杠除法 x = A \ b, 使用 Moore-Penrose 伪逆, 对奇异矩阵的处理与 MATLAB 不同 29 | 30 | 31 | ## Examples (Complex Matrices Only) 用例(仅列举复数矩阵) 32 | ```cpp 33 | #include "matBasic_complex.hpp" 34 | int main(int argc, char **argv) 35 | { 36 | i_complex_matrix matA = { 37 | {{1.0, 0.0}, {0.0, 1.0}, {4.0, 0.0}}, 38 | {{0.0, 5.0}, {1.0, 0.0}, {4.0, 0.0}}, 39 | {{8.0, 0.0}, {1.0, 1.0}, {0.0, 0.0}}}; 40 | showMatrix(matA, "A"); 41 | std::cout << "rank(A) = " << rank(matA) << "\n"; 42 | std::cout << "det(A) = " << det(matA) << "\n"; 43 | showMatrix(inv(matA), "inv(A)"); 44 | showMatrix(pinv(matA), "pinv(A)"); 45 | showMatrix(pinv2(matA), "pinv2(A)"); 46 | 47 | i_complex_matrix matb = { 48 | {{1.0, 0.0}}, 49 | {{1.0, 2.0}}, 50 | {{0.0, 3.0}}}; 51 | showMatrix(matb, "b"); 52 | showMatrix(leftDiv(matA, matb), "A \\ b"); 53 | showMatrix(matMul(inv(matA), matb), "inv(A) * b"); 54 | showMatrix(matMul(pinv(matA), matb), "pinv(A) * b"); 55 | showMatrix(matMul(pinv2(matA), matb), "pinv2(A) * b (== A \\ b)"); 56 | std::cin.get(); 57 | return 0; 58 | } 59 | ``` 60 | ### Output 输出 61 | ``` 62 | A : 3 x 3 Complex Matrix: 63 | row[1]: 1, 0+1i, 4; 64 | row[2]: 0+5i, 1, 4; 65 | row[3]: 8, 1+1i, 0; 66 | 67 | rank(A) = 3 68 | det(A) = (-56,48) 69 | inv(A) : 3 x 3 Complex Matrix: 70 | row[1]: 0.00588235+0.0764706i, -0.00588235-0.0764706i, 0.0764706-0.00588235i; 71 | row[2]: -0.329412-0.282353i, 0.329412+0.282353i, 0.217647-0.170588i; 72 | row[3]: 0.177941+0.0632353i, 0.0720588-0.0632353i, -0.0617647-0.0529412i; 73 | 74 | pinv(A) : 3 x 3 Complex Matrix: 75 | row[1]: 0.00588235+0.0764706i, -0.00588235-0.0764706i, 0.0764706-0.00588235i; 76 | row[2]: -0.329412-0.282353i, 0.329412+0.282353i, 0.217647-0.170588i; 77 | row[3]: 0.177941+0.0632353i, 0.0720588-0.0632353i, -0.0617647-0.0529412i; 78 | 79 | pinv2(A) : 3 x 3 Complex Matrix: 80 | row[1]: 0.00588235+0.0764706i, -0.00588235-0.0764706i, 0.0764706-0.00588235i; 81 | row[2]: -0.329412-0.282353i, 0.329412+0.282353i, 0.217647-0.170588i; 82 | row[3]: 0.177941+0.0632353i, 0.0720588-0.0632353i, -0.0617647-0.0529412i; 83 | 84 | 85 | 86 | b : 3 x 1 Complex Matrix: 87 | row[1]: 1; 88 | row[2]: 1+2i; 89 | row[3]: 0+3i; 90 | 91 | A \ b : 3 x 1 Complex Matrix: 92 | row[1]: 0.170588+0.217647i; 93 | row[2]: -0.0529412+1.31176i; 94 | row[3]: 0.535294-0.0411765i; 95 | 96 | inv(A) * b : 3 x 1 Complex Matrix: 97 | row[1]: 0.170588+0.217647i; 98 | row[2]: -0.0529412+1.31176i; 99 | row[3]: 0.535294-0.0411765i; 100 | 101 | pinv(A) * b : 3 x 1 Complex Matrix: 102 | row[1]: 0.170588+0.217647i; 103 | row[2]: -0.0529412+1.31176i; 104 | row[3]: 0.535294-0.0411765i; 105 | 106 | pinv2(A) * b (== A \ b) : 3 x 1 Complex Matrix: 107 | row[1]: 0.170588+0.217647i; 108 | row[2]: -0.0529412+1.31176i; 109 | row[3]: 0.535294-0.0411765i; 110 | ``` 111 | 112 | ## Reference 参考资料 113 | * Pierre Courrieu, Fast Computation of Moore-Penrose Inverse Matrices, https://arxiv.org/abs/0804.4809 114 | * Permute Sign Calculation, page5 https://www.math.rutgers.edu/docman-lister/math-main/academics/course-materials/250/assignments/1493-250c-lab3-sakai-pdf/file 115 | * LU Decomposition C++ Implementation, https://blog.csdn.net/xx_123_1_rj/article/details/39553809 116 | * LU Decomposition, https://www.math.ucdavis.edu/~linear/old/notes11.pdf 117 |


118 |

*** Project by Fanseline in Ericsson ***

119 | -------------------------------------------------------------------------------- /Test/matBasic_example.cpp: -------------------------------------------------------------------------------- 1 | #include "matBasic_real.hpp" // Real matrices 2 | #include "matBasic_complex.hpp" // Complex matrices 3 | 4 | void realMatrixExamples() 5 | { 6 | std::cout << "\n\n******************** Real Matrix Examples ********************\n\n"; 7 | i_real_matrix matA = { 8 | {1.0, 1.0, 4.0}, 9 | {4.0, 1.0, 5.0}, 10 | {8.0, 1.0, 0.0}}; 11 | showMatrix(matA, "A"); 12 | std::cout << "rank(A) = " << rank(matA) << "\n"; 13 | std::cout << "det(A) = " << det(matA) << "\n"; 14 | showMatrix(inv(matA), "inv(A)"); 15 | showMatrix(pinv(matA), "pinv(A)"); 16 | showMatrix(pinv2(matA), "pinv2(A)"); 17 | std::cout << "\n\n"; 18 | 19 | i_real_matrix matb = { 20 | {1.0}, 21 | {2.0}, 22 | {3.0}}; 23 | showMatrix(matb, "b"); 24 | showMatrix(leftDiv(matA, matb), "A \\ b"); 25 | showMatrix(matMul(inv(matA), matb), "inv(A) * b"); 26 | showMatrix(matMul(pinv(matA), matb), "pinv(A) * b"); 27 | showMatrix(matMul(pinv2(matA), matb), "pinv2(A) * b (== A \\ b)"); 28 | std::cout << "\n\n"; 29 | 30 | i_real_matrix matB = { 31 | {1.0, 1.0, 4.0}, 32 | {4.0, 1.0, 5.0}, 33 | {0.0, 0.0, 0.0}}; 34 | showMatrix(matB, "B"); 35 | std::cout << "rank(B) = " << rank(matB) << "\n"; 36 | std::cout << "det(B) = " << det(matB) << "\n"; 37 | // showMatrix(inv(matB), "inv(B)"); // Error: singular matrix 38 | // showMatrix(pinv(matB), "pinv(B)"); // Error: not a full-rank matrix 39 | showMatrix(pinv2(matB), "pinv2(B)"); 40 | showMatrix(leftDiv(matB, matb), "B \\ b"); // Warning: singular matrix 41 | showMatrix(matMul(pinv2(matB), matb), "pinv2(B) * b (== B \\ b)"); // Warning: singular matrix 42 | std::cout << "\n\n"; 43 | 44 | i_real_matrix matC = { 45 | {1.0, 1.0}, 46 | {4.0, 5.0}, 47 | {1.0, 4.0}}; 48 | showMatrix(matC, "C"); 49 | std::cout << "rank(C) = " << rank(matC) << "\n"; 50 | // std::cout << "det(C) = " << det(matC) << "\n"; // Error: not a square matrix 51 | // showMatrix(inv(matC), "inv(C)"); // Error: not a square matrix 52 | showMatrix(pinv(matC), "pinv(C)"); 53 | showMatrix(pinv2(matC), "pinv2(C)"); 54 | showMatrix(leftDiv(matC, matb), "C \\ b"); 55 | showMatrix(matMul(pinv(matC), matb), "pinv(C) * b"); 56 | showMatrix(matMul(pinv2(matC), matb), "pinv2(C) * b (== C \\ b)"); 57 | std::cout << "\n\n"; 58 | } 59 | 60 | void complexMatrixExamples() 61 | { 62 | std::cout << "\n\n******************** Complex Matrix Examples ********************\n\n"; 63 | i_complex_matrix matA = { 64 | {{1.0, 0.0}, {0.0, 1.0}, {4.0, 0.0}}, 65 | {{0.0, 5.0}, {1.0, 0.0}, {4.0, 0.0}}, 66 | {{8.0, 0.0}, {1.0, 1.0}, {0.0, 0.0}}}; 67 | showMatrix(matA, "A"); 68 | std::cout << "rank(A) = " << rank(matA) << "\n"; 69 | std::cout << "det(A) = " << det(matA) << "\n"; 70 | showMatrix(inv(matA), "inv(A)"); 71 | showMatrix(pinv(matA), "pinv(A)"); 72 | showMatrix(pinv2(matA), "pinv2(A)"); 73 | std::cout << "\n\n"; 74 | 75 | i_complex_matrix matb = { 76 | {{1.0, 0.0}}, 77 | {{1.0, 2.0}}, 78 | {{0.0, 3.0}}}; 79 | showMatrix(matb, "b"); 80 | showMatrix(leftDiv(matA, matb), "A \\ b"); 81 | showMatrix(matMul(inv(matA), matb), "inv(A) * b"); 82 | showMatrix(matMul(pinv(matA), matb), "pinv(A) * b"); 83 | showMatrix(matMul(pinv2(matA), matb), "pinv2(A) * b (== A \\ b)"); 84 | std::cout << "\n\n"; 85 | 86 | i_complex_matrix matB = { 87 | {{1.0, 0.0}, {0.0, 1.0}, {4.0, 0.0}}, 88 | {{0.0, 5.0}, {1.0, 0.0}, {4.0, 0.0}}, 89 | {{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}}; 90 | showMatrix(matB, "B"); 91 | std::cout << "rank(B) = " << rank(matB) << "\n"; 92 | std::cout << "det(B) = " << det(matB) << "\n"; 93 | // showMatrix(inv(matB), "inv(B)"); // Error: singular matrix 94 | // showMatrix(pinv(matB), "pinv(B)"); // Error: not a full-rank matrix 95 | showMatrix(pinv2(matB), "pinv2(B)"); 96 | showMatrix(leftDiv(matB, matb), "B \\ b"); // Warning: singular matrix 97 | showMatrix(matMul(pinv2(matB), matb), "pinv2(B) * b (== B \\ b)"); // Warning: singular matrix 98 | std::cout << "\n\n"; 99 | 100 | i_complex_matrix matC = { 101 | {{1.0, 0.0}, {0.0, 1.0}}, 102 | {{0.0, 5.0}, {1.0, 0.0}}, 103 | {{8.0, 0.0}, {1.0, 1.0}}}; 104 | showMatrix(matC, "C"); 105 | std::cout << "rank(C) = " << rank(matC) << "\n"; 106 | // std::cout << "det(C) = " << det(matC) << "\n"; // Error: not a square matrix 107 | // showMatrix(inv(matC), "inv(C)"); // Error: not a square matrix 108 | showMatrix(pinv(matC), "pinv(C)"); 109 | showMatrix(pinv2(matC), "pinv2(C)"); 110 | showMatrix(leftDiv(matC, matb), "C \\ b"); 111 | showMatrix(matMul(pinv(matC), matb), "pinv(C) * b"); 112 | showMatrix(matMul(pinv2(matC), matb), "pinv2(C) * b (== C \\ b)"); 113 | std::cout << "\n\n"; 114 | } 115 | 116 | int main(int argc, char **argv) 117 | { 118 | realMatrixExamples(); 119 | complexMatrixExamples(); 120 | std::cin.get(); 121 | return 0; 122 | } 123 | -------------------------------------------------------------------------------- /Test/matBasic_real_test.cpp: -------------------------------------------------------------------------------- 1 | #include "matBasic_real.hpp" 2 | #include "matBasic_testUtil.hpp" 3 | 4 | i_real_matrix genTestMatrixA(const std::size_t nAnt) 5 | { 6 | std::size_t nrows = (nAnt - 1) * nAnt + 1; 7 | i_real_matrix resMat{initRealMatrix(nrows, nAnt)}; 8 | for (std::size_t row{0}; row < nAnt; ++row) 9 | { 10 | for (std::size_t col{0}; col < nAnt; ++col) 11 | { 12 | if (col >= row) 13 | { 14 | continue; 15 | } 16 | i_float_t lambda{static_cast((row + 1) * 100 + col + 1)}; 17 | std::size_t row_this = col * (nAnt - 1) + row - (row > col); 18 | std::size_t row_that = row * (nAnt - 1) + col; 19 | resMat[row_this][row] = lambda; 20 | resMat[row_this][col] = -1.0; 21 | resMat[row_that][col] = lambda; 22 | resMat[row_that][row] = -1.0; 23 | } 24 | } 25 | resMat[nrows - 1][0] = 1.0; 26 | return resMat; 27 | } 28 | 29 | i_real_matrix genTestMatrixA(const std::size_t nAnt, const std::size_t nEq) 30 | { 31 | i_real_matrix resMat{initRealMatrix(nEq + 1, nAnt)}; 32 | 33 | if (nAnt > nEq) 34 | { 35 | std::cout << "genTestMatrixA Error: nEq should >= nAnt\n"; 36 | return resMat; 37 | } 38 | 39 | std::size_t maxRow = (nAnt - 1) * nAnt; 40 | if (nEq > maxRow) 41 | { 42 | std::cout << "genTestMatrixA Error: nEq should <= " << maxRow << "\n"; 43 | return resMat; 44 | } 45 | 46 | std::size_t antCount{0}; 47 | std::size_t col{0}; 48 | std::size_t roundCount{0}; 49 | for (std::size_t row{0}; row < nEq; ++row) 50 | { 51 | resMat[row][antCount] = -1.0; 52 | col = (antCount + roundCount + (antCount >= roundCount)) % nAnt; 53 | i_float_t lambda{static_cast((row + 1) * 100 + col + 1)}; 54 | resMat[row][col] = lambda; 55 | ++antCount; 56 | if (antCount == nAnt) 57 | { 58 | antCount = 0; 59 | ++roundCount; 60 | } 61 | } 62 | resMat[nEq][0] = 1.0; 63 | return resMat; 64 | } 65 | 66 | i_real_matrix genTestMatrixb(const std::size_t nAnt) 67 | { 68 | std::size_t nrows = (nAnt - 1) * nAnt + 1; 69 | i_real_matrix resMat{initRealMatrix(nrows, 1)}; 70 | resMat[nrows - 1][0] = 1.0; 71 | return resMat; 72 | } 73 | 74 | i_real_matrix genTestMatrixb(const std::size_t nAnt, const std::size_t nEq) 75 | { 76 | std::size_t nrows = nEq + 1; 77 | i_real_matrix resMat{initRealMatrix(nrows, 1)}; 78 | resMat[nEq][0] = 1.0; 79 | return resMat; 80 | } 81 | 82 | void pinvTest(bool doLargeMatTest = true) 83 | { 84 | std::cout << "\n\n******************** pinv test ********************\n\n"; 85 | 86 | i_real_matrix matA = { 87 | {0.0}}; 88 | std::cout << "rank(matA) = " << rank(matA) << "\n"; 89 | showMatrix(matA, "matA"); 90 | showMatrix(pinv2(matA), "pinv2(matA)"); 91 | std::cout << "\n\n"; 92 | 93 | i_real_matrix matB = { 94 | {5.0}}; 95 | std::cout << "rank(matB) = " << rank(matB) << "\n"; 96 | showMatrix(matB, "matB"); 97 | showMatrix(inv(matB), "inv(matB)"); 98 | showMatrix(pinv(matB), "pinv(matB)"); 99 | showMatrix(pinv2(matB), "pinv2(matB)"); 100 | std::cout << "\n\n"; 101 | 102 | i_real_matrix matC = { 103 | {1.0, 2.0, 3.0, 5.0}, 104 | {2.0, 5.0, 3.0, 6.0}, 105 | {0.0, 4.0, 1.0, 5.0}, 106 | {-6.0, 3.0, 9.0, 1.0}}; 107 | std::cout << "rank(matC) = " << rank(matC) << "\n"; 108 | showMatrix(matC, "matC"); 109 | showMatrix(inv(matC), "inv(matC)"); 110 | showMatrix(pinv(matC), "pinv(matC)"); 111 | showMatrix(pinv2(matC), "pinv2(matC)"); 112 | std::cout << "\n\n"; 113 | 114 | i_real_matrix matD = { 115 | {1.0, 2.0, 3.0, 5.0}, 116 | {2.0, 5.0, 3.0, 6.0}, 117 | {0.0, 4.0, 1.0, 5.0}, 118 | {-6.0, 3.0, 9.0, 1.0}, 119 | {1.0, 1.0, 4.0, 5.0}}; 120 | std::cout << "rank(matD) = " << rank(matD) << "\n"; 121 | showMatrix(matD, "matD"); 122 | showMatrix(pinv(matD), "pinv(matD)"); 123 | showMatrix(pinv2(matD), "pinv2(matD)"); 124 | std::cout << "\n\n"; 125 | 126 | i_real_matrix matE = { 127 | {1.0, 2.0, 3.0, 5.0}, 128 | {2.0, 5.0, 3.0, 6.0}, 129 | {0.0, 4.0, 1.0, 5.0}, 130 | {0.0, 4.0, 1.0, 5.0}}; 131 | std::cout << "rank(matE) = " << rank(matE) << "\n"; 132 | showMatrix(matE, "matE (singular)"); 133 | showMatrix(pinv2(matE), "pinv2(matE)"); 134 | std::cout << "\n\n"; 135 | 136 | i_real_matrix matF = { 137 | {1.0, 2.0, 3.0, 0.0}, 138 | {2.0, 5.0, 3.0, 0.0}, 139 | {0.0, 4.0, 1.0, 0.0}, 140 | {-6.0, 3.0, 9.0, 0.0}, 141 | {1.0, 1.0, 4.0, 0.0}}; 142 | std::cout << "rank(matF) = " << rank(matF) << "\n"; 143 | showMatrix(matF, "matF (singular)"); 144 | showMatrix(pinv2(matF), "pinv2(matF)"); 145 | std::cout << "\n\n"; 146 | 147 | i_real_matrix matG = { 148 | {0.0, 2.0, 3.0, 5.0}, 149 | {2.0, 0.0, 3.0, 6.0}, 150 | {0.0, 4.0, 0.0, 5.0}, 151 | {-6.0, 3.0, 9.0, 0.0}}; 152 | std::cout << "rank(matG) = " << rank(matG) << "\n"; 153 | showMatrix(matG, "matG (diag zero)"); 154 | showMatrix(inv(matG), "inv(matG)"); 155 | showMatrix(pinv(matG), "pinv(matG)"); 156 | showMatrix(pinv2(matG), "pinv2(matG)"); 157 | std::cout << "\n\n"; 158 | 159 | if (!doLargeMatTest) 160 | { 161 | return; 162 | } 163 | 164 | std::size_t nAnt = 64; 165 | std::size_t nEq = 960; 166 | // i_real_matrix largeA = genTestMatrixA(nAnt, nEq); 167 | // i_real_matrix largeb = genTestMatrixb(nAnt, nEq); 168 | i_real_matrix largeA = genTestMatrixA(nAnt); 169 | i_real_matrix largeb = genTestMatrixb(nAnt); 170 | i_real_matrix largeA_pinv; 171 | i_real_matrix largex; 172 | 173 | TestTimer timer; 174 | timer.tic(); 175 | largeA_pinv = pinv(largeA); 176 | largex = matMul(largeA_pinv, largeb); 177 | showMatrix(largex, "mat x"); 178 | timer.toc("pinv1 method"); 179 | 180 | timer.tic(); 181 | largex = leftDiv(largeA, largeb); 182 | showMatrix(largex, "mat x"); 183 | timer.toc("pinv2 method"); 184 | } 185 | 186 | void determinantTest() 187 | { 188 | std::cout << "\n\n******************** determinant test ********************\n\n"; 189 | i_real_matrix matA = { 190 | {5.0}}; 191 | showMatrix(matA, "matA", true); 192 | std::cout << "det(matA) = " << det(matA) << "\n\n\n"; 193 | 194 | i_real_matrix matB = { 195 | {0.0, 1.0}, 196 | {4.0, 0.0}}; 197 | showMatrix(matB, "matB", true); 198 | std::cout << "det(matB) = " << det(matB) << "\n\n\n"; 199 | 200 | i_real_matrix matC = { 201 | {0.0, 1.0, 1.0}, 202 | {4.0, 0.0, 1.0}, 203 | {8.0, 1.0, 0.0}}; 204 | showMatrix(matC, "matC", true); 205 | std::cout << "det(matC) = " << det(matC) << "\n\n\n"; 206 | 207 | i_real_matrix matD = { 208 | {0.0, 1.0, 1.0, 4.0}, 209 | {4.0, 0.0, 1.0, 9.0}, 210 | {8.0, 1.0, 0.0, 0.0}, 211 | {1.0, 1.0, 1.0, 0.0}}; 212 | showMatrix(matD, "matD", true); 213 | std::cout << "det(matD) = " << det(matD) << "\n\n\n"; 214 | 215 | i_real_matrix matE = { 216 | {0.0, 1.0, 1.0, 4.0, 5.0}, 217 | {4.0, 0.0, 1.0, 9.0, 1.0}, 218 | {8.0, 1.0, 0.0, 0.0, 9.0}, 219 | {1.0, 1.0, 1.0, 0.0, 4.0}, 220 | {1.0, 4.0, 8.0, 1.0, 0.0}}; 221 | showMatrix(matE, "matE", true); 222 | std::cout << "det(matE) = " << det(matE) << "\n\n\n"; 223 | 224 | i_real_matrix matF = { 225 | {0.0, 1.0, 1.0, 4.0, 5.0, 1.0}, 226 | {4.0, 0.0, 1.0, 9.0, 1.0, 9.0}, 227 | {8.0, 1.0, 0.0, 0.0, 9.0, 3.0}, 228 | {1.0, 1.0, 1.0, 0.0, 4.0, 5.0}, 229 | {1.0, 4.0, 8.0, 1.0, 0.0, 0.0}, 230 | {9.0, 3.0, 1.0, 1.0, 4.0, 0.0}}; 231 | showMatrix(matF, "matF", true); 232 | std::cout << "det(matF) = " << det(matF) << "\n\n\n"; 233 | 234 | // std::size_t nAnt = 64; 235 | // std::size_t nEq = 960; 236 | // i_real_matrix largeA = genTestMatrixA(nAnt, nEq); 237 | // i_real_matrix largeG = matMul(transpose(largeA), largeA); 238 | // TestTimer timer; 239 | // timer.tic(); 240 | // std::cout << "det(largeG) = " << det(largeG) << "\n"; 241 | // timer.toc("large matrix determinant"); 242 | } 243 | 244 | int main(int argc, char **argv) 245 | { 246 | pinvTest(true); 247 | determinantTest(); 248 | std::cin.get(); 249 | return 0; 250 | } 251 | -------------------------------------------------------------------------------- /Test/matBasic_complex_test.cpp: -------------------------------------------------------------------------------- 1 | #include "matBasic_complex.hpp" 2 | #include "matBasic_testUtil.hpp" 3 | 4 | i_complex_matrix genTestMatrixA(const std::size_t nAnt) 5 | { 6 | std::size_t nrows = (nAnt - 1) * nAnt + 1; 7 | i_complex_matrix resMat{initComplexMatrix(nrows, nAnt)}; 8 | for (std::size_t row{0}; row < nAnt; ++row) 9 | { 10 | for (std::size_t col{0}; col < nAnt; ++col) 11 | { 12 | if (col >= row) 13 | { 14 | continue; 15 | } 16 | // i_complex_t lambda{static_cast((row + 1) * 10 + col + 1)}; 17 | i_complex_t lambda{static_cast(row + 1), static_cast(col + 1)}; 18 | std::size_t row_this = col * (nAnt - 1) + row - (row > col); 19 | std::size_t row_that = row * (nAnt - 1) + col; 20 | resMat[row_this][row] = lambda; 21 | resMat[row_this][col] = -1.0; 22 | resMat[row_that][col] = lambda; 23 | resMat[row_that][row] = -1.0; 24 | } 25 | } 26 | resMat[nrows - 1][0] = 1.0; 27 | return resMat; 28 | } 29 | 30 | i_complex_matrix genTestMatrixA(const std::size_t nAnt, const std::size_t nEq) 31 | { 32 | i_complex_matrix resMat{initComplexMatrix(nEq + 1, nAnt)}; 33 | 34 | if (nAnt > nEq) 35 | { 36 | std::cout << "genTestMatrixA Error: nEq should >= nAnt\n"; 37 | return resMat; 38 | } 39 | 40 | std::size_t maxRow = (nAnt - 1) * nAnt; 41 | if (nEq > maxRow) 42 | { 43 | std::cout << "genTestMatrixA Error: nEq should <= " << maxRow << "\n"; 44 | return resMat; 45 | } 46 | 47 | std::size_t antCount{0}; 48 | std::size_t col{0}; 49 | std::size_t roundCount{0}; 50 | for (std::size_t row{0}; row < nEq; ++row) 51 | { 52 | resMat[row][antCount] = -1.0; 53 | col = (antCount + roundCount + (antCount >= roundCount)) % nAnt; 54 | // lambda{static_cast((col + 1)*10 + antCount + 1), 0.0}; 55 | i_complex_t lambda{static_cast(col + 1), static_cast(antCount + 1)}; 56 | resMat[row][col] = lambda; 57 | ++antCount; 58 | if (antCount == nAnt) 59 | { 60 | antCount = 0; 61 | ++roundCount; 62 | } 63 | } 64 | resMat[nEq][0] = 1.0; 65 | return resMat; 66 | } 67 | 68 | i_complex_matrix genTestMatrixb(const std::size_t nAnt) 69 | { 70 | std::size_t nrows = (nAnt - 1) * nAnt + 1; 71 | i_complex_matrix resMat{initComplexMatrix(nrows, 1)}; 72 | resMat[nrows - 1][0] = 1.0; 73 | return resMat; 74 | } 75 | 76 | i_complex_matrix genTestMatrixb(const std::size_t nAnt, const std::size_t nEq) 77 | { 78 | std::size_t nrows = nEq + 1; 79 | i_complex_matrix resMat{initComplexMatrix(nrows, 1)}; 80 | resMat[nEq][0] = 1.0; 81 | return resMat; 82 | } 83 | 84 | void pinvTest(bool doLargeMatTest = true) 85 | { 86 | std::cout << "\n\n******************** pinv test ********************\n\n"; 87 | 88 | i_complex_matrix matA = { 89 | {{0.0, 0.0}}}; 90 | std::cout << "rank(matA) = " << rank(matA) << "\n"; 91 | showMatrix(matA, "matA"); 92 | showMatrix(pinv2(matA), "pinv2(matA)"); 93 | std::cout << "\n\n"; 94 | 95 | i_complex_matrix matB = { 96 | {{5.0, 3.0}}}; 97 | std::cout << "rank(matB) = " << rank(matB) << "\n"; 98 | showMatrix(matB, "matB"); 99 | showMatrix(inv(matB), "inv(matB)"); 100 | showMatrix(pinv(matB), "pinv(matB)"); 101 | showMatrix(pinv2(matB), "pinv2(matB)"); 102 | std::cout << "\n\n"; 103 | 104 | i_complex_matrix matC = { 105 | {{1.0, 0.0}, {2.0, 0.0}, {3.0, 2.0}, {5.0, 0.0}}, 106 | {{2.0, 0.0}, {5.0, 0.0}, {3.0, 0.0}, {6.0, 0.0}}, 107 | {{0.0, 7.0}, {4.0, 0.0}, {3.0, 0.0}, {1.0, 0.0}}, 108 | {{-6.0, 0.0}, {3.0, 0.0}, {1.0, 0.0}, {9.0, 1.0}}}; 109 | std::cout << "rank(matC) = " << rank(matC) << "\n"; 110 | showMatrix(matC, "matC"); 111 | showMatrix(inv(matC), "inv(matC)"); 112 | showMatrix(pinv(matC), "pinv(matC)"); 113 | showMatrix(pinv2(matC), "pinv2(matC)"); 114 | std::cout << "\n\n"; 115 | 116 | i_complex_matrix matD = { 117 | {{1.0, 0.0}, {2.0, 0.0}, {3.0, 2.0}, {5.0, 0.0}}, 118 | {{2.0, 0.0}, {5.0, 0.0}, {3.0, 0.0}, {6.0, 0.0}}, 119 | {{0.0, 7.0}, {4.0, 0.0}, {3.0, 0.0}, {1.0, 0.0}}, 120 | {{-6.0, 0.0}, {3.0, 0.0}, {1.0, 0.0}, {9.0, 1.0}}, 121 | {{3.0, 0.0}, {2.0, 0.0}, {1.0, 5.0}, {0.0, 1.0}}}; 122 | std::cout << "rank(matD) = " << rank(matD) << "\n"; 123 | showMatrix(matD, "matD"); 124 | showMatrix(pinv(matD), "pinv(matD)"); 125 | showMatrix(pinv2(matD), "pinv2(matD)"); 126 | std::cout << "\n\n"; 127 | 128 | i_complex_matrix matE = { 129 | {{1.0, 0.0}, {2.0, 0.0}, {3.0, 2.0}, {5.0, 0.0}}, 130 | {{2.0, 0.0}, {5.0, 0.0}, {3.0, 0.0}, {6.0, 0.0}}, 131 | {{0.0, 7.0}, {4.0, 0.0}, {3.0, 0.0}, {1.0, 0.0}}, 132 | {{0.0, 7.0}, {4.0, 0.0}, {3.0, 0.0}, {1.0, 0.0}}}; 133 | std::cout << "rank(matE) = " << rank(matE) << "\n"; 134 | showMatrix(matE, "matE (singular)"); 135 | showMatrix(pinv2(matE), "pinv2(matE)"); 136 | std::cout << "\n\n"; 137 | 138 | i_complex_matrix matF = { 139 | {{1.0, 0.0}, {2.0, 0.0}, {3.0, 2.0}, {0.0, 0.0}}, 140 | {{2.0, 0.0}, {5.0, 0.0}, {3.0, 0.0}, {0.0, 0.0}}, 141 | {{0.0, 7.0}, {4.0, 0.0}, {3.0, 0.0}, {0.0, 0.0}}, 142 | {{-6.0, 0.0}, {3.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}}, 143 | {{3.0, 0.0}, {2.0, 0.0}, {1.0, 5.0}, {0.0, 0.0}}}; 144 | std::cout << "rank(matF) = " << rank(matF) << "\n"; 145 | showMatrix(matF, "matF (singular)"); 146 | showMatrix(pinv2(matF), "pinv2(matF)"); 147 | std::cout << "\n\n"; 148 | 149 | i_complex_matrix matG = { 150 | {{0.0, 0.0}, {2.0, 0.0}, {3.0, 2.0}, {5.0, 0.0}}, 151 | {{2.0, 0.0}, {0.0, 0.0}, {3.0, 0.0}, {6.0, 0.0}}, 152 | {{0.0, 7.0}, {4.0, 0.0}, {0.0, 0.0}, {1.0, 0.0}}, 153 | {{-6.0, 0.0}, {3.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}}}; 154 | std::cout << "rank(matG) = " << rank(matG) << "\n"; 155 | showMatrix(matG, "matG (diag zero)"); 156 | showMatrix(inv(matG), "inv(matG)"); 157 | showMatrix(pinv(matG), "pinv(matG)"); 158 | showMatrix(pinv2(matG), "pinv2(matG)"); 159 | std::cout << "\n\n"; 160 | 161 | if (!doLargeMatTest) 162 | { 163 | return; 164 | } 165 | 166 | std::size_t nAnt = 64; 167 | std::size_t nEq = 960; 168 | i_complex_matrix largeA = genTestMatrixA(nAnt, nEq); 169 | i_complex_matrix largeb = genTestMatrixb(nAnt, nEq); 170 | // i_complex_matrix largeA = genTestMatrixA(nAnt); 171 | // i_complex_matrix largeb = genTestMatrixb(nAnt); 172 | i_complex_matrix largeA_pinv; 173 | i_complex_matrix largex; 174 | 175 | TestTimer timer; 176 | timer.tic(); 177 | largeA_pinv = pinv(largeA); 178 | largex = matMul(largeA_pinv, largeb); 179 | showMatrix(largex, "mat x"); 180 | timer.toc("pinv1 method"); 181 | 182 | timer.tic(); 183 | largex = leftDiv(largeA, largeb); 184 | showMatrix(largex, "mat x"); 185 | timer.toc("pinv2 method"); 186 | } 187 | 188 | void determinantTest() 189 | { 190 | std::cout << "\n\n******************** determinant test ********************\n\n"; 191 | i_complex_matrix matA = { 192 | {{5.0, 3.0}}}; 193 | showMatrix(matA, "matA", true); 194 | std::cout << "det(matA) = " << det(matA) << "\n\n\n"; 195 | 196 | i_complex_matrix matB = { 197 | {{1.0, 0.0}, {2.0, 0.0}}, 198 | {{2.0, 0.0}, {0.0, 5.0}}}; 199 | showMatrix(matB, "matB", true); 200 | std::cout << "det(matB) = " << det(matB) << "\n\n\n"; 201 | 202 | i_complex_matrix matC = { 203 | {{1.0, 0.0}, {2.0, 0.0}, {3.0, 0.0}}, 204 | {{2.0, 0.0}, {5.0, 0.0}, {3.0, 0.0}}, 205 | {{0.0, 7.0}, {4.0, 0.0}, {3.0, 0.0}}}; 206 | showMatrix(matC, "matC", true); 207 | std::cout << "det(matC) = " << det(matC) << "\n\n\n"; 208 | 209 | i_complex_matrix matD = { 210 | {{1.0, 0.0}, {2.0, 0.0}, {3.0, 0.0}, {5.0, 0.0}}, 211 | {{2.0, 0.0}, {5.0, 0.0}, {3.0, 0.0}, {6.0, 0.0}}, 212 | {{0.0, 7.0}, {4.0, 0.0}, {3.0, 0.0}, {1.0, 0.0}}, 213 | {{-6.0, 0.0}, {3.0, 0.0}, {1.0, 0.0}, {9.0, 0.0}}}; 214 | showMatrix(matD, "matD", true); 215 | std::cout << "det(matD) = " << det(matD) << "\n\n\n"; 216 | 217 | i_complex_matrix matE = { 218 | {{1.0, 0.0}, {2.0, 0.0}, {3.0, 0.0}, {5.0, 0.0}, {1.0, 0.0}}, 219 | {{2.0, 0.0}, {5.0, 0.0}, {3.0, 0.0}, {6.0, 0.0}, {3.0, 0.0}}, 220 | {{0.0, 7.0}, {4.0, 0.0}, {3.0, 0.0}, {1.0, 0.0}, {1.0, 0.0}}, 221 | {{-6.0, 0.0}, {3.0, 0.0}, {1.0, 0.0}, {9.0, 0.0}, {4.0, 0.0}}, 222 | {{-3.0, 0.0}, {3.0, 5.0}, {7.0, 0.0}, {0.0, 0.0}, {1.0, 0.0}}}; 223 | showMatrix(matE, "matE", true); 224 | std::cout << "det(matE) = " << det(matE) << "\n\n\n"; 225 | 226 | i_complex_matrix matF = { 227 | {{9.0, 12.0}, {2.0, 0.0}, {3.0, 0.0}, {5.0, 0.0}, {1.0, 0.0}, {1.0, 7.0}}, 228 | {{2.0, 0.0}, {5.0, 0.0}, {3.0, 0.0}, {6.0, 0.0}, {3.0, 0.0}, {0.0, 0.0}}, 229 | {{0.0, 7.0}, {4.0, 0.0}, {3.0, 0.0}, {1.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}}, 230 | {{-6.0, 0.0}, {3.0, 0.0}, {1.0, 0.0}, {9.0, 0.0}, {4.0, 0.0}, {5.0, 0.0}}, 231 | {{-3.0, 0.0}, {3.0, 5.0}, {7.0, 0.0}, {0.0, 0.0}, {1.0, 0.0}, {1.0, 0.0}}, 232 | {{1.0, 0.0}, {5.0, 5.0}, {1.0, 0.0}, {0.0, 1.0}, {0.0, 0.0}, {9.0, 0.0}}}; 233 | showMatrix(matF, "matF", true); 234 | std::cout << "det(matF) = " << det(matF) << "\n\n\n"; 235 | 236 | std::size_t nAnt = 64; 237 | std::size_t nEq = 960; 238 | i_complex_matrix largeA = genTestMatrixA(nAnt, nEq); 239 | i_complex_matrix largeG = matMul(transpose(largeA), largeA); 240 | TestTimer timer; 241 | timer.tic(); 242 | std::cout << "det(largeG) = " << det(largeG) << "\n"; 243 | timer.toc("large matrix determinant"); 244 | } 245 | 246 | int main(int argc, char **argv) 247 | { 248 | pinvTest(true); 249 | determinantTest(); 250 | std::cin.get(); 251 | return 0; 252 | } 253 | -------------------------------------------------------------------------------- /matBasic_real.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /* 3 | Mini Matrix Tools for Real Matrix 4 | Vesion: 0.9 - 2021.11.18 by Fanseline 5 | https://ferryyoungfan.github.io 6 | 7 | Main Function List: 8 | [1] rank: Matrix rank (Cholesky decomposition) 9 | [2] det: Matrix determinant calculation 10 | [3] inv: LU decomposition-based matrix inversion 11 | [4] pinv: pinv(G) = inv(G' * G) * G' (WARNING: full-rank matrix only!) 12 | [5] pinv2: Moore-Penrose pseudoinversion (same as pinv(G) in MATLAB) 13 | [6] leftDiv: x = A \ b, using Moore-Penrose pinv, NOT same as MATLAB for a singular matrix 14 | 15 | Reference: 16 | [*1] Pierre Courrieu, Fast Computation of Moore-Penrose Inverse Matrices, https://arxiv.org/abs/0804.4809 17 | [*2] Permute Sign Calculation, page5 https://www.math.rutgers.edu/docman-lister/math-main/academics/course-materials/250/assignments/1493-250c-lab3-sakai-pdf/file 18 | [*3] LU Decomposition C++ Implementation, https://blog.csdn.net/xx_123_1_rj/article/details/39553809 19 | [*4] LU Decomposition, https://www.math.ucdavis.edu/~linear/old/notes11.pdf 20 | */ 21 | #include 22 | #include 23 | #include 24 | 25 | using i_float_t = double; // using i_float_t = float; // Notice: Do NOT use int type! 26 | using i_real_vector = std::vector; 27 | using i_real_matrix = std::vector; 28 | 29 | // Simply print real matrix with description, can be either block or MATLAB format. 30 | void showMatrix(const i_real_matrix &matG, const char *describe = nullptr, bool matlabFormat = false) 31 | { 32 | const std::size_t nrows{matG.size()}, ncols{matG[0].size()}; 33 | matlabFormat = describe && matlabFormat; 34 | if (describe) 35 | { 36 | matlabFormat ? std::cout << describe << " = [" : std::cout << describe << " : " << nrows << " x " << ncols << " Real Matrix:\n"; 37 | } 38 | for (std::size_t row{0}; row < nrows; ++row) 39 | { 40 | if (!matlabFormat) 41 | { 42 | std::cout << " row[" << row + 1 << "]: "; 43 | } 44 | for (std::size_t col{0}; col < ncols; ++col) 45 | { 46 | std::cout << matG[row][col]; 47 | if (col + 1 < ncols) 48 | { 49 | std::cout << ", "; 50 | } 51 | else 52 | { 53 | matlabFormat ? std::cout << "; " : std::cout << ";\n"; 54 | } 55 | } 56 | } 57 | matlabFormat ? std::cout << "];\n" : std::cout << "\n"; 58 | } 59 | 60 | // Generate and fill an nrows x ncols matrix, fill with given value (zero by default) 61 | i_real_matrix initRealMatrix(const std::size_t nrows, const std::size_t ncols, const i_float_t initValue = 0.0) 62 | { 63 | return i_real_matrix(nrows, i_real_vector(ncols, initValue)); 64 | } 65 | 66 | // Matrix transpose 67 | i_real_matrix transpose(const i_real_matrix &matG) 68 | { 69 | const std::size_t nrows{matG.size()}, ncols{matG[0].size()}; 70 | i_real_matrix matGt = initRealMatrix(ncols, nrows); 71 | std::size_t i{0}, j{0}; 72 | for (i = 0; i < nrows; ++i) 73 | { 74 | for (j = 0; j < ncols; ++j) 75 | { 76 | matGt[j][i] = matG[i][j]; 77 | } 78 | } 79 | return matGt; 80 | } 81 | 82 | // Matrix multiplication O(n^3) naive implementation 83 | i_real_matrix matMul(const i_real_matrix &matA, const i_real_matrix &matB) 84 | { 85 | const std::size_t nrowsA{matA.size()}, ncolsA{matA[0].size()}, nrowsB{matB.size()}, ncolsB{matB[0].size()}; 86 | i_real_matrix resMat; 87 | if (ncolsA != nrowsB) 88 | { 89 | std::cout << "Error when using matMul: dimension not match.\n"; 90 | return resMat; 91 | } 92 | resMat = initRealMatrix(nrowsA, ncolsB); 93 | std::size_t i{0}, j{0}, k{0}; 94 | for (i = 0; i < nrowsA; ++i) 95 | { 96 | for (j = 0; j < ncolsB; ++j) 97 | { 98 | for (k = 0; k < ncolsA; ++k) 99 | { 100 | resMat[i][j] += matA[i][k] * matB[k][j]; 101 | } 102 | } 103 | } 104 | return resMat; 105 | } 106 | 107 | // Calculate matrix rank (Cholesky decomposition) [*1] 108 | std::size_t rank(const i_real_matrix &matG, const i_float_t tolerance = 1.0e-9) 109 | { 110 | const std::size_t nrows{matG.size()}, ncols{matG[0].size()}; 111 | std::size_t nSize{ncols}; 112 | std::size_t i{0}, j{0}, k{0}; 113 | 114 | i_real_matrix matA; 115 | if (nrows < nSize) 116 | { 117 | // A = G * G' 118 | nSize = nrows; 119 | matA = initRealMatrix(nSize, nSize); 120 | for (i = 0; i < nSize; ++i) 121 | { 122 | for (j = 0; j < nSize; ++j) 123 | { 124 | for (k = 0; k < ncols; ++k) 125 | { 126 | matA[i][j] += matG[i][k] * matG[j][k]; 127 | } 128 | } 129 | } 130 | } 131 | else 132 | { 133 | // A = G' * G 134 | matA = initRealMatrix(nSize, nSize); 135 | for (i = 0; i < nSize; ++i) 136 | { 137 | for (j = 0; j < nSize; ++j) 138 | { 139 | for (k = 0; k < ncols; ++k) 140 | { 141 | matA[i][j] += matG[k][i] * matG[k][j]; 142 | } 143 | } 144 | } 145 | } 146 | 147 | // Full rank Cholesky decomposition of A 148 | i_float_t tol{std::abs(matA[0][0])}; 149 | for (i = 0; i < nSize; ++i) 150 | { 151 | if (matA[i][i] > 0) 152 | { 153 | const i_float_t temp{std::abs(matA[i][i])}; 154 | if (temp < tol) 155 | { 156 | tol = temp; 157 | } 158 | } 159 | } 160 | tol *= tolerance; 161 | 162 | i_real_matrix matL = initRealMatrix(nSize, nSize); 163 | std::size_t rankA{0}; 164 | for (k = 0; k < nSize; ++k) 165 | { 166 | for (i = k; i < nSize; ++i) 167 | { 168 | matL[i][rankA] = matA[i][k]; 169 | for (j = 0; j < rankA; ++j) 170 | { 171 | matL[i][rankA] -= matL[i][j] * matL[k][j]; 172 | } 173 | } 174 | if (matL[k][rankA] > tol) 175 | { 176 | matL[k][rankA] = std::sqrt(matL[k][rankA]); 177 | if (k < nSize) 178 | { 179 | for (j = k + 1; j < nSize; ++j) 180 | { 181 | matL[j][rankA] /= matL[k][rankA]; 182 | } 183 | } 184 | ++rankA; 185 | } 186 | } 187 | return rankA; // rank(G) = rank(A) 188 | } 189 | 190 | // LU decomposition-based matrix determinant calculation [*2][*3][*4] 191 | i_float_t det(const i_real_matrix &matG) 192 | { 193 | const std::size_t nrows{matG.size()}, ncols{matG[0].size()}; 194 | i_float_t detG = 0.0; 195 | if (nrows != ncols) 196 | { 197 | std::cout << "Error when using det: matrix is not square.\n"; 198 | return detG; 199 | } 200 | 201 | const std::size_t nSize{nrows}; 202 | std::size_t i{0}, j{0}, k{0}; 203 | 204 | // ******************** Step 1: row permutation (swap diagonal zeros) ******************** 205 | i_real_matrix matLU; 206 | std::vector permuteLU; // Permute vector 207 | bool changeSign{false}; 208 | 209 | for (i = 0; i < nSize; ++i) 210 | { 211 | permuteLU.push_back(i); 212 | } 213 | 214 | for (j = 0; j < nSize; ++j) 215 | { 216 | i_float_t maxv{0.0}; 217 | for (i = j; i < nSize; ++i) 218 | { 219 | const i_float_t currentv{std::abs(matG[permuteLU[i]][j])}; 220 | if (currentv > maxv) 221 | { 222 | maxv = currentv; 223 | if (permuteLU[i] != permuteLU[j]) // swap rows 224 | { 225 | changeSign = !changeSign; 226 | const std::size_t tmp{permuteLU[j]}; 227 | permuteLU[j] = permuteLU[i]; 228 | permuteLU[i] = tmp; 229 | } 230 | } 231 | } 232 | } 233 | 234 | for (i = 0; i < nSize; ++i) 235 | { 236 | matLU.push_back(matG[permuteLU[i]]); 237 | } 238 | 239 | // ******************** Step 2: LU decomposition (save both L & U in matLU) ******************** 240 | if (matLU[0][0] == 0.0) 241 | { 242 | return detG; // Singular matrix, det(G) = 0 243 | } 244 | 245 | for (i = 1; i < nSize; ++i) 246 | { 247 | matLU[i][0] /= matLU[0][0]; 248 | } 249 | 250 | for (i = 1; i < nSize; ++i) 251 | { 252 | for (j = i; j < nSize; ++j) 253 | { 254 | for (k = 0; k < i; ++k) 255 | { 256 | matLU[i][j] -= matLU[i][k] * matLU[k][j]; // Calculate U matrix 257 | } 258 | } 259 | if (matLU[i][i] == 0.0) 260 | { 261 | return detG; // Singular matrix, det(G) = 0 262 | } 263 | for (k = i + 1; k < nSize; ++k) 264 | { 265 | for (j = 0; j < i; ++j) 266 | { 267 | matLU[k][i] -= matLU[k][j] * matLU[j][i]; // Calculate L matrix 268 | } 269 | matLU[k][i] /= matLU[i][i]; 270 | } 271 | } 272 | 273 | detG = 1.0; 274 | if (changeSign) 275 | { 276 | detG = -1.0; // Change the sign of the determinant 277 | } 278 | for (i = 0; i < nSize; ++i) 279 | { 280 | detG *= matLU[i][i]; // det(G) = det(L) * det(U). For triangular matrices, det(L) = prod(diag(L)) = 1, det(U) = prod(diag(U)), so det(G) = prod(diag(U)) 281 | } 282 | 283 | return detG; 284 | } 285 | 286 | // LU decomposition-based matrix inversion [*3][*4] 287 | i_real_matrix inv(const i_real_matrix &matG, const bool usePermute = true) 288 | { 289 | const std::size_t nrows{matG.size()}, ncols{matG[0].size()}; 290 | i_real_matrix matLU; 291 | if (nrows != ncols) 292 | { 293 | std::cout << "Error when using inv: matrix is not square.\n"; 294 | return matLU; 295 | } 296 | 297 | const std::size_t nSize{nrows}; 298 | std::size_t i{0}, j{0}, k{0}; 299 | 300 | // ******************** Step 1: row permutation (swap diagonal zeros) ******************** 301 | std::vector permuteLU; // Permute vector 302 | for (i = 0; i < nSize; ++i) 303 | { 304 | permuteLU.push_back(i); // Push back row index 305 | } 306 | 307 | if (usePermute) // Sort rows by pivot element 308 | { 309 | for (j = 0; j < nSize; ++j) 310 | { 311 | i_float_t maxv{0.0}; 312 | for (i = j; i < nSize; ++i) 313 | { 314 | const i_float_t currentv{std::abs(matG[permuteLU[i]][j])}; 315 | if (currentv > maxv) // Swap rows 316 | { 317 | maxv = currentv; 318 | const std::size_t tmp{permuteLU[j]}; 319 | permuteLU[j] = permuteLU[i]; 320 | permuteLU[i] = tmp; 321 | } 322 | } 323 | } 324 | for (i = 0; i < nSize; ++i) 325 | { 326 | matLU.push_back(matG[permuteLU[i]]); // Make a permuted matrix with new row order 327 | } 328 | } 329 | else 330 | { 331 | matLU = i_real_matrix(matG); // Simply duplicate matrix 332 | } 333 | 334 | // ******************** Step 2: LU decomposition (save both L & U in matLU) ******************** 335 | if (matLU[0][0] == 0.0) 336 | { 337 | std::cout << "Warning when using inv: matrix is singular.\n"; 338 | matLU.clear(); 339 | return matLU; 340 | } 341 | for (i = 1; i < nSize; ++i) 342 | { 343 | matLU[i][0] /= matLU[0][0]; // Initialize first column of L matrix 344 | } 345 | for (i = 1; i < nSize; ++i) 346 | { 347 | for (j = i; j < nSize; ++j) 348 | { 349 | for (k = 0; k < i; ++k) 350 | { 351 | matLU[i][j] -= matLU[i][k] * matLU[k][j]; // Calculate U matrix 352 | } 353 | } 354 | if (matLU[i][i] == 0.0) 355 | { 356 | std::cout << "Warning when using inv: matrix is singular.\n"; 357 | matLU.clear(); 358 | return matLU; 359 | } 360 | for (k = i + 1; k < nSize; ++k) 361 | { 362 | for (j = 0; j < i; ++j) 363 | { 364 | matLU[k][i] -= matLU[k][j] * matLU[j][i]; // Calculate L matrix 365 | } 366 | matLU[k][i] /= matLU[i][i]; 367 | } 368 | } 369 | 370 | // ******************** Step 3: L & U inversion (save both L^-1 & U^-1 in matLU_inv) ******************** 371 | i_real_matrix matLU_inv = initRealMatrix(nSize, nSize); 372 | 373 | // matL inverse & matU inverse 374 | for (i = 0; i < nSize; ++i) 375 | { 376 | // L matrix inverse, omit diagonal ones 377 | matLU_inv[i][i] = 1.0; 378 | for (k = i + 1; k < nSize; ++k) 379 | { 380 | for (j = i; j <= k - 1; ++j) 381 | { 382 | matLU_inv[k][i] -= matLU[k][j] * matLU_inv[j][i]; 383 | } 384 | } 385 | // U matrix inverse 386 | matLU_inv[i][i] = 1.0 / matLU[i][i]; 387 | for (k = i; k > 0; --k) 388 | { 389 | for (j = k; j <= i; ++j) 390 | { 391 | matLU_inv[k - 1][i] -= matLU[k - 1][j] * matLU_inv[j][i]; 392 | } 393 | matLU_inv[k - 1][i] /= matLU[k - 1][k - 1]; 394 | } 395 | } 396 | 397 | // ******************** Step 4: Calculate G^-1 = U^-1 * L^-1 ******************** 398 | // Lower part product 399 | for (i = 1; i < nSize; ++i) 400 | { 401 | for (j = 0; j < i; ++j) 402 | { 403 | const std::size_t jp{permuteLU[j]}; // Permute column back 404 | matLU[i][jp] = 0.0; 405 | for (k = i; k < nSize; ++k) 406 | { 407 | matLU[i][jp] += matLU_inv[i][k] * matLU_inv[k][j]; 408 | } 409 | } 410 | } 411 | // Upper part product 412 | for (i = 0; i < nSize; ++i) 413 | { 414 | for (j = i; j < nSize; ++j) 415 | { 416 | const std::size_t jp{permuteLU[j]}; // Permute column back 417 | matLU[i][jp] = matLU_inv[i][j]; 418 | for (k = j + 1; k < nSize; ++k) 419 | { 420 | matLU[i][jp] += matLU_inv[i][k] * matLU_inv[k][j]; 421 | } 422 | } 423 | } 424 | return matLU; // Reused matLU as a result container 425 | } 426 | 427 | // Classic pseudoinversion pinv(G) = inv(G' * G) * G' (WARNING: full-rank matrix only!) 428 | i_real_matrix pinv(const i_real_matrix &matG) 429 | { 430 | i_real_matrix matGt = transpose(matG); 431 | i_real_matrix matGtG_inv = inv(matMul(matGt, matG)); 432 | return matMul(matGtG_inv, matGt); 433 | } 434 | 435 | // Moore-Penrose pseudoinversion (same as pinv(G) in MATLAB) [*1] 436 | i_real_matrix pinv2(const i_real_matrix &matG, const i_float_t tolerance = 1.0e-9) 437 | { 438 | bool useTranspose{false}; 439 | const std::size_t nrows{matG.size()}, ncols{matG[0].size()}; 440 | std::size_t nSize{ncols}; 441 | 442 | i_real_matrix matA, matGt; 443 | matGt = transpose(matG); 444 | if (nrows < nSize) 445 | { 446 | useTranspose = true; 447 | nSize = nrows; 448 | matA = matMul(matG, matGt); // A = G * G' 449 | } 450 | else 451 | { 452 | matA = matMul(matGt, matG); // A = G' * G 453 | } 454 | 455 | // Full rank Cholesky decomposition of A 456 | std::size_t i{0}, j{0}, k{0}; 457 | 458 | i_float_t tol{std::abs(matA[0][0])}; 459 | for (i = 0; i < nSize; ++i) 460 | { 461 | if (matA[i][i] > 0) 462 | { 463 | const i_float_t temp{matA[i][i]}; 464 | if (temp < tol) 465 | { 466 | tol = temp; 467 | } 468 | } 469 | } 470 | tol *= tolerance; 471 | 472 | i_real_matrix matL = initRealMatrix(nSize, nSize); 473 | std::size_t rankA{0}; 474 | for (k = 0; k < nSize; ++k) 475 | { 476 | for (i = k; i < nSize; ++i) 477 | { 478 | matL[i][rankA] = matA[i][k]; 479 | for (j = 0; j < rankA; ++j) 480 | { 481 | matL[i][rankA] -= matL[i][j] * matL[k][j]; 482 | } 483 | } 484 | if (matL[k][rankA] > tol) 485 | { 486 | matL[k][rankA] = std::sqrt(matL[k][rankA]); 487 | if (k < nSize) 488 | { 489 | for (j = k + 1; j < nSize; ++j) 490 | { 491 | matL[j][rankA] /= matL[k][rankA]; 492 | } 493 | } 494 | ++rankA; 495 | } 496 | } 497 | 498 | if (rankA == 0) 499 | { 500 | return matGt; // All-zero matrix's transpose 501 | } 502 | 503 | // Slice L = L(:, 0:r); 504 | for (i = 0; i < nSize; ++i) 505 | { 506 | for (k = 0; k < nSize - rankA; ++k) 507 | { 508 | matL[i].pop_back(); 509 | } 510 | } 511 | 512 | // Generalized inverse 513 | i_real_matrix matLt = transpose(matL); 514 | i_real_matrix matM = inv(matMul(matLt, matL), false); // M = inv(L' * L) 515 | matA = matMul(matMul(matMul(matL, matM), matM), matLt); // A = L * M * M * L' 516 | 517 | if (useTranspose) 518 | { 519 | return matMul(matGt, matA); // pinv(G) = G' * (L * M * M * L') 520 | } 521 | return matMul(matA, matGt); // pinv(G) = (L * M * M * L') * G' 522 | } 523 | 524 | // Calculate left division x = A \ b, using Moore-Penrose pinv, NOT same as MATLAB for a singular matrix 525 | i_real_matrix leftDiv(const i_real_matrix &matA, const i_real_matrix &matb) 526 | { 527 | i_real_matrix matx; 528 | if (matA.size() != matb.size()) 529 | { 530 | std::cout << "Error when using leftDiv: row size not match.\n"; 531 | return matx; 532 | } 533 | matx = matMul(pinv2(matA), matb); // x = A \ b = pinv(A) * b 534 | return matx; 535 | } 536 | -------------------------------------------------------------------------------- /matBasic_complex.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /* 3 | Mini Matrix Tools for Complex Matrix 4 | Vesion: 0.9 - 2021.11.18 by Fanseline 5 | https://ferryyoungfan.github.io 6 | 7 | Main Function List: 8 | [1] rank: Matrix rank (Cholesky decomposition) 9 | [2] det: Matrix determinant calculation 10 | [3] inv: LU decomposition-based matrix inversion 11 | [4] pinv: pinv(G) = inv(G' * G) * G' (WARNING: full-rank matrix only!) 12 | [5] pinv2: Moore-Penrose pseudoinversion (same as pinv(G) in MATLAB) 13 | [6] leftDiv: x = A \ b, using Moore-Penrose pinv, NOT same as MATLAB for a singular matrix 14 | 15 | Reference: 16 | [*1] Pierre Courrieu, Fast Computation of Moore-Penrose Inverse Matrices, https://arxiv.org/abs/0804.4809 17 | [*2] Permute Sign Calculation, page5 https://www.math.rutgers.edu/docman-lister/math-main/academics/course-materials/250/assignments/1493-250c-lab3-sakai-pdf/file 18 | [*3] LU Decomposition C++ Implementation, https://blog.csdn.net/xx_123_1_rj/article/details/39553809 19 | [*4] LU Decomposition, https://www.math.ucdavis.edu/~linear/old/notes11.pdf 20 | */ 21 | #include 22 | #include 23 | #include 24 | 25 | using i_float_t = double; // using i_float_t = float; // Notice: Do NOT use int type! 26 | using i_complex_t = std::complex; 27 | using i_complex_vector = std::vector; 28 | using i_complex_matrix = std::vector; 29 | 30 | // Simply print complex matrix with description, can be either block or MATLAB format. 31 | void showMatrix(const i_complex_matrix &matG, const char *describe = nullptr, bool matlabFormat = false) 32 | { 33 | const std::size_t nrows{matG.size()}, ncols{matG[0].size()}; 34 | matlabFormat = describe && matlabFormat; 35 | if (describe) 36 | { 37 | matlabFormat ? std::cout << describe << " = [" : std::cout << describe << " : " << nrows << " x " << ncols << " Complex Matrix:\n"; 38 | } 39 | for (std::size_t row{0}; row < nrows; ++row) 40 | { 41 | if (!matlabFormat) 42 | { 43 | std::cout << " row[" << row + 1 << "]: "; 44 | } 45 | for (std::size_t col{0}; col < ncols; ++col) 46 | { 47 | std::cout << matG[row][col].real(); 48 | const i_float_t imag{matG[row][col].imag()}; 49 | if (imag != 0) 50 | { 51 | std::cout << (imag > 0 ? "+" : "-") << std::abs(imag) << "i"; 52 | } 53 | if (col + 1 < ncols) 54 | { 55 | std::cout << ", "; 56 | } 57 | else 58 | { 59 | matlabFormat ? std::cout << "; " : std::cout << ";\n"; 60 | } 61 | } 62 | } 63 | matlabFormat ? std::cout << "];\n" : std::cout << "\n"; 64 | } 65 | 66 | // Generate and fill an nrows x ncols matrix, fill with given value (zero by default) 67 | i_complex_matrix initComplexMatrix(const std::size_t nrows, const std::size_t ncols, const i_complex_t initValue = i_complex_t{0.0, 0.0}) 68 | { 69 | return i_complex_matrix(nrows, i_complex_vector(ncols, initValue)); 70 | } 71 | 72 | // Conjugate transpose (a.k.a. Hermitian transpose, G' = G^H = conj(G^T)) 73 | i_complex_matrix transpose(const i_complex_matrix &matG) 74 | { 75 | const std::size_t nrows{matG.size()}, ncols{matG[0].size()}; 76 | i_complex_matrix matGt = initComplexMatrix(ncols, nrows); 77 | std::size_t i{0}, j{0}; 78 | for (i = 0; i < nrows; ++i) 79 | { 80 | for (j = 0; j < ncols; ++j) 81 | { 82 | matGt[j][i] = std::conj(matG[i][j]); // Hermitian transpose 83 | } 84 | } 85 | return matGt; 86 | } 87 | 88 | // Matrix multiplication O(n^3) naive implementation 89 | i_complex_matrix matMul(const i_complex_matrix &matA, const i_complex_matrix &matB) 90 | { 91 | const std::size_t nrowsA{matA.size()}, ncolsA{matA[0].size()}, nrowsB{matB.size()}, ncolsB{matB[0].size()}; 92 | i_complex_matrix resMat; 93 | if (ncolsA != nrowsB) 94 | { 95 | std::cout << "Error when using matMul: dimension not match.\n"; 96 | return resMat; 97 | } 98 | resMat = initComplexMatrix(nrowsA, ncolsB); 99 | std::size_t i{0}, j{0}, k{0}; 100 | for (i = 0; i < nrowsA; ++i) 101 | { 102 | for (j = 0; j < ncolsB; ++j) 103 | { 104 | for (k = 0; k < ncolsA; ++k) 105 | { 106 | resMat[i][j] += matA[i][k] * matB[k][j]; 107 | } 108 | } 109 | } 110 | return resMat; 111 | } 112 | 113 | // Calculate matrix rank (Cholesky decomposition) [*1] 114 | std::size_t rank(const i_complex_matrix &matG, const i_float_t tolerance = 1.0e-9) 115 | { 116 | const std::size_t nrows{matG.size()}, ncols{matG[0].size()}; 117 | std::size_t nSize{ncols}; 118 | std::size_t i{0}, j{0}, k{0}; 119 | 120 | i_complex_matrix matA; 121 | if (nrows < nSize) 122 | { 123 | // A = G * G' 124 | nSize = nrows; 125 | matA = initComplexMatrix(nSize, nSize); 126 | for (i = 0; i < nSize; ++i) 127 | { 128 | for (j = 0; j < nSize; ++j) 129 | { 130 | for (k = 0; k < ncols; ++k) 131 | { 132 | matA[i][j] += matG[i][k] * std::conj(matG[j][k]); 133 | } 134 | } 135 | } 136 | } 137 | else 138 | { 139 | // A = G' * G 140 | matA = initComplexMatrix(nSize, nSize); 141 | for (i = 0; i < nSize; ++i) 142 | { 143 | for (j = 0; j < nSize; ++j) 144 | { 145 | for (k = 0; k < ncols; ++k) 146 | { 147 | matA[i][j] += std::conj(matG[k][i]) * matG[k][j]; 148 | } 149 | } 150 | } 151 | } 152 | 153 | // Full rank Cholesky decomposition of A 154 | i_float_t tol{std::abs(matA[0][0])}; 155 | for (i = 0; i < nSize; ++i) 156 | { 157 | if (matA[i][i].real() > 0) 158 | { 159 | const i_float_t temp{std::abs(matA[i][i])}; 160 | if (temp < tol) 161 | { 162 | tol = temp; 163 | } 164 | } 165 | } 166 | tol *= tolerance; 167 | 168 | i_complex_matrix matL = initComplexMatrix(nSize, nSize); 169 | std::size_t rankA{0}; 170 | for (k = 0; k < nSize; ++k) 171 | { 172 | for (i = k; i < nSize; ++i) 173 | { 174 | matL[i][rankA] = matA[i][k]; 175 | for (j = 0; j < rankA; ++j) 176 | { 177 | matL[i][rankA] -= matL[i][j] * std::conj(matL[k][j]); 178 | } 179 | } 180 | if (matL[k][rankA].real() > tol) 181 | { 182 | matL[k][rankA] = std::sqrt(matL[k][rankA]); 183 | if (k < nSize) 184 | { 185 | for (j = k + 1; j < nSize; ++j) 186 | { 187 | matL[j][rankA] /= matL[k][rankA]; 188 | } 189 | } 190 | ++rankA; 191 | } 192 | } 193 | return rankA; // rank(G) = rank(A) 194 | } 195 | 196 | // LU decomposition-based matrix determinant calculation [*2][*3][*4] 197 | i_complex_t det(const i_complex_matrix &matG) 198 | { 199 | const std::size_t nrows{matG.size()}, ncols{matG[0].size()}; 200 | i_complex_t detG = 0.0; 201 | if (nrows != ncols) 202 | { 203 | std::cout << "Error when using det: matrix is not square.\n"; 204 | return detG; 205 | } 206 | 207 | const std::size_t nSize{nrows}; 208 | std::size_t i{0}, j{0}, k{0}; 209 | 210 | // ******************** Step 1: row permutation (swap diagonal zeros) ******************** 211 | i_complex_matrix matLU; 212 | std::vector permuteLU; // Permute vector 213 | bool changeSign{false}; 214 | 215 | for (i = 0; i < nSize; ++i) 216 | { 217 | permuteLU.push_back(i); 218 | } 219 | 220 | for (j = 0; j < nSize; ++j) 221 | { 222 | i_float_t maxv{0.0}; 223 | i_float_t currentv{0.0}; 224 | for (i = j; i < nSize; ++i) 225 | { 226 | if (matG[permuteLU[i]][j].real() != 0) 227 | { 228 | currentv = std::abs(matG[permuteLU[i]][j].real()); 229 | } 230 | else 231 | { 232 | currentv = std::abs(matG[permuteLU[i]][j].imag()); 233 | } 234 | if (currentv > maxv) 235 | { 236 | maxv = currentv; 237 | if (permuteLU[i] != permuteLU[j]) // swap rows 238 | { 239 | changeSign = !changeSign; 240 | const std::size_t tmp{permuteLU[j]}; 241 | permuteLU[j] = permuteLU[i]; 242 | permuteLU[i] = tmp; 243 | } 244 | } 245 | } 246 | } 247 | 248 | for (i = 0; i < nSize; ++i) 249 | { 250 | matLU.push_back(matG[permuteLU[i]]); 251 | } 252 | 253 | // ******************** Step 2: LU decomposition (save both L & U in matLU) ******************** 254 | if (matLU[0][0] == 0.0) 255 | { 256 | return detG; // Singular matrix, det(G) = 0 257 | } 258 | 259 | for (i = 1; i < nSize; ++i) 260 | { 261 | matLU[i][0] /= matLU[0][0]; 262 | } 263 | 264 | for (i = 1; i < nSize; ++i) 265 | { 266 | for (j = i; j < nSize; ++j) 267 | { 268 | for (k = 0; k < i; ++k) 269 | { 270 | matLU[i][j] -= matLU[i][k] * matLU[k][j]; // Calculate U matrix 271 | } 272 | } 273 | if (matLU[i][i] == 0.0) 274 | { 275 | return detG; // Singular matrix, det(G) = 0 276 | } 277 | for (k = i + 1; k < nSize; ++k) 278 | { 279 | for (j = 0; j < i; ++j) 280 | { 281 | matLU[k][i] -= matLU[k][j] * matLU[j][i]; // Calculate L matrix 282 | } 283 | matLU[k][i] /= matLU[i][i]; 284 | } 285 | } 286 | 287 | detG = 1.0; 288 | if (changeSign) 289 | { 290 | detG = -1.0; // Change the sign of the determinant 291 | } 292 | for (i = 0; i < nSize; ++i) 293 | { 294 | detG *= matLU[i][i]; // det(G) = det(L) * det(U). For triangular matrices, det(L) = prod(diag(L)) = 1, det(U) = prod(diag(U)), so det(G) = prod(diag(U)) 295 | } 296 | 297 | return detG; 298 | } 299 | 300 | // LU decomposition-based matrix inversion [*3][*4] 301 | i_complex_matrix inv(const i_complex_matrix &matG, const bool usePermute = true) 302 | { 303 | const std::size_t nrows{matG.size()}, ncols{matG[0].size()}; 304 | i_complex_matrix matLU; 305 | if (nrows != ncols) 306 | { 307 | std::cout << "Error when using inv: matrix is not square.\n"; 308 | return matLU; 309 | } 310 | 311 | const std::size_t nSize{nrows}; 312 | std::size_t i{0}, j{0}, k{0}; 313 | 314 | // ******************** Step 1: row permutation (swap diagonal zeros) ******************** 315 | std::vector permuteLU; // Permute vector 316 | for (i = 0; i < nSize; ++i) 317 | { 318 | permuteLU.push_back(i); // Push back row index 319 | } 320 | 321 | if (usePermute) // Sort rows by pivot element 322 | { 323 | for (j = 0; j < nSize; ++j) 324 | { 325 | i_float_t maxv{0.0}; 326 | i_float_t currentv{0.0}; 327 | for (i = j; i < nSize; ++i) 328 | { 329 | if (matG[permuteLU[i]][j].real() != 0) 330 | { 331 | currentv = std::abs(matG[permuteLU[i]][j].real()); 332 | } 333 | else 334 | { 335 | currentv = std::abs(matG[permuteLU[i]][j].imag()); 336 | } 337 | if (currentv > maxv) // Swap rows 338 | { 339 | maxv = currentv; 340 | const std::size_t tmp{permuteLU[j]}; 341 | permuteLU[j] = permuteLU[i]; 342 | permuteLU[i] = tmp; 343 | } 344 | } 345 | } 346 | for (i = 0; i < nSize; ++i) 347 | { 348 | matLU.push_back(matG[permuteLU[i]]); // Make a permuted matrix with new row order 349 | } 350 | } 351 | else 352 | { 353 | matLU = i_complex_matrix(matG); // Simply duplicate matrix 354 | } 355 | 356 | // ******************** Step 2: LU decomposition (save both L & U in matLU) ******************** 357 | if (matLU[0][0] == 0.0) 358 | { 359 | std::cout << "Warning when using inv: matrix is singular.\n"; 360 | matLU.clear(); 361 | return matLU; 362 | } 363 | for (i = 1; i < nSize; ++i) 364 | { 365 | matLU[i][0] /= matLU[0][0]; // Initialize first column of L matrix 366 | } 367 | for (i = 1; i < nSize; ++i) 368 | { 369 | for (j = i; j < nSize; ++j) 370 | { 371 | for (k = 0; k < i; ++k) 372 | { 373 | matLU[i][j] -= matLU[i][k] * matLU[k][j]; // Calculate U matrix 374 | } 375 | } 376 | if (matLU[i][i] == 0.0) 377 | { 378 | std::cout << "Warning when using inv: matrix is singular.\n"; 379 | matLU.clear(); 380 | return matLU; 381 | } 382 | for (k = i + 1; k < nSize; ++k) 383 | { 384 | for (j = 0; j < i; ++j) 385 | { 386 | matLU[k][i] -= matLU[k][j] * matLU[j][i]; // Calculate L matrix 387 | } 388 | matLU[k][i] /= matLU[i][i]; 389 | } 390 | } 391 | 392 | // ******************** Step 3: L & U inversion (save both L^-1 & U^-1 in matLU_inv) ******************** 393 | i_complex_matrix matLU_inv = initComplexMatrix(nSize, nSize); 394 | 395 | // matL inverse & matU inverse 396 | for (i = 0; i < nSize; ++i) 397 | { 398 | // L matrix inverse, omit diagonal ones 399 | matLU_inv[i][i] = 1.0; 400 | for (k = i + 1; k < nSize; ++k) 401 | { 402 | for (j = i; j <= k - 1; ++j) 403 | { 404 | matLU_inv[k][i] -= matLU[k][j] * matLU_inv[j][i]; 405 | } 406 | } 407 | // U matrix inverse 408 | matLU_inv[i][i] = 1.0 / matLU[i][i]; 409 | for (k = i; k > 0; --k) 410 | { 411 | for (j = k; j <= i; ++j) 412 | { 413 | matLU_inv[k - 1][i] -= matLU[k - 1][j] * matLU_inv[j][i]; 414 | } 415 | matLU_inv[k - 1][i] /= matLU[k - 1][k - 1]; 416 | } 417 | } 418 | 419 | // ******************** Step 4: Calculate G^-1 = U^-1 * L^-1 ******************** 420 | // Lower part product 421 | for (i = 1; i < nSize; ++i) 422 | { 423 | for (j = 0; j < i; ++j) 424 | { 425 | const std::size_t jp{permuteLU[j]}; // Permute column back 426 | matLU[i][jp] = 0.0; 427 | for (k = i; k < nSize; ++k) 428 | { 429 | matLU[i][jp] += matLU_inv[i][k] * matLU_inv[k][j]; 430 | } 431 | } 432 | } 433 | // Upper part product 434 | for (i = 0; i < nSize; ++i) 435 | { 436 | for (j = i; j < nSize; ++j) 437 | { 438 | const std::size_t jp{permuteLU[j]}; // Permute column back 439 | matLU[i][jp] = matLU_inv[i][j]; 440 | for (k = j + 1; k < nSize; ++k) 441 | { 442 | matLU[i][jp] += matLU_inv[i][k] * matLU_inv[k][j]; 443 | } 444 | } 445 | } 446 | return matLU; // Reused matLU as a result container 447 | } 448 | 449 | // Classic pseudoinversion pinv(G) = inv(G' * G) * G' (WARNING: full-rank matrix only!) 450 | i_complex_matrix pinv(const i_complex_matrix &matG) 451 | { 452 | i_complex_matrix matGt = transpose(matG); 453 | i_complex_matrix matGtG_inv = inv(matMul(matGt, matG)); 454 | return matMul(matGtG_inv, matGt); 455 | } 456 | 457 | // Moore-Penrose pseudoinversion (same as pinv(G) in MATLAB) [*1] 458 | i_complex_matrix pinv2(const i_complex_matrix &matG, const i_float_t tolerance = 1.0e-9) 459 | { 460 | bool useTranspose{false}; 461 | const std::size_t nrows{matG.size()}, ncols{matG[0].size()}; 462 | std::size_t nSize{ncols}; 463 | 464 | i_complex_matrix matA, matGt; 465 | matGt = transpose(matG); 466 | if (nrows < nSize) 467 | { 468 | useTranspose = true; 469 | nSize = nrows; 470 | matA = matMul(matG, matGt); // A = G * G' 471 | } 472 | else 473 | { 474 | matA = matMul(matGt, matG); // A = G' * G 475 | } 476 | 477 | // Full rank Cholesky decomposition of A 478 | std::size_t i{0}, j{0}, k{0}; 479 | 480 | i_float_t tol{std::abs(matA[0][0])}; 481 | for (i = 0; i < nSize; ++i) 482 | { 483 | if (matA[i][i].real() > 0) 484 | { 485 | const i_float_t temp{std::abs(matA[i][i])}; 486 | if (temp < tol) 487 | { 488 | tol = temp; 489 | } 490 | } 491 | } 492 | tol *= tolerance; 493 | 494 | i_complex_matrix matL = initComplexMatrix(nSize, nSize); 495 | std::size_t rankA{0}; 496 | for (k = 0; k < nSize; ++k) 497 | { 498 | for (i = k; i < nSize; ++i) 499 | { 500 | matL[i][rankA] = matA[i][k]; 501 | for (j = 0; j < rankA; ++j) 502 | { 503 | matL[i][rankA] -= matL[i][j] * std::conj(matL[k][j]); 504 | } 505 | } 506 | if (matL[k][rankA].real() > tol) 507 | { 508 | matL[k][rankA] = std::sqrt(matL[k][rankA]); 509 | if (k < nSize) 510 | { 511 | for (j = k + 1; j < nSize; ++j) 512 | { 513 | matL[j][rankA] /= matL[k][rankA]; 514 | } 515 | } 516 | ++rankA; 517 | } 518 | } 519 | 520 | if (rankA == 0) 521 | { 522 | return matGt; // All-zero matrix's transpose 523 | } 524 | 525 | // Slice L = L(:, 0:r); 526 | for (i = 0; i < nSize; ++i) 527 | { 528 | for (k = 0; k < nSize - rankA; ++k) 529 | { 530 | matL[i].pop_back(); 531 | } 532 | } 533 | 534 | // Generalized inverse 535 | i_complex_matrix matLt = transpose(matL); 536 | i_complex_matrix matM = inv(matMul(matLt, matL), false); // M = inv(L' * L) 537 | matA = matMul(matMul(matMul(matL, matM), matM), matLt); // A = L * M * M * L' 538 | 539 | if (useTranspose) 540 | { 541 | return matMul(matGt, matA); // pinv(G) = G' * (L * M * M * L') 542 | } 543 | return matMul(matA, matGt); // pinv(G) = (L * M * M * L') * G' 544 | } 545 | 546 | // Calculate left division x = A \ b, using Moore-Penrose pinv, NOT same as MATLAB for a singular matrix 547 | i_complex_matrix leftDiv(const i_complex_matrix &matA, const i_complex_matrix &matb) 548 | { 549 | i_complex_matrix matx; 550 | if (matA.size() != matb.size()) 551 | { 552 | std::cout << "Error when using leftDiv: row size not match.\n"; 553 | return matx; 554 | } 555 | matx = matMul(pinv2(matA), matb); // x = A \ b = pinv(A) * b 556 | return matx; 557 | } 558 | --------------------------------------------------------------------------------