Line | Branch | Exec | Source |
---|---|---|---|
1 | // This file is part of INSTINCT, the INS Toolkit for Integrated | ||
2 | // Navigation Concepts and Training by the Institute of Navigation of | ||
3 | // the University of Stuttgart, Germany. | ||
4 | // | ||
5 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
6 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
7 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
8 | |||
9 | /// @file Math.hpp | ||
10 | /// @brief Simple Math functions | ||
11 | /// @author T. Topp (topp@ins.uni-stuttgart.de) | ||
12 | /// @author N. Stahl (HiWi: Elliptical integral) | ||
13 | /// @date 2023-07-04 | ||
14 | |||
15 | #pragma once | ||
16 | |||
17 | #include <concepts> | ||
18 | #include <cstdint> | ||
19 | #include <optional> | ||
20 | #include <type_traits> | ||
21 | #include <Eigen/Core> | ||
22 | #include <Eigen/Dense> | ||
23 | #include <gcem.hpp> | ||
24 | #include <fmt/format.h> | ||
25 | |||
26 | #include "util/Assert.h" | ||
27 | |||
28 | namespace NAV::math | ||
29 | { | ||
30 | |||
31 | /// @brief Calculates the factorial of an unsigned integer | ||
32 | /// @param[in] n Unsigned integer | ||
33 | /// @return The factorial of 'n' | ||
34 | uint64_t factorial(uint64_t n); | ||
35 | |||
36 | /// @brief Round the number to the specified amount of digits | ||
37 | /// @param[in] value Value to round | ||
38 | /// @param[in] digits Amount of digits | ||
39 | /// @return The rounded value | ||
40 | template<std::floating_point T> | ||
41 | 372830 | constexpr T round(const T& value, size_t digits) | |
42 | { | ||
43 | 372830 | auto factor = std::pow(10, digits); | |
44 | 372830 | return std::round(value * factor) / factor; | |
45 | } | ||
46 | |||
47 | /// @brief Round the number to the specified amount of significant digits | ||
48 | /// @param[in] value Value to round | ||
49 | /// @param[in] digits Amount of digits | ||
50 | /// @return The rounded value | ||
51 | template<std::floating_point T> | ||
52 | 40 | constexpr T roundSignificantDigits(T value, size_t digits) | |
53 | { | ||
54 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 39 times.
|
40 | if (value == 0.0) { return 0.0; } |
55 | // LOG_DEBUG("value = {:.13e} --> Round to {} digits", value, digits); | ||
56 | 39 | auto absVal = gcem::abs(value); | |
57 | 39 | auto log10 = static_cast<int32_t>(gcem::log10(absVal)); | |
58 |
6/6✓ Branch 0 taken 33 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 15 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 12 times.
|
39 | auto exp = log10 + (log10 > 0 || (log10 == 0 && absVal >= 1.0)); |
59 | 39 | auto fac = static_cast<T>(digits) - static_cast<T>(exp); | |
60 | // LOG_DEBUG(" log10 = {}, exp = {}, fac = {}", log10, exp, fac); | ||
61 | 39 | auto factor = static_cast<T>(gcem::pow(10.0, fac)); | |
62 | // LOG_DEBUG(" factor = {:.0e} --> value * factor = {}", factor, value * factor); | ||
63 | // LOG_DEBUG(" round = {} --> ... / factor = {}", gcem::round(value * factor), gcem::round(value * factor) / factor); | ||
64 | 39 | return static_cast<T>(gcem::round(value * factor) / factor); | |
65 | } | ||
66 | |||
67 | /// @brief Interprets the input integer with certain amount of Bits as Output type. Takes care of sign extension | ||
68 | /// @tparam Out Output type | ||
69 | /// @tparam Bits Size of the input data | ||
70 | /// @tparam In Input data type (needs to be bigger than the amount of Bits) | ||
71 | /// @param[in] in Number as two's complement, with the sign bit (+ or -) occupying the MSB | ||
72 | /// @return Output type | ||
73 | template<std::integral Out, size_t Bits, std::integral In> | ||
74 | 3099 | constexpr Out interpretAs(In in) | |
75 | { | ||
76 | static_assert(Bits < sizeof(In) * 8); | ||
77 | static_assert(Bits < sizeof(Out) * 8); | ||
78 | |||
79 | 3099 | constexpr size_t N = sizeof(Out) * 8 - Bits; | |
80 | 3099 | return static_cast<Out>(static_cast<Out>((in & static_cast<In>(gcem::pow(2, Bits) - 1)) << N) >> N); | |
81 | } | ||
82 | |||
83 | /// @brief Calculates the skew symmetric matrix of the given vector. | ||
84 | /// This is needed to perform the cross product with a scalar product operation | ||
85 | /// @tparam Derived Derived Eigen Type | ||
86 | /// @param[in] a The vector | ||
87 | /// @return Skew symmetric matrix | ||
88 | /// @note See Groves (2013) equation (2.50) | ||
89 | template<typename Derived> | ||
90 | 2983507 | Eigen::Matrix<typename Derived::Scalar, 3, 3> skewSymmetricMatrix(const Eigen::MatrixBase<Derived>& a) | |
91 | { | ||
92 |
1/2✓ Branch 1 taken 2928057 times.
✗ Branch 2 not taken.
|
2983507 | INS_ASSERT_USER_ERROR(a.cols() == 1, "Given Eigen Object must be a vector"); |
93 |
1/2✓ Branch 1 taken 2928848 times.
✗ Branch 2 not taken.
|
2981683 | INS_ASSERT_USER_ERROR(a.rows() == 3, "Given Vector must have 3 Rows"); |
94 | |||
95 | 2982474 | Eigen::Matrix<typename Derived::Scalar, 3, 3> skewMat; | |
96 |
5/10✓ Branch 1 taken 2926234 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2924313 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2928088 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2926590 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2930030 times.
✗ Branch 14 not taken.
|
2977983 | skewMat << 0, -a(2), a(1), |
97 |
5/10✓ Branch 1 taken 2927148 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2931628 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2930533 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2927784 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2931099 times.
✗ Branch 14 not taken.
|
2983656 | a(2), 0, -a(0), |
98 |
5/10✓ Branch 1 taken 2928573 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2931724 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2929457 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2932529 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2932283 times.
✗ Branch 14 not taken.
|
2984725 | -a(1), a(0), 0; |
99 | |||
100 | 2985034 | return skewMat; | |
101 | } | ||
102 | |||
103 | /// @brief Calculates the square of a skew symmetric matrix of the given vector. | ||
104 | /// @tparam Derived Derived Eigen Type | ||
105 | /// @param[in] a The vector | ||
106 | /// @return Square of skew symmetric matrix | ||
107 | template<typename Derived> | ||
108 | ✗ | Eigen::Matrix<typename Derived::Scalar, 3, 3> skewSymmetricMatrixSquared(const Eigen::MatrixBase<Derived>& a) | |
109 | { | ||
110 | ✗ | INS_ASSERT_USER_ERROR(a.cols() == 1, "Given Eigen Object must be a vector"); | |
111 | ✗ | INS_ASSERT_USER_ERROR(a.rows() == 3, "Given Vector must have 3 Rows"); | |
112 | |||
113 | ✗ | Eigen::Matrix<typename Derived::Scalar, 3, 3> skewMat2; | |
114 | ✗ | skewMat2 << std::pow(a(2), 2) + std::pow(a(1), 2), a(0) * a(1), a(0) * a(2), | |
115 | ✗ | a(0) * a(1), std::pow(a(2), 2) + std::pow(a(0), 2), a(1) * a(2), | |
116 | ✗ | a(0) * a(2), a(1) * a(2), std::pow(a(0), 2) + std::pow(a(1), 2); | |
117 | |||
118 | ✗ | return skewMat2; | |
119 | } | ||
120 | |||
121 | /// @brief Calculates the matrix exponential map of the given vector. | ||
122 | /// @tparam Derived Derived Eigen Type | ||
123 | /// @param[in] v The vector | ||
124 | /// @return The matrix exponential map | ||
125 | template<typename Derived> | ||
126 | Eigen::Matrix<typename Derived::Scalar, 3, 3> expMapMatrix(const Eigen::MatrixBase<Derived>& v) | ||
127 | { | ||
128 | INS_ASSERT_USER_ERROR(v.cols() == 1, "Given Eigen Object must be a vector"); | ||
129 | INS_ASSERT_USER_ERROR(v.rows() == 3, "Given Vector must have 3 Rows"); | ||
130 | |||
131 | return math::skewSymmetricMatrix(v).exp(); | ||
132 | } | ||
133 | |||
134 | /// @brief Calculates the quaternionic exponential map of the given vector. | ||
135 | /// @tparam Derived Derived Eigen Type | ||
136 | /// @param[in] v The vector | ||
137 | /// @return The quaternionic exponential map | ||
138 | template<typename Derived> | ||
139 | 299982 | Eigen::Quaternion<typename Derived::Scalar> expMapQuat(const Eigen::MatrixBase<Derived>& v) | |
140 | { | ||
141 |
1/2✓ Branch 1 taken 149991 times.
✗ Branch 2 not taken.
|
299982 | INS_ASSERT_USER_ERROR(v.cols() == 1, "Given Eigen Object must be a vector"); |
142 |
1/2✓ Branch 1 taken 149991 times.
✗ Branch 2 not taken.
|
299982 | INS_ASSERT_USER_ERROR(v.rows() == 3, "Given Vector must have 3 Rows"); |
143 | |||
144 |
2/4✓ Branch 1 taken 149991 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 149991 times.
✗ Branch 5 not taken.
|
299982 | Eigen::Vector3<typename Derived::Scalar> omega = 0.5 * v; |
145 |
1/2✓ Branch 1 taken 149991 times.
✗ Branch 2 not taken.
|
299982 | auto omegaNorm = omega.norm(); |
146 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 149991 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
299982 | if (omegaNorm < 1e-9) { return Eigen::Quaternion<typename Derived::Scalar>::Identity(); } |
147 |
3/6✓ Branch 1 taken 149991 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 149991 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 149991 times.
✗ Branch 8 not taken.
|
299982 | Eigen::Vector3<typename Derived::Scalar> quatVec = omega / omegaNorm * std::sin(omegaNorm); |
148 | |||
149 |
4/8✓ Branch 1 taken 149991 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 149991 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 149991 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 149991 times.
✗ Branch 11 not taken.
|
299982 | return { std::cos(omegaNorm), quatVec.x(), quatVec.y(), quatVec.z() }; |
150 | } | ||
151 | |||
152 | /// @brief Calculates the right Jacobian of SO(3) which relates additive increments in the tangent space to multiplicative increments applied on the right-hand side | ||
153 | /// @param[in] phi Vector applied on the right side | ||
154 | /// @return Right Jacobian J_r | ||
155 | template<typename Derived> | ||
156 | ✗ | [[nodiscard]] Eigen::Matrix3<typename Derived::Scalar> J_r(const Eigen::MatrixBase<Derived>& phi) | |
157 | { | ||
158 | ✗ | INS_ASSERT_USER_ERROR(phi.cols() == 1, "Given Eigen Object must be a vector"); | |
159 | ✗ | INS_ASSERT_USER_ERROR(phi.rows() == 3, "Given Vector must have 3 Rows"); | |
160 | |||
161 | ✗ | auto phiNorm = phi.norm(); | |
162 | ✗ | auto phiNorm2 = phiNorm * phiNorm; | |
163 | ✗ | auto phiNorm3 = phiNorm2 * phiNorm; | |
164 | return Eigen::Matrix3<typename Derived::Scalar>::Identity() | ||
165 | ✗ | - (1.0 - std::cos(phiNorm)) / phiNorm2 * skewSymmetricMatrix(phi) | |
166 | ✗ | + (phiNorm - std::sin(phiNorm)) / phiNorm3 * skewSymmetricMatrixSquared(phi); | |
167 | } | ||
168 | |||
169 | /// @brief Calculates the secant of a value (sec(x) = csc(pi/2 - x) = 1 / cos(x)) | ||
170 | template<std::floating_point T> | ||
171 | ✗ | T sec(const T& x) | |
172 | { | ||
173 | ✗ | return 1.0 / std::cos(x); | |
174 | } | ||
175 | |||
176 | /// @brief Calculates the cosecant of a value (csc(x) = sec(pi/2 - x) = 1 / sin(x)) | ||
177 | template<std::floating_point T> | ||
178 | 156269 | T csc(const T& x) | |
179 | { | ||
180 | 156269 | return 1.0 / std::sin(x); | |
181 | } | ||
182 | |||
183 | /// @brief Returns the sign of the given value | ||
184 | /// @param[in] val Value to get the sign from | ||
185 | /// @return Sign of the given value | ||
186 | template<typename T> | ||
187 | 1343753 | int sgn(const T& val) | |
188 | { | ||
189 | 1343753 | return (T(0) < val) - (val < T(0)); | |
190 | } | ||
191 | |||
192 | /// @brief Calculates the state transition matrix 𝚽 limited to specified order in 𝐅𝜏ₛ | ||
193 | /// @param[in] X Matrix | ||
194 | /// @param[in] order The order of the Taylor polynom to calculate | ||
195 | /// @note See \cite Groves2013 Groves, ch. 3.2.3, eq. 3.34, p. 98 | ||
196 | template<typename Derived> | ||
197 | typename Derived::PlainObject expm(const Eigen::MatrixBase<Derived>& X, size_t order) | ||
198 | { | ||
199 | INS_ASSERT_USER_ERROR(X.rows() == X.cols(), "Matrix exponential calculation only possible for n x n matrices"); | ||
200 | |||
201 | typename Derived::PlainObject e_X; | ||
202 | |||
203 | if constexpr (Derived::RowsAtCompileTime == Eigen::Dynamic) | ||
204 | { | ||
205 | e_X = Eigen::MatrixBase<Derived>::Identity(X.rows(), X.cols()); | ||
206 | } | ||
207 | else | ||
208 | { | ||
209 | e_X = Eigen::MatrixBase<Derived>::Identity(); | ||
210 | } | ||
211 | typename Derived::PlainObject Xpower = X; | ||
212 | for (size_t i = 1; i <= order; i++) | ||
213 | { | ||
214 | e_X += Xpower / static_cast<double>(math::factorial(i)); | ||
215 | |||
216 | if (i < order) | ||
217 | { | ||
218 | Xpower *= X; | ||
219 | } | ||
220 | } | ||
221 | |||
222 | return e_X; | ||
223 | } | ||
224 | |||
225 | /// @brief Find (L^T D L)-decomposition of Q-matrix via outer product method | ||
226 | /// @param[in] Qmatrix Symmetric positive definite matrix to be factored | ||
227 | /// @return L - Factor matrix (strict lower triangular) | ||
228 | /// @return D - Vector with entries of the diagonal matrix | ||
229 | /// @note See \cite deJonge1996 de Jonge 1996, Algorithm FMFAC5 | ||
230 | /// @attention Consider using NAV::math::LtDLdecomp_choleskyFact() because it is faster by up to a factor 10 | ||
231 | template<typename Derived> | ||
232 | std::optional<std::pair<Eigen::Matrix<typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime>, | ||
233 | Eigen::Vector<typename Derived::Scalar, Derived::RowsAtCompileTime>>> | ||
234 | 1 | LtDLdecomp_outerProduct(const Eigen::MatrixBase<Derived>& Qmatrix) | |
235 | { | ||
236 | using Eigen::seq; | ||
237 | |||
238 | 1 | auto n = Qmatrix.rows(); | |
239 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | Eigen::Matrix<typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime> Q = Qmatrix.template triangularView<Eigen::Lower>(); |
240 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | Eigen::Matrix<typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime> L; |
241 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | Eigen::Vector<typename Derived::Scalar, Derived::RowsAtCompileTime> D; |
242 | |||
243 | if constexpr (Derived::RowsAtCompileTime == Eigen::Dynamic) | ||
244 | { | ||
245 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | L = Eigen::Matrix<typename Derived::Scalar, Eigen::Dynamic, Eigen::Dynamic>::Zero(n, n); |
246 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | D.setZero(n); |
247 | } | ||
248 | else | ||
249 | { | ||
250 | L = Eigen::Matrix<typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime>::Zero(); | ||
251 | D.setZero(); | ||
252 | } | ||
253 | |||
254 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
|
4 | for (Eigen::Index i = n - 1; i >= 0; i--) |
255 | { | ||
256 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
3 | D(i) = Q(i, i); |
257 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
|
3 | if (Q(i, i) <= 0.0) { return {}; } |
258 |
7/14✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 3 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 3 times.
✗ Branch 20 not taken.
|
3 | L(i, seq(0, i)) = Q(i, seq(0, i)) / std::sqrt(Q(i, i)); |
259 | |||
260 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
|
6 | for (Eigen::Index j = 0; j <= i - 1; j++) |
261 | { | ||
262 |
7/14✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 3 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 3 times.
✗ Branch 20 not taken.
|
3 | Q(j, seq(0, j)) -= L(i, seq(0, j)) * L(i, j); |
263 | } | ||
264 |
4/8✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
|
3 | L(i, seq(0, i)) /= L(i, i); |
265 | } | ||
266 | |||
267 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | return std::make_pair(L, D); |
268 | 1 | } | |
269 | |||
270 | /// @brief Find (L^T D L)-decomposition of Q-matrix via a backward Cholesky factorization in a bordering method formulation | ||
271 | /// @param[in] Q Symmetric positive definite matrix to be factored | ||
272 | /// @return L - Factor matrix (strict lower triangular) | ||
273 | /// @return D - Vector with entries of the diagonal matrix | ||
274 | /// @note See \cite deJonge1996 de Jonge 1996, Algorithm FMFAC6 | ||
275 | template<typename Derived> | ||
276 | std::optional<std::pair<Eigen::Matrix<typename Derived::Scalar, Derived::RowsAtCompileTime, Derived::ColsAtCompileTime>, | ||
277 | Eigen::Vector<typename Derived::Scalar, Derived::RowsAtCompileTime>>> | ||
278 | 2423 | LtDLdecomp_choleskyFact(const Eigen::MatrixBase<Derived>& Q) | |
279 | { | ||
280 | using Eigen::seq; | ||
281 | |||
282 | 2423 | auto n = Q.rows(); | |
283 |
2/4✓ Branch 1 taken 1213 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1213 times.
✗ Branch 5 not taken.
|
2423 | typename Derived::PlainObject L = Q.template triangularView<Eigen::Lower>(); |
284 |
1/2✓ Branch 1 taken 1213 times.
✗ Branch 2 not taken.
|
2423 | Eigen::Vector<typename Derived::Scalar, Derived::RowsAtCompileTime> D; |
285 | 2423 | double cmin = 1; | |
286 | |||
287 |
1/2✓ Branch 1 taken 1206 times.
✗ Branch 2 not taken.
|
2409 | if constexpr (Derived::RowsAtCompileTime == Eigen::Dynamic) { D.setZero(n); } |
288 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
14 | else { D.setZero(); } |
289 | |||
290 |
2/2✓ Branch 0 taken 32377 times.
✓ Branch 1 taken 1213 times.
|
67060 | for (Eigen::Index j = n - 1; j >= 0; j--) |
291 | { | ||
292 |
2/2✓ Branch 0 taken 448444 times.
✓ Branch 1 taken 32377 times.
|
959299 | for (Eigen::Index i = n - 1; i >= j + 1; i--) |
293 | { | ||
294 |
8/16✓ Branch 1 taken 448444 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 448444 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 448444 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 448444 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 448444 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 448444 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 448444 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 448444 times.
✗ Branch 23 not taken.
|
894662 | L(i, j) = (L(i, j) - L(seq(i + 1, n - 1), j).dot(L(seq(i + 1, n - 1), i))) / L(i, i); |
295 | } | ||
296 |
6/12✓ Branch 1 taken 32377 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 32377 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 32377 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 32377 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 32377 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 32377 times.
✗ Branch 17 not taken.
|
64637 | double t = L(j, j) - L(seq(j + 1, n - 1), j).dot(L(seq(j + 1, n - 1), j)); |
297 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32377 times.
|
64637 | if (t <= 0.0) { return {}; } |
298 |
1/2✓ Branch 1 taken 32377 times.
✗ Branch 2 not taken.
|
64637 | double c = t / L(j, j); |
299 | 64637 | cmin = std::min(c, cmin); | |
300 |
1/2✓ Branch 1 taken 32377 times.
✗ Branch 2 not taken.
|
64637 | L(j, j) = std::sqrt(t); |
301 | } | ||
302 |
2/2✓ Branch 0 taken 32377 times.
✓ Branch 1 taken 1213 times.
|
67060 | for (Eigen::Index i = 0; i < n; i++) |
303 | { | ||
304 |
4/8✓ Branch 1 taken 32377 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 32377 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 32377 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 32377 times.
✗ Branch 11 not taken.
|
64637 | L.row(i).leftCols(i) /= L(i, i); |
305 |
2/4✓ Branch 1 taken 32377 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 32377 times.
✗ Branch 5 not taken.
|
64637 | D(i) = std::pow(L(i, i), 2.0); |
306 |
1/2✓ Branch 1 taken 32377 times.
✗ Branch 2 not taken.
|
64637 | L(i, i) = 1; |
307 | } | ||
308 | |||
309 |
1/2✓ Branch 1 taken 1213 times.
✗ Branch 2 not taken.
|
2423 | return std::make_pair(L, D); |
310 | 2409 | } | |
311 | |||
312 | /// @brief Calculates the squared norm of the vector and matrix | ||
313 | /// | ||
314 | /// \anchor eq-squaredNorm \f{equation}{ \label{eq:eq-squaredNorm} | ||
315 | /// ||\mathbf{\dots}||^2_{\mathbf{Q}} = (\dots)^T \mathbf{Q}^{-1} (\dots) | ||
316 | /// \f} | ||
317 | /// @param a Vector | ||
318 | /// @param Q Covariance matrix of the vector | ||
319 | /// @return Squared norm | ||
320 | template<typename DerivedA, typename DerivedQ> | ||
321 | 8 | typename DerivedA::Scalar squaredNormVectorMatrix(const Eigen::MatrixBase<DerivedA>& a, const Eigen::MatrixBase<DerivedQ>& Q) | |
322 | { | ||
323 | static_assert(DerivedA::ColsAtCompileTime == Eigen::Dynamic || DerivedA::ColsAtCompileTime == 1); | ||
324 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | INS_ASSERT_USER_ERROR(a.cols() == 1, "Parameter 'a' has to be a vector"); |
325 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | INS_ASSERT_USER_ERROR(a.rows() == Q.rows(), "Parameter 'a' and 'Q' need to have same size"); |
326 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | INS_ASSERT_USER_ERROR(Q.cols() == Q.rows(), "Parameter 'Q' needs to be quadratic"); |
327 | |||
328 |
5/10✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 8 times.
✗ Branch 14 not taken.
|
8 | return a.transpose() * Q.inverse() * a; |
329 | } | ||
330 | |||
331 | /// @brief Calculates the cumulative distribution function (CDF) of the standard normal distribution | ||
332 | /// | ||
333 | /// \anchor eq-normalDistCDF \f{equation}{ \label{eq:eq-normalDistCDF} | ||
334 | /// \Phi(x) = \int\displaylimits_{-\infty}^x \frac{1}{\sqrt{2\pi}} \exp{\left(-\frac{1}{2} v^2\right)} \text{d}v | ||
335 | /// \f} | ||
336 | /// which can be expressed with the error function | ||
337 | /// \anchor eq-normalDistCDF-erf \f{equation}{ \label{eq:eq-normalDistCDF-erf} | ||
338 | /// \Phi(x) = \frac{1}{2} \left[ 1 + \text{erf}{\left(\frac{x}{\sqrt{2}}\right)} \right] | ||
339 | /// \f} | ||
340 | /// Using the property | ||
341 | /// \anchor eq-erf-minus \f{equation}{ \label{eq:eq-erf-minus} | ||
342 | /// \text{erf}{\left( -x \right)} = -\text{erf}{\left( x \right)} | ||
343 | /// \f} | ||
344 | /// and the complementary error function | ||
345 | /// \anchor eq-erfc \f{equation}{ \label{eq:eq-erfc} | ||
346 | /// \text{erfc}{\left( x \right)} = 1 - \text{erf}{\left( x \right)} | ||
347 | /// \f} | ||
348 | /// we can simplify equation \eqref{eq-normalDistCDF-erf} to | ||
349 | /// \anchor eq-normalDistCDF-erfc \f{equation}{ \label{eq:eq-normalDistCDF-erfc} | ||
350 | /// \begin{aligned} | ||
351 | /// \Phi(x) &= \frac{1}{2} \left[ 1 - \text{erf}{\left(-\frac{x}{\sqrt{2}}\right)} \right] \\ | ||
352 | /// &= \frac{1}{2} \text{erfc}{\left(-\frac{x}{\sqrt{2}}\right)} | ||
353 | /// \end{aligned} | ||
354 | /// \f} | ||
355 | /// | ||
356 | /// @param value Value to calculate the CDF for | ||
357 | double normalCDF(double value); | ||
358 | |||
359 | /// @brief Returns the inverse square root of a matrix | ||
360 | /// @param matrix Matrix to use | ||
361 | template<typename Derived> | ||
362 | [[nodiscard]] typename Derived::PlainObject inverseSqrt(const Eigen::MatrixBase<Derived>& matrix) | ||
363 | { | ||
364 | INS_ASSERT_USER_ERROR(matrix.rows() == matrix.cols(), "Only square matrix supported"); | ||
365 | if constexpr (std::is_floating_point_v<typename Derived::Scalar>) | ||
366 | { | ||
367 | return matrix.inverse().sqrt(); // Eigen::SelfAdjointEigenSolver<Eigen::MatrixX<T>>{ covMatrix }.operatorInverseSqrt(); | ||
368 | } | ||
369 | else // Eigen gets problems with ceres::Jet in the .sqrt() function | ||
370 | { | ||
371 | Eigen::JacobiSVD<Eigen::MatrixX<typename Derived::Scalar>> svd(matrix.inverse(), Eigen::ComputeFullV); | ||
372 | typename Derived::PlainObject sqrtInverse = svd.matrixV() * svd.singularValues().cwiseSqrt().asDiagonal() * svd.matrixV().transpose(); | ||
373 | INS_ASSERT_USER_ERROR(!sqrtInverse.hasNaN(), "The matrix is not invertible"); | ||
374 | return sqrtInverse; | ||
375 | } | ||
376 | } | ||
377 | |||
378 | /// @brief Change the sign of x according to the value of y | ||
379 | /// @param[in] x input value | ||
380 | /// @param[in] y input value | ||
381 | /// @return -x or +x | ||
382 | template<typename T> | ||
383 | 2612 | T sign(const T& x, const T& y) | |
384 | { | ||
385 | // similar function 'sign' in fortran | ||
386 |
2/2✓ Branch 0 taken 498 times.
✓ Branch 1 taken 2114 times.
|
2612 | if (y >= 0.0) |
387 | { | ||
388 | 498 | return fabs(x); | |
389 | } | ||
390 | 2114 | return -1.0 * fabs(x); | |
391 | } | ||
392 | |||
393 | /// @brief Linear interpolation between vectors | ||
394 | /// @param a Left value | ||
395 | /// @param b Right value | ||
396 | /// @param t Multiplier. [0, 1] for interpolation | ||
397 | /// @return a + t * (b - a) | ||
398 | template<typename Derived> | ||
399 | ✗ | typename Derived::PlainObject lerp(const Eigen::MatrixBase<Derived>& a, const Eigen::MatrixBase<Derived>& b, auto t) | |
400 | { | ||
401 | ✗ | return a + t * (b - a); | |
402 | } | ||
403 | |||
404 | /// Lerp Search Result | ||
405 | struct LerpSearchResult | ||
406 | { | ||
407 | size_t l; ///< Lower bound index | ||
408 | size_t u; ///< Upper bound index (l + 1) | ||
409 | double t; ///< [0, 1] for Interpolation, otherwise Extrapolation | ||
410 | }; | ||
411 | |||
412 | /// @brief Searches the value in the data container | ||
413 | /// @param[in] data Data container | ||
414 | /// @param[in] value Value to search | ||
415 | 3 | LerpSearchResult lerpSearch(const auto& data, const auto& value) | |
416 | { | ||
417 | 3 | auto i = static_cast<size_t>(std::distance(data.begin(), std::upper_bound(data.begin(), data.end(), value))); | |
418 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (i > 0) { i--; } |
419 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (i == data.size() - 1) { i--; } |
420 | 3 | const auto& lb = data.at(i); | |
421 | 3 | const auto& ub = data.at(i + 1); | |
422 | 3 | double t = (value - lb) / (ub - lb); | |
423 | |||
424 | 3 | return { .l = i, .u = i + 1, .t = t }; | |
425 | } | ||
426 | |||
427 | /// @brief Bilinear interpolation | ||
428 | /// @param[in] tx Distance in x component to interpolate [0, 1] | ||
429 | /// @param[in] ty Distance in y component to interpolate [0, 1] | ||
430 | /// @param[in] c00 Value for tx = ty = 0 | ||
431 | /// @param[in] c10 Value for tx = 1 and ty = 0 | ||
432 | /// @param[in] c01 Value for tx = 0 and ty = 1 | ||
433 | /// @param[in] c11 Value for tx = ty = 1 | ||
434 | /// | ||
435 | /// c01 ------ c11 | ||
436 | /// | | | ||
437 | /// | | | ||
438 | /// | | | ||
439 | /// c00 ------ c10 | ||
440 | /// | ||
441 | /// @note See https://www.scratchapixel.com/lessons/mathematics-physics-for-computer-graphics/interpolation/bilinear-filtering.html | ||
442 | 9 | auto bilinearInterpolation(const auto& tx, const auto& ty, | |
443 | const auto& c00, const auto& c10, | ||
444 | const auto& c01, const auto& c11) | ||
445 | { | ||
446 | 9 | auto a = c00 * (1.0 - tx) + c10 * tx; | |
447 | 9 | auto b = c01 * (1.0 - tx) + c11 * tx; | |
448 | 9 | return a * (1.0 - ty) + b * ty; | |
449 | // Alternative implementation: | ||
450 | // return (1.0 - tx) * (1.0 - ty) * c00 + tx * (1.0 - ty) * c10 + (1.0 - tx) * ty * c01 + tx * ty * c11; | ||
451 | } | ||
452 | |||
453 | /// @brief Calculates the incomplete elliptical integral of the second kind | ||
454 | /// @param[in] phi Interval bound the integration uses from 0 to phi | ||
455 | /// @param[in] m Function parameter that is integrated 1-m*sin(t)^2 | ||
456 | /// @return Incomplete elliptical integral of the second kind | ||
457 | /// @note See http://www2.iap.fr/users/pichon/doc/html_xref/elliptic-es.html | ||
458 | double calcEllipticalIntegral(double phi, double m); | ||
459 | |||
460 | } // namespace NAV::math | ||
461 |