1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! The `operator` module defines the types of genetic operators as traits.
//! A genetic operator defines a function that performs a specific stage in
//! the genetic algorithm. Each of these genetic operator can be implemented
//! in variety of ways using different algorithms and methods.
//!
//! The genetic operators are the building blocks of the genetic algorithm.
//! Their different implementations can be combined in a variety of ways to
//! make up the actual simulation of a specific problem.

pub mod prelude;

use crate::{
    algorithm::EvaluatedPopulation,
    genetic::{Children, Fitness, Genotype, Offspring, Parents},
    random::Rng,
};

/// Marker trait for genetic operators and functions that are used for
/// single-objective optimization.
pub trait SingleObjective {}

/// Marker trait for genetic operators and functions that are used for
/// multi-objective optimization.
pub trait MultiObjective {}

/// A `GeneticOperator` defines a function used to guide the genetic algorithm
/// towards a solution to a given problem. There are three main types of
/// operators - Selection, Crossover and Mutation - which must work in
/// conjunction with one another in order for the algorithm to be successful.
///
/// There are unary operators that operate on one genotype at a time, e.g.
/// mutation operators, and binary operators that work on two genotypes
/// at a time, e.g. crossover operators.
pub trait GeneticOperator: Clone {
    /// The name of the operator used for display purposes. The name should
    /// make clear to the user of the simulation which implementation of which
    /// kind of operator is being performed.
    ///
    /// It is recommended to combine some name of the method implemented by
    /// this operator (first part) with some name for the kind of operator
    /// (second part), e.g. "Flip-Bit-Mutation" or "Roulette-Wheel-Selection".
    fn name() -> String;
}

/// A `SelectionOp` defines the function of how to select solutions for being
/// the parents of the next generation.
pub trait SelectionOp<G, F>: GeneticOperator
where
    G: Genotype,
    F: Fitness,
{
    /// Selects individuals from the given population according to the
    /// implemented selection strategy.
    fn select_from<R>(
        &self,
        population: &EvaluatedPopulation<G, F>,
        rng: &mut R,
    ) -> Vec<Parents<G>>
    where
        R: Rng + Sized;
}

/// A `CrossoverOp` defines a function of how to crossover two
/// `genetic::Genotype`s, often called parent genotypes, to derive new
/// `genetic::Genotype`s. It is analogous to reproduction and biological
/// crossover. Cross over is a process of taking two parent solutions and
/// producing an offspring solution from them.
pub trait CrossoverOp<G>: GeneticOperator
where
    G: Genotype,
{
    /// Performs the crossover of the `genetic::Parents` and returns the result
    /// as a new vector of `genetic::Genotype` - the `genetic::Children`.
    fn crossover<R>(&self, parents: Parents<G>, rng: &mut R) -> Children<G>
    where
        R: Rng + Sized;
}

/// A `MutationOp` defines a function of how a `genetic::Genotype` mutates. It
/// is used to maintain genetic diversity from one generation of a population
/// of genetic algorithm genotypes to the next. It is analogous to biological
/// mutation. Mutation alters one or more gene values in a chromosome from its
/// initial state. In mutation, the solution may change entirely from the
/// previous solution. Hence GA can come to a better solution by using
/// mutation. Mutation occurs during evolution according to a user-definable
/// mutation probability. This probability should be set low. If it is set too
/// high, the search will turn into a primitive random search.
pub trait MutationOp<G>: GeneticOperator
where
    G: Genotype,
{
    /// Mutates the given 'Genotype' and returns it as a new 'Genotype'.
    fn mutate<R>(&self, genome: G, rng: &mut R) -> G
    where
        R: Rng + Sized;
}

/// A `ReinsertionOp` defines a function that combines the offspring with the
/// current population to create the population for the next generation.
/// At the end the new population must be of the same size as the original
/// population.
///
/// The `ReinsertionOp` may decide which individuals in the offspring will
/// make it into the new population. It is not required that all individuals
/// of the offspring are reinserted into the new population.
///
/// The only hard requirement is that the new population is of same size as
/// the original population. If the offspring contains less individuals than
/// the size of the original population or not all individuals are moved
/// to the new population then some individuals from the original population
/// are taken over into the new population. If the size of the offspring is
/// bigger than the size of the original population, then not all individuals
/// from the offspring are inserted into the new population.
pub trait ReinsertionOp<G, F>: GeneticOperator
where
    G: Genotype,
    F: Fitness,
{
    /// Combines the given offspring with the current population to create
    /// the population of the next generation.
    ///
    /// The offspring parameter is passed as mutable borrow. It can be
    /// mutated to avoid cloning. The `genetic::Genotype`s that make it up into
    /// the new population should be moved instead of cloned. After this
    /// function finishes the offspring vector should hold only those
    /// `genetic::Genotype`s that have not been included in the resulting
    /// population. If by the end of this function all `genetic::Genotype`s in
    /// offspring have been moved to the resulting population the offspring
    /// vector should be left empty.
    fn combine<R>(
        &self,
        offspring: &mut Offspring<G>,
        population: &EvaluatedPopulation<G, F>,
        rng: &mut R,
    ) -> Vec<G>
    where
        R: Rng + Sized;
}