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_EigenSearch_HeaderFile
0015 #define _MathLin_EigenSearch_HeaderFile
0016 
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <math_Vector.hxx>
0020 #include <math_Matrix.hxx>
0021 
0022 #include <cmath>
0023 
0024 namespace MathLin
0025 {
0026 using namespace MathUtils;
0027 
0028 //! Result for eigenvalue decomposition of tridiagonal matrix.
0029 struct EigenResult
0030 {
0031   MathUtils::Status          Status = MathUtils::Status::NotConverged;
0032   std::optional<math_Vector> EigenValues;  //!< Computed eigenvalues
0033   std::optional<math_Matrix> EigenVectors; //!< Eigenvectors as columns
0034   int                        Dimension = 0;
0035 
0036   bool IsDone() const { return Status == MathUtils::Status::OK; }
0037 
0038   explicit operator bool() const { return IsDone(); }
0039 };
0040 
0041 namespace Internal
0042 {
0043 
0044 //! Computes sqrt(x*x + y*y) avoiding overflow/underflow.
0045 inline double Hypot(double theX, double theY)
0046 {
0047   return std::sqrt(theX * theX + theY * theY);
0048 }
0049 
0050 //! Finds the end of unreduced submatrix using deflation test.
0051 inline int FindSubmatrixEnd(const math_Vector& theDiag,
0052                             const math_Vector& theSubdiag,
0053                             int                theStart,
0054                             int                theN)
0055 {
0056   const int aLower = theDiag.Lower();
0057   int       aEnd;
0058   for (aEnd = theStart; aEnd <= theN - 1; ++aEnd)
0059   {
0060     const double aDiagSum =
0061       std::abs(theDiag(aEnd + aLower - 1)) + std::abs(theDiag(aEnd + 1 + aLower - 1));
0062     // Deflation test: subdiagonal negligible relative to diagonal elements
0063     if (std::abs(theSubdiag(aEnd + aLower - 1)) + aDiagSum == aDiagSum)
0064     {
0065       break;
0066     }
0067   }
0068   return aEnd;
0069 }
0070 
0071 //! Computes Wilkinson's shift for accelerated convergence.
0072 inline double ComputeWilkinsonShift(const math_Vector& theDiag,
0073                                     const math_Vector& theSubdiag,
0074                                     int                theStart,
0075                                     int                theEnd)
0076 {
0077   const int aLower = theDiag.Lower();
0078   double    aShift = (theDiag(theStart + 1 + aLower - 1) - theDiag(theStart + aLower - 1))
0079                   / (2.0 * theSubdiag(theStart + aLower - 1));
0080   const double aRadius = Hypot(1.0, aShift);
0081 
0082   if (aShift < 0.0)
0083   {
0084     aShift = theDiag(theEnd + aLower - 1) - theDiag(theStart + aLower - 1)
0085              + theSubdiag(theStart + aLower - 1) / (aShift - aRadius);
0086   }
0087   else
0088   {
0089     aShift = theDiag(theEnd + aLower - 1) - theDiag(theStart + aLower - 1)
0090              + theSubdiag(theStart + aLower - 1) / (aShift + aRadius);
0091   }
0092   return aShift;
0093 }
0094 
0095 //! Performs a single QL step with implicit shift.
0096 inline bool PerformQLStep(math_Vector& theDiag,
0097                           math_Vector& theSubdiag,
0098                           math_Matrix& theEigenVec,
0099                           int          theStart,
0100                           int          theEnd,
0101                           double       theShift,
0102                           int          theN)
0103 {
0104   const int aLowerD = theDiag.Lower();
0105   const int aLowerV = theEigenVec.LowerRow();
0106 
0107   double aSine     = 1.0;
0108   double aCosine   = 1.0;
0109   double aPrevDiag = 0.0;
0110   double aShift    = theShift;
0111   double aRadius   = 0.0;
0112 
0113   int aRowIdx;
0114   for (aRowIdx = theEnd - 1; aRowIdx >= theStart; --aRowIdx)
0115   {
0116     const double aTempVal                 = aSine * theSubdiag(aRowIdx + aLowerD - 1);
0117     const double aSubdiagTemp             = aCosine * theSubdiag(aRowIdx + aLowerD - 1);
0118     aRadius                               = Hypot(aTempVal, aShift);
0119     theSubdiag(aRowIdx + 1 + aLowerD - 1) = aRadius;
0120 
0121     if (aRadius == 0.0)
0122     {
0123       theDiag(aRowIdx + 1 + aLowerD - 1) -= aPrevDiag;
0124       theSubdiag(theEnd + aLowerD - 1) = 0.0;
0125       break;
0126     }
0127 
0128     aSine   = aTempVal / aRadius;
0129     aCosine = aShift / aRadius;
0130     aShift  = theDiag(aRowIdx + 1 + aLowerD - 1) - aPrevDiag;
0131 
0132     const double aRadiusTemp =
0133       (theDiag(aRowIdx + aLowerD - 1) - aShift) * aSine + 2.0 * aCosine * aSubdiagTemp;
0134     aPrevDiag                          = aSine * aRadiusTemp;
0135     theDiag(aRowIdx + 1 + aLowerD - 1) = aShift + aPrevDiag;
0136     aShift                             = aCosine * aRadiusTemp - aSubdiagTemp;
0137 
0138     // Update eigenvector matrix
0139     for (int aVecIdx = 1; aVecIdx <= theN; ++aVecIdx)
0140     {
0141       const double aTempVec = theEigenVec(aVecIdx + aLowerV - 1, aRowIdx + 1 + aLowerV - 1);
0142       theEigenVec(aVecIdx + aLowerV - 1, aRowIdx + 1 + aLowerV - 1) =
0143         aSine * theEigenVec(aVecIdx + aLowerV - 1, aRowIdx + aLowerV - 1) + aCosine * aTempVec;
0144       theEigenVec(aVecIdx + aLowerV - 1, aRowIdx + aLowerV - 1) =
0145         aCosine * theEigenVec(aVecIdx + aLowerV - 1, aRowIdx + aLowerV - 1) - aSine * aTempVec;
0146     }
0147   }
0148 
0149   if (aRadius == 0.0 && aRowIdx >= 1)
0150   {
0151     return true;
0152   }
0153 
0154   theDiag(theStart + aLowerD - 1) -= aPrevDiag;
0155   theSubdiag(theStart + aLowerD - 1) = aShift;
0156   theSubdiag(theEnd + aLowerD - 1)   = 0.0;
0157 
0158   return true;
0159 }
0160 
0161 } // namespace Internal
0162 
0163 //! Eigenvalue decomposition of symmetric tridiagonal matrix using QL algorithm.
0164 //!
0165 //! The QL algorithm with implicit Wilkinson shifts finds all eigenvalues and
0166 //! eigenvectors of a symmetric tridiagonal matrix T = Q * D * Q^T.
0167 //!
0168 //! Properties:
0169 //! - All eigenvalues are real (matrix is symmetric)
0170 //! - Eigenvectors are orthonormal
0171 //! - Numerically stable with implicit shifts
0172 //!
0173 //! @param theDiagonal diagonal elements of the tridiagonal matrix
0174 //! @param theSubdiagonal subdiagonal elements (one less than diagonal)
0175 //! @param theMaxIterations maximum iterations per eigenvalue (default 30)
0176 //! @return EigenResult containing eigenvalues and eigenvector matrix
0177 inline EigenResult EigenTridiagonal(const math_Vector& theDiagonal,
0178                                     const math_Vector& theSubdiagonal,
0179                                     int                theMaxIterations = 30)
0180 {
0181   EigenResult aResult;
0182 
0183   const int aN = theDiagonal.Length();
0184   if (theSubdiagonal.Length() != aN)
0185   {
0186     aResult.Status = Status::InvalidInput;
0187     return aResult;
0188   }
0189 
0190   aResult.Dimension = aN;
0191 
0192   // Create working copies
0193   math_Vector aDiag(1, aN);
0194   math_Vector aSubdiag(1, aN);
0195 
0196   // Copy diagonal
0197   for (int i = 1; i <= aN; ++i)
0198   {
0199     aDiag(i) = theDiagonal(theDiagonal.Lower() + i - 1);
0200   }
0201 
0202   // Shift subdiagonal: e[i-1] = e[i] for QL algorithm
0203   for (int i = 2; i <= aN; ++i)
0204   {
0205     aSubdiag(i - 1) = theSubdiagonal(theSubdiagonal.Lower() + i - 1);
0206   }
0207   aSubdiag(aN) = 0.0;
0208 
0209   // Initialize eigenvector matrix as identity
0210   math_Matrix aEigenVec(1, aN, 1, aN, 0.0);
0211   for (int i = 1; i <= aN; ++i)
0212   {
0213     aEigenVec(i, i) = 1.0;
0214   }
0215 
0216   // Special case: 1x1 matrix
0217   if (aN == 1)
0218   {
0219     aResult.EigenValues  = aDiag;
0220     aResult.EigenVectors = aEigenVec;
0221     aResult.Status       = Status::OK;
0222     return aResult;
0223   }
0224 
0225   // QL Algorithm with implicit shifts
0226   for (int aStart = 1; aStart <= aN; ++aStart)
0227   {
0228     int aIterCount = 0;
0229     int aEnd;
0230 
0231     do
0232     {
0233       aEnd = Internal::FindSubmatrixEnd(aDiag, aSubdiag, aStart, aN);
0234 
0235       if (aEnd != aStart)
0236       {
0237         if (aIterCount++ >= theMaxIterations)
0238         {
0239           aResult.Status = Status::MaxIterations;
0240           return aResult;
0241         }
0242 
0243         const double aShift = Internal::ComputeWilkinsonShift(aDiag, aSubdiag, aStart, aEnd);
0244 
0245         if (!Internal::PerformQLStep(aDiag, aSubdiag, aEigenVec, aStart, aEnd, aShift, aN))
0246         {
0247           aResult.Status = Status::NotConverged;
0248           return aResult;
0249         }
0250       }
0251     } while (aEnd != aStart);
0252   }
0253 
0254   aResult.EigenValues  = aDiag;
0255   aResult.EigenVectors = aEigenVec;
0256   aResult.Status       = Status::OK;
0257   return aResult;
0258 }
0259 
0260 //! Get a single eigenvector from the result.
0261 //!
0262 //! @param theResult eigenvalue decomposition result
0263 //! @param theIndex 1-based index of eigenvector
0264 //! @return eigenvector as math_Vector
0265 inline math_Vector GetEigenVector(const EigenResult& theResult, int theIndex)
0266 {
0267   if (!theResult.EigenVectors.has_value())
0268   {
0269     return math_Vector(1, 1);
0270   }
0271 
0272   const math_Matrix& aVecs   = *theResult.EigenVectors;
0273   const int          aN      = aVecs.RowNumber();
0274   const int          aLowerR = aVecs.LowerRow();
0275   const int          aLowerC = aVecs.LowerCol();
0276 
0277   math_Vector aVec(1, aN);
0278   for (int i = 1; i <= aN; ++i)
0279   {
0280     aVec(i) = aVecs(i + aLowerR - 1, theIndex + aLowerC - 1);
0281   }
0282   return aVec;
0283 }
0284 
0285 } // namespace MathLin
0286 
0287 #endif // _MathLin_EigenSearch_HeaderFile