Title: | Genetic Algorithm and Particle Swarm Optimization |
---|---|
Description: | Implements genetic algorithm and particle swarm algorithm for real-valued functions. Various modifications (including hybridization and elitism) of these algorithms are provided. Implemented functions are based on ideas described in S. Katoch, S. Chauhan, V. Kumar (2020) <doi:10.1007/s11042-020-10139-6> and M. Clerc (2012) <https://hal.archives-ouvertes.fr/hal-00764996>. |
Authors: | Bogdan Potanin [aut, cre, ctb] |
Maintainer: | Bogdan Potanin <[email protected]> |
License: | GPL (>= 2) |
Version: | 1.0.0 |
Built: | 2024-11-08 05:13:08 UTC |
Source: | https://github.com/cran/gena |
This function allows to use genetic algorithm for numeric global optimization of real-valued functions.
gena( fn, gr = NULL, lower, upper, pop.n = 100, pop.initial = NULL, pop.method = "uniform", mating.method = "rank", mating.par = NULL, mating.self = FALSE, crossover.method = "local", crossover.par = NULL, crossover.prob = 0.8, mutation.method = "constant", mutation.par = NULL, mutation.prob = 0.2, mutation.genes.prob = 1/length(lower), elite.n = min(10, 2 * round(pop.n/20)), elite.duplicates = FALSE, hybrid.method = "rank", hybrid.par = 2, hybrid.prob = 0, hybrid.opt.par = NULL, hybrid.n = 1, constr.method = NULL, constr.par = NULL, maxiter = 100, is.max = TRUE, info = TRUE, ... )
gena( fn, gr = NULL, lower, upper, pop.n = 100, pop.initial = NULL, pop.method = "uniform", mating.method = "rank", mating.par = NULL, mating.self = FALSE, crossover.method = "local", crossover.par = NULL, crossover.prob = 0.8, mutation.method = "constant", mutation.par = NULL, mutation.prob = 0.2, mutation.genes.prob = 1/length(lower), elite.n = min(10, 2 * round(pop.n/20)), elite.duplicates = FALSE, hybrid.method = "rank", hybrid.par = 2, hybrid.prob = 0, hybrid.opt.par = NULL, hybrid.n = 1, constr.method = NULL, constr.par = NULL, maxiter = 100, is.max = TRUE, info = TRUE, ... )
fn |
function to be maximized i.e. fitness function. |
gr |
gradient of the |
lower |
lower bound of the search space. |
upper |
upper bound of the search space. |
pop.n |
integer representing the size of the population. |
pop.initial |
numeric matrix which rows are chromosomes to be included into the initial population. Numeric vector will be coerced to single row matrix. |
pop.method |
the algorithm to be applied for a creation of the initial population. See 'Details' for additional information. |
mating.method |
the algorithm to be applied for a mating i.e. selection of parents. See 'Details' for additional information. |
mating.par |
parameters of the mating (selection) algorithm. |
mating.self |
logical; if |
crossover.method |
an algorithm to be applied for crossover i.e. creation of the children. See 'Details' for additional information. |
crossover.par |
parameters of the crossover algorithm. |
crossover.prob |
probability of the crossover for each pair of parents. |
mutation.method |
algorithm to be applied for mutation i.e. random change in some genes of the children. See 'Details' for additional information. |
mutation.par |
parameters of the mutation algorithm. |
mutation.prob |
mutation probability for the chromosomes. |
mutation.genes.prob |
mutation probability for the genes. |
elite.n |
number of the elite children i.e. those which have the highest function value and will be preserved for the next population. |
elite.duplicates |
logical; if |
hybrid.method |
hybrids selection algorithm i.e. mechanism determining which chromosomes should be subject to local optimization. See 'Details' for additional information. |
hybrid.par |
parameters of the hybridization algorithm. |
hybrid.prob |
probability of generating the hybrids each iteration. |
hybrid.opt.par |
parameters of the local optimization function
to be used for hybridization algorithm (including |
hybrid.n |
number of hybrids that appear if hybridization should take place during the iteration. |
constr.method |
the algorithm to be applied for imposing constraints on the chromosomes. See 'Details' for additional information. |
constr.par |
parameters of the constraint algorithm. |
maxiter |
maximum number of iterations of the algorithm. |
is.max |
logical; if |
info |
logical; if |
... |
additional parameters to be passed to
|
To find information on particular methods available via
pop.method
,
mating.method
, crossover.method
, mutation.method
,
hybrid.method
and constr.method
arguments please see 'Details' section of
gena.population
, gena.crossover
,
gena.mutation
, gena.hybrid
and gena.constr
correspondingly. For example to
find information on possible values of mutation.method
and
mutation.par
arguments see description of method
and
par
arguments of gena.mutation
function.
It is possible to provide manually implemented functions for
population initialization, mating, crossover, mutation and hybridization.
For example manual mutation function may be provided through
mutation.method
argument. It should have the same signature
(arguments) as gena.mutation
function and return
the same object i.e. the matrix of chromosomes of the appropriate size.
Manually implemented functions for other operators (crossover, mating
and so on) may be provided in a similar way.
By default function does not impose any constraints upon the parameters.
If constr.method = "bounds"
then lower
and upper
constraints will be imposed. Lower bounds should be strictly smaller
then upper bounds.
Currently the only available termination condition is maxiter
. We
are going to provide some additional termination conditions during
future updates.
Infinite values in lower
and upper
are substituted with
-(.Machine$double.xmax * 0.9)
and .Machine$double.xmax * 0.9
correspondingly.
By default if gr
is provided then BFGS algorithm will be used inside
optim
during hybridization.
Otherwise Nelder-Mead
will be used.
Manual values for optim
arguments may be provided
(as a list) through hybrid.opt.par
argument.
Arguments pop.n
and elite.n
should be even integers and
elite.n
should be greater then 2. If these arguments are odd integers
then they will be coerced to even integers by adding 1.
Also pop.n
should be greater then elite.n
at least by 2.
For more information on the genetic algorithm please see Katoch et. al. (2020).
This function returns an object of class gena
that is a list
containing the following elements:
par
- chromosome (solution) with the highest fitness
(objective function) value.
value
- value of fn
at par
.
population
- matrix of chromosomes (solutions) of the
last iteration of the algorithm.
counts
- a two-element integer vector giving the number of
calls to fn
and gr
respectively.
is.max
- identical to is.max
input argument.
fitness.history
- vector which i-th element is fitness
of the best chromosome in i-th iteration.
iter
- last iteration number.
S. Katoch, S. Chauhan, V. Kumar (2020). A review on genetic algorithm: past, present, and future. Multimedia Tools and Applications, 80, 8091-8126. <doi:10.1007/s11042-020-10139-6>
## Consider Ackley function fn <- function(par, a = 20, b = 0.2) { val <- a * exp(-b * sqrt(0.5 * (par[1] ^ 2 + par[2] ^ 2))) + exp(0.5 * (cos(2 * pi * par[1]) + cos(2 * pi * par[2]))) - exp(1) - a return(val) } # Maximize this function using classical # genetic algorithm setup set.seed(123) lower <- c(-5, -100) upper <- c(100, 5) opt <- gena(fn = fn, lower = lower, upper = upper, hybrid.prob = 0, a = 20, b = 0.2) print(opt$par) # Replicate optimization using hybridization opt <- gena(fn = fn, lower = lower, upper = upper, hybrid.prob = 0.2, a = 20, b = 0.2) print(opt$par) ## Consider Rosenbrock function fn <- function(par, a = 100) { val <- -(a * (par[2] - par[1] ^ 2) ^ 2 + (1 - par[1]) ^ 2 + a * (par[3] - par[2] ^ 2) ^ 2 + (1 - par[2]) ^ 2) return(val) } # Apply genetic algorithm lower <- rep(-10, 3) upper <- rep(10, 3) set.seed(123) opt <- gena(fn = fn, lower = lower, upper = upper, a = 100) print(opt$par) # Improve the results by hybridization opt <- gena(fn = fn, lower = lower, upper = upper, hybrid.prob = 0.2, a = 100) print(opt$par) # Provide manually implemented mutation function # which simply randomly sorts genes. # Note that this function should have the same # arguments as gena.mutation. mutation.my <- function(children, lower, upper, prob, prob.genes, method, par, iter) { # Get dimensional data children.n <- nrow(children) genes.n <- ncol(children) # Select chromosomes that should mutate random_values <- runif(children.n, 0, 1) mutation_ind <- which(random_values <= prob) # Mutate chromosomes by randomly sorting # their genes for (i in mutation_ind) { children[i, ] <- children[i, sample(1:genes.n)] } # Return mutated chromosomes return(children) } opt <- gena(fn = fn, lower = lower, upper = upper, mutation.method = mutation.my, a = 100) print(opt$par)
## Consider Ackley function fn <- function(par, a = 20, b = 0.2) { val <- a * exp(-b * sqrt(0.5 * (par[1] ^ 2 + par[2] ^ 2))) + exp(0.5 * (cos(2 * pi * par[1]) + cos(2 * pi * par[2]))) - exp(1) - a return(val) } # Maximize this function using classical # genetic algorithm setup set.seed(123) lower <- c(-5, -100) upper <- c(100, 5) opt <- gena(fn = fn, lower = lower, upper = upper, hybrid.prob = 0, a = 20, b = 0.2) print(opt$par) # Replicate optimization using hybridization opt <- gena(fn = fn, lower = lower, upper = upper, hybrid.prob = 0.2, a = 20, b = 0.2) print(opt$par) ## Consider Rosenbrock function fn <- function(par, a = 100) { val <- -(a * (par[2] - par[1] ^ 2) ^ 2 + (1 - par[1]) ^ 2 + a * (par[3] - par[2] ^ 2) ^ 2 + (1 - par[2]) ^ 2) return(val) } # Apply genetic algorithm lower <- rep(-10, 3) upper <- rep(10, 3) set.seed(123) opt <- gena(fn = fn, lower = lower, upper = upper, a = 100) print(opt$par) # Improve the results by hybridization opt <- gena(fn = fn, lower = lower, upper = upper, hybrid.prob = 0.2, a = 100) print(opt$par) # Provide manually implemented mutation function # which simply randomly sorts genes. # Note that this function should have the same # arguments as gena.mutation. mutation.my <- function(children, lower, upper, prob, prob.genes, method, par, iter) { # Get dimensional data children.n <- nrow(children) genes.n <- ncol(children) # Select chromosomes that should mutate random_values <- runif(children.n, 0, 1) mutation_ind <- which(random_values <= prob) # Mutate chromosomes by randomly sorting # their genes for (i in mutation_ind) { children[i, ] <- children[i, sample(1:genes.n)] } # Return mutated chromosomes return(children) } opt <- gena(fn = fn, lower = lower, upper = upper, mutation.method = mutation.my, a = 100) print(opt$par)
Impose constraints on chromosomes.
gena.constr(population, method = "bounds", par, iter)
gena.constr(population, method = "bounds", par, iter)
population |
numeric matrix which rows are chromosomes i.e. vectors of parameters values. |
method |
method used to impose constraints. |
par |
additional parameters to be passed depending on the |
iter |
iteration number of the genetic algorithm. |
If method = "bounds"
then chromosomes will be bounded
between par$lower
and par$upper
.
The function returns a list with the following elements:
population
- matrix which rows are chromosomes after
constraints have been imposed.
constr.ind
- matrix of logical values which (i, j)-th
elements equals TRUE
(FALSE
otherwise) if j-th jene of
i-th chromosome is a subject to constraint.
# Randomly initialize population set.seed(123) population <- gena.population(pop.n = 10, lower = c(-5, -5), upper = c(5, 5)) # Impose lower and upper bounds constraints pop.constr <- gena.constr(population, method = "bounds", par = list(lower = c(-1, 2), upper = c(1, 5))) print(pop.constr)
# Randomly initialize population set.seed(123) population <- gena.population(pop.n = 10, lower = c(-5, -5), upper = c(5, 5)) # Impose lower and upper bounds constraints pop.constr <- gena.constr(population, method = "bounds", par = list(lower = c(-1, 2), upper = c(1, 5))) print(pop.constr)
Crossover method (algorithm) to be used in the genetic algorithm.
gena.crossover( parents, fitness = NULL, prob = 0.8, method = "local", par = NULL, iter = NULL )
gena.crossover( parents, fitness = NULL, prob = 0.8, method = "local", par = NULL, iter = NULL )
parents |
numeric matrix which rows are parents i.e. vectors of parameters values. |
fitness |
numeric vector which |
prob |
probability of crossover. |
method |
crossover method to be used for making children. |
par |
additional parameters to be passed depending on the |
iter |
iteration number of the genetic algorithm. |
Denote parents
by which
i
-th row
parents[i, ]
is a chromosome i.e. the vector of
parameter values of the function being optimized
that is
provided via
fn
argument of gena
.
The elements of chromosome are genes
representing parameters values.
Crossover algorithm determines the way parents produce children.
During crossover each of randomly selected pairs of parents
,
produce two children
,
,
where
is odd. Each pair of parents is selected with
probability
prob
. If pair of parents have not been selected
for crossover then corresponding children and parents are coincide i.e.
and
.
Argument method
determines particular crossover algorithm to
be applied. Denote by the vector of parameters used by the
algorithm. Note that
corresponds to
par
.
If method = "split"
then each gene of the first child will
be equiprobably picked from the first or from the second parent. So
may be equal to
or
with equal probability. The second
child is the reversal of the first one in a sense that if the first child
gets particular gene of the first (second) parent then the second child gets
this gene from the first (second) parent i.e. if
then
; if
then
.
If method = "arithmetic"
then:
where is
par[1]
. By default par[1] = 0.5
.
If method = "local"
then the procedure is the same as
for "arithmetic" method but is a uniform random
value between 0 and 1.
If method = "flat"
then is a uniform
random number between
and
.
Similarly for the second child
.
For more information on crossover algorithms please see Kora, Yadlapalli (2017).
The function returns a matrix which rows are children.
P. Kora, P. Yadlapalli. (2017). Crossover Operators in Genetic Algorithms: A Review. International Journal of Computer Applications, 162 (10), 34-36, <doi:10.5120/ijca2017913370>.
# Randomly initialize the parents set.seed(123) parents.n <- 10 parents <- gena.population(pop.n = parents.n, lower = c(-5, -5), upper = c(5, 5)) # Perform the crossover children <- gena.crossover(parents = parents, prob = 0.6, method = "local") print(children)
# Randomly initialize the parents set.seed(123) parents.n <- 10 parents <- gena.population(pop.n = parents.n, lower = c(-5, -5), upper = c(5, 5)) # Perform the crossover children <- gena.crossover(parents = parents, prob = 0.6, method = "local") print(children)
Hybridization method (algorithm) to be used in the genetic algorithm.
gena.hybrid( population, fitness, hybrid.n = 1, method, par, opt.par, info = FALSE, iter = NULL, ... )
gena.hybrid( population, fitness, hybrid.n = 1, method, par, opt.par, info = FALSE, iter = NULL, ... )
population |
numeric matrix which rows are chromosomes i.e. vectors of parameters values. |
fitness |
numeric vector which |
hybrid.n |
positive integer representing the number of hybrids. |
method |
hybridization method to improve chromosomes via local search. |
par |
additional parameters to be passed depending on the |
opt.par |
parameters of the local optimization function
to be used for hybridization algorithm (including |
info |
logical; if |
iter |
iteration number of the genetic algorithm. |
... |
additional parameters to be passed to
|
This function uses gena.mating
function to
select hybrids. Therefore method
and par
arguments will
be passed to this function. If some chromosomes selected to become hybrids
are duplicated then these duplicates will not be subject to local
optimization i.e. the number of hybrids will be decreased by the number
of duplicates (actual number of hybrids during some iterations may be
lower than hybrid.n
).
Currently optim
is the only available local
optimizer. Therefore opt.par
is a list containing parameters
that should be passed to optim
.
For more information on hybridization please see El-mihoub et. al. (2006).
The function returns a list with the following elements:
population
- matrix which rows are chromosomes including hybrids.
fitness
- vector which i-th element is the fitness of the
i-th chromosome.
hybrids.ind
- vector of indexes of chromosomes selected for
hybridization.
counts
a two-element integer vector giving the number of
calls to fn
and gr
respectively.
T. El-mihoub, A. Hopgood, L. Nolle, B. Alan (2006). Hybrid Genetic Algorithms: A Review. Engineering Letters, 13 (3), 124-137.
# Consider the following fitness function fn <- function(x) { val <- x[1] * x[2] - x[1] ^ 2 - x[2] ^ 2 } # Also let's provide it's gradient (optional) gr <- function(x) { val <- c(x[2] - 2 * x[1], x[1] - 2 * x[2]) } # Randomly initialize the population set.seed(123) n_population <- 10 population <- gena.population(pop.n = n_population, lower = c(-5, -5), upper = c(5, 5)) # Calculate fitness of each chromosome fitness <- rep(NA, n_population) for(i in 1:n_population) { fitness[i] <- fn(population[i, ]) } # Perform hybridization hybrids <- gena.hybrid(population = population, fitness = fitness, opt.par = list(fn = fn, gr = gr, method = "BFGS", control = list(fnscale = -1, abstol = 1e-10, reltol = 1e-10, maxit = 1000)), hybrid.n = 2, method = "rank", par = 0.8) print(hybrids)
# Consider the following fitness function fn <- function(x) { val <- x[1] * x[2] - x[1] ^ 2 - x[2] ^ 2 } # Also let's provide it's gradient (optional) gr <- function(x) { val <- c(x[2] - 2 * x[1], x[1] - 2 * x[2]) } # Randomly initialize the population set.seed(123) n_population <- 10 population <- gena.population(pop.n = n_population, lower = c(-5, -5), upper = c(5, 5)) # Calculate fitness of each chromosome fitness <- rep(NA, n_population) for(i in 1:n_population) { fitness[i] <- fn(population[i, ]) } # Perform hybridization hybrids <- gena.hybrid(population = population, fitness = fitness, opt.par = list(fn = fn, gr = gr, method = "BFGS", control = list(fnscale = -1, abstol = 1e-10, reltol = 1e-10, maxit = 1000)), hybrid.n = 2, method = "rank", par = 0.8) print(hybrids)
Mating (selection) method (algorithm) to be used in the genetic algorithm.
gena.mating( population, fitness, parents.n, method = "rank", par = NULL, self = FALSE, iter = NULL )
gena.mating( population, fitness, parents.n, method = "rank", par = NULL, self = FALSE, iter = NULL )
population |
numeric matrix which rows are chromosomes i.e. vectors of parameters values. |
fitness |
numeric vector which |
parents.n |
even positive integer representing the number of parents. |
method |
mating method to be used for selection of parents. |
par |
additional parameters to be passed depending on the |
self |
logical; if |
iter |
iteration number of the genetic algorithm. |
Denote population
by which
i
-th row
population[i, ]
is a chromosome i.e. the vector of
parameter values of the function being optimized
that is
provided via
fn
argument of gena
.
The elements of chromosome are genes representing parameters
values. Argument
fitness
is a vector of function values at
corresponding chromosomes i.e. fitness[i]
corresponds to
. Total number of chromosomes in population
equals to
nrow(population)
.
Mating algorithm determines selection of chromosomes that will become parents.
During mating each iteration one of chromosomes become a parent until
there are (i.e.
parents.n
) parents selected.
Each chromosome may become a parent multiple times or not become a
parent at all.
Denote by the
-th of selected parents. Parents
and
form a pair that will further
produce a child (offspring), where
is odd.
If
self = FALSE
then for each pair of parents
it is insured that
except the case when there are several
identical chromosomes in population. However
self
is ignored
if method
is "tournament"
, so in this case self-mating
is always possible.
Denote by the probability of a chromosome to become a parent.
Remind that each chromosome may become a parent multiple times.
Probability
is a function
of fitness
. Usually this function is non-decreasing so
more fitted chromosomes have higher probability of becoming a parent.
There is also an intermediate value
called weight such that:
Therefore all weights are proportional to corresponding
probabilities
by the same factor (sum of weights).
Argument method
determines particular mating algorithm to be applied.
Denote by the vector of parameters used by the algorithm.
Note that
corresponds to
par
. The algorithm determines
a particular form of the function which
in turn determines
.
If method = "constant"
then all weights and probabilities are equal:
If method = "rank"
then each chromosome receives a rank
based on the fitness
value. So if
j
-th chromosome is the
fittest one and k
-th chromosome has the lowest fitness value then
and
. The relationship
between weight
and rank
is as follows:
The greater value of the greater portion of probability will
be delivered to more fitted chromosomes.
Default value is
so
par = 0.5
.
If method = "fitness"
then weights are calculated as follows:
By default and
i.e.
par = c(10, 0.5)
. There is a restriction
insuring that expression in brackets is non-negative.
If method = "tournament"
then (i.e.
par
)
chromosomes will be randomly selected with equal probabilities and without
replacement. Then the chromosome with the highest fitness
(among these selected chromosomes) value will become a parent.
It is possible to provide representation of this algorithm via
probabilities but the formulas are numerically unstable.
By default
par = min(5, ceiling(parents.n * 0.1))
.
Validation and default values assignment for par
is performed inside
gena
function not in gena.mating
.
It allows to perform validation a single time instead of repeating it
each iteration of genetic algorithm.
For more information on mating (selection) algorithms please see Shukla et. al. (2015).
The function returns a list with the following elements:
parents
- matrix which rows are parents. The number of
rows of this matrix equals to parents.n
while the number of columns
is ncol(population)
.
fitness
- vector which i-th element is the fitness of the
i-th parent.
ind
- vector which i-th element is the index of i-th
parent in population so $parents[i, ]
equals to
population[ind[i], ]
.
A. Shukla, H. Pandey, D. Mehrotra (2015). Comparative review of selection techniques in genetic algorithm. 2015 International Conference on Futuristic Trends on Computational Analysis and Knowledge Management (ABLAZE), 515-519, <doi:10.1109/ABLAZE.2015.7154916>.
# Consider the following fitness function fn <- function(x) { val <- x[1] * x[2] - x[1] ^ 2 - x[2] ^ 2 } # Randomly initialize the population set.seed(123) pop.nulation <- 10 population <- gena.population(pop.n = pop.nulation, lower = c(-5, -5), upper = c(5, 5)) # Calculate fitness of each chromosome fitness <- rep(NA, pop.nulation) for(i in 1:pop.nulation) { fitness[i] <- fn(population[i, ]) } # Perform mating to select parents parents <- gena.mating(population = population, fitness = fitness, parents.n = pop.nulation, method = "rank", par = 0.8) print(parents)
# Consider the following fitness function fn <- function(x) { val <- x[1] * x[2] - x[1] ^ 2 - x[2] ^ 2 } # Randomly initialize the population set.seed(123) pop.nulation <- 10 population <- gena.population(pop.n = pop.nulation, lower = c(-5, -5), upper = c(5, 5)) # Calculate fitness of each chromosome fitness <- rep(NA, pop.nulation) for(i in 1:pop.nulation) { fitness[i] <- fn(population[i, ]) } # Perform mating to select parents parents <- gena.mating(population = population, fitness = fitness, parents.n = pop.nulation, method = "rank", par = 0.8) print(parents)
Mutation method (algorithm) to be used in the genetic algorithm.
gena.mutation( children, lower, upper, prob = 0.2, prob.genes = 1/nrow(children), method = "constant", par = 1, iter = NULL )
gena.mutation( children, lower, upper, prob = 0.2, prob.genes = 1/nrow(children), method = "constant", par = 1, iter = NULL )
children |
numeric matrix which rows are children i.e. vectors of parameters values. |
lower |
lower bound of the search space. |
upper |
upper bound of the search space. |
prob |
probability of mutation for a child. |
prob.genes |
numeric vector or numeric value representing the probability of mutation of a child's gene. See 'Details'. |
method |
mutation method to be used for transforming genes of children. |
par |
additional parameters to be passed depending on the |
iter |
iteration number of the genetic algorithm. |
Denote children
by which
i
-th row
children[i, ]
is a chromosome i.e. the vector of
parameter values of the function being optimized
that is
provided via
fn
argument of gena
.
The elements of chromosome are genes
representing parameters values.
Mutation algorithm determines random transformation of children's genes.
Each child may be selected for mutation with probability prob
.
If -th child is selected for mutation and
prob.genes
is a
vector then -th gene of this child
is transformed with probability
prob.genes[j]
. If prob.genes
is a constant then this probability is the same for all genes.
Argument method
determines particular mutation algorithm to
be applied. Denote by the vector of parameters used by the
algorithm. Note that
corresponds to
par
.
Also let's denote by the value of
gene
after mutation.
If method = "constant"
then
is a uniform random variable between
lower[j]
and upper[j]
.
If method = "normal"
then
equals to the sum of
and normal random variable
with zero mean and standard deviation
par[j]
.
By default par
is identity vector of length ncol(children)
so par[j] = 1
for all j
.
If method = "percent"
then is generated
from
by equiprobably increasing or decreasing it
by
percent,
where
is a uniform random variable between
and
par[j]
.
Note that par
may also be a constant then all
genes have the same maximum possible percentage change.
By default par = 20
.
For more information on mutation algorithms please see Patil, Bhende (2014).
The function returns a matrix which rows are children (after mutation has been applied to some of them).
S. Patil, M. Bhende. (2014). Comparison and Analysis of Different Mutation Strategies to improve the Performance of Genetic Algorithm. International Journal of Computer Science and Information Technologies, 5 (3), 4669-4673.
# Randomly initialize some children set.seed(123) children.n <- 10 children <- gena.population(pop.n = children.n, lower = c(-5, -5), upper = c(5, 5)) # Perform the mutation mutants <- gena.mutation(children = children, prob = 0.6, prob.genes = c(0.7, 0.8), par = 30, method = "percent") print(mutants)
# Randomly initialize some children set.seed(123) children.n <- 10 children <- gena.population(pop.n = children.n, lower = c(-5, -5), upper = c(5, 5)) # Perform the mutation mutants <- gena.mutation(children = children, prob = 0.6, prob.genes = c(0.7, 0.8), par = 30, method = "percent") print(mutants)
Initialize the population of chromosomes.
gena.population(pop.n, lower, upper, pop.initial = NULL, method = "uniform")
gena.population(pop.n, lower, upper, pop.initial = NULL, method = "uniform")
pop.n |
positive integer representing the number of chromosomes in population. |
lower |
numeric vector which i-th element determines the minimum possible value for i-th gene. |
upper |
numeric vector which i-th element determines the maximum possible value for i-th gene. |
pop.initial |
numeric matrix which rows are initial chromosomes suggested by user. |
method |
string representing the initialization method to be used. For a list of possible values see Details. |
If "method = uniform"
then i-th gene of each chromosome is randomly
(uniformly) chosen between lower[i]
and upper[i]
bounds. If
"method = normal"
then i-th gene is generated from a truncated
normal distribution with mean (upper[i] + lower[i]) / 2
and
standard deviation (upper[i] - lower[i]) / 6
where lower[i]
and upper[i]
are lower and upper truncation bounds correspondingly.
If "method = hypersphere"
then population is simulated uniformly
from the hypersphere with center upper - lower
and radius
sqrt(sum((upper - lower) ^ 2))
via rhypersphere
function setting type = "inside"
.
This function returns a matrix which rows are chromosomes.
B. Kazimipour, X. Li, A. Qin (2014). A review of population initialization techniques for evolutionary algorithms. 2014 IEEE Congress on Evolutionary Computation, 2585-2592, <doi:10.1109/CEC.2014.6900618>.
set.seed(123) gena.population(pop.n = 10, lower = c(-1, -2, -3), upper = c(1, 0, -1), pop.initial = rbind(c(0, -1, -2), c(0.1, -1.2, -2.3)), method = "normal")
set.seed(123) gena.population(pop.n = 10, lower = c(-1, -2, -3), upper = c(1, 0, -1), pop.initial = rbind(c(0, -1, -2), c(0.1, -1.2, -2.3)), method = "normal")
Numeric estimation of the gradient and Hessian.
gena.grad( fn, par, eps = sqrt(.Machine$double.eps) * abs(par), method = "central-difference", fn.args = NULL ) gena.hessian( fn = NULL, gr = NULL, par, eps = sqrt(.Machine$double.eps) * abs(par), fn.args = NULL, gr.args = NULL )
gena.grad( fn, par, eps = sqrt(.Machine$double.eps) * abs(par), method = "central-difference", fn.args = NULL ) gena.hessian( fn = NULL, gr = NULL, par, eps = sqrt(.Machine$double.eps) * abs(par), fn.args = NULL, gr.args = NULL )
fn |
function for which gradient or Hessian should be calculated. |
par |
point (parameters' value) at which |
eps |
numeric vector representing increment of the |
method |
numeric differentiation method: "central-difference" or "forward-difference". |
fn.args |
list containing arguments of |
gr |
gradient function of |
gr.args |
list containing arguments of |
It is possible to substantially improve numeric Hessian accuracy
by using analytical gradient gr
. If both fn
and gr
are provided then only gr
will be used. If only fn
is provided
for gena.hessian
then eps
will be transformed to
sqrt(eps)
for numeric stability purposes.
Function gena.grad
returns a vector that is a gradient of
fn
at point par
calculated via method
numeric
differentiation approach using increment eps
.
Function gena.hessian
returns a matrix that is a Hessian of
fn
at point par
.
# Consider the following function fn <- function(par, a = 1, b = 2) { val <- par[1] * par[2] - a * par[1] ^ 2 - b * par[2] ^ 2 } # Calculate the gradient at point (2, 5) respect to 'par' # when 'a = 1' and 'b = 1' par <- c(2, 5) fn.args = list(a = 1, b = 1) gena.grad(fn = fn, par = par, fn.args = fn.args) # Calculate Hessian at the same point gena.hessian(fn = fn, par = par, fn.args = fn.args) # Repeat calculation of the Hessian using analytical gradient gr <- function(par, a = 1, b = 2) { val <- c(par[2] - 2 * a * par[1], par[1] - 2 * b * par[2]) } gena.hessian(gr = gr, par = par, gr.args = fn.args)
# Consider the following function fn <- function(par, a = 1, b = 2) { val <- par[1] * par[2] - a * par[1] ^ 2 - b * par[2] ^ 2 } # Calculate the gradient at point (2, 5) respect to 'par' # when 'a = 1' and 'b = 1' par <- c(2, 5) fn.args = list(a = 1, b = 1) gena.grad(fn = fn, par = par, fn.args = fn.args) # Calculate Hessian at the same point gena.hessian(fn = fn, par = par, fn.args = fn.args) # Repeat calculation of the Hessian using analytical gradient gr <- function(par, a = 1, b = 2) { val <- c(par[2] - 2 * a * par[1], par[1] - 2 * b * par[2]) } gena.hessian(gr = gr, par = par, gr.args = fn.args)
Plot best found fitnesses during genetic algorithm
## S3 method for class 'gena' plot(x, y = NULL, ...)
## S3 method for class 'gena' plot(x, y = NULL, ...)
x |
Object of class "gena" |
y |
this parameter currently ignored |
... |
further arguments (currently ignored) |
This function does not return anything.
Plot best found fitnesses during genetic algorithm
## S3 method for class 'pso' plot(x, y = NULL, ...)
## S3 method for class 'pso' plot(x, y = NULL, ...)
x |
Object of class "pso" |
y |
this parameter currently ignored |
... |
further arguments (currently ignored) |
This function does not return anything.
Print method for "gena" object
## S3 method for class 'gena' print(x, ...)
## S3 method for class 'gena' print(x, ...)
x |
Object of class "gena" |
... |
further arguments (currently ignored) |
This function does not return anything.
Print method for "pso" object
## S3 method for class 'pso' print(x, ...)
## S3 method for class 'pso' print(x, ...)
x |
Object of class "pso" |
... |
further arguments (currently ignored) |
This function does not return anything.
Summary for "gena" object
## S3 method for class 'summary.gena' print(x, ...)
## S3 method for class 'summary.gena' print(x, ...)
x |
Object of class "gena" |
... |
further arguments (currently ignored) |
This function returns x
input argument.
Summary for "pso" object
## S3 method for class 'summary.pso' print(x, ...)
## S3 method for class 'summary.pso' print(x, ...)
x |
Object of class "pso" |
... |
further arguments (currently ignored) |
This function returns x
input argument.
This function allows to use particle swarm algorithm for numeric global optimization of real-valued functions.
pso( fn, gr = NULL, lower, upper, pop.n = 40, pop.initial = NULL, pop.method = "uniform", nh.method = "random", nh.par = 3, nh.adaptive = TRUE, velocity.method = "hypersphere", velocity.par = list(w = 1/(2 * log(2)), c1 = 0.5 + log(2), c2 = 0.5 + log(2)), hybrid.method = "rank", hybrid.par = 2, hybrid.prob = 0, hybrid.opt.par = NULL, hybrid.n = 1, constr.method = NULL, constr.par = NULL, random.order = TRUE, maxiter = 100, is.max = TRUE, info = TRUE, ... )
pso( fn, gr = NULL, lower, upper, pop.n = 40, pop.initial = NULL, pop.method = "uniform", nh.method = "random", nh.par = 3, nh.adaptive = TRUE, velocity.method = "hypersphere", velocity.par = list(w = 1/(2 * log(2)), c1 = 0.5 + log(2), c2 = 0.5 + log(2)), hybrid.method = "rank", hybrid.par = 2, hybrid.prob = 0, hybrid.opt.par = NULL, hybrid.n = 1, constr.method = NULL, constr.par = NULL, random.order = TRUE, maxiter = 100, is.max = TRUE, info = TRUE, ... )
fn |
function to be maximized i.e. fitness function. |
gr |
gradient of the |
lower |
lower bound of the search space. |
upper |
upper bound of the search space. |
pop.n |
integer representing the size of the population. |
pop.initial |
numeric matrix which rows are particles to be included into the initial population. Numeric vector will be coerced to single row matrix. |
pop.method |
the algorithm to be applied for a creation of the initial population. See 'Details' for additional information. |
nh.method |
string representing the method (topology) to be used for the creation of neighbourhoods. See 'Details' for additional information. |
nh.par |
parameters of the topology algorithm. |
nh.adaptive |
logical; if |
velocity.method |
string representing the method to be used for the update of velocities. |
velocity.par |
parameters of the velocity formula. |
hybrid.method |
hybrids selection algorithm i.e. mechanism determining which particles should be subject to local optimization. See 'Details' for additional information. |
hybrid.par |
parameters of the hybridization algorithm. |
hybrid.prob |
probability of generating the hybrids each iteration. |
hybrid.opt.par |
parameters of the local optimization function
to be used for hybridization algorithm (including |
hybrid.n |
number of hybrids that appear if hybridization should take place during the iteration. |
constr.method |
the algorithm to be applied for imposing constraints on the particles. See 'Details' for additional information. |
constr.par |
parameters of the constraint algorithm. |
random.order |
logical; if |
maxiter |
maximum number of iterations of the algorithm. |
is.max |
logical; if |
info |
logical; if |
... |
additional parameters to be passed to
|
Default arguments have been set in accordance with SPSO 2011 algorithm proposed by M. Clerc (2012).
To find information on particular methods available via
pop.method
, nh.method
, velocity.method
,
hybrid.method
and constr.method
arguments please see 'Details' section of
gena.population
, pso.nh
,
pso.velocity
, gena.hybrid
and gena.constr
correspondingly.
It is possible to provide manually implemented functions for population
initialization, neighbourhoods creation, velocity updated, hybridization
and constraints in a similar way as for gena
.
By default function does not impose any constraints upon the parameters.
If constr.method = "bounds"
then lower
and upper
constraints will be imposed. Lower bounds should be strictly smaller
then upper bounds.
Currently the only available termination condition is maxiter
. We
are going to provide some additional termination conditions during
future updates.
Infinite values in lower
and upper
are substituted with
-(.Machine$double.xmax * 0.9)
and .Machine$double.xmax * 0.9
correspondingly.
By default if gr
is provided then BFGS algorithm will be used inside
optim
during hybridization.
Otherwise Nelder-Mead
will be used.
Manual values for optim
arguments may be provided
(as a list) through hybrid.opt.par
argument.
For more information on particle swarm optimization please see M. Clerc (2012).
This function returns an object of class pso
that is a list
containing the following elements:
par
- particle (solution) with the highest fitness
(objective function) value.
value
- value of fn
at par
.
population
- matrix of particles (solutions) of the
last iteration of the algorithm.
counts
- a two-element integer vector giving the number of
calls to fn
and gr
respectively.
is.max
- identical to is.max
input argument.
fitness.history
- vector which i-th element is fitness
of the best particle in i-th iteration.
iter
- last iteration number.
M. Clerc (2012). Standard Particle Swarm Optimisation. HAL archieve.
## Consider Ackley function fn <- function(par, a = 20, b = 0.2) { val <- a * exp(-b * sqrt(0.5 * (par[1] ^ 2 + par[2] ^ 2))) + exp(0.5 * (cos(2 * pi * par[1]) + cos(2 * pi * par[2]))) - exp(1) - a return(val) } # Maximize this function using particle swarm algorithm set.seed(123) lower <- c(-5, -100) upper <- c(100, 5) opt <- pso(fn = fn, lower = lower, upper = upper, a = 20, b = 0.2) print(opt$par) ## Consider Bukin function number 6 fn <- function(x, a = 20, b = 0.2) { val <- 100 * sqrt(abs(x[2] - 0.01 * x[1] ^ 2)) + 0.01 * abs(x[1] + 10) return(val) } # Minimize this function using initially provided # position for one of the particles set.seed(777) lower <- c(-15, -3) upper <- c(-5, 3) opt <- pso(fn = fn, pop.init = c(8, 2), lower = lower, upper = upper, is.max = FALSE) print(opt$par)
## Consider Ackley function fn <- function(par, a = 20, b = 0.2) { val <- a * exp(-b * sqrt(0.5 * (par[1] ^ 2 + par[2] ^ 2))) + exp(0.5 * (cos(2 * pi * par[1]) + cos(2 * pi * par[2]))) - exp(1) - a return(val) } # Maximize this function using particle swarm algorithm set.seed(123) lower <- c(-5, -100) upper <- c(100, 5) opt <- pso(fn = fn, lower = lower, upper = upper, a = 20, b = 0.2) print(opt$par) ## Consider Bukin function number 6 fn <- function(x, a = 20, b = 0.2) { val <- 100 * sqrt(abs(x[2] - 0.01 * x[1] ^ 2)) + 0.01 * abs(x[1] + 10) return(val) } # Minimize this function using initially provided # position for one of the particles set.seed(777) lower <- c(-15, -3) upper <- c(-5, 3) opt <- pso(fn = fn, pop.init = c(8, 2), lower = lower, upper = upper, is.max = FALSE) print(opt$par)
Constructs a neighbourhood of each particle using particular topology.
pso.nh(pop.n = 40, method = "ring", par = 3, iter = 1)
pso.nh(pop.n = 40, method = "ring", par = 3, iter = 1)
pop.n |
integer representing the size of the population. |
method |
string representing the topology to be used for construction of the neighbourhood. See 'Details' for additional information. |
par |
additional parameters to be passed depending on the |
iter |
iteration number of the genetic algorithm. |
If method = "ring"
then each particle will have
par[1]
neighbours. By default par[1] = 3
.
See section 3.2.1 of M. Clerc (2012) for
additional details.
If method = "wheel"
then there is a single (randomly selected)
particle which informs (and informed by) other particles while there is
no direct communication between other particles.
If method = "random"
then each particle randomly informs other
par[1]
particles and itself. Note that duplicates are possible so
sometimes each particle may inform less then par[1]
particles.
By default par[1] = 3
.
See section 3.2.2 of M. Clerc (2012) for more details.
If method = "star"
then all particles are fully informed
by each other.
If method = "random2"
then each particle will be self-informed
and informed by the j-th particle with probability par[1]
(value between 0 and 1). By default par[1] = 0.1
.
This function returns a list which i-th element is a vector of particles' indexes which inform i-th particle i.e. neighbourhood of the i-th particle.
Maurice Clerc (2012). Standard Particle Swarm Optimisation. HAL archieve.
# Prepare random number generator set.seed(123) # Ring topology with 5 neighbours pso.nh(pop.n = 10, method = "ring", par = 5) # Wheel topology pso.nh(pop.n = 10, method = "wheel") # Star topology pso.nh(pop.n = 10, method = "star") # Random topology where each particle # randomly informs 3 other particles pso.nh(pop.n = 10, method = "random", par = 3) # Random2 topology wehere each particle could # be informed by the other with probability 0.2 pso.nh(pop.n = 10, method = "random2", par = 0.2)
# Prepare random number generator set.seed(123) # Ring topology with 5 neighbours pso.nh(pop.n = 10, method = "ring", par = 5) # Wheel topology pso.nh(pop.n = 10, method = "wheel") # Star topology pso.nh(pop.n = 10, method = "star") # Random topology where each particle # randomly informs 3 other particles pso.nh(pop.n = 10, method = "random", par = 3) # Random2 topology wehere each particle could # be informed by the other with probability 0.2 pso.nh(pop.n = 10, method = "random2", par = 0.2)
Calculates (updates) velocities of the particles.
pso.velocity( population, method = "hypersphere", par = list(w = 1/(2 * log(2)), c1 = 0.5 + log(2), c2 = 0.5 + log(2)), velocity, best.pn, best.nh, best.pn.fitness, best.nh.fitness, iter = 1 )
pso.velocity( population, method = "hypersphere", par = list(w = 1/(2 * log(2)), c1 = 0.5 + log(2), c2 = 0.5 + log(2)), velocity, best.pn, best.nh, best.pn.fitness, best.nh.fitness, iter = 1 )
population |
numeric matrix which rows are particles i.e. vectors of parameters values. |
method |
string representing method to be used for velocities calculation. See 'Details' for additional information. |
par |
additional parameters to be passed depending on the |
velocity |
matrix which i-th row is a velocity of the i-th particle. |
best.pn |
numeric matrix which i-th row is a best personal position known by the i-th particle. |
best.nh |
numeric matrix which i-th row is a best personal position in a neighbourhood of the i-th particle. |
best.pn.fitness |
numeric vector which i-th row is the value of
a fitness function at point |
best.nh.fitness |
numeric vector which i-th row is the value of
a fitness function at point |
iter |
iteration number of the genetic algorithm. |
If method = "classic"
then classical velocity formula
is used:
where is a velocity of the
-th particle
respect to the
-th component at time
. Random variables
and
are i.i.d. respect to all indexes and
follow standard uniform distribution
.
Variable
is
-th component of the best known
particle's (personal) position up to time period
.
Similarly
is
-th component of the best of best
known particle's position in a neighbourhood of the
-th particle.
Hyperparameters
,
and
may be provided
via
par
argument as a list with elements par$w
, par$c1
and par$c2
correspondingly.
If method = "hypersphere"
then rotation invariant formula from
sections 3.4.2 and 3.4.3 of M. Clerc (2012) is used with arguments
identical to the classical method. To simulate a random variate from
the hypersphere function rhypersphere
is used
setting type = "non-uniform"
.
In accordance with M. Clerc (2012)
default values are par$w = 1/(2 * log(2))
,
par$c1 = 0.5 + log(2)
and par$c2 = 0.5 + log(2)
.
This function returns a matrix which i-th row represents updated velocity of the i-th particle.
Maurice Clerc (2012). Standard Particle Swarm Optimisation. HAL archieve.
Simulates uniform random variates from the hypersphere.
rhypersphere(n, dim = 2, radius = 1, center = rep(0, dim), type = "boundary")
rhypersphere(n, dim = 2, radius = 1, center = rep(0, dim), type = "boundary")
n |
number of observations to simulate. |
dim |
dimensions of hypersphere. |
radius |
radius of hypersphere. |
center |
center of hypersphere. |
type |
character; if |
The function returns a vector of random variates.
set.seed(123) # Get 5 random uniform variates from 3D hypersphere # of radius 10 centered at (2, 3, 1) rhypersphere(n = 5, dim = 3, radius = 10, center = c(2, 3, 1))
set.seed(123) # Get 5 random uniform variates from 3D hypersphere # of radius 10 centered at (2, 3, 1) rhypersphere(n = 5, dim = 3, radius = 10, center = c(2, 3, 1))
Summarizing gena Fits
## S3 method for class 'gena' summary(object, ...)
## S3 method for class 'gena' summary(object, ...)
object |
Object of class "gena" |
... |
further arguments (currently ignored) |
This function returns the same list as gena
function changing its class to "summary.gena".
Summarizing pso Fits
## S3 method for class 'pso' summary(object, ...)
## S3 method for class 'pso' summary(object, ...)
object |
Object of class "pso" |
... |
further arguments (currently ignored) |
This function returns the same list as pso
function changing its class to "summary.pso".