File indexing completed on 2026-09-28 09:20:50
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
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
0029 struct EigenResult
0030 {
0031 MathUtils::Status Status = MathUtils::Status::NotConverged;
0032 std::optional<math_Vector> EigenValues;
0033 std::optional<math_Matrix> EigenVectors;
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
0045 inline double Hypot(double theX, double theY)
0046 {
0047 return std::sqrt(theX * theX + theY * theY);
0048 }
0049
0050
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
0063 if (std::abs(theSubdiag(aEnd + aLower - 1)) + aDiagSum == aDiagSum)
0064 {
0065 break;
0066 }
0067 }
0068 return aEnd;
0069 }
0070
0071
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
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
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 }
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
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
0193 math_Vector aDiag(1, aN);
0194 math_Vector aSubdiag(1, aN);
0195
0196
0197 for (int i = 1; i <= aN; ++i)
0198 {
0199 aDiag(i) = theDiagonal(theDiagonal.Lower() + i - 1);
0200 }
0201
0202
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
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
0217 if (aN == 1)
0218 {
0219 aResult.EigenValues = aDiag;
0220 aResult.EigenVectors = aEigenVec;
0221 aResult.Status = Status::OK;
0222 return aResult;
0223 }
0224
0225
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
0261
0262
0263
0264
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 }
0286
0287 #endif