LCOV - code coverage report
Current view: top level - math/linalg - square_matrix.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 81.8 % 11 9
Test Date: 2026-04-26 21:52:20 Functions: 100.0 % 10 10
Legend: Lines: hit not hit

            Line data    Source code
       1              : #ifndef BE2AA4BF_8A4F_4A6D_8DFB_4E140C9F0A08
       2              : #define BE2AA4BF_8A4F_4A6D_8DFB_4E140C9F0A08
       3              : 
       4              : #include "base/first_include.h" // IWYU pragma: keep
       5              : #include "math/linalg/errors.h" // IWYU pragma: keep
       6              : #include "math/linalg/matrix.h" // IWYU pragma: keep
       7              : #include "math/linalg/vector.h" // IWYU pragma: keep
       8              : #include <initializer_list>
       9              : #include <utility> // std::pair
      10              : 
      11              : namespace tracking
      12              : {
      13              : namespace math
      14              : {
      15              : 
      16              : // forward declaration to prevent cyclic includes
      17              : template <typename ValueType_, sint32 Size_, bool IsLower_, bool IsRowMajor_>
      18              : class TriangularMatrix;
      19              : 
      20              : // forward declaration to prevent cyclic includes
      21              : template <typename ValueType_, sint32 Size_>
      22              : class DiagonalMatrix;
      23              : 
      24              : // TODO(matthias): add interface contract
      25              : 
      26              : /// \brief A square matrix specialization of the Matrix class providing additional operations
      27              : /// specific to square matrixes such as decompositions, inverse calculations, symmetry checks,
      28              : /// and matrix property validation.
      29              : ///
      30              : /// This class inherits from Matrix<ValueType_, Size_, Size_, IsRowMajor_> and extends it with
      31              : /// operations that are only meaningful for square matrixes. It supports various matrix
      32              : /// decompositions (QR, LLT, LDLT, UDUT), matrix inversion, symmetry operations, and
      33              : /// property checking functions for validation and debugging.
      34              : ///
      35              : /// \tparam ValueType_ The atomic data type of internal elements
      36              : /// \tparam Size_ The dimension of the square matrix (compile-time constant)
      37              : /// \tparam IsRowMajor_ Storage layout flag (true for row-major, false for column-major)
      38              : ///
      39              : /// \note All operations maintain the square property and may change storage layout for
      40              : ///       optimal performance (e.g., inverse operations may toggle IsRowMajor_)
      41              : ///
      42              : /// \see Matrix for the base matrix functionality
      43              : /// \see TriangularMatrix for triangular matrix operations
      44              : /// \see DiagonalMatrix for diagonal matrix operations
      45              : template <typename ValueType_, sint32 Size_, bool IsRowMajor_ = true>
      46         2529 : class SquareMatrix: public Matrix<ValueType_, Size_, Size_, IsRowMajor_>
      47              : {
      48              : public:
      49              :   using BaseMatrix = Matrix<ValueType_, Size_, Size_, IsRowMajor_>; ///< type of the parent class
      50              : 
      51              :   // unhide ctor of base class to allow implicit call in derived default ctors
      52          324 :   using BaseMatrix::BaseMatrix;
      53              : 
      54              :   //////////////////////////////////////////////////
      55              :   // additional constructors  --->
      56              :   /// \brief Construct a new Square Matrix<ValueType_, Size_> object
      57              :   /// \param[in] other A base class object
      58            0 :   explicit SquareMatrix(const BaseMatrix& other)
      59            0 :       : BaseMatrix{other}
      60              :   {
      61              :   }
      62              : 
      63              :   /// \brief Move construct a new Square Matrix<ValueType_, Size_> object
      64              :   /// \param[in] other A base class object
      65         1113 :   explicit SquareMatrix(BaseMatrix&& other) noexcept
      66          914 :       : BaseMatrix{std::move(other)}
      67              :   {
      68              :   }
      69              : 
      70              :   /// \brief Construct a square matrix from a diagonal matrix.
      71              :   ///
      72              :   /// Creates a square matrix with the diagonal elements from the input diagonal matrix
      73              :   /// and zeros elsewhere.
      74              :   ///
      75              :   /// \param[in] other The diagonal matrix to convert from
      76              :   ///
      77              :   /// \note This is an implicit conversion constructor for convenience
      78              :   /// \note This constructor is defined in the implementation file to avoid circular dependencies
      79              :   SquareMatrix(const DiagonalMatrix<ValueType_, Size_>& other); // NOLINT(google-explicit-constructor)
      80              : 
      81              : 
      82              :   /// \brief Construct an identity matrix with ones on the diagonal and zeros elsewhere.
      83              :   ///
      84              :   /// \return SquareMatrix An identity matrix of the specified size and storage layout
      85              :   ///
      86              :   /// \note The identity matrix I satisfies I * A = A * I = A for any square matrix A
      87              :   [[nodiscard]] static auto Identity() -> SquareMatrix;
      88              : 
      89              :   /// \brief Creates a SquareMatrix from a nested initializer list
      90              :   ///
      91              :   /// This function constructs a SquareMatrix from a nested initializer list where each inner list
      92              :   /// represents a row of the matrix. The dimensions must be square and match the template parameter.
      93              :   ///
      94              :   /// \tparam ValueType_ The atomic data type of internal elements
      95              :   /// \tparam Size_ The dimension of the square matrix
      96              :   /// \tparam IsRowMajor_ The storage layout (true for row-major, false for column-major)
      97              :   /// \param[in] list Nested initializer list in logical row-major format
      98              :   /// \return SquareMatrix instance initialized with the provided values
      99              :   /// \throws std::runtime_error If the list dimensions don't match the square matrix size
     100              :   /// \see SquareFromDiagonal() for creating from diagonal matrixes
     101              :   /// \see MatrixFromList() for general matrix creation
     102              :   [[nodiscard]] static auto FromList(const std::initializer_list<std::initializer_list<ValueType_>>& list) -> SquareMatrix;
     103              : 
     104              :   /// \brief Set the matrix to the identity matrix in-place.
     105              :   ///
     106              :   /// Modifies the current matrix to have ones on the diagonal and zeros elsewhere.
     107              :   ///
     108              :   /// \note This operation modifies the matrix in-place and does not change its size or layout
     109              :   void setIdentity();
     110              : 
     111              :   /// \brief Fast transpose without changing the layout (zero-copy view)
     112              :   /// \return const transpose_type&   const reference to same data as Self, but differently interpreted as transposed
     113              :   /// \note This creates a view with swapped dimensions and opposite layout. No data is copied.
     114              :   [[nodiscard]] auto transpose() const -> const SquareMatrix<ValueType_, Size_, !IsRowMajor_>&
     115              :   {
     116              :     return static_cast<const SquareMatrix<ValueType_, Size_, !IsRowMajor_>&>(BaseMatrix::transpose());
     117              :   };
     118              : 
     119              :   /// \brief Fast transpose without changing the layout (zero-copy view)
     120              :   /// \return transpose_type&   reference to same data as Self, but differently interpreted as transposed
     121              :   /// \note This creates a view with swapped dimensions and opposite layout. No data is copied.
     122           10 :   [[nodiscard]] auto transpose() -> SquareMatrix<ValueType_, Size_, !IsRowMajor_>&
     123              :   {
     124              :     return static_cast<SquareMatrix<ValueType_, Size_, !IsRowMajor_>&>(BaseMatrix::transpose());
     125              :   };
     126              : 
     127              :   /// \brief Calculate the trace of the square matrix.
     128              :   ///
     129              :   /// Computes the sum of all diagonal elements of the matrix.
     130              :   /// The trace is defined as the sum of elements A_ii for i = 1 to n.
     131              :   ///
     132              :   /// \return ValueType_ The trace of the matrix(sum of diagonal elements)
     133              :   [[nodiscard]] auto trace() const -> ValueType_;
     134              : 
     135              :   /// \brief Calculate the determinant of the square matrix.
     136              :   ///
     137              :   /// Computes the determinant using LU decomposition with partial pivoting.
     138              :   /// The determinant is the product of the diagonal elements of the upper triangular
     139              :   /// matrix from the LU decomposition, multiplied by (-1)^k where k is the number
     140              :   /// of row permutations.
     141              :   ///
     142              :   /// \return ValueType_ The determinant of the matrix
     143              :   /// \note Time complexity: O(n^3) where n is the matrix dimension
     144              :   /// \note For singular matrixes, the determinant will be zero or very close to zero
     145              :   /// \note Uses partial pivoting for numerical stability
     146              :   [[nodiscard]] auto determinant() const -> ValueType_;
     147              : 
     148              :   /// \brief Performs Householder QR decomposition of a square matrix
     149              :   ///
     150              :   /// Decomposes a square matrix A into the product A = Q*R, where Q is an orthogonal matrix
     151              :   /// and R is an upper triangular matrix. This implementation uses Householder reflections
     152              :   /// for numerical stability.
     153              :   ///
     154              :   /// The Householder QR decomposition is based on successive Householder transformations that
     155              :   /// zero out subdiagonal elements column by column. Each Householder reflection is represented
     156              :   /// by a vector v and a scalar τ, where the reflection matrix is I - τ*v*v^T.
     157              :   ///
     158              :   /// \return std::pair<SquareMatrix, TriangularMatrix> A pair containing Q and R matrixes,
     159              :   ///         where Q is orthogonal and R is upper triangular such that A = Q * R
     160              :   ///
     161              :   /// \note The decomposition satisfies A = Q * R, with Q being orthogonal (Q^T * Q = I)
     162              :   ///       and R being upper triangular. This is useful for solving linear systems and
     163              :   ///       computing matrix inverses.
     164              :   /// \note Time complexity: O(n^3) for an n x n matrix
     165              :   /// \note Space complexity: O(n^2) additional space for Q and R matrixes
     166              :   /// \note Numerically stable and suitable for general square matrixes
     167              :   ///
     168              :   /// \see qrSolve for solving linear systems using this decomposition
     169              :   /// \see inverse for matrix inversion using QR decomposition
     170              :   /// \see https://www.cs.cornell.edu/~bindel/class/cs6210-f09/lec18.pdf for algorithm reference
     171              :   [[nodiscard]] auto householderQR() const -> std::pair<SquareMatrix, TriangularMatrix<ValueType_, Size_, false, IsRowMajor_>>;
     172              : 
     173              :   /// \brief Solve the linear system A * x = b using QR decomposition.
     174              :   ///
     175              :   /// Uses the QR decomposition of this matrix to solve for x in A * x = b.
     176              :   /// This method is numerically stable and suitable for well-conditioned matrixes.
     177              :   ///
     178              :   /// \tparam IsRowMajor2_ The row-majority of the right-hand side matrix b
     179              :   /// \param[in] b The right-hand side matrix of the equation A * x = b
     180              :   /// \return Matrix The solution matrix x such that A * x ≈ b
     181              :   ///
     182              :   /// \note This method internally performs QR decomposition, which has O(n³) complexity.
     183              :   ///       For multiple right-hand sides with the same A, consider using householderQR()
     184              :   ///       directly and reusing the decomposition.
     185              :   ///
     186              :   /// \see householderQR for the underlying decomposition
     187              :   template <sint32 Cols_, bool IsRowMajor2_>
     188              :   [[nodiscard]] auto qrSolve(const Matrix<ValueType_, Size_, Cols_, IsRowMajor2_>& b) const
     189              :       -> Matrix<ValueType_, Size_, Cols_, !IsRowMajor_>;
     190              : 
     191           40 :   [[nodiscard]] auto qrSolve(const Vector<ValueType_, Size_>& b) const -> Vector<ValueType_, Size_>
     192              :   {
     193           40 :     return Vector<ValueType_, Size_>{qrSolve<1, true>(b)};
     194              :   }
     195              : 
     196              :   template <bool IsRowMajor2_>
     197           82 :   [[nodiscard]] auto qrSolve(const SquareMatrix<ValueType_, Size_, IsRowMajor2_>& b) const
     198              :       -> SquareMatrix<ValueType_, Size_, !IsRowMajor_>
     199              :   {
     200           82 :     return static_cast<SquareMatrix<ValueType_, Size_, !IsRowMajor_>>(qrSolve<Size_, IsRowMajor2_>(b));
     201              :   }
     202              : 
     203              :   /// \brief Performs Cholesky decomposition (LLT) of a symmetric positive definite matrix
     204              :   ///
     205              :   /// Decomposes a symmetric positive definite matrix A into the product A = L*L^T, where L
     206              :   /// is a lower triangular matrix with positive diagonal elements. This is also known as
     207              :   /// the Cholesky decomposition.
     208              :   ///
     209              :   /// The algorithm uses a recursive approach where each column of L is computed using
     210              :   /// previous columns. For a symmetric positive definite matrix, the Cholesky factor L
     211              :   /// satisfies L*L^T = A, with L being lower triangular.
     212              :   ///
     213              :   /// \return tl::expected<TriangularMatrix, Errors> The lower triangular matrix L on success,
     214              :   ///         or Errors::matrix_not_symmetric if not symmetric, or
     215              :   ///         Errors::matrix_not_positive_definite if not positive definite
     216              :   ///
     217              :   /// \note The matrix must be symmetric and positive definite
     218              :   /// \note Time complexity: O(n^3) for an n x n matrix
     219              :   /// \note Space complexity: O(n^2) for the result matrix
     220              :   /// \note Numerically stable for well-conditioned positive definite matrixes
     221              :   ///
     222              :   /// \warning The input matrix must be symmetric. Use symmetrize() if needed.
     223              :   /// \warning Fails if the matrix is not symmetric or not positive definite
     224              :   ///
     225              :   /// \see decomposeLDLT for a more numerically stable variant
     226              :   /// \see isSymmetric for symmetry checking
     227              :   [[nodiscard]] auto decomposeLLT() const -> tl::expected<TriangularMatrix<ValueType_, Size_, true, IsRowMajor_>, Errors>;
     228              : 
     229              :   /// \brief Performs LDL^T decomposition of a symmetric positive definite matrix
     230              :   ///
     231              :   /// Decomposes a symmetric positive definite matrix A into the product A = L*D*L^T, where
     232              :   /// L is a unit lower triangular matrix (diagonal elements are 1) and D is a diagonal
     233              :   /// matrix with positive diagonal elements.
     234              :   ///
     235              :   /// This decomposition is more numerically stable than LLT for certain matrixes and is
     236              :   /// particularly useful in Kalman filtering applications where the matrix structure
     237              :   /// needs to be maintained. The LDL^T form separates the triangular structure (L) from
     238              :   /// the scaling factors (D).
     239              :   ///
     240              :   /// \return tl::expected<std::pair<TriangularMatrix, DiagonalMatrix>, Errors>
     241              :   ///         A pair containing L and D matrixes on success, or Errors::matrix_not_symmetric
     242              :   ///         if not symmetric, or Errors::matrix_not_positive_definite if not positive definite
     243              :   ///
     244              :   /// \note The matrix must be symmetric and positive definite
     245              :   /// \note Time complexity: O(n^3) for an n x n matrix
     246              :   /// \note Space complexity: O(n^2) for L and O(n) for D
     247              :   /// \note More numerically stable than LLT for some applications
     248              :   /// \note L has unit diagonal (all diagonal elements are 1)
     249              :   ///
     250              :   /// \warning Fails if the matrix is not symmetric or not positive definite
     251              :   ///
     252              :   /// \see decomposeLLT for the standard Cholesky decomposition
     253              :   /// \see decomposeUDUT for the UDU^T variant
     254              :   [[nodiscard]] auto decomposeLDLT() const
     255              :       -> tl::expected<std::pair<TriangularMatrix<ValueType_, Size_, true, IsRowMajor_>, DiagonalMatrix<ValueType_, Size_>>,
     256              :                       Errors>;
     257              : 
     258              :   /// \brief Performs UDU^T decomposition of a symmetric matrix
     259              :   ///
     260              :   /// Decomposes a symmetric matrix A into the product A = U*D*U^T, where U is a unit upper
     261              :   /// triangular matrix (diagonal elements are 1) and D is a diagonal matrix. This is also
     262              :   /// known as the UDU^T factorization.
     263              :   ///
     264              :   /// This decomposition is particularly important in Kalman filtering for maintaining the
     265              :   /// UDU^T form of covariance matrixes, which provides better numerical stability than
     266              :   /// standard covariance representations. The algorithm works backwards from the last
     267              :   /// column to the first, computing U and D simultaneously.
     268              :   ///
     269              :   /// The implementation is based on modified Cholesky decomposition and includes numerical
     270              :   /// safeguards to ensure positive definiteness even for near-singular matrixes.
     271              :   ///
     272              :   /// \return tl::expected<std::pair<TriangularMatrix, DiagonalMatrix>, Errors>
     273              :   ///         A pair containing U and D matrixes on success, or Errors::matrix_not_symmetric
     274              :   ///         if not symmetric
     275              :   ///
     276              :   /// \note The matrix must be symmetric
     277              :   /// \note Time complexity: O(n^3) for an n x n matrix
     278              :   /// \note Space complexity: O(n^2) for U and O(n) for D
     279              :   /// \note Numerically stable with safeguards for near-singular matrixes
     280              :   /// \note U has unit diagonal (all diagonal elements are 1)
     281              :   /// \note D diagonal elements are clamped to a minimum value for numerical stability
     282              :   ///
     283              :   /// \warning Fails if the matrix is not symmetric
     284              :   ///
     285              :   /// \see decomposeLDLT for the lower triangular variant
     286              :   /// \see CovarianceMatrixFactored for UDU usage in Kalman filters
     287              :   /// \see Grewal & Andrews, Kalman Filtering Theory and Practice Using MATLAB, 4th Edition, Wiley, 2014
     288              :   /// \see Gerald J. Bierman, "Factorization Methods for Discrete Sequential Estimation", 1977
     289              :   /// \see Catherine L. Thornton, "Triangular Covariance Factorizations for Kalman Filtering", 1976
     290              :   [[nodiscard]] auto decomposeUDUT() const
     291              :       -> tl::expected<std::pair<TriangularMatrix<ValueType_, Size_, false, IsRowMajor_>, DiagonalMatrix<ValueType_, Size_>>,
     292              :                       Errors>;
     293              : 
     294              :   /// \brief Compute the matrix inverse using QR decomposition.
     295              :   ///
     296              :   /// Calculates the inverse of the matrix using QR decomposition for numerical stability.
     297              :   /// The result has toggled storage layout for optimal performance.
     298              :   ///
     299              :   /// \return SquareMatrix The inverse matrix such that A * A^(-1) = I
     300              :   ///
     301              :   /// \note Uses QR decomposition internally, which provides good numerical stability
     302              :   ///       but has O(n³) complexity. The storage layout is toggled in the result.
     303              :   ///
     304              :   /// \warning Fails for singular or near-singular matrixes. Check condition number if needed.
     305              :   ///
     306              :   /// \see householderQR for the underlying decomposition
     307              :   [[nodiscard]] auto inverse() const -> SquareMatrix<ValueType_, Size_, !IsRowMajor_>;
     308              : 
     309              :   /// \brief Symmetrize the matrix by averaging with its transpose.
     310              :   ///
     311              :   /// Modifies the matrix to be symmetric by computing (A + A^T) / 2.
     312              :   /// This is useful for ensuring symmetry when small numerical errors
     313              :   /// have made a theoretically symmetric matrix asymmetric.
     314              :   ///
     315              :   /// \note This operation modifies the matrix in-place and ensures perfect symmetry
     316              :   ///       at the cost of slightly changing the original values.
     317              :   void symmetrize();
     318              : 
     319              :   //////////////////////////////////////////////////
     320              :   // square matrix property checks  --->
     321              :   /// \brief Check if the matrix is symmetric.
     322              :   ///
     323              :   /// Tests whether the matrix equals its transpose (A = A^T).
     324              :   ///
     325              :   /// \param tolerance Tolerance for numerical comparison (default: 1e-6)
     326              :   /// \return true if the matrix is symmetric, false otherwise
     327              :   ///
     328              :   /// \note Symmetry checking uses element-wise comparison and may have floating-point precision issues
     329              :   [[nodiscard]] auto isSymmetric(ValueType_ tolerance = 1e-6) const -> bool;
     330              : 
     331              :   /// \brief Check if the diagonal matrix is positive definite.
     332              :   ///
     333              :   /// A diagonal matrix is positive definite if all diagonal elements are positive.
     334              :   ///
     335              :   /// \return true if all diagonal elements are > 0, false otherwise
     336              :   ///
     337              :   /// \note For diagonal matrixes, positive definiteness is equivalent to all elements > 0
     338              :   [[nodiscard]] auto isPositiveDefinite() const -> bool;
     339              : 
     340              :   /// \brief Checks if the matrix is positive semi-definite.
     341              :   ///
     342              :   /// Tests whether all eigenvalues of the matrix are non-negative.
     343              :   /// A positive semi-definite matrix satisfies x^T * A * x >= 0 for all non-zero vectors x.
     344              :   ///
     345              :   /// This is a key property for covariance matrixes in statistics and machine learning.
     346              :   ///
     347              :   /// The check is performed using Cholesky decomposition; if it succeeds, the matrix
     348              :   /// is positive semi-definite.
     349              :   ///
     350              :   /// \return true if the matrix is positive semi-definite, false otherwise
     351              :   [[nodiscard]] auto isPositiveSemiDefinite() const -> bool;
     352              : 
     353              :   /// \brief Checks if the matrix is orthogonal within a specified tolerance
     354              :   ///
     355              :   /// Tests whether the matrix is orthogonal by checking if Q^T * Q = I within tolerance.
     356              :   /// An orthogonal matrix satisfies Q^T * Q = I, where I is the identity matrix.
     357              :   ///
     358              :   /// \param tolerance Tolerance for numerical comparison (default: 1e-6)
     359              :   /// \return true if matrix is orthogonal within tolerance, false otherwise
     360              :   ///
     361              :   /// \note This is useful for validating QR decomposition results
     362              :   /// \note Time complexity: O(n^3) due to matrix multiplication
     363              :   /// \see householderQR() for QR decomposition that produces orthogonal matrixes
     364              :   [[nodiscard]] auto isOrthogonal(ValueType_ tolerance = 1e-6) const -> bool;
     365              : 
     366              :   /// \brief Checks if the matrix is upper triangular within a specified tolerance
     367              :   ///
     368              :   /// Tests whether all elements below the main diagonal are zero within tolerance.
     369              :   /// An upper triangular matrix has all elements below the diagonal equal to zero.
     370              :   ///
     371              :   /// \param tolerance Tolerance for numerical comparison (default: 1e-6)
     372              :   /// \return true if matrix is upper triangular within tolerance, false otherwise
     373              :   ///
     374              :   /// \note This is useful for validating decomposition results (QR, UDUT)
     375              :   /// \note Time complexity: O(n^2)
     376              :   /// \see householderQR() for QR decomposition that produces upper triangular matrixes
     377              :   [[nodiscard]] auto isUpperTriangular(ValueType_ tolerance = 1e-6) const -> bool;
     378              : 
     379              :   /// \brief Checks if the matrix is lower triangular within a specified tolerance
     380              :   ///
     381              :   /// Tests whether all elements above the main diagonal are zero within tolerance.
     382              :   /// A lower triangular matrix has all elements above the diagonal equal to zero.
     383              :   ///
     384              :   /// \param tolerance Tolerance for numerical comparison (default: 1e-6)
     385              :   /// \return true if matrix is lower triangular within tolerance, false otherwise
     386              :   ///
     387              :   /// \note This is useful for validating decomposition results (LLT, LDLT)
     388              :   /// \note Time complexity: O(n^2)
     389              :   /// \see decomposeLLT() for LLT decomposition that produces lower triangular matrixes
     390              :   [[nodiscard]] auto isLowerTriangular(ValueType_ tolerance = 1e-6) const -> bool;
     391              : 
     392              :   /// \brief Checks if the matrix has a unit diagonal within a specified tolerance
     393              :   ///
     394              :   /// Tests whether all diagonal elements are equal to 1 within tolerance.
     395              :   /// A unit diagonal matrix has all diagonal elements equal to 1.
     396              :   ///
     397              :   /// \param tolerance Tolerance for numerical comparison (default: 1e-6)
     398              :   /// \return true if all diagonal elements are 1 within tolerance, false otherwise
     399              :   ///
     400              :   /// \note This is useful for validating decomposition results (LDLT, UDUT)
     401              :   /// \note Time complexity: O(n)
     402              :   /// \see decomposeLDLT() for LDLT decomposition that produces unit diagonal matrixes
     403              :   [[nodiscard]] auto hasUnitDiagonal(ValueType_ tolerance = 1e-6) const -> bool;
     404              :   // <---
     405              : 
     406              : private:
     407              :   /// \brief Check if all diagonal elements are strictly positive.
     408              :   ///
     409              :   /// Tests whether all diagonal elements satisfy d_ii > 0.
     410              :   /// This is used internally for positive definiteness checks.
     411              :   ///
     412              :   /// \return true if all diagonal elements are positive, false otherwise
     413              :   ///
     414              :   /// \note This is a necessary but not sufficient condition for positive definiteness
     415              :   [[nodiscard]] auto hasStrictlyPositiveDiagonalElems() const -> bool;
     416              : };
     417              : 
     418              : } // namespace math
     419              : } // namespace tracking
     420              : #endif // BE2AA4BF_8A4F_4A6D_8DFB_4E140C9F0A08
        

Generated by: LCOV version 2.0-1