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
//! The 'genetic' module defines types for the genetic algorithm. The traits
//! defined in this module should be implemented to formulate an optimization
//! or search problem. The types are named after terms as they are found in
//! genetic biology.
use std::fmt::Debug;
/// A `Phenotype` is a candidate solution of the optimization or search problem.
/// Phenotypes are also called individuals or creatures. A `Phenotype` is the
/// type of data for which the optimal value for solving an optimization or
/// search problem should be found.
///
/// The `Phenotype` represents a subject in the problem domain. It holds its
/// genes which are its representation in the search space of the genetic
/// algorithm. The genes are represented as a vector of `Genotype`s.
pub trait Phenotype<G>: Clone + Debug
where
G: Genotype,
{
/// Returns its genes as a `Genotype`.
///
/// Hint: The simulation may access this function several times. Therefore
/// this method should be as fast as possible, e.g. through storing or
/// caching the genes.
fn genes(&self) -> G;
/// Clones this `Phenotype` into a new one but with the given genes.
fn derive(&self, new_genes: G) -> Self;
}
/// A `Genotype` defines those properties of a `Phenotype` that are relevant
/// for the genetic algorithm. Respectively they are used to determine the
/// `Fitness` value of the solution candidate. These properties are also called
/// chromosomes.
///
/// In order to achieve an efficient execution of the genetic algorithm these
/// properties should be stored in a compact form such as strings or vectors
/// of primitive types.
pub trait Genotype: Clone + Debug + PartialEq + Send + Sync {
type Dna: Clone + Debug + PartialEq;
}
/// The `Locus` is a position within a `Genotype`.
pub type Locus = usize;
/// The `Parents` type defines a tuple of individuals that are needed for
/// breeding one offspring. The `operator::SelectionOp` selects a list of
/// parents which are taken by the `operator::CrossoverOp` for breeding
/// the offspring.
///
/// Commonly parents are defined as tuple of two `Genotype`s but some
/// derivation of the genetic algorithm wants to use three or more
/// `Genotype`s for breeding.
///
/// Note: For an efficient and easy to use implementation the logical tuple
/// of parents is software technically typed as vector.
pub type Parents<G> = Vec<G>;
pub type ParentsSlice<'a, G> = &'a [G];
/// The `Children` type defines a set of `Genotype`s which is the outcome of
/// the `operator::CrossoverOp` function.
pub type Children<G> = Vec<G>;
/// The `Offspring` type defines the set of `Children` of type `Genotype`
/// which represents the all children of all `Parents` of one generation.
pub type Offspring<G> = Vec<G>;
/// A `Fitness` value is used to determine the quality of a `Genotype`.
/// `Fitness` values should have an ordering, also called ranking.
///
/// **Make sure the following statement holds:**
/// A `Genotype` with a `Fitness` value of `f1` performs better than another
/// `Genotype` with a `Fitness` value of `f2` if `f1 > f2`.
///
/// For multi-objective `Fitness` values either `operator::GeneticOperator`s
/// suitable for multi-objective optimization are used or the implementation
/// of the multi-objective `Fitness` value additionally implements the
/// `AsScalar` trait. Using single-objective optimization for multi-objective
/// problems has some drawbacks though.
pub trait Fitness: Eq + Ord + Clone + Debug + Sized {
/// Returns the zero value of this `Fitness` value.
/// The internal value should be 0.
fn zero() -> Self;
/// Returns the absolute difference between this `Fitness` value and the
/// other one, i.e. result = |self| - |other|
fn abs_diff(&self, other: &Self) -> Self;
}
/// In order to be able to use `operator::GeneticOperator`s designed for
/// single-objective optimization to be used for multi-objective `Fitness`
/// values the struct implementing the `Fitness` trait must also implement
/// this `AsScalar` trait.
///
/// The implementation will use a scalarization method to convert a
/// multi-objective `Fitness` value into a scalar representation. A well-known
/// method is calculating the weighted sum: F = Sum(W * f).
pub trait AsScalar {
/// Returns a float value that represents this type in scalar form.
fn as_scalar(&self) -> f64;
}
/// Defines the evaluation function to calculate the `Fitness` value of a
/// `Genotype` based on its properties.
pub trait FitnessFunction<G, F>: Clone
where
G: Genotype,
F: Fitness,
{
/// Calculates the `Fitness` value of the given `Genotype`.
fn fitness_of(&self, a: &G) -> F;
/// Calculates the average `Fitness` value of the given `Fitness` values.
fn average(&self, a: &[F]) -> F;
/// Returns the very best of all theoretically possible `Fitness` values.
fn highest_possible_fitness(&self) -> F;
/// Returns the worst of all theoretically possible `Fitness` values.
/// This is usually a value equivalent to zero.
fn lowest_possible_fitness(&self) -> F;
}