File indexing completed on 2026-09-28 09:20:54
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #ifndef _MathUtils_LineSearch_HeaderFile
0015 #define _MathUtils_LineSearch_HeaderFile
0016
0017 #include <math_Vector.hxx>
0018 #include <MathUtils_Core.hxx>
0019
0020 #include <cmath>
0021
0022
0023 namespace MathUtils
0024 {
0025
0026
0027 struct LineSearchResult
0028 {
0029 bool IsValid = false;
0030 double Alpha = 0.0;
0031 double FNew = 0.0;
0032 int NbEvals = 0;
0033 };
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055 template <typename Function>
0056 LineSearchResult ArmijoBacktrack(Function& theFunc,
0057 const math_Vector& theX,
0058 const math_Vector& theDir,
0059 const math_Vector& theGrad,
0060 double theFx,
0061 double theAlphaInit = 1.0,
0062 double theC1 = 1.0e-4,
0063 double theRho = 0.5,
0064 int theMaxIter = 50)
0065 {
0066 LineSearchResult aResult;
0067 aResult.Alpha = theAlphaInit;
0068 aResult.NbEvals = 0;
0069
0070 const int aLower = theX.Lower();
0071 const int aUpper = theX.Upper();
0072
0073
0074 double aDirDeriv = 0.0;
0075 for (int i = aLower; i <= aUpper; ++i)
0076 {
0077 aDirDeriv += theGrad(i) * theDir(i);
0078 }
0079
0080
0081 if (aDirDeriv >= 0.0)
0082 {
0083
0084 aResult.IsValid = false;
0085 return aResult;
0086 }
0087
0088
0089 math_Vector aXNew(aLower, aUpper);
0090
0091 for (int k = 0; k < theMaxIter; ++k)
0092 {
0093
0094 for (int i = aLower; i <= aUpper; ++i)
0095 {
0096 aXNew(i) = theX(i) + aResult.Alpha * theDir(i);
0097 }
0098
0099
0100 double aFNew = 0.0;
0101 if (!theFunc.Value(aXNew, aFNew))
0102 {
0103
0104 aResult.Alpha *= theRho;
0105 ++aResult.NbEvals;
0106 continue;
0107 }
0108 ++aResult.NbEvals;
0109
0110
0111 if (aFNew <= theFx + theC1 * aResult.Alpha * aDirDeriv)
0112 {
0113 aResult.IsValid = true;
0114 aResult.FNew = aFNew;
0115 return aResult;
0116 }
0117
0118
0119 aResult.Alpha *= theRho;
0120
0121
0122 if (aResult.Alpha < THE_EPSILON)
0123 {
0124 break;
0125 }
0126 }
0127
0128
0129 aResult.IsValid = false;
0130 return aResult;
0131 }
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153 template <typename Function>
0154 LineSearchResult WolfeSearch(Function& theFunc,
0155 const math_Vector& theX,
0156 const math_Vector& theDir,
0157 const math_Vector& theGrad,
0158 double theFx,
0159 double theAlphaInit = 1.0,
0160 double theC1 = 1.0e-4,
0161 double theC2 = 0.9,
0162 int theMaxIter = 20)
0163 {
0164 LineSearchResult aResult;
0165 aResult.NbEvals = 0;
0166
0167 const int aLower = theX.Lower();
0168 const int aUpper = theX.Upper();
0169
0170
0171 double aPhi0Prime = 0.0;
0172 for (int i = aLower; i <= aUpper; ++i)
0173 {
0174 aPhi0Prime += theGrad(i) * theDir(i);
0175 }
0176
0177 if (aPhi0Prime >= 0.0)
0178 {
0179 aResult.IsValid = false;
0180 return aResult;
0181 }
0182
0183 math_Vector aXNew(aLower, aUpper);
0184 math_Vector aGradNew(aLower, aUpper);
0185
0186 double aAlphaLo = 0.0;
0187 double aAlphaHi = theAlphaInit * 2.0;
0188 double aAlpha = theAlphaInit;
0189
0190 double aPhiLo = theFx;
0191
0192
0193 for (int k = 0; k < theMaxIter; ++k)
0194 {
0195
0196 for (int i = aLower; i <= aUpper; ++i)
0197 {
0198 aXNew(i) = theX(i) + aAlpha * theDir(i);
0199 }
0200
0201 double aPhi = 0.0;
0202 if (!theFunc.Value(aXNew, aPhi))
0203 {
0204
0205 aAlphaHi = aAlpha;
0206 aAlpha = 0.5 * (aAlphaLo + aAlphaHi);
0207 ++aResult.NbEvals;
0208 continue;
0209 }
0210 ++aResult.NbEvals;
0211
0212
0213 if (aPhi > theFx + theC1 * aAlpha * aPhi0Prime || (k > 0 && aPhi >= aPhiLo))
0214 {
0215
0216 aAlphaHi = aAlpha;
0217 break;
0218 }
0219
0220
0221 if (!theFunc.Gradient(aXNew, aGradNew))
0222 {
0223 aResult.IsValid = false;
0224 return aResult;
0225 }
0226
0227 double aPhiPrime = 0.0;
0228 for (int i = aLower; i <= aUpper; ++i)
0229 {
0230 aPhiPrime += aGradNew(i) * theDir(i);
0231 }
0232
0233
0234 if (std::abs(aPhiPrime) <= -theC2 * aPhi0Prime)
0235 {
0236 aResult.IsValid = true;
0237 aResult.Alpha = aAlpha;
0238 aResult.FNew = aPhi;
0239 return aResult;
0240 }
0241
0242 if (aPhiPrime >= 0.0)
0243 {
0244
0245 aAlphaHi = aAlphaLo;
0246 aAlphaLo = aAlpha;
0247 aPhiLo = aPhi;
0248 break;
0249 }
0250
0251
0252 aAlphaLo = aAlpha;
0253 aPhiLo = aPhi;
0254 aAlpha = 0.5 * (aAlpha + aAlphaHi);
0255 }
0256
0257
0258 for (int k = 0; k < theMaxIter; ++k)
0259 {
0260
0261 aAlpha = 0.5 * (aAlphaLo + aAlphaHi);
0262
0263 for (int i = aLower; i <= aUpper; ++i)
0264 {
0265 aXNew(i) = theX(i) + aAlpha * theDir(i);
0266 }
0267
0268 double aPhi = 0.0;
0269 if (!theFunc.Value(aXNew, aPhi))
0270 {
0271 aAlphaHi = aAlpha;
0272 ++aResult.NbEvals;
0273 continue;
0274 }
0275 ++aResult.NbEvals;
0276
0277 if (aPhi > theFx + theC1 * aAlpha * aPhi0Prime || aPhi >= aPhiLo)
0278 {
0279 aAlphaHi = aAlpha;
0280 }
0281 else
0282 {
0283 if (!theFunc.Gradient(aXNew, aGradNew))
0284 {
0285 break;
0286 }
0287
0288 double aPhiPrime = 0.0;
0289 for (int i = aLower; i <= aUpper; ++i)
0290 {
0291 aPhiPrime += aGradNew(i) * theDir(i);
0292 }
0293
0294 if (std::abs(aPhiPrime) <= -theC2 * aPhi0Prime)
0295 {
0296 aResult.IsValid = true;
0297 aResult.Alpha = aAlpha;
0298 aResult.FNew = aPhi;
0299 return aResult;
0300 }
0301
0302 if (aPhiPrime * (aAlphaHi - aAlphaLo) >= 0.0)
0303 {
0304 aAlphaHi = aAlphaLo;
0305 }
0306
0307 aAlphaLo = aAlpha;
0308 aPhiLo = aPhi;
0309 }
0310
0311
0312 if (std::abs(aAlphaHi - aAlphaLo) < THE_EPSILON)
0313 {
0314 break;
0315 }
0316 }
0317
0318
0319 aResult.IsValid = true;
0320 aResult.Alpha = aAlpha;
0321 aResult.FNew = aPhiLo;
0322 return aResult;
0323 }
0324
0325
0326
0327
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337
0338 template <typename Function>
0339 LineSearchResult ExactLineSearch(Function& theFunc,
0340 const math_Vector& theX,
0341 const math_Vector& theDir,
0342 double theAlphaMax = 10.0,
0343 double theTolerance = 1.0e-6,
0344 int theMaxIter = 100)
0345 {
0346 LineSearchResult aResult;
0347 aResult.NbEvals = 0;
0348
0349 const int aLower = theX.Lower();
0350 const int aUpper = theX.Upper();
0351
0352 math_Vector aXNew(aLower, aUpper);
0353
0354
0355 auto aEvalPhi = [&](double theAlpha, double& thePhi) -> bool {
0356 for (int i = aLower; i <= aUpper; ++i)
0357 {
0358 aXNew(i) = theX(i) + theAlpha * theDir(i);
0359 }
0360 ++aResult.NbEvals;
0361 return theFunc.Value(aXNew, thePhi);
0362 };
0363
0364
0365
0366 double aA = -theAlphaMax;
0367 double aB = theAlphaMax;
0368 double aX = 0.0;
0369 double aW = aX;
0370 double aV = aX;
0371
0372 double aFx = 0.0;
0373 if (!aEvalPhi(aX, aFx))
0374 {
0375 aResult.IsValid = false;
0376 return aResult;
0377 }
0378 double aFw = aFx;
0379 double aFv = aFx;
0380
0381 double aD = 0.0;
0382 double aE = 0.0;
0383
0384 for (int anIter = 0; anIter < theMaxIter; ++anIter)
0385 {
0386 const double aXm = 0.5 * (aA + aB);
0387 const double aTol1 = theTolerance * std::abs(aX) + THE_ZERO_TOL / 10.0;
0388 const double aTol2 = 2.0 * aTol1;
0389
0390
0391 if (std::abs(aX - aXm) <= (aTol2 - 0.5 * (aB - aA)))
0392 {
0393 aResult.IsValid = true;
0394 aResult.Alpha = aX;
0395 aResult.FNew = aFx;
0396 return aResult;
0397 }
0398
0399 double aU = 0.0;
0400 bool aUseParabolic = false;
0401
0402
0403 if (std::abs(aE) > aTol1)
0404 {
0405 const double aR = (aX - aW) * (aFx - aFv);
0406 double aQ = (aX - aV) * (aFx - aFw);
0407 double aP = (aX - aV) * aQ - (aX - aW) * aR;
0408 aQ = 2.0 * (aQ - aR);
0409
0410 if (aQ > 0.0)
0411 {
0412 aP = -aP;
0413 }
0414 else
0415 {
0416 aQ = -aQ;
0417 }
0418
0419 const double aETmp = aE;
0420 aE = aD;
0421
0422 if (std::abs(aP) < std::abs(0.5 * aQ * aETmp) && aP > aQ * (aA - aX) && aP < aQ * (aB - aX))
0423 {
0424 aD = aP / aQ;
0425 aU = aX + aD;
0426 if ((aU - aA) < aTol2 || (aB - aU) < aTol2)
0427 {
0428 aD = SignTransfer(aTol1, aXm - aX);
0429 }
0430 aUseParabolic = true;
0431 }
0432 }
0433
0434 if (!aUseParabolic)
0435 {
0436 aE = (aX < aXm) ? (aB - aX) : (aA - aX);
0437 aD = THE_GOLDEN_SECTION * aE;
0438 }
0439
0440 if (std::abs(aD) >= aTol1)
0441 {
0442 aU = aX + aD;
0443 }
0444 else
0445 {
0446 aU = aX + SignTransfer(aTol1, aD);
0447 }
0448
0449 double aFu = 0.0;
0450 if (!aEvalPhi(aU, aFu))
0451 {
0452 aResult.IsValid = false;
0453 aResult.Alpha = aX;
0454 aResult.FNew = aFx;
0455 return aResult;
0456 }
0457
0458
0459 if (aFu <= aFx)
0460 {
0461 if (aU < aX)
0462 {
0463 aB = aX;
0464 }
0465 else
0466 {
0467 aA = aX;
0468 }
0469
0470 aV = aW;
0471 aW = aX;
0472 aX = aU;
0473 aFv = aFw;
0474 aFw = aFx;
0475 aFx = aFu;
0476 }
0477 else
0478 {
0479 if (aU < aX)
0480 {
0481 aA = aU;
0482 }
0483 else
0484 {
0485 aB = aU;
0486 }
0487
0488 if (aFu <= aFw || aW == aX)
0489 {
0490 aV = aW;
0491 aW = aU;
0492 aFv = aFw;
0493 aFw = aFu;
0494 }
0495 else if (aFu <= aFv || aV == aX || aV == aW)
0496 {
0497 aV = aU;
0498 aFv = aFu;
0499 }
0500 }
0501 }
0502
0503 aResult.IsValid = true;
0504 aResult.Alpha = aX;
0505 aResult.FNew = aFx;
0506 return aResult;
0507 }
0508
0509
0510
0511
0512
0513
0514
0515
0516
0517 inline double QuadraticInterpolation(double thePhi0,
0518 double thePhi0Prime,
0519 double theAlpha1,
0520 double thePhi1)
0521 {
0522
0523
0524
0525 const double aNum = thePhi0Prime * theAlpha1 * theAlpha1;
0526 const double aDenom = 2.0 * (thePhi1 - thePhi0 - thePhi0Prime * theAlpha1);
0527
0528 if (std::abs(aDenom) < THE_ZERO_TOL)
0529 {
0530 return 0.5 * theAlpha1;
0531 }
0532
0533 double aAlphaNew = -aNum / aDenom;
0534
0535
0536 if (aAlphaNew < 0.1 * theAlpha1)
0537 {
0538 aAlphaNew = 0.1 * theAlpha1;
0539 }
0540 else if (aAlphaNew > 0.9 * theAlpha1)
0541 {
0542 aAlphaNew = 0.5 * theAlpha1;
0543 }
0544
0545 return aAlphaNew;
0546 }
0547
0548
0549
0550
0551
0552
0553
0554
0555
0556
0557
0558
0559
0560
0561
0562
0563
0564
0565
0566 template <typename Function>
0567 bool BrentAlongCoordinate(Function& theFunc,
0568 math_Vector& thePoint,
0569 int theDimIdx,
0570 double theLoBound,
0571 double theUpBound,
0572 double& theFx,
0573 double theTolerance,
0574 int theMaxIter,
0575 int& theEvalCount)
0576 {
0577 const double aOrigCoord = thePoint(theDimIdx);
0578
0579
0580 double aA = theLoBound;
0581 double aB = theUpBound;
0582 double aX = aOrigCoord;
0583 double aW = aX;
0584 double aV = aX;
0585
0586 double aFx = theFx;
0587 double aFw = aFx;
0588 double aFv = aFx;
0589
0590 double aD = 0.0;
0591 double aE = 0.0;
0592
0593 for (int anIter = 0; anIter < theMaxIter; ++anIter)
0594 {
0595 const double aXm = 0.5 * (aA + aB);
0596 const double aTol1 = theTolerance * std::abs(aX) + THE_ZERO_TOL / 10.0;
0597 const double aTol2 = 2.0 * aTol1;
0598
0599
0600 if (std::abs(aX - aXm) <= (aTol2 - 0.5 * (aB - aA)))
0601 {
0602 break;
0603 }
0604
0605 double aU = 0.0;
0606 bool aUseParabolic = false;
0607
0608
0609 if (std::abs(aE) > aTol1)
0610 {
0611 const double aR = (aX - aW) * (aFx - aFv);
0612 double aQ = (aX - aV) * (aFx - aFw);
0613 double aP = (aX - aV) * aQ - (aX - aW) * aR;
0614 aQ = 2.0 * (aQ - aR);
0615
0616 if (aQ > 0.0)
0617 {
0618 aP = -aP;
0619 }
0620 else
0621 {
0622 aQ = -aQ;
0623 }
0624
0625 const double aETmp = aE;
0626 aE = aD;
0627
0628 if (std::abs(aP) < std::abs(0.5 * aQ * aETmp) && aP > aQ * (aA - aX) && aP < aQ * (aB - aX))
0629 {
0630 aD = aP / aQ;
0631 aU = aX + aD;
0632 if ((aU - aA) < aTol2 || (aB - aU) < aTol2)
0633 {
0634 aD = SignTransfer(aTol1, aXm - aX);
0635 }
0636 aUseParabolic = true;
0637 }
0638 }
0639
0640 if (!aUseParabolic)
0641 {
0642 aE = (aX < aXm) ? (aB - aX) : (aA - aX);
0643 aD = THE_GOLDEN_SECTION * aE;
0644 }
0645
0646 if (std::abs(aD) >= aTol1)
0647 {
0648 aU = aX + aD;
0649 }
0650 else
0651 {
0652 aU = aX + SignTransfer(aTol1, aD);
0653 }
0654
0655
0656 thePoint(theDimIdx) = aU;
0657 double aFu = 0.0;
0658 if (!theFunc.Value(thePoint, aFu))
0659 {
0660
0661 thePoint(theDimIdx) = aOrigCoord;
0662 return false;
0663 }
0664 ++theEvalCount;
0665
0666
0667 if (aFu <= aFx)
0668 {
0669 if (aU < aX)
0670 {
0671 aB = aX;
0672 }
0673 else
0674 {
0675 aA = aX;
0676 }
0677 aV = aW;
0678 aW = aX;
0679 aX = aU;
0680 aFv = aFw;
0681 aFw = aFx;
0682 aFx = aFu;
0683 }
0684 else
0685 {
0686 if (aU < aX)
0687 {
0688 aA = aU;
0689 }
0690 else
0691 {
0692 aB = aU;
0693 }
0694 if (aFu <= aFw || aW == aX)
0695 {
0696 aV = aW;
0697 aW = aU;
0698 aFv = aFw;
0699 aFw = aFu;
0700 }
0701 else if (aFu <= aFv || aV == aX || aV == aW)
0702 {
0703 aV = aU;
0704 aFv = aFu;
0705 }
0706 }
0707 }
0708
0709
0710 if (aFx < theFx)
0711 {
0712 thePoint(theDimIdx) = aX;
0713 theFx = aFx;
0714 return true;
0715 }
0716
0717
0718 thePoint(theDimIdx) = aOrigCoord;
0719 return false;
0720 }
0721
0722 }
0723
0724 #endif