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:
2026-05-14 03:41:38 -06:00
parent 2ca8d71b94
commit 6aee6d6318
29 changed files with 466 additions and 161 deletions
+52 -14
View File
@@ -33,23 +33,59 @@ use heuropt::prelude::*;
/// First 25 cities of TSPLIB KroA100 (EUC_2D).
const KROA_25: [(f64, f64); 25] = [
(1380.0, 939.0), (2848.0, 96.0), (3510.0, 1671.0), (457.0, 334.0),
(3888.0, 666.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),
(1380.0, 939.0),
(2848.0, 96.0),
(3510.0, 1671.0),
(457.0, 334.0),
(3888.0, 666.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),
];
/// First 25 cities of TSPLIB KroB100 (EUC_2D).
const KROB_25: [(f64, f64); 25] = [
(3140.0, 1401.0),(556.0, 1056.0), (3675.0, 1522.0), (1182.0, 1853.0),
(3595.0, 1340.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),
(3140.0, 1401.0),
(556.0, 1056.0),
(3675.0, 1522.0),
(1182.0, 1853.0),
(3595.0, 1340.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),
];
@@ -182,6 +218,8 @@ fn main() {
let owned: Vec<Candidate<Vec<usize>>> = result.pareto_front.to_vec();
let hv = hypervolume_2d(&owned, &problem.objectives(), ref_point);
println!();
println!("Hypervolume vs. reference ({}, {}): {:.0}",
ref_point[0], ref_point[1], hv);
println!(
"Hypervolume vs. reference ({}, {}): {:.0}",
ref_point[0], ref_point[1], hv
);
}
+32 -24
View File
@@ -39,24 +39,21 @@ const N_ITEMS: usize = 30;
/// Profit vector A (one of two objectives), U(10, 100) style.
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,
23.0, 91.0, 12.0, 67.0, 45.0, 58.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,
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,
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,
];
/// Profit vector B (the other objective). Intentionally anti-correlated with
/// A on many items so the Pareto front spans a wide trade-off.
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,
77.0, 34.0, 95.0, 22.0, 71.0, 88.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,
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,
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,
];
/// Item weights.
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,
44.0, 16.0, 67.0, 88.0, 25.0, 51.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,
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,
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,
];
/// 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 {
let (pa, pb, w) = take.iter().enumerate().fold(
(0.0_f64, 0.0_f64, 0.0_f64),
|(pa, pb, w), (i, &t)| {
if t {
(pa + PROFITS_A[i], pb + PROFITS_B[i], w + WEIGHTS[i])
} else {
(pa, pb, w)
}
},
);
let (pa, pb, w) =
take.iter()
.enumerate()
.fold((0.0_f64, 0.0_f64, 0.0_f64), |(pa, pb, w), (i, &t)| {
if t {
(pa + PROFITS_A[i], pb + PROFITS_B[i], w + WEIGHTS[i])
} else {
(pa, pb, w)
}
});
// Penalty: large coefficient on weight overrun, applied to both objectives.
let overrun = (w - self.cap).max(0.0);
let penalty = 1000.0 * overrun;
@@ -122,7 +119,10 @@ struct OnePointCrossoverBool;
impl Variation<Vec<bool>> for OnePointCrossoverBool {
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 p2 = &parents[1];
assert_eq!(p1.len(), p2.len(), "parent lengths differ");
@@ -154,14 +154,19 @@ fn main() {
RandomBinary { n: N_ITEMS },
CompositeVariation {
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);
println!("Bi-objective 0/1 knapsack — ZitzlerThiele style, 30 items");
println!("Capacity = {:.0} (≈ half of total weight {:.0})",
cap, WEIGHTS.iter().sum::<f64>());
println!(
"Capacity = {:.0} (≈ half of total weight {:.0})",
cap,
WEIGHTS.iter().sum::<f64>()
);
println!();
println!("Total evaluations: {}", result.evaluations);
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 hv = hypervolume_2d(&owned, &problem.objectives(), ref_point);
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
);
}
+57 -21
View File
@@ -27,23 +27,59 @@ use std::time::Instant;
/// First 25 cities of TSPLIB KroA100 (EUC_2D).
const KROA_25: [(f64, f64); 25] = [
(1380.0, 939.0), (2848.0, 96.0), (3510.0, 1671.0), (457.0, 334.0),
(3888.0, 666.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),
(1380.0, 939.0),
(2848.0, 96.0),
(3510.0, 1671.0),
(457.0, 334.0),
(3888.0, 666.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),
];
/// First 25 cities of TSPLIB KroB100 (EUC_2D).
const KROB_25: [(f64, f64); 25] = [
(3140.0, 1401.0),(556.0, 1056.0), (3675.0, 1522.0), (1182.0, 1853.0),
(3595.0, 1340.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),
(3140.0, 1401.0),
(556.0, 1056.0),
(3675.0, 1522.0),
(1182.0, 1853.0),
(3595.0, 1340.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),
];
@@ -177,7 +213,10 @@ fn main() {
println!("Bi-objective TSP (KroAB-25): NSGA-II crossover showdown");
println!("Same population, generations, seed across all runs.");
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!();
let runs = vec![
@@ -189,13 +228,7 @@ fn main() {
println!(
" {:<24} | {:>5} {:>5} | {:>17} | {:>17} | {:>14} | {:>6}",
"crossover",
"size",
"uniq",
"A-corner (A, B)",
"B-corner (A, B)",
"hypervolume",
"time"
"crossover", "size", "uniq", "A-corner (A, B)", "B-corner (A, B)", "hypervolume", "time"
);
println!(" {}", "-".repeat(106));
for r in &runs {
@@ -223,5 +256,8 @@ fn main() {
.unwrap_or(std::cmp::Ordering::Equal)
})
.expect("non-empty runs");
println!("Best by hypervolume: {} ({:.0})", winner.name, winner.hypervolume);
println!(
"Best by hypervolume: {} ({:.0})",
winner.name, winner.hypervolume
);
}
+4 -1
View File
@@ -152,7 +152,10 @@ fn main() {
println!("Source: TSPLIB95 (Groetschel/Padberg)");
println!();
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!("Total evaluations: {}", result.evaluations);
println!("Final population: {}", result.population.len());