use ndarray::{Array1, Array2, Axis};
use moors::{
AlgorithmBuilder, CloseDuplicatesCleaner, GaussianMutation, PopulationSOO, RandomSamplingFloat,
SimulatedBinaryCrossover, impl_constraints_fn,
selection::soo::RankSelection,
survival::soo::{FitnessConstraintsPenaltySurvival, FitnessSurvival},
};
fn fitness_sphere(population: &Array2<f64>) -> Array1<f64> {
population.map_axis(Axis(1), |row| 1.0 - row.dot(&row))
}
fn constraints_sphere(population: &Array2<f64>) -> Array1<f64> {
population.map_axis(Axis(1), |row| row.dot(&row)) - 1.0
}
#[test]
fn test_ga_minimize_parabolid() {
let mut algorithm = AlgorithmBuilder::default()
.sampler(RandomSamplingFloat::new(-1.0, 1.0))
.crossover(SimulatedBinaryCrossover::new(15.0))
.mutation(GaussianMutation::new(0.05, 0.1))
.selector(RankSelection)
.survivor(FitnessSurvival)
.duplicates_cleaner(CloseDuplicatesCleaner::new(1e-6))
.fitness_fn(fitness_sphere)
.constraints_fn(constraints_sphere)
.num_vars(3)
.population_size(100)
.num_offsprings(50)
.num_iterations(150)
.mutation_rate(0.1)
.crossover_rate(0.9)
.keep_infeasible(false)
.verbose(true)
.seed(123)
.build()
.expect("failed to build GA");
algorithm.run().expect("GA run failed");
let population: PopulationSOO = algorithm
.population
.expect("population should have been initialized");
for &fit in population.fitness.iter() {
assert!(fit.abs() < 1e-3);
}
for gene in population.genes.rows() {
let x = gene[0];
let y = gene[1];
let z = gene[2];
let constraint_value = x * x + y * y + z * z - 1.0;
assert!(constraint_value.abs() < 1e-3);
}
}
fn fitness_quadratic(population: &Array2<f64>) -> Array1<f64> {
population.map_axis(Axis(1), |row| row.dot(&row))
}
fn line_constraint(genes: &Array2<f64>) -> Array1<f64> {
genes.map_axis(Axis(1), |row| 1.0 - row.sum())
}
impl_constraints_fn!(
LineProjectionConstraints,
eq = [line_constraint],
lower_bound = 0.0,
upper_bound = 1.0
);
#[test]
#[should_panic]
fn test_minimize_projection_on_line() {
let mut algorithm = AlgorithmBuilder::default()
.sampler(RandomSamplingFloat::new(0.0, 1.0))
.crossover(SimulatedBinaryCrossover::new(15.0))
.mutation(GaussianMutation::new(0.9, 0.1))
.selector(RankSelection)
.survivor(FitnessSurvival)
.duplicates_cleaner(CloseDuplicatesCleaner::new(1e-6))
.fitness_fn(fitness_quadratic)
.constraints_fn(LineProjectionConstraints)
.num_vars(2)
.population_size(200)
.num_offsprings(200)
.num_iterations(600)
.mutation_rate(0.2)
.crossover_rate(0.95)
.keep_infeasible(true)
.verbose(true)
.seed(123)
.build()
.expect("failed to build GA");
algorithm.run().expect("GA run failed");
let population = algorithm
.population
.expect("population should have been initialized");
println!("GENES {}", population.best().genes);
for gene in population.best().genes.rows() {
assert!((gene[0] - 0.5).abs() < 0.01, "x ≈ 0.5, got {}", gene[0]);
assert!((gene[1] - 0.5).abs() < 0.01, "y ≈ 0.5, got {}", gene[1]);
}
}
#[test]
fn test_minimize_projection_on_line_constraints_penalty_survival() {
let mut algorithm = AlgorithmBuilder::default()
.sampler(RandomSamplingFloat::new(0.0, 1.0))
.crossover(SimulatedBinaryCrossover::new(15.0))
.mutation(GaussianMutation::new(0.9, 0.1))
.selector(RankSelection)
.survivor(FitnessConstraintsPenaltySurvival::new(1.0))
.duplicates_cleaner(CloseDuplicatesCleaner::new(1e-6))
.fitness_fn(fitness_quadratic)
.constraints_fn(LineProjectionConstraints)
.num_vars(2)
.population_size(200)
.num_offsprings(200)
.num_iterations(600)
.mutation_rate(0.2)
.crossover_rate(0.95)
.keep_infeasible(true)
.verbose(true)
.seed(123)
.build()
.expect("failed to build GA");
algorithm.run().expect("GA run failed");
let population = algorithm
.population
.expect("population should have been initialized");
for gene in population.best().genes.rows() {
assert!((gene[0] - 0.5).abs() < 0.01, "x ≈ 0.5, got {}", gene[0]);
assert!((gene[1] - 0.5).abs() < 0.01, "y ≈ 0.5, got {}", gene[1]);
}
}