test(algorithm_info): pin name/full_name/seed for every algorithm
Phase 0.1 of the mutation-testing campaign: a sweep test per algorithm (33 total) asserting the exact strings returned by AlgorithmInfo::name() and AlgorithmInfo::full_name() plus the seed propagated through AlgorithmInfo::seed(). Before: cargo mutants survived dozens of mutants per algorithm replacing the name/full_name return values with "" or "xyzzy", and the seed return with None/Some(0)/Some(1). After: every such mutant is caught by an exact-equality assertion. NelderMead is deterministic and has no seed override (intentionally); its test asserts seed() == None to pin the default-trait-impl behavior.
This commit is contained in:
@@ -731,3 +731,579 @@ proptest! {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// AlgorithmInfo sweep — exact name / full_name / seed per algorithm
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Why this exists: every algorithm has three trivial trait methods returning
|
||||
// `&'static str` and `Option<u64>`. A `cargo mutants` run discovers that
|
||||
// these are unconstrained — replacing `"NSGA-II"` with `""` or `"xyzzy"`
|
||||
// survives because no test reads the string. The constants below pin every
|
||||
// algorithm's identifying strings exactly. Updating an algorithm's name
|
||||
// requires updating its test, by design.
|
||||
|
||||
#[test]
|
||||
fn age_moea_algorithm_info_is_correct() {
|
||||
let opt = AgeMoea::new(
|
||||
AgeMoeaConfig { population_size: 4, generations: 1, seed: 42 },
|
||||
RealBounds::new(mo_bounds()),
|
||||
mo_variation(),
|
||||
);
|
||||
assert_eq!(opt.name(), "AGE-MOEA");
|
||||
assert_eq!(
|
||||
opt.full_name(),
|
||||
"Adaptive Geometry Estimation Multi-Objective Evolutionary Algorithm",
|
||||
);
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ant_colony_tsp_algorithm_info_is_correct() {
|
||||
let opt = AntColonyTsp::new(
|
||||
AntColonyTspConfig {
|
||||
ants: 2,
|
||||
generations: 1,
|
||||
alpha: 1.0,
|
||||
beta: 2.0,
|
||||
evaporation: 0.5,
|
||||
deposit: 1.0,
|
||||
initial_pheromone: 1.0,
|
||||
seed: 42,
|
||||
},
|
||||
vec![vec![0.0, 1.0], vec![1.0, 0.0]],
|
||||
);
|
||||
assert_eq!(opt.name(), "Ant Colony");
|
||||
assert_eq!(opt.full_name(), "Ant Colony System for TSP");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bayesian_opt_algorithm_info_is_correct() {
|
||||
let opt = BayesianOpt::new(
|
||||
BayesianOptConfig {
|
||||
initial_samples: 2,
|
||||
iterations: 1,
|
||||
length_scales: None,
|
||||
signal_variance: 1.0,
|
||||
noise_variance: 1e-3,
|
||||
acquisition_samples: 4,
|
||||
seed: 42,
|
||||
},
|
||||
so_bounds(),
|
||||
);
|
||||
assert_eq!(opt.name(), "Bayesian Optimization");
|
||||
assert_eq!(
|
||||
opt.full_name(),
|
||||
"Gaussian Process Bayesian Optimization with Expected Improvement",
|
||||
);
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cma_es_algorithm_info_is_correct() {
|
||||
let opt = CmaEs::new(
|
||||
CmaEsConfig {
|
||||
population_size: 4,
|
||||
generations: 1,
|
||||
initial_sigma: 0.5,
|
||||
eigen_decomposition_period: 1,
|
||||
initial_mean: None,
|
||||
seed: 42,
|
||||
},
|
||||
so_bounds(),
|
||||
);
|
||||
assert_eq!(opt.name(), "CMA-ES");
|
||||
assert_eq!(opt.full_name(), "Covariance Matrix Adaptation Evolution Strategy");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn differential_evolution_algorithm_info_is_correct() {
|
||||
let opt = DifferentialEvolution::new(
|
||||
DifferentialEvolutionConfig {
|
||||
population_size: 4,
|
||||
generations: 1,
|
||||
differential_weight: 0.5,
|
||||
crossover_probability: 0.9,
|
||||
seed: 42,
|
||||
},
|
||||
so_bounds(),
|
||||
);
|
||||
assert_eq!(opt.name(), "DE");
|
||||
assert_eq!(opt.full_name(), "Differential Evolution");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epsilon_moea_algorithm_info_is_correct() {
|
||||
let opt = EpsilonMoea::new(
|
||||
EpsilonMoeaConfig {
|
||||
population_size: 4,
|
||||
evaluations: 4,
|
||||
epsilon: vec![0.1, 0.1],
|
||||
seed: 42,
|
||||
},
|
||||
RealBounds::new(mo_bounds()),
|
||||
mo_variation(),
|
||||
);
|
||||
assert_eq!(opt.name(), "ε-MOEA");
|
||||
assert_eq!(
|
||||
opt.full_name(),
|
||||
"ε-dominance Multi-Objective Evolutionary Algorithm",
|
||||
);
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn genetic_algorithm_algorithm_info_is_correct() {
|
||||
let bounds = mo_bounds();
|
||||
let opt = GeneticAlgorithm::new(
|
||||
GeneticAlgorithmConfig {
|
||||
population_size: 4,
|
||||
generations: 1,
|
||||
tournament_size: 2,
|
||||
elitism: 1,
|
||||
seed: 42,
|
||||
},
|
||||
RealBounds::new(bounds.clone()),
|
||||
CompositeVariation {
|
||||
crossover: SimulatedBinaryCrossover::new(bounds.clone(), 15.0, 0.5),
|
||||
mutation: PolynomialMutation::new(bounds, 20.0, 1.0),
|
||||
},
|
||||
);
|
||||
assert_eq!(opt.name(), "GA");
|
||||
assert_eq!(opt.full_name(), "Genetic Algorithm");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grea_algorithm_info_is_correct() {
|
||||
let opt = Grea::new(
|
||||
GreaConfig {
|
||||
population_size: 4,
|
||||
generations: 1,
|
||||
grid_divisions: 4,
|
||||
seed: 42,
|
||||
},
|
||||
RealBounds::new(mo_bounds()),
|
||||
mo_variation(),
|
||||
);
|
||||
assert_eq!(opt.name(), "GrEA");
|
||||
assert_eq!(opt.full_name(), "Grid-based Evolutionary Algorithm");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hill_climber_algorithm_info_is_correct() {
|
||||
let opt = HillClimber::new(
|
||||
HillClimberConfig { iterations: 1, seed: 42 },
|
||||
so_bounds(),
|
||||
GaussianMutation { sigma: 0.1 },
|
||||
);
|
||||
assert_eq!(opt.name(), "Hill Climber");
|
||||
assert_eq!(opt.full_name(), "Hill Climbing");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hyperband_algorithm_info_is_correct() {
|
||||
let opt: Hyperband<RealBounds, Vec<f64>> = Hyperband::new(
|
||||
HyperbandConfig {
|
||||
max_budget: 8.0,
|
||||
eta: 2.0,
|
||||
max_brackets: 2,
|
||||
seed: 42,
|
||||
},
|
||||
so_bounds(),
|
||||
);
|
||||
assert_eq!(opt.name(), "Hyperband");
|
||||
assert_eq!(opt.full_name(), "Hyperband multi-fidelity bandit search");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hype_algorithm_info_is_correct() {
|
||||
let opt = Hype::new(
|
||||
HypeConfig {
|
||||
population_size: 4,
|
||||
generations: 1,
|
||||
reference_point: vec![10.0, 10.0],
|
||||
mc_samples: 4,
|
||||
seed: 42,
|
||||
},
|
||||
RealBounds::new(mo_bounds()),
|
||||
mo_variation(),
|
||||
);
|
||||
assert_eq!(opt.name(), "HypE");
|
||||
assert_eq!(opt.full_name(), "Hypervolume Estimation Algorithm");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ibea_algorithm_info_is_correct() {
|
||||
let opt = Ibea::new(
|
||||
IbeaConfig {
|
||||
population_size: 4,
|
||||
generations: 1,
|
||||
kappa: 0.05,
|
||||
seed: 42,
|
||||
},
|
||||
RealBounds::new(mo_bounds()),
|
||||
mo_variation(),
|
||||
);
|
||||
assert_eq!(opt.name(), "IBEA");
|
||||
assert_eq!(opt.full_name(), "Indicator-Based Evolutionary Algorithm");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipop_cma_es_algorithm_info_is_correct() {
|
||||
let opt = IpopCmaEs::new(
|
||||
IpopCmaEsConfig {
|
||||
initial_population_size: 4,
|
||||
total_generations: 1,
|
||||
initial_sigma: 0.5,
|
||||
eigen_decomposition_period: 1,
|
||||
stall_generations: None,
|
||||
seed: 42,
|
||||
},
|
||||
so_bounds(),
|
||||
);
|
||||
assert_eq!(opt.name(), "IPOP-CMA-ES");
|
||||
assert_eq!(opt.full_name(), "Increasing-Population CMA-ES with Restarts");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn knea_algorithm_info_is_correct() {
|
||||
let opt = Knea::new(
|
||||
KneaConfig { population_size: 4, generations: 1, seed: 42 },
|
||||
RealBounds::new(mo_bounds()),
|
||||
mo_variation(),
|
||||
);
|
||||
assert_eq!(opt.name(), "KnEA");
|
||||
assert_eq!(opt.full_name(), "Knee point-driven Evolutionary Algorithm");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn moead_algorithm_info_is_correct() {
|
||||
let opt = Moead::new(
|
||||
MoeadConfig {
|
||||
generations: 1,
|
||||
reference_divisions: 3,
|
||||
neighborhood_size: 2,
|
||||
seed: 42,
|
||||
},
|
||||
RealBounds::new(mo_bounds()),
|
||||
mo_variation(),
|
||||
);
|
||||
assert_eq!(opt.name(), "MOEA/D");
|
||||
assert_eq!(
|
||||
opt.full_name(),
|
||||
"Multi-Objective Evolutionary Algorithm based on Decomposition",
|
||||
);
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mopso_algorithm_info_is_correct() {
|
||||
let opt = Mopso::new(
|
||||
MopsoConfig {
|
||||
swarm_size: 4,
|
||||
generations: 1,
|
||||
archive_size: 4,
|
||||
inertia: 0.5,
|
||||
cognitive: 1.0,
|
||||
social: 1.0,
|
||||
seed: 42,
|
||||
},
|
||||
RealBounds::new(mo_bounds()),
|
||||
);
|
||||
assert_eq!(opt.name(), "MOPSO");
|
||||
assert_eq!(opt.full_name(), "Multi-Objective Particle Swarm Optimization");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nelder_mead_algorithm_info_is_correct() {
|
||||
let opt = NelderMead::new(
|
||||
NelderMeadConfig { iterations: 1, ..NelderMeadConfig::default() },
|
||||
so_bounds(),
|
||||
);
|
||||
assert_eq!(opt.name(), "Nelder-Mead");
|
||||
assert_eq!(opt.full_name(), "Nelder-Mead simplex direct search");
|
||||
// NelderMead is deterministic — no seed. Matches the default AlgorithmInfo
|
||||
// impl which returns None.
|
||||
assert_eq!(opt.seed(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nsga2_algorithm_info_is_correct() {
|
||||
let opt = Nsga2::new(
|
||||
Nsga2Config { population_size: 4, generations: 1, seed: 42 },
|
||||
RealBounds::new(mo_bounds()),
|
||||
mo_variation(),
|
||||
);
|
||||
assert_eq!(opt.name(), "NSGA-II");
|
||||
assert_eq!(opt.full_name(), "Non-dominated Sorting Genetic Algorithm II");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nsga3_algorithm_info_is_correct() {
|
||||
let opt = Nsga3::new(
|
||||
Nsga3Config {
|
||||
population_size: 4,
|
||||
generations: 1,
|
||||
reference_divisions: 4,
|
||||
seed: 42,
|
||||
},
|
||||
RealBounds::new(mo_bounds()),
|
||||
mo_variation(),
|
||||
);
|
||||
assert_eq!(opt.name(), "NSGA-III");
|
||||
assert_eq!(opt.full_name(), "Non-dominated Sorting Genetic Algorithm III");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_plus_one_es_algorithm_info_is_correct() {
|
||||
let opt = OnePlusOneEs::new(
|
||||
OnePlusOneEsConfig {
|
||||
iterations: 1,
|
||||
initial_sigma: 0.5,
|
||||
adaptation_period: 4,
|
||||
step_increase: 1.5,
|
||||
seed: 42,
|
||||
},
|
||||
so_bounds(),
|
||||
);
|
||||
assert_eq!(opt.name(), "(1+1)-ES");
|
||||
assert_eq!(
|
||||
opt.full_name(),
|
||||
"(1+1) Evolution Strategy with one-fifth success rule",
|
||||
);
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paes_algorithm_info_is_correct() {
|
||||
let opt = Paes::new(
|
||||
PaesConfig { iterations: 1, archive_size: 4, seed: 42 },
|
||||
RealBounds::new(mo_bounds()),
|
||||
GaussianMutation { sigma: 0.1 },
|
||||
);
|
||||
assert_eq!(opt.name(), "PAES");
|
||||
assert_eq!(opt.full_name(), "Pareto Archived Evolution Strategy");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn particle_swarm_algorithm_info_is_correct() {
|
||||
let opt = ParticleSwarm::new(
|
||||
ParticleSwarmConfig {
|
||||
swarm_size: 4,
|
||||
generations: 1,
|
||||
inertia: 0.5,
|
||||
cognitive: 1.0,
|
||||
social: 1.0,
|
||||
seed: 42,
|
||||
},
|
||||
so_bounds(),
|
||||
);
|
||||
assert_eq!(opt.name(), "PSO");
|
||||
assert_eq!(opt.full_name(), "Particle Swarm Optimization");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pesa_ii_algorithm_info_is_correct() {
|
||||
let opt = PesaII::new(
|
||||
PesaIIConfig {
|
||||
population_size: 4,
|
||||
archive_size: 4,
|
||||
generations: 1,
|
||||
grid_divisions: 4,
|
||||
seed: 42,
|
||||
},
|
||||
RealBounds::new(mo_bounds()),
|
||||
mo_variation(),
|
||||
);
|
||||
assert_eq!(opt.name(), "PESA-II");
|
||||
assert_eq!(opt.full_name(), "Pareto Envelope-based Selection Algorithm II");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_search_algorithm_info_is_correct() {
|
||||
let opt = RandomSearch::new(
|
||||
RandomSearchConfig { iterations: 1, batch_size: 1, seed: 42 },
|
||||
so_bounds(),
|
||||
);
|
||||
assert_eq!(opt.name(), "Random Search");
|
||||
// No `full_name` override — defaults to `name`.
|
||||
assert_eq!(opt.full_name(), "Random Search");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rvea_algorithm_info_is_correct() {
|
||||
let opt = Rvea::new(
|
||||
RveaConfig {
|
||||
population_size: 4,
|
||||
generations: 1,
|
||||
reference_divisions: 4,
|
||||
alpha: 2.0,
|
||||
seed: 42,
|
||||
},
|
||||
RealBounds::new(mo_bounds()),
|
||||
mo_variation(),
|
||||
);
|
||||
assert_eq!(opt.name(), "RVEA");
|
||||
assert_eq!(opt.full_name(), "Reference Vector-guided Evolutionary Algorithm");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simulated_annealing_algorithm_info_is_correct() {
|
||||
let opt = SimulatedAnnealing::new(
|
||||
SimulatedAnnealingConfig {
|
||||
iterations: 1,
|
||||
initial_temperature: 1.0,
|
||||
final_temperature: 0.1,
|
||||
seed: 42,
|
||||
},
|
||||
so_bounds(),
|
||||
GaussianMutation { sigma: 0.1 },
|
||||
);
|
||||
assert_eq!(opt.name(), "Simulated Annealing");
|
||||
// No `full_name` override — defaults to `name`.
|
||||
assert_eq!(opt.full_name(), "Simulated Annealing");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sms_emoa_algorithm_info_is_correct() {
|
||||
let opt = SmsEmoa::new(
|
||||
SmsEmoaConfig {
|
||||
population_size: 4,
|
||||
generations: 1,
|
||||
reference_point: vec![100.0, 100.0],
|
||||
seed: 42,
|
||||
},
|
||||
RealBounds::new(mo_bounds()),
|
||||
mo_variation(),
|
||||
);
|
||||
assert_eq!(opt.name(), "SMS-EMOA");
|
||||
assert_eq!(
|
||||
opt.full_name(),
|
||||
"S-Metric Selection Evolutionary Multi-Objective Algorithm",
|
||||
);
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn separable_nes_algorithm_info_is_correct() {
|
||||
let opt = SeparableNes::new(
|
||||
SeparableNesConfig {
|
||||
population_size: 4,
|
||||
generations: 1,
|
||||
initial_sigma: 0.5,
|
||||
mean_learning_rate: 1.0,
|
||||
sigma_learning_rate: Some(0.1),
|
||||
seed: 42,
|
||||
},
|
||||
so_bounds(),
|
||||
);
|
||||
assert_eq!(opt.name(), "sNES");
|
||||
assert_eq!(opt.full_name(), "Separable Natural Evolution Strategy");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spea2_algorithm_info_is_correct() {
|
||||
let opt = Spea2::new(
|
||||
Spea2Config {
|
||||
population_size: 4,
|
||||
archive_size: 4,
|
||||
generations: 1,
|
||||
seed: 42,
|
||||
},
|
||||
RealBounds::new(mo_bounds()),
|
||||
mo_variation(),
|
||||
);
|
||||
assert_eq!(opt.name(), "SPEA2");
|
||||
assert_eq!(opt.full_name(), "Strength Pareto Evolutionary Algorithm 2");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tabu_search_algorithm_info_is_correct() {
|
||||
struct StartAtZero;
|
||||
impl Initializer<Vec<i32>> for StartAtZero {
|
||||
fn initialize(
|
||||
&mut self,
|
||||
_size: usize,
|
||||
_rng: &mut heuropt::core::rng::Rng,
|
||||
) -> Vec<Vec<i32>> {
|
||||
vec![vec![0]]
|
||||
}
|
||||
}
|
||||
let neighbors = |x: &Vec<i32>, _rng: &mut heuropt::core::rng::Rng| {
|
||||
vec![vec![x[0] - 1], vec![x[0] + 1]]
|
||||
};
|
||||
let opt = TabuSearch::new(
|
||||
TabuSearchConfig { iterations: 1, tabu_tenure: 4, seed: 42 },
|
||||
StartAtZero,
|
||||
neighbors,
|
||||
);
|
||||
assert_eq!(opt.name(), "Tabu Search");
|
||||
// No `full_name` override — defaults to `name`.
|
||||
assert_eq!(opt.full_name(), "Tabu Search");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tlbo_algorithm_info_is_correct() {
|
||||
let opt = Tlbo::new(
|
||||
TlboConfig { population_size: 4, generations: 1, seed: 42 },
|
||||
so_bounds(),
|
||||
);
|
||||
assert_eq!(opt.name(), "TLBO");
|
||||
assert_eq!(opt.full_name(), "Teaching-Learning-Based Optimization");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tpe_algorithm_info_is_correct() {
|
||||
let opt = Tpe::new(
|
||||
TpeConfig {
|
||||
initial_samples: 2,
|
||||
iterations: 1,
|
||||
good_fraction: 0.25,
|
||||
candidate_samples: 4,
|
||||
bandwidth_factor: 0.1,
|
||||
seed: 42,
|
||||
},
|
||||
so_bounds(),
|
||||
);
|
||||
assert_eq!(opt.name(), "TPE");
|
||||
assert_eq!(opt.full_name(), "Tree-structured Parzen Estimator");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn umda_algorithm_info_is_correct() {
|
||||
let opt = Umda::new(UmdaConfig {
|
||||
bits: 4,
|
||||
population_size: 4,
|
||||
selected_size: 2,
|
||||
generations: 1,
|
||||
seed: 42,
|
||||
});
|
||||
assert_eq!(opt.name(), "UMDA");
|
||||
assert_eq!(opt.full_name(), "Univariate Marginal Distribution Algorithm");
|
||||
assert_eq!(opt.seed(), Some(42));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user