File indexing completed on 2026-09-28 09:20:51
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #ifndef _MathOpt_Newton_HeaderFile
0015 #define _MathOpt_Newton_HeaderFile
0016
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <MathLin_Gauss.hxx>
0020 #include <MathUtils_Core.hxx>
0021 #include <MathUtils_LineSearch.hxx>
0022 #include <MathUtils_Deriv.hxx>
0023
0024 #include <cmath>
0025
0026 namespace MathOpt
0027 {
0028 using namespace MathUtils;
0029
0030
0031 struct NewtonConfig : Config
0032 {
0033 double Regularization = 1.0e-8;
0034 bool UseLineSearch = true;
0035
0036
0037 NewtonConfig() = default;
0038
0039
0040 explicit NewtonConfig(double theTolerance, int theMaxIter = 100)
0041 : Config(theTolerance, theMaxIter)
0042 {
0043 }
0044 };
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068 template <typename Function>
0069 VectorResult Newton(Function& theFunc,
0070 const math_Vector& theStartingPoint,
0071 const NewtonConfig& theConfig = NewtonConfig())
0072 {
0073 VectorResult aResult;
0074
0075 const int aLower = theStartingPoint.Lower();
0076 const int aUpper = theStartingPoint.Upper();
0077
0078
0079 math_Vector aX(aLower, aUpper);
0080 aX = theStartingPoint;
0081
0082 double aFx = 0.0;
0083 if (!theFunc.Value(aX, aFx))
0084 {
0085 aResult.Status = Status::NumericalError;
0086 return aResult;
0087 }
0088
0089
0090 math_Vector aGrad(aLower, aUpper);
0091 if (!theFunc.Gradient(aX, aGrad))
0092 {
0093 aResult.Status = Status::NumericalError;
0094 return aResult;
0095 }
0096
0097
0098 double aGradNorm = 0.0;
0099 for (int i = aLower; i <= aUpper; ++i)
0100 {
0101 aGradNorm += MathUtils::Sqr(aGrad(i));
0102 }
0103 aGradNorm = std::sqrt(aGradNorm);
0104
0105 if (aGradNorm < theConfig.FTolerance)
0106 {
0107 aResult.Status = Status::OK;
0108 aResult.Solution = aX;
0109 aResult.Value = aFx;
0110 aResult.Gradient = aGrad;
0111 return aResult;
0112 }
0113
0114
0115 math_Vector aDir(aLower, aUpper);
0116 math_Vector aXNew(aLower, aUpper);
0117 math_Vector aGradNew(aLower, aUpper);
0118 math_Matrix aHessian(aLower, aUpper, aLower, aUpper);
0119 math_Vector aNegGrad(aLower, aUpper);
0120
0121 for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0122 {
0123 aResult.NbIterations = anIter + 1;
0124
0125
0126 if (!theFunc.Hessian(aX, aHessian))
0127 {
0128 aResult.Status = Status::NumericalError;
0129 aResult.Solution = aX;
0130 aResult.Value = aFx;
0131 return aResult;
0132 }
0133
0134
0135 for (int i = aLower; i <= aUpper; ++i)
0136 {
0137 aNegGrad(i) = -aGrad(i);
0138 }
0139
0140
0141 auto aLinResult = MathLin::Solve(aHessian, aNegGrad);
0142
0143 if (!aLinResult.IsDone())
0144 {
0145
0146 double aLambda = theConfig.Regularization;
0147 bool aSolved = false;
0148
0149 for (int k = 0; k < 10 && !aSolved; ++k)
0150 {
0151 math_Matrix aRegHessian = aHessian;
0152 for (int i = aLower; i <= aUpper; ++i)
0153 {
0154 aRegHessian(i, i) += aLambda;
0155 }
0156
0157 aLinResult = MathLin::Solve(aRegHessian, aNegGrad);
0158 if (aLinResult.IsDone())
0159 {
0160 aSolved = true;
0161 }
0162 else
0163 {
0164 aLambda *= 10.0;
0165 }
0166 }
0167
0168 if (!aSolved)
0169 {
0170
0171 for (int i = aLower; i <= aUpper; ++i)
0172 {
0173 aDir(i) = -aGrad(i);
0174 }
0175 goto perform_line_search;
0176 }
0177 }
0178
0179 aDir = *aLinResult.Solution;
0180
0181
0182 {
0183 double aDirDeriv = 0.0;
0184 for (int i = aLower; i <= aUpper; ++i)
0185 {
0186 aDirDeriv += aGrad(i) * aDir(i);
0187 }
0188
0189 if (aDirDeriv >= 0.0)
0190 {
0191
0192 for (int i = aLower; i <= aUpper; ++i)
0193 {
0194 aDir(i) = -aGrad(i);
0195 }
0196 }
0197 }
0198
0199 perform_line_search:
0200 if (theConfig.UseLineSearch)
0201 {
0202
0203 MathUtils::LineSearchResult aLineResult =
0204 MathUtils::ArmijoBacktrack(theFunc, aX, aDir, aGrad, aFx, 1.0, 1.0e-4, 0.5, 50);
0205
0206 if (!aLineResult.IsValid)
0207 {
0208
0209 for (int i = aLower; i <= aUpper; ++i)
0210 {
0211 aDir(i) = -aGrad(i);
0212 }
0213 aLineResult =
0214 MathUtils::ArmijoBacktrack(theFunc, aX, aDir, aGrad, aFx, 1.0, 1.0e-4, 0.5, 50);
0215
0216 if (!aLineResult.IsValid)
0217 {
0218 aResult.Status = Status::NotConverged;
0219 aResult.Solution = aX;
0220 aResult.Value = aFx;
0221 aResult.Gradient = aGrad;
0222 return aResult;
0223 }
0224 }
0225
0226
0227 for (int i = aLower; i <= aUpper; ++i)
0228 {
0229 aXNew(i) = aX(i) + aLineResult.Alpha * aDir(i);
0230 }
0231 aFx = aLineResult.FNew;
0232 }
0233 else
0234 {
0235
0236 for (int i = aLower; i <= aUpper; ++i)
0237 {
0238 aXNew(i) = aX(i) + aDir(i);
0239 }
0240
0241 if (!theFunc.Value(aXNew, aFx))
0242 {
0243 aResult.Status = Status::NumericalError;
0244 aResult.Solution = aX;
0245 return aResult;
0246 }
0247 }
0248
0249
0250 double aMaxDiff = 0.0;
0251 for (int i = aLower; i <= aUpper; ++i)
0252 {
0253 aMaxDiff = std::max(aMaxDiff, std::abs(aXNew(i) - aX(i)));
0254 }
0255
0256
0257 if (!theFunc.Gradient(aXNew, aGradNew))
0258 {
0259 aResult.Status = Status::NumericalError;
0260 aResult.Solution = aXNew;
0261 aResult.Value = aFx;
0262 return aResult;
0263 }
0264
0265
0266 aGradNorm = 0.0;
0267 for (int i = aLower; i <= aUpper; ++i)
0268 {
0269 aGradNorm += MathUtils::Sqr(aGradNew(i));
0270 }
0271 aGradNorm = std::sqrt(aGradNorm);
0272
0273 if (aGradNorm < theConfig.FTolerance)
0274 {
0275 aResult.Status = Status::OK;
0276 aResult.Solution = aXNew;
0277 aResult.Value = aFx;
0278 aResult.Gradient = aGradNew;
0279 return aResult;
0280 }
0281
0282 if (aMaxDiff < theConfig.XTolerance)
0283 {
0284 aResult.Status = Status::OK;
0285 aResult.Solution = aXNew;
0286 aResult.Value = aFx;
0287 aResult.Gradient = aGradNew;
0288 return aResult;
0289 }
0290
0291
0292 aX = aXNew;
0293 aGrad = aGradNew;
0294 }
0295
0296
0297 aResult.Status = Status::MaxIterations;
0298 aResult.Solution = aX;
0299 aResult.Value = aFx;
0300 aResult.Gradient = aGrad;
0301 return aResult;
0302 }
0303
0304
0305
0306
0307
0308
0309
0310
0311
0312
0313 template <typename Function>
0314 VectorResult NewtonModified(Function& theFunc,
0315 const math_Vector& theStartingPoint,
0316 const NewtonConfig& theConfig = NewtonConfig())
0317 {
0318 return Newton(theFunc, theStartingPoint, theConfig);
0319 }
0320
0321
0322
0323
0324
0325
0326
0327
0328
0329
0330
0331
0332
0333 template <typename Function>
0334 VectorResult NewtonNumericalHessian(Function& theFunc,
0335 const math_Vector& theStartingPoint,
0336 double theHessStep = 1.0e-6,
0337 const NewtonConfig& theConfig = NewtonConfig())
0338 {
0339
0340 class FuncWithHessian
0341 {
0342 public:
0343 FuncWithHessian(Function& theF, double theStep)
0344 : myFunc(theF),
0345 myStep(theStep)
0346 {
0347 }
0348
0349 bool Value(const math_Vector& theX, double& theF) { return myFunc.Value(theX, theF); }
0350
0351 bool Gradient(const math_Vector& theX, math_Vector& theGrad)
0352 {
0353 return myFunc.Gradient(theX, theGrad);
0354 }
0355
0356 bool Hessian(const math_Vector& theX, math_Matrix& theHess)
0357 {
0358 math_Vector aXMod = theX;
0359 return MathUtils::NumericalHessian(myFunc, aXMod, theHess, myStep);
0360 }
0361
0362 private:
0363 Function& myFunc;
0364 double myStep;
0365 };
0366
0367 FuncWithHessian aWrapper(theFunc, theHessStep);
0368 return Newton(aWrapper, theStartingPoint, theConfig);
0369 }
0370
0371
0372
0373
0374
0375
0376
0377
0378
0379
0380
0381 template <typename Function>
0382 VectorResult NewtonNumerical(Function& theFunc,
0383 const math_Vector& theStartingPoint,
0384 double theGradStep = 1.0e-8,
0385 double theHessStep = 1.0e-6,
0386 const NewtonConfig& theConfig = NewtonConfig())
0387 {
0388
0389 class FuncWithDerivatives
0390 {
0391 public:
0392 FuncWithDerivatives(Function& theF, double theGStep, double theHStep)
0393 : myFunc(theF),
0394 myGradStep(theGStep),
0395 myHessStep(theHStep)
0396 {
0397 }
0398
0399 bool Value(const math_Vector& theX, double& theF) { return myFunc.Value(theX, theF); }
0400
0401 bool Gradient(const math_Vector& theX, math_Vector& theGrad)
0402 {
0403 math_Vector aXMod = theX;
0404 return MathUtils::NumericalGradientAdaptive(myFunc, aXMod, theGrad, myGradStep);
0405 }
0406
0407 bool Hessian(const math_Vector& theX, math_Matrix& theHess)
0408 {
0409
0410 const int aLower = theX.Lower();
0411 const int aUpper = theX.Upper();
0412
0413 math_Vector aXMod = theX;
0414 math_Vector aGradPlus(aLower, aUpper);
0415 math_Vector aGradMinus(aLower, aUpper);
0416
0417 for (int j = aLower; j <= aUpper; ++j)
0418 {
0419 const double aXj = aXMod(j);
0420
0421 aXMod(j) = aXj + myHessStep;
0422 if (!MathUtils::NumericalGradientAdaptive(myFunc, aXMod, aGradPlus, myGradStep))
0423 {
0424 aXMod(j) = aXj;
0425 return false;
0426 }
0427
0428 aXMod(j) = aXj - myHessStep;
0429 if (!MathUtils::NumericalGradientAdaptive(myFunc, aXMod, aGradMinus, myGradStep))
0430 {
0431 aXMod(j) = aXj;
0432 return false;
0433 }
0434
0435 aXMod(j) = aXj;
0436
0437 for (int i = aLower; i <= aUpper; ++i)
0438 {
0439 theHess(i, j) = (aGradPlus(i) - aGradMinus(i)) / (2.0 * myHessStep);
0440 }
0441 }
0442
0443
0444 for (int i = aLower; i <= aUpper; ++i)
0445 {
0446 for (int j = i + 1; j <= aUpper; ++j)
0447 {
0448 double aAvg = 0.5 * (theHess(i, j) + theHess(j, i));
0449 theHess(i, j) = aAvg;
0450 theHess(j, i) = aAvg;
0451 }
0452 }
0453
0454 return true;
0455 }
0456
0457 private:
0458 Function& myFunc;
0459 double myGradStep;
0460 double myHessStep;
0461 };
0462
0463 FuncWithDerivatives aWrapper(theFunc, theGradStep, theHessStep);
0464 return Newton(aWrapper, theStartingPoint, theConfig);
0465 }
0466
0467
0468
0469
0470
0471
0472
0473
0474
0475
0476
0477
0478
0479 template <typename Function>
0480 VectorResult NewtonBounded(Function& theFunc,
0481 const math_Vector& theStartingPoint,
0482 const math_Vector& theLowerBounds,
0483 const math_Vector& theUpperBounds,
0484 const NewtonConfig& theConfig = NewtonConfig())
0485 {
0486 VectorResult aResult;
0487
0488 const int aLower = theStartingPoint.Lower();
0489 const int aUpper = theStartingPoint.Upper();
0490 const int aN = aUpper - aLower + 1;
0491
0492
0493 if (theLowerBounds.Length() != aN || theUpperBounds.Length() != aN)
0494 {
0495 aResult.Status = Status::InvalidInput;
0496 return aResult;
0497 }
0498
0499
0500 auto ClampToBounds = [&](math_Vector& theX) {
0501 for (int i = aLower; i <= aUpper; ++i)
0502 {
0503 const int aBndIdx = theLowerBounds.Lower() + (i - aLower);
0504 if (theX(i) < theLowerBounds(aBndIdx))
0505 {
0506 theX(i) = theLowerBounds(aBndIdx);
0507 }
0508 if (theX(i) > theUpperBounds(aBndIdx))
0509 {
0510 theX(i) = theUpperBounds(aBndIdx);
0511 }
0512 }
0513 };
0514
0515
0516 auto ProjectGradient = [&](const math_Vector& theX, math_Vector& theGrad) {
0517 for (int i = aLower; i <= aUpper; ++i)
0518 {
0519 const int aBndIdx = theLowerBounds.Lower() + (i - aLower);
0520 const double aTol = MathUtils::THE_EPSILON * std::max(1.0, std::abs(theX(i)));
0521
0522 if (theX(i) - theLowerBounds(aBndIdx) < aTol && theGrad(i) > 0.0)
0523 {
0524 theGrad(i) = 0.0;
0525 }
0526 if (theUpperBounds(aBndIdx) - theX(i) < aTol && theGrad(i) < 0.0)
0527 {
0528 theGrad(i) = 0.0;
0529 }
0530 }
0531 };
0532
0533
0534 auto ComputeAlphaMax = [&](const math_Vector& theX, const math_Vector& theDir) -> double {
0535 double aAlphaMax = 1.0;
0536 for (int i = aLower; i <= aUpper; ++i)
0537 {
0538 const int aBndIdx = theLowerBounds.Lower() + (i - aLower);
0539 if (theDir(i) < -MathUtils::THE_EPSILON)
0540 {
0541 double aMaxStep = (theLowerBounds(aBndIdx) - theX(i)) / theDir(i);
0542 aAlphaMax = std::min(aAlphaMax, aMaxStep);
0543 }
0544 else if (theDir(i) > MathUtils::THE_EPSILON)
0545 {
0546 double aMaxStep = (theUpperBounds(aBndIdx) - theX(i)) / theDir(i);
0547 aAlphaMax = std::min(aAlphaMax, aMaxStep);
0548 }
0549 }
0550 return std::max(aAlphaMax, MathUtils::THE_EPSILON);
0551 };
0552
0553
0554 math_Vector aX(aLower, aUpper);
0555 aX = theStartingPoint;
0556 ClampToBounds(aX);
0557
0558 double aFx = 0.0;
0559 if (!theFunc.Value(aX, aFx))
0560 {
0561 aResult.Status = Status::NumericalError;
0562 return aResult;
0563 }
0564
0565
0566 math_Vector aGrad(aLower, aUpper);
0567 if (!theFunc.Gradient(aX, aGrad))
0568 {
0569 aResult.Status = Status::NumericalError;
0570 return aResult;
0571 }
0572 ProjectGradient(aX, aGrad);
0573
0574
0575 double aGradNorm = 0.0;
0576 for (int i = aLower; i <= aUpper; ++i)
0577 {
0578 aGradNorm += MathUtils::Sqr(aGrad(i));
0579 }
0580 aGradNorm = std::sqrt(aGradNorm);
0581
0582 if (aGradNorm < theConfig.FTolerance)
0583 {
0584 aResult.Status = Status::OK;
0585 aResult.Solution = aX;
0586 aResult.Value = aFx;
0587 aResult.Gradient = aGrad;
0588 return aResult;
0589 }
0590
0591
0592 math_Vector aDir(aLower, aUpper);
0593 math_Vector aXNew(aLower, aUpper);
0594 math_Vector aGradNew(aLower, aUpper);
0595 math_Matrix aHessian(aLower, aUpper, aLower, aUpper);
0596 math_Vector aNegGrad(aLower, aUpper);
0597
0598 for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0599 {
0600 aResult.NbIterations = anIter + 1;
0601
0602
0603 if (!theFunc.Hessian(aX, aHessian))
0604 {
0605 aResult.Status = Status::NumericalError;
0606 aResult.Solution = aX;
0607 aResult.Value = aFx;
0608 return aResult;
0609 }
0610
0611
0612 for (int i = aLower; i <= aUpper; ++i)
0613 {
0614 aNegGrad(i) = -aGrad(i);
0615 }
0616
0617
0618 auto aLinResult = MathLin::Solve(aHessian, aNegGrad);
0619
0620 if (!aLinResult.IsDone())
0621 {
0622
0623 double aLambda = theConfig.Regularization;
0624 bool aSolved = false;
0625
0626 for (int k = 0; k < 10 && !aSolved; ++k)
0627 {
0628 math_Matrix aRegHessian = aHessian;
0629 for (int i = aLower; i <= aUpper; ++i)
0630 {
0631 aRegHessian(i, i) += aLambda;
0632 }
0633
0634 aLinResult = MathLin::Solve(aRegHessian, aNegGrad);
0635 if (aLinResult.IsDone())
0636 {
0637 aSolved = true;
0638 }
0639 else
0640 {
0641 aLambda *= 10.0;
0642 }
0643 }
0644
0645 if (!aSolved)
0646 {
0647
0648 for (int i = aLower; i <= aUpper; ++i)
0649 {
0650 aDir(i) = -aGrad(i);
0651 }
0652 goto perform_bounded_line_search;
0653 }
0654 }
0655
0656 aDir = *aLinResult.Solution;
0657
0658
0659 {
0660 double aDirDeriv = 0.0;
0661 for (int i = aLower; i <= aUpper; ++i)
0662 {
0663 aDirDeriv += aGrad(i) * aDir(i);
0664 }
0665
0666 if (aDirDeriv >= 0.0)
0667 {
0668 for (int i = aLower; i <= aUpper; ++i)
0669 {
0670 aDir(i) = -aGrad(i);
0671 }
0672 }
0673 }
0674
0675 perform_bounded_line_search:
0676 if (theConfig.UseLineSearch)
0677 {
0678 double aAlphaMax = ComputeAlphaMax(aX, aDir);
0679
0680 MathUtils::LineSearchResult aLineResult =
0681 MathUtils::ArmijoBacktrack(theFunc, aX, aDir, aGrad, aFx, aAlphaMax, 1.0e-4, 0.5, 50);
0682
0683 if (!aLineResult.IsValid)
0684 {
0685
0686 for (int i = aLower; i <= aUpper; ++i)
0687 {
0688 aDir(i) = -aGrad(i);
0689 }
0690 aAlphaMax = ComputeAlphaMax(aX, aDir);
0691 aLineResult =
0692 MathUtils::ArmijoBacktrack(theFunc, aX, aDir, aGrad, aFx, aAlphaMax, 1.0e-4, 0.5, 50);
0693
0694 if (!aLineResult.IsValid)
0695 {
0696 aResult.Status = Status::NotConverged;
0697 aResult.Solution = aX;
0698 aResult.Value = aFx;
0699 aResult.Gradient = aGrad;
0700 return aResult;
0701 }
0702 }
0703
0704 for (int i = aLower; i <= aUpper; ++i)
0705 {
0706 aXNew(i) = aX(i) + aLineResult.Alpha * aDir(i);
0707 }
0708 ClampToBounds(aXNew);
0709
0710 if (!theFunc.Value(aXNew, aFx))
0711 {
0712 aResult.Status = Status::NumericalError;
0713 aResult.Solution = aX;
0714 return aResult;
0715 }
0716 }
0717 else
0718 {
0719 for (int i = aLower; i <= aUpper; ++i)
0720 {
0721 aXNew(i) = aX(i) + aDir(i);
0722 }
0723 ClampToBounds(aXNew);
0724
0725 if (!theFunc.Value(aXNew, aFx))
0726 {
0727 aResult.Status = Status::NumericalError;
0728 aResult.Solution = aX;
0729 return aResult;
0730 }
0731 }
0732
0733
0734 double aMaxDiff = 0.0;
0735 for (int i = aLower; i <= aUpper; ++i)
0736 {
0737 aMaxDiff = std::max(aMaxDiff, std::abs(aXNew(i) - aX(i)));
0738 }
0739
0740
0741 if (!theFunc.Gradient(aXNew, aGradNew))
0742 {
0743 aResult.Status = Status::NumericalError;
0744 aResult.Solution = aXNew;
0745 aResult.Value = aFx;
0746 return aResult;
0747 }
0748 ProjectGradient(aXNew, aGradNew);
0749
0750
0751 aGradNorm = 0.0;
0752 for (int i = aLower; i <= aUpper; ++i)
0753 {
0754 aGradNorm += MathUtils::Sqr(aGradNew(i));
0755 }
0756 aGradNorm = std::sqrt(aGradNorm);
0757
0758 if (aGradNorm < theConfig.FTolerance)
0759 {
0760 aResult.Status = Status::OK;
0761 aResult.Solution = aXNew;
0762 aResult.Value = aFx;
0763 aResult.Gradient = aGradNew;
0764 return aResult;
0765 }
0766
0767 if (aMaxDiff < theConfig.XTolerance)
0768 {
0769 aResult.Status = Status::OK;
0770 aResult.Solution = aXNew;
0771 aResult.Value = aFx;
0772 aResult.Gradient = aGradNew;
0773 return aResult;
0774 }
0775
0776 aX = aXNew;
0777 aGrad = aGradNew;
0778 }
0779
0780 aResult.Status = Status::MaxIterations;
0781 aResult.Solution = aX;
0782 aResult.Value = aFx;
0783 aResult.Gradient = aGrad;
0784 return aResult;
0785 }
0786
0787 }
0788
0789 #endif