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_SVD_HeaderFile
0015 #define _MathLin_SVD_HeaderFile
0016 
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <math_Recipes.hxx>
0020 #include <MathUtils_Core.hxx>
0021 
0022 #include <cmath>
0023 #include <algorithm>
0024 
0025 namespace MathLin
0026 {
0027 using namespace MathUtils;
0028 
0029 //! Result for SVD decomposition.
0030 struct SVDResult
0031 {
0032   MathUtils::Status          Status = MathUtils::Status::NotConverged;
0033   std::optional<math_Matrix> U;              //!< Left singular vectors (m x n)
0034   std::optional<math_Vector> SingularValues; //!< Singular values (n elements)
0035   std::optional<math_Matrix> V;              //!< Right singular vectors (n x n)
0036   int                        Rank = 0;       //!< Numerical rank
0037 
0038   bool IsDone() const { return Status == MathUtils::Status::OK; }
0039 
0040   explicit operator bool() const { return IsDone(); }
0041 };
0042 
0043 //! Singular Value Decomposition: A = U * diag(S) * V^T.
0044 //!
0045 //! Decomposes an m x n matrix A into:
0046 //! - U: m x n matrix of left singular vectors (orthonormal columns)
0047 //! - S: n singular values in descending order
0048 //! - V: n x n matrix of right singular vectors (orthonormal)
0049 //!
0050 //! Properties:
0051 //! - Works for any m x n matrix (m can be less, equal, or greater than n)
0052 //! - Singular values are always non-negative
0053 //! - Provides the best low-rank approximation of a matrix
0054 //! - Useful for solving ill-conditioned linear systems
0055 //!
0056 //! @param theA input matrix A (m x n)
0057 //! @param theTolerance for rank determination (relative to largest singular value)
0058 //! @return SVD decomposition result
0059 inline SVDResult SVD(const math_Matrix& theA, double theTolerance = 1.0e-15)
0060 {
0061   SVDResult 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 aM        = aRowUpper - aRowLower + 1; // Number of rows
0068   const int aN        = aColUpper - aColLower + 1; // Number of columns
0069 
0070   // U matrix needs to be at least n x n for the algorithm
0071   const int aURows = std::max(aM, aN);
0072 
0073   // Create working matrices with 1-based indexing as required by SVD_Decompose
0074   math_Matrix aU(1, aURows, 1, aN, 0.0);
0075   math_Vector aW(1, aN);
0076   math_Matrix aV(1, aN, 1, aN);
0077 
0078   // Copy input matrix to U
0079   for (int i = aRowLower; i <= aRowUpper; ++i)
0080   {
0081     for (int j = aColLower; j <= aColUpper; ++j)
0082     {
0083       aU(i - aRowLower + 1, j - aColLower + 1) = theA(i, j);
0084     }
0085   }
0086 
0087   // Perform SVD decomposition
0088   if (SVD_Decompose(aU, aW, aV) != 0)
0089   {
0090     aResult.Status = Status::NumericalError;
0091     return aResult;
0092   }
0093 
0094   // Determine numerical rank
0095   double aMaxSV = 0.0;
0096   for (int i = 1; i <= aN; ++i)
0097   {
0098     aMaxSV = std::max(aMaxSV, aW(i));
0099   }
0100 
0101   double aThreshold = theTolerance * aMaxSV;
0102   aResult.Rank      = 0;
0103   for (int i = 1; i <= aN; ++i)
0104   {
0105     if (aW(i) > aThreshold)
0106     {
0107       ++aResult.Rank;
0108     }
0109   }
0110 
0111   // Copy results back with original indexing
0112   aResult.U = math_Matrix(aRowLower, aRowLower + aM - 1, aColLower, aColLower + aN - 1);
0113   for (int i = 1; i <= aM; ++i)
0114   {
0115     for (int j = 1; j <= aN; ++j)
0116     {
0117       (*aResult.U)(aRowLower + i - 1, aColLower + j - 1) = aU(i, j);
0118     }
0119   }
0120 
0121   aResult.SingularValues = math_Vector(aColLower, aColLower + aN - 1);
0122   for (int i = 1; i <= aN; ++i)
0123   {
0124     (*aResult.SingularValues)(aColLower + i - 1) = aW(i);
0125   }
0126 
0127   aResult.V = math_Matrix(aColLower, aColLower + aN - 1, aColLower, aColLower + aN - 1);
0128   for (int i = 1; i <= aN; ++i)
0129   {
0130     for (int j = 1; j <= aN; ++j)
0131     {
0132       (*aResult.V)(aColLower + i - 1, aColLower + j - 1) = aV(i, j);
0133     }
0134   }
0135 
0136   aResult.Status = Status::OK;
0137   return aResult;
0138 }
0139 
0140 //! Solve linear system Ax = b using SVD decomposition.
0141 //! This is particularly useful for ill-conditioned or singular systems.
0142 //!
0143 //! For overdetermined systems (m > n), finds the least squares solution.
0144 //! For underdetermined systems (m < n), finds the minimum norm solution.
0145 //!
0146 //! @param theA coefficient matrix (m x n)
0147 //! @param theB right-hand side vector (length m)
0148 //! @param theTolerance for singular value threshold
0149 //! @return result containing solution vector
0150 inline LinearResult SolveSVD(const math_Matrix& theA,
0151                              const math_Vector& theB,
0152                              double             theTolerance = 1.0e-6)
0153 {
0154   LinearResult aResult;
0155 
0156   // Perform SVD
0157   SVDResult aSVD = SVD(theA, theTolerance);
0158   if (!aSVD.IsDone())
0159   {
0160     aResult.Status = aSVD.Status;
0161     return aResult;
0162   }
0163 
0164   const int aRowLower = theA.LowerRow();
0165   const int aRowUpper = theA.UpperRow();
0166   const int aColLower = theA.LowerCol();
0167   const int aColUpper = theA.UpperCol();
0168   const int aM        = aRowUpper - aRowLower + 1;
0169 
0170   // Check dimensions
0171   if (theB.Length() != aM)
0172   {
0173     aResult.Status = Status::InvalidInput;
0174     return aResult;
0175   }
0176 
0177   const int aBLower = theB.Lower(); // B vector may have different indexing
0178 
0179   const math_Matrix& aU = *aSVD.U;
0180   const math_Vector& aW = *aSVD.SingularValues;
0181   const math_Matrix& aV = *aSVD.V;
0182 
0183   // Compute threshold for singular values
0184   double aMaxSV = 0.0;
0185   for (int i = aColLower; i <= aColUpper; ++i)
0186   {
0187     aMaxSV = std::max(aMaxSV, aW(i));
0188   }
0189   double aWMin = theTolerance * aMaxSV;
0190 
0191   // Solve: x = V * diag(1/w) * U^T * b
0192   // First compute tmp = U^T * b
0193   math_Vector aTmp(aColLower, aColUpper, 0.0);
0194   for (int j = aColLower; j <= aColUpper; ++j)
0195   {
0196     double aSum = 0.0;
0197     for (int i = aRowLower; i <= aRowUpper; ++i)
0198     {
0199       // Map i to B's index space: B[aBLower + (i - aRowLower)]
0200       aSum += aU(i, j) * theB(aBLower + (i - aRowLower));
0201     }
0202     // Divide by singular value if above threshold
0203     if (aW(j) > aWMin)
0204     {
0205       aTmp(j) = aSum / aW(j);
0206     }
0207     // else aTmp(j) remains 0 (regularization)
0208   }
0209 
0210   // Compute x = V * tmp
0211   aResult.Solution = math_Vector(aColLower, aColUpper, 0.0);
0212   for (int i = aColLower; i <= aColUpper; ++i)
0213   {
0214     double aSum = 0.0;
0215     for (int j = aColLower; j <= aColUpper; ++j)
0216     {
0217       aSum += aV(i, j) * aTmp(j);
0218     }
0219     (*aResult.Solution)(i) = aSum;
0220   }
0221 
0222   aResult.Status = Status::OK;
0223   return aResult;
0224 }
0225 
0226 //! Compute pseudo-inverse (Moore-Penrose inverse) of matrix A.
0227 //! A^+ = V * diag(1/w) * U^T where singular values below threshold are set to 0.
0228 //!
0229 //! Properties:
0230 //! - A * A^+ * A = A
0231 //! - A^+ * A * A^+ = A^+
0232 //! - (A * A^+)^T = A * A^+
0233 //! - (A^+ * A)^T = A^+ * A
0234 //!
0235 //! @param theA input matrix (m x n)
0236 //! @param theTolerance for singular value threshold
0237 //! @return result containing pseudo-inverse matrix (n x m)
0238 inline InverseResult PseudoInverse(const math_Matrix& theA, double theTolerance = 1.0e-6)
0239 {
0240   InverseResult aResult;
0241 
0242   // Perform SVD
0243   SVDResult aSVD = SVD(theA, theTolerance);
0244   if (!aSVD.IsDone())
0245   {
0246     aResult.Status = aSVD.Status;
0247     return aResult;
0248   }
0249 
0250   const int aRowLower = theA.LowerRow();
0251   const int aRowUpper = theA.UpperRow();
0252   const int aColLower = theA.LowerCol();
0253   const int aColUpper = theA.UpperCol();
0254 
0255   const math_Matrix& aU = *aSVD.U;
0256   const math_Vector& aW = *aSVD.SingularValues;
0257   const math_Matrix& aV = *aSVD.V;
0258 
0259   // Compute threshold
0260   double aMaxSV = 0.0;
0261   for (int i = aColLower; i <= aColUpper; ++i)
0262   {
0263     aMaxSV = std::max(aMaxSV, aW(i));
0264   }
0265   double aWMin = theTolerance * aMaxSV;
0266 
0267   // Compute A^+ = V * diag(1/w) * U^T
0268   // Result is n x m
0269   aResult.Inverse = math_Matrix(aColLower, aColUpper, aRowLower, aRowUpper, 0.0);
0270 
0271   for (int i = aColLower; i <= aColUpper; ++i)
0272   {
0273     for (int j = aRowLower; j <= aRowUpper; ++j)
0274     {
0275       double aSum = 0.0;
0276       for (int k = aColLower; k <= aColUpper; ++k)
0277       {
0278         if (aW(k) > aWMin)
0279         {
0280           aSum += aV(i, k) * aU(j, k) / aW(k);
0281         }
0282       }
0283       (*aResult.Inverse)(i, j) = aSum;
0284     }
0285   }
0286 
0287   aResult.Status = Status::OK;
0288   return aResult;
0289 }
0290 
0291 //! Compute condition number of matrix using SVD.
0292 //! Condition number = sigma_max / sigma_min (ratio of largest to smallest singular value).
0293 //!
0294 //! High condition number (> 1e10) indicates ill-conditioned matrix.
0295 //!
0296 //! @param theA input matrix
0297 //! @return condition number (infinity if matrix is singular)
0298 inline double ConditionNumber(const math_Matrix& theA)
0299 {
0300   SVDResult aSVD = SVD(theA);
0301   if (!aSVD.IsDone())
0302   {
0303     return std::numeric_limits<double>::infinity();
0304   }
0305 
0306   const math_Vector& aW     = *aSVD.SingularValues;
0307   const int          aLower = aW.Lower();
0308   const int          aUpper = aW.Upper();
0309 
0310   double aMaxSV = 0.0;
0311   double aMinSV = std::numeric_limits<double>::max();
0312 
0313   for (int i = aLower; i <= aUpper; ++i)
0314   {
0315     if (aW(i) > 0.0)
0316     {
0317       aMaxSV = std::max(aMaxSV, aW(i));
0318       aMinSV = std::min(aMinSV, aW(i));
0319     }
0320   }
0321 
0322   if (aMinSV <= 0.0 || aMaxSV <= 0.0)
0323   {
0324     return std::numeric_limits<double>::infinity();
0325   }
0326 
0327   return aMaxSV / aMinSV;
0328 }
0329 
0330 //! Compute numerical rank of matrix using SVD.
0331 //! Rank is the number of singular values above the threshold.
0332 //!
0333 //! @param theA input matrix
0334 //! @param theTolerance relative tolerance for singular values
0335 //! @return numerical rank
0336 inline int NumericalRank(const math_Matrix& theA, double theTolerance = 1.0e-15)
0337 {
0338   SVDResult aSVD = SVD(theA, theTolerance);
0339   return aSVD.IsDone() ? aSVD.Rank : 0;
0340 }
0341 
0342 } // namespace MathLin
0343 
0344 #endif // _MathLin_SVD_HeaderFile