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_Gauss_HeaderFile
0015 #define _MathLin_Gauss_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 #include <math_IntegerVector.hxx>
0023 
0024 #include <cmath>
0025 
0026 namespace MathLin
0027 {
0028 using namespace MathUtils;
0029 
0030 //! Result for LU decomposition.
0031 struct LUResult
0032 {
0033   MathUtils::Status                 Status = MathUtils::Status::NotConverged;
0034   std::optional<math_Matrix>        LU;    //!< Combined L and U matrices
0035   std::optional<math_IntegerVector> Pivot; //!< Pivot indices
0036   std::optional<double>             Determinant;
0037   int                               Sign = 1; //!< Sign from row interchanges
0038 
0039   bool IsDone() const { return Status == MathUtils::Status::OK; }
0040 
0041   explicit operator bool() const { return IsDone(); }
0042 };
0043 
0044 //! Perform LU decomposition of matrix A with partial pivoting.
0045 //! Decomposes A into L*U where L is lower triangular with unit diagonal
0046 //! and U is upper triangular. The result stores L and U in a combined matrix.
0047 //!
0048 //! @param theA input square matrix
0049 //! @param theMinPivot minimum pivot value (smaller treated as singular)
0050 //! @return LU decomposition result
0051 inline LUResult LU(const math_Matrix& theA, double theMinPivot = 1.0e-20)
0052 {
0053   LUResult aResult;
0054 
0055   const int aRowLower = theA.LowerRow();
0056   const int aRowUpper = theA.UpperRow();
0057   const int aColLower = theA.LowerCol();
0058   const int aColUpper = theA.UpperCol();
0059   const int aRowCount = aRowUpper - aRowLower + 1;
0060   const int aColCount = aColUpper - aColLower + 1;
0061 
0062   // Check for square matrix
0063   if (aRowCount != aColCount)
0064   {
0065     aResult.Status = Status::InvalidInput;
0066     return aResult;
0067   }
0068 
0069   // Initialize LU matrix as copy of A
0070   aResult.LU    = theA;
0071   aResult.Pivot = math_IntegerVector(aRowLower, aRowUpper);
0072   aResult.Sign  = 1;
0073 
0074   math_Matrix&        aLU    = *aResult.LU;
0075   math_IntegerVector& aPivot = *aResult.Pivot;
0076 
0077   // Scaling factors for implicit pivoting
0078   math_Vector aScale(aRowLower, aRowUpper);
0079   for (int i = aRowLower; i <= aRowUpper; ++i)
0080   {
0081     double aMax = 0.0;
0082     for (int j = aColLower; j <= aColUpper; ++j)
0083     {
0084       const double aAbs = std::abs(aLU(i, j));
0085       if (aAbs > aMax)
0086       {
0087         aMax = aAbs;
0088       }
0089     }
0090     if (aMax < theMinPivot)
0091     {
0092       aResult.Status = Status::NumericalError; // Singular matrix
0093       return aResult;
0094     }
0095     aScale(i) = 1.0 / aMax;
0096   }
0097 
0098   // Crout's algorithm with partial pivoting
0099   for (int k = aRowLower; k <= aRowUpper; ++k)
0100   {
0101     // Find pivot
0102     double aMaxScaled = 0.0;
0103     int    aPivotRow  = k;
0104 
0105     for (int i = k; i <= aRowUpper; ++i)
0106     {
0107       const double aScaled = aScale(i) * std::abs(aLU(i, k));
0108       if (aScaled > aMaxScaled)
0109       {
0110         aMaxScaled = aScaled;
0111         aPivotRow  = i;
0112       }
0113     }
0114 
0115     // Swap rows if needed
0116     if (aPivotRow != k)
0117     {
0118       for (int j = aColLower; j <= aColUpper; ++j)
0119       {
0120         std::swap(aLU(aPivotRow, j), aLU(k, j));
0121       }
0122       aResult.Sign = -aResult.Sign;
0123       std::swap(aScale(aPivotRow), aScale(k));
0124     }
0125     aPivot(k) = aPivotRow;
0126 
0127     // Check for singular matrix
0128     if (std::abs(aLU(k, k)) < theMinPivot)
0129     {
0130       aResult.Status = Status::NumericalError;
0131       return aResult;
0132     }
0133 
0134     // Eliminate below diagonal
0135     for (int i = k + 1; i <= aRowUpper; ++i)
0136     {
0137       aLU(i, k) /= aLU(k, k);
0138       const double aFactor = aLU(i, k);
0139       for (int j = k + 1; j <= aColUpper; ++j)
0140       {
0141         aLU(i, j) -= aFactor * aLU(k, j);
0142       }
0143     }
0144   }
0145 
0146   // Compute determinant
0147   aResult.Determinant = static_cast<double>(aResult.Sign);
0148   for (int i = aRowLower; i <= aRowUpper; ++i)
0149   {
0150     *aResult.Determinant *= aLU(i, i);
0151   }
0152 
0153   aResult.Status = Status::OK;
0154   return aResult;
0155 }
0156 
0157 //! Solve linear system AX = B using LU decomposition.
0158 //!
0159 //! @param theA coefficient matrix (square)
0160 //! @param theB right-hand side vector
0161 //! @param theMinPivot minimum pivot value
0162 //! @return result containing solution vector
0163 inline LinearResult Solve(const math_Matrix& theA,
0164                           const math_Vector& theB,
0165                           double             theMinPivot = 1.0e-20)
0166 {
0167   LinearResult aResult;
0168 
0169   // Perform LU decomposition
0170   LUResult aLURes = LU(theA, theMinPivot);
0171   if (!aLURes.IsDone())
0172   {
0173     aResult.Status = aLURes.Status;
0174     return aResult;
0175   }
0176 
0177   const int aRowLower = theA.LowerRow();
0178   const int aRowUpper = theA.UpperRow();
0179 
0180   // Check dimensions
0181   if (theB.Lower() != aRowLower || theB.Upper() != aRowUpper)
0182   {
0183     aResult.Status = Status::InvalidInput;
0184     return aResult;
0185   }
0186 
0187   const math_Matrix&        aLU    = *aLURes.LU;
0188   const math_IntegerVector& aPivot = *aLURes.Pivot;
0189 
0190   // Copy B to working vector (we modify it in-place like the legacy algorithm)
0191   math_Vector aX = theB;
0192 
0193   // Forward substitution with in-place permutation (matches legacy LU_Solve)
0194   int aFirstNonZero = 0;
0195   for (int i = aRowLower; i <= aRowUpper; ++i)
0196   {
0197     const int aPivotIdx = aPivot(i);
0198     double    aSum      = aX(aPivotIdx);
0199     aX(aPivotIdx)       = aX(i); // Swap elements in-place
0200 
0201     if (aFirstNonZero != 0)
0202     {
0203       for (int j = aFirstNonZero; j < i; ++j)
0204       {
0205         aSum -= aLU(i, j) * aX(j);
0206       }
0207     }
0208     else if (!MathUtils::IsZero(aSum))
0209     {
0210       aFirstNonZero = i;
0211     }
0212     aX(i) = aSum;
0213   }
0214 
0215   // Back substitution (solve Ux = y)
0216   for (int i = aRowUpper; i >= aRowLower; --i)
0217   {
0218     double aSum = aX(i);
0219     for (int j = i + 1; j <= aRowUpper; ++j)
0220     {
0221       aSum -= aLU(i, j) * aX(j);
0222     }
0223     aX(i) = aSum / aLU(i, i);
0224   }
0225 
0226   aResult.Status      = Status::OK;
0227   aResult.Solution    = aX;
0228   aResult.Determinant = aLURes.Determinant;
0229   return aResult;
0230 }
0231 
0232 //! Solve multiple linear systems AX = B where B is a matrix.
0233 //! Each column of B is a separate right-hand side.
0234 //!
0235 //! @param theA coefficient matrix (square)
0236 //! @param theB right-hand side matrix
0237 //! @param theMinPivot minimum pivot value
0238 //! @return result containing solution matrix
0239 inline LinearMultipleResult SolveMultiple(const math_Matrix& theA,
0240                                           const math_Matrix& theB,
0241                                           double             theMinPivot = 1.0e-20)
0242 {
0243   LinearMultipleResult aResult;
0244 
0245   // Perform LU decomposition
0246   LUResult aLURes = LU(theA, theMinPivot);
0247   if (!aLURes.IsDone())
0248   {
0249     aResult.Status = aLURes.Status;
0250     return aResult;
0251   }
0252 
0253   const int aRowLower = theA.LowerRow();
0254   const int aRowUpper = theA.UpperRow();
0255 
0256   // Check dimensions
0257   if (theB.LowerRow() != aRowLower || theB.UpperRow() != aRowUpper)
0258   {
0259     aResult.Status = Status::InvalidInput;
0260     return aResult;
0261   }
0262 
0263   const math_Matrix&        aLU    = *aLURes.LU;
0264   const math_IntegerVector& aPivot = *aLURes.Pivot;
0265 
0266   // Solve for each column of B
0267   math_Matrix aX(aRowLower, aRowUpper, theB.LowerCol(), theB.UpperCol());
0268 
0269   for (int col = theB.LowerCol(); col <= theB.UpperCol(); ++col)
0270   {
0271     // Extract column as working vector
0272     math_Vector aWork(aRowLower, aRowUpper);
0273     for (int i = aRowLower; i <= aRowUpper; ++i)
0274     {
0275       aWork(i) = theB(i, col);
0276     }
0277 
0278     // Forward substitution with in-place permutation
0279     int aFirstNonZero = 0;
0280     for (int i = aRowLower; i <= aRowUpper; ++i)
0281     {
0282       const int aPivotIdx = aPivot(i);
0283       double    aSum      = aWork(aPivotIdx);
0284       aWork(aPivotIdx)    = aWork(i); // Swap elements in-place
0285 
0286       if (aFirstNonZero != 0)
0287       {
0288         for (int j = aFirstNonZero; j < i; ++j)
0289         {
0290           aSum -= aLU(i, j) * aWork(j);
0291         }
0292       }
0293       else if (!MathUtils::IsZero(aSum))
0294       {
0295         aFirstNonZero = i;
0296       }
0297       aWork(i) = aSum;
0298     }
0299 
0300     // Back substitution
0301     for (int i = aRowUpper; i >= aRowLower; --i)
0302     {
0303       double aSum = aWork(i);
0304       for (int j = i + 1; j <= aRowUpper; ++j)
0305       {
0306         aSum -= aLU(i, j) * aWork(j);
0307       }
0308       aWork(i) = aSum / aLU(i, i);
0309     }
0310 
0311     // Copy result to output matrix
0312     for (int i = aRowLower; i <= aRowUpper; ++i)
0313     {
0314       aX(i, col) = aWork(i);
0315     }
0316   }
0317 
0318   aResult.Status      = Status::OK;
0319   aResult.Determinant = aLURes.Determinant;
0320   aResult.Solutions   = aX;
0321   return aResult;
0322 }
0323 
0324 //! Compute determinant of matrix A.
0325 //!
0326 //! @param theA input square matrix
0327 //! @param theMinPivot minimum pivot value
0328 //! @return result containing determinant value
0329 inline LinearResult Determinant(const math_Matrix& theA, double theMinPivot = 1.0e-20)
0330 {
0331   LinearResult aResult;
0332 
0333   LUResult aLURes = LU(theA, theMinPivot);
0334   if (!aLURes.IsDone())
0335   {
0336     aResult.Status      = aLURes.Status;
0337     aResult.Determinant = 0.0;
0338     return aResult;
0339   }
0340 
0341   aResult.Status      = Status::OK;
0342   aResult.Determinant = aLURes.Determinant;
0343   return aResult;
0344 }
0345 
0346 //! Compute inverse of matrix A.
0347 //!
0348 //! @param theA input square matrix
0349 //! @param theMinPivot minimum pivot value
0350 //! @return result containing inverse matrix
0351 inline InverseResult Invert(const math_Matrix& theA, double theMinPivot = 1.0e-20)
0352 {
0353   InverseResult aResult;
0354 
0355   // Perform LU decomposition
0356   LUResult aLURes = LU(theA, theMinPivot);
0357   if (!aLURes.IsDone())
0358   {
0359     aResult.Status = aLURes.Status;
0360     return aResult;
0361   }
0362 
0363   const int aRowLower = theA.LowerRow();
0364   const int aRowUpper = theA.UpperRow();
0365   const int aColLower = theA.LowerCol();
0366   const int aColUpper = theA.UpperCol();
0367 
0368   const math_Matrix&        aLU    = *aLURes.LU;
0369   const math_IntegerVector& aPivot = *aLURes.Pivot;
0370 
0371   // Compute inverse column by column using LU_Solve approach
0372   math_Matrix aInv(aRowLower, aRowUpper, aColLower, aColUpper, 0.0);
0373   math_Vector aCol(aRowLower, aRowUpper);
0374 
0375   for (int col = aColLower; col <= aColUpper; ++col)
0376   {
0377     // Initialize column as unit vector
0378     for (int i = aRowLower; i <= aRowUpper; ++i)
0379     {
0380       aCol(i) = 0.0;
0381     }
0382     aCol(col - aColLower + aRowLower) = 1.0;
0383 
0384     // Forward substitution with in-place permutation
0385     int aFirstNonZero = 0;
0386     for (int i = aRowLower; i <= aRowUpper; ++i)
0387     {
0388       const int aPivotIdx = aPivot(i);
0389       double    aSum      = aCol(aPivotIdx);
0390       aCol(aPivotIdx)     = aCol(i); // Swap elements in-place
0391 
0392       if (aFirstNonZero != 0)
0393       {
0394         for (int j = aFirstNonZero; j < i; ++j)
0395         {
0396           aSum -= aLU(i, j) * aCol(j);
0397         }
0398       }
0399       else if (!MathUtils::IsZero(aSum))
0400       {
0401         aFirstNonZero = i;
0402       }
0403       aCol(i) = aSum;
0404     }
0405 
0406     // Back substitution
0407     for (int i = aRowUpper; i >= aRowLower; --i)
0408     {
0409       double aSum = aCol(i);
0410       for (int j = i + 1; j <= aRowUpper; ++j)
0411       {
0412         aSum -= aLU(i, j) * aCol(j);
0413       }
0414       aCol(i) = aSum / aLU(i, i);
0415     }
0416 
0417     // Copy result to inverse matrix
0418     for (int i = aRowLower; i <= aRowUpper; ++i)
0419     {
0420       aInv(i, col) = aCol(i);
0421     }
0422   }
0423 
0424   aResult.Status      = Status::OK;
0425   aResult.Inverse     = aInv;
0426   aResult.Determinant = aLURes.Determinant;
0427   return aResult;
0428 }
0429 
0430 } // namespace MathLin
0431 
0432 #endif // _MathLin_Gauss_HeaderFile