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_PSO_HeaderFile
0015 #define _MathOpt_PSO_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_Random.hxx>
0022
0023 #include <NCollection_DynamicArray.hxx>
0024
0025 #include <cmath>
0026 #include <optional>
0027
0028 namespace MathOpt
0029 {
0030 using namespace MathUtils;
0031
0032
0033 enum class PSOInitMode
0034 {
0035 RandomOnly,
0036 SeededOnly,
0037 SeededPlusRandom
0038 };
0039
0040
0041 enum class PSOBoundaryMode
0042 {
0043 Clamp,
0044 Reflect,
0045 Wrap
0046 };
0047
0048
0049 enum class PSOInertiaSchedule
0050 {
0051 Constant,
0052 LinearDecay
0053 };
0054
0055
0056 struct PSOSeedParticle
0057 {
0058 math_Vector Position;
0059 std::optional<double> Value;
0060 std::optional<math_Vector> Velocity;
0061
0062 PSOSeedParticle(const math_Vector& thePos)
0063 : Position(thePos)
0064 {
0065 }
0066
0067 PSOSeedParticle(const math_Vector& thePos, const double theValue)
0068 : Position(thePos),
0069 Value(theValue)
0070 {
0071 }
0072 };
0073
0074
0075 struct PSOStats
0076 {
0077 int NbFunctionEvals = 0;
0078 int NbIterations = 0;
0079 int NbBoundaryCorrections = 0;
0080 int NbStagnationEvents = 0;
0081 int NbRestarts = 0;
0082 double InitialBest = std::numeric_limits<double>::max();
0083 double FinalBest = std::numeric_limits<double>::max();
0084 };
0085
0086
0087 struct PSOConfig : NDimConfig
0088 {
0089 int NbParticles = 40;
0090 double Omega = 0.7;
0091 double PhiPersonal = 1.5;
0092 double PhiGlobal = 1.5;
0093 double VelocityClamp = 0.5;
0094 unsigned int Seed = 6;
0095
0096 PSOInitMode InitMode = PSOInitMode::RandomOnly;
0097 PSOBoundaryMode BoundaryMode = PSOBoundaryMode::Clamp;
0098 PSOInertiaSchedule InertiaSchedule = PSOInertiaSchedule::Constant;
0099 double OmegaMin = 0.4;
0100 int MinIterations = 0;
0101 std::optional<double> TargetValue;
0102 double NoImproveTol = 0.0;
0103 int NoImproveIters = 10;
0104 double RestartFraction = 0.0;
0105 int MaxRestarts = 0;
0106 int PolishBudgetPerDim = 50;
0107
0108
0109 PSOConfig()
0110 : NDimConfig(1.0e-8, 100, true)
0111 {
0112 }
0113
0114
0115 PSOConfig(int theNbParticles, int theMaxIter = 100, double theTolerance = 1.0e-8)
0116 : NDimConfig(theTolerance, theMaxIter, true),
0117 NbParticles(theNbParticles)
0118 {
0119 }
0120 };
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135 template <typename Function>
0136 void PolishCoordinateWise(Function& theFunc,
0137 math_Vector& thePosition,
0138 double& theValue,
0139 const math_Vector& theLowerBounds,
0140 const math_Vector& theUpperBounds,
0141 double theTolerance,
0142 int theMaxPolishEvals,
0143 int& theEvalCount)
0144 {
0145 theEvalCount = 0;
0146
0147 const int aLower = thePosition.Lower();
0148 const int aUpper = thePosition.Upper();
0149 const int aNbDims = aUpper - aLower + 1;
0150
0151
0152 const int aNbPasses = (aNbDims > 1) ? 2 : 1;
0153 const int aPerCoordLimit = std::max(1, theMaxPolishEvals / (aNbDims * aNbPasses));
0154
0155 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0156 {
0157 if (theEvalCount >= theMaxPolishEvals)
0158 {
0159 break;
0160 }
0161
0162 const int aMaxIter = std::min(aPerCoordLimit, theMaxPolishEvals - theEvalCount);
0163 BrentAlongCoordinate(theFunc,
0164 thePosition,
0165 aDimIdx,
0166 theLowerBounds(aDimIdx),
0167 theUpperBounds(aDimIdx),
0168 theValue,
0169 theTolerance,
0170 aMaxIter,
0171 theEvalCount);
0172 }
0173
0174
0175 if (theEvalCount < theMaxPolishEvals && aNbDims > 1)
0176 {
0177 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0178 {
0179 if (theEvalCount >= theMaxPolishEvals)
0180 {
0181 break;
0182 }
0183
0184 const int aMaxIter = std::min(aPerCoordLimit, theMaxPolishEvals - theEvalCount);
0185 BrentAlongCoordinate(theFunc,
0186 thePosition,
0187 aDimIdx,
0188 theLowerBounds(aDimIdx),
0189 theUpperBounds(aDimIdx),
0190 theValue,
0191 theTolerance,
0192 aMaxIter,
0193 theEvalCount);
0194 }
0195 }
0196 }
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226 template <typename Function>
0227 VectorResult PSO(Function& theFunc,
0228 const math_Vector& theLowerBounds,
0229 const math_Vector& theUpperBounds,
0230 const PSOConfig& theConfig,
0231 const NCollection_DynamicArray<PSOSeedParticle>* theSeeds,
0232 PSOStats* theStats = nullptr)
0233 {
0234 VectorResult aResult;
0235
0236 const int aLower = theLowerBounds.Lower();
0237 const int aUpper = theLowerBounds.Upper();
0238 const int aNbDims = aUpper - aLower + 1;
0239
0240
0241 if (theUpperBounds.Length() != aNbDims)
0242 {
0243 aResult.Status = Status::InvalidInput;
0244 return aResult;
0245 }
0246
0247
0248 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0249 {
0250 if (theLowerBounds(aDimIdx) >= theUpperBounds(aDimIdx))
0251 {
0252 aResult.Status = Status::InvalidInput;
0253 return aResult;
0254 }
0255 }
0256
0257 const int aNbParticles = theConfig.NbParticles;
0258 if (aNbParticles <= 0)
0259 {
0260 aResult.Status = Status::InvalidInput;
0261 return aResult;
0262 }
0263
0264
0265 PSOStats aLocalStats;
0266
0267
0268 struct Particle
0269 {
0270 math_Vector Position;
0271 math_Vector Velocity;
0272 math_Vector BestPosition;
0273 double BestValue;
0274 double CurrentValue;
0275
0276 Particle(int theLower, int theUpper)
0277 : Position(theLower, theUpper),
0278 Velocity(theLower, theUpper),
0279 BestPosition(theLower, theUpper),
0280 BestValue(std::numeric_limits<double>::max()),
0281 CurrentValue(std::numeric_limits<double>::max())
0282 {
0283 }
0284 };
0285
0286 NCollection_DynamicArray<Particle> aSwarm;
0287 for (int aPartIdx = 0; aPartIdx < aNbParticles; ++aPartIdx)
0288 {
0289 aSwarm.Append(Particle(aLower, aUpper));
0290 }
0291
0292
0293 MathUtils::RandomGenerator aRNG(theConfig.Seed);
0294
0295
0296 math_Vector aVelMax(aLower, aUpper);
0297 math_Vector aRange(aLower, aUpper);
0298 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0299 {
0300 aRange(aDimIdx) = theUpperBounds(aDimIdx) - theLowerBounds(aDimIdx);
0301 aVelMax(aDimIdx) = theConfig.VelocityClamp * aRange(aDimIdx);
0302 }
0303
0304
0305 math_Vector aGlobalBest(aLower, aUpper);
0306 double aGlobalBestValue = std::numeric_limits<double>::max();
0307
0308
0309 const int aNbSeeds = (theSeeds != nullptr) ? theSeeds->Length() : 0;
0310 int aSeeded = 0;
0311
0312
0313 if (aNbSeeds > 0)
0314 {
0315 for (int aSeedIdx = 0; aSeedIdx < aNbSeeds; ++aSeedIdx)
0316 {
0317 const PSOSeedParticle& aSeed = theSeeds->Value(aSeedIdx);
0318 if (aSeed.Position.Lower() != aLower || aSeed.Position.Upper() != aUpper)
0319 {
0320 aResult.Status = Status::InvalidInput;
0321 return aResult;
0322 }
0323 if (aSeed.Velocity.has_value()
0324 && (aSeed.Velocity->Lower() != aLower || aSeed.Velocity->Upper() != aUpper))
0325 {
0326 aResult.Status = Status::InvalidInput;
0327 return aResult;
0328 }
0329 }
0330 }
0331
0332
0333 if (theConfig.InitMode == PSOInitMode::SeededOnly && aNbSeeds == 0)
0334 {
0335 aResult.Status = Status::InvalidInput;
0336 return aResult;
0337 }
0338
0339 if (aNbSeeds > 0
0340 && (theConfig.InitMode == PSOInitMode::SeededPlusRandom
0341 || theConfig.InitMode == PSOInitMode::SeededOnly))
0342 {
0343
0344 const int aNbDirect = std::min(aNbSeeds, aNbParticles);
0345 for (int aSeedIdx = 0; aSeedIdx < aNbDirect; ++aSeedIdx)
0346 {
0347 Particle& aParticle = aSwarm.ChangeValue(aSeedIdx);
0348 const PSOSeedParticle& aSeed = theSeeds->Value(aSeedIdx);
0349
0350
0351 bool aWasClamped = false;
0352 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0353 {
0354 const double aClamped = MathUtils::Clamp(aSeed.Position(aDimIdx),
0355 theLowerBounds(aDimIdx),
0356 theUpperBounds(aDimIdx));
0357 if (aClamped != aSeed.Position(aDimIdx))
0358 {
0359 aWasClamped = true;
0360 }
0361 aParticle.Position(aDimIdx) = aClamped;
0362 }
0363
0364
0365 if (aSeed.Velocity.has_value())
0366 {
0367 const math_Vector& aSeedVel = *aSeed.Velocity;
0368 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0369 {
0370 aParticle.Velocity(aDimIdx) =
0371 MathUtils::Clamp(aSeedVel(aDimIdx), -aVelMax(aDimIdx), aVelMax(aDimIdx));
0372 }
0373 }
0374 else
0375 {
0376 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0377 {
0378 aParticle.Velocity(aDimIdx) = (2.0 * aRNG.NextReal() - 1.0) * aVelMax(aDimIdx);
0379 }
0380 }
0381
0382
0383 if (aSeed.Value.has_value() && !aWasClamped)
0384 {
0385 aParticle.CurrentValue = *aSeed.Value;
0386 }
0387 else
0388 {
0389 if (!theFunc.Value(aParticle.Position, aParticle.CurrentValue))
0390 {
0391 aParticle.CurrentValue = std::numeric_limits<double>::max();
0392 }
0393 ++aLocalStats.NbFunctionEvals;
0394 }
0395
0396 aParticle.BestPosition = aParticle.Position;
0397 aParticle.BestValue = aParticle.CurrentValue;
0398
0399 if (aParticle.BestValue < aGlobalBestValue)
0400 {
0401 aGlobalBestValue = aParticle.BestValue;
0402 aGlobalBest = aParticle.BestPosition;
0403 }
0404 }
0405 aSeeded = aNbDirect;
0406
0407
0408 if (theConfig.InitMode == PSOInitMode::SeededOnly && aSeeded < aNbParticles)
0409 {
0410 for (int aPartIdx = aSeeded; aPartIdx < aNbParticles; ++aPartIdx)
0411 {
0412 Particle& aParticle = aSwarm.ChangeValue(aPartIdx);
0413
0414 const int aSrcIdx = aPartIdx % aSeeded;
0415 const PSOSeedParticle& aSrc = theSeeds->Value(aSrcIdx);
0416
0417 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0418 {
0419 double aJitter = (2.0 * aRNG.NextReal() - 1.0) * 0.1 * aRange(aDimIdx);
0420 aParticle.Position(aDimIdx) = MathUtils::Clamp(aSrc.Position(aDimIdx) + aJitter,
0421 theLowerBounds(aDimIdx),
0422 theUpperBounds(aDimIdx));
0423 aParticle.Velocity(aDimIdx) = (2.0 * aRNG.NextReal() - 1.0) * aVelMax(aDimIdx);
0424 }
0425
0426 if (!theFunc.Value(aParticle.Position, aParticle.CurrentValue))
0427 {
0428 aParticle.CurrentValue = std::numeric_limits<double>::max();
0429 }
0430 ++aLocalStats.NbFunctionEvals;
0431
0432 aParticle.BestPosition = aParticle.Position;
0433 aParticle.BestValue = aParticle.CurrentValue;
0434
0435 if (aParticle.BestValue < aGlobalBestValue)
0436 {
0437 aGlobalBestValue = aParticle.BestValue;
0438 aGlobalBest = aParticle.BestPosition;
0439 }
0440 }
0441 aSeeded = aNbParticles;
0442 }
0443 }
0444
0445
0446 for (int aPartIdx = aSeeded; aPartIdx < aNbParticles; ++aPartIdx)
0447 {
0448 Particle& aParticle = aSwarm.ChangeValue(aPartIdx);
0449 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0450 {
0451 aParticle.Position(aDimIdx) = theLowerBounds(aDimIdx) + aRNG.NextReal() * aRange(aDimIdx);
0452 aParticle.Velocity(aDimIdx) = (2.0 * aRNG.NextReal() - 1.0) * aVelMax(aDimIdx);
0453 }
0454
0455 if (!theFunc.Value(aParticle.Position, aParticle.CurrentValue))
0456 {
0457 aParticle.CurrentValue = std::numeric_limits<double>::max();
0458 }
0459 ++aLocalStats.NbFunctionEvals;
0460
0461 aParticle.BestPosition = aParticle.Position;
0462 aParticle.BestValue = aParticle.CurrentValue;
0463
0464 if (aParticle.BestValue < aGlobalBestValue)
0465 {
0466 aGlobalBestValue = aParticle.BestValue;
0467 aGlobalBest = aParticle.BestPosition;
0468 }
0469 }
0470
0471 aLocalStats.InitialBest = aGlobalBestValue;
0472
0473
0474 double aPrevBest = aGlobalBestValue;
0475 int aStagnationCount = 0;
0476 const double aStagnTol =
0477 (theConfig.NoImproveTol > 0.0) ? theConfig.NoImproveTol : theConfig.Tolerance;
0478
0479 for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0480 {
0481 aResult.NbIterations = anIter + 1;
0482
0483
0484 double anOmega = theConfig.Omega;
0485 if (theConfig.InertiaSchedule == PSOInertiaSchedule::LinearDecay)
0486 {
0487 anOmega = theConfig.Omega
0488 - (theConfig.Omega - theConfig.OmegaMin) * static_cast<double>(anIter)
0489 / static_cast<double>(theConfig.MaxIterations);
0490 }
0491
0492
0493 for (int aPartIdx = 0; aPartIdx < aNbParticles; ++aPartIdx)
0494 {
0495 Particle& aParticle = aSwarm.ChangeValue(aPartIdx);
0496
0497
0498 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0499 {
0500 const double aRand1 = aRNG.NextReal();
0501 const double aRand2 = aRNG.NextReal();
0502
0503 double aVnew =
0504 anOmega * aParticle.Velocity(aDimIdx)
0505 + theConfig.PhiPersonal * aRand1
0506 * (aParticle.BestPosition(aDimIdx) - aParticle.Position(aDimIdx))
0507 + theConfig.PhiGlobal * aRand2 * (aGlobalBest(aDimIdx) - aParticle.Position(aDimIdx));
0508
0509
0510 aVnew = MathUtils::Clamp(aVnew, -aVelMax(aDimIdx), aVelMax(aDimIdx));
0511 aParticle.Velocity(aDimIdx) = aVnew;
0512 }
0513
0514
0515 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0516 {
0517 double aXnew = aParticle.Position(aDimIdx) + aParticle.Velocity(aDimIdx);
0518
0519 if (aXnew < theLowerBounds(aDimIdx) || aXnew > theUpperBounds(aDimIdx))
0520 {
0521 ++aLocalStats.NbBoundaryCorrections;
0522
0523 switch (theConfig.BoundaryMode)
0524 {
0525 case PSOBoundaryMode::Clamp: {
0526 aXnew = MathUtils::Clamp(aXnew, theLowerBounds(aDimIdx), theUpperBounds(aDimIdx));
0527 aParticle.Velocity(aDimIdx) = -0.5 * aParticle.Velocity(aDimIdx);
0528 break;
0529 }
0530 case PSOBoundaryMode::Reflect: {
0531 if (aXnew < theLowerBounds(aDimIdx))
0532 {
0533 aXnew = 2.0 * theLowerBounds(aDimIdx) - aXnew;
0534 }
0535 else
0536 {
0537 aXnew = 2.0 * theUpperBounds(aDimIdx) - aXnew;
0538 }
0539
0540 aXnew = MathUtils::Clamp(aXnew, theLowerBounds(aDimIdx), theUpperBounds(aDimIdx));
0541 aParticle.Velocity(aDimIdx) = -0.5 * aParticle.Velocity(aDimIdx);
0542 break;
0543 }
0544 case PSOBoundaryMode::Wrap: {
0545 const double aRangeSize = aRange(aDimIdx);
0546 aXnew = aXnew - theLowerBounds(aDimIdx);
0547 aXnew = std::fmod(aXnew, aRangeSize);
0548 if (aXnew < 0.0)
0549 {
0550 aXnew += aRangeSize;
0551 }
0552 aXnew += theLowerBounds(aDimIdx);
0553 break;
0554 }
0555 }
0556 }
0557
0558 aParticle.Position(aDimIdx) = aXnew;
0559 }
0560
0561
0562 if (!theFunc.Value(aParticle.Position, aParticle.CurrentValue))
0563 {
0564 aParticle.CurrentValue = std::numeric_limits<double>::max();
0565 }
0566 ++aLocalStats.NbFunctionEvals;
0567
0568
0569 if (aParticle.CurrentValue < aParticle.BestValue)
0570 {
0571 aParticle.BestValue = aParticle.CurrentValue;
0572 aParticle.BestPosition = aParticle.Position;
0573 }
0574
0575
0576 if (aParticle.BestValue < aGlobalBestValue)
0577 {
0578 aGlobalBestValue = aParticle.BestValue;
0579 aGlobalBest = aParticle.BestPosition;
0580 }
0581 }
0582
0583
0584 if (theConfig.TargetValue.has_value() && aGlobalBestValue <= *theConfig.TargetValue)
0585 {
0586 break;
0587 }
0588
0589
0590 if (anIter >= theConfig.MinIterations)
0591 {
0592 if (std::abs(aGlobalBestValue - aPrevBest) < aStagnTol * (1.0 + std::abs(aGlobalBestValue)))
0593 {
0594 ++aStagnationCount;
0595 if (aStagnationCount >= theConfig.NoImproveIters)
0596 {
0597 ++aLocalStats.NbStagnationEvents;
0598
0599
0600 if (theConfig.RestartFraction > 0.0
0601 && (theConfig.MaxRestarts == 0 || aLocalStats.NbRestarts < theConfig.MaxRestarts))
0602 {
0603 ++aLocalStats.NbRestarts;
0604 aStagnationCount = 0;
0605
0606
0607 int aNbRestart = static_cast<int>(std::ceil(theConfig.RestartFraction * aNbParticles));
0608 aNbRestart = std::min(aNbRestart, aNbParticles - 1);
0609
0610
0611 int aBestIdx = 0;
0612 for (int aFindIdx = 1; aFindIdx < aNbParticles; ++aFindIdx)
0613 {
0614 if (aSwarm.Value(aFindIdx).BestValue < aSwarm.Value(aBestIdx).BestValue)
0615 {
0616 aBestIdx = aFindIdx;
0617 }
0618 }
0619
0620
0621
0622 NCollection_DynamicArray<int> aWorstIndices;
0623 for (int aCollIdx = 0; aCollIdx < aNbParticles; ++aCollIdx)
0624 {
0625 if (aCollIdx != aBestIdx)
0626 {
0627 aWorstIndices.Append(aCollIdx);
0628 }
0629 }
0630
0631 for (int aSortOuter = 0; aSortOuter < aWorstIndices.Length() - 1; ++aSortOuter)
0632 {
0633 for (int aSortInner = aSortOuter + 1; aSortInner < aWorstIndices.Length();
0634 ++aSortInner)
0635 {
0636 if (aSwarm.Value(aWorstIndices.Value(aSortOuter)).BestValue
0637 < aSwarm.Value(aWorstIndices.Value(aSortInner)).BestValue)
0638 {
0639 const int aTmp = aWorstIndices.Value(aSortOuter);
0640 aWorstIndices.ChangeValue(aSortOuter) = aWorstIndices.Value(aSortInner);
0641 aWorstIndices.ChangeValue(aSortInner) = aTmp;
0642 }
0643 }
0644 }
0645
0646 const int aNbToRestart = std::min(aNbRestart, aWorstIndices.Length());
0647 for (int aRestIdx = 0; aRestIdx < aNbToRestart; ++aRestIdx)
0648 {
0649 Particle& aRestartPart = aSwarm.ChangeValue(aWorstIndices.Value(aRestIdx));
0650 for (int aDimIdx = aLower; aDimIdx <= aUpper; ++aDimIdx)
0651 {
0652 aRestartPart.Position(aDimIdx) =
0653 theLowerBounds(aDimIdx) + aRNG.NextReal() * aRange(aDimIdx);
0654 aRestartPart.Velocity(aDimIdx) = (2.0 * aRNG.NextReal() - 1.0) * aVelMax(aDimIdx);
0655 }
0656
0657 if (!theFunc.Value(aRestartPart.Position, aRestartPart.CurrentValue))
0658 {
0659 aRestartPart.CurrentValue = std::numeric_limits<double>::max();
0660 }
0661 ++aLocalStats.NbFunctionEvals;
0662
0663 aRestartPart.BestPosition = aRestartPart.Position;
0664 aRestartPart.BestValue = aRestartPart.CurrentValue;
0665
0666 if (aRestartPart.BestValue < aGlobalBestValue)
0667 {
0668 aGlobalBestValue = aRestartPart.BestValue;
0669 aGlobalBest = aRestartPart.BestPosition;
0670 }
0671 }
0672 }
0673 else
0674 {
0675
0676 break;
0677 }
0678 }
0679 }
0680 else
0681 {
0682 aStagnationCount = 0;
0683 }
0684 }
0685 aPrevBest = aGlobalBestValue;
0686 }
0687
0688
0689 int aPolishEvals = 0;
0690 if (theConfig.PolishBudgetPerDim > 0)
0691 {
0692 PolishCoordinateWise(theFunc,
0693 aGlobalBest,
0694 aGlobalBestValue,
0695 theLowerBounds,
0696 theUpperBounds,
0697 theConfig.Tolerance,
0698 theConfig.PolishBudgetPerDim * aNbDims,
0699 aPolishEvals);
0700 aLocalStats.NbFunctionEvals += aPolishEvals;
0701 }
0702
0703 aLocalStats.NbIterations = static_cast<int>(aResult.NbIterations);
0704 aLocalStats.FinalBest = aGlobalBestValue;
0705
0706 if (theStats != nullptr)
0707 {
0708 *theStats = aLocalStats;
0709 }
0710
0711 aResult.Status = Status::OK;
0712 aResult.Solution = aGlobalBest;
0713 aResult.Value = aGlobalBestValue;
0714 return aResult;
0715 }
0716
0717
0718
0719
0720
0721
0722
0723
0724
0725 template <typename Function>
0726 VectorResult PSO(Function& theFunc,
0727 const math_Vector& theLowerBounds,
0728 const math_Vector& theUpperBounds,
0729 const PSOConfig& theConfig = PSOConfig())
0730 {
0731 return PSO(theFunc, theLowerBounds, theUpperBounds, theConfig, nullptr, nullptr);
0732 }
0733
0734 }
0735
0736 #endif