pub use self::inversion::inversion;
pub use self::scramble::scramble;
pub use self::swap::swap;
use super::Mutation;
use super::{
CauchyParams, CreepParams, GaussianParams, LevyFlightParams, PolynomialParams,
SelfAdaptiveGaussianParams,
};
use crate::chromosomes::ChromosomeLength;
use crate::error::GaError;
use crate::traits::{ChromosomeT, LinearChromosome, MutationOperator, RealValuedMutation};
pub mod bit_flip;
pub mod cauchy;
pub mod creep;
pub mod differential;
pub mod gaussian;
pub mod insertion;
pub mod inversion;
pub mod length_mutation;
pub mod levy_flight;
pub mod list_value;
pub mod non_uniform;
pub mod polynomial;
pub mod scramble;
pub mod self_adaptive_gaussian;
pub mod swap;
pub mod uniform;
pub mod value;
const DEFAULT_POLYNOMIAL_ETA: f64 = 20.0;
pub trait ValueMutable: LinearChromosome {
fn value_mutate(&mut self) {
crate::log_warn!(
"value_mutate() not overridden for this chromosome type; \
falling back to swap mutation. Implement ValueMutable::value_mutate() \
for proper value mutation behavior."
);
swap(self);
}
fn bit_flip_mutate(&mut self) {
crate::log_warn!(
"bit_flip_mutate() not overridden for this chromosome type; \
falling back to swap mutation. Implement ValueMutable::bit_flip_mutate() \
for proper bit-flip behavior."
);
swap(self);
}
fn creep_mutate(&mut self, _step: f64) {
crate::log_warn!(
"creep_mutate() not overridden for this chromosome type; \
falling back to swap mutation. Implement ValueMutable::creep_mutate() \
for proper creep mutation behavior."
);
swap(self);
}
fn gaussian_mutate(&mut self, _sigma: f64) {
crate::log_warn!(
"gaussian_mutate() not overridden for this chromosome type; \
falling back to swap mutation. Implement ValueMutable::gaussian_mutate() \
for proper gaussian mutation behavior."
);
swap(self);
}
}
impl MutationOperator for Mutation {
fn mutate<U>(&self, individual: &mut U, mutation: &Mutation) -> Result<(), GaError>
where
U: LinearChromosome + ValueMutable + RealValuedMutation + 'static,
{
match mutation {
Mutation::Swap => swap(individual),
Mutation::Inversion => inversion(individual),
Mutation::Scramble => scramble(individual),
Mutation::Value => individual.value_mutate(),
Mutation::BitFlip => individual.bit_flip_mutate(),
Mutation::Creep(CreepParams { step }) => {
let s = step.unwrap_or(0.01);
individual.creep_mutate(s);
}
Mutation::Gaussian(GaussianParams { sigma }) => {
let s = sigma.unwrap_or(0.1);
individual.gaussian_mutate(s);
}
Mutation::Polynomial(PolynomialParams { eta }) => {
let eta_val = eta.unwrap_or(DEFAULT_POLYNOMIAL_ETA);
return individual.polynomial_mutation(eta_val);
}
Mutation::NonUniform(..) => {
return Err(GaError::MutationError(
"Mutation::NonUniform requires generation context (generation, max_generations). \
It is applied automatically by the GA engine."
.to_string(),
));
}
Mutation::PermutationInsert => {
return insertion::insertion_mutation(individual);
}
Mutation::Insertion => {
return Err(GaError::MutationError(
"Mutation::Insertion requires ChromosomeLength::Variable configuration. \
Use with_chromosome_length(ChromosomeLength::Variable { min, max }) on your engine, \
or call length_mutation::length_insertion_mutation() directly with a ChromosomeLength."
.to_string(),
));
}
Mutation::Deletion => {
return Err(GaError::MutationError(
"Mutation::Deletion requires ChromosomeLength::Variable configuration. \
Use with_chromosome_length(ChromosomeLength::Variable { min, max }) on your engine, \
or call length_mutation::length_deletion_mutation() directly with a ChromosomeLength."
.to_string(),
));
}
Mutation::ListValue => individual.value_mutate(),
Mutation::Differential(..) => {
return Err(GaError::MutationError(
"Mutation::Differential requires population context. \
It is applied automatically by the GA engine when configured."
.to_string(),
));
}
Mutation::Cauchy(CauchyParams { scale }) => {
let s = scale.unwrap_or(1.0);
return individual.cauchy_mutation(s);
}
Mutation::LevyFlight(LevyFlightParams { alpha }) => {
let a = alpha.unwrap_or(1.5);
return individual.levy_flight_mutation(a);
}
Mutation::Uniform => {
return individual.uniform_mutation();
}
Mutation::SelfAdaptiveGaussian(SelfAdaptiveGaussianParams {
tau,
tau_prime,
sigma_min,
sigma_max,
}) => {
let n_hint = individual.dna().len().max(1);
let effective_tau = tau.unwrap_or_else(|| 1.0 / (2.0 * n_hint as f64).sqrt());
let effective_tau_prime =
tau_prime.unwrap_or_else(|| 1.0 / (2.0 * (n_hint as f64).sqrt()).sqrt());
let effective_sigma_min = sigma_min.unwrap_or(1e-5_f64);
return individual.self_adaptive_gaussian_mutation(
effective_tau,
effective_tau_prime,
effective_sigma_min,
*sigma_max,
);
}
}
Ok(())
}
}
pub fn factory<U>(mutation: Mutation, individual: &mut U) -> Result<(), GaError>
where
U: LinearChromosome + ValueMutable + RealValuedMutation + 'static,
{
mutation.mutate(individual, &mutation.clone())
}
pub fn factory_with_chromosome_length<U>(
mutation: Mutation,
individual: &mut U,
chromosome_length: Option<ChromosomeLength>,
) -> Result<(), GaError>
where
U: LinearChromosome + ValueMutable + RealValuedMutation + 'static,
{
match mutation {
Mutation::Insertion => {
let cl = chromosome_length.unwrap_or(ChromosomeLength::Fixed(0));
length_mutation::length_insertion_mutation(individual, cl)
}
Mutation::Deletion => {
let cl = chromosome_length.unwrap_or(ChromosomeLength::Fixed(0));
length_mutation::length_deletion_mutation(individual, cl)
}
other => factory(other, individual),
}
}
pub fn factory_self_adaptive<U: LinearChromosome + ValueMutable + RealValuedMutation + 'static>(
individual: &mut U,
tau: Option<f64>,
tau_prime: Option<f64>,
sigma_min: Option<f64>,
sigma_max: Option<f64>,
) -> Result<(), GaError> {
let variant = Mutation::SelfAdaptiveGaussian(SelfAdaptiveGaussianParams {
tau,
tau_prime,
sigma_min,
sigma_max,
});
variant.mutate(individual, &variant.clone())
}
pub fn factory_non_value<U>(mutation: Mutation, individual: &mut U) -> Result<(), GaError>
where
U: LinearChromosome + 'static,
{
match mutation {
Mutation::Swap => {
swap(individual);
Ok(())
}
Mutation::Inversion => {
inversion(individual);
Ok(())
}
Mutation::Scramble => {
scramble(individual);
Ok(())
}
Mutation::Value => Err(GaError::MutationError(
"Mutation::Value requires the chromosome type to implement ValueMutable. \
Use Swap, Inversion, or Scramble instead, or implement ValueMutable for your type."
.to_string(),
)),
Mutation::BitFlip => Err(GaError::MutationError(
"Mutation::BitFlip requires a Binary chromosome type. \
Use Swap, Inversion, or Scramble instead."
.to_string(),
)),
Mutation::Creep(..) => Err(GaError::MutationError(
"Mutation::Creep requires the chromosome type to implement ValueMutable. \
Use Swap, Inversion, or Scramble instead, or implement ValueMutable for your type."
.to_string(),
)),
Mutation::Gaussian(..) => Err(GaError::MutationError(
"Mutation::Gaussian requires the chromosome type to implement ValueMutable. \
Use Swap, Inversion, or Scramble instead, or implement ValueMutable for your type."
.to_string(),
)),
Mutation::Polynomial(..) => Err(GaError::MutationError(
"Mutation::Polynomial requires Range<T> chromosomes where T is f64, f32, i32, or i64. \
Use Swap, Inversion, or Scramble instead."
.to_string(),
)),
Mutation::NonUniform(..) => Err(GaError::MutationError(
"Mutation::NonUniform requires Range<T> chromosomes and generation context. \
Call non_uniform::non_uniform_mutation() directly."
.to_string(),
)),
Mutation::PermutationInsert => {
insertion::insertion_mutation(individual)
}
Mutation::Insertion => Err(GaError::MutationError(
"Mutation::Insertion requires ChromosomeLength::Variable configuration. \
Use with_chromosome_length(ChromosomeLength::Variable { min, max }) on your engine, \
or call length_mutation::length_insertion_mutation() directly with a ChromosomeLength."
.to_string(),
)),
Mutation::Deletion => Err(GaError::MutationError(
"Mutation::Deletion requires ChromosomeLength::Variable configuration. \
Use with_chromosome_length(ChromosomeLength::Variable { min, max }) on your engine, \
or call length_mutation::length_deletion_mutation() directly with a ChromosomeLength."
.to_string(),
)),
Mutation::ListValue => Err(GaError::MutationError(
"Mutation::ListValue requires a ListChromosome type. \
Use Swap, Inversion, or Scramble instead."
.to_string(),
)),
Mutation::Differential(..) => Err(GaError::MutationError(
"Mutation::Differential requires Range<T> chromosomes and population context. \
Use Swap, Inversion, or Scramble instead.".to_string(),
)),
Mutation::Cauchy(..) => Err(GaError::MutationError(
"Mutation::Cauchy requires Range<T> chromosomes where T is f64, f32, i32, or i64. \
Use Swap, Inversion, or Scramble for non-Range chromosomes."
.to_string(),
)),
Mutation::LevyFlight(..) => Err(GaError::MutationError(
"Mutation::LevyFlight requires Range<T> chromosomes where T is f64, f32, i32, or i64. \
Use Swap, Inversion, or Scramble for non-Range chromosomes.".to_string(),
)),
Mutation::Uniform => Err(GaError::MutationError(
"Mutation::Uniform requires Range<T> chromosomes where T is f64, f32, i32, or i64. \
Use Swap, Inversion, or Scramble for non-Range chromosomes.".to_string(),
)),
Mutation::SelfAdaptiveGaussian(..) => Err(GaError::MutationError(
"Mutation::SelfAdaptiveGaussian requires a chromosome implementing SelfAdaptive. \
Use Swap, Inversion, or Scramble for non-SelfAdaptive chromosomes.".to_string(),
)),
}
}
pub fn aga_probability<U: ChromosomeT>(
parent_1: &U,
parent_2: &U,
f_avg: f64,
probability_max: f64,
probability_min: f64,
) -> f64 {
let larger_f = if parent_1.fitness() > parent_2.fitness() {
parent_1.fitness()
} else {
parent_2.fitness()
};
if larger_f >= f_avg {
probability_min
} else {
probability_max
}
}
pub fn compute_cardinality<U: ChromosomeT>(chromosomes: &[U]) -> f64 {
if chromosomes.is_empty() {
return 0.0;
}
let mut seen = std::collections::HashSet::new();
for c in chromosomes {
seen.insert(c.fitness().to_bits());
}
seen.len() as f64 / chromosomes.len() as f64
}
pub fn dynamic_probability(
current_probability: f64,
cardinality: f64,
target_cardinality: f64,
probability_step: f64,
probability_max: f64,
probability_min: f64,
) -> f64 {
if cardinality < target_cardinality {
(current_probability + probability_step).min(probability_max)
} else if cardinality > target_cardinality {
(current_probability - probability_step).max(probability_min)
} else {
current_probability
}
}