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_FRPR_HeaderFile
0015 #define _MathOpt_FRPR_HeaderFile
0016
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <MathUtils_Core.hxx>
0020 #include <MathUtils_LineSearch.hxx>
0021 #include <MathUtils_Deriv.hxx>
0022
0023 #include <cmath>
0024
0025 namespace MathOpt
0026 {
0027 using namespace MathUtils;
0028
0029
0030 enum class ConjugateGradientFormula
0031 {
0032 FletcherReeves,
0033 PolakRibiere,
0034 HestenesStiefel,
0035 DaiYuan
0036 };
0037
0038
0039 struct FRPRConfig : Config
0040 {
0041 ConjugateGradientFormula Formula = ConjugateGradientFormula::PolakRibiere;
0042 int RestartInterval = 0;
0043
0044
0045 FRPRConfig() = default;
0046
0047
0048 explicit FRPRConfig(double theTolerance, int theMaxIter = 100)
0049 : Config(theTolerance, theMaxIter)
0050 {
0051 }
0052 };
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077 template <typename Function>
0078 VectorResult FRPR(Function& theFunc,
0079 const math_Vector& theStartingPoint,
0080 const FRPRConfig& theConfig = FRPRConfig())
0081 {
0082 VectorResult aResult;
0083
0084 const int aLower = theStartingPoint.Lower();
0085 const int aUpper = theStartingPoint.Upper();
0086 const int aN = aUpper - aLower + 1;
0087
0088
0089 const int aRestartInterval = (theConfig.RestartInterval > 0) ? theConfig.RestartInterval : aN;
0090
0091
0092 math_Vector aX(aLower, aUpper);
0093 aX = theStartingPoint;
0094
0095 double aFx = 0.0;
0096 if (!theFunc.Value(aX, aFx))
0097 {
0098 aResult.Status = Status::NumericalError;
0099 return aResult;
0100 }
0101
0102
0103 math_Vector aGrad(aLower, aUpper);
0104 if (!theFunc.Gradient(aX, aGrad))
0105 {
0106 aResult.Status = Status::NumericalError;
0107 return aResult;
0108 }
0109
0110
0111 double aGradNormSq = 0.0;
0112 for (int i = aLower; i <= aUpper; ++i)
0113 {
0114 aGradNormSq += MathUtils::Sqr(aGrad(i));
0115 }
0116
0117 if (std::sqrt(aGradNormSq) < theConfig.FTolerance)
0118 {
0119 aResult.Status = Status::OK;
0120 aResult.Solution = aX;
0121 aResult.Value = aFx;
0122 aResult.Gradient = aGrad;
0123 return aResult;
0124 }
0125
0126
0127 math_Vector aDir(aLower, aUpper);
0128 for (int i = aLower; i <= aUpper; ++i)
0129 {
0130 aDir(i) = -aGrad(i);
0131 }
0132
0133
0134 math_Vector aXNew(aLower, aUpper);
0135 math_Vector aGradNew(aLower, aUpper);
0136 math_Vector aGradDiff(aLower, aUpper);
0137
0138 int aRestartCount = 0;
0139
0140 for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0141 {
0142 aResult.NbIterations = anIter + 1;
0143
0144
0145 MathUtils::LineSearchResult aLineResult =
0146 MathUtils::ArmijoBacktrack(theFunc, aX, aDir, aGrad, aFx, 1.0, 1.0e-4, 0.5, 50);
0147
0148 if (!aLineResult.IsValid || aLineResult.Alpha < MathUtils::THE_EPSILON)
0149 {
0150
0151 for (int i = aLower; i <= aUpper; ++i)
0152 {
0153 aDir(i) = -aGrad(i);
0154 }
0155 aLineResult = MathUtils::ArmijoBacktrack(theFunc, aX, aDir, aGrad, aFx, 1.0, 1.0e-4, 0.5, 50);
0156
0157 if (!aLineResult.IsValid)
0158 {
0159 aResult.Status = Status::NotConverged;
0160 aResult.Solution = aX;
0161 aResult.Value = aFx;
0162 aResult.Gradient = aGrad;
0163 return aResult;
0164 }
0165 aRestartCount = 0;
0166 }
0167
0168
0169 for (int i = aLower; i <= aUpper; ++i)
0170 {
0171 aXNew(i) = aX(i) + aLineResult.Alpha * aDir(i);
0172 }
0173
0174
0175 double aMaxDiff = 0.0;
0176 for (int i = aLower; i <= aUpper; ++i)
0177 {
0178 aMaxDiff = std::max(aMaxDiff, std::abs(aXNew(i) - aX(i)));
0179 }
0180
0181
0182 if (!theFunc.Gradient(aXNew, aGradNew))
0183 {
0184 aResult.Status = Status::NumericalError;
0185 aResult.Solution = aX;
0186 aResult.Value = aFx;
0187 return aResult;
0188 }
0189
0190
0191 double aGradNewNormSq = 0.0;
0192 for (int i = aLower; i <= aUpper; ++i)
0193 {
0194 aGradNewNormSq += MathUtils::Sqr(aGradNew(i));
0195 }
0196
0197 if (std::sqrt(aGradNewNormSq) < theConfig.FTolerance)
0198 {
0199 aResult.Status = Status::OK;
0200 aResult.Solution = aXNew;
0201 aResult.Value = aLineResult.FNew;
0202 aResult.Gradient = aGradNew;
0203 return aResult;
0204 }
0205
0206 if (aMaxDiff < theConfig.XTolerance)
0207 {
0208 aResult.Status = Status::OK;
0209 aResult.Solution = aXNew;
0210 aResult.Value = aLineResult.FNew;
0211 aResult.Gradient = aGradNew;
0212 return aResult;
0213 }
0214
0215
0216 for (int i = aLower; i <= aUpper; ++i)
0217 {
0218 aGradDiff(i) = aGradNew(i) - aGrad(i);
0219 }
0220
0221
0222 double aBeta = 0.0;
0223 ++aRestartCount;
0224
0225 if (aRestartCount >= aRestartInterval)
0226 {
0227
0228 aBeta = 0.0;
0229 aRestartCount = 0;
0230 }
0231 else
0232 {
0233 switch (theConfig.Formula)
0234 {
0235 case ConjugateGradientFormula::FletcherReeves:
0236
0237 if (aGradNormSq > MathUtils::THE_ZERO_TOL)
0238 {
0239 aBeta = aGradNewNormSq / aGradNormSq;
0240 }
0241 break;
0242
0243 case ConjugateGradientFormula::PolakRibiere: {
0244
0245 double aDot = 0.0;
0246 for (int i = aLower; i <= aUpper; ++i)
0247 {
0248 aDot += aGradNew(i) * aGradDiff(i);
0249 }
0250 if (aGradNormSq > MathUtils::THE_ZERO_TOL)
0251 {
0252 aBeta = aDot / aGradNormSq;
0253 }
0254
0255 if (aBeta < 0.0)
0256 {
0257 aBeta = 0.0;
0258 aRestartCount = 0;
0259 }
0260 }
0261 break;
0262
0263 case ConjugateGradientFormula::HestenesStiefel: {
0264
0265 double aNum = 0.0;
0266 double aDen = 0.0;
0267 for (int i = aLower; i <= aUpper; ++i)
0268 {
0269 aNum += aGradNew(i) * aGradDiff(i);
0270 aDen += aDir(i) * aGradDiff(i);
0271 }
0272 if (std::abs(aDen) > MathUtils::THE_ZERO_TOL)
0273 {
0274 aBeta = aNum / aDen;
0275 }
0276 if (aBeta < 0.0)
0277 {
0278 aBeta = 0.0;
0279 aRestartCount = 0;
0280 }
0281 }
0282 break;
0283
0284 case ConjugateGradientFormula::DaiYuan: {
0285
0286 double aDen = 0.0;
0287 for (int i = aLower; i <= aUpper; ++i)
0288 {
0289 aDen += aDir(i) * aGradDiff(i);
0290 }
0291 if (std::abs(aDen) > MathUtils::THE_ZERO_TOL)
0292 {
0293 aBeta = aGradNewNormSq / aDen;
0294 }
0295 }
0296 break;
0297 }
0298 }
0299
0300
0301 for (int i = aLower; i <= aUpper; ++i)
0302 {
0303 aDir(i) = -aGradNew(i) + aBeta * aDir(i);
0304 }
0305
0306
0307 double aDirDeriv = 0.0;
0308 for (int i = aLower; i <= aUpper; ++i)
0309 {
0310 aDirDeriv += aGradNew(i) * aDir(i);
0311 }
0312
0313 if (aDirDeriv >= 0.0)
0314 {
0315
0316 for (int i = aLower; i <= aUpper; ++i)
0317 {
0318 aDir(i) = -aGradNew(i);
0319 }
0320 aRestartCount = 0;
0321 }
0322
0323
0324 aX = aXNew;
0325 aGrad = aGradNew;
0326 aGradNormSq = aGradNewNormSq;
0327 aFx = aLineResult.FNew;
0328 }
0329
0330
0331 aResult.Status = Status::MaxIterations;
0332 aResult.Solution = aX;
0333 aResult.Value = aFx;
0334 aResult.Gradient = aGrad;
0335 return aResult;
0336 }
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346
0347 template <typename Function>
0348 VectorResult FRPRNumerical(Function& theFunc,
0349 const math_Vector& theStartingPoint,
0350 double theGradStep = 1.0e-8,
0351 const FRPRConfig& theConfig = FRPRConfig())
0352 {
0353
0354 class FuncWithGradient
0355 {
0356 public:
0357 FuncWithGradient(Function& theF, double theStep)
0358 : myFunc(theF),
0359 myStep(theStep)
0360 {
0361 }
0362
0363 bool Value(const math_Vector& theX, double& theF) { return myFunc.Value(theX, theF); }
0364
0365 bool Gradient(const math_Vector& theX, math_Vector& theGrad)
0366 {
0367 math_Vector aXMod = theX;
0368 return MathUtils::NumericalGradientAdaptive(myFunc, aXMod, theGrad, myStep);
0369 }
0370
0371 private:
0372 Function& myFunc;
0373 double myStep;
0374 };
0375
0376 FuncWithGradient aWrapper(theFunc, theGradStep);
0377 return FRPR(aWrapper, theStartingPoint, theConfig);
0378 }
0379
0380 }
0381
0382 #endif