style: rustfmt the Phase 1 test additions
The per-file Phase 1 test commits were written without running rustfmt as I went; this pass formats the new test code (long assert_eq! lines wrapped, etc.). Formatting-only — no behavioural change.
This commit is contained in:
+52
-14
@@ -33,23 +33,59 @@ use heuropt::prelude::*;
|
|||||||
|
|
||||||
/// First 25 cities of TSPLIB KroA100 (EUC_2D).
|
/// First 25 cities of TSPLIB KroA100 (EUC_2D).
|
||||||
const KROA_25: [(f64, f64); 25] = [
|
const KROA_25: [(f64, f64); 25] = [
|
||||||
(1380.0, 939.0), (2848.0, 96.0), (3510.0, 1671.0), (457.0, 334.0),
|
(1380.0, 939.0),
|
||||||
(3888.0, 666.0), (984.0, 965.0), (2721.0, 1482.0), (1286.0, 525.0),
|
(2848.0, 96.0),
|
||||||
(2716.0, 1432.0),(738.0, 1325.0), (1251.0, 1832.0), (2728.0, 1698.0),
|
(3510.0, 1671.0),
|
||||||
(3815.0, 169.0), (3683.0, 1533.0),(1247.0, 1945.0), (123.0, 862.0),
|
(457.0, 334.0),
|
||||||
(1234.0, 1946.0),(252.0, 1240.0), (611.0, 673.0), (2576.0, 1676.0),
|
(3888.0, 666.0),
|
||||||
(928.0, 1700.0), (53.0, 857.0), (1807.0, 1711.0), (274.0, 1420.0),
|
(984.0, 965.0),
|
||||||
|
(2721.0, 1482.0),
|
||||||
|
(1286.0, 525.0),
|
||||||
|
(2716.0, 1432.0),
|
||||||
|
(738.0, 1325.0),
|
||||||
|
(1251.0, 1832.0),
|
||||||
|
(2728.0, 1698.0),
|
||||||
|
(3815.0, 169.0),
|
||||||
|
(3683.0, 1533.0),
|
||||||
|
(1247.0, 1945.0),
|
||||||
|
(123.0, 862.0),
|
||||||
|
(1234.0, 1946.0),
|
||||||
|
(252.0, 1240.0),
|
||||||
|
(611.0, 673.0),
|
||||||
|
(2576.0, 1676.0),
|
||||||
|
(928.0, 1700.0),
|
||||||
|
(53.0, 857.0),
|
||||||
|
(1807.0, 1711.0),
|
||||||
|
(274.0, 1420.0),
|
||||||
(2574.0, 946.0),
|
(2574.0, 946.0),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// First 25 cities of TSPLIB KroB100 (EUC_2D).
|
/// First 25 cities of TSPLIB KroB100 (EUC_2D).
|
||||||
const KROB_25: [(f64, f64); 25] = [
|
const KROB_25: [(f64, f64); 25] = [
|
||||||
(3140.0, 1401.0),(556.0, 1056.0), (3675.0, 1522.0), (1182.0, 1853.0),
|
(3140.0, 1401.0),
|
||||||
(3595.0, 1340.0),(1936.0, 953.0), (2722.0, 1311.0), (2839.0, 2055.0),
|
(556.0, 1056.0),
|
||||||
(2253.0, 1242.0),(3142.0, 1591.0),(627.0, 1336.0), (936.0, 211.0),
|
(3675.0, 1522.0),
|
||||||
(4014.0, 471.0), (1376.0, 1452.0),(3289.0, 593.0), (1453.0, 67.0),
|
(1182.0, 1853.0),
|
||||||
(1014.0, 1944.0),(2811.0, 1080.0),(3010.0, 1290.0), (1817.0, 1517.0),
|
(3595.0, 1340.0),
|
||||||
(510.0, 458.0), (1717.0, 1693.0),(1252.0, 1633.0), (1693.0, 1374.0),
|
(1936.0, 953.0),
|
||||||
|
(2722.0, 1311.0),
|
||||||
|
(2839.0, 2055.0),
|
||||||
|
(2253.0, 1242.0),
|
||||||
|
(3142.0, 1591.0),
|
||||||
|
(627.0, 1336.0),
|
||||||
|
(936.0, 211.0),
|
||||||
|
(4014.0, 471.0),
|
||||||
|
(1376.0, 1452.0),
|
||||||
|
(3289.0, 593.0),
|
||||||
|
(1453.0, 67.0),
|
||||||
|
(1014.0, 1944.0),
|
||||||
|
(2811.0, 1080.0),
|
||||||
|
(3010.0, 1290.0),
|
||||||
|
(1817.0, 1517.0),
|
||||||
|
(510.0, 458.0),
|
||||||
|
(1717.0, 1693.0),
|
||||||
|
(1252.0, 1633.0),
|
||||||
|
(1693.0, 1374.0),
|
||||||
(539.0, 1378.0),
|
(539.0, 1378.0),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -182,6 +218,8 @@ fn main() {
|
|||||||
let owned: Vec<Candidate<Vec<usize>>> = result.pareto_front.to_vec();
|
let owned: Vec<Candidate<Vec<usize>>> = result.pareto_front.to_vec();
|
||||||
let hv = hypervolume_2d(&owned, &problem.objectives(), ref_point);
|
let hv = hypervolume_2d(&owned, &problem.objectives(), ref_point);
|
||||||
println!();
|
println!();
|
||||||
println!("Hypervolume vs. reference ({}, {}): {:.0}",
|
println!(
|
||||||
ref_point[0], ref_point[1], hv);
|
"Hypervolume vs. reference ({}, {}): {:.0}",
|
||||||
|
ref_point[0], ref_point[1], hv
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-24
@@ -39,24 +39,21 @@ const N_ITEMS: usize = 30;
|
|||||||
|
|
||||||
/// Profit vector A (one of two objectives), U(10, 100) style.
|
/// Profit vector A (one of two objectives), U(10, 100) style.
|
||||||
const PROFITS_A: [f64; N_ITEMS] = [
|
const PROFITS_A: [f64; N_ITEMS] = [
|
||||||
61.0, 17.0, 92.0, 49.0, 73.0, 28.0, 84.0, 36.0, 55.0, 78.0,
|
61.0, 17.0, 92.0, 49.0, 73.0, 28.0, 84.0, 36.0, 55.0, 78.0, 23.0, 91.0, 12.0, 67.0, 45.0, 58.0,
|
||||||
23.0, 91.0, 12.0, 67.0, 45.0, 58.0, 33.0, 71.0, 14.0, 26.0,
|
33.0, 71.0, 14.0, 26.0, 87.0, 42.0, 19.0, 65.0, 30.0, 51.0, 79.0, 22.0, 47.0, 88.0,
|
||||||
87.0, 42.0, 19.0, 65.0, 30.0, 51.0, 79.0, 22.0, 47.0, 88.0,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Profit vector B (the other objective). Intentionally anti-correlated with
|
/// Profit vector B (the other objective). Intentionally anti-correlated with
|
||||||
/// A on many items so the Pareto front spans a wide trade-off.
|
/// A on many items so the Pareto front spans a wide trade-off.
|
||||||
const PROFITS_B: [f64; N_ITEMS] = [
|
const PROFITS_B: [f64; N_ITEMS] = [
|
||||||
24.0, 81.0, 16.0, 67.0, 29.0, 73.0, 41.0, 60.0, 52.0, 19.0,
|
24.0, 81.0, 16.0, 67.0, 29.0, 73.0, 41.0, 60.0, 52.0, 19.0, 77.0, 34.0, 95.0, 22.0, 71.0, 88.0,
|
||||||
77.0, 34.0, 95.0, 22.0, 71.0, 88.0, 56.0, 27.0, 64.0, 90.0,
|
56.0, 27.0, 64.0, 90.0, 18.0, 43.0, 79.0, 31.0, 85.0, 25.0, 38.0, 92.0, 70.0, 13.0,
|
||||||
18.0, 43.0, 79.0, 31.0, 85.0, 25.0, 38.0, 92.0, 70.0, 13.0,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Item weights.
|
/// Item weights.
|
||||||
const WEIGHTS: [f64; N_ITEMS] = [
|
const WEIGHTS: [f64; N_ITEMS] = [
|
||||||
35.0, 58.0, 22.0, 71.0, 14.0, 86.0, 31.0, 53.0, 78.0, 19.0,
|
35.0, 58.0, 22.0, 71.0, 14.0, 86.0, 31.0, 53.0, 78.0, 19.0, 44.0, 16.0, 67.0, 88.0, 25.0, 51.0,
|
||||||
44.0, 16.0, 67.0, 88.0, 25.0, 51.0, 33.0, 74.0, 12.0, 47.0,
|
33.0, 74.0, 12.0, 47.0, 63.0, 28.0, 91.0, 36.0, 55.0, 17.0, 82.0, 41.0, 24.0, 68.0,
|
||||||
63.0, 28.0, 91.0, 36.0, 55.0, 17.0, 82.0, 41.0, 24.0, 68.0,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Capacity = roughly half the total weight (standard Zitzler-Thiele convention).
|
/// Capacity = roughly half the total weight (standard Zitzler-Thiele convention).
|
||||||
@@ -79,16 +76,16 @@ impl Problem for BiKnapsack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate(&self, take: &Vec<bool>) -> Evaluation {
|
fn evaluate(&self, take: &Vec<bool>) -> Evaluation {
|
||||||
let (pa, pb, w) = take.iter().enumerate().fold(
|
let (pa, pb, w) =
|
||||||
(0.0_f64, 0.0_f64, 0.0_f64),
|
take.iter()
|
||||||
|(pa, pb, w), (i, &t)| {
|
.enumerate()
|
||||||
if t {
|
.fold((0.0_f64, 0.0_f64, 0.0_f64), |(pa, pb, w), (i, &t)| {
|
||||||
(pa + PROFITS_A[i], pb + PROFITS_B[i], w + WEIGHTS[i])
|
if t {
|
||||||
} else {
|
(pa + PROFITS_A[i], pb + PROFITS_B[i], w + WEIGHTS[i])
|
||||||
(pa, pb, w)
|
} else {
|
||||||
}
|
(pa, pb, w)
|
||||||
},
|
}
|
||||||
);
|
});
|
||||||
// Penalty: large coefficient on weight overrun, applied to both objectives.
|
// Penalty: large coefficient on weight overrun, applied to both objectives.
|
||||||
let overrun = (w - self.cap).max(0.0);
|
let overrun = (w - self.cap).max(0.0);
|
||||||
let penalty = 1000.0 * overrun;
|
let penalty = 1000.0 * overrun;
|
||||||
@@ -122,7 +119,10 @@ struct OnePointCrossoverBool;
|
|||||||
|
|
||||||
impl Variation<Vec<bool>> for OnePointCrossoverBool {
|
impl Variation<Vec<bool>> for OnePointCrossoverBool {
|
||||||
fn vary(&mut self, parents: &[Vec<bool>], rng: &mut Rng) -> Vec<Vec<bool>> {
|
fn vary(&mut self, parents: &[Vec<bool>], rng: &mut Rng) -> Vec<Vec<bool>> {
|
||||||
assert!(parents.len() >= 2, "OnePointCrossoverBool requires 2 parents");
|
assert!(
|
||||||
|
parents.len() >= 2,
|
||||||
|
"OnePointCrossoverBool requires 2 parents"
|
||||||
|
);
|
||||||
let p1 = &parents[0];
|
let p1 = &parents[0];
|
||||||
let p2 = &parents[1];
|
let p2 = &parents[1];
|
||||||
assert_eq!(p1.len(), p2.len(), "parent lengths differ");
|
assert_eq!(p1.len(), p2.len(), "parent lengths differ");
|
||||||
@@ -154,14 +154,19 @@ fn main() {
|
|||||||
RandomBinary { n: N_ITEMS },
|
RandomBinary { n: N_ITEMS },
|
||||||
CompositeVariation {
|
CompositeVariation {
|
||||||
crossover: OnePointCrossoverBool,
|
crossover: OnePointCrossoverBool,
|
||||||
mutation: BitFlipMutation { probability: 1.0 / N_ITEMS as f64 },
|
mutation: BitFlipMutation {
|
||||||
|
probability: 1.0 / N_ITEMS as f64,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let result = optimizer.run(&problem);
|
let result = optimizer.run(&problem);
|
||||||
|
|
||||||
println!("Bi-objective 0/1 knapsack — Zitzler–Thiele style, 30 items");
|
println!("Bi-objective 0/1 knapsack — Zitzler–Thiele style, 30 items");
|
||||||
println!("Capacity = {:.0} (≈ half of total weight {:.0})",
|
println!(
|
||||||
cap, WEIGHTS.iter().sum::<f64>());
|
"Capacity = {:.0} (≈ half of total weight {:.0})",
|
||||||
|
cap,
|
||||||
|
WEIGHTS.iter().sum::<f64>()
|
||||||
|
);
|
||||||
println!();
|
println!();
|
||||||
println!("Total evaluations: {}", result.evaluations);
|
println!("Total evaluations: {}", result.evaluations);
|
||||||
println!("Pareto-front size: {}", result.pareto_front.len());
|
println!("Pareto-front size: {}", result.pareto_front.len());
|
||||||
@@ -202,5 +207,8 @@ fn main() {
|
|||||||
let owned: Vec<Candidate<Vec<bool>>> = result.pareto_front.to_vec();
|
let owned: Vec<Candidate<Vec<bool>>> = result.pareto_front.to_vec();
|
||||||
let hv = hypervolume_2d(&owned, &problem.objectives(), ref_point);
|
let hv = hypervolume_2d(&owned, &problem.objectives(), ref_point);
|
||||||
println!();
|
println!();
|
||||||
println!("Hypervolume vs. reference (profit_A=0, profit_B=0): {:.0}", hv);
|
println!(
|
||||||
|
"Hypervolume vs. reference (profit_A=0, profit_B=0): {:.0}",
|
||||||
|
hv
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,23 +27,59 @@ use std::time::Instant;
|
|||||||
|
|
||||||
/// First 25 cities of TSPLIB KroA100 (EUC_2D).
|
/// First 25 cities of TSPLIB KroA100 (EUC_2D).
|
||||||
const KROA_25: [(f64, f64); 25] = [
|
const KROA_25: [(f64, f64); 25] = [
|
||||||
(1380.0, 939.0), (2848.0, 96.0), (3510.0, 1671.0), (457.0, 334.0),
|
(1380.0, 939.0),
|
||||||
(3888.0, 666.0), (984.0, 965.0), (2721.0, 1482.0), (1286.0, 525.0),
|
(2848.0, 96.0),
|
||||||
(2716.0, 1432.0),(738.0, 1325.0), (1251.0, 1832.0), (2728.0, 1698.0),
|
(3510.0, 1671.0),
|
||||||
(3815.0, 169.0), (3683.0, 1533.0),(1247.0, 1945.0), (123.0, 862.0),
|
(457.0, 334.0),
|
||||||
(1234.0, 1946.0),(252.0, 1240.0), (611.0, 673.0), (2576.0, 1676.0),
|
(3888.0, 666.0),
|
||||||
(928.0, 1700.0), (53.0, 857.0), (1807.0, 1711.0), (274.0, 1420.0),
|
(984.0, 965.0),
|
||||||
|
(2721.0, 1482.0),
|
||||||
|
(1286.0, 525.0),
|
||||||
|
(2716.0, 1432.0),
|
||||||
|
(738.0, 1325.0),
|
||||||
|
(1251.0, 1832.0),
|
||||||
|
(2728.0, 1698.0),
|
||||||
|
(3815.0, 169.0),
|
||||||
|
(3683.0, 1533.0),
|
||||||
|
(1247.0, 1945.0),
|
||||||
|
(123.0, 862.0),
|
||||||
|
(1234.0, 1946.0),
|
||||||
|
(252.0, 1240.0),
|
||||||
|
(611.0, 673.0),
|
||||||
|
(2576.0, 1676.0),
|
||||||
|
(928.0, 1700.0),
|
||||||
|
(53.0, 857.0),
|
||||||
|
(1807.0, 1711.0),
|
||||||
|
(274.0, 1420.0),
|
||||||
(2574.0, 946.0),
|
(2574.0, 946.0),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// First 25 cities of TSPLIB KroB100 (EUC_2D).
|
/// First 25 cities of TSPLIB KroB100 (EUC_2D).
|
||||||
const KROB_25: [(f64, f64); 25] = [
|
const KROB_25: [(f64, f64); 25] = [
|
||||||
(3140.0, 1401.0),(556.0, 1056.0), (3675.0, 1522.0), (1182.0, 1853.0),
|
(3140.0, 1401.0),
|
||||||
(3595.0, 1340.0),(1936.0, 953.0), (2722.0, 1311.0), (2839.0, 2055.0),
|
(556.0, 1056.0),
|
||||||
(2253.0, 1242.0),(3142.0, 1591.0),(627.0, 1336.0), (936.0, 211.0),
|
(3675.0, 1522.0),
|
||||||
(4014.0, 471.0), (1376.0, 1452.0),(3289.0, 593.0), (1453.0, 67.0),
|
(1182.0, 1853.0),
|
||||||
(1014.0, 1944.0),(2811.0, 1080.0),(3010.0, 1290.0), (1817.0, 1517.0),
|
(3595.0, 1340.0),
|
||||||
(510.0, 458.0), (1717.0, 1693.0),(1252.0, 1633.0), (1693.0, 1374.0),
|
(1936.0, 953.0),
|
||||||
|
(2722.0, 1311.0),
|
||||||
|
(2839.0, 2055.0),
|
||||||
|
(2253.0, 1242.0),
|
||||||
|
(3142.0, 1591.0),
|
||||||
|
(627.0, 1336.0),
|
||||||
|
(936.0, 211.0),
|
||||||
|
(4014.0, 471.0),
|
||||||
|
(1376.0, 1452.0),
|
||||||
|
(3289.0, 593.0),
|
||||||
|
(1453.0, 67.0),
|
||||||
|
(1014.0, 1944.0),
|
||||||
|
(2811.0, 1080.0),
|
||||||
|
(3010.0, 1290.0),
|
||||||
|
(1817.0, 1517.0),
|
||||||
|
(510.0, 458.0),
|
||||||
|
(1717.0, 1693.0),
|
||||||
|
(1252.0, 1633.0),
|
||||||
|
(1693.0, 1374.0),
|
||||||
(539.0, 1378.0),
|
(539.0, 1378.0),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -177,7 +213,10 @@ fn main() {
|
|||||||
println!("Bi-objective TSP (KroAB-25): NSGA-II crossover showdown");
|
println!("Bi-objective TSP (KroAB-25): NSGA-II crossover showdown");
|
||||||
println!("Same population, generations, seed across all runs.");
|
println!("Same population, generations, seed across all runs.");
|
||||||
println!("Mutation held constant at InversionMutation.");
|
println!("Mutation held constant at InversionMutation.");
|
||||||
println!("Reference point for hypervolume: ({:.0}, {:.0})", REF_POINT[0], REF_POINT[1]);
|
println!(
|
||||||
|
"Reference point for hypervolume: ({:.0}, {:.0})",
|
||||||
|
REF_POINT[0], REF_POINT[1]
|
||||||
|
);
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
let runs = vec![
|
let runs = vec![
|
||||||
@@ -189,13 +228,7 @@ fn main() {
|
|||||||
|
|
||||||
println!(
|
println!(
|
||||||
" {:<24} | {:>5} {:>5} | {:>17} | {:>17} | {:>14} | {:>6}",
|
" {:<24} | {:>5} {:>5} | {:>17} | {:>17} | {:>14} | {:>6}",
|
||||||
"crossover",
|
"crossover", "size", "uniq", "A-corner (A, B)", "B-corner (A, B)", "hypervolume", "time"
|
||||||
"size",
|
|
||||||
"uniq",
|
|
||||||
"A-corner (A, B)",
|
|
||||||
"B-corner (A, B)",
|
|
||||||
"hypervolume",
|
|
||||||
"time"
|
|
||||||
);
|
);
|
||||||
println!(" {}", "-".repeat(106));
|
println!(" {}", "-".repeat(106));
|
||||||
for r in &runs {
|
for r in &runs {
|
||||||
@@ -223,5 +256,8 @@ fn main() {
|
|||||||
.unwrap_or(std::cmp::Ordering::Equal)
|
.unwrap_or(std::cmp::Ordering::Equal)
|
||||||
})
|
})
|
||||||
.expect("non-empty runs");
|
.expect("non-empty runs");
|
||||||
println!("Best by hypervolume: {} ({:.0})", winner.name, winner.hypervolume);
|
println!(
|
||||||
|
"Best by hypervolume: {} ({:.0})",
|
||||||
|
winner.name, winner.hypervolume
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,7 +152,10 @@ fn main() {
|
|||||||
println!("Source: TSPLIB95 (Groetschel/Padberg)");
|
println!("Source: TSPLIB95 (Groetschel/Padberg)");
|
||||||
println!();
|
println!();
|
||||||
println!("Known optimum: {:>8.0}", KNOWN_OPTIMUM);
|
println!("Known optimum: {:>8.0}", KNOWN_OPTIMUM);
|
||||||
println!("GA best found: {:>8.0} (gap {:+.0}, {:+.2}%)", best_len, gap_abs, gap_pct);
|
println!(
|
||||||
|
"GA best found: {:>8.0} (gap {:+.0}, {:+.2}%)",
|
||||||
|
best_len, gap_abs, gap_pct
|
||||||
|
);
|
||||||
println!();
|
println!();
|
||||||
println!("Total evaluations: {}", result.evaluations);
|
println!("Total evaluations: {}", result.evaluations);
|
||||||
println!("Final population: {}", result.population.len());
|
println!("Final population: {}", result.population.len());
|
||||||
|
|||||||
@@ -599,7 +599,10 @@ mod tests {
|
|||||||
// direction and the loss tie-breaking.
|
// direction and the loss tie-breaking.
|
||||||
let translated = vec![vec![1.0, 0.0], vec![0.0, 1.0]];
|
let translated = vec![vec![1.0, 0.0], vec![0.0, 1.0]];
|
||||||
let p = estimate_p(&[0, 1], &translated, 2);
|
let p = estimate_p(&[0, 1], &translated, 2);
|
||||||
assert!((p - 0.25).abs() < 1e-12, "expected smallest candidate, got {p}");
|
assert!(
|
||||||
|
(p - 0.25).abs() < 1e-12,
|
||||||
|
"expected smallest candidate, got {p}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -625,7 +628,11 @@ mod tests {
|
|||||||
mutation: PolynomialMutation::new(bounds, 20.0, 1.0),
|
mutation: PolynomialMutation::new(bounds, 20.0, 1.0),
|
||||||
};
|
};
|
||||||
let mut opt = AgeMoea::new(
|
let mut opt = AgeMoea::new(
|
||||||
AgeMoeaConfig { population_size: 8, generations: 10, seed: 7 },
|
AgeMoeaConfig {
|
||||||
|
population_size: 8,
|
||||||
|
generations: 10,
|
||||||
|
seed: 7,
|
||||||
|
},
|
||||||
initializer,
|
initializer,
|
||||||
variation,
|
variation,
|
||||||
);
|
);
|
||||||
@@ -666,7 +673,11 @@ mod tests {
|
|||||||
};
|
};
|
||||||
for pop in [4_usize, 12, 30] {
|
for pop in [4_usize, 12, 30] {
|
||||||
let mut opt = AgeMoea::new(
|
let mut opt = AgeMoea::new(
|
||||||
AgeMoeaConfig { population_size: pop, generations: 5, seed: 13 },
|
AgeMoeaConfig {
|
||||||
|
population_size: pop,
|
||||||
|
generations: 5,
|
||||||
|
seed: 13,
|
||||||
|
},
|
||||||
initializer.clone(),
|
initializer.clone(),
|
||||||
variation.clone(),
|
variation.clone(),
|
||||||
);
|
);
|
||||||
@@ -690,7 +701,11 @@ mod tests {
|
|||||||
let pop = 6_usize;
|
let pop = 6_usize;
|
||||||
let gens = 4_usize;
|
let gens = 4_usize;
|
||||||
let mut opt = AgeMoea::new(
|
let mut opt = AgeMoea::new(
|
||||||
AgeMoeaConfig { population_size: pop, generations: gens, seed: 13 },
|
AgeMoeaConfig {
|
||||||
|
population_size: pop,
|
||||||
|
generations: gens,
|
||||||
|
seed: 13,
|
||||||
|
},
|
||||||
initializer,
|
initializer,
|
||||||
variation,
|
variation,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -644,7 +644,10 @@ mod tests {
|
|||||||
// k = exp(-0.5 * (1)^2) = exp(-0.5) ≈ 0.6065
|
// k = exp(-0.5 * (1)^2) = exp(-0.5) ≈ 0.6065
|
||||||
let got = rbf_kernel(&[0.0], &[1.0], &[1.0], 1.0);
|
let got = rbf_kernel(&[0.0], &[1.0], &[1.0], 1.0);
|
||||||
let expected = (-0.5_f64).exp();
|
let expected = (-0.5_f64).exp();
|
||||||
assert!((got - expected).abs() < 1e-12, "got {got}, expected {expected}");
|
assert!(
|
||||||
|
(got - expected).abs() < 1e-12,
|
||||||
|
"got {got}, expected {expected}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -766,19 +766,37 @@ mod tests {
|
|||||||
a.constraint_violation = 0.0;
|
a.constraint_violation = 0.0;
|
||||||
let mut b = Evaluation::new(vec![1.0]);
|
let mut b = Evaluation::new(vec![1.0]);
|
||||||
b.constraint_violation = 1.0;
|
b.constraint_violation = 1.0;
|
||||||
assert_eq!(compare_so(&a, &b, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
assert_eq!(compare_so(&b, &a, Direction::Minimize), std::cmp::Ordering::Greater);
|
compare_so(&a, &b, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
compare_so(&b, &a, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compare_so_two_feasible_under_min_and_max() {
|
fn compare_so_two_feasible_under_min_and_max() {
|
||||||
let a = Evaluation::new(vec![1.0]);
|
let a = Evaluation::new(vec![1.0]);
|
||||||
let b = Evaluation::new(vec![2.0]);
|
let b = Evaluation::new(vec![2.0]);
|
||||||
assert_eq!(compare_so(&a, &b, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
assert_eq!(compare_so(&b, &a, Direction::Minimize), std::cmp::Ordering::Greater);
|
compare_so(&a, &b, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
compare_so(&b, &a, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
);
|
||||||
// Maximize inverts.
|
// Maximize inverts.
|
||||||
assert_eq!(compare_so(&a, &b, Direction::Maximize), std::cmp::Ordering::Greater);
|
assert_eq!(
|
||||||
assert_eq!(compare_so(&b, &a, Direction::Maximize), std::cmp::Ordering::Less);
|
compare_so(&a, &b, Direction::Maximize),
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
compare_so(&b, &a, Direction::Maximize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -787,8 +805,14 @@ mod tests {
|
|||||||
a.constraint_violation = 0.5;
|
a.constraint_violation = 0.5;
|
||||||
let mut b = Evaluation::new(vec![0.0]);
|
let mut b = Evaluation::new(vec![0.0]);
|
||||||
b.constraint_violation = 1.0;
|
b.constraint_violation = 1.0;
|
||||||
assert_eq!(compare_so(&a, &b, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
assert_eq!(compare_so(&b, &a, Direction::Minimize), std::cmp::Ordering::Greater);
|
compare_so(&a, &b, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
compare_so(&b, &a, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -446,15 +446,24 @@ mod tests {
|
|||||||
fn compare_for_fitness_two_feasible_min_and_max() {
|
fn compare_for_fitness_two_feasible_min_and_max() {
|
||||||
let lo = fc(1.0);
|
let lo = fc(1.0);
|
||||||
let hi = fc(2.0);
|
let hi = fc(2.0);
|
||||||
assert_eq!(compare_for_fitness(&lo, &hi, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
assert_eq!(compare_for_fitness(&lo, &hi, Direction::Maximize), std::cmp::Ordering::Greater);
|
compare_for_fitness(&lo, &hi, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
compare_for_fitness(&lo, &hi, Direction::Maximize),
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compare_for_fitness_two_infeasible_lower_violation_wins() {
|
fn compare_for_fitness_two_infeasible_lower_violation_wins() {
|
||||||
let low = fc_cv(0.0, 0.3);
|
let low = fc_cv(0.0, 0.3);
|
||||||
let high = fc_cv(0.0, 0.9);
|
let high = fc_cv(0.0, 0.9);
|
||||||
assert_eq!(compare_for_fitness(&low, &high, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
|
compare_for_fitness(&low, &high, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `survival_selection` carries `elitism` parents and `n - elitism`
|
/// `survival_selection` carries `elitism` parents and `n - elitism`
|
||||||
|
|||||||
@@ -415,7 +415,12 @@ mod tests {
|
|||||||
mutation: PolynomialMutation::new(bounds, 20.0, 1.0),
|
mutation: PolynomialMutation::new(bounds, 20.0, 1.0),
|
||||||
};
|
};
|
||||||
let mut opt = Grea::new(
|
let mut opt = Grea::new(
|
||||||
GreaConfig { population_size: pop, generations: 5, grid_divisions: 8, seed: 3 },
|
GreaConfig {
|
||||||
|
population_size: pop,
|
||||||
|
generations: 5,
|
||||||
|
grid_divisions: 8,
|
||||||
|
seed: 3,
|
||||||
|
},
|
||||||
initializer,
|
initializer,
|
||||||
variation,
|
variation,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -290,7 +290,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn hill_climber_never_worsens_objective() {
|
fn hill_climber_never_worsens_objective() {
|
||||||
let mut opt = HillClimber::new(
|
let mut opt = HillClimber::new(
|
||||||
HillClimberConfig { iterations: 200, seed: 5 },
|
HillClimberConfig {
|
||||||
|
iterations: 200,
|
||||||
|
seed: 5,
|
||||||
|
},
|
||||||
RealBounds::new(vec![(-3.0, 3.0); 2]),
|
RealBounds::new(vec![(-3.0, 3.0); 2]),
|
||||||
GaussianMutation { sigma: 0.3 },
|
GaussianMutation { sigma: 0.3 },
|
||||||
);
|
);
|
||||||
@@ -306,7 +309,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn hill_climber_decreases_sphere() {
|
fn hill_climber_decreases_sphere() {
|
||||||
let mut opt = HillClimber::new(
|
let mut opt = HillClimber::new(
|
||||||
HillClimberConfig { iterations: 500, seed: 11 },
|
HillClimberConfig {
|
||||||
|
iterations: 500,
|
||||||
|
seed: 11,
|
||||||
|
},
|
||||||
RealBounds::new(vec![(-3.0, 3.0)]),
|
RealBounds::new(vec![(-3.0, 3.0)]),
|
||||||
GaussianMutation { sigma: 0.2 },
|
GaussianMutation { sigma: 0.2 },
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -595,6 +595,9 @@ mod tests {
|
|||||||
wins0 += 1;
|
wins0 += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert!(wins0 > 130, "index 0 won only {wins0}/200 — comparison likely flipped");
|
assert!(
|
||||||
|
wins0 > 130,
|
||||||
|
"index 0 won only {wins0}/200 — comparison likely flipped"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -451,16 +451,31 @@ mod tests {
|
|||||||
fn compare_feasibility_first_and_direction() {
|
fn compare_feasibility_first_and_direction() {
|
||||||
let feasible = Evaluation::new(vec![10.0]);
|
let feasible = Evaluation::new(vec![10.0]);
|
||||||
let infeasible = Evaluation::constrained(vec![0.0], 1.0);
|
let infeasible = Evaluation::constrained(vec![0.0], 1.0);
|
||||||
assert_eq!(compare(&feasible, &infeasible, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
assert_eq!(compare(&infeasible, &feasible, Direction::Minimize), std::cmp::Ordering::Greater);
|
compare(&feasible, &infeasible, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
compare(&infeasible, &feasible, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
);
|
||||||
let lo = Evaluation::new(vec![1.0]);
|
let lo = Evaluation::new(vec![1.0]);
|
||||||
let hi = Evaluation::new(vec![2.0]);
|
let hi = Evaluation::new(vec![2.0]);
|
||||||
assert_eq!(compare(&lo, &hi, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
assert_eq!(compare(&lo, &hi, Direction::Maximize), std::cmp::Ordering::Greater);
|
compare(&lo, &hi, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
compare(&lo, &hi, Direction::Maximize),
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
);
|
||||||
// two infeasible: smaller violation is "Less" (better).
|
// two infeasible: smaller violation is "Less" (better).
|
||||||
let v_lo = Evaluation::constrained(vec![0.0], 0.2);
|
let v_lo = Evaluation::constrained(vec![0.0], 0.2);
|
||||||
let v_hi = Evaluation::constrained(vec![0.0], 0.8);
|
let v_hi = Evaluation::constrained(vec![0.0], 0.8);
|
||||||
assert_eq!(compare(&v_lo, &v_hi, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
|
compare(&v_lo, &v_hi, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -501,7 +501,10 @@ mod tests {
|
|||||||
let pool = vec![ibea_cand(vec![1.0, 1.0]), ibea_cand(vec![2.0, 2.0])];
|
let pool = vec![ibea_cand(vec![1.0, 1.0]), ibea_cand(vec![2.0, 2.0])];
|
||||||
let fit = compute_fitness(&pool, &ibea_space(), 0.05);
|
let fit = compute_fitness(&pool, &ibea_space(), 0.05);
|
||||||
assert_eq!(fit.len(), 2);
|
assert_eq!(fit.len(), 2);
|
||||||
assert!(fit[0] > fit[1], "dominating point should score higher: {fit:?}");
|
assert!(
|
||||||
|
fit[0] > fit[1],
|
||||||
|
"dominating point should score higher: {fit:?}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+10
-2
@@ -408,8 +408,16 @@ mod tests {
|
|||||||
// Determinism cross-check.
|
// Determinism cross-check.
|
||||||
let mut opt2 = make_optimizer(7);
|
let mut opt2 = make_optimizer(7);
|
||||||
let r2 = opt2.run(&SchafferN1);
|
let r2 = opt2.run(&SchafferN1);
|
||||||
let f1: Vec<Vec<f64>> = r.pareto_front.iter().map(|c| c.evaluation.objectives.clone()).collect();
|
let f1: Vec<Vec<f64>> = r
|
||||||
let f2: Vec<Vec<f64>> = r2.pareto_front.iter().map(|c| c.evaluation.objectives.clone()).collect();
|
.pareto_front
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.evaluation.objectives.clone())
|
||||||
|
.collect();
|
||||||
|
let f2: Vec<Vec<f64>> = r2
|
||||||
|
.pareto_front
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.evaluation.objectives.clone())
|
||||||
|
.collect();
|
||||||
assert_eq!(f1, f2);
|
assert_eq!(f1, f2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -560,14 +560,26 @@ mod tests {
|
|||||||
fn compare_feasibility_first_and_direction() {
|
fn compare_feasibility_first_and_direction() {
|
||||||
let feasible = Evaluation::new(vec![10.0]);
|
let feasible = Evaluation::new(vec![10.0]);
|
||||||
let infeasible = Evaluation::constrained(vec![0.0], 1.0);
|
let infeasible = Evaluation::constrained(vec![0.0], 1.0);
|
||||||
assert_eq!(compare(&feasible, &infeasible, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
|
compare(&feasible, &infeasible, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
let lo = Evaluation::new(vec![1.0]);
|
let lo = Evaluation::new(vec![1.0]);
|
||||||
let hi = Evaluation::new(vec![2.0]);
|
let hi = Evaluation::new(vec![2.0]);
|
||||||
assert_eq!(compare(&lo, &hi, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
assert_eq!(compare(&lo, &hi, Direction::Maximize), std::cmp::Ordering::Greater);
|
compare(&lo, &hi, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
compare(&lo, &hi, Direction::Maximize),
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
);
|
||||||
let v_lo = Evaluation::constrained(vec![0.0], 0.2);
|
let v_lo = Evaluation::constrained(vec![0.0], 0.2);
|
||||||
let v_hi = Evaluation::constrained(vec![0.0], 0.8);
|
let v_hi = Evaluation::constrained(vec![0.0], 0.8);
|
||||||
assert_eq!(compare(&v_lo, &v_hi, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
|
compare(&v_lo, &v_hi, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+15
-3
@@ -313,7 +313,11 @@ mod tests {
|
|||||||
fn produces_deterministic_nonempty_front() {
|
fn produces_deterministic_nonempty_front() {
|
||||||
let make = || {
|
let make = || {
|
||||||
Paes::new(
|
Paes::new(
|
||||||
PaesConfig { iterations: 40, archive_size: 10, seed: 5 },
|
PaesConfig {
|
||||||
|
iterations: 40,
|
||||||
|
archive_size: 10,
|
||||||
|
seed: 5,
|
||||||
|
},
|
||||||
RealBounds::new(vec![(-5.0, 5.0)]),
|
RealBounds::new(vec![(-5.0, 5.0)]),
|
||||||
GaussianMutation { sigma: 0.3 },
|
GaussianMutation { sigma: 0.3 },
|
||||||
)
|
)
|
||||||
@@ -321,8 +325,16 @@ mod tests {
|
|||||||
let r1 = make().run(&SchafferN1);
|
let r1 = make().run(&SchafferN1);
|
||||||
let r2 = make().run(&SchafferN1);
|
let r2 = make().run(&SchafferN1);
|
||||||
assert!(!r1.pareto_front.is_empty());
|
assert!(!r1.pareto_front.is_empty());
|
||||||
let f1: Vec<Vec<f64>> = r1.pareto_front.iter().map(|c| c.evaluation.objectives.clone()).collect();
|
let f1: Vec<Vec<f64>> = r1
|
||||||
let f2: Vec<Vec<f64>> = r2.pareto_front.iter().map(|c| c.evaluation.objectives.clone()).collect();
|
.pareto_front
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.evaluation.objectives.clone())
|
||||||
|
.collect();
|
||||||
|
let f2: Vec<Vec<f64>> = r2
|
||||||
|
.pareto_front
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.evaluation.objectives.clone())
|
||||||
|
.collect();
|
||||||
assert_eq!(f1, f2);
|
assert_eq!(f1, f2);
|
||||||
// Archive never exceeds its configured cap.
|
// Archive never exceeds its configured cap.
|
||||||
assert!(r1.pareto_front.len() <= 10);
|
assert!(r1.pareto_front.len() <= 10);
|
||||||
|
|||||||
@@ -560,6 +560,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
// Index 2 wins whenever it's drawn against 0 or 1, plus half its
|
// Index 2 wins whenever it's drawn against 0 or 1, plus half its
|
||||||
// self-draws — clear majority.
|
// self-draws — clear majority.
|
||||||
assert!(picked_uncrowded > 150, "uncrowded picked {picked_uncrowded}/300");
|
assert!(
|
||||||
|
picked_uncrowded > 150,
|
||||||
|
"uncrowded picked {picked_uncrowded}/300"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,7 +224,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn best_is_no_worse_than_any_sample() {
|
fn best_is_no_worse_than_any_sample() {
|
||||||
let mut opt = RandomSearch::new(
|
let mut opt = RandomSearch::new(
|
||||||
RandomSearchConfig { iterations: 50, batch_size: 2, seed: 9 },
|
RandomSearchConfig {
|
||||||
|
iterations: 50,
|
||||||
|
batch_size: 2,
|
||||||
|
seed: 9,
|
||||||
|
},
|
||||||
RealBounds::new(vec![(-3.0, 3.0)]),
|
RealBounds::new(vec![(-3.0, 3.0)]),
|
||||||
);
|
);
|
||||||
let r = opt.run(&Sphere1D);
|
let r = opt.run(&Sphere1D);
|
||||||
|
|||||||
@@ -591,6 +591,9 @@ mod tests {
|
|||||||
fn smallest_neighbor_angle_of_orthogonal_refs_is_pi_over_2() {
|
fn smallest_neighbor_angle_of_orthogonal_refs_is_pi_over_2() {
|
||||||
let refs = vec![vec![1.0, 0.0], vec![0.0, 1.0]];
|
let refs = vec![vec![1.0, 0.0], vec![0.0, 1.0]];
|
||||||
let a = smallest_neighbor_angle(&refs);
|
let a = smallest_neighbor_angle(&refs);
|
||||||
assert!((a - std::f64::consts::FRAC_PI_2).abs() < 1e-9, "angle = {a}");
|
assert!(
|
||||||
|
(a - std::f64::consts::FRAC_PI_2).abs() < 1e-9,
|
||||||
|
"angle = {a}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-3
@@ -483,11 +483,20 @@ mod tests {
|
|||||||
fn compare_feasibility_first_and_direction() {
|
fn compare_feasibility_first_and_direction() {
|
||||||
let feasible = Evaluation::new(vec![100.0]);
|
let feasible = Evaluation::new(vec![100.0]);
|
||||||
let infeasible = Evaluation::constrained(vec![0.0], 1.0);
|
let infeasible = Evaluation::constrained(vec![0.0], 1.0);
|
||||||
assert_eq!(compare(&feasible, &infeasible, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
|
compare(&feasible, &infeasible, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
let lo = Evaluation::new(vec![1.0]);
|
let lo = Evaluation::new(vec![1.0]);
|
||||||
let hi = Evaluation::new(vec![2.0]);
|
let hi = Evaluation::new(vec![2.0]);
|
||||||
assert_eq!(compare(&lo, &hi, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
assert_eq!(compare(&lo, &hi, Direction::Maximize), std::cmp::Ordering::Greater);
|
compare(&lo, &hi, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
compare(&lo, &hi, Direction::Maximize),
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+16
-4
@@ -456,14 +456,26 @@ mod tests {
|
|||||||
fn compare_so_feasibility_first_and_direction() {
|
fn compare_so_feasibility_first_and_direction() {
|
||||||
let feasible = Evaluation::new(vec![100.0]);
|
let feasible = Evaluation::new(vec![100.0]);
|
||||||
let infeasible = Evaluation::constrained(vec![0.0], 1.0);
|
let infeasible = Evaluation::constrained(vec![0.0], 1.0);
|
||||||
assert_eq!(compare_so(&feasible, &infeasible, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
|
compare_so(&feasible, &infeasible, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
let lo = Evaluation::new(vec![1.0]);
|
let lo = Evaluation::new(vec![1.0]);
|
||||||
let hi = Evaluation::new(vec![2.0]);
|
let hi = Evaluation::new(vec![2.0]);
|
||||||
assert_eq!(compare_so(&lo, &hi, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
assert_eq!(compare_so(&lo, &hi, Direction::Maximize), std::cmp::Ordering::Greater);
|
compare_so(&lo, &hi, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
compare_so(&lo, &hi, Direction::Maximize),
|
||||||
|
std::cmp::Ordering::Greater
|
||||||
|
);
|
||||||
let v_lo = Evaluation::constrained(vec![0.0], 0.2);
|
let v_lo = Evaluation::constrained(vec![0.0], 0.2);
|
||||||
let v_hi = Evaluation::constrained(vec![0.0], 0.8);
|
let v_hi = Evaluation::constrained(vec![0.0], 0.8);
|
||||||
assert_eq!(compare_so(&v_lo, &v_hi, Direction::Minimize), std::cmp::Ordering::Less);
|
assert_eq!(
|
||||||
|
compare_so(&v_lo, &v_hi, Direction::Minimize),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
+33
-22
@@ -598,9 +598,18 @@ mod tests {
|
|||||||
let v: Vec<i64> = vec![-3, 0, 7];
|
let v: Vec<i64> = vec![-3, 0, 7];
|
||||||
let got = v.to_decision_values();
|
let got = v.to_decision_values();
|
||||||
assert_eq!(got.len(), 3);
|
assert_eq!(got.len(), 3);
|
||||||
assert_eq!(got[0], serde_json::Value::Number(serde_json::Number::from(-3i64)));
|
assert_eq!(
|
||||||
assert_eq!(got[1], serde_json::Value::Number(serde_json::Number::from(0i64)));
|
got[0],
|
||||||
assert_eq!(got[2], serde_json::Value::Number(serde_json::Number::from(7i64)));
|
serde_json::Value::Number(serde_json::Number::from(-3i64))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
got[1],
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(0i64))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
got[2],
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(7i64))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pins the *exact* booleans, not just the count.
|
/// Pins the *exact* booleans, not just the count.
|
||||||
@@ -642,8 +651,7 @@ mod tests {
|
|||||||
fn from_result_propagates_evaluation_and_generation_counts() {
|
fn from_result_propagates_evaluation_and_generation_counts() {
|
||||||
let problem = SingleObjMin;
|
let problem = SingleObjMin;
|
||||||
let cands = vec![Candidate::new(vec![1.0], Evaluation::new(vec![1.0]))];
|
let cands = vec![Candidate::new(vec![1.0], Evaluation::new(vec![1.0]))];
|
||||||
let result =
|
let result = OptimizationResult::new(Population::new(cands.clone()), cands, None, 137, 9);
|
||||||
OptimizationResult::new(Population::new(cands.clone()), cands, None, 137, 9);
|
|
||||||
let export = ExplorerExport::from_result(&problem, &result);
|
let export = ExplorerExport::from_result(&problem, &result);
|
||||||
assert_eq!(export.run.evaluations, 137);
|
assert_eq!(export.run.evaluations, 137);
|
||||||
assert_eq!(export.run.generations, 9);
|
assert_eq!(export.run.generations, 9);
|
||||||
@@ -654,8 +662,8 @@ mod tests {
|
|||||||
fn with_problem_name_sets_field_and_preserves_other_state() {
|
fn with_problem_name_sets_field_and_preserves_other_state() {
|
||||||
let problem = SingleObjMin;
|
let problem = SingleObjMin;
|
||||||
let result = make_result(vec![vec![1.0]], |d| vec![d[0]]);
|
let result = make_result(vec![vec![1.0]], |d| vec![d[0]]);
|
||||||
let export = ExplorerExport::from_result(&problem, &result)
|
let export =
|
||||||
.with_problem_name("Toy Problem");
|
ExplorerExport::from_result(&problem, &result).with_problem_name("Toy Problem");
|
||||||
assert_eq!(export.run.problem_name.as_deref(), Some("Toy Problem"));
|
assert_eq!(export.run.problem_name.as_deref(), Some("Toy Problem"));
|
||||||
// The candidates and objectives should still be intact, proving the
|
// The candidates and objectives should still be intact, proving the
|
||||||
// chained builder isn't replacing the whole struct.
|
// chained builder isn't replacing the whole struct.
|
||||||
@@ -678,9 +686,12 @@ mod tests {
|
|||||||
fn with_timestamp_sets_field_and_preserves_other_state() {
|
fn with_timestamp_sets_field_and_preserves_other_state() {
|
||||||
let problem = SingleObjMin;
|
let problem = SingleObjMin;
|
||||||
let result = make_result(vec![vec![1.0]], |d| vec![d[0]]);
|
let result = make_result(vec![vec![1.0]], |d| vec![d[0]]);
|
||||||
let export = ExplorerExport::from_result(&problem, &result)
|
let export =
|
||||||
.with_timestamp("2025-01-01T00:00:00Z");
|
ExplorerExport::from_result(&problem, &result).with_timestamp("2025-01-01T00:00:00Z");
|
||||||
assert_eq!(export.run.timestamp.as_deref(), Some("2025-01-01T00:00:00Z"));
|
assert_eq!(
|
||||||
|
export.run.timestamp.as_deref(),
|
||||||
|
Some("2025-01-01T00:00:00Z")
|
||||||
|
);
|
||||||
assert_eq!(export.candidates.len(), 1);
|
assert_eq!(export.candidates.len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -708,8 +719,7 @@ mod tests {
|
|||||||
fn to_writer_emits_full_export() {
|
fn to_writer_emits_full_export() {
|
||||||
let problem = SingleObjMin;
|
let problem = SingleObjMin;
|
||||||
let result = make_result(vec![vec![1.0]], |d| vec![d[0]]);
|
let result = make_result(vec![vec![1.0]], |d| vec![d[0]]);
|
||||||
let export = ExplorerExport::from_result(&problem, &result)
|
let export = ExplorerExport::from_result(&problem, &result).with_problem_name("MyProblem");
|
||||||
.with_problem_name("MyProblem");
|
|
||||||
let mut buf: Vec<u8> = Vec::new();
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
export.to_writer(&mut buf).unwrap();
|
export.to_writer(&mut buf).unwrap();
|
||||||
assert!(!buf.is_empty());
|
assert!(!buf.is_empty());
|
||||||
@@ -725,16 +735,15 @@ mod tests {
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
let problem = SingleObjMin;
|
let problem = SingleObjMin;
|
||||||
let result = make_result(vec![vec![1.0]], |d| vec![d[0]]);
|
let result = make_result(vec![vec![1.0]], |d| vec![d[0]]);
|
||||||
let export = ExplorerExport::from_result(&problem, &result)
|
let export = ExplorerExport::from_result(&problem, &result).with_problem_name("OnDisk");
|
||||||
.with_problem_name("OnDisk");
|
|
||||||
let dir = std::env::temp_dir();
|
let dir = std::env::temp_dir();
|
||||||
let path = dir.join(format!(
|
let path = dir.join(format!("heuropt-explorer-test-{}.json", std::process::id()));
|
||||||
"heuropt-explorer-test-{}.json",
|
|
||||||
std::process::id()
|
|
||||||
));
|
|
||||||
export.to_file(&path).unwrap();
|
export.to_file(&path).unwrap();
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
std::fs::File::open(&path).unwrap().read_to_string(&mut s).unwrap();
|
std::fs::File::open(&path)
|
||||||
|
.unwrap()
|
||||||
|
.read_to_string(&mut s)
|
||||||
|
.unwrap();
|
||||||
let _ = std::fs::remove_file(&path);
|
let _ = std::fs::remove_file(&path);
|
||||||
let back: ExplorerExport = serde_json::from_str(&s).unwrap();
|
let back: ExplorerExport = serde_json::from_str(&s).unwrap();
|
||||||
assert_eq!(back.run.problem_name.as_deref(), Some("OnDisk"));
|
assert_eq!(back.run.problem_name.as_deref(), Some("OnDisk"));
|
||||||
@@ -777,7 +786,10 @@ mod tests {
|
|||||||
));
|
));
|
||||||
super::to_file(&path, &problem, &DummyAlgo, &result).unwrap();
|
super::to_file(&path, &problem, &DummyAlgo, &result).unwrap();
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
std::fs::File::open(&path).unwrap().read_to_string(&mut s).unwrap();
|
std::fs::File::open(&path)
|
||||||
|
.unwrap()
|
||||||
|
.read_to_string(&mut s)
|
||||||
|
.unwrap();
|
||||||
let _ = std::fs::remove_file(&path);
|
let _ = std::fs::remove_file(&path);
|
||||||
let back: ExplorerExport = serde_json::from_str(&s).unwrap();
|
let back: ExplorerExport = serde_json::from_str(&s).unwrap();
|
||||||
assert_eq!(back.run.algorithm.as_deref(), Some("DummyAlgo"));
|
assert_eq!(back.run.algorithm.as_deref(), Some("DummyAlgo"));
|
||||||
@@ -877,8 +889,7 @@ mod tests {
|
|||||||
/// the pad (too few objectives) and truncate (too many) cases.
|
/// the pad (too few objectives) and truncate (too many) cases.
|
||||||
#[test]
|
#[test]
|
||||||
fn candidate_to_export_pads_short_objectives_with_nan() {
|
fn candidate_to_export_pads_short_objectives_with_nan() {
|
||||||
let c: Candidate<Vec<f64>> =
|
let c: Candidate<Vec<f64>> = Candidate::new(vec![1.0], Evaluation::new(vec![1.0]));
|
||||||
Candidate::new(vec![1.0], Evaluation::new(vec![1.0]));
|
|
||||||
let exported = candidate_to_export(&c, 0, 3);
|
let exported = candidate_to_export(&c, 0, 3);
|
||||||
assert_eq!(exported.objectives.len(), 3);
|
assert_eq!(exported.objectives.len(), 3);
|
||||||
assert_eq!(exported.objectives[0], 1.0);
|
assert_eq!(exported.objectives[0], 1.0);
|
||||||
|
|||||||
+10
-10
@@ -560,12 +560,12 @@ mod nd_tests {
|
|||||||
/// positive value for a dominating point.
|
/// positive value for a dominating point.
|
||||||
#[test]
|
#[test]
|
||||||
fn hypervolume_nd_from_evaluations_empty_and_nonempty() {
|
fn hypervolume_nd_from_evaluations_empty_and_nonempty() {
|
||||||
let s = ObjectiveSpace::new(vec![
|
let s = ObjectiveSpace::new(vec![Objective::minimize("f1"), Objective::minimize("f2")]);
|
||||||
Objective::minimize("f1"),
|
|
||||||
Objective::minimize("f2"),
|
|
||||||
]);
|
|
||||||
let empty: Vec<&Evaluation> = Vec::new();
|
let empty: Vec<&Evaluation> = Vec::new();
|
||||||
assert_eq!(hypervolume_nd_from_evaluations(&empty, &s, &[2.0, 2.0]), 0.0);
|
assert_eq!(
|
||||||
|
hypervolume_nd_from_evaluations(&empty, &s, &[2.0, 2.0]),
|
||||||
|
0.0
|
||||||
|
);
|
||||||
|
|
||||||
let e = Evaluation::new(vec![1.0, 1.0]);
|
let e = Evaluation::new(vec![1.0, 1.0]);
|
||||||
let evals = vec![&e];
|
let evals = vec![&e];
|
||||||
@@ -577,13 +577,13 @@ mod nd_tests {
|
|||||||
/// A point that does not strictly dominate the reference contributes 0.
|
/// A point that does not strictly dominate the reference contributes 0.
|
||||||
#[test]
|
#[test]
|
||||||
fn hypervolume_nd_from_evaluations_skips_non_dominating() {
|
fn hypervolume_nd_from_evaluations_skips_non_dominating() {
|
||||||
let s = ObjectiveSpace::new(vec![
|
let s = ObjectiveSpace::new(vec![Objective::minimize("f1"), Objective::minimize("f2")]);
|
||||||
Objective::minimize("f1"),
|
|
||||||
Objective::minimize("f2"),
|
|
||||||
]);
|
|
||||||
// (2, 1): axis 0 equals the reference → not strictly dominating.
|
// (2, 1): axis 0 equals the reference → not strictly dominating.
|
||||||
let e = Evaluation::new(vec![2.0, 1.0]);
|
let e = Evaluation::new(vec![2.0, 1.0]);
|
||||||
let evals = vec![&e];
|
let evals = vec![&e];
|
||||||
assert_eq!(hypervolume_nd_from_evaluations(&evals, &s, &[2.0, 2.0]), 0.0);
|
assert_eq!(
|
||||||
|
hypervolume_nd_from_evaluations(&evals, &s, &[2.0, 2.0]),
|
||||||
|
0.0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,10 @@ mod tests {
|
|||||||
];
|
];
|
||||||
let got = spacing(&front, &s);
|
let got = spacing(&front, &s);
|
||||||
let expected = (1536.0_f64 / 27.0).sqrt();
|
let expected = (1536.0_f64 / 27.0).sqrt();
|
||||||
assert!((got - expected).abs() < 1e-9, "got {got}, expected {expected}");
|
assert!(
|
||||||
|
(got - expected).abs() < 1e-9,
|
||||||
|
"got {got}, expected {expected}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A perfectly even front has zero spacing — the variance term is 0.
|
/// A perfectly even front has zero spacing — the variance term is 0.
|
||||||
|
|||||||
@@ -994,7 +994,10 @@ mod tests {
|
|||||||
let c = m.vary(std::slice::from_ref(&parent), &mut rng);
|
let c = m.vary(std::slice::from_ref(&parent), &mut rng);
|
||||||
c[0] != parent
|
c[0] != parent
|
||||||
});
|
});
|
||||||
assert!(any_changed, "InversionMutation never modified an 8-element parent across 30 seeds");
|
assert!(
|
||||||
|
any_changed,
|
||||||
|
"InversionMutation never modified an 8-element parent across 30 seeds"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `InsertionMutation` shifts an element across many seeds; at least one
|
/// `InsertionMutation` shifts an element across many seeds; at least one
|
||||||
@@ -1195,6 +1198,9 @@ mod tests {
|
|||||||
let kids = erx.vary(&[p1.clone(), p2.clone()], &mut rng);
|
let kids = erx.vary(&[p1.clone(), p2.clone()], &mut rng);
|
||||||
kids[0] != kids[1]
|
kids[0] != kids[1]
|
||||||
});
|
});
|
||||||
assert!(any_distinct, "ERX never produced distinct children across 30 seeds");
|
assert!(
|
||||||
|
any_distinct,
|
||||||
|
"ERX never produced distinct children across 30 seeds"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-7
@@ -733,7 +733,11 @@ mod tests {
|
|||||||
// snapshot, by design.
|
// snapshot, by design.
|
||||||
|
|
||||||
fn assert_close_slice(got: &[f64], want: &[f64], tol: f64) {
|
fn assert_close_slice(got: &[f64], want: &[f64], tol: f64) {
|
||||||
assert_eq!(got.len(), want.len(), "length mismatch: got {got:?} want {want:?}");
|
assert_eq!(
|
||||||
|
got.len(),
|
||||||
|
want.len(),
|
||||||
|
"length mismatch: got {got:?} want {want:?}"
|
||||||
|
);
|
||||||
for (g, w) in got.iter().zip(want.iter()) {
|
for (g, w) in got.iter().zip(want.iter()) {
|
||||||
assert!((g - w).abs() < tol, "got {g}, want {w}; full got = {got:?}");
|
assert!((g - w).abs() < tol, "got {g}, want {w}; full got = {got:?}");
|
||||||
}
|
}
|
||||||
@@ -747,7 +751,11 @@ mod tests {
|
|||||||
let children = m.vary(std::slice::from_ref(&parent), &mut rng);
|
let children = m.vary(std::slice::from_ref(&parent), &mut rng);
|
||||||
assert_close_slice(
|
assert_close_slice(
|
||||||
&children[0],
|
&children[0],
|
||||||
&[1.034_713_959_180_981_7, 2.066_469_060_997_062_6, 3.131_288_178_686_977],
|
&[
|
||||||
|
1.034_713_959_180_981_7,
|
||||||
|
2.066_469_060_997_062_6,
|
||||||
|
3.131_288_178_686_977,
|
||||||
|
],
|
||||||
1e-12,
|
1e-12,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -854,7 +862,10 @@ mod tests {
|
|||||||
// Same seed → same δ; the only difference is the (hi-lo) factor.
|
// Same seed → same δ; the only difference is the (hi-lo) factor.
|
||||||
// Ratio must be ≈ 10.
|
// Ratio must be ≈ 10.
|
||||||
let ratio = wide / narrow;
|
let ratio = wide / narrow;
|
||||||
assert!((ratio - 10.0).abs() < 1e-12, "ratio = {ratio}, narrow={narrow}, wide={wide}");
|
assert!(
|
||||||
|
(ratio - 10.0).abs() < 1e-12,
|
||||||
|
"ratio = {ratio}, narrow={narrow}, wide={wide}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -890,10 +901,7 @@ mod tests {
|
|||||||
fn mantegna_sigma_u_alpha_1_0_pinned() {
|
fn mantegna_sigma_u_alpha_1_0_pinned() {
|
||||||
// alpha = 1.0: sin(π/2) = 1, gamma(2) = 1, gamma(1) = 1 → σᵤ ≈ 1.
|
// alpha = 1.0: sin(π/2) = 1, gamma(2) = 1, gamma(1) = 1 → σᵤ ≈ 1.
|
||||||
let got = mantegna_sigma_u(1.0);
|
let got = mantegna_sigma_u(1.0);
|
||||||
assert!(
|
assert!((got - 1.0).abs() < 1e-12, "mantegna_sigma_u(1.0) = {got}",);
|
||||||
(got - 1.0).abs() < 1e-12,
|
|
||||||
"mantegna_sigma_u(1.0) = {got}",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -317,9 +317,15 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn infeasible_candidate_with_smaller_violation_evicts_larger() {
|
fn infeasible_candidate_with_smaller_violation_evicts_larger() {
|
||||||
let mut a = ParetoArchive::<u32>::new(space_min2());
|
let mut a = ParetoArchive::<u32>::new(space_min2());
|
||||||
a.insert(Candidate::new(1u32, Evaluation::constrained(vec![0.0, 0.0], 1.0)));
|
a.insert(Candidate::new(
|
||||||
|
1u32,
|
||||||
|
Evaluation::constrained(vec![0.0, 0.0], 1.0),
|
||||||
|
));
|
||||||
// Smaller violation → dominates the existing infeasible member.
|
// Smaller violation → dominates the existing infeasible member.
|
||||||
a.insert(Candidate::new(2u32, Evaluation::constrained(vec![9.0, 9.0], 0.5)));
|
a.insert(Candidate::new(
|
||||||
|
2u32,
|
||||||
|
Evaluation::constrained(vec![9.0, 9.0], 0.5),
|
||||||
|
));
|
||||||
assert_eq!(a.members().len(), 1);
|
assert_eq!(a.members().len(), 1);
|
||||||
assert_eq!(a.members()[0].decision, 2);
|
assert_eq!(a.members()[0].decision, 2);
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-2
@@ -169,7 +169,11 @@ mod tests {
|
|||||||
fn interior_point_distance_is_pinned() {
|
fn interior_point_distance_is_pinned() {
|
||||||
let s = space_min2();
|
let s = space_min2();
|
||||||
// Front along the line f1 + f2 = 4: (0,4), (2,2), (4,0).
|
// Front along the line f1 + f2 = 4: (0,4), (2,2), (4,0).
|
||||||
let pop = [cand(vec![0.0, 4.0]), cand(vec![2.0, 2.0]), cand(vec![4.0, 0.0])];
|
let pop = [
|
||||||
|
cand(vec![0.0, 4.0]),
|
||||||
|
cand(vec![2.0, 2.0]),
|
||||||
|
cand(vec![4.0, 0.0]),
|
||||||
|
];
|
||||||
let d = crowding_distance(&pop, &[0, 1, 2], &s);
|
let d = crowding_distance(&pop, &[0, 1, 2], &s);
|
||||||
// Boundary points are infinite; the middle point gets
|
// Boundary points are infinite; the middle point gets
|
||||||
// (4-0)/4 + (4-0)/4 = 2.0 (objective 0 span 4, objective 1 span 4).
|
// (4-0)/4 + (4-0)/4 = 2.0 (objective 0 span 4, objective 1 span 4).
|
||||||
@@ -184,7 +188,11 @@ mod tests {
|
|||||||
fn asymmetric_interior_distance_is_pinned() {
|
fn asymmetric_interior_distance_is_pinned() {
|
||||||
let s = space_min2();
|
let s = space_min2();
|
||||||
// (0,10), (1,2), (10,0): objective-0 span = 10, objective-1 span = 10.
|
// (0,10), (1,2), (10,0): objective-0 span = 10, objective-1 span = 10.
|
||||||
let pop = [cand(vec![0.0, 10.0]), cand(vec![1.0, 2.0]), cand(vec![10.0, 0.0])];
|
let pop = [
|
||||||
|
cand(vec![0.0, 10.0]),
|
||||||
|
cand(vec![1.0, 2.0]),
|
||||||
|
cand(vec![10.0, 0.0]),
|
||||||
|
];
|
||||||
let d = crowding_distance(&pop, &[0, 1, 2], &s);
|
let d = crowding_distance(&pop, &[0, 1, 2], &s);
|
||||||
// middle point: obj0 (10-0)/10 = 1.0; obj1 (10-0)/10 = 1.0 → 2.0.
|
// middle point: obj0 (10-0)/10 = 1.0; obj1 (10-0)/10 = 1.0 → 2.0.
|
||||||
assert!((d[1] - 2.0).abs() < 1e-12, "got {}", d[1]);
|
assert!((d[1] - 2.0).abs() < 1e-12, "got {}", d[1]);
|
||||||
|
|||||||
+42
-10
@@ -282,15 +282,27 @@ mod tests {
|
|||||||
let feasible = cand_min(1, 100.0);
|
let feasible = cand_min(1, 100.0);
|
||||||
let infeasible = constrained(2, 0.0, 1.0);
|
let infeasible = constrained(2, 0.0, 1.0);
|
||||||
assert!(challenger_wins(&feasible, &infeasible, Direction::Minimize));
|
assert!(challenger_wins(&feasible, &infeasible, Direction::Minimize));
|
||||||
assert!(!challenger_wins(&infeasible, &feasible, Direction::Minimize));
|
assert!(!challenger_wins(
|
||||||
|
&infeasible,
|
||||||
|
&feasible,
|
||||||
|
Direction::Minimize
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn challenger_wins_two_infeasible_compares_violation() {
|
fn challenger_wins_two_infeasible_compares_violation() {
|
||||||
let less_violating = constrained(1, 0.0, 0.5);
|
let less_violating = constrained(1, 0.0, 0.5);
|
||||||
let more_violating = constrained(2, 0.0, 1.0);
|
let more_violating = constrained(2, 0.0, 1.0);
|
||||||
assert!(challenger_wins(&less_violating, &more_violating, Direction::Minimize));
|
assert!(challenger_wins(
|
||||||
assert!(!challenger_wins(&more_violating, &less_violating, Direction::Minimize));
|
&less_violating,
|
||||||
|
&more_violating,
|
||||||
|
Direction::Minimize
|
||||||
|
));
|
||||||
|
assert!(!challenger_wins(
|
||||||
|
&more_violating,
|
||||||
|
&less_violating,
|
||||||
|
Direction::Minimize
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -330,17 +342,37 @@ mod tests {
|
|||||||
let feasible_a = Evaluation::new(vec![10.0]);
|
let feasible_a = Evaluation::new(vec![10.0]);
|
||||||
let infeasible_b = Evaluation::constrained(vec![0.0], 1.0);
|
let infeasible_b = Evaluation::constrained(vec![0.0], 1.0);
|
||||||
// feasible vs infeasible
|
// feasible vs infeasible
|
||||||
assert!(better_by_feasibility(&feasible_a, &infeasible_b, Direction::Minimize));
|
assert!(better_by_feasibility(
|
||||||
assert!(!better_by_feasibility(&infeasible_b, &feasible_a, Direction::Minimize));
|
&feasible_a,
|
||||||
|
&infeasible_b,
|
||||||
|
Direction::Minimize
|
||||||
|
));
|
||||||
|
assert!(!better_by_feasibility(
|
||||||
|
&infeasible_b,
|
||||||
|
&feasible_a,
|
||||||
|
Direction::Minimize
|
||||||
|
));
|
||||||
// two infeasible: smaller violation wins
|
// two infeasible: smaller violation wins
|
||||||
let low_cv = Evaluation::constrained(vec![0.0], 0.3);
|
let low_cv = Evaluation::constrained(vec![0.0], 0.3);
|
||||||
let high_cv = Evaluation::constrained(vec![0.0], 0.9);
|
let high_cv = Evaluation::constrained(vec![0.0], 0.9);
|
||||||
assert!(better_by_feasibility(&low_cv, &high_cv, Direction::Minimize));
|
assert!(better_by_feasibility(
|
||||||
assert!(!better_by_feasibility(&high_cv, &low_cv, Direction::Minimize));
|
&low_cv,
|
||||||
|
&high_cv,
|
||||||
|
Direction::Minimize
|
||||||
|
));
|
||||||
|
assert!(!better_by_feasibility(
|
||||||
|
&high_cv,
|
||||||
|
&low_cv,
|
||||||
|
Direction::Minimize
|
||||||
|
));
|
||||||
// two feasible: delegates to better_by_objective
|
// two feasible: delegates to better_by_objective
|
||||||
let feasible_lower = Evaluation::new(vec![1.0]);
|
let feasible_lower = Evaluation::new(vec![1.0]);
|
||||||
let feasible_higher = Evaluation::new(vec![2.0]);
|
let feasible_higher = Evaluation::new(vec![2.0]);
|
||||||
assert!(better_by_feasibility(&feasible_lower, &feasible_higher, Direction::Minimize));
|
assert!(better_by_feasibility(
|
||||||
|
&feasible_lower,
|
||||||
|
&feasible_higher,
|
||||||
|
Direction::Minimize
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -349,8 +381,8 @@ mod tests {
|
|||||||
// must rank first regardless of objective value.
|
// must rank first regardless of objective value.
|
||||||
let s = ObjectiveSpace::new(vec![Objective::minimize("f")]);
|
let s = ObjectiveSpace::new(vec![Objective::minimize("f")]);
|
||||||
let pop = [
|
let pop = [
|
||||||
constrained(1, 0.0, 2.0), // infeasible, great objective
|
constrained(1, 0.0, 2.0), // infeasible, great objective
|
||||||
cand_min(2, 100.0), // feasible, terrible objective
|
cand_min(2, 100.0), // feasible, terrible objective
|
||||||
];
|
];
|
||||||
let mut rng = rng_from_seed(7);
|
let mut rng = rng_from_seed(7);
|
||||||
let picks = stochastic_ranking_select(&pop, &s, 0.0, 1, &mut rng);
|
let picks = stochastic_ranking_select(&pop, &s, 0.0, 1, &mut rng);
|
||||||
|
|||||||
Reference in New Issue
Block a user