Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-28 09:20:50

0001 // Copyright (c) 2025 OPEN CASCADE SAS
0002 //
0003 // This file is part of Open CASCADE Technology software library.
0004 //
0005 // This library is free software; you can redistribute it and/or modify it under
0006 // the terms of the GNU Lesser General Public License version 2.1 as published
0007 // by the Free Software Foundation, with special exception defined in the file
0008 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
0009 // distribution for complete text of the license and disclaimer of any warranty.
0010 //
0011 // Alternatively, this file may be used under the terms of Open CASCADE
0012 // commercial license or contractual agreement.
0013 
0014 #ifndef _MathLin_Crout_HeaderFile
0015 #define _MathLin_Crout_HeaderFile
0016 
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <MathUtils_Core.hxx>
0020 #include <math_Vector.hxx>
0021 #include <math_Matrix.hxx>
0022 
0023 #include <cmath>
0024 
0025 namespace MathLin
0026 {
0027 using namespace MathUtils;
0028 
0029 //! Result for Crout LDL^T decomposition.
0030 //! Specialized for symmetric matrices.
0031 struct CroutResult
0032 {
0033   MathUtils::Status          Status = MathUtils::Status::NotConverged;
0034   std::optional<math_Matrix> L;           //!< Lower triangular matrix (unit diagonal)
0035   std::optional<math_Vector> D;           //!< Diagonal elements
0036   std::optional<math_Matrix> Inverse;     //!< Inverse matrix (lower triangle only)
0037   std::optional<double>      Determinant; //!< Matrix determinant
0038 
0039   bool IsDone() const { return Status == MathUtils::Status::OK; }
0040 
0041   explicit operator bool() const { return IsDone(); }
0042 };
0043 
0044 //! Crout decomposition for symmetric matrices: A = L * D * L^T.
0045 //!
0046 //! This algorithm decomposes a symmetric matrix A into:
0047 //! - L: lower triangular matrix with unit diagonal
0048 //! - D: diagonal matrix
0049 //!
0050 //! Properties:
0051 //! - Only the lower triangle of A is used
0052 //! - Faster than general LU for symmetric matrices
0053 //! - Computes inverse efficiently
0054 //! - Requires positive definiteness for stability
0055 //!
0056 //! @param theA input symmetric matrix (only lower triangle used)
0057 //! @param theMinPivot minimum pivot value (smaller treated as singular)
0058 //! @return Crout decomposition result
0059 inline CroutResult Crout(const math_Matrix& theA, double theMinPivot = 1.0e-20)
0060 {
0061   CroutResult aResult;
0062 
0063   const int aRowLower = theA.LowerRow();
0064   const int aRowUpper = theA.UpperRow();
0065   const int aColLower = theA.LowerCol();
0066   const int aColUpper = theA.UpperCol();
0067   const int aN        = aRowUpper - aRowLower + 1;
0068 
0069   // Check for square matrix
0070   if (aN != aColUpper - aColLower + 1)
0071   {
0072     aResult.Status = Status::InvalidInput;
0073     return aResult;
0074   }
0075 
0076   // Working matrices with 1-based indexing
0077   math_Matrix aL(1, aN, 1, aN, 0.0);
0078   math_Vector aDiag(1, aN);
0079 
0080   double aDet = 1.0;
0081 
0082   // Crout decomposition: A = L * D * L^T
0083   for (int i = 1; i <= aN; ++i)
0084   {
0085     // Compute L(i,j) for j < i
0086     for (int j = 1; j <= i - 1; ++j)
0087     {
0088       double aScale = 0.0;
0089       for (int k = 1; k <= j - 1; ++k)
0090       {
0091         aScale += aL(i, k) * aL(j, k) * aDiag(k);
0092       }
0093       aL(i, j) = (theA(i + aRowLower - 1, j + aColLower - 1) - aScale) / aDiag(j);
0094     }
0095 
0096     // Compute D(i)
0097     double aScale = 0.0;
0098     for (int k = 1; k <= i - 1; ++k)
0099     {
0100       aScale += aL(i, k) * aL(i, k) * aDiag(k);
0101     }
0102     aDiag(i) = theA(i + aRowLower - 1, i + aColLower - 1) - aScale;
0103     aDet *= aDiag(i);
0104 
0105     // Check for singularity
0106     if (std::abs(aDiag(i)) <= theMinPivot)
0107     {
0108       aResult.Status = Status::Singular;
0109       return aResult;
0110     }
0111 
0112     aL(i, i) = 1.0;
0113   }
0114 
0115   // Compute inverse of L
0116   aL(1, 1) = 1.0 / aL(1, 1);
0117   for (int i = 2; i <= aN; ++i)
0118   {
0119     for (int k = 1; k <= i - 1; ++k)
0120     {
0121       double aScale = 0.0;
0122       for (int j = k; j <= i - 1; ++j)
0123       {
0124         aScale += aL(i, j) * aL(j, k);
0125       }
0126       aL(i, k) = -aScale / aL(i, i);
0127     }
0128     aL(i, i) = 1.0 / aL(i, i);
0129   }
0130 
0131   // Compute inverse of A (lower triangle only)
0132   math_Matrix aInv(1, aN, 1, aN, 0.0);
0133   for (int j = 1; j <= aN; ++j)
0134   {
0135     double aScale = aL(j, j) * aL(j, j) / aDiag(j);
0136     for (int k = j + 1; k <= aN; ++k)
0137     {
0138       aScale += aL(k, j) * aL(k, j) / aDiag(k);
0139     }
0140     aInv(j, j) = aScale;
0141 
0142     for (int i = j + 1; i <= aN; ++i)
0143     {
0144       aScale = aL(i, j) * aL(i, i) / aDiag(i);
0145       for (int k = i + 1; k <= aN; ++k)
0146       {
0147         aScale += aL(k, j) * aL(k, i) / aDiag(k);
0148       }
0149       aInv(i, j) = aScale;
0150     }
0151   }
0152 
0153   // Copy results with original indexing
0154   aResult.L = math_Matrix(aRowLower, aRowUpper, aColLower, aColUpper, 0.0);
0155   for (int i = 1; i <= aN; ++i)
0156   {
0157     for (int j = 1; j <= i; ++j)
0158     {
0159       (*aResult.L)(i + aRowLower - 1, j + aColLower - 1) = aL(i, j);
0160     }
0161   }
0162 
0163   aResult.D = math_Vector(aRowLower, aRowUpper);
0164   for (int i = 1; i <= aN; ++i)
0165   {
0166     (*aResult.D)(i + aRowLower - 1) = aDiag(i);
0167   }
0168 
0169   aResult.Inverse = math_Matrix(aRowLower, aRowUpper, aColLower, aColUpper, 0.0);
0170   for (int i = 1; i <= aN; ++i)
0171   {
0172     for (int j = 1; j <= i; ++j)
0173     {
0174       (*aResult.Inverse)(i + aRowLower - 1, j + aColLower - 1) = aInv(i, j);
0175     }
0176     // Mirror to upper triangle for full symmetric matrix
0177     for (int j = i + 1; j <= aN; ++j)
0178     {
0179       (*aResult.Inverse)(i + aRowLower - 1, j + aColLower - 1) = aInv(j, i);
0180     }
0181   }
0182 
0183   aResult.Determinant = aDet;
0184   aResult.Status      = Status::OK;
0185   return aResult;
0186 }
0187 
0188 //! Solve symmetric linear system Ax = b using Crout decomposition.
0189 //!
0190 //! Uses precomputed Crout decomposition to solve for x.
0191 //! More efficient than LU for symmetric positive definite matrices.
0192 //!
0193 //! @param theA coefficient matrix (symmetric)
0194 //! @param theB right-hand side vector
0195 //! @param theMinPivot minimum pivot value
0196 //! @return result containing solution vector
0197 inline LinearResult SolveCrout(const math_Matrix& theA,
0198                                const math_Vector& theB,
0199                                double             theMinPivot = 1.0e-20)
0200 {
0201   LinearResult aResult;
0202 
0203   // Perform Crout decomposition
0204   CroutResult aCrout = Crout(theA, theMinPivot);
0205   if (!aCrout.IsDone())
0206   {
0207     aResult.Status = aCrout.Status;
0208     return aResult;
0209   }
0210 
0211   const int aRowLower = theA.LowerRow();
0212   const int aRowUpper = theA.UpperRow();
0213   const int aN        = aRowUpper - aRowLower + 1;
0214 
0215   // Check dimensions
0216   if (theB.Length() != aN)
0217   {
0218     aResult.Status = Status::InvalidInput;
0219     return aResult;
0220   }
0221 
0222   const math_Matrix& aInv    = *aCrout.Inverse;
0223   const int          aBLower = theB.Lower();
0224 
0225   // Compute X = Inverse * B using symmetry
0226   aResult.Solution = math_Vector(aRowLower, aRowUpper, 0.0);
0227   for (int i = 1; i <= aN; ++i)
0228   {
0229     double aSum = aInv(i + aRowLower - 1, 1 + aRowLower - 1) * theB(1 + aBLower - 1);
0230     for (int j = 2; j <= i; ++j)
0231     {
0232       aSum += aInv(i + aRowLower - 1, j + aRowLower - 1) * theB(j + aBLower - 1);
0233     }
0234     for (int j = i + 1; j <= aN; ++j)
0235     {
0236       aSum += aInv(j + aRowLower - 1, i + aRowLower - 1) * theB(j + aBLower - 1);
0237     }
0238     (*aResult.Solution)(i + aRowLower - 1) = aSum;
0239   }
0240 
0241   aResult.Determinant = aCrout.Determinant;
0242   aResult.Status      = Status::OK;
0243   return aResult;
0244 }
0245 
0246 //! Compute inverse of symmetric matrix using Crout decomposition.
0247 //!
0248 //! @param theA input symmetric matrix
0249 //! @param theMinPivot minimum pivot value
0250 //! @return result containing full symmetric inverse matrix
0251 inline InverseResult InvertCrout(const math_Matrix& theA, double theMinPivot = 1.0e-20)
0252 {
0253   InverseResult aResult;
0254 
0255   CroutResult aCrout = Crout(theA, theMinPivot);
0256   if (!aCrout.IsDone())
0257   {
0258     aResult.Status = aCrout.Status;
0259     return aResult;
0260   }
0261 
0262   aResult.Status      = Status::OK;
0263   aResult.Inverse     = aCrout.Inverse;
0264   aResult.Determinant = aCrout.Determinant;
0265   return aResult;
0266 }
0267 
0268 } // namespace MathLin
0269 
0270 #endif // _MathLin_Crout_HeaderFile