INSTINCT Code Coverage Report


Directory: src/
File: Navigation/Math/KeyedLeastSquares.hpp
Date: 2025-02-07 16:54:41
Exec Total Coverage
Lines: 13 26 50.0%
Functions: 1 2 50.0%
Branches: 19 66 28.8%

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 KeyedLeastSquares.hpp
10 /// @brief Least squares with keyed states
11 /// @author P. Peitschat (HiWi)
12 /// @author T. Topp (topp@ins.uni-stuttgart.de)
13 /// @date 2023-09-26
14
15 #pragma once
16
17 #include "util/Container/KeyedMatrix.hpp"
18
19 namespace NAV
20 {
21 /// @brief Least Squares Uncertainties return value
22 template<typename Scalar, typename StateKeyType>
23 struct KeyedLeastSquaresResult
24 {
25 KeyedVectorX<Scalar, StateKeyType> solution; ///< Least squares solution
26 KeyedMatrixX<Scalar, StateKeyType, StateKeyType> variance; ///< Least squares variance
27 };
28
29 /// @brief Finds the "least squares" solution for the equation \f$ \mathbf{v} = \mathbf{dz} - \mathbf{H} \mathbf{x} \f$
30 ///
31 /// Minimizes the functional (see LeastSquares.hpp)
32 /// @param[in] H Design Matrix
33 /// @param[in] dz Residual vector
34 /// @return Least squares solution
35 template<typename Scalar, typename StateKeyType, typename MeasKeyType>
36 KeyedVectorX<Scalar, StateKeyType> solveLinearLeastSquares(const KeyedMatrixX<Scalar, MeasKeyType, StateKeyType>& H, const KeyedVectorX<Scalar, MeasKeyType>& dz)
37 {
38 KeyedVectorX<Scalar, StateKeyType> dx = KeyedVectorX<Scalar, StateKeyType>(Eigen::VectorX<Scalar>::Zero(H.cols()), H.colKeys());
39 dx(all) = (H(all, all).transpose() * H(all, all)).inverse() * H(all, all).transpose() * dz(all);
40 return dx;
41 }
42
43 /// @brief Finds the "weighted least squares" solution (see LeastSquares.hpp)
44 /// @param[in] H Design Matrix
45 /// @param[in] W Weight matrix
46 /// @param[in] dz Residual vector
47 /// @return Least squares solution
48 template<typename Scalar, typename StateKeyType, typename MeasKeyType>
49 KeyedVectorX<Scalar, StateKeyType> solveWeightedLinearLeastSquares(const KeyedMatrixX<Scalar, MeasKeyType, StateKeyType>& H, const KeyedMatrixX<Scalar, MeasKeyType, MeasKeyType>& W, const KeyedVectorX<Scalar, MeasKeyType>& dz)
50 {
51 KeyedVectorX<Scalar, StateKeyType> dx = KeyedVectorX<Scalar, StateKeyType>(Eigen::VectorX<Scalar>::Zero(H.cols()), H.colKeys());
52 dx(all) = (H(all, all).transpose() * W(all, all) * H(all, all)).inverse() * H(all, all).transpose() * W(all, all) * dz(all);
53 return dx;
54 }
55
56 /// @brief Finds the "least squares" solution for the equation \f$ \mathbf{v} = \mathbf{dz} - \mathbf{H} \mathbf{x} \f$
57 /// @param[in] H Design Matrix
58 /// @param[in] dz Residual vector
59 /// @return Least squares solution and variance
60 template<typename Scalar, typename StateKeyType, typename MeasKeyType>
61 KeyedLeastSquaresResult<Scalar, StateKeyType>
62 solveLinearLeastSquaresUncertainties(const KeyedMatrixX<Scalar, MeasKeyType, StateKeyType>& H, const KeyedVectorX<Scalar, MeasKeyType>& dz)
63 {
64 // Amount of equations
65 auto m = static_cast<int>(H.rows());
66 // Amount of variables
67 auto n = static_cast<int>(H.cols());
68
69 // Cofactor matrix
70 KeyedMatrixX<Scalar, StateKeyType, StateKeyType> Q = KeyedMatrixX<Scalar, StateKeyType, StateKeyType>(Eigen::MatrixX<Scalar>::Zero(n, n), H.colKeys(), H.colKeys());
71 Q(all, all) = (H(all, all).transpose() * H(all, all)).inverse();
72 LOG_DATA("Q = \n{}", Q(all, all));
73 // Least squares solution
74 KeyedVectorX<Scalar, StateKeyType> dx = KeyedVectorX<Scalar, StateKeyType>(Eigen::VectorX<Scalar>::Zero(n), H.colKeys());
75 dx(all) = Q(all, all) * H(all, all).transpose() * dz(all);
76 LOG_DATA("dx = {}", dx(all).transpose());
77
78 // Residual sum of squares
79 double RSS = std::pow(dz(all).norm(), 2);
80 LOG_DATA("RSS = {}", RSS);
81
82 // Statistical degrees of freedom
83 auto dof = m - n;
84 LOG_DATA("dof = {}", dof);
85
86 // Estimated error variance (reduced chi-squared statistic)
87 double sigma2 = RSS / static_cast<double>(dof);
88 LOG_DATA("sigma2 = {}", sigma2);
89
90 // Covariance matrix
91 Q(all, all) *= sigma2;
92 LOG_DATA("variance = \n{}", Q(all, all));
93
94 return { .solution = dx, .variance = Q };
95 }
96
97 /// @brief Finds the "weighted least squares" solution
98 /// @param[in] H Design Matrix
99 /// @param[in] W Weight matrix
100 /// @param[in] dz Residual vector
101 /// @return Weighted least squares solution and variance
102 template<typename Scalar, typename StateKeyType, typename MeasKeyType>
103 KeyedLeastSquaresResult<Scalar, StateKeyType>
104 930 solveWeightedLinearLeastSquaresUncertainties(const KeyedMatrixX<Scalar, MeasKeyType, StateKeyType>& H, const KeyedMatrixX<Scalar, MeasKeyType, MeasKeyType>& W, const KeyedVectorX<Scalar, MeasKeyType>& dz)
105 {
106 // Amount of equations
107 930 auto m = static_cast<int>(H.rows());
108 // Amount of variables
109 930 auto n = static_cast<int>(H.cols());
110
111 // Cofactor matrix
112
2/4
✓ Branch 3 taken 930 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 930 times.
✗ Branch 7 not taken.
930 KeyedMatrixX<Scalar, StateKeyType, StateKeyType> Q = KeyedMatrixX<Scalar, StateKeyType, StateKeyType>(Eigen::MatrixX<Scalar>::Zero(n, n), H.colKeys(), H.colKeys());
113
5/10
✓ Branch 4 taken 930 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 930 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 930 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 930 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 930 times.
✗ Branch 18 not taken.
930 Q(all, all) = (H(all, all).transpose() * W(all, all) * H(all, all)).inverse();
114 LOG_DATA("Q = \n{}", Q(all, all));
115
116 // Least squares solution
117
2/4
✓ Branch 2 taken 930 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 930 times.
✗ Branch 6 not taken.
930 KeyedVectorX<Scalar, StateKeyType> dx = KeyedVectorX<Scalar, StateKeyType>(Eigen::VectorX<Scalar>::Zero(n), H.colKeys());
118
5/10
✓ Branch 4 taken 930 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 930 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 930 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 930 times.
✗ Branch 15 not taken.
✓ Branch 18 taken 930 times.
✗ Branch 19 not taken.
930 dx(all) = Q(all, all) * H(all, all).transpose() * W(all, all) * dz(all);
119 LOG_DATA("dx = {}", dx(all).transpose());
120
121 // Residual sum of squares
122
4/8
✓ Branch 4 taken 930 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 930 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 930 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 930 times.
✗ Branch 14 not taken.
930 double RSS = dz(all).transpose() * W(all, all) * dz(all);
123 LOG_DATA("RSS = {}", RSS);
124
125 // Statistical degrees of freedom
126 930 auto dof = m - n;
127 LOG_DATA("dof = {}", dof);
128
129 // Estimated error variance (reduced chi-squared statistic)
130 930 double sigma2 = RSS / static_cast<double>(dof);
131 LOG_DATA("sigma2 = {}", sigma2);
132
133 // Covariance matrix
134
1/2
✓ Branch 2 taken 930 times.
✗ Branch 3 not taken.
930 Q(all, all) *= sigma2;
135 LOG_DATA("Covariance matrix = \n{}", Q(all, all));
136
137 930 return { .solution = dx, .variance = Q };
138 930 }
139
140 } // namespace NAV
141