LCOV - code coverage report
Current view: top level - math/linalg - covariance_matrix_factored.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 60.0 % 10 6
Test Date: 2026-04-26 21:52:20 Functions: 0.0 % 19 0
Legend: Lines: hit not hit

            Line data    Source code
       1              : #ifndef F9044FD7_A3A8_43F4_BDD6_F43011384722
       2              : #define F9044FD7_A3A8_43F4_BDD6_F43011384722
       3              : 
       4              : #include "base/first_include.h"                           // IWYU pragma: keep
       5              : #include "math/linalg/contracts/covariance_matrix_intf.h" // IWYU pragma: keep
       6              : #include <initializer_list>
       7              : #include <iostream> // IWYU pragma: keep
       8              : 
       9              : namespace tracking
      10              : {
      11              : namespace math
      12              : {
      13              : 
      14              : template <typename ValueType_, sint32 Rows_, sint32 Cols_, bool IsRowMajor_>
      15              : class Matrix;
      16              : 
      17              : template <typename ValueType_, sint32 Size_>
      18              : class DiagonalMatrix;
      19              : 
      20              : template <typename ValueType_, sint32 Size_, bool IsRowMajor_>
      21              : class SquareMatrix;
      22              : 
      23              : template <typename ValueType_, sint32 Size_, bool IsLower_, bool IsRowMajor_>
      24              : class TriangularMatrix;
      25              : 
      26              : template <typename ValueType_, sint32 Size_>
      27              : class Vector;
      28              : 
      29              : template <typename ValueType_, sint32 Size_>
      30              : class CovarianceMatrixFull;
      31              : 
      32              : /// \brief UDU factored covariance matrix for numerical stability
      33              : ///
      34              : /// This class implements the UDU^T factorization of covariance matrixes, where
      35              : /// P = U*D*U^T. Here U is a unit upper triangular matrix (diagonal elements = 1)
      36              : /// and D is a diagonal matrix containing the diagonal elements of the factorization.
      37              : ///
      38              : /// UDU factorization provides superior numerical stability compared to full matrix
      39              : /// representations, especially for ill-conditioned covariance matrixes. This is
      40              : /// particularly important in Kalman filtering where covariance matrixes can become
      41              : /// near-singular over time.
      42              : ///
      43              : /// Key algorithms implemented:
      44              : /// - Thornton update: P = Φ*P*Φ^T + G*Q*G^T (prediction step)
      45              : /// - Agee-Turner rank-1 update: P = P ± x*x^T (measurement updates)
      46              : ///
      47              : /// The factorization maintains the symmetry and positive semi-definiteness of
      48              : /// the covariance matrix while avoiding matrix inversion operations.
      49              : ///
      50              : /// \tparam ValueType_ The atomic data type of internal elements
      51              : /// \tparam Size_ Dimension of the square covariance matrix
      52              : ///
      53              : /// \see CovarianceMatrixFull for standard matrix representation
      54              : /// \see Thornton, C. L. "Triangular Covariance Factorizations for Kalman Filtering"
      55              : /// \see Bierman, G. J. "Factorization Methods for Discrete Sequential Estimation"
      56              : ///
      57              : /// \note Based on academic publications by Thornton, Bierman, and D'Souza
      58              : template <typename ValueType_, sint32 Size_>
      59              : class CovarianceMatrixFactored: public contract::CovarianceMatrixIntf<CovarianceMatrixFactored<ValueType_, Size_>>
      60              : {
      61              : public:
      62              :   using value_type          = ValueType_;
      63              :   using compose_type        = CovarianceMatrixFull<ValueType_, Size_>;
      64              :   static constexpr auto dim = Size_;
      65              : 
      66              :   // rule of 5 declarations
      67              :   CovarianceMatrixFactored()                                                       = default;
      68           46 :   CovarianceMatrixFactored(const CovarianceMatrixFactored&)                        = default;
      69           19 :   CovarianceMatrixFactored(CovarianceMatrixFactored&&) noexcept                    = default;
      70              :   auto operator=(const CovarianceMatrixFactored&) -> CovarianceMatrixFactored&     = default;
      71           20 :   auto operator=(CovarianceMatrixFactored&&) noexcept -> CovarianceMatrixFactored& = default;
      72          171 :   virtual ~CovarianceMatrixFactored()                                              = default;
      73              : 
      74              :   //////////////////////////////////////////////////
      75              :   // additional constructors  --->
      76              :   /// \brief Construct a new Covariance Matrix Factored object
      77              :   /// \param[in] u   Unit upper triangular matrix
      78              :   /// \param[in] d   Diagonal matrix
      79              :   explicit CovarianceMatrixFactored(const TriangularMatrix<ValueType_, Size_, false, true>& u,
      80              :                                     const DiagonalMatrix<ValueType_, Size_>&                d);
      81              : 
      82              :   /// \brief Construct an Identity matrix
      83              :   /// \return CovarianceMatrixFactored
      84              :   static auto Identity() -> CovarianceMatrixFactored;
      85              : 
      86              :   /// \brief Construct a diagonal covariance matrix
      87              :   /// \param[in] diag  Diagonal matrix
      88              :   /// \return CovarianceMatrixFactored
      89              :   static auto FromDiagonal(const DiagonalMatrix<ValueType_, Size_>& diag) -> CovarianceMatrixFactored;
      90              : 
      91              :   /// \brief Creates a CovarianceMatrixFactored from separate U and D initializer lists
      92              :   ///
      93              :   /// This function constructs a factored covariance matrix from separate initializer lists
      94              :   /// for the upper triangular U matrix and diagonal D matrix components.
      95              :   ///
      96              :   /// \param[in] u Nested initializer list for the upper triangular U matrix
      97              :   /// \param[in] d Flat initializer list for the diagonal D matrix
      98              :   /// \return CovarianceMatrixFactored instance with the specified U and D components
      99              :   static auto FromList(const std::initializer_list<std::initializer_list<ValueType_>>& u,
     100              :                        const std::initializer_list<ValueType_>&                        d) -> CovarianceMatrixFactored;
     101              :   // <---
     102              : 
     103              :   /// \brief Set Identity covariance
     104              :   void setIdentity();
     105              : 
     106              :   /// \brief Access operator to the covariance value at (row, col)
     107              :   /// \param[in,out] row  The specified row
     108              :   /// \param[in,out] col  The specified column
     109              :   /// \return tl::expected<value_type, Errors>   either the value at (row,col) or an Error descriptor
     110              :   auto operator()(sint32 row, sint32 col) const -> tl::expected<value_type, Errors>;
     111              : 
     112              :   /// \brief Creates the composed covariance
     113              :   /// \return compose_type
     114              :   auto operator()() const -> compose_type;
     115              : 
     116              :   /// \brief Access operator to the triangular matrix U
     117              :   /// \return const reference to the internal triangular matrix U
     118            0 :   [[nodiscard]] auto U() const -> const TriangularMatrix<ValueType_, Size_, false, true>& { return _u; }
     119              : 
     120              :   /// \brief Access operator to the diagonal matrix D
     121              :   /// \return const reference to the internal diagonal matrix D
     122           40 :   [[nodiscard]] auto D() const -> const DiagonalMatrix<ValueType_, Size_>& { return _d; }
     123              : 
     124              :   /// \brief Calculate the trace of the square matrix.
     125              :   ///
     126              :   /// Computes the sum of all diagonal elements of the matrix.
     127              :   /// The trace is defined as the sum of elements A_ii for i = 1 to n.
     128              :   ///
     129              :   /// \return ValueType_ The trace of the matrix(sum of diagonal elements)
     130              :   [[nodiscard]] auto trace() const -> ValueType_;
     131              : 
     132              :   /// \brief Calculate the determinant of the covariance matrix.
     133              :   ///
     134              :   /// Computes the determinant as the product of the diagonal elements.
     135              :   ///     det(cov) = det(U)*det(D)*det(U') = det(D)
     136              :   ///
     137              :   /// \return ValueType_ The determinant of the matrix
     138              :   /// \note Time complexity: O(n) where n is the matrix dimension
     139              :   /// \note For singular matrixes, the determinant will be zero or very close to zero
     140           10 :   [[nodiscard]] auto determinant() const -> ValueType_ { return _d.determinant(); }
     141              : 
     142              :   /// \brief Compute inverse in factored form
     143              :   ///
     144              :   /// Calculates the inverse of the covariance matrix while maintaining
     145              :   /// the UDU factorization. This is more numerically stable than
     146              :   /// inverting the composed full matrix.
     147              :   ///
     148              :   /// \return tl::expected containing the inverse in factored form on success
     149              :   /// \return Errors::NotPositiveDefinite if the matrix is singular or near-singular
     150              :   ///
     151              :   /// \note Time complexity: O(n^3) where n = Size_
     152              :   /// \note More stable than full matrix inversion for ill-conditioned covariances
     153              :   auto inverse() const -> tl::expected<CovarianceMatrixFactored, Errors>;
     154              : 
     155              :   /// \brief Compute inverse as full covariance matrix
     156              :   ///
     157              :   /// Calculates the inverse and returns it as a full covariance matrix.
     158              :   /// This composes the inverse of UDU factorization into a full matrix.
     159              :   ///
     160              :   /// \return Full covariance matrix containing the inverse
     161              :   ///
     162              :   /// \note Time complexity: O(n^3) where n = Size_
     163              :   auto composed_inverse() const -> tl::expected<compose_type, Errors>;
     164              : 
     165              :   /// \brief Compute A*P*A^T in-place (factored covariance propagation)
     166              :   ///
     167              :   /// Performs the matrix operation A*P*A^T where P is this factored covariance matrix.
     168              :   /// The operation is performed in-place, overwriting the current matrix.
     169              :   /// This maintains the UDU factorization throughout the computation.
     170              :   ///
     171              :   /// \tparam IsRowMajor_ Storage layout of the transformation matrix A
     172              :   /// \param[in] A Square transformation matrix
     173              :   ///
     174              :   /// \note Used for coordinate transformations in factored Kalman filters
     175              :   /// \note Maintains numerical stability of the UDU representation
     176              :   template <bool IsRowMajor_>
     177              :   void apaT(const SquareMatrix<ValueType_, Size_, IsRowMajor_>& A);
     178              : 
     179              :   /// \brief Compute A*P*A^T (factored covariance propagation)
     180              :   ///
     181              :   /// Performs the matrix operation A*P*A^T where P is this factored covariance matrix.
     182              :   /// Returns a new factored covariance matrix without modifying this one.
     183              :   ///
     184              :   /// \tparam IsRowMajor_ Storage layout of the transformation matrix A
     185              :   /// \param[in] A Square transformation matrix
     186              :   /// \return New factored covariance matrix containing A*P*A^T
     187              :   ///
     188              :   /// \note Used for coordinate transformations in factored Kalman filters
     189              :   /// \note Maintains numerical stability of the UDU representation
     190              :   template <bool IsRowMajor_>
     191              :   auto apaT(const SquareMatrix<ValueType_, Size_, IsRowMajor_>& A) const -> CovarianceMatrixFactored;
     192              : 
     193              :   /// \brief Thornton update: Φ*P*Φ^T + G*Q*G^T (Kalman prediction)
     194              :   ///
     195              :   /// Implements Thornton's algorithm for updating the factored covariance matrix
     196              :   /// during the Kalman filter prediction step. This computes:
     197              :   /// P_{k+1} = Φ*P_k*Φ^T + G*Q*G^T
     198              :   ///
     199              :   /// Where:
     200              :   /// - Φ is the state transition matrix
     201              :   /// - P_k is the current covariance (this matrix)
     202              :   /// - G is the process noise coupling matrix
     203              :   /// - Q is the diagonal process noise covariance
     204              :   ///
     205              :   /// The algorithm maintains the UDU factorization throughout the computation,
     206              :   /// providing better numerical stability than full matrix operations.
     207              :   ///
     208              :   /// \tparam SizeQ_ Dimension of the process noise
     209              :   /// \param[in] Phi State transition matrix (Size_ x Size_)
     210              :   /// \param[in] G Process noise coupling matrix (Size_ x SizeQ_)
     211              :   /// \param[in] Q Diagonal process noise covariance (SizeQ_ x SizeQ_)
     212              :   ///
     213              :   /// \note This is the primary prediction update in factored Kalman filters
     214              :   /// \note Time complexity: O(n^2) where n = Size_
     215              :   /// \note Numerically stable for ill-conditioned matrixes
     216              :   ///
     217              :   /// \see Thornton, C. L. "Triangular Covariance Factorizations for Kalman Filtering"
     218              :   template <sint32 SizeQ_>
     219              :   void thornton(const SquareMatrix<ValueType_, Size_, true>&   Phi,
     220              :                 const Matrix<ValueType_, Size_, SizeQ_, true>& G,
     221              :                 const DiagonalMatrix<ValueType_, SizeQ_>&      Q);
     222              : 
     223              :   /// \brief Agee-Turner rank-1 update: P ± x*x^T
     224              :   ///
     225              :   /// Performs a rank-1 update of the covariance matrix: P = P + c*x*x^T
     226              :   /// where c is a scalar and x is a vector. This operation is fundamental
     227              :   /// to Kalman filter measurement updates.
     228              :   ///
     229              :   /// When c > 0: rank-1 update (increases uncertainty)
     230              :   /// When c < 0: rank-1 downdate (decreases uncertainty)
     231              :   ///
     232              :   /// The algorithm maintains the UDU factorization and is numerically stable.
     233              :   ///
     234              :   /// \param[in] c Scalar multiplier (positive for update, negative for downdate)
     235              :   /// \param[in] x Update vector defining the outer product x*x^T
     236              :   ///
     237              :   /// \note Used in Kalman measurement updates and Joseph stabilized updates
     238              :   /// \note Maintains positive semi-definiteness when c >= -1/λ_min where λ_min is the smallest eigenvalue
     239              :   /// \note Time complexity: O(n^2) where n = Size_
     240              :   ///
     241              :   /// \see Agee, W. H. and Turner, R. C. "Triangular Decomposition of a Positive Definite Matrix Plus a Symmetric Dyad"
     242              :   void rank1Update(const ValueType_ c, const Vector<ValueType_, Size_>& x);
     243              : 
     244              :   /// \brief Set the variance at (idx,idx) and clears any correlations
     245              :   /// \param[in] idx  Index in diagonal matrix
     246              :   /// \param[in] val  The value to be set
     247              :   void setVariance(const sint32 idx, const ValueType_ val);
     248              : 
     249              :   /// \brief Fill the covariance with first N=SrcCount rows and cols of the other covariance
     250              :   /// \tparam SrcSize   Size of the other covariance
     251              :   /// \tparam SrcCount  Count rows/cols to copy from other
     252              :   /// \param[in] other  The other matrix to copy from
     253              :   template <sint32 SrcSize_, sint32 SrcCount_>
     254              :   void fill(const CovarianceMatrixFactored<ValueType_, SrcSize_>& other);
     255              : 
     256              :   /// \brief Set the Diagonal matrix element to given value
     257              :   /// \param[in] idx  Index in diagonal matrix
     258              :   /// \param[in] val  The value to be set
     259              :   void D(const sint32 idx, const ValueType_ val);
     260              : 
     261              :   /// \brief Check if the matrix is symmetric.
     262              :   ///
     263              :   /// The factored covariance is symmetric by design.
     264              :   ///
     265              :   /// \return always true
     266            0 :   [[nodiscard]] auto isSymmetric() const -> bool { return true; }
     267              : 
     268              :   /// \brief Check if the matrix matrix is positive definite.
     269              :   ///
     270              :   /// Tests whether all eigenvalues of the matrix are non-negative.
     271              :   /// A positive definite matrix satisfies x^T * A * x > 0 for all non-zero vectors x.
     272              :   ///
     273              :   /// \return true if the matrix is positive definite, false otherwise
     274            0 :   [[nodiscard]] auto isPositiveDefinite() const -> bool { return _d.isPositiveDefinite(); }
     275              : 
     276              :   /// \brief Checks if the matrix is positive semi-definite.
     277              :   ///
     278              :   /// Tests whether all eigenvalues of the matrix are non-negative.
     279              :   /// A positive semi-definite matrix satisfies x^T * A * x >= 0 for all non-zero vectors x.
     280              :   ///
     281              :   /// This is a key property for covariance matrixes in statistics and machine learning.
     282              :   ///
     283              :   /// \return true if the matrix is positive semi-definite, false otherwise
     284            0 :   [[nodiscard]] auto isPositiveSemiDefinite() const -> bool { return _d.isPositiveSemiDefinite(); }
     285              : 
     286              :   //////////////////////////////////////////////////
     287              :   // unsafe access operators  --->
     288              :   /// \brief Unsafe element read-only access
     289              :   /// \param[in] row  row of the element to read
     290              :   /// \param[in] col  column of the element to read
     291              :   /// \return ValueType_   the value at (row,col)
     292              :   [[nodiscard]] auto at_unsafe(sint32 row, sint32 col) const -> ValueType_;
     293              :   /// <---
     294              : 
     295              :   // clang-format off
     296              : TEST_REMOVE_PRIVATE:
     297              :   ; // workaround for correct indentation
     298              :   // clang-format on
     299              : 
     300              :   TriangularMatrix<ValueType_, Size_, false, true> _u{};
     301              :   DiagonalMatrix<ValueType_, Size_>                _d{};
     302              : };
     303              : 
     304              : } // namespace math
     305              : } // namespace tracking
     306              : 
     307              : #endif // F9044FD7_A3A8_43F4_BDD6_F43011384722
        

Generated by: LCOV version 2.0-1