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_GlobOpt_HeaderFile
0015 #define _MathOpt_GlobOpt_HeaderFile
0016
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <MathUtils_Random.hxx>
0020 #include <MathOpt_PSO.hxx>
0021 #include <MathOpt_BFGS.hxx>
0022 #include <MathOpt_Powell.hxx>
0023 #include <MathUtils_Core.hxx>
0024
0025 #include <NCollection_DynamicArray.hxx>
0026
0027 #include <cmath>
0028
0029 namespace MathOpt
0030 {
0031 using namespace MathUtils;
0032
0033
0034 enum class GlobalStrategy
0035 {
0036 PSO,
0037 MultiStart,
0038 PSOHybrid,
0039 DifferentialEvolution
0040 };
0041
0042
0043 struct GlobalConfig : NDimConfig
0044 {
0045 GlobalStrategy Strategy = GlobalStrategy::PSOHybrid;
0046 int NbPopulation = 40;
0047 int NbStarts = 10;
0048 double MutationScale = 0.8;
0049 double CrossoverProb = 0.9;
0050 unsigned int Seed = 6;
0051 int PolishBudgetPerDim = 50;
0052
0053
0054 GlobalConfig()
0055 : NDimConfig(1.0e-8, 200, true)
0056 {
0057 }
0058
0059
0060 GlobalConfig(GlobalStrategy theStrategy, int theMaxIter = 200)
0061 : NDimConfig(1.0e-8, theMaxIter, true),
0062 Strategy(theStrategy)
0063 {
0064 }
0065 };
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079 template <typename Function>
0080 VectorResult DifferentialEvolution(Function& theFunc,
0081 const math_Vector& theLowerBounds,
0082 const math_Vector& theUpperBounds,
0083 const GlobalConfig& theConfig = GlobalConfig())
0084 {
0085 VectorResult aResult;
0086
0087 const int aLower = theLowerBounds.Lower();
0088 const int aUpper = theLowerBounds.Upper();
0089 const int aNbDims = aUpper - aLower + 1;
0090
0091 if (theUpperBounds.Length() != aNbDims)
0092 {
0093 aResult.Status = Status::InvalidInput;
0094 return aResult;
0095 }
0096
0097 const int aNbPop = theConfig.NbPopulation;
0098 if (aNbPop < 4)
0099 {
0100
0101 aResult.Status = Status::InvalidInput;
0102 return aResult;
0103 }
0104
0105 const double aMutScale = theConfig.MutationScale;
0106 const double aCrossProb = theConfig.CrossoverProb;
0107
0108
0109 MathUtils::RandomGenerator aRNG(theConfig.Seed);
0110
0111
0112 NCollection_DynamicArray<math_Vector> aPopulation;
0113 math_Vector aFitness(0, aNbPop - 1);
0114
0115
0116 for (int aMemberIdx = 0; aMemberIdx < aNbPop; ++aMemberIdx)
0117 {
0118 aPopulation.Append(math_Vector(aLower, aUpper));
0119 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0120 {
0121 const double aRandVal = aRNG.NextReal();
0122 aPopulation.ChangeValue(aMemberIdx)(aDimIdx) =
0123 theLowerBounds(aDimIdx) + aRandVal * (theUpperBounds(aDimIdx) - theLowerBounds(aDimIdx));
0124 }
0125
0126 double aFitVal;
0127 if (!theFunc.Value(aPopulation.Value(aMemberIdx), aFitVal))
0128 {
0129 aFitVal = std::numeric_limits<double>::max();
0130 }
0131 aFitness(aMemberIdx) = aFitVal;
0132 }
0133
0134
0135 int aBestIdx = 0;
0136 double aBestValue = aFitness(0);
0137 for (int aMemberIdx = 1; aMemberIdx < aNbPop; ++aMemberIdx)
0138 {
0139 if (aFitness(aMemberIdx) < aBestValue)
0140 {
0141 aBestValue = aFitness(aMemberIdx);
0142 aBestIdx = aMemberIdx;
0143 }
0144 }
0145
0146
0147 math_Vector aTrial(aLower, aUpper);
0148
0149
0150 for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0151 {
0152 aResult.NbIterations = anIter + 1;
0153
0154 for (int aMemberIdx = 0; aMemberIdx < aNbPop; ++aMemberIdx)
0155 {
0156
0157 int anIdxA, anIdxB, anIdxC;
0158 do
0159 {
0160 anIdxA = static_cast<int>(aRNG.NextReal() * aNbPop);
0161 } while (anIdxA == aMemberIdx);
0162 do
0163 {
0164 anIdxB = static_cast<int>(aRNG.NextReal() * aNbPop);
0165 } while (anIdxB == aMemberIdx || anIdxB == anIdxA);
0166 do
0167 {
0168 anIdxC = static_cast<int>(aRNG.NextReal() * aNbPop);
0169 } while (anIdxC == aMemberIdx || anIdxC == anIdxA || anIdxC == anIdxB);
0170
0171
0172 const int aJRand = aLower + static_cast<int>(aRNG.NextReal() * aNbDims);
0173
0174 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0175 {
0176 if (aRNG.NextReal() < aCrossProb || aDimIdx == aJRand)
0177 {
0178
0179 const double aMutVal =
0180 aPopulation.Value(anIdxA)(aDimIdx)
0181 + aMutScale * (aPopulation.Value(anIdxB)(aDimIdx) - aPopulation.Value(anIdxC)(aDimIdx));
0182
0183 aTrial(aDimIdx) =
0184 MathUtils::Clamp(aMutVal, theLowerBounds(aDimIdx), theUpperBounds(aDimIdx));
0185 }
0186 else
0187 {
0188 aTrial(aDimIdx) = aPopulation.Value(aMemberIdx)(aDimIdx);
0189 }
0190 }
0191
0192
0193 double aTrialFitness;
0194 if (!theFunc.Value(aTrial, aTrialFitness))
0195 {
0196 aTrialFitness = std::numeric_limits<double>::max();
0197 }
0198
0199 if (aTrialFitness <= aFitness(aMemberIdx))
0200 {
0201 aPopulation.ChangeValue(aMemberIdx) = aTrial;
0202 aFitness(aMemberIdx) = aTrialFitness;
0203
0204 if (aTrialFitness < aBestValue)
0205 {
0206 aBestValue = aTrialFitness;
0207 aBestIdx = aMemberIdx;
0208 }
0209 }
0210 }
0211
0212
0213 double aMaxDiff = 0.0;
0214 for (int aMemberIdx = 0; aMemberIdx < aNbPop; ++aMemberIdx)
0215 {
0216 aMaxDiff = std::max(aMaxDiff, std::abs(aFitness(aMemberIdx) - aBestValue));
0217 }
0218
0219 if (aMaxDiff < theConfig.Tolerance)
0220 {
0221 break;
0222 }
0223 }
0224
0225
0226 math_Vector aPolished = aPopulation.Value(aBestIdx);
0227 double aPolishedValue = aBestValue;
0228 if (theConfig.PolishBudgetPerDim > 0)
0229 {
0230 int aPolishEvals = 0;
0231 PolishCoordinateWise(theFunc,
0232 aPolished,
0233 aPolishedValue,
0234 theLowerBounds,
0235 theUpperBounds,
0236 theConfig.Tolerance,
0237 theConfig.PolishBudgetPerDim * aNbDims,
0238 aPolishEvals);
0239 }
0240
0241 aResult.Status = Status::OK;
0242 aResult.Solution = aPolished;
0243 aResult.Value = aPolishedValue;
0244 return aResult;
0245 }
0246
0247
0248
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259 template <typename Function>
0260 VectorResult MultiStart(Function& theFunc,
0261 const math_Vector& theLowerBounds,
0262 const math_Vector& theUpperBounds,
0263 const GlobalConfig& theConfig = GlobalConfig())
0264 {
0265 VectorResult aResult;
0266
0267 const int aLower = theLowerBounds.Lower();
0268 const int aUpper = theLowerBounds.Upper();
0269 const int aNbDims = aUpper - aLower + 1;
0270
0271 if (theUpperBounds.Length() != aNbDims)
0272 {
0273 aResult.Status = Status::InvalidInput;
0274 return aResult;
0275 }
0276
0277 MathUtils::RandomGenerator aRNG(theConfig.Seed);
0278
0279 math_Vector aBestSolution(aLower, aUpper);
0280 double aBestValue = std::numeric_limits<double>::max();
0281 bool aFound = false;
0282
0283
0284 Config aPowellConfig;
0285 aPowellConfig.Tolerance = theConfig.Tolerance;
0286 aPowellConfig.XTolerance = theConfig.Tolerance;
0287 aPowellConfig.FTolerance = theConfig.Tolerance;
0288 aPowellConfig.MaxIterations = theConfig.MaxIterations / theConfig.NbStarts;
0289 if (aPowellConfig.MaxIterations < 10)
0290 {
0291 aPowellConfig.MaxIterations = 10;
0292 }
0293
0294 for (int aStartIdx = 0; aStartIdx < theConfig.NbStarts; ++aStartIdx)
0295 {
0296
0297 math_Vector aStart(aLower, aUpper);
0298 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0299 {
0300 const double aRandVal = aRNG.NextReal();
0301 aStart(aDimIdx) =
0302 theLowerBounds(aDimIdx) + aRandVal * (theUpperBounds(aDimIdx) - theLowerBounds(aDimIdx));
0303 }
0304
0305
0306 VectorResult aLocalResult = Powell(theFunc, aStart, aPowellConfig);
0307
0308 if (aLocalResult.IsDone() && aLocalResult.Value)
0309 {
0310
0311 math_Vector aSol = *aLocalResult.Solution;
0312 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0313 {
0314 aSol(aDimIdx) =
0315 MathUtils::Clamp(aSol(aDimIdx), theLowerBounds(aDimIdx), theUpperBounds(aDimIdx));
0316 }
0317
0318
0319 double aValue;
0320 if (theFunc.Value(aSol, aValue) && aValue < aBestValue)
0321 {
0322 aBestValue = aValue;
0323 aBestSolution = aSol;
0324 aFound = true;
0325 }
0326 }
0327 else
0328 {
0329
0330 double aValue;
0331 if (theFunc.Value(aStart, aValue) && aValue < aBestValue)
0332 {
0333 aBestValue = aValue;
0334 aBestSolution = aStart;
0335 aFound = true;
0336 }
0337 }
0338 }
0339
0340 if (!aFound)
0341 {
0342 aResult.Status = Status::NumericalError;
0343 return aResult;
0344 }
0345
0346 aResult.Status = Status::OK;
0347 aResult.Solution = aBestSolution;
0348 aResult.Value = aBestValue;
0349 return aResult;
0350 }
0351
0352
0353
0354
0355
0356
0357
0358
0359
0360
0361
0362
0363 template <typename Function>
0364 VectorResult GlobalMinimum(Function& theFunc,
0365 const math_Vector& theLowerBounds,
0366 const math_Vector& theUpperBounds,
0367 const GlobalConfig& theConfig = GlobalConfig())
0368 {
0369 return GlobalMinimum(theFunc,
0370 theLowerBounds,
0371 theUpperBounds,
0372 theConfig,
0373 nullptr,
0374 nullptr,
0375 nullptr);
0376 }
0377
0378
0379
0380
0381
0382
0383
0384
0385
0386
0387
0388
0389
0390
0391
0392 template <typename Function>
0393 VectorResult GlobalMinimum(Function& theFunc,
0394 const math_Vector& theLowerBounds,
0395 const math_Vector& theUpperBounds,
0396 const GlobalConfig& theConfig,
0397 const PSOConfig* thePSOConfig,
0398 const NCollection_DynamicArray<PSOSeedParticle>* theSeeds = nullptr,
0399 PSOStats* theStats = nullptr)
0400 {
0401 switch (theConfig.Strategy)
0402 {
0403 case GlobalStrategy::PSO: {
0404 PSOConfig aPSOConfig;
0405 if (thePSOConfig != nullptr)
0406 {
0407 aPSOConfig = *thePSOConfig;
0408 }
0409 else
0410 {
0411 aPSOConfig.NbParticles = theConfig.NbPopulation;
0412 aPSOConfig.MaxIterations = theConfig.MaxIterations;
0413 aPSOConfig.Tolerance = theConfig.Tolerance;
0414 aPSOConfig.Seed = theConfig.Seed;
0415 aPSOConfig.PolishBudgetPerDim = theConfig.PolishBudgetPerDim;
0416 }
0417 return PSO(theFunc, theLowerBounds, theUpperBounds, aPSOConfig, theSeeds, theStats);
0418 }
0419
0420 case GlobalStrategy::MultiStart:
0421 return MultiStart(theFunc, theLowerBounds, theUpperBounds, theConfig);
0422
0423 case GlobalStrategy::PSOHybrid: {
0424
0425 PSOConfig aPSOConfig;
0426 if (thePSOConfig != nullptr)
0427 {
0428
0429 aPSOConfig = *thePSOConfig;
0430 }
0431 else
0432 {
0433
0434
0435 aPSOConfig.NbParticles = theConfig.NbPopulation;
0436 aPSOConfig.MaxIterations = theConfig.MaxIterations / 2;
0437 aPSOConfig.Tolerance = theConfig.Tolerance * 10.0;
0438 aPSOConfig.Seed = theConfig.Seed;
0439 aPSOConfig.PolishBudgetPerDim = theConfig.PolishBudgetPerDim;
0440 }
0441
0442 VectorResult aPSOResult =
0443 PSO(theFunc, theLowerBounds, theUpperBounds, aPSOConfig, theSeeds, theStats);
0444
0445 if (!aPSOResult.IsDone() || !aPSOResult.Solution)
0446 {
0447 return aPSOResult;
0448 }
0449
0450
0451 Config aPowellConfig;
0452 aPowellConfig.Tolerance = theConfig.Tolerance;
0453 aPowellConfig.XTolerance = theConfig.Tolerance;
0454 aPowellConfig.FTolerance = theConfig.Tolerance;
0455 aPowellConfig.MaxIterations = theConfig.MaxIterations / 2;
0456
0457 VectorResult aLocalResult = Powell(theFunc, *aPSOResult.Solution, aPowellConfig);
0458
0459 if (aLocalResult.IsDone() && aLocalResult.Value && aPSOResult.Value
0460 && *aLocalResult.Value < *aPSOResult.Value)
0461 {
0462
0463 const int aLower = theLowerBounds.Lower();
0464 const int aUpper = theLowerBounds.Upper();
0465 math_Vector aSol = *aLocalResult.Solution;
0466 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0467 {
0468 aSol(aDimIdx) =
0469 MathUtils::Clamp(aSol(aDimIdx), theLowerBounds(aDimIdx), theUpperBounds(aDimIdx));
0470 }
0471 aLocalResult.Solution = aSol;
0472
0473
0474 double aValue;
0475 if (theFunc.Value(aSol, aValue))
0476 {
0477 aLocalResult.Value = aValue;
0478 }
0479 return aLocalResult;
0480 }
0481
0482 return aPSOResult;
0483 }
0484
0485 case GlobalStrategy::DifferentialEvolution:
0486 return DifferentialEvolution(theFunc, theLowerBounds, theUpperBounds, theConfig);
0487
0488 default:
0489 VectorResult aResult;
0490 aResult.Status = Status::InvalidInput;
0491 return aResult;
0492 }
0493 }
0494
0495 }
0496
0497 #endif