From c882194254060c6fc31fd1d65eb7fe8a4f5b8ea2 Mon Sep 17 00:00:00 2001 From: Jakob Bossek Date: Sat, 23 Jan 2016 01:22:23 +0100 Subject: [PATCH] add (1+1) genetic algorithm --- NAMESPACE | 1 + R/ea.oneplusoneGA.R | 82 +++++++++++++++++++++++++++++++++++++ man/onePlusOneGA.Rd | 50 ++++++++++++++++++++++ tests/testthat/helper_zzz.R | 2 +- tests/testthat/test_ea.R | 15 ++++++- 5 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 R/ea.oneplusoneGA.R create mode 100644 man/onePlusOneGA.Rd diff --git a/NAMESPACE b/NAMESPACE index 10c9a66..798cce5 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -76,6 +76,7 @@ export(makeTournamentSelector) export(makeUniformGenerator) export(makeUniformMutator) export(nsga2) +export(onePlusOneGA) export(rescalePoints) export(setupECRControl) export(setupEvolutionaryOperators) diff --git a/R/ea.oneplusoneGA.R b/R/ea.oneplusoneGA.R new file mode 100644 index 0000000..ce9defd --- /dev/null +++ b/R/ea.oneplusoneGA.R @@ -0,0 +1,82 @@ +#' @title +#' Simple (1 + 1) Genetic Algorithm. +#' +#' @description +#' The simplest evolutionary algorithm one can imagine, namely the (1+1) EA/GA. +#' Maintains a population of a single individual x and uses just bitplip mutation +#' to generate a child y (obviously no recombination takes place), i.e., each gene +#' of x is flipped with probability \code{p} independently. The best individual +#' survives. +#' This algorithm is of particular interest in the theory of evolutionary algorithms +#' and its performance is well understood for different function families. +#' A lot of interesting results exist. +#' +#' @note This helper function hides the regular \pkg{ecr} interface and offers a more +#' R like interface to a simple evolutionary algorithm which works on binary valued +#' vectors. +#' +#' @keywords optimize +#' +#' @template arg_optimization_task +#' @param p [\code{numeric(1)}]\cr +#' Mutation probability for bitplip mutation. +#' Default is \eqn{1/n} where n is the length of the gene. +#' @template arg_max_iter +#' @template arg_max_evals +#' @template arg_max_time +#' @param ... [any]\cr +#' Further arguments passed to \code{\link{setupECRControl}}. +#' @return [\code{ecr_single_objective_result}] +#' @export +onePlusOneGA = function( + task, + p = NULL, + max.iter = NULL, + max.evals = NULL, + max.time = NULL, ...) { + + if (isSmoofFunction(task)) { + task = makeOptimizationTask(task) + } + assertClass(task, "ecr_optimization_task") + if (!isSmoofFunction(task$fitness.fun)) { + stopf("Objective fun needs to be of type smoof_function.") + } + if (!isNumeric(task$par.set, include.int = FALSE)) { + stopf("(1+1)-GA works for numeric functions only.") + } + + if (!is.null(p)) { + assertNumber(p, lower = 0, upper = 1, na.ok = FALSE) + } + + # get lengths of genome to determine best theoretical mutation probability 1/n + par.set = task$par.set + n = getParamLengths(par.set) + + # control object + ctrl = setupECRControl( + n.population = 1L, + n.mating.pool = 1L, + n.offspring = 1L, + survival.strategy = "plus", + representation = "binary", + stopping.conditions = list( + makeMaximumEvaluationsStoppingCondition(max.evals), + makeMaximumTimeStoppingCondition(max.time), + makeMaximumIterationsStoppingCondition(max.iter) + ), + ... + ) + + # operator setup + ctrl = setupEvolutionaryOperators( + ctrl, + parent.selector = makeSimpleSelector(), + recombinator = makeNullRecombinator(), # no recombination at all + mutator = makeBitFlipMutator(p = coalesce(p, 1.0 / n)), + survival.selector = makeGreedySelector() + ) + + return(doTheEvolution(task, ctrl)) +} diff --git a/man/onePlusOneGA.Rd b/man/onePlusOneGA.Rd new file mode 100644 index 0000000..dd24442 --- /dev/null +++ b/man/onePlusOneGA.Rd @@ -0,0 +1,50 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ea.oneplusoneGA.R +\name{onePlusOneGA} +\alias{onePlusOneGA} +\title{Simple (1 + 1) Genetic Algorithm.} +\usage{ +onePlusOneGA(task, p = NULL, max.iter = NULL, max.evals = NULL, + max.time = NULL, ...) +} +\arguments{ +\item{task}{[\code{ecr_optimization_task}]\cr +Optimization task. If a \code{smoof_function} is passed it is automatically +converted into a task.} + +\item{p}{[\code{numeric(1)}]\cr +Mutation probability for bitplip mutation. +Default is \eqn{1/n} where n is the length of the gene.} + +\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}.} + +\item{...}{[any]\cr +Further arguments passed to \code{\link{setupECRControl}}.} +} +\value{ +[\code{ecr_single_objective_result}] +} +\description{ +The simplest evolutionary algorithm one can imagine, namely the (1+1) EA/GA. +Maintains a population of a single individual x and uses just bitplip mutation +to generate a child y (obviously no recombination takes place), i.e., each gene +of x is flipped with probability \code{p} independently. The best individual +survives. +This algorithm is of particular interest in the theory of evolutionary algorithms +and its performance is well understood for different function families. +A lot of interesting results exist. +} +\note{ +This helper function hides the regular \pkg{ecr} interface and offers a more +R like interface to a simple evolutionary algorithm which works on binary valued +vectors. +} +\keyword{optimize} + diff --git a/tests/testthat/helper_zzz.R b/tests/testthat/helper_zzz.R index 86d2e85..e76e2c6 100644 --- a/tests/testthat/helper_zzz.R +++ b/tests/testthat/helper_zzz.R @@ -1,4 +1,4 @@ -set.seed(1234) +set.seed(1) makeOneMinFunction = function(dimensions) { assertInteger(dimensions, len = 1L, lower = 2L, upper = 100L) diff --git a/tests/testthat/test_ea.R b/tests/testthat/test_ea.R index a778e86..e10e1b6 100644 --- a/tests/testthat/test_ea.R +++ b/tests/testthat/test_ea.R @@ -1,8 +1,21 @@ context("Single-Objective Algorithms") -test_that("preimplemented EAs work well", { +test_that("simpleEA work well", { fn = makeSphereFunction(2L) res = simpleEA(fn, n.population = 30L, max.iter = 30L, monitor = makeNullMonitor()) expect_is(res, "ecr_single_objective_result") expect_true(abs(res$best.value - getGlobalOptimum(fn)$value) < 0.1) }) + +test_that("(1+1) GA works well", { + gene.length = 10L + fn = makeSingleObjectiveFunction( + name = "One-Max", + fn = function(x) length(x) - sum(x), + par.set = makeNumericParamSet("bin", lower = 0, upper = 1, len = gene.length) + ) + res = onePlusOneGA(fn, max.iter = 100L, monitor = makeNullMonitor()) + expect_is(res, "ecr_single_objective_result") + expect_true(res$best.value == 0) + expect_equal(sum(res$best.param), gene.length) +})