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_LeastSquares_HeaderFile
0015 #define _MathLin_LeastSquares_HeaderFile
0016
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <MathLin_Gauss.hxx>
0020 #include <MathLin_SVD.hxx>
0021 #include <MathLin_Householder.hxx>
0022 #include <MathUtils_Core.hxx>
0023
0024 #include <cmath>
0025
0026 namespace MathLin
0027 {
0028 using namespace MathUtils;
0029
0030
0031 enum class LeastSquaresMethod
0032 {
0033 NormalEquations,
0034 QR,
0035 SVD
0036 };
0037
0038
0039 struct LeastSquaresResult
0040 {
0041 MathUtils::Status Status = MathUtils::Status::NotConverged;
0042 std::optional<math_Vector> Solution;
0043 std::optional<double> Residual;
0044 std::optional<double> ResidualSq;
0045 int Rank = 0;
0046
0047 bool IsDone() const { return Status == MathUtils::Status::OK; }
0048
0049 explicit operator bool() const { return IsDone(); }
0050 };
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067 inline LeastSquaresResult LeastSquares(const math_Matrix& theA,
0068 const math_Vector& theB,
0069 LeastSquaresMethod theMethod = LeastSquaresMethod::QR,
0070 double theTolerance = 1.0e-15)
0071 {
0072 LeastSquaresResult aResult;
0073
0074 const int aRowLower = theA.LowerRow();
0075 const int aRowUpper = theA.UpperRow();
0076 const int aColLower = theA.LowerCol();
0077 const int aColUpper = theA.UpperCol();
0078 const int aM = aRowUpper - aRowLower + 1;
0079 const int aN = aColUpper - aColLower + 1;
0080
0081
0082 if (theB.Length() != aM)
0083 {
0084 aResult.Status = Status::InvalidInput;
0085 return aResult;
0086 }
0087
0088 LinearResult aLinResult;
0089
0090 switch (theMethod)
0091 {
0092 case LeastSquaresMethod::NormalEquations: {
0093
0094 math_Matrix aAtA(aColLower, aColUpper, aColLower, aColUpper, 0.0);
0095 math_Vector aAtb(aColLower, aColUpper, 0.0);
0096
0097
0098 for (int i = aColLower; i <= aColUpper; ++i)
0099 {
0100 for (int j = aColLower; j <= aColUpper; ++j)
0101 {
0102 double aSum = 0.0;
0103 for (int k = aRowLower; k <= aRowUpper; ++k)
0104 {
0105 aSum += theA(k, i) * theA(k, j);
0106 }
0107 aAtA(i, j) = aSum;
0108 }
0109 }
0110
0111
0112 for (int i = aColLower; i <= aColUpper; ++i)
0113 {
0114 double aSum = 0.0;
0115 for (int k = aRowLower; k <= aRowUpper; ++k)
0116 {
0117 aSum += theA(k, i) * theB(theB.Lower() + k - aRowLower);
0118 }
0119 aAtb(i) = aSum;
0120 }
0121
0122
0123 aLinResult = Solve(aAtA, aAtb, theTolerance);
0124 aResult.Rank = (aLinResult.IsDone()) ? aN : 0;
0125 }
0126 break;
0127
0128 case LeastSquaresMethod::QR: {
0129 aLinResult = SolveQR(theA, theB, theTolerance);
0130 if (aLinResult.IsDone())
0131 {
0132
0133 aResult.Rank = aN;
0134 }
0135 }
0136 break;
0137
0138 case LeastSquaresMethod::SVD: {
0139 aLinResult = SolveSVD(theA, theB, theTolerance);
0140 if (aLinResult.IsDone())
0141 {
0142
0143 SVDResult aSVD = SVD(theA, theTolerance);
0144 aResult.Rank = aSVD.IsDone() ? aSVD.Rank : 0;
0145 }
0146 }
0147 break;
0148 }
0149
0150 if (!aLinResult.IsDone())
0151 {
0152 aResult.Status = aLinResult.Status;
0153 return aResult;
0154 }
0155
0156 aResult.Solution = aLinResult.Solution;
0157
0158
0159 const math_Vector& aX = *aResult.Solution;
0160 double aResidualSq = 0.0;
0161
0162 for (int i = aRowLower; i <= aRowUpper; ++i)
0163 {
0164 double aAxi = 0.0;
0165 for (int j = aColLower; j <= aColUpper; ++j)
0166 {
0167 aAxi += theA(i, j) * aX(j);
0168 }
0169 double aRi = aAxi - theB(theB.Lower() + i - aRowLower);
0170 aResidualSq += aRi * aRi;
0171 }
0172
0173 aResult.ResidualSq = aResidualSq;
0174 aResult.Residual = std::sqrt(aResidualSq);
0175 aResult.Status = Status::OK;
0176 return aResult;
0177 }
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190 inline LeastSquaresResult WeightedLeastSquares(
0191 const math_Matrix& theA,
0192 const math_Vector& theB,
0193 const math_Vector& theW,
0194 LeastSquaresMethod theMethod = LeastSquaresMethod::QR,
0195 double theTolerance = 1.0e-15)
0196 {
0197 LeastSquaresResult aResult;
0198
0199 const int aRowLower = theA.LowerRow();
0200 const int aRowUpper = theA.UpperRow();
0201 const int aColLower = theA.LowerCol();
0202 const int aColUpper = theA.UpperCol();
0203 const int aM = aRowUpper - aRowLower + 1;
0204
0205
0206 if (theB.Length() != aM || theW.Length() != aM)
0207 {
0208 aResult.Status = Status::InvalidInput;
0209 return aResult;
0210 }
0211
0212
0213 for (int i = theW.Lower(); i <= theW.Upper(); ++i)
0214 {
0215 if (theW(i) <= 0.0)
0216 {
0217 aResult.Status = Status::InvalidInput;
0218 return aResult;
0219 }
0220 }
0221
0222
0223 math_Matrix aWA(aRowLower, aRowUpper, aColLower, aColUpper);
0224 math_Vector aWB(theB.Lower(), theB.Upper());
0225
0226 for (int i = aRowLower; i <= aRowUpper; ++i)
0227 {
0228 double aSqrtW = std::sqrt(theW(theW.Lower() + i - aRowLower));
0229 for (int j = aColLower; j <= aColUpper; ++j)
0230 {
0231 aWA(i, j) = aSqrtW * theA(i, j);
0232 }
0233 aWB(theB.Lower() + i - aRowLower) = aSqrtW * theB(theB.Lower() + i - aRowLower);
0234 }
0235
0236
0237 return LeastSquares(aWA, aWB, theMethod, theTolerance);
0238 }
0239
0240
0241
0242
0243
0244
0245
0246
0247
0248
0249
0250
0251 inline LeastSquaresResult RegularizedLeastSquares(const math_Matrix& theA,
0252 const math_Vector& theB,
0253 double theLambda,
0254 double theTolerance = 1.0e-15)
0255 {
0256 LeastSquaresResult aResult;
0257
0258 const int aRowLower = theA.LowerRow();
0259 const int aRowUpper = theA.UpperRow();
0260 const int aColLower = theA.LowerCol();
0261 const int aColUpper = theA.UpperCol();
0262 const int aM = aRowUpper - aRowLower + 1;
0263 const int aN = aColUpper - aColLower + 1;
0264
0265
0266 if (theB.Length() != aM)
0267 {
0268 aResult.Status = Status::InvalidInput;
0269 return aResult;
0270 }
0271
0272 if (theLambda < 0.0)
0273 {
0274 aResult.Status = Status::InvalidInput;
0275 return aResult;
0276 }
0277
0278
0279 math_Matrix aAtA(aColLower, aColUpper, aColLower, aColUpper, 0.0);
0280 math_Vector aAtb(aColLower, aColUpper, 0.0);
0281
0282
0283 for (int i = aColLower; i <= aColUpper; ++i)
0284 {
0285 for (int j = aColLower; j <= aColUpper; ++j)
0286 {
0287 double aSum = 0.0;
0288 for (int k = aRowLower; k <= aRowUpper; ++k)
0289 {
0290 aSum += theA(k, i) * theA(k, j);
0291 }
0292 aAtA(i, j) = aSum;
0293 }
0294
0295 aAtA(i, i) += theLambda;
0296 }
0297
0298
0299 for (int i = aColLower; i <= aColUpper; ++i)
0300 {
0301 double aSum = 0.0;
0302 for (int k = aRowLower; k <= aRowUpper; ++k)
0303 {
0304 aSum += theA(k, i) * theB(theB.Lower() + k - aRowLower);
0305 }
0306 aAtb(i) = aSum;
0307 }
0308
0309
0310 LinearResult aLinResult = Solve(aAtA, aAtb, theTolerance);
0311 if (!aLinResult.IsDone())
0312 {
0313 aResult.Status = aLinResult.Status;
0314 return aResult;
0315 }
0316
0317 aResult.Solution = aLinResult.Solution;
0318 aResult.Rank = aN;
0319
0320
0321 const math_Vector& aX = *aResult.Solution;
0322 double aResidualSq = 0.0;
0323
0324 for (int i = aRowLower; i <= aRowUpper; ++i)
0325 {
0326 double aAxi = 0.0;
0327 for (int j = aColLower; j <= aColUpper; ++j)
0328 {
0329 aAxi += theA(i, j) * aX(j);
0330 }
0331 double aRi = aAxi - theB(theB.Lower() + i - aRowLower);
0332 aResidualSq += aRi * aRi;
0333 }
0334
0335 aResult.ResidualSq = aResidualSq;
0336 aResult.Residual = std::sqrt(aResidualSq);
0337 aResult.Status = Status::OK;
0338 return aResult;
0339 }
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352 inline double OptimalRegularization(const math_Matrix& theA,
0353 const math_Vector& theB,
0354 double theLambdaMin = 1.0e-10,
0355 double theLambdaMax = 1.0e2,
0356 int theNbPoints = 20)
0357 {
0358 double aBestLambda = theLambdaMin;
0359 double aBestScore = std::numeric_limits<double>::max();
0360
0361
0362 const double aLogMin = std::log10(theLambdaMin);
0363 const double aLogMax = std::log10(theLambdaMax);
0364 const double aLogStep = (aLogMax - aLogMin) / (theNbPoints - 1);
0365
0366 for (int k = 0; k < theNbPoints; ++k)
0367 {
0368 double aLambda = std::pow(10.0, aLogMin + k * aLogStep);
0369
0370 auto aResult = RegularizedLeastSquares(theA, theB, aLambda);
0371 if (aResult.IsDone() && aResult.ResidualSq)
0372 {
0373 double aScore = *aResult.ResidualSq;
0374 if (aScore < aBestScore)
0375 {
0376 aBestScore = aScore;
0377 aBestLambda = aLambda;
0378 }
0379 }
0380 }
0381
0382 return aBestLambda;
0383 }
0384
0385 }
0386
0387 #endif