diff --git a/ChangeLog b/ChangeLog index 9b71137..5e58ac5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2024-11-29 James Balamuta + + * inst/include/ensmallen_bits/agemoea/agemoea.hpp: removed unused private field variable + * inst/include/problems/dtlz/dtlz2_function.hpp: removed unused variable. + * inst/include/problems/dtlz/dtlz4_function.hpp: ditto + * inst/include/problems/dtlz/dtlz5_function.hpp: ditto + * inst/include/problems/dtlz/dtlz6_function.hpp: ditto + * inst/include/problems/dtlz/dtlz7_function.hpp: ditto + * inst/include/problems/maf/maf5_function.hpp: ditto + * inst/include/problems/maf/maf6_function.hpp: ditto + + * DESCRIPTION (Version): Release 2.22.0 + * NEWS.md: Update for Ensmallen release 2.22.0 + * inst/include/ensmallen_bits: Upgraded to Ensmallen 2.22.0 + * inst/include/ensmallen.hpp: ditto + 2024-02-16 James Balamuta * DESCRIPTION (Version): Release 2.21.1 diff --git a/DESCRIPTION b/DESCRIPTION index ae6694c..0f6c938 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: RcppEnsmallen Title: Header-Only C++ Mathematical Optimization Library for 'Armadillo' -Version: 0.2.21.1.1 +Version: 0.2.22.0.1 Authors@R: c( person("James Joseph", "Balamuta", email = "balamut2@illinois.edu", role = c("aut", "cre", "cph"), diff --git a/NEWS.md b/NEWS.md index 0be574d..279373f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,17 @@ +# RcppEnsmallen 0.2.22.0.1 + +- Upgraded to ensmallen 2.22.0: "E-Bike Excitement" (2024-11-29) + - Update to C++14 standard + ([#400](https://github.com/mlpack/ensmallen/pull/400)). + - Bump minimum Armadillo version to 10.8 + ([#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)). + - Added IPOP and BIPOP restart mechanisms for CMA-ES. + ([#403](https://github.com/mlpack/ensmallen/pull/403)). +- Addressed uninitialized variable and private field warnings + ([#65](https://github.com/coatless-rpkg/rcppensmallen/pull/65)) + # RcppEnsmallen 0.2.21.1.1 - Upgraded to ensmallen 2.21.1: "Bent Antenna" (2024-02-16) diff --git a/inst/include/ensmallen.hpp b/inst/include/ensmallen.hpp index bacc210..b7b08c6 100644 --- a/inst/include/ensmallen.hpp +++ b/inst/include/ensmallen.hpp @@ -15,22 +15,27 @@ #ifndef ENSMALLEN_HPP #define ENSMALLEN_HPP -// certain compilers are way behind the curve -#if (defined(_MSVC_LANG) && (_MSVC_LANG >= 201402L)) - #undef ARMA_USE_CXX11 - #define ARMA_USE_CXX11 +#undef ENS_HAVE_CXX14 + +#if (__cplusplus >= 201402L) + #define ENS_HAVE_CXX14 #endif -#include +#if defined(_MSVC_LANG) + #if (_MSVC_LANG >= 201402L) + #undef ENS_HAVE_CXX14 + #define ENS_HAVE_CXX14 + #endif +#endif -#if !defined(ARMA_USE_CXX11) - // armadillo automatically enables ARMA_USE_CXX11 - // when a C++11/C++14/C++17/etc compiler is detected - #error "please enable C++11/C++14 mode in your compiler" +#if !defined(ENS_HAVE_CXX14) + #error "*** C++14 compiler required; enable C++14 mode in your compiler, or use an earlier version of ensmallen" #endif -#if ((ARMA_VERSION_MAJOR < 9) || ((ARMA_VERSION_MAJOR == 9) && (ARMA_VERSION_MINOR < 800))) - #error "need Armadillo version 9.800 or later" +#include + +#if ((ARMA_VERSION_MAJOR < 10) || ((ARMA_VERSION_MAJOR == 10) && (ARMA_VERSION_MINOR < 8))) + #error "need Armadillo version 10.8 or newer" #endif #include @@ -66,6 +71,7 @@ #include "ensmallen_bits/utility/any.hpp" #include "ensmallen_bits/utility/arma_traits.hpp" #include "ensmallen_bits/utility/indicators/epsilon.hpp" +#include "ensmallen_bits/utility/indicators/igd.hpp" #include "ensmallen_bits/utility/indicators/igd_plus.hpp" // Contains traits, must be placed before report callback. @@ -98,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" @@ -111,6 +118,7 @@ #include "ensmallen_bits/katyusha/katyusha.hpp" #include "ensmallen_bits/lbfgs/lbfgs.hpp" #include "ensmallen_bits/lookahead/lookahead.hpp" +#include "ensmallen_bits/agemoea/agemoea.hpp" #include "ensmallen_bits/moead/moead.hpp" #include "ensmallen_bits/nsga2/nsga2.hpp" #include "ensmallen_bits/padam/padam.hpp" diff --git a/inst/include/ensmallen_bits/agemoea/agemoea.hpp b/inst/include/ensmallen_bits/agemoea/agemoea.hpp new file mode 100644 index 0000000..0748678 --- /dev/null +++ b/inst/include/ensmallen_bits/agemoea/agemoea.hpp @@ -0,0 +1,490 @@ +/** + * @file agemoea.hpp + * @author Satyam Shukla + * + * AGE-MOEA is a multi-objective optimization algorithm, widely used in + * many real-world applications. AGE-MOEA generates offsprings using + * crossover and mutation and then selects the next generation according + * to non-dominated-sorting and survival score comparison. + * + * 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_AGEMOEA_AGEMOEA_HPP +#define ENSMALLEN_AGEMOEA_AGEMOEA_HPP + +namespace ens { + +/** + * This class implements the AGEMOEA algorithm. + * + * The algorithm works by generating a candidate population from a fixed + * starting point. At each stage of optimization, a new population of children + * is generated. This new population along with its predecessor is sorted using + * non-domination as the metric. Following this, the population is further + * segregated in fronts. A new population is generated from these fronts having + * size equal to that of the starting population. + * + * During evolution, two parents are randomly chosen using binary tournament + * selection. A pair of children are generated by crossing over these two + * candidates followed by mutation. + * + * The best front (Pareto optimal) is returned by the Optimize() method. + * + * For more information, see the following: + * + * @code + * @inproceedings{panichella2019adaptive, + * title={An adaptive evolutionary algorithm based on non-euclidean geometry for many-objective optimization}, + * author={Panichella, Annibale}, + * booktitle={Proceedings of the genetic and evolutionary computation conference}, + * pages={595--603}, + * year={2019} + * } + * @endcode + * + */ +class AGEMOEA +{ + public: + /** + * Constructor for the AGE-MOEA optimizer. + * + * The default values provided over here are not necessarily suitable for a + * given function. Therefore it is highly recommended to adjust the + * parameters according to the problem. + * + * @param populationSize The number of candidates in the population. + * This should be atleast 4 in size and a multiple of 4. + * @param maxGenerations The maximum number of generations allowed for NSGA-II. + * @param crossoverProb The probability that a crossover will occur. + * @param distributionIndex The crowding degree of the mutation. + * @param epsilon The minimum difference required to distinguish between + * candidate solutions. + * @param eta The distance parameters of the crossover distribution. + * @param lowerBound Lower bound of the coordinates of the initial population. + * @param upperBound Upper bound of the coordinates of the initial population. + */ + AGEMOEA(const size_t populationSize = 100, + const size_t maxGenerations = 2000, + const double crossoverProb = 0.6, + const double distributionIndex = 20, + const double epsilon = 1e-6, + const double eta = 20, + const arma::vec& lowerBound = arma::zeros(1, 1), + const arma::vec& upperBound = arma::ones(1, 1)); + + /** + * Constructor for the AGE-MOEA optimizer. This constructor provides an overload + * to use `lowerBound` and `upperBound` of type double. + * + * The default values provided over here are not necessarily suitable for a + * given function. Therefore it is highly recommended to adjust the + * parameters according to the problem. + * + * @param populationSize The number of candidates in the population. + * This should be atleast 4 in size and a multiple of 4. + * @param maxGenerations The maximum number of generations allowed for NSGA-II. + * @param crossoverProb The probability that a crossover will occur. + * @param distributionIndex The crowding degree of the mutation. + * @param epsilon The minimum difference required to distinguish between + * candidate solutions. + * @param eta The distance parameters of the crossover distribution + * @param lowerBound Lower bound of the coordinates of the initial population. + * @param upperBound Upper bound of the coordinates of the initial population. + */ + AGEMOEA(const size_t populationSize = 100, + const size_t maxGenerations = 2000, + const double crossoverProb = 0.6, + const double distributionIndex = 20, + const double epsilon = 1e-6, + const double eta = 20, + const double lowerBound = 0, + const double upperBound = 1); + + /** + * Optimize a set of objectives. The initial population is generated using the + * starting point. The output is the best generated front. + * + * @tparam ArbitraryFunctionType std::tuple of multiple objectives. + * @tparam MatType Type of matrix to optimize. + * @tparam CallbackTypes Types of callback functions. + * @param objectives Vector of objective functions to optimize for. + * @param iterate Starting point. + * @param callbacks Callback functions. + * @return MatType::elem_type The minimum of the accumulated sum over the + * objective values in the best front. + */ + template + typename MatType::elem_type Optimize( + std::tuple& objectives, + MatType& iterate, + CallbackTypes&&... callbacks); + + //! Get the population size. + size_t PopulationSize() const { return populationSize; } + //! Modify the population size. + size_t& PopulationSize() { return populationSize; } + + //! Get the maximum number of generations. + size_t MaxGenerations() const { return maxGenerations; } + //! Modify the maximum number of generations. + size_t& MaxGenerations() { return maxGenerations; } + + //! Get the crossover rate. + double CrossoverRate() const { return crossoverProb; } + //! Modify the crossover rate. + double& CrossoverRate() { return crossoverProb; } + + //! Retrieve value of the distribution index. + double DistributionIndex() const { return distributionIndex; } + //! Modify the value of the distribution index. + double& DistributionIndex() { return distributionIndex; } + + //! Retrieve value of eta. + double Eta() const { return eta; } + //! Modify the value of eta. + double& Eta() { return eta; } + + //! Get the tolerance. + double Epsilon() const { return epsilon; } + //! Modify the tolerance. + double& Epsilon() { return epsilon; } + + //! Retrieve value of lowerBound. + const arma::vec& LowerBound() const { return lowerBound; } + //! Modify value of lowerBound. + arma::vec& LowerBound() { return lowerBound; } + + //! Retrieve value of upperBound. + const arma::vec& UpperBound() const { return upperBound; } + //! Modify value of upperBound. + arma::vec& UpperBound() { return upperBound; } + + //! Retrieve the Pareto optimal points in variable space. This returns an empty cube + //! until `Optimize()` has been called. + const arma::cube& ParetoSet() const { return paretoSet; } + + //! Retrieve the best front (the Pareto frontier). This returns an empty cube until + //! `Optimize()` has been called. + const arma::cube& ParetoFront() const { return paretoFront; } + + /** + * Retrieve the best front (the Pareto frontier). This returns an empty + * vector until `Optimize()` has been called. Note that this function is + * deprecated and will be removed in ensmallen 3.x! Use `ParetoFront()` + * instead. + */ + const std::vector& Front() + { + if (rcFront.size() == 0) + { + // Match the old return format. + for (size_t i = 0; i < paretoFront.n_slices; ++i) + { + rcFront.push_back(arma::mat(paretoFront.slice(i))); + } + } + + return rcFront; + } + + private: + /** + * Evaluate objectives for the elite population. + * + * @tparam ArbitraryFunctionType std::tuple of multiple function types. + * @tparam MatType Type of matrix to optimize. + * @param population The elite population. + * @param objectives The set of objectives. + * @param calculatedObjectives Vector to store calculated objectives. + */ + template + typename std::enable_if::type + EvaluateObjectives(std::vector&, + std::tuple&, + std::vector >&); + + template + typename std::enable_if::type + EvaluateObjectives(std::vector& population, + std::tuple& objectives, + std::vector >& + calculatedObjectives); + + /** + * Reproduce candidates from the elite population to generate a new + * population. + * + * @tparam MatType Type of matrix to optimize. + * @param objectives The set of objectives. + * @param lowerBound Lower bound of the coordinates of the initial population. + * @param upperBound Upper bound of the coordinates of the initial population. + */ + template + void BinaryTournamentSelection(std::vector& population, + const MatType& lowerBound, + const MatType& upperBound); + + /** + * Crossover two parents to create a pair of new children. + * + * @tparam MatType Type of matrix to optimize. + * @param childA A newly generated candidate. + * @param childB Another newly generated candidate. + * @param parentA First parent from elite population. + * @param parentB Second parent from elite population. + * @param lowerBound The lower bound of the objectives. + * @param upperBound The upper bound of the objectives. + */ + template + void Crossover(MatType& childA, + MatType& childB, + const MatType& parentA, + const MatType& parentB, + const MatType& lowerBound, + const MatType& upperBound); + + /** + * Mutate the coordinates for a candidate. + * + * @tparam MatType Type of matrix to optimize. + * @param candidate The candidate whose coordinates are being modified. + * @param mutationRate The probablity of a mutation to occur. + * @param lowerBound Lower bound of the coordinates of the initial population. + * @param upperBound Upper bound of the coordinates of the initial population. + */ + template + void Mutate(MatType& candidate, + double mutationRate, + const MatType& lowerBound, + const MatType& upperBound); + + /** + * Sort the candidate population using their domination count and the set of + * dominated nodes. + * + * @tparam MatType Type of matrix to optimize. + * @param fronts The population is sorted into these Pareto fronts. The first + * front is the best, the second worse and so on. + * @param ranks The assigned ranks, used for crowding distance based sorting. + * @param calculatedObjectives The previously calculated objectives. + */ + template + void FastNonDominatedSort( + std::vector >& fronts, + std::vector& ranks, + std::vector >& calculatedObjectives); + + /** + * Operator to check if one candidate Pareto-dominates the other. + * + * A candidate is said to dominate the other if it is at least as good as the + * other candidate for all the objectives and there exists at least one + * objective for which it is strictly better than the other candidate. + * + * @tparam MatType Type of matrix to optimize. + * @param calculatedObjectives The previously calculated objectives. + * @param candidateP The candidate being compared from the elite population. + * @param candidateQ The candidate being compared against. + * @return true if candidateP Pareto dominates candidateQ, otherwise, false. + */ + template + bool Dominates( + std::vector>& calculatedObjectives, + size_t candidateP, + size_t candidateQ); + + /** + * Assigns Survival Score metric for sorting. + * + * @param front The previously generated Pareto fronts. + * @param idealPoint The ideal point of teh first front. + * @param calculatedObjectives The previously calculated objectives. + * @param survivalScore The Survival Score vector to be updated for each individual in the population. + * @param normalize The normlization vector of the fronts. + * @param dimension The dimension of the first front. + * @param fNum teh current front index. + */ + template + void SurvivalScoreAssignment( + const std::vector& front, + const arma::Col& idealPoint, + std::vector>& calculatedObjectives, + std::vector& survivalScore, + arma::Col& normalize, + double& dimension, + size_t fNum); + + /** + * The operator used in the AGE-MOEA survival score based sorting. + * + * If a candidate has a lower rank then it is preferred. + * Otherwise, if the ranks are equal then the candidate with the larger + * Survival Score is preferred. + * + * @param idxP The index of the first cadidate from the elite population being + * sorted. + * @param idxQ The index of the second cadidate from the elite population + * being sorted. + * @param ranks The previously calculated ranks. + * @param survivalScore The Survival score for each individual in + * the population. + * @return true if the first candidate is preferred, otherwise, false. + */ + template + bool SurvivalScoreOperator( + size_t idxP, + size_t idxQ, + const std::vector& ranks, + const std::vector& survivalScore); + + /** + * Normalizes the front given the extreme points in the current front. + * + * @tparam The type of population datapoints. + * @param calculatedObjectives The current population evaluated objectives. + * @param normalization The normalizing vector. + * @param front The previously generated Pareto front. + * @param extreme The indexes of the extreme points in the front. + */ + template + void NormalizeFront( + std::vector>& calculatedObjectives, + arma::Col& normalization, + const std::vector& front, + const arma::Row& extreme); + + /** + * Get the geometry information p of Lp norm (p > 0). + * + * @param calculatedObjectives The current population evaluated objectives. + * @param front The previously generated Pareto fronts. + * @param extreme The indexes of the extreme points in the front. + * @return The variable p in the Lp norm that best fits the geometry of the current front. + */ + template + double GetGeometry( + std::vector >& calculatedObjectives, + const std::vector& front, + const arma::Row& extreme); + + /** + * Finds the pairwise Lp distance between all the points in the front. + * + * @param final The current population evaluated objectives. + * @param calculatedObjectives The current population evaluated objectives. + * @param front The front of the current generation. + * @param dimension The calculated dimension of the front. + */ + template + void PairwiseDistance( + MatType& final, + std::vector >& calculatedObjectives, + const std::vector& front, + double dimension); + + /** + * Finding the indexes of the extreme points in the front. + * + * @param indexes vector containing the slected indexes. + * @param calculatedObjectives The current population objectives. + * @param front The front of the current generation. + */ + template + void FindExtremePoints( + arma::Row& indexes, + std::vector >& calculatedObjectives, + const std::vector& front); + + /** + * Finding the distance of each point in the front from the line formed + * by pointA and pointB. + * + * @param distance The vector containing the distances of the points in the fron from the line. + * @param calculatedObjectives Reference to the current population evaluated Objectives. + * @param front The front of the current generation(indices of population). + * @param pointA The first point on the line. + * @param pointB The second point on the line. + */ + template + void PointToLineDistance( + arma::Row& distances, + std::vector >& calculatedObjectives, + const std::vector& front, + const arma::Col& pointA, + const arma::Col& pointB); + + /** + * Find the Diversity score corresponding the solution S using the selected set. + * + * @param selected The current selected set. + * @param pairwiseDistance The current pairwise distance for the whole front. + * @param S The relative index of S being considered within the front. + * @return The diversity score for S which the sum of the two smallest elements. + */ + template + typename MatType::elem_type DiversityScore(std::set& selected, + const MatType& pairwiseDistance, + size_t S); + + //! The number of objectives being optimised for. + size_t numObjectives; + + //! The numbeer of variables used per objectives. + size_t numVariables; + + //! The number of candidates in the population. + size_t populationSize; + + //! Maximum number of generations before termination criteria is met. + size_t maxGenerations; + + //! Probability that crossover will occur. + double crossoverProb; + + //! The crowding degree of the mutation. Higher value produces a mutant + //! resembling its parent. + double distributionIndex; + + //! The tolerance for termination. + double epsilon; + + //! The distance parameters of the crossover distribution. + double eta; + + //! Lower bound of the initial swarm. + arma::vec lowerBound; + + //! Upper bound of the initial swarm. + arma::vec upperBound; + + //! The set of all the Pareto optimal points. + //! Stored after Optimize() is called. + arma::cube paretoSet; + + //! The set of all the Pareto optimal objective vectors. + //! Stored after Optimize() is called. + arma::cube paretoFront; + + //! A different representation of the Pareto front, for reverse compatibility + //! purposes. This can be removed when ensmallen 3.x is released! (Along + //! with `Front()`.) This is only populated when `Front()` is called. + std::vector rcFront; +}; + +} // namespace ens + +// Include implementation. +#include "agemoea_impl.hpp" + +#endif diff --git a/inst/include/ensmallen_bits/agemoea/agemoea_impl.hpp b/inst/include/ensmallen_bits/agemoea/agemoea_impl.hpp new file mode 100644 index 0000000..c226095 --- /dev/null +++ b/inst/include/ensmallen_bits/agemoea/agemoea_impl.hpp @@ -0,0 +1,819 @@ +/** + * @file agemoea_impl.hpp + * @author Satyam Shukla + * + * Implementation of the AGEMOEA algorithm. Used for multi-objective + * optimization problems on arbitrary functions. + * + * 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_AGEMOEA_AGEMOEA_IMPL_HPP +#define ENSMALLEN_AGEMOEA_AGEMOEA_IMPL_HPP + +#include "agemoea.hpp" +#include + +namespace ens { + +inline AGEMOEA::AGEMOEA(const size_t populationSize, + const size_t maxGenerations, + const double crossoverProb, + const double distributionIndex, + const double epsilon, + const double eta, + const arma::vec& lowerBound, + const arma::vec& upperBound) : + numObjectives(0), + numVariables(0), + populationSize(populationSize), + maxGenerations(maxGenerations), + crossoverProb(crossoverProb), + distributionIndex(distributionIndex), + epsilon(epsilon), + eta(eta), + lowerBound(lowerBound), + upperBound(upperBound) +{ /* Nothing to do here. */ } + +inline AGEMOEA::AGEMOEA(const size_t populationSize, + const size_t maxGenerations, + const double crossoverProb, + const double distributionIndex, + const double epsilon, + const double eta, + const double lowerBound, + const double upperBound) : + numObjectives(0), + numVariables(0), + populationSize(populationSize), + maxGenerations(maxGenerations), + crossoverProb(crossoverProb), + distributionIndex(distributionIndex), + epsilon(epsilon), + eta(eta), + lowerBound(lowerBound * arma::ones(1, 1)), + upperBound(upperBound * arma::ones(1, 1)) +{ /* Nothing to do here. */ } + +//! Optimize the function. +template +typename MatType::elem_type AGEMOEA::Optimize( + std::tuple& objectives, + MatType& iterateIn, + CallbackTypes&&... callbacks) +{ + // Make sure for evolution to work at least four candidates are present. + if (populationSize < 4 && populationSize % 4 != 0) + { + throw std::logic_error("AGEMOEA::Optimize(): population size should be at" + " least 4, and, a multiple of 4!"); + } + + // Convenience typedefs. + typedef typename MatType::elem_type ElemType; + typedef typename MatTypeTraits::BaseMatType BaseMatType; + + BaseMatType& iterate = (BaseMatType&) iterateIn; + + // Make sure that we have the methods that we need. Long name... + traits::CheckArbitraryFunctionTypeAPI(); + RequireDenseFloatingPointType(); + + // Check if lower bound is a vector of a single dimension. + if (lowerBound.n_rows == 1) + lowerBound = lowerBound(0, 0) * arma::ones(iterate.n_rows, iterate.n_cols); + + // Check if upper bound is a vector of a single dimension. + if (upperBound.n_rows == 1) + upperBound = upperBound(0, 0) * arma::ones(iterate.n_rows, iterate.n_cols); + + // Check the dimensions of lowerBound and upperBound. + assert(lowerBound.n_rows == iterate.n_rows && "The dimensions of " + "lowerBound are not the same as the dimensions of iterate."); + assert(upperBound.n_rows == iterate.n_rows && "The dimensions of " + "upperBound are not the same as the dimensions of iterate."); + + numObjectives = sizeof...(ArbitraryFunctionType); + numVariables = iterate.n_rows; + + // Cache calculated objectives. + std::vector > calculatedObjectives(populationSize); + + // Population size reserved to 2 * populationSize + 1 to accommodate + // for the size of intermediate candidate population. + std::vector population; + population.reserve(2 * populationSize + 1); + + // Pareto fronts, initialized during non-dominated sorting. + // Stores indices of population belonging to a certain front. + std::vector > fronts; + // Initialised in SurvivalScoreAssignment. + std::vector survivalScore; + // Initialised during non-dominated sorting. + std::vector ranks; + + //! Useful temporaries for float-like comparisons. + const BaseMatType castedLowerBound = arma::conv_to::from(lowerBound); + const BaseMatType castedUpperBound = arma::conv_to::from(upperBound); + + // Controls early termination of the optimization process. + bool terminate = false; + + // Generate the population based on a uniform distribution around the given + // starting point. + for (size_t i = 0; i < populationSize; i++) + { + population.push_back(arma::randu(iterate.n_rows, + iterate.n_cols) - 0.5 + iterate); + + // Constrain all genes to be within bounds. + population[i] = arma::min(arma::max(population[i], castedLowerBound), + castedUpperBound); + } + + Info << "AGEMOEA initialized successfully. Optimization started." << std::endl; + + // Iterate until maximum number of generations is obtained. + Callback::BeginOptimization(*this, objectives, iterate, callbacks...); + + for (size_t generation = 1; generation <= maxGenerations && !terminate; generation++) + { + // Create new population of candidate from the present elite population. + // Have P_t, generate G_t using P_t. + BinaryTournamentSelection(population, castedLowerBound, castedUpperBound); + + // Evaluate the objectives for the new population. + calculatedObjectives.resize(population.size()); + std::fill(calculatedObjectives.begin(), calculatedObjectives.end(), + arma::Col(numObjectives, arma::fill::zeros)); + EvaluateObjectives(population, objectives, calculatedObjectives); + + // Perform fast non dominated sort on P_t ∪ G_t. + ranks.resize(population.size()); + FastNonDominatedSort(fronts, ranks, calculatedObjectives); + + arma::Col idealPoint(calculatedObjectives[fronts[0][0]]); + for (size_t index = 1; index < fronts[0].size(); index++) + { + idealPoint = arma::min(idealPoint, + calculatedObjectives[fronts[0][index]]); + } + + // Perform survival score assignment. + survivalScore.resize(population.size()); + std::fill(survivalScore.begin(), survivalScore.end(), 0.); + double dimension; + arma::Col normalize(numObjectives, + arma::fill::zeros); + for (size_t fNum = 0; fNum < fronts.size(); fNum++) + { + SurvivalScoreAssignment(fronts[fNum], idealPoint, + calculatedObjectives, survivalScore, normalize, dimension, fNum); + } + + // Sort based on survival score. + std::sort(population.begin(), population.end(), + [this, ranks, survivalScore, population] + (BaseMatType candidateP, BaseMatType candidateQ) + { + size_t idxP{}, idxQ{}; + for (size_t i = 0; i < population.size(); i++) + { + if (arma::approx_equal(population[i], candidateP, + "absdiff", epsilon)) + idxP = i; + + if (arma::approx_equal(population[i], candidateQ, + "absdiff", epsilon)) + idxQ = i; + } + + return SurvivalScoreOperator(idxP, idxQ, ranks, + survivalScore); + } + ); + + // Yield a new population P_{t+1} of size populationSize. + // Discards unfit population from the R_{t} to yield P_{t+1}. + population.resize(populationSize); + + terminate |= Callback::GenerationalStepTaken(*this, objectives, iterate, + calculatedObjectives, fronts, callbacks...); + } + EvaluateObjectives(population, objectives, calculatedObjectives); + // Set the candidates from the Pareto Set as the output. + paretoSet.set_size(population[0].n_rows, population[0].n_cols, + population.size()); + // The Pareto Set is stored, can be obtained via ParetoSet() getter. + for (size_t solutionIdx = 0; solutionIdx < population.size(); ++solutionIdx) + { + paretoSet.slice(solutionIdx) = + arma::conv_to::from(population[solutionIdx]); + } + + // Set the candidates from the Pareto Front as the output. + paretoFront.set_size(calculatedObjectives[0].n_rows, + calculatedObjectives[0].n_cols, population.size()); + // The Pareto Front is stored, can be obtained via ParetoFront() getter. + for (size_t solutionIdx = 0; solutionIdx < population.size(); ++solutionIdx) + { + paretoFront.slice(solutionIdx) = + arma::conv_to::from(calculatedObjectives[solutionIdx]); + } + + // Clear rcFront, in case it is later requested by the user for reverse + // compatibility reasons. + rcFront.clear(); + + // Assign iterate to first element of the Pareto Set. + iterate = population[fronts[0][0]]; + + Callback::EndOptimization(*this, objectives, iterate, callbacks...); + + ElemType performance = std::numeric_limits::max(); + + for (const arma::Col& objective: calculatedObjectives) + if (arma::accu(objective) < performance) + performance = arma::accu(objective); + + return performance; +} + +//! No objectives to evaluate. +template +typename std::enable_if::type +AGEMOEA::EvaluateObjectives( + std::vector&, + std::tuple&, + std::vector >&) +{ + // Nothing to do here. +} + +//! Evaluate the objectives for the entire population. +template +typename std::enable_if::type +AGEMOEA::EvaluateObjectives( + std::vector& population, + std::tuple& objectives, + std::vector >& calculatedObjectives) +{ + for (size_t i = 0; i < population.size(); i++) + { + calculatedObjectives[i](I) = std::get(objectives).Evaluate(population[i]); + EvaluateObjectives(population, objectives, + calculatedObjectives); + } +} + +//! Reproduce and generate new candidates. +template +inline void AGEMOEA::BinaryTournamentSelection(std::vector& population, + const MatType& lowerBound, + const MatType& upperBound) +{ + std::vector children; + + while (children.size() < population.size()) + { + // Choose two random parents for reproduction from the elite population. + size_t indexA = arma::randi(arma::distr_param(0, populationSize - 1)); + size_t indexB = arma::randi(arma::distr_param(0, populationSize - 1)); + + // Make sure that the parents differ. + if (indexA == indexB) + { + if (indexB < populationSize - 1) + indexB++; + else + indexB--; + } + + // Initialize the children to the respective parents. + MatType childA = population[indexA], childB = population[indexB]; + + if (arma::randu() <= crossoverProb) + Crossover(childA, childB, population[indexA], population[indexB], + lowerBound, upperBound); + + Mutate(childA, 1.0 / static_cast(numVariables), + lowerBound, upperBound); + Mutate(childB, 1.0 / static_cast(numVariables), + lowerBound, upperBound); + + // Add the children to the candidate population. + children.push_back(childA); + children.push_back(childB); + } + + // Add the candidates to the elite population. + population.insert(std::end(population), std::begin(children), std::end(children)); +} + +//! Perform simulated binary crossover (SBX) of genes for the children. +template +inline void AGEMOEA::Crossover(MatType& childA, + MatType& childB, + const MatType& parentA, + const MatType& parentB, + const MatType& lowerBound, + const MatType& upperBound) +{ + //! Generates a child from two parent individuals + // according to the polynomial probability distribution. + arma::Cube parents(parentA.n_rows, + parentA.n_cols, 2); + parents.slice(0) = parentA; + parents.slice(1) = parentB; + MatType current_min = arma::min(parents, 2); + MatType current_max = arma::max(parents, 2); + + if (arma::accu(parentA - parentB < 1e-14)) + { + childA = parentA; + childB = parentB; + return; + } + MatType current_diff = current_max - current_min; + current_diff.transform( [](typename MatType::elem_type val) + { return (val < 1e-10 ? 1e-10:val); } ); + + // Calculating beta used for the final crossover. + MatType beta1 = 1 + 2.0 * (current_min - lowerBound) / current_diff; + MatType beta2 = 1 + 2.0 * (upperBound - current_max) / current_diff; + MatType alpha1 = 2 - arma::pow(beta1, -(eta + 1)); + MatType alpha2 = 2 - arma::pow(beta2, -(eta + 1)); + + MatType us(arma::size(alpha1), arma::fill::randu); + arma::umat mask1 = us > (1.0 / alpha1); + MatType betaq1 = arma::pow(us % alpha1, 1. / (eta + 1)); + betaq1 = betaq1 % (mask1 != 1.0) + arma::pow((1.0 / (2.0 - us % alpha1)), + 1.0 / (eta + 1)) % mask1; + arma::umat mask2 = us > (1.0 / alpha2); + MatType betaq2 = arma::pow(us % alpha2, 1 / (eta + 1)); + betaq2 = betaq2 % (mask1 != 1.0) + arma::pow((1.0 / (2.0 - us % alpha2)), + 1.0 / (eta + 1)) % mask2; + + // Variables after the cross over for all of them. + MatType c1 = 0.5 * ((current_min + current_max) - betaq1 % current_diff); + MatType c2 = 0.5 * ((current_min + current_max) + betaq2 % current_diff); + c1 = arma::min(arma::max(c1, lowerBound), upperBound); + c2 = arma::min(arma::max(c2, lowerBound), upperBound); + + // Decision for the crossover between the two parents for each variable. + us.randu(); + childA = parentA % (us <= 0.5); + childB = parentB % (us <= 0.5); + us.randu(); + childA = childA + c1 % ((us <= 0.5) % (childA == 0)); + childA = childA + c2 % ((us > 0.5) % (childA == 0)); + childB = childB + c2 % ((us <= 0.5) % (childB == 0)); + childB = childB + c1 % ((us > 0.5) % (childB == 0)); +} + +//! Perform Polynomial mutation of the candidate. +template +inline void AGEMOEA::Mutate(MatType& candidate, + double mutationRate, + const MatType& lowerBound, + const MatType& upperBound) +{ + const size_t numVariables = candidate.n_rows; + for (size_t geneIdx = 0; geneIdx < numVariables; ++geneIdx) + { + // Should this gene be mutated? + if (arma::randu() > mutationRate) + continue; + + const double geneRange = upperBound(geneIdx) - lowerBound(geneIdx); + // Normalised distance from the bounds. + const double lowerDelta = (candidate(geneIdx) + - lowerBound(geneIdx)) / geneRange; + const double upperDelta = (upperBound(geneIdx) + - candidate(geneIdx)) / geneRange; + const double mutationPower = 1. / (distributionIndex + 1.0); + const double rand = arma::randu(); + double value, perturbationFactor; + if (rand < 0.5) + { + value = 2.0 * rand + (1.0 - 2.0 * rand) * + std::pow(upperDelta, distributionIndex + 1.0); + perturbationFactor = std::pow(value, mutationPower) - 1.0; + } + else + { + value = 2.0 * (1.0 - rand) + 2.0 *(rand - 0.5) * + std::pow(lowerDelta, distributionIndex + 1.0); + perturbationFactor = 1.0 - std::pow(value, mutationPower); + } + + candidate(geneIdx) += perturbationFactor * geneRange; + } + //! Enforce bounds. + candidate = arma::min(arma::max(candidate, lowerBound), upperBound); +} + +template +inline void AGEMOEA::NormalizeFront( + std::vector >& calculatedObjectives, + arma::Col& normalization, + const std::vector& front, + const arma::Row& extreme) +{ + arma::Mat vectorizedObjectives(numObjectives, + front.size()); + arma::Mat vectorizedExtremes(numObjectives, + extreme.n_elem); + for (size_t i = 0; i < front.size(); i++) + { + vectorizedObjectives.col(i) = calculatedObjectives[front[i]]; + } + for (size_t i = 0; i < extreme.n_elem; i++) + { + vectorizedExtremes.col(i) = calculatedObjectives[front[extreme[i]]]; + } + + if (front.size() < numObjectives) + { + normalization = arma::max(vectorizedObjectives, 1); + return; + } + arma::Col temp; + arma::uvec unique = arma::find_unique(extreme); + if (extreme.n_elem != unique.n_elem) + { + normalization = arma::max(vectorizedObjectives, 1); + return; + } + arma::Col one(extreme.n_elem, arma::fill::ones); + arma::Col hyperplane(numObjectives, arma::fill::zeros); + try{ + hyperplane = arma::solve( + vectorizedExtremes.t(), one); + } + catch(...) + { + normalization = arma::max(vectorizedObjectives, 1); + normalization = normalization + (normalization == 0); + return; + } + if (hyperplane.has_inf() || hyperplane.has_nan() || (arma::accu(hyperplane < 0.0) > 0)) + { + normalization = arma::max(vectorizedObjectives, 1); + } + else + { + normalization = 1. / hyperplane; + if (normalization.has_inf() || normalization.has_nan()) + { + normalization = arma::max(vectorizedObjectives, 1); + } + } + normalization = normalization + (normalization == 0); +} + +template +inline double AGEMOEA::GetGeometry( + std::vector >& calculatedObjectives, + const std::vector& front, + const arma::Row& extreme) +{ + arma::Row d; + arma::Col zero(numObjectives, arma::fill::zeros); + arma::Col one(numObjectives, arma::fill::ones); + + PointToLineDistance (d, calculatedObjectives, front, zero, one); + + for (size_t i = 0; i < extreme.size(); i++) + { + d[extreme[i]] = arma::datum::inf; + } + size_t index = arma::index_min(d); + double avg = arma::accu(calculatedObjectives[front[index]]) / static_cast (numObjectives); + double p = std::log(numObjectives) / std::log(1.0 / avg); + if (p <= 0.1 || std::isnan(p)) + p = 1.0; + + return p; +} + +//! Pairwise distance for each point in the given front. +template +inline void AGEMOEA::PairwiseDistance( + MatType& f, + std::vector >& calculatedObjectives, + const std::vector& front, + double dimension) +{ + for (size_t i = 0; i < front.size(); i++) + { + for (size_t j = i + 1; j < front.size(); j++) + { + f(i, j) = std::pow(arma::accu(arma::pow(arma::abs(calculatedObjectives[front[i]] - calculatedObjectives[front[j]]), dimension)), 1.0 / dimension); + f(j, i) = f(i, j); + } + } +} + +//! Find the index of the of the extreme points in the given front. +template +void AGEMOEA::FindExtremePoints( + arma::Row& indexes, + std::vector >& calculatedObjectives, + const std::vector& front) +{ + typedef typename MatType::elem_type ElemType; + + if (numObjectives >= front.size()) + { + indexes = arma::linspace>(0, front.size() - 1, front.size()); + return; + } + + arma::Mat W(numObjectives, numObjectives, arma::fill::eye); + W = W + 1e-6; + std::vector selected(front.size()); + arma::Col z(numObjectives, arma::fill::zeros); + arma::Row dists; + for (size_t i = 0; i < numObjectives; i++) + { + PointToLineDistance(dists, calculatedObjectives, front, z, W.col(i)); + for (size_t j = 0; j < front.size(); j++) + if (selected[j]){dists[j] = arma::datum::inf;} + indexes[i] = dists.index_min(); + selected[dists.index_min()] = true; + } +} + +//! Find the distance of a front from a line formed by two points. +template +void AGEMOEA::PointToLineDistance( + arma::Row& distances, + std::vector >& calculatedObjectives, + const std::vector& front, + const arma::Col& pointA, + const arma::Col& pointB) +{ + typedef typename MatType::elem_type ElemType; + arma::Row distancesTemp(front.size()); + arma::Col ba = pointB - pointA; + arma::Col pa; + + for (size_t i = 0; i < front.size(); i++) + { + size_t ind = front[i]; + + pa = (calculatedObjectives[ind] - pointA); + double t = arma::dot(pa, ba) / arma::dot(ba, ba); + distancesTemp[i] = arma::accu(arma::pow((pa - t * ba), 2)); + } + distances = distancesTemp; +} + +//! Sort population into Pareto fronts. +template +inline void AGEMOEA::FastNonDominatedSort( + std::vector >& fronts, + std::vector& ranks, + std::vector >& calculatedObjectives) +{ + std::map dominationCount; + std::map > dominated; + + // Reset and initialize fronts. + fronts.clear(); + fronts.push_back(std::vector()); + + for (size_t p = 0; p < calculatedObjectives.size(); p++) + { + dominated[p] = std::set(); + dominationCount[p] = 0; + + for (size_t q = 0; q < calculatedObjectives.size(); q++) + { + if (Dominates(calculatedObjectives, p, q)) + dominated[p].insert(q); + else if (Dominates(calculatedObjectives, q, p)) + dominationCount[p] += 1; + } + + if (dominationCount[p] == 0) + { + ranks[p] = 0; + fronts[0].push_back(p); + } + } + + size_t i = 0; + + while (!fronts[i].empty()) + { + std::vector nextFront; + + for (size_t p: fronts[i]) + { + for (size_t q: dominated[p]) + { + dominationCount[q]--; + + if (dominationCount[q] == 0) + { + ranks[q] = i + 1; + nextFront.push_back(q); + } + } + } + + i++; + fronts.push_back(nextFront); + } + // Remove the empty final set. + fronts.pop_back(); +} + +//! Check if a candidate Pareto dominates another candidate. +template +inline bool AGEMOEA::Dominates( + std::vector >& calculatedObjectives, + size_t candidateP, + size_t candidateQ) +{ + bool allBetterOrEqual = true; + bool atleastOneBetter = false; + size_t n_objectives = calculatedObjectives[0].n_elem; + + for (size_t i = 0; i < n_objectives; i++) + { + // P is worse than Q for the i-th objective function. + if (calculatedObjectives[candidateP](i) > calculatedObjectives[candidateQ](i)) + allBetterOrEqual = false; + + // P is better than Q for the i-th objective function. + else if (calculatedObjectives[candidateP](i) < + calculatedObjectives[candidateQ](i)) + atleastOneBetter = true; + } + + return allBetterOrEqual && atleastOneBetter; +} + +//! Assign diversity score for a given point and the selected set. +template +inline typename MatType::elem_type AGEMOEA::DiversityScore( + std::set& selected, + const MatType& pairwiseDistance, + size_t S) +{ + typedef typename MatType::elem_type ElemType; + ElemType m = arma::datum::inf; + ElemType m1 = arma::datum::inf; + std::set::iterator it; + for (it = selected.begin(); it != selected.end(); it++) + { + if (*it == S){ continue; } + if (pairwiseDistance(S, *it) < m) + { + m1 = m; + m = pairwiseDistance(S, *it); + } + else if (pairwiseDistance(S, *it) < m1) + { + m1 = pairwiseDistance(S, *it); + } + } + m1 = (m1 == arma::datum::inf) ? 0 : m1; + m = (m == arma::datum::inf) ? 0 : m; + return m + m1; +} + +//! Assign survival score for a front of the population. +template +inline void AGEMOEA::SurvivalScoreAssignment( + const std::vector& front, + const arma::Col& idealPoint, + std::vector>& calculatedObjectives, + std::vector& survivalScore, + arma::Col& normalize, + double& dimension, + size_t fNum) +{ + typedef typename MatType::elem_type ElemType; + + // Calculations for the first front. + if (fNum == 0) + { + if (front.size() < numObjectives) + { + dimension = 1; + arma::Row extreme(numObjectives, arma::fill::zeros); + NormalizeFront(calculatedObjectives, normalize, front, extreme); + return; + } + + for (size_t index = 0; index < front.size(); index++) + { + calculatedObjectives[front[index]] = calculatedObjectives[front[index]] + - idealPoint; + } + + arma::Row extreme(numObjectives, arma::fill::zeros); + FindExtremePoints(extreme, calculatedObjectives, front); + NormalizeFront(calculatedObjectives, normalize, front, extreme); + + for (size_t index = 0; index < front.size(); index++) + { + calculatedObjectives[front[index]] = calculatedObjectives[front[index]] + / normalize; + } + + std::set selected; + std::set remaining; + + // Create the selected and remaining sets. + for (size_t index: extreme) + { + selected.insert(index); + survivalScore[front[index]] = arma::datum::inf; + } + + dimension = GetGeometry(calculatedObjectives, front, + extreme); + for (size_t i = 0; i < front.size(); i++) + { + if (selected.count(i) == 0) + { + remaining.insert(i); + } + } + + arma::Mat pairwise(front.size(), front.size(), arma::fill::zeros); + PairwiseDistance(pairwise,calculatedObjectives,front,dimension); + arma::Row value(front.size(), + arma::fill::zeros); + + // Calculate the diversity and proximity score. + for (size_t i = 0; i < front.size(); i++) + { + pairwise.col(i) = pairwise.col(i) / std::pow(arma::accu(arma::pow( + arma::abs(calculatedObjectives[front[i]]), dimension)), 1.0 / dimension); + } + + while (remaining.size() > 0) + { + std::set::iterator it; + value = value.fill(-1); + for (it = remaining.begin(); it != remaining.end(); it++) + { + value[*it] = DiversityScore(selected, pairwise, *it); + } + size_t index = arma::index_max(value); + survivalScore[front[index]] = value[index]; + selected.insert(index); + remaining.erase(index); + } + } + + // Calculations for the other fronts. + else + { + for (size_t i = 0; i < front.size(); i++) + { + calculatedObjectives[front[i]] = (calculatedObjectives[front[i]]) / normalize; + survivalScore[front[i]] = 1.0 / std::pow(arma::accu(arma::pow(arma::abs( + calculatedObjectives[front[i]] - idealPoint), dimension)), + 1.0 / dimension); + } + + } +} + +//! Comparator for survival score based sorting. +template +inline bool AGEMOEA::SurvivalScoreOperator( + size_t idxP, + size_t idxQ, + const std::vector& ranks, + const std::vector& survivalScore) +{ + if (ranks[idxP] < ranks[idxQ]) + return true; + else if (ranks[idxP] == ranks[idxQ] && survivalScore[idxP] > survivalScore[idxQ]) + return true; + + return false; +} + +} // namespace ens + +#endif diff --git a/inst/include/ensmallen_bits/cmaes/cmaes.hpp b/inst/include/ensmallen_bits/cmaes/cmaes.hpp index 0ae3234..6fed17a 100644 --- a/inst/include/ensmallen_bits/cmaes/cmaes.hpp +++ b/inst/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/inst/include/ensmallen_bits/cmaes/cmaes_impl.hpp b/inst/include/ensmallen_bits/cmaes/cmaes_impl.hpp index 73a5c7f..4d2c18e 100644 --- a/inst/include/ensmallen_bits/cmaes/cmaes_impl.hpp +++ b/inst/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/inst/include/ensmallen_bits/cmaes/pop_cmaes_impl.hpp b/inst/include/ensmallen_bits/cmaes/pop_cmaes_impl.hpp new file mode 100644 index 0000000..38b8011 --- /dev/null +++ b/inst/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/inst/include/ensmallen_bits/config.hpp b/inst/include/ensmallen_bits/config.hpp index 4ed04c4..f355abd 100644 --- a/inst/include/ensmallen_bits/config.hpp +++ b/inst/include/ensmallen_bits/config.hpp @@ -56,22 +56,6 @@ #endif -// Define ens_deprecated for deprecated functionality. -// This is adapted from Armadillo's implementation. -#if defined(_MSC_VER) - #define ens_deprecated __declspec(deprecated) -#elif defined(__GNUG__) && (!defined(__clang__)) - #define ens_deprecated __attribute__((__deprecated__)) -#elif defined(__clang__) - #if __has_attribute(__deprecated__) - #define ens_deprecated __attribute__((__deprecated__)) - #else - #define ens_deprecated - #endif -#else - #define ens_deprecated -#endif - // undefine conflicting macros #if defined(As) #pragma message ("WARNING: undefined conflicting 'As' macro") diff --git a/inst/include/ensmallen_bits/ens_version.hpp b/inst/include/ensmallen_bits/ens_version.hpp index eb11b4d..45412ba 100644 --- a/inst/include/ensmallen_bits/ens_version.hpp +++ b/inst/include/ensmallen_bits/ens_version.hpp @@ -15,17 +15,17 @@ #define ENS_VERSION_MAJOR 2 // The minor version is two digits so regular numerical comparisons of versions // work right. The first minor version of a release is always 10. -#define ENS_VERSION_MINOR 21 -#define ENS_VERSION_PATCH 1 +#define ENS_VERSION_MINOR 22 +#define ENS_VERSION_PATCH 0 // If this is a release candidate, it will be reflected in the version name // (i.e. the version name will be "RC1", "RC2", etc.). Otherwise the version // name will typically be a seemingly arbitrary set of words that does not // contain the capitalized string "RC". -#define ENS_VERSION_NAME "Bent Antenna" +#define ENS_VERSION_NAME "E-Bike Excitement" // Incorporate the date the version was released. #define ENS_VERSION_YEAR "2024" -#define ENS_VERSION_MONTH "02" -#define ENS_VERSION_DAY "15" +#define ENS_VERSION_MONTH "11" +#define ENS_VERSION_DAY "29" namespace ens { diff --git a/inst/include/ensmallen_bits/function/arma_traits.hpp b/inst/include/ensmallen_bits/function/arma_traits.hpp index 5aff5f8..e13297b 100644 --- a/inst/include/ensmallen_bits/function/arma_traits.hpp +++ b/inst/include/ensmallen_bits/function/arma_traits.hpp @@ -77,11 +77,6 @@ struct MatTypeTraits> }; -#if ((ARMA_VERSION_MAJOR >= 10) || \ - ((ARMA_VERSION_MAJOR == 9) && (ARMA_VERSION_MINOR >= 869))) - -// Armadillo 9.869+ has SpSubview_col and SpSubview_row - template struct MatTypeTraits> { @@ -98,9 +93,6 @@ struct MatTypeTraits> "or a matrix alias instead!"); }; -#endif - - template struct MatTypeTraits> { diff --git a/inst/include/ensmallen_bits/function/sfinae_utility.hpp b/inst/include/ensmallen_bits/function/sfinae_utility.hpp index 1210a48..c8b8677 100644 --- a/inst/include/ensmallen_bits/function/sfinae_utility.hpp +++ b/inst/include/ensmallen_bits/function/sfinae_utility.hpp @@ -188,7 +188,7 @@ struct NAME \ template \ static no& chk(...); \ \ - static const bool value = sizeof(chk(0)) == sizeof(yes); \ + static constexpr bool value = sizeof(chk(0)) == sizeof(yes); \ }; \ \ template \ @@ -201,10 +201,10 @@ struct NAME \ N < MAXN, \ WithGreaterOrEqualNumberOfAdditionalArgs, \ std::false_type>::type>::type; \ - static const bool value = type::value; \ + static constexpr bool value = type::value; \ }; \ \ - static const bool value = \ + static constexpr bool value = \ WithGreaterOrEqualNumberOfAdditionalArgs::value; \ }; @@ -235,7 +235,7 @@ struct NAME \ template \ static char f(char) { return 0; } \ \ - static const bool value = sizeof(f(0)) != sizeof(char); \ + static constexpr bool value = sizeof(f(0)) != sizeof(char); \ }; /* * A macro that can be used for passing arguments containing commas to other diff --git a/inst/include/ensmallen_bits/fw/atoms.hpp b/inst/include/ensmallen_bits/fw/atoms.hpp index 8a72c6c..ffef05f 100644 --- a/inst/include/ensmallen_bits/fw/atoms.hpp +++ b/inst/include/ensmallen_bits/fw/atoms.hpp @@ -96,8 +96,7 @@ class Atoms // Find possible atom to be deleted. arma::vec gap = sqTerm - currentCoeffs % trans(gradient.t() * currentAtoms); - arma::uword ind; - gap.min(ind); + arma::uword ind = gap.index_min(); // Try deleting the atom. arma::mat newAtoms(currentAtoms.n_rows, currentAtoms.n_cols - 1); diff --git a/inst/include/ensmallen_bits/fw/constr_lpball.hpp b/inst/include/ensmallen_bits/fw/constr_lpball.hpp index 2d2979e..9cddbcb 100644 --- a/inst/include/ensmallen_bits/fw/constr_lpball.hpp +++ b/inst/include/ensmallen_bits/fw/constr_lpball.hpp @@ -118,8 +118,8 @@ class ConstrLpBallSolver else s = arma::abs(v); - arma::uword k = 0; - s.max(k); // k is the linear index of the largest element. + // k is the linear index of the largest element. + arma::uword k = s.index_max(); s.zeros(); // Take the sign of v(k). s(k) = -((0.0 < v(k)) - (v(k) < 0.0)); diff --git a/inst/include/ensmallen_bits/nsga2/nsga2.hpp b/inst/include/ensmallen_bits/nsga2/nsga2.hpp index 0ae02f8..06035dd 100644 --- a/inst/include/ensmallen_bits/nsga2/nsga2.hpp +++ b/inst/include/ensmallen_bits/nsga2/nsga2.hpp @@ -184,7 +184,7 @@ class NSGA2 * deprecated and will be removed in ensmallen 3.x! Use `ParetoFront()` * instead. */ - ens_deprecated const std::vector& Front() + [[deprecated("use ParetoFront() instead")]] const std::vector& Front() { if (rcFront.size() == 0) { diff --git a/inst/include/ensmallen_bits/problems/dtlz/dtlz1_function.hpp b/inst/include/ensmallen_bits/problems/dtlz/dtlz1_function.hpp new file mode 100644 index 0000000..d1ff522 --- /dev/null +++ b/inst/include/ensmallen_bits/problems/dtlz/dtlz1_function.hpp @@ -0,0 +1,216 @@ +/** + * @file dtlz1_function.hpp + * @author Satyam Shukla + * + * Implementation of the first DTLZ(Deb, Thiele, Laumanns, and Zitzler) test. + * + * 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_PROBLEMS_DTLZ_ONE_FUNCTION_HPP +#define ENSMALLEN_PROBLEMS_DTLZ_ONE_FUNCTION_HPP + +#include "../../moead/weight_init_policies/uniform_init.hpp" + +namespace ens { +namespace test { + +/** + * The DTLZ1 function, defined by: + * \f[ + * x_M = [x_i, n - M + 1 <= i <= n] + * g(x) = 100 * [|x_M| + \Sigma{i = n - M + 1}^n (x_i - 0.5)^2 - cos(20 * pi * + * (x_i - 0.5))] + * + * f_1(x) = 0.5 * x_1 * x_2 * ... x_M-1 * (1 + g(x_M)) + * f_2(x) = 0.5 * x_1 * x_2 * ... (1 - x_M-1) * (1 + g(x_M)) + * . + * . + * f_M(x) = 0.5 * (1 - x_1) * (1 + g(x_M)) + * \f] + * + * Bounds of the variable space is: + * 0 <= x_i <= 1 for i = 1,...,n. + * + * This should be optimized to x_i = 0.5 (for all x_i in x_M), at: + * the objective function values lie on the linear hyper-plane: + * \Sigma { m = 1}^M f_m* =0.5. + * + * For more information, please refer to: + * + * @code + * @incollection{deb2005scalable, + * title={Scalable test problems for evolutionary multiobjective optimization}, + * author={Deb, Kalyanmoy and Thiele, Lothar and Laumanns, Marco and Zitzler, Eckart}, + * booktitle={Evolutionary multiobjective optimization: theoretical advances and applications}, + * pages={105--145}, + * year={2005}, + * publisher={Springer} + * } + * @endcode + * + * @tparam MatType Type of matrix to optimize. + */ + template + class DTLZ1 + { + private: + + // A fixed no. of Objectives and Variables(|x| = 7, M = 3). + size_t numObjectives {3}; + size_t numVariables {7}; + size_t numParetoPoints; + + public: + + /** + * Object Constructor. + * Initializes the individual objective functions. + * + * @param numParetoPoint No. of pareto points in the reference front. + */ + DTLZ1 (size_t numParetoPoint = 136) : + numParetoPoints(numParetoPoint), + objectiveF1(0, *this), + objectiveF2(1, *this), + objectiveF3(2, *this) + {/* Nothing to do here */} + + // Get the private variables. + + // Get the number of objectives. + size_t GetNumObjectives() + { return this -> numObjectives; } + + // Get the number of Variables. + size_t GetNumVariables() + { return this -> numVariables; } + + /** + * Set the no. of pareto points. + * + * @param numParetoPoint No. of pareto points in the reference front. + */ + void SetNumParetoPoint (size_t numParetoPoint) + { this -> numParetoPoints = numParetoPoint; } + + // Get the starting point. + arma::Col GetInitialPoint() + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + return arma::Col(numVariables, arma::fill::ones); + } + + /** + * Evaluate the G(x) with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Row + */ + arma::Row g(const MatType& coords) + { + size_t k = numVariables - numObjectives + 1; + + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Row innerSum(size(coords)[1], arma::fill::zeros); + + for (size_t i = numObjectives - 1; i < numVariables; i++) + { + innerSum += arma::pow((coords.row(i) - 0.5), 2) - + arma::cos(20 * arma::datum::pi * (coords.row(i) - 0.5)); + } + + return 100 * (k + innerSum); + } + + /** + * Evaluate the objectives with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Mat + */ + arma::Mat Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Mat objectives(numObjectives, size(coords)[1]); + arma::Row G = g(coords); + arma::Row value = 0.5 * (1. + G); + for (size_t i = 0; i < numObjectives - 1; i++) + { + objectives.row(i) = value % (1.0 - coords.row(i)); + value = value % coords.row(i); + } + objectives.row(numObjectives - 1) = value; + return objectives; + } + + // Individual Objective function. + // Changes based on stop variable provided. + struct DTLZObjective + { + DTLZObjective(size_t stop, DTLZ1& dtlz) : stop(stop), dtlz(dtlz) + {/* Nothing to do here. */} + + /** + * Evaluate one objective with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Col + */ + typename MatType::elem_type Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + ElemType value = 0.5; + for (size_t i = 0; i < stop; i++) + { + value = value * coords[i]; + } + + if(stop != dtlz.numObjectives - 1) + { + value = value * (1. - coords[stop]); + } + else + { + value = value * coords[stop]; + } + + value = value * (1. + dtlz.g(coords)[0]); + return value; + } + + DTLZ1& dtlz; + size_t stop; + }; + + // Return back a tuple of objective functions. + std::tuple GetObjectives() + { + return std::make_tuple(objectiveF1, objectiveF2, objectiveF3); + } + + //! Get the Reference Front. + //! Front. The implementation has been taken from pymoo. + arma::mat GetReferenceFront() + { + Uniform refGenerator; + return 0.5 * refGenerator.Generate(3, numParetoPoints, 0); + } + + DTLZObjective objectiveF1; + DTLZObjective objectiveF2; + DTLZObjective objectiveF3; + }; + } //namespace test + } //namespace ens + +#endif \ No newline at end of file diff --git a/inst/include/ensmallen_bits/problems/dtlz/dtlz2_function.hpp b/inst/include/ensmallen_bits/problems/dtlz/dtlz2_function.hpp new file mode 100644 index 0000000..aa56a14 --- /dev/null +++ b/inst/include/ensmallen_bits/problems/dtlz/dtlz2_function.hpp @@ -0,0 +1,216 @@ +/** + * @file dtlz2_function.hpp + * @author Satyam Shukla + * + * Implementation of the second DTLZ(Deb, Thiele, Laumanns, and Zitzler) test. + * + * 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_PROBLEMS_DTLZ_TWO_FUNCTION_HPP +#define ENSMALLEN_PROBLEMS_DTLZ_TWO_FUNCTION_HPP + +#include "../../moead/weight_init_policies/uniform_init.hpp" + +namespace ens { +namespace test { + +/** + * The DTLZ2 function, defined by: + * \f[ + * x_M = [x_i, n - M + 1 <= i <= n] + * g(x) = \Sigma{i = n - M + 1}^n (x_i - 0.5)^2 + * + * f_1(x) = 0.5 * cos(x_1 * pi * 0.5) * cos(x_2 * pi * 0.5) * ... cos(x_2 * pi * 0.5) * (1 + g(x_M)) + * f_2(x) = 0.5 * cos(x_1 * pi * 0.5) * cos(x_2 * pi * 0.5) * ... sin(x_M-1 * pi * 0.5) * (1 + g(x_M)) + * . + * . + * f_M(x) = 0.5 * sin(x_1 * pi * 0.5) * (1 + g(x_M)) + * \f] + * + * Bounds of the variable space is: + * 0 <= x_i <= 1 for i = 1,...,n. + * + * This should be optimized to x_i = 0.5 (for all x_i in x_M), at: + * + * For more information, please refer to: + * + * @code + * @incollection{deb2005scalable, + * title={Scalable test problems for evolutionary multiobjective optimization}, + * author={Deb, Kalyanmoy and Thiele, Lothar and Laumanns, Marco and Zitzler, Eckart}, + * booktitle={Evolutionary multiobjective optimization: theoretical advances and applications}, + * pages={105--145}, + * year={2005}, + * publisher={Springer} + * } + * @endcode + * + * @tparam MatType Type of matrix to optimize. + */ + template + class DTLZ2 + { + private: + + // A fixed no. of Objectives and Variables(|x| = 7, M = 3). + size_t numObjectives {3}; + size_t numVariables {7}; + size_t numParetoPoints; + + public: + + /** + * Object Constructor. + * Initializes the individual objective functions. + * + * @param numParetoPoint No. of pareto points in the reference front. + */ + DTLZ2(size_t numParetoPoints = 136) : + numParetoPoints(numParetoPoints), + objectiveF1(0, *this), + objectiveF2(1, *this), + objectiveF3(2, *this) + {/*Nothing to do here.*/} + + //! Get the starting point. + arma::Col GetInitialPoint() + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + return arma::Col(numVariables, 1, arma::fill::zeros); + } + + // Get the private variables. + + // Get the number of objectives. + size_t GetNumObjectives () + { return this -> numObjectives; } + + // Get the number of variables. + size_t GetNumVariables () + { return this -> numVariables; } + + /** + * Set the no. of pareto points. + * + * @param numParetoPoint + */ + void SetNumParetoPoint (size_t numParetoPoint) + { this -> numParetoPoints = numParetoPoint; } + + /** + * Evaluate the G(x) with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Row + */ + arma::Row g(const MatType& coords) + { + + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Row innerSum(size(coords)[1], arma::fill::zeros); + + for(size_t i = numObjectives - 1; i < numVariables; i++) + { + innerSum += arma::pow((coords.row(i) - 0.5), 2); + } + + return innerSum; + } + + /** + * Evaluate the objectives with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Mat + */ + arma::Mat Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Mat objectives(numObjectives, size(coords)[1]); + arma::Row G = g(coords); + arma::Row value = 0.5 * (1.0 + G); + for(size_t i = 0; i < numObjectives - 1; i++) + { + objectives.row(i) = value % + arma::sin(coords.row(i) * arma::datum::pi * 0.5); + value = value % arma::cos(coords.row(i) * arma::datum::pi * 0.5); + } + objectives.row(numObjectives - 1) = value; + return objectives; + } + + // Individual Objective function. + // Changes based on stop variable provided. + struct DTLZ2Objective + { + DTLZ2Objective(size_t stop, DTLZ2& dtlz): stop(stop), dtlz(dtlz) + {/* Nothing to do here. */} + + /** + * Evaluate one objective with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Col + */ + typename MatType::elem_type Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + ElemType value = 1.0; + for(size_t i = 0; i < stop; i++) + { + value = value * std::cos(coords[i] * arma::datum::pi * 0.5); + } + + if(stop != dtlz.numObjectives - 1) + { + value = value * std::sin(coords[stop] * arma::datum::pi * 0.5); + } + else + { + value = value * std::cos(coords[stop] * arma::datum::pi * 0.5); + } + + value = value * (1.0 + dtlz.g(coords)[0]); + return value; + } + + DTLZ2& dtlz; + size_t stop; + }; + + // Return back a tuple of objective functions. + std::tuple GetObjectives() + { + return std::make_tuple(objectiveF1, objectiveF2, objectiveF3); + } + + //! Get the Reference Front. + //! Front. The implementation has been taken from pymoo. + arma::mat GetReferenceFront() + { + Uniform refGenerator; + arma::mat refDirs = refGenerator.Generate(3, this -> numParetoPoints, 0); + arma::colvec x = arma::normalise(refDirs, 2, 1); + arma::mat A(size(refDirs), arma::fill::ones); + A.each_col() = x; + return refDirs / A; + } + + DTLZ2Objective objectiveF1; + DTLZ2Objective objectiveF2; + DTLZ2Objective objectiveF3; + }; + } //namespace test + } //namespace ens + +#endif diff --git a/inst/include/ensmallen_bits/problems/dtlz/dtlz3_function.hpp b/inst/include/ensmallen_bits/problems/dtlz/dtlz3_function.hpp new file mode 100644 index 0000000..895dee4 --- /dev/null +++ b/inst/include/ensmallen_bits/problems/dtlz/dtlz3_function.hpp @@ -0,0 +1,219 @@ +/** + * @file dtlz3_function.hpp + * @author Satyam Shukla + * + * Implementation of the third DTLZ(Deb, Thiele, Laumanns, and Zitzler) test. + * + * 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_PROBLEMS_DTLZ_THREE_FUNCTION_HPP +#define ENSMALLEN_PROBLEMS_DTLZ_THREE_FUNCTION_HPP + +#include "../../moead/weight_init_policies/uniform_init.hpp" + +namespace ens { +namespace test { + +/** + * The DTLZ3 function, defined by: + * \f[ + * x_M = [x_i, n - M + 1 <= i <= n] + * g(x) = 100 * [|x_M| + \Sigma{i = n - M + 1}^n (x_i - 0.5)^2 - cos(20 * pi * + * (x_i - 0.5))] + * + * f_1(x) = 0.5 * cos(x_1 * pi * 0.5) * cos(x_2 * pi * 0.5) * ... cos(x_2 * pi * 0.5) * (1 + g(x_M)) + * f_2(x) = 0.5 * cos(x_1 * pi * 0.5) * cos(x_2 * pi * 0.5) * ... sin(x_M-1 * pi * 0.5) * (1 + g(x_M)) + * . + * . + * f_M(x) = 0.5 * sin(x_1 * pi * 0.5) * (1 + g(x_M)) + * \f] + * + * Bounds of the variable space is: + * 0 <= x_i <= 1 for i = 1,...,n. + * + * This should be optimized to x_i = 0.5 (for all x_i in x_M), at: + * + * For more information, please refer to: + * + * @code + * @incollection{deb2005scalable, + * title={Scalable test problems for evolutionary multiobjective optimization}, + * author={Deb, Kalyanmoy and Thiele, Lothar and Laumanns, Marco and Zitzler, Eckart}, + * booktitle={Evolutionary multiobjective optimization: theoretical advances and applications}, + * pages={105--145}, + * year={2005}, + * publisher={Springer} + * } + * @endcode + * + * @tparam MatType Type of matrix to optimize. + */ + template + class DTLZ3 + { + private: + + // A fixed no. of Objectives and Variables(|x| = 7, M = 3). + size_t numObjectives {3}; + size_t numVariables {7}; + size_t numParetoPoints; + + public: + + /** + * Object Constructor. + * Initializes the individual objective functions. + * + * @param numParetoPoint No. of pareto points in the reference front. + */ + DTLZ3(size_t numParetoPoints = 136) : + numParetoPoints(numParetoPoints), + objectiveF1(0, *this), + objectiveF2(1, *this), + objectiveF3(2, *this) + {/*Nothing to do here.*/} + + //! Get the starting point. + arma::Col GetInitialPoint() + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + return arma::Col(numVariables, 1, arma::fill::zeros); + } + + // Get the private variables. + + // Get the number of objectives. + size_t GetNumObjectives() + { return this -> numObjectives; } + + // Get the number of variables. + size_t GetNumVariables() + { return this -> numVariables;} + + /** + * Set the no. of pareto points. + * + * @param numParetoPoint No. of pareto points in the reference front. + */ + void SetNumParetoPoint(size_t numParetoPoint) + { this -> numParetoPoints = numParetoPoint;} + + /** + * Evaluate the G(x) with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Row + */ + arma::Row g(const MatType& coords) + { + size_t k = numVariables - numObjectives + 1; + + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Row innerSum(size(coords)[1], arma::fill::zeros); + + for(size_t i = numObjectives - 1; i < numVariables; i++) + { + innerSum += arma::pow((coords.row(i) - 0.5), 2) - + arma::cos(20 * arma::datum::pi * (coords.row(i) - 0.5)); + } + + return 100 * (k + innerSum); + } + + /** + * Evaluate the objectives with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Mat + */ + arma::Mat Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Mat objectives(numObjectives, size(coords)[1]); + arma::Row G = g(coords); + arma::Row value = 0.5 * (1.0 + G); + for(size_t i = 0; i < numObjectives - 1; i++) + { + objectives.row(i) = value % + arma::sin(coords.row(i) * arma::datum::pi * 0.5); + value = value % arma::cos(coords.row(i) * arma::datum::pi * 0.5); + } + objectives.row(numObjectives - 1) = value; + return objectives; + } + + // Individual Objective function. + // Changes based on stop variable provided. + struct DTLZ3Objective + { + DTLZ3Objective(size_t stop, DTLZ3& dtlz): stop(stop), dtlz(dtlz) + {/* Nothing to do here. */} + + /** + * Evaluate one objective with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Col + */ + typename MatType::elem_type Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + ElemType value = 1.0; + for(size_t i = 0; i < stop; i++) + { + value = value * std::cos(coords[i] * arma::datum::pi * 0.5); + } + + if(stop != dtlz.numObjectives - 1) + { + value = value * std::sin(coords[stop] * arma::datum::pi * 0.5); + } + else + { + value = value * std::cos(coords[stop] * arma::datum::pi * 0.5); + } + + value = value * (1. + dtlz.g(coords)[0]); + return value; + } + + DTLZ3& dtlz; + size_t stop; + }; + + // Return back a tuple of objective functions. + std::tuple GetObjectives() + { + return std::make_tuple(objectiveF1, objectiveF2, objectiveF3); + } + + //! Get the Reference Front. + //! Front. The implementation has been taken from pymoo. + arma::mat GetReferenceFront() + { + Uniform refGenerator; + arma::mat refDirs = refGenerator.Generate(3, this -> numParetoPoints, 0); + arma::colvec x = arma::normalise(refDirs, 2, 1); + arma::mat A(size(refDirs), arma::fill::ones); + A.each_col() = x; + return refDirs / A; + } + + DTLZ3Objective objectiveF1; + DTLZ3Objective objectiveF2; + DTLZ3Objective objectiveF3; + }; + } //namespace test + } //namespace ens + +#endif \ No newline at end of file diff --git a/inst/include/ensmallen_bits/problems/dtlz/dtlz4_function.hpp b/inst/include/ensmallen_bits/problems/dtlz/dtlz4_function.hpp new file mode 100644 index 0000000..b01fd30 --- /dev/null +++ b/inst/include/ensmallen_bits/problems/dtlz/dtlz4_function.hpp @@ -0,0 +1,219 @@ +/** + * @file dtlz4_function.hpp + * @author Satyam Shukla + * + * Implementation of the fourth DTLZ(Deb, Thiele, Laumanns, and Zitzler) test. + * + * 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_PROBLEMS_DTLZ_FOUR_FUNCTION_HPP +#define ENSMALLEN_PROBLEMS_DTLZ_FOUR_FUNCTION_HPP + +#include "../../moead/weight_init_policies/uniform_init.hpp" + +namespace ens { +namespace test { + +/** + * The DTLZ4 function, defined by: + * \f[ + * x_M = [x_i, n - M + 1 <= i <= n] + * g(x) = \Sigma{i = n - M + 1}^n (x_i - 0.5)^2 + * + * f_1(x) = 0.5 * cos(x_1^alpha * pi * 0.5) * cos(x_2^alpha * pi * 0.5) * ... cos(x_2^alpha * pi * 0.5) * (1 + g(x_M)) + * f_2(x) = 0.5 * cos(x_1^alpha * pi * 0.5) * cos(x_2^alpha * pi * 0.5) * ... sin(x_M-1^alpha * pi * 0.5) * (1 + g(x_M)) + * . + * . + * f_M(x) = 0.5 * sin(x_1^alpha * pi * 0.5) * (1 + g(x_M)) + * \f] + * + * Bounds of the variable space is: + * 0 <= x_i <= 1 for i = 1,...,n. + * + * This should be optimized to x_i = 0.5 (for all x_i in x_M), at: + * + * For more information, please refer to: + * + * @code + * @incollection{deb2005scalable, + * title={Scalable test problems for evolutionary multiobjective optimization}, + * author={Deb, Kalyanmoy and Thiele, Lothar and Laumanns, Marco and Zitzler, Eckart}, + * booktitle={Evolutionary multiobjective optimization: theoretical advances and applications}, + * pages={105--145}, + * year={2005}, + * publisher={Springer} + * } + * @endcode + * + * @tparam MatType Type of matrix to optimize. + */ + template + class DTLZ4 + { + private: + + // A fixed no. of Objectives and Variables(|x| = 7, M = 3). + size_t numObjectives {3}; + size_t numVariables {7}; + size_t numParetoPoints; + size_t alpha; + + public: + + /** + * Object Constructor. + * Initializes the individual objective functions. + * + * @param alpha The power which each variable is raised to. + * @param numParetoPoint No. of pareto points in the reference front. + */ + DTLZ4 (size_t alpha = 100, size_t numParetoPoints = 136) : + alpha(alpha), + numParetoPoints(numParetoPoints), + objectiveF1(0, *this), + objectiveF2(1, *this), + objectiveF3(2, *this) + {/*Nothing to do here.*/} + + //! Get the starting point. + arma::Col GetInitialPoint() + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + return arma::Col(numVariables, 1, arma::fill::zeros); + } + + // Get the private variables. + + // Get the number of objectives. + size_t GetNumObjectives() + { return this -> numObjectives; } + + // Get the number of variables. + size_t GetNumVariables() + { return this -> numVariables; } + + /** + * Set the no. of pareto points. + * + * @param numParetoPoint + */ + void SetNumParetoPoint(size_t numParetoPoint) + { this -> numParetoPoints = numParetoPoint; } + + /** + * Evaluate the G(x) with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Row + */ + arma::Row g(const MatType& coords) + { + + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Row innerSum(size(coords)[1], arma::fill::zeros); + + for(size_t i = numObjectives - 1; i < numVariables; i++) + { + innerSum += arma::pow((coords.row(i) - 0.5), 2); + } + + return innerSum; + } + + /** + * Evaluate the objectives with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Mat + */ + arma::Mat Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Mat objectives(numObjectives, size(coords)[1]); + arma::Row G = g(coords); + arma::Row value = 0.5 * (1.0 + G); + for(size_t i = 0; i < numObjectives - 1; i++) + { + objectives.row(i) = value % + arma::sin(arma::pow(coords.row(i), alpha) * arma::datum::pi * 0.5); + value = value % arma::cos(arma::pow(coords.row(i), alpha) * arma::datum::pi * 0.5); + } + objectives.row(numObjectives - 1) = value; + return objectives; + } + + // Individual Objective function. + // Changes based on stop variable provided. + struct DTLZ4Objective + { + DTLZ4Objective(size_t stop, DTLZ4& dtlz): stop(stop), dtlz(dtlz) + {/* Nothing to do here.*/} + + /** + * Evaluate one objective with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Col + */ + typename MatType::elem_type Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + ElemType value = 1.0; + for(size_t i = 0; i < stop; i++) + { + value = value * std::cos(std::pow(coords[i], dtlz.alpha) * arma::datum::pi * 0.5); + } + + if(stop != dtlz.numObjectives - 1) + { + value = value * std::sin(std::pow(coords[stop], dtlz.alpha) * arma::datum::pi * 0.5); + } + else + { + value = value * std::cos(std::pow(coords[stop], dtlz.alpha) * arma::datum::pi * 0.5); + } + + value = value * (1 + dtlz.g(coords)[0]); + return value; + } + + DTLZ4& dtlz; + size_t stop; + }; + + // Return back a tuple of objective functions. + std::tuple GetObjectives() + { + return std::make_tuple(objectiveF1, objectiveF2, objectiveF3); + } + + //! Get the Reference Front. + //! Front. The implementation has been taken from pymoo. + arma::mat GetReferenceFront() + { + Uniform refGenerator; + arma::mat refDirs = refGenerator.Generate(3, this -> numParetoPoints, 0); + arma::colvec x = arma::normalise(refDirs, 2, 1); + arma::mat A(size(refDirs), arma::fill::ones); + A.each_col() = x; + return refDirs / A; + } + + DTLZ4Objective objectiveF1; + DTLZ4Objective objectiveF2; + DTLZ4Objective objectiveF3; + }; + } //namespace test + } //namespace ens + +#endif diff --git a/inst/include/ensmallen_bits/problems/dtlz/dtlz5_function.hpp b/inst/include/ensmallen_bits/problems/dtlz/dtlz5_function.hpp new file mode 100644 index 0000000..6c7de5c --- /dev/null +++ b/inst/include/ensmallen_bits/problems/dtlz/dtlz5_function.hpp @@ -0,0 +1,209 @@ +/** + * @file dtlz5_function.hpp + * @author Satyam Shukla + * + * Implementation of the fifth DTLZ(Deb, Thiele, Laumanns, and Zitzler) test. + * + * 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_PROBLEMS_DTLZ_FIVE_FUNCTION_HPP +#define ENSMALLEN_PROBLEMS_DTLZ_FIVE_FUNCTION_HPP + +namespace ens { +namespace test { + +/** + * The DTLZ5 function, defined by: + * \f[ + * theta_M = [theta_i, n - M + 1 <= i <= n] + * g(x) = \Sigma{i = n - M + 1}^n (x_i - 0.5)^2 + * + * f_1(x) = 0.5 * cos(theta_1 * pi * 0.5) * cos(theta_2 * pi * 0.5) * ... cos(theta_M-1 * pi * 0.5) * (1 + g(theta_M)) + * f_2(x) = 0.5 * cos(theta_1 * pi * 0.5) * cos(theta_2 * pi * 0.5) * ... sin(theta_M-1 * pi * 0.5) * (1 + g(theta_M)) + * . + * . + * f_M(x) = 0.5 * sin(theta_1 * pi * 0.5) * (1 + g(theta_M)) + * \f] + * + * Bounds of the variable space is: + * 0 <= x_i <= 1 for i = 1,...,n. + * + * Where theta_i = 0.5 * (1 + 2 * g(X_M) * x_i) / (1 + g(X_M)) + * + * This should be optimized to x_i = 0.5 (for all x_i in X_M), at: + * + * For more information, please refer to: + * + * @code + * @incollection{deb2005scalable, + * title={Scalable test problems for evolutionary multiobjective optimization}, + * author={Deb, Kalyanmoy and Thiele, Lothar and Laumanns, Marco and Zitzler, Eckart}, + * booktitle={Evolutionary multiobjective optimization: theoretical advances and applications}, + * pages={105--145}, + * year={2005}, + * publisher={Springer} + * } + * @endcode + * + * @tparam MatType Type of matrix to optimize. + */ + template + class DTLZ5 + { + private: + + // A fixed no. of Objectives and Variables(|x| = 7, M = 3). + size_t numObjectives {3}; + size_t numVariables {7}; + size_t numParetoPoints; + + public: + + /** + * Object Constructor. + * Initializes the individual objective functions. + * + * @param numParetoPoint No. of pareto points in the reference front. + */ + DTLZ5(size_t numParetoPoints = 136) : + numParetoPoints(numParetoPoints), + objectiveF1(0, *this), + objectiveF2(1, *this), + objectiveF3(2, *this) + {/*Nothing to do here.*/} + + //! Get the starting point. + arma::Col GetInitialPoint() + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + return arma::Col(numVariables, 1, arma::fill::zeros); + } + + // Get the private variables. + + // Get the number of objectives. + size_t GetNumObjectives() + { return this -> numObjectives; } + + // Get the number of variables. + size_t GetNumVariables() + { return this -> numVariables; } + + /** + * Set the no. of pareto points. + * + * @param numParetoPoint The no. points in the reference front. + */ + void SetNumParetoPoint(size_t numParetoPoint) + { this -> numParetoPoints = numParetoPoint; } + + /** + * Evaluate the G(x) with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Row + */ + arma::Row g(const MatType& coords) + { + + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Row innerSum(size(coords)[1], arma::fill::zeros); + + for(size_t i = numObjectives - 1; i < numVariables; i++) + { + innerSum += arma::pow((coords.row(i) - 0.5), 2); + } + + return innerSum; + } + + /** + * Evaluate the objectives with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Mat + */ + arma::Mat Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Mat objectives(numObjectives, size(coords)[1]); + arma::Row G = g(coords); + arma::Row value = 0.5 * (1.0 + G); + arma::Row theta; + for(size_t i = 0; i < numObjectives - 1; i++) + { + theta = 0.5 * (1.0 + 2.0 * coords.row(i) % G) / (1.0 + G); + objectives.row(i) = value % + arma::sin(theta * arma::datum::pi * 0.5); + value = value % arma::cos(theta * arma::datum::pi * 0.5); + } + objectives.row(numObjectives - 1) = value; + return objectives; + } + + // Individual Objective function. + // Changes based on stop variable provided. + struct DTLZ5Objective + { + DTLZ5Objective(size_t stop, DTLZ5& dtlz): stop(stop), dtlz(dtlz) + {/* Nothing to do here. */} + + /** + * Evaluate one objective with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Col + */ + typename MatType::elem_type Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + ElemType value = 1.0; + ElemType theta; + ElemType G = dtlz.g(coords)[0]; + for(size_t i = 0; i < stop; i++) + { + theta = 0.5 * (1.0 + 2.0 * coords[i] * G) / (1.0 + G); + value = value * std::cos(theta * arma::datum::pi * 0.5); + } + theta = 0.5 * (1.0 + 2.0 * coords[stop] * G) / (1.0 + G); + if(stop != dtlz.numObjectives - 1) + { + value = value * std::sin(theta * arma::datum::pi * 0.5); + } + else + { + value = value * std::cos(theta * arma::datum::pi * 0.5); + } + + value = value * (1.0 + G); + return value; + } + + DTLZ5& dtlz; + size_t stop; + }; + + // Return back a tuple of objective functions. + std::tuple GetObjectives () + { + return std::make_tuple(objectiveF1, objectiveF2, objectiveF3); + } + + DTLZ5Objective objectiveF1; + DTLZ5Objective objectiveF2; + DTLZ5Objective objectiveF3; + }; + } //namespace test + } //namespace ens + +#endif diff --git a/inst/include/ensmallen_bits/problems/dtlz/dtlz6_function.hpp b/inst/include/ensmallen_bits/problems/dtlz/dtlz6_function.hpp new file mode 100644 index 0000000..c9e4f11 --- /dev/null +++ b/inst/include/ensmallen_bits/problems/dtlz/dtlz6_function.hpp @@ -0,0 +1,209 @@ +/** + * @file dtlz6_function.hpp + * @author Satyam Shukla + * + * Implementation of the sixth DTLZ(Deb, Thiele, Laumanns, and Zitzler) test. + * + * 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_PROBLEMS_DTLZ_SIX_FUNCTION_HPP +#define ENSMALLEN_PROBLEMS_DTLZ_SIX_FUNCTION_HPP + +namespace ens { +namespace test { + +/** + * The DTLZ5 function, defined by: + * \f[ + * theta_M = [theta_i, n - M + 1 <= i <= n] + * g(x) = \Sigma{i = n - M + 1}^n (x_i)^0.1 + * + * f_1(x) = 0.5 * cos(theta_1 * pi * 0.5) * cos(theta_2 * pi * 0.5) * ... cos(theta_2 * pi * 0.5) * (1 + g(theta_M)) + * f_2(x) = 0.5 * cos(theta_1 * pi * 0.5) * cos(theta_2 * pi * 0.5) * ... sin(theta_M-1 * pi * 0.5) * (1 + g(theta_M)) + * . + * . + * f_M(x) = 0.5 * sin(theta_1 * pi * 0.5) * (1 + g(theta_M)) + * \f] + * + * Bounds of the variable space is: + * 0 <= x_i <= 1 for i = 1,...,n. + * + * Where theta_i = 0.5 * (1 + 2 * g(X_M) * x_i) / (1 + g(X_M)) + * + * This should be optimized to x_i = 0.5 (for all x_i in X_M), at: + * + * For more information, please refer to: + * + * @code + * @incollection{deb2005scalable, + * title={Scalable test problems for evolutionary multiobjective optimization}, + * author={Deb, Kalyanmoy and Thiele, Lothar and Laumanns, Marco and Zitzler, Eckart}, + * booktitle={Evolutionary multiobjective optimization: theoretical advances and applications}, + * pages={105--145}, + * year={2005}, + * publisher={Springer} + * } + * @endcode + * + * @tparam MatType Type of matrix to optimize. + */ + template + class DTLZ6 + { + private: + + // A fixed no. of Objectives and Variables(|x| = 7, M = 3). + size_t numObjectives {3}; + size_t numVariables {7}; + size_t numParetoPoints; + + public: + + /** + * Object Constructor. + * Initializes the individual objective functions. + * + * @param numParetoPoint No. of pareto points in the reference front. + */ + DTLZ6(size_t numParetoPoints = 136) : + numParetoPoints(numParetoPoints), + objectiveF1(0, *this), + objectiveF2(1, *this), + objectiveF3(2, *this) + {/*Nothing to do here.*/} + + //! Get the starting point. + arma::Col GetInitialPoint () + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + return arma::Col(numVariables, 1, arma::fill::zeros); + } + + // Get the private variables. + + // Get number of obectives. + size_t GetNumObjectives() + { return this -> numObjectives; } + + // Get number of variables. + size_t GetNumVariables() + { return this -> numVariables; } + + /** + * Set the no. of pareto points. + * + * @param numParetoPoint No. of pareto points in the reference front. + */ + void SetNumParetoPoint(size_t numParetoPoint) + { this -> numParetoPoints = numParetoPoint; } + + /** + * Evaluate the G(x) with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Row + */ + arma::Row g(const MatType& coords) + { + + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Row innerSum(size(coords)[1], arma::fill::zeros); + + for(size_t i = numObjectives - 1; i < numVariables; i++) + { + innerSum += arma::pow(coords.row(i), 0.1); + } + + return innerSum; + } + + /** + * Evaluate the objectives with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Mat + */ + arma::Mat Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Mat objectives(numObjectives, size(coords)[1]); + arma::Row G = g(coords); + arma::Row value = 0.5 * (1.0 + G); + arma::Row theta; + for(size_t i = 0; i < numObjectives - 1; i++) + { + theta = 0.5 * (1.0 + 2.0 * coords.row(i) % G) / (1.0 + G); + objectives.row(i) = value % + arma::sin(theta * arma::datum::pi * 0.5); + value = value % arma::cos(theta * arma::datum::pi * 0.5); + } + objectives.row(numObjectives - 1) = value; + return objectives; + } + + // Individual Objective function. + // Changes based on stop variable provided. + struct DTLZ6Objective + { + DTLZ6Objective(size_t stop, DTLZ6& dtlz): stop(stop), dtlz(dtlz) + {/* Nothing to do here. */} + + /** + * Evaluate one objective with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Col + */ + typename MatType::elem_type Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + ElemType value = 1.0; + ElemType theta; + ElemType G = dtlz.g(coords)[0]; + for(size_t i = 0; i < stop; i++) + { + theta = 0.5 * (1.0 + 2.0 * coords[i] * G) / (1.0 + G); + value = value * std::cos(theta * arma::datum::pi * 0.5); + } + theta = 0.5 * (1.0 + 2.0 * coords[stop] * G) / (1.0 + G); + if(stop != dtlz.numObjectives - 1) + { + value = value * std::sin(theta * arma::datum::pi * 0.5); + } + else + { + value = value * std::cos(theta * arma::datum::pi * 0.5); + } + + value = value * (1.0 + G); + return value; + } + + DTLZ6& dtlz; + size_t stop; + }; + + // Return back a tuple of objective functions. + std::tuple GetObjectives () + { + return std::make_tuple(objectiveF1, objectiveF2, objectiveF3); + } + + DTLZ6Objective objectiveF1; + DTLZ6Objective objectiveF2; + DTLZ6Objective objectiveF3; + }; + } //namespace test + } //namespace ens + +#endif diff --git a/inst/include/ensmallen_bits/problems/dtlz/dtlz7_function.hpp b/inst/include/ensmallen_bits/problems/dtlz/dtlz7_function.hpp new file mode 100644 index 0000000..fc2959e --- /dev/null +++ b/inst/include/ensmallen_bits/problems/dtlz/dtlz7_function.hpp @@ -0,0 +1,213 @@ +/** + * @file dtlz7_function.hpp + * @author Satyam Shukla + * + * Implementation of the seventh DTLZ(Deb, Thiele, Laumanns, and Zitzler) test. + * + * 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_PROBLEMS_DTLZ_SEVEN_FUNCTION_HPP +#define ENSMALLEN_PROBLEMS_DTLZ_SEVEN_FUNCTION_HPP + +namespace ens { +namespace test { + +/** + * The DTLZ7 function, defined by: + * \f[ + * x_M = [x_i, n - M + 1 <= i <= n] + * g(x) = 1 + (9 / |X_M|) * (\Sigma{i = n - M + 1}^n x_i) + * + * f_1(x) = x_1 + * f_2(x) = x_2 + * . + * . + * f_M(x) = (1 + g(X_M)) * h(f_1, f_2,...,g) + * \f] + * + * Bounds of the variable space is: + * 0 <= x_i <= 1 for i = 1,...,n. + * + * This should be optimized to x_i = 0.5 (for all x_i in x_M), at: + * the objective function values lie on the linear hyper-plane: + * \Sigma { m = 1}^M f_m* =0.5. + * + * For more information, please refer to: + * + * @code + * @incollection{deb2005scalable, + * title={Scalable test problems for evolutionary multiobjective optimization}, + * author={Deb, Kalyanmoy and Thiele, Lothar and Laumanns, Marco and Zitzler, Eckart}, + * booktitle={Evolutionary multiobjective optimization: theoretical advances and applications}, + * pages={105--145}, + * year={2005}, + * publisher={Springer} + * } + * @endcode + * + * @tparam MatType Type of matrix to optimize. + */ + template + class DTLZ7 + { + private: + + // A fixed no. of Objectives and Variables(|x| = 7, M = 3). + size_t numObjectives {3}; + size_t numVariables {7}; + size_t numParetoPoints; + + public: + + /** + * Object Constructor. + * Initializes the individual objective functions. + * + * @param numParetoPoint No. of pareto points in the reference front. + */ + DTLZ7(size_t numParetoPoint = 136) : + numParetoPoints(numParetoPoint), + objectiveF1(0, *this), + objectiveF2(1, *this), + objectiveF3(2, *this) + {/* Nothing to do here */} + + // Get the private variables. + + // Get the number of objectives. + size_t GetNumObjectives() + { return this -> numObjectives; } + + // Get the number of variables. + size_t GetNumVariables() + { return this -> numVariables; } + + /** + * Set the no. of pareto points. + * + * @param numParetoPoint No. of pareto points in the reference front. + */ + void SetNumParetoPoint(size_t numParetoPoint) + { this -> numParetoPoints = numParetoPoint; } + + // Get the starting point. + arma::Col GetInitialPoint() + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + return arma::Col(numVariables, arma::fill::zeros); + } + + /** + * Evaluate the G(x) with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Row + */ + arma::Row g(const MatType& coords) + { + size_t k = numVariables - numObjectives + 1; + + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Row innerSum(size(coords)[1], arma::fill::zeros); + + innerSum = (9.0 / k) * arma::sum(coords.rows(numObjectives - 1, + numVariables - 1) , 0) + 1.0; + return innerSum; + } + + /** + * Evaluate the H(f_i,...) with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Row + */ + arma::Row h( + const MatType& coords, const arma::Row& G) + { + + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Row innerSum(size(coords)[1], arma::fill::ones); + innerSum = innerSum * numObjectives; + for(size_t i = 0;i < numObjectives - 1;i++) + { + innerSum -= coords.row(i) % (1.0 + + arma::cos(arma::datum::pi * 3 * coords.row(i))) / (1 + G); + } + return innerSum; + } + + /** + * Evaluate the objectives with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Mat + */ + arma::Mat Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Mat objectives(numObjectives, size(coords)[1]); + arma::Row G = g(coords); + arma::Row H = h(coords, G); + objectives.rows(0, numObjectives - 2) = coords.rows(0, numObjectives - 2); + objectives.row(numObjectives - 1) = (1 + G) % H; + return objectives; + } + + // Individual Objective function. + // Changes based on stop variable provided. + struct DTLZ7Objective + { + DTLZ7Objective(size_t stop, DTLZ7& dtlz): stop(stop), dtlz(dtlz) + {/* Nothing to do here. */} + + /** + * Evaluate one objective with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Col + */ + typename MatType::elem_type Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + ElemType value = 0.5; + if(stop != dtlz.numObjectives - 1) + { return coords[stop];} + + value = (1.0 + dtlz.g(coords)[0]) * dtlz.h(coords, dtlz.g(coords))[0]; + return value; + } + + DTLZ7& dtlz; + size_t stop; + }; + + // Return back a tuple of objective functions. + std::tuple GetObjectives() + { + return std::make_tuple(objectiveF1, objectiveF2, objectiveF3); + } + + DTLZ7Objective objectiveF1; + DTLZ7Objective objectiveF2; + DTLZ7Objective objectiveF3; + using MAF7Objective = DTLZ7Objective; + }; + + template + using MAF7 = DTLZ7; + } //namespace test + } //namespace ens + +#endif diff --git a/inst/include/ensmallen_bits/problems/maf/maf1_function.hpp b/inst/include/ensmallen_bits/problems/maf/maf1_function.hpp new file mode 100644 index 0000000..0b2f133 --- /dev/null +++ b/inst/include/ensmallen_bits/problems/maf/maf1_function.hpp @@ -0,0 +1,188 @@ +/** + * @file maf1_function.hpp + * @author Satyam Shukla + * + * Implementation of the first Maf test. + * + * 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_PROBLEMS_MAF_ONE_FUNCTION_HPP +#define ENSMALLEN_PROBLEMS_MAF_ONE_FUNCTION_HPP + +#include "../../moead/weight_init_policies/uniform_init.hpp" + +namespace ens { +namespace test { + +/** + * The MAF1 function, defined by: + * \f[ + * x_M = [x_i, n - M + 1 <= i <= n] + * g(x) = \Sigma{i = n - M + 1}^n (x_i - 0.5)^2 + * + * f_1(x) = 1 - x_1 * x_2 * ... x_M-1 * (1 + g(x_M)) + * f_2(x) = 1 - x_1 * x_2 * ... (1 - x_M-1) * (1 + g(x_M)) + * . + * . + * f_M(x) = (x_1) * (1 + g(x_M)) + * \f] + * + * Bounds of the variable space is: + * 0 <= x_i <= 1 for i = 1,...,n. + * + * For more information, please refer to: + * + * @code + * @article{cheng2017benchmark, + * title={A benchmark test suite for evolutionary many-objective optimization}, + * author={Cheng, Ran and Li, Miqing and Tian, Ye and Zhang, Xingyi and Yang, Shengxiang and Jin, Yaochu and Yao, Xin}, + * journal={Complex \& Intelligent Systems}, + * volume={3}, + * pages={67--81}, + * year={2017}, + * publisher={Springer} + * } + * @endcode + * + * @tparam MatType Type of matrix to optimize. + */ + template + class MAF1 + { + private: + + // A fixed no. of Objectives and Variables(|x| = 7, M = 3). + size_t numObjectives {3}; + size_t numVariables {12}; + + public: + + /** + * Object Constructor. + * Initializes the individual objective functions. + * + * @param numParetoPoint No. of pareto points in the reference front. + */ + MAF1() : + objectiveF1(0, *this), + objectiveF2(1, *this), + objectiveF3(2, *this) + {/* Nothing to do here */} + + // Get the private variables. + size_t GetNumObjectives() + { return this -> numObjectives; } + + size_t GetNumVariables() + { return this -> numVariables; } + + // Get the starting point. + arma::Col GetInitialPoint() + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + return arma::Col(numVariables, arma::fill::ones); + } + + /** + * Evaluate the G(x) with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Row + */ + arma::Row g(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Row innerSum(size(coords)[1], arma::fill::zeros); + + for (size_t i = numObjectives - 1;i < numVariables;i++) + { + innerSum += arma::pow((coords.row(i) - 0.5), 2); + } + + return innerSum; + } + + /** + * Evaluate the objectives with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Mat + */ + arma::Mat Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Mat objectives(numObjectives, size(coords)[1]); + arma::Row G = g(coords); + arma::Row value(coords.n_cols, arma::fill::ones); + for (size_t i = 0;i < numObjectives - 1;i++) + { + objectives.row(i) = (1 - value % (1.0 - coords.row(i))) % (1. + G); + value = value % coords.row(i); + } + objectives.row(numObjectives - 1) = (1 - value) % (1. + G); + return objectives; + } + + // Individual Objective function. + // Changes based on stop variable provided. + struct MAF1Objective + { + MAF1Objective(size_t stop, MAF1& maf): stop(stop), maf(maf) + {/* Nothing to do here. */} + + /** + * Evaluate one objective with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Col + */ + typename MatType::elem_type Evaluate(const MatType& coords) + { + // Convenience typedef. + if(stop == 0) + { + return coords[0] * (1. + maf.g(coords)[0]); + } + typedef typename MatType::elem_type ElemType; + ElemType value = 1.0; + for (size_t i = 0; i < stop; i++) + { + value = value * coords[i]; + } + + if(stop != maf.GetNumObjectives() - 1) + { + value = value * (1. - coords[stop]); + } + + value = (1.0 - value) * (1. + maf.g(coords)[0]); + return value; + } + + MAF1& maf; + size_t stop; + }; + + //! Get objective functions. + std::tuple GetObjectives() + { + return std::make_tuple(objectiveF1, objectiveF2, objectiveF3); + } + + MAF1Objective objectiveF1; + MAF1Objective objectiveF2; + MAF1Objective objectiveF3; + }; + } //namespace test + } //namespace ens + +#endif \ No newline at end of file diff --git a/inst/include/ensmallen_bits/problems/maf/maf2_function.hpp b/inst/include/ensmallen_bits/problems/maf/maf2_function.hpp new file mode 100644 index 0000000..697b26b --- /dev/null +++ b/inst/include/ensmallen_bits/problems/maf/maf2_function.hpp @@ -0,0 +1,204 @@ +/** + * @file maf2_function.hpp + * @author Satyam Shukla + * + * Implementation of the second Maf test. + * + * 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_PROBLEMS_MAF_TWO_FUNCTION_HPP +#define ENSMALLEN_PROBLEMS_MAF_TWO_FUNCTION_HPP + +namespace ens { +namespace test { + +/** + * The MAF2 function, defined by: + * \f[ + * theta_M = [theta_i, n - M + 1 <= i <= n] + * g_i(x) = \Sigma{i = M + (i - 1) * (n - M + 1) / N}^ + * {M - 1 + (i) * (n - M + 1) / N} (x_i - 0.5)^2 * 0.25 + * + * f_1(x) = cos(theta_1) * cos(theta_2) * ... cos(theta_M-1) * (1 + g_1(theta_M)) + * f_2(x) = cos(theta_1) * cos(theta_2) * ... sin(theta_M-1) * (1 + g_2(theta_M)) + * . + * . + * f_M(x) = sin(theta_1) * (1 + g_M(theta_M)) + * \f] + * + * Bounds of the variable space is: + * 0 <= x_i <= 1 for i = 1,...,n. + * + * Where theta_i = 0.5 * (1 + 2 * g(X_M) * x_i) / (1 + g(X_M)) + * + * + * For more information, please refer to: + * + * @code + * @article{cheng2017benchmark, + * title={A benchmark test suite for evolutionary many-objective optimization}, + * author={Cheng, Ran and Li, Miqing and Tian, Ye and Zhang, Xingyi and Yang, Shengxiang and Jin, Yaochu and Yao, Xin}, + * journal={Complex \& Intelligent Systems}, + * volume={3}, + * pages={67--81}, + * year={2017}, + * publisher={Springer} + * } + * @endcode + * + * @tparam MatType Type of matrix to optimize. + */ + template + class MAF2 + { + private: + + // A fixed no. of Objectives and Variables(|x| = 7, M = 3). + size_t numObjectives {3}; + size_t numVariables {12}; + size_t numParetoPoints; + + public: + + /** + * Object Constructor. + * Initializes the individual objective functions. + * + * @param numParetoPoint No. of pareto points in the reference front. + */ + MAF2() : + objectiveF1(0, *this), + objectiveF2(1, *this), + objectiveF3(2, *this) + {/*Nothing to do here.*/} + + //! Get the starting point. + arma::Col GetInitialPoint() + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + return arma::Col(numVariables, 1, arma::fill::zeros); + } + + // Get the private variables. + + // Get the number of objectives. + size_t GetNumObjectives() + { return this -> numObjectives; } + + // Get the number of variables. + size_t GetNumVariables() + { return this -> numVariables; } + + /** + * Evaluate the G(x) with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Row + */ + arma::Mat g(const MatType& coords) + { + size_t k = numVariables - numObjectives + 1; + size_t c = std::floor(k / numObjectives); + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Mat innerSum(numObjectives, size(coords)[1], + arma::fill::zeros); + + for (size_t i = 0; i < numObjectives; i++) + { + size_t j = numObjectives - 1 + (i * c); + for(; j < numVariables - 1 + (i + 1) *c && j < numObjectives; j++) + { + innerSum.row(i) += arma::pow((coords.row(i) - 0.5), 2) * 0.25; + } + } + + return innerSum; + } + + /** + * Evaluate the objectives with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Mat + */ + arma::Mat Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Mat objectives(numObjectives, size(coords)[1]); + arma::Mat G = g(coords); + arma::Row value(size(coords)[1], arma::fill::ones); + arma::Row theta; + for (size_t i = 0; i < numObjectives - 1; i++) + { + theta = arma::datum::pi * 0.5 * ((coords.row(i) / 2) + 0.25); + objectives.row(i) = value % + arma::sin(theta) % (1.0 + G.row(numObjectives - 1 - i)); + value = value % arma::cos(theta); + } + objectives.row(numObjectives - 1) = value % + (1.0 + G.row(0)); + return objectives; + } + + // Individual Objective function. + // Changes based on stop variable provided. + struct MAF2Objective + { + MAF2Objective(size_t stop, MAF2& maf): stop(stop), maf(maf) + {/* Nothing to do here. */} + + /** + * Evaluate one objective with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Col + */ + typename MatType::elem_type Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + ElemType value = 1.0; + ElemType theta; + arma::Col G = maf.g(coords).col(0); + for (size_t i = 0; i < stop; i++) + { + theta = arma::datum::pi * 0.5 * ((coords[i] / 2) + 0.25); + value = value * std::cos(theta); + } + theta = arma::datum::pi * 0.5 * ((coords[stop] / 2) + 0.25); + if(stop != maf.numObjectives - 1) + { + value = value * std::sin(theta); + } + + value = value * (1.0 + G[maf.GetNumObjectives() - 1 - stop]); + return value; + } + + MAF2& maf; + size_t stop; + }; + + // Return back a tuple of objective functions. + std::tuple GetObjectives() + { + return std::make_tuple(objectiveF1, objectiveF2, objectiveF3); + } + + MAF2Objective objectiveF1; + MAF2Objective objectiveF2; + MAF2Objective objectiveF3; + }; + } //namespace test + } //namespace ens + +#endif \ No newline at end of file diff --git a/inst/include/ensmallen_bits/problems/maf/maf3_function.hpp b/inst/include/ensmallen_bits/problems/maf/maf3_function.hpp new file mode 100644 index 0000000..4ea0963 --- /dev/null +++ b/inst/include/ensmallen_bits/problems/maf/maf3_function.hpp @@ -0,0 +1,196 @@ +/** + * @file maf3_function.hpp + * @author Satyam Shukla + * + * Implementation of the third Maf test. + * + * 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_PROBLEMS_MAF_THREE_FUNCTION_HPP +#define ENSMALLEN_PROBLEMS_MAF_THREE_FUNCTION_HPP + +#include "../../moead/weight_init_policies/uniform_init.hpp" + +namespace ens { +namespace test { + +/** + * The MAF3 function, defined by: + * \f[ + * x_M = [x_i, n - M + 1 <= i <= n] + * g(x) = 100 * [|x_M| + \Sigma{i = n - M + 1}^n (x_i - 0.5)^2 - cos(20 * pi * + * (x_i - 0.5))] + * + * f_1(x) = (cos(x_1 * pi * 0.5) * cos(x_2 * pi * 0.5) * ... cos(x_2 * pi * 0.5) * (1 + g(x_M)))^4 + * f_2(x) = (cos(x_1 * pi * 0.5) * cos(x_2 * pi * 0.5) * ... sin(x_M-1 * pi * 0.5) * (1 + g(x_M)))^4 + * . + * . + * f_M(x) = (sin(x_1 * pi * 0.5) * (1 + g(x_M)))^2 + * \f] + * + * Bounds of the variable space is: + * 0 <= x_i <= 1 for i = 1,...,n. + * + * For more information, please refer to: + * + * @code + * @article{cheng2017benchmark, + * title={A benchmark test suite for evolutionary many-objective optimization}, + * author={Cheng, Ran and Li, Miqing and Tian, Ye and Zhang, Xingyi and Yang, Shengxiang and Jin, Yaochu and Yao, Xin}, + * journal={Complex \& Intelligent Systems}, + * volume={3}, + * pages={67--81}, + * year={2017}, + * publisher={Springer} + * } + * @endcode + * + * @tparam MatType Type of matrix to optimize. + */ + template + class MAF3 + { + private: + + // A fixed no. of Objectives and Variables(|x| = 12, M = 3). + size_t numObjectives {3}; + size_t numVariables {12}; + + public: + + /** + * Object Constructor. + * Initializes the individual objective functions. + * + * @param numParetoPoint No. of pareto points in the reference front. + */ + MAF3() : + objectiveF1(0, *this), + objectiveF2(1, *this), + objectiveF3(2, *this) + {/*Nothing to do here.*/} + + //! Get the starting point. + arma::Col GetInitialPoint() + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + return arma::Col(numVariables, 1, arma::fill::zeros); + } + + // Get the private variables. + + // Get the number of objectives. + size_t GetNumObjectives() + { return this -> numObjectives; } + + // Get the number of variables. + size_t GetNumVariables() + { return this -> numVariables;} + + /** + * Evaluate the G(x) with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Row + */ + arma::Row g(const MatType& coords) + { + size_t k = numVariables - numObjectives + 1; + + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Row innerSum(size(coords)[1], arma::fill::zeros); + + for (size_t i = numObjectives - 1; i < numVariables; i++) + { + innerSum += arma::pow((coords.row(i) - 0.5), 2) - + arma::cos(20 * arma::datum::pi * (coords.row(i) - 0.5)); + } + + return 100 * (k + innerSum); + } + + /** + * Evaluate the objectives with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Mat + */ + arma::Mat Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Mat objectives(numObjectives, size(coords)[1], arma::fill::ones); + arma::Row G = g(coords); + arma::Row value = (1.0 + G); + for (size_t i = 0; i < numObjectives - 1; i++) + { + objectives.row(i) = arma::pow(value, i == 0 ? 2:4) % + arma::pow(arma::sin(coords.row(i) * arma::datum::pi * 0.5), i == 0 ? 2:4); + value = value % arma::cos(coords.row(i) * arma::datum::pi * 0.5); + } + objectives.row(numObjectives - 1) = arma::pow(value, 4); + return objectives; + } + + // Individual Objective function. + // Changes based on stop variable provided. + struct MAF3Objective + { + MAF3Objective(size_t stop, MAF3& maf): stop(stop), maf(maf) + {/* Nothing to do here. */} + + /** + * Evaluate one objective with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Col + */ + typename MatType::elem_type Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + ElemType value = 1.0; + for (size_t i = 0; i < stop; i++) + { + value = value * std::cos(coords[i] * arma::datum::pi * 0.5); + } + + if(stop != maf.GetNumObjectives() - 1) + { + value = value * std::sin(coords[stop] * arma::datum::pi * 0.5); + } + + value = value * (1. + maf.g(coords)[0]); + + if(stop == 0) { + return std::pow(value, 2); + } + return std::pow(value, 4); + } + + MAF3& maf; + size_t stop; + }; + + // Return back a tuple of objective functions. + std::tuple GetObjectives() + { + return std::make_tuple(objectiveF1, objectiveF2, objectiveF3); + } + + MAF3Objective objectiveF1; + MAF3Objective objectiveF2; + MAF3Objective objectiveF3; + }; + } //namespace test + } //namespace ens + +#endif \ No newline at end of file diff --git a/inst/include/ensmallen_bits/problems/maf/maf4_function.hpp b/inst/include/ensmallen_bits/problems/maf/maf4_function.hpp new file mode 100644 index 0000000..9258468 --- /dev/null +++ b/inst/include/ensmallen_bits/problems/maf/maf4_function.hpp @@ -0,0 +1,211 @@ +/** + * @file maf4_function.hpp + * @author Satyam Shukla + * + * Implementation of the fourth Maf test. + * + * 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_PROBLEMS_MAF_FOUR_FUNCTION_HPP +#define ENSMALLEN_PROBLEMS_MAF_FOUR_FUNCTION_HPP + +#include "../../moead/weight_init_policies/uniform_init.hpp" + +namespace ens { +namespace test { + +/** + * The MAF4 function, defined by: + * \f[ + * x_M = [x_i, n - M + 1 <= i <= n] + * g(x) = 100 * [|x_M| + \Sigma{i = n - M + 1}^n (x_i - 0.5)^2 - cos(20 * pi * + * (x_i - 0.5))] + * + * f_1(x) = a * (1 - cos(x_1 * pi * 0.5) * cos(x_2 * pi * 0.5) * ... cos(x_2 * pi * 0.5))* (1 + g(x_M)) + * f_2(x) = a^2 * (1 - cos(x_1 * pi * 0.5) * cos(x_2 * pi * 0.5) * ... sin(x_M-1 * pi * 0.5)) * (1 + g(x_M)) + * . + * . + * f_M(x) = a^M * (1 - sin(x_1 * pi * 0.5)) * (1 + g(x_M)) + * \f] + * + * Bounds of the variable space is: + * 0 <= x_i <= 1 for i = 1,...,n. + * + * For more information, please refer to: + * + * @code + * @article{cheng2017benchmark, + * title={A benchmark test suite for evolutionary many-objective optimization}, + * author={Cheng, Ran and Li, Miqing and Tian, Ye and Zhang, Xingyi and Yang, Shengxiang and Jin, Yaochu and Yao, Xin}, + * journal={Complex \& Intelligent Systems}, + * volume={3}, + * pages={67--81}, + * year={2017}, + * publisher={Springer} + * } + * @endcode + * + * @tparam MatType Type of matrix to optimize. + */ + template + class MAF4 + { + private: + + // A fixed no. of Objectives and Variables(|x| = 7, M = 3). + size_t numObjectives {3}; + size_t numVariables {12}; + double a; + + public: + + /** + * Object Constructor. + * Initializes the individual objective functions. + * + * @param numParetoPoint No. of pareto points in the reference front. + * @param a The scale factor of the objectives. + */ + MAF4(double a = 2) : + objectiveF1(0, *this), + objectiveF2(1, *this), + objectiveF3(2, *this), + a(a) + {/*Nothing to do here.*/} + + //! Get the starting point. + arma::Col GetInitialPoint() + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + return arma::Col(numVariables, 1, arma::fill::zeros); + } + + // Get the private variables. + + // Get the number of objectives. + size_t GetNumObjectives() + { return this -> numObjectives; } + + // Get the number of variables. + size_t GetNumVariables() + { return this -> numVariables;} + + //Get the scaling parameter a. + size_t GetA() + { return this -> a; } + + /** + * Set the scale factor of the objectives. + * + * @param a The scale factor a of the objectives. + */ + void SetA(double a) + { this -> a = a; } + + /** + * Evaluate the G(x) with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Row + */ + arma::Row g(const MatType& coords) + { + size_t k = numVariables - numObjectives + 1; + + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Row innerSum(size(coords)[1], arma::fill::zeros); + + for (size_t i = numObjectives - 1; i < numVariables; i++) + { + innerSum += arma::pow((coords.row(i) - 0.5), 2) - + arma::cos(20 * arma::datum::pi * (coords.row(i) - 0.5)); + } + + return 100 * (k + innerSum); + } + + /** + * Evaluate the objectives with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Mat + */ + arma::Mat Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Mat objectives(numObjectives, size(coords)[1]); + arma::Row G = g(coords); + arma::Row value(coords.n_cols, arma::fill::ones); + for (size_t i = 0; i < numObjectives - 1; i++) + { + objectives.row(i) = (1.0 - value % + arma::sin(coords.row(i) * arma::datum::pi * 0.5)) % (1. + G) * + std::pow(a, numObjectives - i); + value = value % arma::cos(coords.row(i) * arma::datum::pi * 0.5); + } + objectives.row(numObjectives - 1) = (1 - value) % (1. + G) * + std::pow(a, 1); + return objectives; + } + + // Individual Objective function. + // Changes based on stop variable provided. + struct MAF4Objective + { + MAF4Objective(size_t stop, MAF4& maf): stop(stop), maf(maf) + {/* Nothing to do here. */} + + /** + * Evaluate one objective with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Col + */ + typename MatType::elem_type Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + ElemType value = 1.0; + for (size_t i = 0; i < stop; i++) + { + value = value * std::cos(coords[i] * arma::datum::pi * 0.5); + } + + if(stop != maf.GetNumObjectives() - 1) + { + value = value * std::sin(coords[stop] * arma::datum::pi * 0.5); + } + + value = std::pow(maf.GetA(), maf.GetNumObjectives() - stop) * + (1 - value) * (1. + maf.g(coords)[0]); + + return value; + } + + MAF4& maf; + size_t stop; + }; + + // Return back a tuple of objective functions. + std::tuple GetObjectives() + { + return std::make_tuple(objectiveF1, objectiveF2, objectiveF3); + } + + MAF4Objective objectiveF1; + MAF4Objective objectiveF2; + MAF4Objective objectiveF3; + }; + } //namespace test + } //namespace ens + +#endif diff --git a/inst/include/ensmallen_bits/problems/maf/maf5_function.hpp b/inst/include/ensmallen_bits/problems/maf/maf5_function.hpp new file mode 100644 index 0000000..1ac5f1b --- /dev/null +++ b/inst/include/ensmallen_bits/problems/maf/maf5_function.hpp @@ -0,0 +1,226 @@ +/** + * @file maf5_function.hpp + * @author Satyam Shukla + * + * Implementation of the fifth MAF test. + * + * 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_PROBLEMS_MAF_FIVE_FUNCTION_HPP +#define ENSMALLEN_PROBLEMS_MAF_FIVE_FUNCTION_HPP + +#include "../../moead/weight_init_policies/uniform_init.hpp" + +namespace ens { +namespace test { + +/** + * The MAF5 function, defined by: + * \f[ + * x_M = [x_i, n - M + 1 <= i <= n] + * g(x) = \Sigma{i = n - M + 1}^n (x_i - 0.5)^2 + * + * f_1(x) = a^M * cos(x_1^alpha * pi * 0.5) * cos(x_2^alpha * pi * 0.5) * ... cos(x_2^alpha * pi * 0.5) * (1 + g(x_M)) + * f_2(x) = a^M-1 * cos(x_1^alpha * pi * 0.5) * cos(x_2^alpha * pi * 0.5) * ... sin(x_M-1^alpha * pi * 0.5) * (1 + g(x_M)) + * . + * . + * f_M(x) = a * sin(x_1^alpha * pi * 0.5) * (1 + g(x_M)) + * \f] + * + * Bounds of the variable space is: + * 0 <= x_i <= 1 for i = 1,...,n. + * + * This should be optimized to x_i = 0.5 (for all x_i in x_M), at: + * + * For more information, please refer to: + * + * @code + * @article{cheng2017benchmark, + * title={A benchmark test suite for evolutionary many-objective optimization}, + * author={Cheng, Ran and Li, Miqing and Tian, Ye and Zhang, Xingyi and Yang, Shengxiang and Jin, Yaochu and Yao, Xin}, + * journal={Complex \& Intelligent Systems}, + * volume={3}, + * pages={67--81}, + * year={2017}, + * publisher={Springer} + * } + * @endcodes + * + * @tparam MatType Type of matrix to optimize. + */ + template + class MAF5 + { + private: + + // A fixed no. of Objectives and Variables(|x| = 7, M = 3). + size_t numObjectives {3}; + size_t numVariables {12}; + size_t alpha; + size_t a; + + public: + + /** + * Object Constructor. + * Initializes the individual objective functions. + * + * @param alpha The power which each variable is raised to. + * @param numParetoPoint No. of pareto points in the reference front. + * @param a The scale factor of the objectives. + */ + MAF5(size_t alpha = 100, double a = 2) : + alpha(alpha), + a(a), + objectiveF1(0, *this), + objectiveF2(1, *this), + objectiveF3(2, *this) + {/*Nothing to do here.*/} + + //! Get the starting point. + arma::Col GetInitialPoint() + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + return arma::Col(numVariables, 1, arma::fill::zeros); + } + + // Get the private variables. + + // Get the number of objectives. + size_t GetNumObjectives() + { return this -> numObjectives; } + + // Get the number of variables. + size_t GetNumVariables() + { return this -> numVariables; } + + // Get the scale factor a. + double GetA() + { return this -> a; } + + // Get the power alpha of each variable. + size_t GetAlpha() + { return this -> alpha; } + + /** + * Set the scale factor a. + * + * @param a The scale factor of the objectives. + */ + void SetA(double a) + { this -> a = a; } + + /** + * Set the power of each variable alpha. + * + * @param alpha The power of each variable. + */ + void SetAlpha(size_t alpha) + { this -> alpha = alpha; } + + /** + * Evaluate the G(x) with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Row + */ + arma::Row g(const MatType& coords) + { + + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Row innerSum(size(coords)[1], arma::fill::zeros); + + for (size_t i = numObjectives - 1; i < numVariables; i++) + { + innerSum += arma::pow((coords.row(i) - 0.5), 2); + } + + return innerSum; + } + + /** + * Evaluate the objectives with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Mat + */ + arma::Mat Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Mat objectives(numObjectives, size(coords)[1]); + arma::Row G = g(coords); + arma::Row value = (1.0 + G); + for (size_t i = 0; i < numObjectives - 1; i++) + { + objectives.row(i) = std::pow(a, i + 1) * arma::pow(value, 4) % + arma::pow(arma::sin(arma::pow(coords.row(i), alpha) * + arma::datum::pi * 0.5), 4); + value = value % arma::cos(arma::pow(coords.row(i), alpha) * arma::datum::pi * 0.5); + } + objectives.row(numObjectives - 1) = arma::pow(value, 4) * std::pow(a, numObjectives); + return objectives; + } + + // Individual Objective function. + // Changes based on stop variable provided. + struct MAF5Objective + { + MAF5Objective(size_t stop, MAF5& maf): stop(stop), maf(maf) + {/* Nothing to do here.*/} + + /** + * Evaluate one objective with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Col + */ + typename MatType::elem_type Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + ElemType value = 1.0; + for (size_t i = 0; i < stop; i++) + { + value = value * std::cos(std::pow(coords[i], maf.GetAlpha()) + * arma::datum::pi * 0.5); + } + + if(stop != maf.GetNumObjectives() - 1) + { + value = value * std::sin(std::pow(coords[stop], maf.GetAlpha()) + * arma::datum::pi * 0.5); + } + + value = value * (1 + maf.g(coords)[0]); + value = std::pow(value, 4); + value = value * std::pow(maf.GetA(), stop + 1); + return value; + } + + MAF5& maf; + size_t stop; + }; + + // Return back a tuple of objective functions. + std::tuple GetObjectives() + { + return std::make_tuple(objectiveF1, objectiveF2, objectiveF3); + } + + MAF5Objective objectiveF1; + MAF5Objective objectiveF2; + MAF5Objective objectiveF3; + }; + } //namespace test + } //namespace ens + +#endif diff --git a/inst/include/ensmallen_bits/problems/maf/maf6_function.hpp b/inst/include/ensmallen_bits/problems/maf/maf6_function.hpp new file mode 100644 index 0000000..e023906 --- /dev/null +++ b/inst/include/ensmallen_bits/problems/maf/maf6_function.hpp @@ -0,0 +1,234 @@ +/** + * @file maf6_function.hpp + * @author Satyam Shukla + * + * Implementation of the sixth Maf test. + * + * 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_PROBLEMS_MAF_SIX_FUNCTION_HPP +#define ENSMALLEN_PROBLEMS_MAF_SIX_FUNCTION_HPP + +namespace ens { +namespace test { + +/** + * The MAF6 function, defined by: + * \f[ + * theta_M = [theta_i, n - M + 1 <= i <= n] + * g(x) = \Sigma{i = n - M + 1}^n (x_i - 0.5)^2 + * + * f_1(x) = 0.5 * cos(theta_1 * pi * 0.5) * cos(theta_2 * pi * 0.5) * ... cos(theta_2 * pi * 0.5) * (1 + g(theta_M)) + * f_2(x) = 0.5 * cos(theta_1 * pi * 0.5) * cos(theta_2 * pi * 0.5) * ... sin(theta_M-1 * pi * 0.5) * (1 + g(theta_M)) + * . + * . + * f_M(x) = 0.5 * sin(theta_1 * pi * 0.5) * (1 + g(theta_M)) + * \f] + * + * Bounds of the variable space is: + * 0 <= x_i <= 1 for i = 1,...,n. + * + * Where theta_i = 0.5 * (1 + 2 * g(X_M) * x_i) / (1 + g(X_M)) + * + * This should be optimized to x_i = 0.5 (for all x_i in X_M), at: + * + * For more information, please refer to: + * + * @code + * @article{cheng2017benchmark, + * title={A benchmark test suite for evolutionary many-objective optimization}, + * author={Cheng, Ran and Li, Miqing and Tian, Ye and Zhang, Xingyi and Yang, Shengxiang and Jin, Yaochu and Yao, Xin}, + * journal={Complex \& Intelligent Systems}, + * volume={3}, + * pages={67--81}, + * year={2017}, + * publisher={Springer} + * } + * @endcode + * + * @tparam MatType Type of matrix to optimize. + */ + template + class MAF6 + { + private: + + // A fixed no. of Objectives and Variables(|x| = 7, M = 3). + size_t numObjectives {3}; + size_t numVariables {12}; + size_t I; + + public: + + /** + * Object Constructor. + * Initializes the individual objective functions. + * + * @param numParetoPoint No. of pareto points in the reference front. + * @param I The manifold dimension (zero indexed). + */ + MAF6(size_t I = 2) : + objectiveF1(0, *this), + objectiveF2(1, *this), + objectiveF3(2, *this), + I(I) + {/*Nothing to do here.*/} + + //! Get the starting point. + arma::Col GetInitialPoint() + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + return arma::Col(numVariables, 1, arma::fill::zeros); + } + + // Get the private variables. + + // Get the number of objectives. + size_t GetNumObjectives() + { return this -> numObjectives; } + + // Get the number of variables. + size_t GetNumVariables() + { return this -> numVariables; } + + // Get the manifold dimension. + size_t GetI() + { return this -> I; } + + /** + * Set the no. of pareto points. + * + * @param I The manifold dimension (0 indexed). + */ + void SetI(size_t I) + { this -> I = I; } + + /** + * Evaluate the G(x) with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Row + */ + arma::Row g(const MatType& coords) + { + + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Row innerSum(size(coords)[1], arma::fill::zeros); + + for (size_t i = numObjectives - 1; i < numVariables; i++) + { + innerSum += arma::pow((coords.row(i) - 0.5), 2); + } + + return innerSum; + } + + /** + * Evaluate the objectives with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Mat + */ + arma::Mat Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + + arma::Mat objectives(numObjectives, size(coords)[1]); + arma::Row G = g(coords); + arma::Row value = (1.0 + 100 * G); + arma::Row theta; + for (size_t i = 0; i < numObjectives - 1; i++) + { + if(i < I) + { + theta = coords.row(i) * arma::datum::pi * 0.5; + } + else + { + theta = 0.25 * (1.0 + 2.0 * coords.row(i) % G) / (1.0 + G); + } + objectives.row(i) = value % + arma::sin(theta); + value = value % arma::cos(theta); + } + objectives.row(numObjectives - 1) = value; + return objectives; + } + + // Individual Objective function. + // Changes based on stop variable provided. + struct MAF6Objective + { + MAF6Objective(size_t stop, MAF6& maf): stop(stop), maf(maf) + {/* Nothing to do here. */} + + /** + * Evaluate one objective with the given coordinate. + * + * @param coords The function coordinates. + * @return arma::Col + */ + typename MatType::elem_type Evaluate(const MatType& coords) + { + // Convenience typedef. + typedef typename MatType::elem_type ElemType; + ElemType value = 1.0; + ElemType theta; + ElemType G = maf.g(coords)[0]; + for (size_t i = 0; i < stop; i++) + { + if(i < maf.GetI()) + { + theta = arma::datum::pi * coords[i] * 0.5; + } + else + { + theta = 0.25 * (1.0 + 2.0 * coords[i] * G) / (1.0 + G); + } + value = value * std::cos(theta); + } + + if(stop < maf.GetI()) + { + theta = arma::datum::pi * coords[stop] * 0.5; + } + else + { + theta = 0.25 * (1.0 + 2.0 * coords[stop] * G) / (1.0 + G); + } + + if (stop != maf.GetNumObjectives() - 1) + { + value = value * std::sin(theta); + } + + value = value * (1.0 + 100 * G); + return value; + } + + MAF6& maf; + size_t stop; + }; + + // Return back a tuple of objective functions. + std::tuple GetObjectives() + { + return std::make_tuple(objectiveF1, objectiveF2, objectiveF3); + } + + MAF6Objective objectiveF1; + MAF6Objective objectiveF2; + MAF6Objective objectiveF3; + }; + } //namespace test + } //namespace ens + +#endif diff --git a/inst/include/ensmallen_bits/problems/problems.hpp b/inst/include/ensmallen_bits/problems/problems.hpp index dbcb042..ad2a9a2 100644 --- a/inst/include/ensmallen_bits/problems/problems.hpp +++ b/inst/include/ensmallen_bits/problems/problems.hpp @@ -47,5 +47,18 @@ #include "zdt/zdt3_function.hpp" #include "zdt/zdt4_function.hpp" #include "zdt/zdt6_function.hpp" +#include "dtlz/dtlz1_function.hpp" +#include "dtlz/dtlz2_function.hpp" +#include "dtlz/dtlz3_function.hpp" +#include "dtlz/dtlz4_function.hpp" +#include "dtlz/dtlz5_function.hpp" +#include "dtlz/dtlz6_function.hpp" +#include "dtlz/dtlz7_function.hpp" +#include "maf/maf1_function.hpp" +#include "maf/maf2_function.hpp" +#include "maf/maf3_function.hpp" +#include "maf/maf4_function.hpp" +#include "maf/maf5_function.hpp" +#include "maf/maf6_function.hpp" #endif diff --git a/inst/include/ensmallen_bits/utility/arma_traits.hpp b/inst/include/ensmallen_bits/utility/arma_traits.hpp index a2cc81e..0555ea5 100644 --- a/inst/include/ensmallen_bits/utility/arma_traits.hpp +++ b/inst/include/ensmallen_bits/utility/arma_traits.hpp @@ -98,26 +98,17 @@ struct IsArmaType > const static bool value = true; }; +template +struct IsArmaType > +{ + const static bool value = true; +}; -#if ((ARMA_VERSION_MAJOR >= 10) || \ - ((ARMA_VERSION_MAJOR == 9) && (ARMA_VERSION_MINOR >= 869))) - - // Armadillo 9.869+ has SpSubview_col and SpSubview_row - - template - struct IsArmaType > - { - const static bool value = true; - }; - - template - struct IsArmaType > - { - const static bool value = true; - }; - -#endif - +template +struct IsArmaType > +{ + const static bool value = true; +}; // template<> template diff --git a/inst/include/ensmallen_bits/utility/indicators/igd.hpp b/inst/include/ensmallen_bits/utility/indicators/igd.hpp new file mode 100644 index 0000000..3adf1d7 --- /dev/null +++ b/inst/include/ensmallen_bits/utility/indicators/igd.hpp @@ -0,0 +1,95 @@ +/** + * @file igd.hpp + * @author Satyam Shukla + * + * Inverse Generational Distance Plus (IGD) indicator. + * + * 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_INDICATORS_IGD_HPP +#define ENSMALLEN_INDICATORS_IGD_HPP + +namespace ens { + +/** + * The inverted generational distance( IGD) is a metric for assessing the quality + * of approximations to the Pareto front obtained by multi-objective optimization + * algorithms.The IGD indicator returns the average distance from each point in + * the reference front to the nearest point to it's solution. + * + * \f[ d(z,a) = \sqrt{\sum_{i = 1}^{n}(a_i - z_i)^2 \ } \ + * \f] + * + * For more information see: + * + * @code + * @inproceedings{coello2004study, + * title={A study of the parallelization of a coevolutionary multi-objective evolutionary algorithm}, + * author={Coello Coello, Carlos A and Reyes Sierra, Margarita}, + * booktitle={MICAI 2004: Advances in Artificial Intelligence: Third Mexican International Conference on Artificial Intelligence, Mexico City, Mexico, April 26-30, 2004. Proceedings 3}, + * pages={688--697}, + * year={2004}, + * organization={Springer} + * } + * @endcode + */ + class IGD + { + public: + /** + * Default constructor does nothing, but is required to satisfy the Indicator + * policy. + */ + IGD() { } + + /** + * Find the IGD value of the front with respect to the given reference + * front. + * + * @tparam CubeType The cube data type of front. + * @param front The given approximation front. + * @param referenceFront The given reference front. + * @param p The power constant in the distance formula. + * @return The IGD value of the front. + */ + template + static typename CubeType::elem_type Evaluate(const CubeType& front, + const CubeType& referenceFront, + double p) + { + // Convenience typedefs. + typedef typename CubeType::elem_type ElemType; + ElemType igd = 0; + for (size_t i = 0; i < referenceFront.n_slices; i++) + { + ElemType min = std::numeric_limits::max(); + for (size_t j = 0; j < front.n_slices; j++) + { + ElemType dist = 0; + for (size_t k = 0; k < front.slice(j).n_rows; k++) + { + ElemType z = referenceFront(k, 0, i); + ElemType a = front(k, 0, j); + // Assuming minimization of all objectives. + //! IGD does not clip negative differences to 0 + dist += std::pow(a - z, 2); + } + dist = std::sqrt(dist); + if (dist < min) + min = dist; + } + igd += std::pow(min,p); + } + igd /= referenceFront.n_slices; + igd = std::pow(igd, 1.0 / p); + return igd; + } + }; + +} // namespace ens + +#endif \ No newline at end of file diff --git a/revdep/README.md b/revdep/README.md index bcef618..7dc2156 100644 --- a/revdep/README.md +++ b/revdep/README.md @@ -1,24 +1,30 @@ # Platform -|field |value | -|:--------|:----------------------------------------| -|version |R version 4.3.1 (2023-06-16) | -|os |macOS Sonoma 14.0 | -|system |aarch64, darwin20 | -|ui |RStudio | -|language |(EN) | -|collate |en_US.UTF-8 | -|ctype |en_US.UTF-8 | -|tz |US/Pacific | -|date |2023-10-05 | -|rstudio |2023.09.0+463 Desert Sunflower (desktop) | -|pandoc |NA | +|field |value | +|:--------|:------------------------------------------| +|version |R version 4.4.2 (2024-10-31) | +|os |macOS Sequoia 15.1.1 | +|system |aarch64, darwin20 | +|ui |RStudio | +|language |(EN) | +|collate |en_US.UTF-8 | +|ctype |en_US.UTF-8 | +|tz |America/Chicago | +|date |2024-11-30 | +|rstudio |2024.09.1+394 Cranberry Hibiscus (desktop) | +|pandoc |3.5 @ /opt/homebrew/bin/pandoc | # Dependencies |package |old |new |Δ | |:-------------|:----------|:----------|:--| -|RcppEnsmallen |0.2.19.0.1 |0.2.20.0.1 |* | +|RcppEnsmallen |0.2.21.1.1 |0.2.22.0.1 |* | # Revdeps +## Failed to check (1) + +|package |version |error |warning |note | +|:--------|:-------|:------|:-------|:----| +|[sparsevb](failures.md#sparsevb)|0.1.0 |__+1__ | |-1 | + diff --git a/revdep/cran.md b/revdep/cran.md index fdf1792..353f5aa 100644 --- a/revdep/cran.md +++ b/revdep/cran.md @@ -1,7 +1,12 @@ ## revdepcheck results -We checked 5 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package. +We checked 6 reverse dependencies, comparing R CMD check results across CRAN and dev versions of this package. * We saw 0 new problems - * We failed to check 0 packages + * We failed to check 1 packages +Issues with CRAN packages are summarised below. + +### Failed to check + +* sparsevb (NA) diff --git a/revdep/failures.md b/revdep/failures.md index 9a20736..8bcc287 100644 --- a/revdep/failures.md +++ b/revdep/failures.md @@ -1 +1,81 @@ -*Wow, no problems at all. :)* \ No newline at end of file +# sparsevb + +
+ +* Version: 0.1.0 +* GitHub: NA +* Source code: https://github.com/cran/sparsevb +* Date/Publication: 2021-01-15 09:20:02 UTC +* Number of recursive dependencies: 18 + +Run `revdepcheck::revdep_details(, "sparsevb")` for more info + +
+ +## Newly broken + +* checking whether package ‘sparsevb’ can be installed ... ERROR + ``` + Installation failed. + See ‘/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/checks.noindex/sparsevb/new/sparsevb.Rcheck/00install.out’ for details. + ``` + +## Newly fixed + +* checking C++ specification ... NOTE + ``` + Specified C++11: please drop specification unless essential + ``` + +## Installation + +### Devel + +``` +* installing *source* package ‘sparsevb’ ... +** package ‘sparsevb’ successfully unpacked and MD5 sums checked +** using staged installation +** libs +using C++ compiler: ‘Apple clang version 16.0.0 (clang-1600.0.26.4)’ +using C++11 +using SDK: ‘MacOSX15.1.sdk’ +clang++ -arch arm64 -std=gnu++11 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/Rcpp/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/RcppArmadillo/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/RcppEnsmallen/new/RcppEnsmallen/include' -I/opt/R/arm64/include -fPIC -falign-functions=64 -Wall -g -O2 -c RcppExports.cpp -o RcppExports.o +In file included from RcppExports.cpp:5: +In file included from /Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/RcppEnsmallen/new/RcppEnsmallen/include/RcppEnsmallen.h:25: +/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/RcppEnsmallen/new/RcppEnsmallen/include/ensmallen.hpp:32:4: error: "*** C++14 compiler required; enable C++14 mode in your compiler, or use an earlier version of ensmallen" + 32 | #error "*** C++14 compiler required; enable C++14 mode in your compiler, or use an earlier version of ensmallen" + | ^ +1 error generated. +make: *** [RcppExports.o] Error 1 +ERROR: compilation failed for package ‘sparsevb’ +* removing ‘/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/checks.noindex/sparsevb/new/sparsevb.Rcheck/sparsevb’ + + +``` +### CRAN + +``` +* installing *source* package ‘sparsevb’ ... +** package ‘sparsevb’ successfully unpacked and MD5 sums checked +** using staged installation +** libs +using C++ compiler: ‘Apple clang version 16.0.0 (clang-1600.0.26.4)’ +using C++11 +using SDK: ‘MacOSX15.1.sdk’ +clang++ -arch arm64 -std=gnu++11 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/Rcpp/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/RcppArmadillo/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/RcppEnsmallen/old/RcppEnsmallen/include' -I/opt/R/arm64/include -fPIC -falign-functions=64 -Wall -g -O2 -c RcppExports.cpp -o RcppExports.o +clang++ -arch arm64 -std=gnu++11 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/Rcpp/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/RcppArmadillo/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/RcppEnsmallen/old/RcppEnsmallen/include' -I/opt/R/arm64/include -fPIC -falign-functions=64 -Wall -g -O2 -c fit_linear.cpp -o fit_linear.o +clang++ -arch arm64 -std=gnu++11 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/Rcpp/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/RcppArmadillo/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/RcppEnsmallen/old/RcppEnsmallen/include' -I/opt/R/arm64/include -fPIC -falign-functions=64 -Wall -g -O2 -c fit_logit.cpp -o fit_logit.o +... +** R +** byte-compile and prepare package for lazy loading +** help +*** installing help indices +** building package indices +** testing if installed package can be loaded from temporary location +** checking absolute paths in shared objects and dynamic libraries +** testing if installed package can be loaded from final location +** testing if installed package keeps a record of temporary installation path +* DONE (sparsevb) + + +``` diff --git a/revdep/problems.md b/revdep/problems.md index 9a20736..8bcc287 100644 --- a/revdep/problems.md +++ b/revdep/problems.md @@ -1 +1,81 @@ -*Wow, no problems at all. :)* \ No newline at end of file +# sparsevb + +
+ +* Version: 0.1.0 +* GitHub: NA +* Source code: https://github.com/cran/sparsevb +* Date/Publication: 2021-01-15 09:20:02 UTC +* Number of recursive dependencies: 18 + +Run `revdepcheck::revdep_details(, "sparsevb")` for more info + +
+ +## Newly broken + +* checking whether package ‘sparsevb’ can be installed ... ERROR + ``` + Installation failed. + See ‘/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/checks.noindex/sparsevb/new/sparsevb.Rcheck/00install.out’ for details. + ``` + +## Newly fixed + +* checking C++ specification ... NOTE + ``` + Specified C++11: please drop specification unless essential + ``` + +## Installation + +### Devel + +``` +* installing *source* package ‘sparsevb’ ... +** package ‘sparsevb’ successfully unpacked and MD5 sums checked +** using staged installation +** libs +using C++ compiler: ‘Apple clang version 16.0.0 (clang-1600.0.26.4)’ +using C++11 +using SDK: ‘MacOSX15.1.sdk’ +clang++ -arch arm64 -std=gnu++11 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/Rcpp/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/RcppArmadillo/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/RcppEnsmallen/new/RcppEnsmallen/include' -I/opt/R/arm64/include -fPIC -falign-functions=64 -Wall -g -O2 -c RcppExports.cpp -o RcppExports.o +In file included from RcppExports.cpp:5: +In file included from /Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/RcppEnsmallen/new/RcppEnsmallen/include/RcppEnsmallen.h:25: +/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/RcppEnsmallen/new/RcppEnsmallen/include/ensmallen.hpp:32:4: error: "*** C++14 compiler required; enable C++14 mode in your compiler, or use an earlier version of ensmallen" + 32 | #error "*** C++14 compiler required; enable C++14 mode in your compiler, or use an earlier version of ensmallen" + | ^ +1 error generated. +make: *** [RcppExports.o] Error 1 +ERROR: compilation failed for package ‘sparsevb’ +* removing ‘/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/checks.noindex/sparsevb/new/sparsevb.Rcheck/sparsevb’ + + +``` +### CRAN + +``` +* installing *source* package ‘sparsevb’ ... +** package ‘sparsevb’ successfully unpacked and MD5 sums checked +** using staged installation +** libs +using C++ compiler: ‘Apple clang version 16.0.0 (clang-1600.0.26.4)’ +using C++11 +using SDK: ‘MacOSX15.1.sdk’ +clang++ -arch arm64 -std=gnu++11 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/Rcpp/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/RcppArmadillo/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/RcppEnsmallen/old/RcppEnsmallen/include' -I/opt/R/arm64/include -fPIC -falign-functions=64 -Wall -g -O2 -c RcppExports.cpp -o RcppExports.o +clang++ -arch arm64 -std=gnu++11 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/Rcpp/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/RcppArmadillo/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/RcppEnsmallen/old/RcppEnsmallen/include' -I/opt/R/arm64/include -fPIC -falign-functions=64 -Wall -g -O2 -c fit_linear.cpp -o fit_linear.o +clang++ -arch arm64 -std=gnu++11 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/Rcpp/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/sparsevb/RcppArmadillo/include' -I'/Users/ronin/Documents/GitHub/r-pkg/rcppensmallen/revdep/library.noindex/RcppEnsmallen/old/RcppEnsmallen/include' -I/opt/R/arm64/include -fPIC -falign-functions=64 -Wall -g -O2 -c fit_logit.cpp -o fit_logit.o +... +** R +** byte-compile and prepare package for lazy loading +** help +*** installing help indices +** building package indices +** testing if installed package can be loaded from temporary location +** checking absolute paths in shared objects and dynamic libraries +** testing if installed package can be loaded from final location +** testing if installed package keeps a record of temporary installation path +* DONE (sparsevb) + + +``` diff --git a/tools/HISTORYold.md b/tools/HISTORYold.md index 537b6ad..9b53459 100644 --- a/tools/HISTORYold.md +++ b/tools/HISTORYold.md @@ -1,3 +1,18 @@ +### ensmallen 2.22.0: "E-Bike Excitement" +###### 2024-11-29 + * Update to C++14 standard + ([#400](https://github.com/mlpack/ensmallen/pull/400)). + + * Bump minimum Armadillo version to 10.8 + ([#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)). + + * 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 * Fix numerical precision issues for small-gradient L-BFGS scaling factor