/*=========================================================================
*
* Copyright NumFOCUS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
#ifndef itkParticleSwarmOptimizerBase_h
#define itkParticleSwarmOptimizerBase_h
#include "itkSingleValuedNonLinearOptimizer.h"
#include "itkMersenneTwisterRandomVariateGenerator.h"
#include "ITKOptimizersExport.h"
namespace itk
{
/** \class ParticleSwarmOptimizerBase
* \brief Abstract implementation of a Particle Swarm Optimization (PSO) algorithm.
*
* The PSO algorithm was originally presented in:
* J. Kennedy, R. Eberhart, "Particle Swarm Optimization",
* Proc. IEEE Int. Neural Networks, 1995.
*
* The algorithm is a stochastic global search optimization approach.
* Optimization is performed by maintaining a swarm of particles that traverse
* the parameter space, searching for the optimal function value. Associated
* with each particle are its location and speed, in parameter space.
*
* Swarm initialization is performed within the user supplied parameter bounds
* using either a uniform distribution or a normal distribution centered on
* the initial parameter values supplied by the user. The search terminates when
* the maximal number of iterations has been reached or when the change in the
* best value in the past \f$g\f$ generations is below a threshold and the swarm
* has collapsed (i.e. best personal particle locations are close to the
* swarm's best location in parameter space).
*
* The actual optimization procedure, updating the swarm, is performed in the
* subclasses, required to implement the UpdateSwarm() method.
*
* NOTE: This implementation only performs minimization.
*
* \ingroup Numerics Optimizers
* \ingroup ITKOptimizers
*/
class ITKOptimizers_EXPORT ParticleSwarmOptimizerBase : public SingleValuedNonLinearOptimizer
{
public:
ITK_DISALLOW_COPY_AND_MOVE(ParticleSwarmOptimizerBase);
/** Standard "Self" type alias. */
using Self = ParticleSwarmOptimizerBase;
using Superclass = SingleValuedNonLinearOptimizer;
using Pointer = SmartPointer;
using ConstPointer = SmartPointer;
/** \see LightObject::GetNameOfClass() */
itkOverrideGetNameOfClassMacro(ParticleSwarmOptimizerBase);
using ParameterBoundsType = std::vector>;
struct ParticleData
{
ParametersType m_CurrentParameters;
ParametersType m_CurrentVelocity;
CostFunctionType::MeasureType m_CurrentValue;
ParametersType m_BestParameters;
CostFunctionType::MeasureType m_BestValue;
};
using SwarmType = std::vector;
using NumberOfIterationsType = unsigned int;
using NumberOfParticlesType = unsigned int;
using NumberOfGenerationsType = unsigned int;
using MeasureType = CostFunctionType::MeasureType;
using ValueType = ParametersType::ValueType;
using RandomVariateGeneratorType = Statistics::MersenneTwisterRandomVariateGenerator;
/** Specify whether to initialize the particles using a normal distribution
* centered on the user supplied initial value or a uniform distribution.
* If the optimum is expected to be near the initial value it is likely
* that initializing with a normal distribution will result in faster
* convergence.*/
itkSetMacro(InitializeNormalDistribution, bool);
itkGetMacro(InitializeNormalDistribution, bool);
itkBooleanMacro(InitializeNormalDistribution);
/**
* Specify the initial swarm. Useful for evaluating PSO variants. If the
* initial swarm is set it will be used. To revert to random initialization
* (uniform or normal particle distributions) set using an empty swarm.
*/
void
SetInitialSwarm(const SwarmType & initialSwarm);
void
ClearSwarm();
/**
* Indicate whether or not to output the swarm information when printing an
* object. By default this option is turned off as it generates too much
* information.
*/
itkSetMacro(PrintSwarm, bool);
itkGetMacro(PrintSwarm, bool);
itkBooleanMacro(PrintSwarm);
/** Start optimization. */
void
StartOptimization() override;
/** Set/Get number of particles in the swarm - the maximal number of function
evaluations is m_MaximalNumberOfIterations*m_NumberOfParticles */
void
SetNumberOfParticles(NumberOfParticlesType n);
itkGetMacro(NumberOfParticles, NumberOfParticlesType);
/** Set/Get maximal number of iterations - the maximal number of function
evaluations is m_MaximalNumberOfIterations*m_NumberOfParticles */
itkSetMacro(MaximalNumberOfIterations, NumberOfIterationsType);
itkGetMacro(MaximalNumberOfIterations, NumberOfIterationsType);
/** Set/Get the number of generations to continue with minimal improvement in
* the function value, |f_best(g_i) - f_best(g_k)| & bounds, unsigned int n);
ParameterBoundsType
GetParameterBounds() const;
/** The optimization algorithm will terminate when the function improvement
* in the last m_NumberOfGenerationsWithMinimalImprovement generations
* is less than m_FunctionConvergenceTolerance and the maximal distance
* between particles and the best particle in each dimension is less than
* m_ParametersConvergenceTolerance[i] for the specified percentage of the
* particles.
* That is, we haven't improved the best function value for a while and in
* the parameter space most (m%) of our particles attained their best value
* close to the swarm's best value.
* NOTE: The use of different tolerances for each dimension is desired when
* optimizing over non-commensurate parameters (e.g. rotation and
* translation). Alternatively, we could use ITK's parameter scaling
* approach. The current approach seems more intuitive.
*/
itkSetMacro(FunctionConvergenceTolerance, MeasureType);
itkGetMacro(FunctionConvergenceTolerance, MeasureType);
/**Set parameters convergence tolerance using the same value for all, sz,
parameters*/
void
SetParametersConvergenceTolerance(ValueType convergenceTolerance, unsigned int sz);
itkSetMacro(ParametersConvergenceTolerance, ParametersType);
itkGetMacro(ParametersConvergenceTolerance, ParametersType);
itkGetMacro(PercentageParticlesConverged, double);
itkSetMacro(PercentageParticlesConverged, double);
/**Set the random number seed for the swarm. Use this method to
* produce repeatable results, typically, for testing.
*/
itkSetMacro(Seed, RandomVariateGeneratorType::IntegerType);
itkGetMacro(Seed, RandomVariateGeneratorType::IntegerType);
/** Use a specific seed to initialize the random number
* generator. If On, use m_Seed to seed the random number
* generator. Default is Off. */
itkSetMacro(UseSeed, bool);
itkGetMacro(UseSeed, bool);
itkBooleanMacro(UseSeed);
/** Get the function value for the current position.
* NOTE: This value is only valid during and after the execution of the
* StartOptimization() method.*/
MeasureType
GetValue() const;
/** Get the reason for termination */
const std::string
GetStopConditionDescription() const override;
/** Print the swarm information to the given output stream. Each line
* (particle data) is of the form:
* current_parameters current_velocity current_value best_parameters best_value
*/
void
PrintSwarm(std::ostream & os, Indent indent) const;
protected:
ParticleSwarmOptimizerBase();
~ParticleSwarmOptimizerBase() override;
void
PrintSelf(std::ostream & os, Indent indent) const override;
void
PrintParamtersType(const ParametersType & x, std::ostream & os) const;
/**
* Implement your update rule in this function.*/
virtual void
UpdateSwarm() = 0;
virtual void
ValidateSettings();
/**
* Initialize the particle swarm, and seed the random number generator.
*/
virtual void
Initialize();
void
RandomInitialization();
void
FileInitialization();
bool m_PrintSwarm{};
std::ostringstream m_StopConditionDescription{};
bool m_InitializeNormalDistribution{};
NumberOfParticlesType m_NumberOfParticles{};
NumberOfIterationsType m_MaximalNumberOfIterations{};
NumberOfGenerationsType m_NumberOfGenerationsWithMinimalImprovement{};
ParameterBoundsType m_ParameterBounds{};
ParametersType m_ParametersConvergenceTolerance{};
double m_PercentageParticlesConverged{};
CostFunctionType::MeasureType m_FunctionConvergenceTolerance{};
std::vector m_Particles{};
CostFunctionType::MeasureType m_FunctionBestValue{ 0 };
std::vector m_FunctionBestValueMemory{};
ParametersType m_ParametersBestValue{};
NumberOfIterationsType m_IterationIndex{ 0 };
RandomVariateGeneratorType::IntegerType m_Seed{};
bool m_UseSeed{};
};
} // end namespace itk
#endif