100 lines
2.8 KiB
Rust
100 lines
2.8 KiB
Rust
#![no_main]
|
|
//! Fuzz SBX + PolynomialMutation: in-bounds parents must produce in-bounds
|
|
//! children for any seed and any (η, per-variable-probability) pair.
|
|
|
|
use arbitrary::Arbitrary;
|
|
use libfuzzer_sys::fuzz_target;
|
|
|
|
use heuropt::core::rng::rng_from_seed;
|
|
use heuropt::prelude::*;
|
|
|
|
#[derive(Arbitrary, Debug)]
|
|
struct Input {
|
|
bounds: Vec<(f64, f64)>,
|
|
eta_sbx: f64,
|
|
eta_pm: f64,
|
|
pvp_sbx: f64,
|
|
pvp_pm: f64,
|
|
a_frac: Vec<f64>,
|
|
b_frac: Vec<f64>,
|
|
seed: u64,
|
|
}
|
|
|
|
fuzz_target!(|input: Input| {
|
|
let n = input.bounds.len();
|
|
if n == 0 || n > 8 {
|
|
return;
|
|
}
|
|
if !(input.eta_sbx.is_finite() && input.eta_pm.is_finite()) {
|
|
return;
|
|
}
|
|
if !(input.eta_sbx >= 1.0 && input.eta_sbx <= 100.0) {
|
|
return;
|
|
}
|
|
if !(input.eta_pm >= 1.0 && input.eta_pm <= 100.0) {
|
|
return;
|
|
}
|
|
let pvp_sbx = match input.pvp_sbx {
|
|
v if v.is_finite() && (0.0..=1.0).contains(&v) => v,
|
|
_ => return,
|
|
};
|
|
let pvp_pm = match input.pvp_pm {
|
|
v if v.is_finite() && (0.0..=1.0).contains(&v) => v,
|
|
_ => return,
|
|
};
|
|
// Sanitize bounds: lo < hi, finite.
|
|
let bounds: Vec<(f64, f64)> = input
|
|
.bounds
|
|
.iter()
|
|
.filter_map(|&(lo, hi)| {
|
|
if lo.is_finite() && hi.is_finite() && hi - lo > 1e-9 {
|
|
Some((lo, hi))
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect();
|
|
if bounds.len() != n {
|
|
return;
|
|
}
|
|
if input.a_frac.len() < n || input.b_frac.len() < n {
|
|
return;
|
|
}
|
|
|
|
let p1: Vec<f64> = bounds
|
|
.iter()
|
|
.zip(&input.a_frac)
|
|
.map(|(&(lo, hi), &f)| {
|
|
let frac = if f.is_finite() { f.fract().abs() } else { 0.5 };
|
|
lo + frac * (hi - lo)
|
|
})
|
|
.collect();
|
|
let p2: Vec<f64> = bounds
|
|
.iter()
|
|
.zip(&input.b_frac)
|
|
.map(|(&(lo, hi), &f)| {
|
|
let frac = if f.is_finite() { f.fract().abs() } else { 0.5 };
|
|
lo + frac * (hi - lo)
|
|
})
|
|
.collect();
|
|
|
|
let mut rng = rng_from_seed(input.seed);
|
|
let mut sbx = SimulatedBinaryCrossover::new(bounds.clone(), input.eta_sbx, pvp_sbx);
|
|
let kids = sbx.vary(&[p1, p2], &mut rng);
|
|
assert_eq!(kids.len(), 2);
|
|
for c in &kids {
|
|
for (j, &v) in c.iter().enumerate() {
|
|
let (lo, hi) = bounds[j];
|
|
assert!(v >= lo && v <= hi, "SBX child[{j}] = {v} out of [{lo}, {hi}]");
|
|
}
|
|
}
|
|
|
|
let mut pm = PolynomialMutation::new(bounds.clone(), input.eta_pm, pvp_pm);
|
|
let mutated = pm.vary(std::slice::from_ref(&kids[0]), &mut rng);
|
|
assert_eq!(mutated.len(), 1);
|
|
for (j, &v) in mutated[0].iter().enumerate() {
|
|
let (lo, hi) = bounds[j];
|
|
assert!(v >= lo && v <= hi, "PM child[{j}] = {v} out of [{lo}, {hi}]");
|
|
}
|
|
});
|