Skip to content

Commit

Permalink
add alternative, more R like interface to ecr
Browse files Browse the repository at this point in the history
  • Loading branch information
jakobbossek committed May 24, 2016
1 parent 5e9e569 commit 3a340d9
Show file tree
Hide file tree
Showing 16 changed files with 214 additions and 56 deletions.
12 changes: 2 additions & 10 deletions R/doTheEvolution.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,8 @@
#'
#' @template arg_optimization_task
#' @template arg_control
#' @param initial.population [\code{list}]\cr
#' List of individuals which should be placed in the initial population.
#' The function will stop with an error message if the number of passed individuals
#' is larger than \code{control$n.population}. If the number of passed individuals
#' is lower than \code{control$n.population}, the population will be filled up
#' by individuals generated by the corresponding generator.
#' Default is \code{NULL}, i.e., the entire population is generated by the
#' population generator.
#' @param more.args [\code{list}]\cr
#' Additional arguments passed to objective function.
#' @template arg_initial_population
#' @template arg_more_args
#' @return [\code{\link{ecr_result}}]
#'
#' @example examples/ex_doTheEvolution.R
Expand Down
125 changes: 125 additions & 0 deletions R/ecr.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#' @title
#' Interface to \pkg{ecr} similar to the \code{\link[stats]{optim}} function.
#'
#' @description
#' The most flexible way to setup evolutionary algorithms with \pkg{ecr} is by
#' explicitely generating a task and a control object and passing both to
#' \code{\link{doTheEvolution}}. Although this approach is highly flexible
#' and very readable it requires quite a lot of code. However, in everyday
#' life R users frequently need to optimize a single-objective R function.
#' The \code{ecr} function thus provides a more R like interface for single
#' objective optimization similar to the interface of the \code{\link[stats]{optim}}
#' function.
#'
#' @note
#' This helper function is applicable for single-objective optimization based
#' on default encodings, i.e., binary, float and permutation, only.
#' If your function at hand has multiple objectives or you need special
#' encodings and operators you need to work with \code{\link{doTheEvolution}}
#' directly.
#'
#' @keywords optimize
#'
#' @seealso \code{\link{setupECRControl}} for building the control object,
#' \code{\link{makeOptimizationTask}} to define an optimization problem and
#' \code{\link{doTheEvolution}} for the main working horse of \pkg{ecr}.
#'
#' @param obj.fun [\code{function}]\cr
#' The single-objective target function. Can be any R function which takes a
#' single vector as input and returns a scalar value describing the vectors
#' fitness.
#' @param n.dim [\code{integer(1)}]\cr
#' Dimension of the decision space.
#' @param lower [\code{numeric}]\cr
#' Vector of minimal values for each parameter of the decision space in case
#' of float or permutation encoding.
#' @param upper [\code{numeric}]\cr
#' Vector of maximal values for each parameter of the decision space in case
#' of float or permutation encoding.
#' @param n.bits [\code{integer(1)}]\cr
#' Number of bits to use for binary representation.
#' @template arg_representation
#' @template arg_n_population
#' @template arg_n_offspring
#' @template arg_n_mating_pool
#' @template arg_survival_strategy
#' @template arg_n_elite
#' @template arg_vectorized_evaluation
#' @template arg_custom_constants
#' @template arg_logger
#' @template arg_monitor
#' @template arg_max_iter
#' @template arg_max_evals
#' @template arg_max_time
#' @template arg_more_args
#' @template arg_parent_selector
#' @template arg_survival_selector
#' @template arg_generator
#' @template arg_mutator
#' @template arg_recombinator
#' @return [\code{\link{ecr_result}}]
#' @example
#' fn = function(x) {
#' sum(x^2)
#' }
#'
#' res = ecr(fn, n.dim = 2L, lower = c(-5, -5), upper = c(5, 5),
#' representation = "float", n.population = 20L, n.offspring = 10L, max.iter = 30L)
#' @export
ecr = function(
obj.fun, n.dim, lower = NULL, upper = NULL, n.bits,
representation, n.population, n.offspring, n.mating.pool = floor(n.population / 2),
survival.strategy = "plus", n.elite = 0L, vectorized.evaluation = FALSE,
custom.constants = list(), logger = NULL, monitor = setupConsoleMonitor(),
max.iter = 100L, max.evals = NULL, max.time = NULL,
more.args = list(), initial.population = NULL,
parent.selector = getDefaultEvolutionaryOperators(representation, "parent.selector"),
survival.selector = getDefaultEvolutionaryOperators(representation, "survival.selector"),
generator = getDefaultEvolutionaryOperators(representation, "generator"),
mutator = getDefaultEvolutionaryOperators(representation, "mutator"),
recombinator = getDefaultEvolutionaryOperators(representation, "recombinator")) {

# simply pass stuff down to control object constructor
control = setupECRControl(
n.population = n.population,
n.offspring = n.offspring,
n.mating.pool = n.mating.pool,
n.elite = n.elite,
survival.strategy = survival.strategy,
representation = representation,
custom.constants = custom.constants,
logger = logger,
monitor = monitor,
vectorized.evaluation = vectorized.evaluation,
list(
setupMaximumIterationsTerminator(max.iter = max.iter),
setupMaximumEvaluationsTerminator(max.evals),
setupMaximumTimeTerminator(max.time)
)
)

control = setupEvolutionaryOperators(
control,
parent.selector = parent.selector,
survival.selector = survival.selector,
generator = generator,
mutator = mutator,
recombinator = recombinator
)

# now build the task
# We distinguish in binary, float and permutation here
par.set = if (representation == "float") {
if (length(lower) != n.dim | length(upper) != n.dim) {
stopf("Lower and upper bounds need to be of length %i!", n.dim)
}
makeNumericParamSet("x", lower = lower, upper = upper, len = n.dim)
} else if (representation == "binary") {
makeParamSet(makeIntegerVectorParam("x", lower = 0, upper = 1, len = n.dim))
} else if (representation == "permutation") {
makeNumericParamSet("x", lower = 1, upper = n.dim, len = n.dim)
}
smoof.fun = makeSingleObjectiveFunction(fn = obj.fun, par.set = par.set, name = "ECR")

return(doTheEvolution(smoof.fun, control, initial.population, more.args))
}
55 changes: 11 additions & 44 deletions R/setupECRControl.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,23 @@
#'
#' @description
#' The ecr package offers a framework for evolutionary computing and therefore offers
#' a lot of customization options. The control object is a simple but powerful
#' a lot of customization options. The control object is a simple
#' wrapper for all these options and sets convenient defaults.
#'
#' @param n.population [\code{integer(1)}]\cr
#' Number of individuals in the population.
#' @param n.offspring [\code{integer(1)}]\cr
#' Number of individuals generated in each generation.
#' @param n.mating.pool [\code{integer(1)}]\cr
#' Number of individuals which can potentially participate in the
#' generation of offspring.
#' Default is half of the population size.
#' @param representation [\code{character(1)}]\cr
#' Genotype representation of the parameters. Available are \dQuote{binary},
#' \dQuote{float}, \dQuote{permutation} and \dQuote{custom}.
#' @param survival.strategy [\code{character(1)}]\cr
#' Determines the survival strategy used by the EA. Possible are \dQuote{plus} for
#' a classical (mu + lambda) strategy and \dQuote{comma} for (mu, lambda).
#' Default is \dQuote{plus}.
#' @param n.elite [\code{integer(1)}]\cr
#' Number of fittest individuals of the current generation that shall be copied to the
#' next generation without changing. Keep in mind, that the algorithm
#' does not care about this option if the \code{survival.strategy} is set to 'plus'.
#' Default is 0.
#' @param monitor [\code{function}]\cr
#' Monitoring function.
#' Default is \code{NULL}, i.e. no monitoring.
#' @template arg_n_population
#' @template arg_n_offspring
#' @template arg_n_mating_pool
#' @template arg_representation
#' @template arg_survival_strategy
#' @template arg_n_elite
#' @template arg_monitor
#' @param stopping.conditions [\code{list}]\cr
#' List of functions of type \code{ecr_terminator}. At least one stopping
#' condition needs to be passed.
#' Default is the empty list.
#' @param logger [\code{function}]\cr
#' Monitoring object used to log stuff.
#' Default is \code{NULL} which means no logging at all.
#' See \code{\link{setupOptPathLoggingMonitor}} for ecr's build-in logger.
#' @param custom.constants [\code{list}]\cr
#' Additional constants which should be available to all generators and operators.
#' Defaults to empty list.
#' @param vectorized.evaluation [\code{logical(1L)}]\cr
#' Is the fitness/objective function vectorized? I.e., does the fitness function accept
#' a list? This allows for faster execution or parallelization by hand.
#' If \code{TRUE} the following destinction on the type of the objective function is made:
#' \describe{
#' \item{Is \code{smoof_function}}{If the objective function is of type \code{smoof_function} from package \pkg{smoof}
#' and the smoof function is vectorized, the population - which is a list internally -
#' is reduced to a matrix and passed to the smoof function (vectorization in smoof
#' is allowed for continuous functions only).}
#' \item{Is not a \code{smoof_function}}{In this case the individuals of
#' the population are passed entirely as a list to the objective function.}
#' }
#' Default is \code{FALSE}.
#' @template arg_logger
#' @template arg_custom_constants
#' @template arg_vectorized_evaluation
#' @return
#' S3 object of type \code{ecr_control}.
#' @export
Expand Down
3 changes: 3 additions & 0 deletions man-roxygen/arg_custom_constants.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#' @param custom.constants [\code{list}]\cr
#' Additional constants which should be available to all generators and operators.
#' Defaults to empty list.
8 changes: 8 additions & 0 deletions man-roxygen/arg_initial_population.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#' @param initial.population [\code{list}]\cr
#' List of individuals which should be placed in the initial population.
#' The function will stop with an error message if the number of passed individuals
#' is larger than \code{control$n.population}. If the number of passed individuals
#' is lower than \code{control$n.population}, the population will be filled up
#' by individuals generated by the corresponding generator.
#' Default is \code{NULL}, i.e., the entire population is generated by the
#' population generator.
4 changes: 4 additions & 0 deletions man-roxygen/arg_logger.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#' @param logger [\code{function}]\cr
#' Monitoring object used to log stuff.
#' Default is \code{NULL} which means no logging at all.
#' See \code{\link{setupOptPathLoggingMonitor}} for ecr's build-in logger.
4 changes: 2 additions & 2 deletions man-roxygen/arg_monitor.R
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#' @param monitor [\code{ecr_monitor}]\cr
#' @param monitor [\code{function}]\cr
#' Monitoring function.
#' Default is the console monitor (see \code{\link{setupConsoleMonitor}}).
#' Default is \code{NULL}, i.e. no monitoring.
2 changes: 2 additions & 0 deletions man-roxygen/arg_more_args.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#' @param more.args [\code{list}]\cr
#' Additional arguments passed to objective function.
5 changes: 5 additions & 0 deletions man-roxygen/arg_n_elite.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#' @param n.elite [\code{integer(1)}]\cr
#' Number of fittest individuals of the current generation that shall be copied to the
#' next generation without changing. Keep in mind, that the algorithm
#' does not care about this option if the \code{survival.strategy} is set to 'plus'.
#' Default is 0.
4 changes: 4 additions & 0 deletions man-roxygen/arg_n_mating_pool.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#' @param n.mating.pool [\code{integer(1)}]\cr
#' Number of individuals which can potentially participate in the
#' generation of offspring.
#' Default is half of the population size.
2 changes: 2 additions & 0 deletions man-roxygen/arg_n_offspring.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#' @param n.offspring [\code{integer(1)}]\cr
#' Number of individuals generated in each generation.
2 changes: 2 additions & 0 deletions man-roxygen/arg_n_population.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#' @param n.population [\code{integer(1)}]\cr
#' Number of individuals in the population.
3 changes: 3 additions & 0 deletions man-roxygen/arg_representation.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#' @param representation [\code{character(1)}]\cr
#' Genotype representation of the parameters. Available are \dQuote{binary},
#' \dQuote{float}, \dQuote{permutation} and \dQuote{custom}.
4 changes: 4 additions & 0 deletions man-roxygen/arg_survival_strategy.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#' @param survival.strategy [\code{character(1)}]\cr
#' Determines the survival strategy used by the EA. Possible are \dQuote{plus} for
#' a classical (mu + lambda) strategy and \dQuote{comma} for (mu, lambda).
#' Default is \dQuote{plus}.
13 changes: 13 additions & 0 deletions man-roxygen/arg_vectorized_evaluation.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#' @param vectorized.evaluation [\code{logical(1L)}]\cr
#' Is the fitness/objective function vectorized? I.e., does the fitness function accept
#' a list? This allows for faster execution or parallelization by hand.
#' If \code{TRUE} the following destinction on the type of the objective function is made:
#' \describe{
#' \item{Is \code{smoof_function}}{If the objective function is of type \code{smoof_function} from package \pkg{smoof}
#' and the smoof function is vectorized, the population - which is a list internally -
#' is reduced to a matrix and passed to the smoof function (vectorization in smoof
#' is allowed for continuous functions only).}
#' \item{Is not a \code{smoof_function}}{In this case the individuals of
#' the population are passed entirely as a list to the objective function.}
#' }
#' Default is \code{FALSE}.
24 changes: 24 additions & 0 deletions tests/testthat/test_ecr.R
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,27 @@ test_that("ecr can handle initial populations", {
# stop if initial population is to large
expect_error(doTheEvolution(fn, control, c(initial.population, c(2, 2.5))), "exceeds", ignore.case = TRUE)
})

test_that("ecr(...) shortcut function works as expected for floating point representation", {
fn = function(x) {
sum(x^2)
}

res = ecr(fn, n.dim = 2L, lower = c(-5, -5), upper = c(5, 5),
representation = "float", n.population = 20L, n.offspring = 10L, max.iter = 30L,
monitor = NULL)
expect_true(abs(res$best.value) < 0.01)
expect_true(all(res$best.param < 0.01))
})

test_that("ecr(...) shortcut function works as expected for binary point representation", {
fn = function(x) {
sum(x)
}

res = ecr(fn, n.dim = 15L, n.bits = 15L,
representation = "binary", n.population = 20L, n.offspring = 10L, max.iter = 30L,
monitor = NULL)
expect_true(res$best.value == 0)
expect_true(all(res$best.param == 0))
})

0 comments on commit 3a340d9

Please sign in to comment.