diff --git a/NAMESPACE b/NAMESPACE index 8b35d48..c13a73a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -11,6 +11,7 @@ S3method(print,ecr_optimization_task) S3method(print,ecr_result) export(approximateIdealPoint) export(approximateNadirPoint) +export(asemoa) export(computeAverageHausdorffDistance) export(computeCrowdingDistance) export(computeDistanceFromPointToSetOfPoints) diff --git a/R/emoa.as-emoa.R b/R/emoa.as-emoa.R new file mode 100644 index 0000000..41ce641 --- /dev/null +++ b/R/emoa.as-emoa.R @@ -0,0 +1,161 @@ +#' @title +#' Implementation of the NSGA-II EMOA algorithm by Deb. +#' +#' @description +#' The AS-EMOA, short for aspiration set evolutionary multi-objective algorithm, +#' aims to incorporate expert knowledge into multi-objective optimization [1]. +#' The algorithm expects an aspiration set, i.e., a set of reference points. It +#' then creates an appriximation of the pareto front close to the aspiration set +#' utilizing the average Hausdorff distance. +#' +#' @note +#' This is a pure R implementation of the NSGA-II algorithm. It hides the regular +#' \pkg{ecr} interface and offers a more R like interface while still being quite +#' adaptable. +#' +#' @references Rudolph, G., Schuetze, S., Grimme, C., Trautmann, H: An Aspiration Set +#' EMOA Based on Averaged Hausdorff Distances. LION 2014: 153-156. +#' +#' @param task [\code{ecr_optimization_task} | \code{smoof_function}]\cr +#' Optimization task or objective function of type \code{smoof_function}. +#' @param n.population [\code{integer(1)}]\cr +#' Population size. Default is \code{100}. +#' @param n.offspring [\code{integer(1)}]\cr +#' Offspring size, i.e., number of individuals generated by variation operators +#' in each iteration. Default is \code{n.population}. +#' @param aspiration.set [\code{matrix}]\cr +#' The aspiration set. Each column contains one point of the set. +#' @param n.archive [\code{integer(1)}]\cr +#' Size of the pareto archive, i.e., the number of nondominated points which we +#' aim to generate. Default is \code{ncol(aspiration.set)}. +#' @template arg_parent_selector +#' @template arg_mutator +#' @template arg_recombinator +#' @param max.iter [\code{integer(1)}]\cr +#' Maximal number of iterations. Default ist \code{100L}. +#' @param max.evals [\code{integer(1)}]\cr +#' Maximal number of iterations/generations. Default is \code{Inf}. +#' @param max.time [\code{integer(1)}]\cr +#' Time budget in seconds. Default ist \code{Inf}. +#' @return [\code{ecr_ecr_multi_objective_result}] +#' @export +asemoa = function( + task, + n.population = 100L, n.offspring = n.population, + aspiration.set = NULL, + n.archive, + parent.selector = makeSimpleSelector(), + mutator = makeGaussMutator(), + recombinator = makeCrossoverRecombinator(), + max.iter = 100L, + max.evals = NULL, + max.time = NULL) { + + if (isSmoofFunction(task)) { + task = makeOptimizationTask(task) + } + assertMatrix(aspiration.set, mode = "numeric", any.missing = FALSE, all.missing = FALSE, min.rows = 2L) + if (nrow(aspiration.set) != task$n.objectives) { + stopf("AS-EMAO: Dimension of the aspiration set needs to be equal to the number of objectives, + but %i <> %i.", nrow(aspiration.set), task$n.objectives) + } + if (is.null(n.archive)) { + n.archive = ncol(aspiration.set) + } + assertInt(n.archive, na.ok = FALSE, lower = 2L) + + # This is the main selection mechanism of the AS-EMOA. + # Remove the point which leads to highest + deltaOneUpdate = function(set, aspiration.set) { + # here we need to apply this strange information. See the reference for details + # yeah, I could use range here but it is more readable this way + min1 = min(aspiration.set[1L, ]) + min2 = min(aspiration.set[2L, ]) + max1 = max(aspiration.set[1L, ]) + max2 = max(aspiration.set[2L, ]) + + # transform + set[1L, ] = (set[1L, ] - min1) / (max2 - min2) + set[2L, ] = (set[2L, ] - min2) / (max1 - min1) + + return(computeAverageHausdorffDistance(set, aspiration.set)) + } + + # Implementation of surival selection operator of the AS-EMOA algorithm. + asemoaSelector = makeSelector( + selector = function(population, storage, n.select, control) { + fitness = population$fitness + population = population$individuals + + # filter nondominated points + nondom.idx = which.nondominated(fitness) + population = population[nondom.idx] + fitness = fitness[, nondom.idx, drop = FALSE] + + n.archive = control$n.archive + # if maximal number of individuals is not exceeded yet + # simply return + if (length(population) <= n.archive) { + return(makePopulation(population, fitness)) + } + + # Otherwise we need to do the computationally more expensive part + hausdorffDistances = lapply(seq(length(population)), function(idx) { + deltaOneUpdate(fitness[, -idx, drop = FALSE], control$aspiration.set) + }) + + #FIXME: here we need to check if there are multiple elements with this distance + tmp = getMinIndex(hausdorffDistances) + + return(makePopulation(population[-tmp], fitness[, -tmp, drop = FALSE])) + }, + supported.objectives = "multi-objective", + name = "AS-EMOA selector", + description = "Selection takes place based on (modified) average Hausdorff metric" + ) + + asemoaGenerator = makeGenerator( + generator = function(size, control) { + uniformGenerator = makeUniformGenerator() + population = uniformGenerator(size, control) + #NOTE: here we use the objective function to compute the fitness values + fitness = computeFitness(population, task$fitness.fun) + # now filter out dominated solutions + nondom.idx = which.nondominated(fitness) + population$individuals = population$individuals[nondom.idx] + return(population) + }, + name = "AS-EMOA generator", + description = "Generates uniformaly and reduces to non-dominated set", + supported = "float" + ) + + # AS-EMOA control object + ctrl = setupECRControl( + n.population = n.population, + n.offspring = n.offspring, + representation = "float", + monitor = makeConsoleMonitor(), + stopping.conditions = list( + makeMaximumEvaluationsStoppingCondition(max.evals), + makeMaximumTimeStoppingCondition(max.time), + makeMaximumIterationsStoppingCondition(max.iter) + ) + ) + + ctrl = setupEvolutionaryOperators( + ctrl, + parent.selector = parent.selector, + recombinator = recombinator, + generator = asemoaGenerator, + mutator = mutator, + survival.selector = asemoaSelector + ) + + #FIXME: this is rather ugly. We simply add some more args to the control object + # without sanity checks and stuff like that. + ctrl$n.archive = n.archive + ctrl$aspiration.set = aspiration.set + + return(doTheEvolution(task, ctrl)) +} diff --git a/man/asemoa.Rd b/man/asemoa.Rd new file mode 100644 index 0000000..356282b --- /dev/null +++ b/man/asemoa.Rd @@ -0,0 +1,68 @@ +% Generated by roxygen2 (4.1.1): do not edit by hand +% Please edit documentation in R/emoa.as-emoa.R +\name{asemoa} +\alias{asemoa} +\title{Implementation of the NSGA-II EMOA algorithm by Deb.} +\usage{ +asemoa(task, n.population = 100L, n.offspring = n.population, + aspiration.set = NULL, n.archive, parent.selector = makeSimpleSelector(), + mutator = makeGaussMutator(), recombinator = makeCrossoverRecombinator(), + max.iter = 100L, max.evals = NULL, max.time = NULL) +} +\arguments{ +\item{task}{[\code{ecr_optimization_task} | \code{smoof_function}]\cr +Optimization task or objective function of type \code{smoof_function}.} + +\item{n.population}{[\code{integer(1)}]\cr +Population size. Default is \code{100}.} + +\item{n.offspring}{[\code{integer(1)}]\cr +Offspring size, i.e., number of individuals generated by variation operators +in each iteration. Default is \code{n.population}.} + +\item{aspiration.set}{[\code{matrix}]\cr +The aspiration set. Each column contains one point of the set.} + +\item{n.archive}{[\code{integer(1)}]\cr +Size of the pareto archive, i.e., the number of nondominated points which we +aim to generate. Default is \code{ncol(aspiration.set)}.} + +\item{parent.selector}{[\code{ecr_selector}]\cr +Selection operator which implements a procedure to copy individuals from a +given population to the mating pool, i. e., allow them to become parents.} + +\item{mutator}{[\code{ecr_mutator}]\cr +Mutation operator of type \code{ecr_mutator}.} + +\item{recombinator}{[\code{ecr_recombinator}]\cr +Recombination operator of type \code{ecr_recombinator}.} + +\item{max.iter}{[\code{integer(1)}]\cr +Maximal number of iterations. Default ist \code{100L}.} + +\item{max.evals}{[\code{integer(1)}]\cr +Maximal number of iterations/generations. Default is \code{Inf}.} + +\item{max.time}{[\code{integer(1)}]\cr +Time budget in seconds. Default ist \code{Inf}.} +} +\value{ +[\code{ecr_ecr_multi_objective_result}] +} +\description{ +The AS-EMOA, short for aspiration set evolutionary multi-objective algorithm, + aims to incorporate expert knowledge into multi-objective optimization [1]. + The algorithm expects an aspiration set, i.e., a set of reference points. It + then creates an appriximation of the pareto front close to the aspiration set + utilizing the average Hausdorff distance. +} +\note{ +This is a pure R implementation of the NSGA-II algorithm. It hides the regular + \pkg{ecr} interface and offers a more R like interface while still being quite + adaptable. +} +\references{ +Rudolph, G., Schuetze, S., Grimme, C., Trautmann, H: An Aspiration Set + EMOA Based on Averaged Hausdorff Distances. LION 2014: 153-156. +} +