Tetrapod Project
linalg_utils.h
Go to the documentation of this file.
1 /*******************************************************************/
2 /* AUTHOR: Paal Arthur S. Thorseth */
3 /* ORGN: Dept of Eng Cybernetics, NTNU Trondheim */
4 /* FILE: linalg_utils.h */
5 /* DATE: May 9, 2021 */
6 /* */
7 /* Copyright (C) 2021 Paal Arthur S. Thorseth, */
8 /* Adrian B. Ghansah */
9 /* */
10 /* This program is free software: you can redistribute it */
11 /* and/or modify it under the terms of the GNU General */
12 /* Public License as published by the Free Software Foundation, */
13 /* either version 3 of the License, or (at your option) any */
14 /* later version. */
15 /* */
16 /* This program is distributed in the hope that it will be useful, */
17 /* but WITHOUT ANY WARRANTY; without even the implied warranty */
18 /* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */
19 /* See the GNU General Public License for more details. */
20 /* */
21 /* You should have received a copy of the GNU General Public */
22 /* License along with this program. If not, see */
23 /* <https://www.gnu.org/licenses/>. */
24 /* */
25 /*******************************************************************/
26 
27 #pragma once
28 
29 // C++ Standard Library
30 #include <cmath>
31 
32 // Kindr
33 #include <kindr/Core>
34 
35 // Eigen
36 #include <Eigen/Core>
37 #include <Eigen/SVD>
38 
39 namespace math_utils
40 {
48  // TODO Fix Singularity issue, as it stands now the pseudo inverse fails when the input matrix is singular.
49  template<typename Matrix_TypeA, typename Matrix_TypeB>
50  bool static dampedPseudoInverse(const Matrix_TypeA &_J,
51  Matrix_TypeB &_dPinvJ,
52  const double _lambda = 0,
53  const double _epsilon = std::numeric_limits<typename Matrix_TypeA::Scalar>::epsilon())
54  {
55  // Dimensions
56  constexpr auto rowsA = static_cast<int>(Matrix_TypeA::RowsAtCompileTime);
57  constexpr auto colsA = static_cast<int>(Matrix_TypeA::ColsAtCompileTime);
58  constexpr auto rowsB = static_cast<int>(Matrix_TypeB::RowsAtCompileTime);
59  constexpr auto colsB = static_cast<int>(Matrix_TypeB::ColsAtCompileTime);
60  constexpr auto rankA = static_cast<int>(Eigen::JacobiSVD<Matrix_TypeA>::DiagSizeAtCompileTime);
61 
62  // Validate matrices
63  static_assert(std::is_same<typename Matrix_TypeA::Scalar, typename Matrix_TypeB::Scalar>::value,
64  "[angle_utils::dampedPseduoInverse] Matrix scalars does not match!");
65  static_assert(rowsA == colsB && colsA == rowsB,
66  "[angle_utils::dampedPseduoInverse] Input and Output matrix dimensions does not match!");
67 
68  // Calculate pseudo-inverse pinv_J = V S+ U*
69  Eigen::JacobiSVD<Eigen::Matrix<typename Matrix_TypeA::Scalar, Eigen::Dynamic, Eigen::Dynamic> > svd(_J, Eigen::ComputeThinU | Eigen::ComputeThinV);
70  const Eigen::Matrix<typename Matrix_TypeA::Scalar, rowsA, rankA> U = svd.matrixU();
71  const Eigen::Matrix<typename Matrix_TypeA::Scalar, rankA, 1> SV = svd.singularValues();
72  const Eigen::Matrix<typename Matrix_TypeA::Scalar, colsA, rankA> V = svd.matrixV();
73 
74  // Calculate damped singular-values
75  Eigen::Matrix<typename Matrix_TypeA::Scalar, rankA, 1> damped_inv_SV;
76  for (size_t i = 0; i < rankA; ++i)
77  {
78  if (std::fabs(SV(i) > _epsilon))
79  {
80  damped_inv_SV(i) = 1 / SV(i);
81  }
82  else
83  {
84  damped_inv_SV(i) = SV(i) / ( std::pow(SV(i),2) + std::pow(_lambda, 2) );
85  }
86  }
87 
88  // Calculate damped pseudo-inverse
89  _dPinvJ = V * damped_inv_SV.asDiagonal() * U.transpose();
90 
91  return true;
92  }
93 
103  template<typename Matrix_TypeA, typename Matrix_TypeB>
104  bool static nullSpaceProjector(const Matrix_TypeA &_A,
105  Matrix_TypeB &_N,
106  const double _alpha = 1)
107  {
108  constexpr auto rowsA = static_cast<int>(Matrix_TypeA::RowsAtCompileTime);
109  constexpr auto colsA = static_cast<int>(Matrix_TypeA::ColsAtCompileTime);
110  constexpr auto rowsB = static_cast<int>(Matrix_TypeB::RowsAtCompileTime);
111  constexpr auto colsB = static_cast<int>(Matrix_TypeB::ColsAtCompileTime);
112 
113  // Validate matrices
114  static_assert(std::is_same<typename Matrix_TypeA::Scalar, typename Matrix_TypeB::Scalar>::value,
115  "[angle_utils::nullSpaceProjector] Matrix scalars does not match!");
116  static_assert(colsA == rowsB && colsA == colsB,
117  "[angle_utils::nullSpaceProjector] Input and Output matrix dimensions does not match!");
118 
119  // Calculate pseudo-inverse of A
120  Eigen::Matrix<typename Matrix_TypeA::Scalar, colsA, rowsA> pinvA;
121 
122  kindr::pseudoInverse(_A, pinvA);
123 
124  // Set identity matrix
125  Eigen::Matrix<typename Matrix_TypeA::Scalar, colsA, colsA> I = Eigen::Matrix<typename Matrix_TypeA::Scalar, colsA, colsA>::Identity();
126 
127  // Caculate null-space projector
128  _N = I - _alpha * pinvA * _A;
129 
130  return true;
131  }
132 
143  template<typename Matrix_TypeA>
144  Eigen::Matrix<typename Matrix_TypeA::Scalar, Eigen::Dynamic, Eigen::Dynamic> SVDNullSpaceProjector(const Matrix_TypeA &_A)
145  {
146  // Null-space projector
147  Eigen::Matrix<typename Matrix_TypeA::Scalar, Eigen::Dynamic, Eigen::Dynamic> N;
148 
149  // Dimensions
150  const auto rowsA = _A.rows();
151  const auto colsA = _A.cols();
152 
153  // Jacobi SVD
154  Eigen::JacobiSVD< Eigen::Matrix<typename Matrix_TypeA::Scalar, Eigen::Dynamic, Eigen::Dynamic> > svd(_A, Eigen::ComputeThinU | Eigen::ComputeFullV);
155 
156  // Rank of input matrix
157  const auto rankA = svd.rank();
158 
159  // Validate
160  if (rankA == colsA)
161  {
162  N.resize(colsA, 1);
163  N.setZero();
164  }
165  else
166  {
167  N.resize(colsA, colsA - rankA);
168  N = svd.matrixV().rightCols(colsA - rankA);
169  }
170 
171  return N;
172  }
173 
179  template<typename Vector_TypeA, typename Vector_TypeB>
180  Eigen::Matrix<typename Vector_TypeA::Scalar, Eigen::Dynamic, 1> boxMinus(const Vector_TypeA &_a, const Vector_TypeB &_b)
181  {
182  // Dimensions
183  constexpr auto rowsA = static_cast<int>(Vector_TypeA::RowsAtCompileTime);
184  constexpr auto colsA = static_cast<int>(Vector_TypeA::ColsAtCompileTime);
185  constexpr auto rowsB = static_cast<int>(Vector_TypeB::RowsAtCompileTime);
186  constexpr auto colsB = static_cast<int>(Vector_TypeB::ColsAtCompileTime);
187 
188  // Validate vectors
189  static_assert(std::is_same<typename Vector_TypeA::Scalar, typename Vector_TypeB::Scalar>::value,
190  "[math_utils::boxMinus] Vectors must be of the same Scalar type!");
191  static_assert(rowsA == rowsB && colsA == 1 && colsB == 1, "[math_utils::boxMinus] Inputs has wrong size!");
192 
193  // Return vector
194  Eigen::Matrix<typename Vector_TypeA::Scalar, rowsA, 1> res;
195 
196  // Fill result
197  for (size_t i = 0; i < rowsA; i++)
198  {
199  res(i) = std::log( _a(i) / _b(i) );
200  }
201 
202  return res;
203  }
204 
205 } // namespace math_utils
Eigen::Matrix< typename Vector_TypeA::Scalar, Eigen::Dynamic, 1 > boxMinus(const Vector_TypeA &_a, const Vector_TypeB &_b)
The boxMinus function calculates the box-minus operation between two vectors of equal size.
Definition: linalg_utils.h:180
Eigen::Matrix< typename Matrix_TypeA::Scalar, Eigen::Dynamic, Eigen::Dynamic > SVDNullSpaceProjector(const Matrix_TypeA &_A)
The SVDNullSpaceProjector function calculates the null-space projector of a given matrix....
Definition: linalg_utils.h:144
static bool nullSpaceProjector(const Matrix_TypeA &_A, Matrix_TypeB &_N, const double _alpha=1)
The nullSpaceProjector function calculates the null-space projector of a given matrix....
Definition: linalg_utils.h:104
static bool dampedPseudoInverse(const Matrix_TypeA &_J, Matrix_TypeB &_dPinvJ, const double _lambda=0, const double _epsilon=std::numeric_limits< typename Matrix_TypeA::Scalar >::epsilon())
The dampedPseudoInverse function calculates the damped pseudo-inverse of a matrix using SVD.
Definition: linalg_utils.h:50