File indexing completed on 2026-09-28 09:20:52
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #ifndef _MathSys_LevenbergMarquardt_HeaderFile
0015 #define _MathSys_LevenbergMarquardt_HeaderFile
0016
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <MathLin_Gauss.hxx>
0020 #include <MathUtils_Core.hxx>
0021
0022 #include <cmath>
0023
0024 namespace MathSys
0025 {
0026 using namespace MathUtils;
0027
0028
0029
0030 struct LMConfig : Config
0031 {
0032 double LambdaInit = 1.0e-3;
0033 double LambdaIncrease = 10.0;
0034 double LambdaDecrease = 0.1;
0035 double LambdaMax = 1.0e10;
0036 double LambdaMin = 1.0e-12;
0037
0038
0039 LMConfig() = default;
0040
0041
0042
0043
0044 explicit LMConfig(double theTolerance, int theMaxIter = 100)
0045 : Config(theTolerance, theMaxIter)
0046 {
0047 }
0048 };
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071 template <typename FuncSetType>
0072 VectorResult LevenbergMarquardt(FuncSetType& theFunc,
0073 const math_Vector& theStart,
0074 const LMConfig& theConfig = LMConfig())
0075 {
0076 VectorResult aResult;
0077
0078 const int aNbVars = theFunc.NbVariables();
0079 const int aNbEqs = theFunc.NbEquations();
0080
0081
0082 if (theStart.Length() != aNbVars)
0083 {
0084 aResult.Status = Status::InvalidInput;
0085 return aResult;
0086 }
0087
0088 const int aVarLower = theStart.Lower();
0089 const int aVarUpper = theStart.Upper();
0090
0091
0092 math_Vector aSol = theStart;
0093 math_Vector aF(1, aNbEqs);
0094 math_Vector aFNew(1, aNbEqs);
0095 math_Vector aDeltaX(aVarLower, aVarUpper);
0096 math_Vector aGrad(aVarLower, aVarUpper);
0097 math_Matrix aJac(1, aNbEqs, aVarLower, aVarUpper);
0098 math_Matrix aJtJ(aVarLower, aVarUpper, aVarLower, aVarUpper);
0099 math_Vector aJtF(aVarLower, aVarUpper);
0100
0101 double aLambda = theConfig.LambdaInit;
0102
0103
0104 if (!theFunc.Value(aSol, aF))
0105 {
0106 aResult.Status = Status::NumericalError;
0107 return aResult;
0108 }
0109
0110
0111 double aChi2 = 0.0;
0112 for (int i = 1; i <= aNbEqs; ++i)
0113 {
0114 aChi2 += aF(i) * aF(i);
0115 }
0116
0117
0118 for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0119 {
0120 aResult.NbIterations = anIter + 1;
0121
0122
0123 bool aFConverged = true;
0124 for (int i = 1; i <= aNbEqs; ++i)
0125 {
0126 if (std::abs(aF(i)) > theConfig.FTolerance)
0127 {
0128 aFConverged = false;
0129 break;
0130 }
0131 }
0132
0133 if (aFConverged)
0134 {
0135 aResult.Status = Status::OK;
0136 aResult.Solution = aSol;
0137 aResult.Value = aChi2;
0138 return aResult;
0139 }
0140
0141
0142 if (!theFunc.Derivatives(aSol, aJac))
0143 {
0144 aResult.Status = Status::NumericalError;
0145 return aResult;
0146 }
0147
0148
0149 for (int i = aVarLower; i <= aVarUpper; ++i)
0150 {
0151 for (int j = aVarLower; j <= aVarUpper; ++j)
0152 {
0153 double aSum = 0.0;
0154 for (int k = 1; k <= aNbEqs; ++k)
0155 {
0156 aSum += aJac(k, i) * aJac(k, j);
0157 }
0158 aJtJ(i, j) = aSum;
0159 }
0160 }
0161
0162
0163 for (int i = aVarLower; i <= aVarUpper; ++i)
0164 {
0165 double aSum = 0.0;
0166 for (int k = 1; k <= aNbEqs; ++k)
0167 {
0168 aSum += aJac(k, i) * aF(k);
0169 }
0170 aJtF(i) = aSum;
0171 aGrad(i) = 2.0 * aSum;
0172 }
0173
0174
0175 double aGradNorm = 0.0;
0176 for (int i = aVarLower; i <= aVarUpper; ++i)
0177 {
0178 aGradNorm += aGrad(i) * aGrad(i);
0179 }
0180 aGradNorm = std::sqrt(aGradNorm);
0181
0182 if (aGradNorm < theConfig.Tolerance)
0183 {
0184 aResult.Status = Status::OK;
0185 aResult.Solution = aSol;
0186 aResult.Value = aChi2;
0187 aResult.Gradient = aGrad;
0188 return aResult;
0189 }
0190
0191
0192 bool aStepAccepted = false;
0193 for (int aLamIter = 0; aLamIter < 20 && !aStepAccepted; ++aLamIter)
0194 {
0195
0196 math_Matrix aDamped = aJtJ;
0197 for (int i = aVarLower; i <= aVarUpper; ++i)
0198 {
0199 aDamped(i, i) += aLambda;
0200 }
0201
0202
0203 math_Vector aNegJtF(aVarLower, aVarUpper);
0204 for (int i = aVarLower; i <= aVarUpper; ++i)
0205 {
0206 aNegJtF(i) = -aJtF(i);
0207 }
0208
0209 auto aLinResult = MathLin::Solve(aDamped, aNegJtF);
0210 if (!aLinResult.IsDone())
0211 {
0212
0213 aLambda *= theConfig.LambdaIncrease;
0214 if (aLambda > theConfig.LambdaMax)
0215 {
0216 aResult.Status = Status::Singular;
0217 aResult.Solution = aSol;
0218 aResult.Value = aChi2;
0219 return aResult;
0220 }
0221 continue;
0222 }
0223
0224 aDeltaX = *aLinResult.Solution;
0225
0226
0227 math_Vector aSolNew(aVarLower, aVarUpper);
0228 for (int i = aVarLower; i <= aVarUpper; ++i)
0229 {
0230 aSolNew(i) = aSol(i) + aDeltaX(i);
0231 }
0232
0233
0234 if (!theFunc.Value(aSolNew, aFNew))
0235 {
0236
0237 aLambda *= theConfig.LambdaIncrease;
0238 if (aLambda > theConfig.LambdaMax)
0239 {
0240 aResult.Status = Status::NumericalError;
0241 aResult.Solution = aSol;
0242 aResult.Value = aChi2;
0243 return aResult;
0244 }
0245 continue;
0246 }
0247
0248
0249 double aChi2New = 0.0;
0250 for (int i = 1; i <= aNbEqs; ++i)
0251 {
0252 aChi2New += aFNew(i) * aFNew(i);
0253 }
0254
0255
0256 if (aChi2New < aChi2)
0257 {
0258
0259 aSol = aSolNew;
0260 aF = aFNew;
0261 aChi2 = aChi2New;
0262 aLambda *= theConfig.LambdaDecrease;
0263 if (aLambda < theConfig.LambdaMin)
0264 {
0265 aLambda = theConfig.LambdaMin;
0266 }
0267 aStepAccepted = true;
0268
0269
0270 bool aXConverged = true;
0271 for (int i = aVarLower; i <= aVarUpper; ++i)
0272 {
0273 if (std::abs(aDeltaX(i)) > theConfig.XTolerance * (1.0 + std::abs(aSol(i))))
0274 {
0275 aXConverged = false;
0276 break;
0277 }
0278 }
0279
0280 if (aXConverged)
0281 {
0282 aResult.Status = Status::OK;
0283 aResult.Solution = aSol;
0284 aResult.Value = aChi2;
0285 return aResult;
0286 }
0287 }
0288 else
0289 {
0290
0291 aLambda *= theConfig.LambdaIncrease;
0292 if (aLambda > theConfig.LambdaMax)
0293 {
0294 aResult.Status = Status::NotConverged;
0295 aResult.Solution = aSol;
0296 aResult.Value = aChi2;
0297 return aResult;
0298 }
0299 }
0300 }
0301
0302 if (!aStepAccepted)
0303 {
0304
0305 aResult.Status = Status::NotConverged;
0306 aResult.Solution = aSol;
0307 aResult.Value = aChi2;
0308 return aResult;
0309 }
0310 }
0311
0312
0313 aResult.Status = Status::MaxIterations;
0314 aResult.Solution = aSol;
0315 aResult.Value = aChi2;
0316 return aResult;
0317 }
0318
0319
0320
0321
0322
0323
0324
0325
0326
0327
0328
0329
0330 template <typename FuncSetType>
0331 VectorResult LevenbergMarquardtBounded(FuncSetType& theFunc,
0332 const math_Vector& theStart,
0333 const math_Vector& theInfBound,
0334 const math_Vector& theSupBound,
0335 const LMConfig& theConfig = LMConfig())
0336 {
0337 VectorResult aResult;
0338
0339 const int aNbVars = theFunc.NbVariables();
0340 const int aNbEqs = theFunc.NbEquations();
0341
0342
0343 if (theStart.Length() != aNbVars || theInfBound.Length() != aNbVars
0344 || theSupBound.Length() != aNbVars)
0345 {
0346 aResult.Status = Status::InvalidInput;
0347 return aResult;
0348 }
0349
0350 const int aVarLower = theStart.Lower();
0351 const int aVarUpper = theStart.Upper();
0352
0353
0354 math_Vector aSol = theStart;
0355 math_Vector aF(1, aNbEqs);
0356 math_Vector aFNew(1, aNbEqs);
0357 math_Vector aDeltaX(aVarLower, aVarUpper);
0358 math_Vector aGrad(aVarLower, aVarUpper);
0359 math_Matrix aJac(1, aNbEqs, aVarLower, aVarUpper);
0360 math_Matrix aJtJ(aVarLower, aVarUpper, aVarLower, aVarUpper);
0361 math_Vector aJtF(aVarLower, aVarUpper);
0362
0363
0364 for (int i = aVarLower; i <= aVarUpper; ++i)
0365 {
0366 aSol(i) = MathUtils::Clamp(aSol(i), theInfBound(i), theSupBound(i));
0367 }
0368
0369 double aLambda = theConfig.LambdaInit;
0370
0371
0372 if (!theFunc.Value(aSol, aF))
0373 {
0374 aResult.Status = Status::NumericalError;
0375 return aResult;
0376 }
0377
0378
0379 double aChi2 = 0.0;
0380 for (int i = 1; i <= aNbEqs; ++i)
0381 {
0382 aChi2 += aF(i) * aF(i);
0383 }
0384
0385
0386 for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0387 {
0388 aResult.NbIterations = anIter + 1;
0389
0390
0391 bool aFConverged = true;
0392 for (int i = 1; i <= aNbEqs; ++i)
0393 {
0394 if (std::abs(aF(i)) > theConfig.FTolerance)
0395 {
0396 aFConverged = false;
0397 break;
0398 }
0399 }
0400
0401 if (aFConverged)
0402 {
0403 aResult.Status = Status::OK;
0404 aResult.Solution = aSol;
0405 aResult.Value = aChi2;
0406 return aResult;
0407 }
0408
0409
0410 if (!theFunc.Derivatives(aSol, aJac))
0411 {
0412 aResult.Status = Status::NumericalError;
0413 return aResult;
0414 }
0415
0416
0417 for (int i = aVarLower; i <= aVarUpper; ++i)
0418 {
0419 for (int j = aVarLower; j <= aVarUpper; ++j)
0420 {
0421 double aSum = 0.0;
0422 for (int k = 1; k <= aNbEqs; ++k)
0423 {
0424 aSum += aJac(k, i) * aJac(k, j);
0425 }
0426 aJtJ(i, j) = aSum;
0427 }
0428 }
0429
0430
0431 for (int i = aVarLower; i <= aVarUpper; ++i)
0432 {
0433 double aSum = 0.0;
0434 for (int k = 1; k <= aNbEqs; ++k)
0435 {
0436 aSum += aJac(k, i) * aF(k);
0437 }
0438 aJtF(i) = aSum;
0439 aGrad(i) = 2.0 * aSum;
0440 }
0441
0442
0443 double aGradNorm = 0.0;
0444 for (int i = aVarLower; i <= aVarUpper; ++i)
0445 {
0446 aGradNorm += aGrad(i) * aGrad(i);
0447 }
0448 aGradNorm = std::sqrt(aGradNorm);
0449
0450 if (aGradNorm < theConfig.Tolerance)
0451 {
0452 aResult.Status = Status::OK;
0453 aResult.Solution = aSol;
0454 aResult.Value = aChi2;
0455 aResult.Gradient = aGrad;
0456 return aResult;
0457 }
0458
0459
0460 bool aStepAccepted = false;
0461 for (int aLamIter = 0; aLamIter < 20 && !aStepAccepted; ++aLamIter)
0462 {
0463
0464 math_Matrix aDamped = aJtJ;
0465 for (int i = aVarLower; i <= aVarUpper; ++i)
0466 {
0467 aDamped(i, i) += aLambda;
0468 }
0469
0470
0471 math_Vector aNegJtF(aVarLower, aVarUpper);
0472 for (int i = aVarLower; i <= aVarUpper; ++i)
0473 {
0474 aNegJtF(i) = -aJtF(i);
0475 }
0476
0477 auto aLinResult = MathLin::Solve(aDamped, aNegJtF);
0478 if (!aLinResult.IsDone())
0479 {
0480 aLambda *= theConfig.LambdaIncrease;
0481 if (aLambda > theConfig.LambdaMax)
0482 {
0483 aResult.Status = Status::Singular;
0484 aResult.Solution = aSol;
0485 aResult.Value = aChi2;
0486 return aResult;
0487 }
0488 continue;
0489 }
0490
0491 aDeltaX = *aLinResult.Solution;
0492
0493
0494 math_Vector aSolNew(aVarLower, aVarUpper);
0495 for (int i = aVarLower; i <= aVarUpper; ++i)
0496 {
0497 aSolNew(i) = MathUtils::Clamp(aSol(i) + aDeltaX(i), theInfBound(i), theSupBound(i));
0498 }
0499
0500
0501 if (!theFunc.Value(aSolNew, aFNew))
0502 {
0503 aLambda *= theConfig.LambdaIncrease;
0504 if (aLambda > theConfig.LambdaMax)
0505 {
0506 aResult.Status = Status::NumericalError;
0507 aResult.Solution = aSol;
0508 aResult.Value = aChi2;
0509 return aResult;
0510 }
0511 continue;
0512 }
0513
0514
0515 double aChi2New = 0.0;
0516 for (int i = 1; i <= aNbEqs; ++i)
0517 {
0518 aChi2New += aFNew(i) * aFNew(i);
0519 }
0520
0521
0522 if (aChi2New < aChi2)
0523 {
0524 aSol = aSolNew;
0525 aF = aFNew;
0526 aChi2 = aChi2New;
0527 aLambda *= theConfig.LambdaDecrease;
0528 if (aLambda < theConfig.LambdaMin)
0529 {
0530 aLambda = theConfig.LambdaMin;
0531 }
0532 aStepAccepted = true;
0533
0534
0535 bool aXConverged = true;
0536 for (int i = aVarLower; i <= aVarUpper; ++i)
0537 {
0538 if (std::abs(aDeltaX(i)) > theConfig.XTolerance * (1.0 + std::abs(aSol(i))))
0539 {
0540 aXConverged = false;
0541 break;
0542 }
0543 }
0544
0545 if (aXConverged)
0546 {
0547 aResult.Status = Status::OK;
0548 aResult.Solution = aSol;
0549 aResult.Value = aChi2;
0550 return aResult;
0551 }
0552 }
0553 else
0554 {
0555 aLambda *= theConfig.LambdaIncrease;
0556 if (aLambda > theConfig.LambdaMax)
0557 {
0558 aResult.Status = Status::NotConverged;
0559 aResult.Solution = aSol;
0560 aResult.Value = aChi2;
0561 return aResult;
0562 }
0563 }
0564 }
0565
0566 if (!aStepAccepted)
0567 {
0568 aResult.Status = Status::NotConverged;
0569 aResult.Solution = aSol;
0570 aResult.Value = aChi2;
0571 return aResult;
0572 }
0573 }
0574
0575
0576 aResult.Status = Status::MaxIterations;
0577 aResult.Solution = aSol;
0578 aResult.Value = aChi2;
0579 return aResult;
0580 }
0581
0582 }
0583
0584 #endif