diff --git a/HISTORY.md b/HISTORY.md index 0742d6e4c..7ce367b4b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -7,7 +7,11 @@ ([#404](https://github.com/mlpack/ensmallen/pull/404)). * For Armadillo 14.2.0 switch to `.index_min()` and `.index_max()` - ([#409](https://github.com/mlpack/ensmallen/pull/409). + ([#409](https://github.com/mlpack/ensmallen/pull/409)). + + * Added IPOP and BIPOP restart mechanisms for CMA-ES. + ([#403](https://github.com/mlpack/ensmallen/pull/403)). + ### ensmallen 2.21.1: "Bent Antenna" ###### 2024-02-15 diff --git a/doc/function_types.md b/doc/function_types.md index b5f282136..3da52a000 100644 --- a/doc/function_types.md +++ b/doc/function_types.md @@ -358,7 +358,10 @@ Each of the implemented methods is allowed to have additional cv-modifiers The following optimizers can be used with arbitrary separable functions: - - [CMAES](#cmaes) + - [Active CMA-ES](#active-cma-es) + - [BIPOP CMA-ES](#bipop-cma-es) + - [CMA-ES](#cma-es) + - [IPOP CMA-ES](#ipop-cma-es) Each of these optimizers has an `Optimize()` function that is called as `Optimize(f, x)` where `f` is the function to be optimized and `x` holds the @@ -453,7 +456,7 @@ int main() // parameters, so the shape is 10x1. arma::mat params(10, 1, arma::fill::randn); - // Use the CMAES optimizer with default parameters to minimize the + // Use the CMA-ES optimizer with default parameters to minimize the // LinearRegressionFunction. // The ens::CMAES type can be replaced with any suitable ensmallen optimizer // that can handle arbitrary separable functions. @@ -461,7 +464,7 @@ int main() LinearRegressionFunction lrf(data, responses); cmaes.Optimize(lrf, params); - std::cout << "The optimized linear regression model found by CMAES has the " + std::cout << "The optimized linear regression model found by CMA-ES has the " << "parameters " << params.t(); } ``` diff --git a/doc/optimizers.md b/doc/optimizers.md index b5f31c470..2691eb751 100644 --- a/doc/optimizers.md +++ b/doc/optimizers.md @@ -1,4 +1,4 @@ -## ActiveCMAES +## Active CMA-ES *An optimizer for [separable functions](#separable-functions).* @@ -72,12 +72,12 @@ the coordinates respectively. RosenbrockFunction f; arma::mat coordinates = f.GetInitialPoint(); -// ActiveCMAES with the FullSelection and BoundaryBoxConstraint policies. +// Active CMA-ES with the FullSelection and BoundaryBoxConstraint policies. BoundaryBoxConstraint b(-1, 1); ActiveCMAES optimizer(0, b, 32, 200, 1e-4); optimizer.Optimize(f, coordinates); -// ActiveCMAES with the RandomSelection and BoundaryBoxConstraint policies. +// Active CMA-ES with the RandomSelection and BoundaryBoxConstraint policies. ApproxActiveCMAES> cmaes(0, b, 32, 200, 1e-4); approxOptimizer.Optimize(f, coordinates); ``` @@ -86,7 +86,9 @@ approxOptimizer.Optimize(f, coordinates); #### See also: - * [CMAES](#cmaes) + * [BIPOP CMA-ES](#bipop-cma-es) + * [CMA-ES](#cma-es) + * [IPOP CMA-ES](#ipop-cma-es) * [Improving Evolution Strategies through Active Covariance Matrix Adaptation](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.114.4239&rep=rep1&type=pdfn) * [Evolution strategy in Wikipedia](https://en.wikipedia.org/wiki/Evolution_strategy) @@ -869,6 +871,78 @@ optimizer2.Optimize(f, coordinates); * [SGD in Wikipedia](https://en.wikipedia.org/wiki/Stochastic_gradient_descent) * [SGD](#standard-sgd) +## BIPOP CMA-ES + +*An optimizer for [separable functions](#separable-functions).* + +BIPOP CMA-ES (Bi-Population CMA-ES) extends the idea of [IPOP CMA-ES](#ipop-cma-es) by using +two intertwined restart strategies: one with an increasing population size and +another maintaining a smaller, variable population size. This strategy allows +BIPOP CMA-ES to adaptively balance exploration and exploitation across the +fitness landscape, and can outperform IPOP. The larger population restarts aim +to explore broadly, improving global search capabilities, while the smaller +populations intensify the search in promising regions. + +### Constructors + +* `BIPOP_CMAES<`_`SelectionPolicyType, TransformationPolicyType`_`>()` +* `BIPOP_CMAES<`_`SelectionPolicyType, TransformationPolicyType`_`>(`_`lambda, transformationPolicy`_`)` +* `BIPOP_CMAES<`_`SelectionPolicyType, TransformationPolicyType`_`>(`_`lambda, transformationPolicy, batchSize, maxIterations, tolerance`_`)` +* `BIPOP_CMAES<`_`SelectionPolicyType, TransformationPolicyType`_`>(`_`lambda, transformationPolicy, batchSize, maxIterations, tolerance, selectionPolicy, stepSize, maxRestarts, populationFactor, maxFunctionEvaluations`_`)` + +### Attributes + +| **type** | **name** | **description** | **default** | +|----------|----------|-----------------|-------------| +| `size_t` | **`lambda`** | The population size (0 uses a default size). | `0` | +| `TransformationPolicyType` | **`transformationPolicy`** | Instantiated transformation policy used to map the coordinates to the desired domain. | `TransformationPolicyType()` | +| `size_t` | **`batchSize`** | Batch size to use for the objective calculation. | `32` | +| `size_t` | **`maxIterations`** | Maximum number of iterations. | `1000` | +| `double` | **`tolerance`** | Maximum absolute tolerance to terminate the algorithm. | `1e-5` | +| `SelectionPolicyType` | **`selectionPolicy`** | Instantiated selection policy used to calculate the objective. | `SelectionPolicyType()` | +| `double` | **`stepSize`** | Initial step size. | `0` | +| `size_t` | **`maxRestarts`** | Maximum number of restarts. | `9` | +| `double` | **`populationFactor`** | The factor by which the population increases after each restart. | `2.0` | +| `size_t` | **`maxFunctionEvaluations`** | Maximum number of function evaluations. | `1e9` | + +The _`SelectionPolicyType`_ and _`TransformationPolicyType`_ template parameters +are used to control how the selection of points and the transformation of the +decision variables are handled during the optimization process. For more +information, check the [`CMAES`](#cma-es) class. The `lambda`, +`transformationPolicy`, `batchSize`, `maxIterations`, `tolerance`, +`selectionPolicy`, and `stepSize` are all parameters that affect the underlying +CMA-ES process. + +For convenience, the following type can be used: + + * **`BIPOP_CMAES<>`** (equivalent to `BIPOP_CMAES>`): uses all separable functions to compute objective + +#### Examples: + +
+Click to collapse/expand example code. + + +```c++ +RosenbrockFunction f; +arma::mat coordinates = f.GetInitialPoint(); + +// BIPOP CMA-ES +BoundaryBoxConstraint<> b(-1, 1); +BIPOP_CMAES optimizer(0, b, 32, 200, 1e-4, 5, 2, 9, 1e4); +Optimizer.Optimize(f, coordinates); +``` + +
+ +#### See also: + + * [CMA-ES](#cma-es) + * [Active CMA-ES](#active-cma-es) + * [IPOP CMA-ES](#ipop-cma-es) + * [Benchmarking a BI-Population CMA-ES on the BBOB-2009 Function Testbed](https://dl.acm.org/doi/pdf/10.1145/1570256.1570333) + * [Evolution strategy in Wikipedia](https://en.wikipedia.org/wiki/Evolution_strategy) + ## Coordinate Descent (CD) *An optimizer for [partially differentiable functions](#partially-differentiable-functions).* @@ -945,7 +1019,7 @@ cyclicscd.Optimize(f, coordinates); * [Stochastic Methods for L1-Regularized Loss Minimization](https://www.jmlr.org/papers/volume12/shalev-shwartz11a/shalev-shwartz11a.pdf) * [Partially differentiable functions](#partially-differentiable-functions) -## CMAES +## CMA-ES *An optimizer for [separable functions](#separable-functions).* @@ -1015,12 +1089,12 @@ the coordinates respectively. RosenbrockFunction f; arma::mat coordinates = f.GetInitialPoint(); -// CMAES with the FullSelection and BoundaryBoxConstraint policies. +// CMA-ES with the FullSelection and BoundaryBoxConstraint policies. BoundaryBoxConstraint b(-1, 1); CMAES optimizer(0, b, 32, 200, 1e-4); optimizer.Optimize(f, coordinates); -// CMAES with the RandomSelection and BoundaryBoxConstraint policies. +// CMA-ES with the RandomSelection and BoundaryBoxConstraint policies. ApproxCMAES> cmaes(0, b, 32, 200, 1e-4); approxOptimizer.Optimize(f, coordinates); ``` @@ -1029,6 +1103,9 @@ approxOptimizer.Optimize(f, coordinates); #### See also: + * [Active CMAES](#active-cma-es) + * [BIPOP CMA-ES](#bipop-cma-es) + * [IPOP CMA-ES](#ipop-cma-es) * [Completely Derandomized Self-Adaptation in Evolution Strategies](http://www.cmap.polytechnique.fr/~nikolaus.hansen/cmaartic.pdf) * [CMA-ES in Wikipedia](https://en.wikipedia.org/wiki/CMA-ES) * [Evolution strategy in Wikipedia](https://en.wikipedia.org/wiki/Evolution_strategy) @@ -1575,6 +1652,79 @@ optimizer.Optimize(f, coordinates); * [HOGWILD!: A Lock-Free Approach to Parallelizing Stochastic Gradient Descent](https://arxiv.org/abs/1106.5730) * [Sparse differentiable separable functions](#sparse-differentiable-separable-functions) +## IPOP CMA-ES + +*An optimizer for [separable functions](#separable-functions).* + +IPOP CMA-ES (Increasing Population Size CMA-ES) is an extension of the +Covariance Matrix Adaptation Evolution Strategy (CMA-ES). It introduces a +restart mechanism that progressively increases the population size. This +approach is beneficial for optimizing multi-modal functions, +characterized by numerous local optima. The restart mechanism is designed to +improve the adaptability of CMA-ES by improving the likelihood of escaping +local optima, thus increasing the chances of discovering the global optimum. + +### Constructors + +* `IPOP_CMAES<`_`SelectionPolicyType, TransformationPolicyType`_`>()` +* `IPOP_CMAES<`_`SelectionPolicyType, TransformationPolicyType`_`>(`_`lambda, transformationPolicy`_`)` +* `IPOP_CMAES<`_`SelectionPolicyType, TransformationPolicyType`_`>(`_`lambda, transformationPolicy, batchSize, maxIterations, tolerance`_`)` +* `IPOP_CMAES<`_`SelectionPolicyType, TransformationPolicyType`_`>(`_`lambda, transformationPolicy, batchSize, maxIterations, tolerance, selectionPolicy, stepSize, maxRestarts, populationFactor, maxFunctionEvaluations`_`)` + +### Attributes + +| **type** | **name** | **description** | **default** | +|----------|----------|-----------------|-------------| +| `size_t` | **`lambda`** | The population size (0 uses a default size). | `0` | +| `TransformationPolicyType` | **`transformationPolicy`** | Instantiated transformation policy used to map the coordinates to the desired domain. | `TransformationPolicyType()` | +| `size_t` | **`batchSize`** | Batch size to use for the objective calculation. | `32` | +| `size_t` | **`maxIterations`** | Maximum number of iterations. | `1000` | +| `double` | **`tolerance`** | Maximum absolute tolerance to terminate the algorithm. | `1e-5` | +| `SelectionPolicyType` | **`selectionPolicy`** | Instantiated selection policy used to calculate the objective. | `SelectionPolicyType()` | +| `double` | **`stepSize`** | Initial step size. | `0` | +| `size_t` | **`maxRestarts`** | Maximum number of restarts. | `9` | +| `double` | **`populationFactor`** | The factor by which the population increases after each restart. | `2.0` | +| `size_t` | **`maxFunctionEvaluations`** | Maximum number of function evaluations. | `1e9` | + +The _`SelectionPolicyType`_ and _`TransformationPolicyType`_ template parameters +are used to control how the selection of points and the transformation of the +decision variables are handled during the optimization process. For more +information, check the [`CMAES`](#cma-es) class. The `lambda`, +`transformationPolicy`, `batchSize`, `maxIterations`, `tolerance`, +`selectionPolicy`, and `stepSize` are all parameters that affect the underlying +CMA-ES process. + +For convenience, the following type can be used: + + * **`IPOP_CMAES<>`** (equivalent to `IPOP_CMAES>`): uses all separable functions to compute objective + +#### Examples: + +
+Click to collapse/expand example code. + + +```c++ +RosenbrockFunction f; +arma::mat coordinates = f.GetInitialPoint(); + +// IPOP CMA-ES +BoundaryBoxConstraint<> b(-1, 1); +IPOP_CMAES optimizer(0, b, 32, 200, 1e-4, 5, 2, 9, 1e4); +Optimizer.Optimize(f, coordinates); +``` + +
+ +#### See also: + + * [CMA-ES](#cmaes) + * [Active CMA-ES](#active-cma-es) + * [BIPOP CMA-ES](#bipop-cma-es) + * [A Restart CMA Evolution Strategy With Increasing Population Size](http://www.cmap.polytechnique.fr/~nikolaus.hansen/cec2005ipopcmaes.pdf) + * [Evolution strategy in Wikipedia](https://en.wikipedia.org/wiki/Evolution_strategy) + + ## IQN *An optimizer for [differentiable separable functions](#differentiable-separable-functions).* diff --git a/include/ensmallen.hpp b/include/ensmallen.hpp index a6ca139dd..b7b08c653 100644 --- a/include/ensmallen.hpp +++ b/include/ensmallen.hpp @@ -104,6 +104,7 @@ #include "ensmallen_bits/bigbatch_sgd/bigbatch_sgd.hpp" #include "ensmallen_bits/cmaes/cmaes.hpp" #include "ensmallen_bits/cmaes/active_cmaes.hpp" +#include "ensmallen_bits/cmaes/pop_cmaes.hpp" #include "ensmallen_bits/cd/cd.hpp" #include "ensmallen_bits/cne/cne.hpp" #include "ensmallen_bits/de/de.hpp" diff --git a/include/ensmallen_bits/cmaes/cmaes.hpp b/include/ensmallen_bits/cmaes/cmaes.hpp index 0ae323490..6fed17a98 100644 --- a/include/ensmallen_bits/cmaes/cmaes.hpp +++ b/include/ensmallen_bits/cmaes/cmaes.hpp @@ -172,6 +172,9 @@ class CMAES double& StepSize() { return stepSize; } + //! Get the total number of function evaluations. + size_t FunctionEvaluations() const { return functionEvaluations; } + private: //! Population size. size_t lambda; @@ -195,6 +198,9 @@ class CMAES //! The step size. double stepSize; + + //! Counter for the number of function evaluations. + size_t functionEvaluations = 0; }; /** diff --git a/include/ensmallen_bits/cmaes/cmaes_impl.hpp b/include/ensmallen_bits/cmaes/cmaes_impl.hpp index 73a5c7f02..4d2c18e3d 100644 --- a/include/ensmallen_bits/cmaes/cmaes_impl.hpp +++ b/include/ensmallen_bits/cmaes/cmaes_impl.hpp @@ -151,6 +151,7 @@ typename MatType::elem_type CMAES::max(); @@ -235,6 +236,8 @@ typename MatType::elem_type CMAES, + bool UseBIPOPFlag = true> +class POP_CMAES : public CMAES +{ + public: + /** + * Construct the POP-CMA-ES optimizer with the given parameters. + * Other than the same CMA-ES parameters, it also adds the maximum number of + * restarts, the increase in population factor, the maximum number of + * evaluations, as well as a flag indicating to use BIPOP or not. + * The suggested values are not necessarily good for the given problem, so it + * is suggested that the values used be tailored to the task at hand. The + * maximum number of iterations refers to the maximum number of points that + * are processed (i.e., one iteration equals one point; one iteration does not + * equal one pass over the dataset). + * + * @param lambda The initial population size (0 use the default size). + * @param transformationPolicy Instantiated transformation policy used to + * map the coordinates to the desired domain. + * @param batchSize Batch size to use for the objective calculation. + * @param maxIterations Maximum number of iterations allowed. + * @param tolerance Maximum absolute tolerance to terminate algorithm. + * @param selectionPolicy Instantiated selection policy used to calculate the + * objective. + * @param stepSize Starting sigma/step size (will be modified). + * @param populationFactor The factor by which population increases + * after each restart. + * @param maxRestarts Maximum number of restarts. + * @param maxFunctionEvaluations Maximum number of function evaluations. + */ + POP_CMAES(const size_t lambda = 0, + const TransformationPolicyType& transformationPolicy = + TransformationPolicyType(), + const size_t batchSize = 32, + const size_t maxIterations = 1000, + const double tolerance = 1e-5, + const SelectionPolicyType& selectionPolicy = SelectionPolicyType(), + double stepSize = 0, + const size_t maxRestarts = 9, + const double populationFactor = 2, + const size_t maxFunctionEvaluations = 1e9); + + /** + * Set POP-CMA-ES specific parameters. + */ + template + typename MatType::elem_type Optimize(SeparableFunctionType& function, + MatType& iterate, + CallbackTypes&&... callbacks); + + //! Get the population factor. + double PopulationFactor() const { return populationFactor; } + //! Modify the population factor. + double& PopulationFactor() { return populationFactor; } + + //! Get the maximum number of restarts. + size_t MaxRestarts() const { return maxRestarts; } + //! Modify the maximum number of restarts. + size_t& MaxRestarts() { return maxRestarts; } + + //! Get the maximum number of function evaluations. + size_t MaxFunctionEvaluations() const { return maxFunctionEvaluations; } + //! Modify the maximum number of function evaluations. + size_t& MaxFunctionEvaluations() { return maxFunctionEvaluations; } + + //! Get the BIPOP mode flag. + static constexpr bool UseBIPOP() { return UseBIPOPFlag; } + + private: + //! Population factor + double populationFactor; + + //! Maximum number of restarts. + size_t maxRestarts; + + //! Maximum number of function evaluations. + size_t maxFunctionEvaluations; + +}; + +// Define IPOP_CMAES and BIPOP_CMAES using the POP_CMAES template +template> +using IPOP_CMAES = POP_CMAES; + +template> +using BIPOP_CMAES = POP_CMAES; + +} // namespace ens + +// Include implementation. +#include "pop_cmaes_impl.hpp" + +#endif \ No newline at end of file diff --git a/include/ensmallen_bits/cmaes/pop_cmaes_impl.hpp b/include/ensmallen_bits/cmaes/pop_cmaes_impl.hpp new file mode 100644 index 000000000..38b801126 --- /dev/null +++ b/include/ensmallen_bits/cmaes/pop_cmaes_impl.hpp @@ -0,0 +1,163 @@ +/** + * @file ipop_cmaes_impl.hpp + * @author Marcus Edel + * @author Benjami Parellada + * + * Implementation of the IPOP Covariance Matrix Adaptation Evolution Strategy + * as proposed by A. Auger and N. Hansen in "A Restart CMA Evolution + * Strategy With Increasing Population Size" and BIPOP Covariance Matrix + * Adaptation Evolution Strategy as proposed by N. Hansen in "Benchmarking + * a BI-population CMA-ES on the BBOB-2009 function testbed". + * + * ensmallen is free software; you may redistribute it and/or modify it under + * the terms of the 3-clause BSD license. You should have received a copy of + * the 3-clause BSD license along with ensmallen. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef ENSMALLEN_CMAES_POP_CMAES_IMPL_HPP +#define ENSMALLEN_CMAES_POP_CMAES_IMPL_HPP + +#include "pop_cmaes.hpp" +#include + +namespace ens { + +template +POP_CMAES::POP_CMAES( + const size_t lambda, + const TransformationPolicyType& transformationPolicy, + const size_t batchSize, + const size_t maxIterations, + const double tolerance, + const SelectionPolicyType& selectionPolicy, + double stepSize, + const size_t maxRestarts, + const double populationFactor, + const size_t maxFunctionEvaluations) : + CMAES( + lambda, transformationPolicy, batchSize, maxIterations, + tolerance, selectionPolicy, stepSize), + populationFactor(populationFactor), + maxRestarts(maxRestarts), + maxFunctionEvaluations(maxFunctionEvaluations) +{ /* Nothing to do. */ } + +template +template +typename MatType::elem_type POP_CMAES::Optimize( + SeparableFunctionType& function, + MatType& iterateIn, + CallbackTypes&&... callbacks) +{ + // Convenience typedefs. + typedef typename MatType::elem_type ElemType; + + StoreBestCoordinates sbc; + StoreBestCoordinates overallSBC; + size_t totalFunctionEvaluations = 0; + size_t largePopulationBudget = 0; + size_t smallPopulationBudget = 0; + + // First single run with default population size + MatType iterate = iterateIn; + ElemType overallObjective = CMAES::Optimize(function, iterate, sbc, + callbacks...); + + overallSBC = sbc; + ElemType objective; + size_t evaluations; + + size_t defaultLambda = this->PopulationSize(); + size_t currentLargeLambda = defaultLambda; + + double stepSizeDefault = this->StepSize(); + + // Print out the default population size + Info << "Default population size: " << defaultLambda << "." << std::endl; + + size_t restart = 0; + + while (restart < maxRestarts) + { + if (!UseBIPOPFlag || largePopulationBudget <= smallPopulationBudget || + restart == 0 || restart == maxRestarts - 1) + { + // Large population regime (IPOP or BIPOP) + currentLargeLambda *= populationFactor; + this->PopulationSize() = currentLargeLambda; + this->StepSize() = stepSizeDefault; + + Info << "POP-CMA-ES: restart " << restart << ", large population size" << + " (lambda): " << this->PopulationSize() << "." << std::endl; + + iterate = iterateIn; + + // Optimize using the CMAES object. + objective = CMAES::Optimize(function, iterate, sbc, + callbacks...); + + evaluations = this->FunctionEvaluations(); + largePopulationBudget += evaluations; + } + else if (UseBIPOPFlag) + { + // Small population regime (BIPOP only) + double u = arma::randu(); + size_t smallLambda = static_cast(defaultLambda * std::pow(0.5 * + currentLargeLambda / defaultLambda, u * u)); + double stepSizeSmall = 2 * std::pow(10, -2 * arma::randu()); + + this->PopulationSize() = smallLambda; + this->StepSize() = stepSizeSmall; + + Info << "BIPOP-CMA-ES: restart " << restart << ", small population" << + " size (lambda): " << this->PopulationSize() << "." << std::endl; + + iterate = iterateIn; + + // Optimize using the CMAES object. + objective = CMAES::Optimize(function, iterate, sbc, + callbacks...); + + evaluations = this->FunctionEvaluations(); + smallPopulationBudget += evaluations; + } + + if (objective < overallObjective) + { + overallObjective = objective; + overallSBC = sbc; + Info << "POP-CMA-ES: New best objective: " << overallObjective + << "." << std::endl; + } + + totalFunctionEvaluations += evaluations; + // Check if the total number of evaluations has exceeded the limit + if (totalFunctionEvaluations >= maxFunctionEvaluations) { + Warn << "POP-CMA-ES: Maximum function overall evaluations reached. " + << "terminating optimization." << std::endl; + + Callback::EndOptimization(*this, function, iterate, callbacks...); + iterateIn = std::move(overallSBC.BestCoordinates()); + return overallSBC.BestObjective(); + } + + ++restart; + } + + Callback::EndOptimization(*this, function, iterate, callbacks...); + iterateIn = std::move(overallSBC.BestCoordinates()); + return overallSBC.BestObjective(); +} + +} // namespace ens + +#endif \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 515247ee0..99d38c56e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -36,6 +36,7 @@ set(ENSMALLEN_TESTS_SOURCES nesterov_momentum_sgd_test.cpp nsga2_test.cpp parallel_sgd_test.cpp + pop_cmaes_test.cpp proximal_test.cpp pso_test.cpp quasi_hyperbolic_momentum_sgd_test.cpp diff --git a/tests/pop_cmaes_test.cpp b/tests/pop_cmaes_test.cpp new file mode 100644 index 000000000..46e1d427f --- /dev/null +++ b/tests/pop_cmaes_test.cpp @@ -0,0 +1,144 @@ +/** + * @file pop_cmaes_test.cpp + * @author Benjami Parellada + * + * Tests for the POP_CMAES class, including IPOP CMA-ES and BIPOP CMA-ES. + * + * ensmallen is free software; you may redistribute it and/or modify it under + * the terms of the 3-clause BSD license. You should have received a copy of + * the 3-clause BSD license along with ensmallen. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include +#include "catch.hpp" +#include "test_function_tools.hpp" +#include + +using namespace ens; +using namespace ens::test; + +/** + * Run IPOP-CMA-ES on the Rastrigin function and check whether the optimizer + * converges to the expected solution within tolerance limits. + */ +TEST_CASE("IPOPCMAESRastriginFunctionTest", "[POPCMAESTest]") +{ + RastriginFunction f(2); + BoundaryBoxConstraint<> b(-10, 10); + + IPOP_CMAES> ipopcmaes( + 15, // lambda + b, // transformationPolicy + 32, // batchSize + 10000, // maxIterations + 1e-8, // tolerance + FullSelection(), // selectionPolicy + 3.72, // stepSize + 2.0, // populationFactor + 7, // maxRestarts + 1e6 // maxFunctionEvaluations + ); + + arma::mat initialPoint = f.GetInitialPoint(); + arma::mat expectedResult = f.GetFinalPoint(); + + MultipleTrialOptimizerTest(f, ipopcmaes, initialPoint, expectedResult, 0.5, f.GetFinalObjective(), 0.5, 5); +} + +/** + * Run BIPOP-CMA-ES on the Rastrigin function and check whether the optimizer + * converges to the expected solution within tolerance limits. + */ +TEST_CASE("BIPOPCMAESRastriginFunctionTest", "[POPCMAESTest]") +{ + RastriginFunction f(2); + BoundaryBoxConstraint<> b(-10, 10); + + IPOP_CMAES> ipopcmaes( + 15, // lambda + b, // transformationPolicy + 32, // batchSize + 10000, // maxIterations + 1e-8, // tolerance + FullSelection(), // selectionPolicy + 3.72, // stepSize + 2.0, // populationFactor + 7, // maxRestarts + 1e6 // maxFunctionEvaluations + ); + + arma::mat initialPoint = f.GetInitialPoint(); + arma::mat expectedResult = f.GetFinalPoint(); + + MultipleTrialOptimizerTest(f, ipopcmaes, initialPoint, expectedResult, 0.5, f.GetFinalObjective(), 0.5, 5); +} + +/** + * Run IPOP-CMA-ES on the Rosenbrock function and check whether the optimizer + * converges to the expected solution within tolerance limits. + */ +TEST_CASE("IPOPCMAESRosenbrockFunctionTest", "[POPCMAESTest]") +{ + BoundaryBoxConstraint<> b(0, 2); + + BIPOP_CMAES> bipopcmaes( + 15, // lambda + b, // transformationPolicy + 32, // batchSize + 10000, // maxIterations + 1e-8, // tolerance + FullSelection(), // selectionPolicy + 0.25, // stepSize + 1.5, // populationFactor + 7, // maxRestarts + 1e6 // maxFunctionEvaluations + ); + + FunctionTest(bipopcmaes, 0.5, 0.5); +} + +/** + * Run BIPOP-CMA-ES on the Rosenbrock function and check whether the optimizer + * converges to the expected solution within tolerance limits. + */ +TEST_CASE("BIPOPCMAESRosenbrockFunctionTest", "[POPCMAESTest]") +{ + BoundaryBoxConstraint<> b(0, 2); + + BIPOP_CMAES> bipopcmaes( + 15, // lambda + b, // transformationPolicy + 32, // batchSize + 10000, // maxIterations + 1e-8, // tolerance + FullSelection(), // selectionPolicy + 0.25, // stepSize + 1.5, // populationFactor + 7, // maxRestarts + 1e6 // maxFunctionEvaluations + ); + + FunctionTest(bipopcmaes, 0.5, 0.5); +} + +/** + * Run IPOP-CMA-ES with the full selection policy on logistic regression and + * make sure the results are acceptable. + */ +TEST_CASE("IPOPCMAESLogisticRegressionTest", "[POPCMAESTest]") +{ + BoundaryBoxConstraint<> b(-10, 10); + IPOP_CMAES> cmaes(0, b, 32, 1000, 1e-3, FullSelection(), 0.6, 2.0, 7, 1e7); + LogisticRegressionFunctionTest(cmaes, 0.003, 0.006, 5); +} + +/** + * Run BIPOP-CMA-ES with the random selection policy on logistic regression and + * make sure the results are acceptable. + */ +TEST_CASE("BIPOPCMAESLogisticRegressionTest", "[POPCMAESTest]") +{ + BoundaryBoxConstraint<> b(-10, 10); + BIPOP_CMAES> cmaes(0, b, 32, 1000, 1e-3, FullSelection(), 0.6, 2.0, 7, 1e7); + LogisticRegressionFunctionTest(cmaes, 0.003, 0.006, 5); +}