metalforge 0.1.2

forge: a deterministic metaheuristic optimization substrate in Rust. Unified Problem/MultiProblem/Anneal traits; DDS, SCE-UA, DE, PSO, NSGA-II and simulated annealing; reproducible by seed; optional Rayon parallelism.
Documentation
//! Exports the objective vectors of forge's multi-objective fronts to CSV, for
//! cross-validation against pymoo (hypervolume comparison).
//!
//! Usage: `cargo run --example export_fronts -- <nsga2_zdt1.csv> <nsga3_dtlz2.csv>`
//!
//! - NSGA-II on ZDT1 (30 variables) — matches pymoo's default ZDT1.
//! - NSGA-III on DTLZ2 (3 objectives, 12 variables) — matches pymoo's default.

use std::fs::File;
use std::io::Write;

use forge_core::testfn::{Dtlz2, Zdt1};
use forge_core::{NsgaII, NsgaIII, ParetoFront, Termination};

fn write_csv(path: &str, front: &ParetoFront) {
    let mut f = File::create(path).expect("create csv");
    let m = front.solutions[0].objectives.len();
    let header: Vec<String> = (0..m).map(|j| format!("f{}", j + 1)).collect();
    writeln!(f, "{}", header.join(",")).unwrap();
    for o in front.objective_vectors() {
        let row: Vec<String> = o.iter().map(|v| format!("{v:.10}")).collect();
        writeln!(f, "{}", row.join(",")).unwrap();
    }
}

fn main() {
    let args: Vec<String> = std::env::args().collect();
    let zdt1_out = args.get(1).expect("arg1: nsga2 zdt1 csv path");
    let dtlz2_out = args.get(2).expect("arg2: nsga3 dtlz2 csv path");

    // NSGA-II on ZDT1 (n=30), pop 100, ~200 generations.
    let zdt1 = Zdt1::new(30);
    let f1 = NsgaII {
        pop_size: 100,
        seed: 1,
        ..NsgaII::default()
    }
    .optimize(&zdt1, &Termination::budget(20_000));
    write_csv(zdt1_out, &f1);
    eprintln!(
        "NSGA-II/ZDT1: {} solutions, {} evals",
        f1.len(),
        f1.evaluations
    );

    // NSGA-III on DTLZ2 (3 obj, n=12), pop 92, p=12 reference points.
    let dtlz2 = Dtlz2::new(3, 12);
    let f2 = NsgaIII {
        pop_size: 92,
        divisions: 12,
        seed: 1,
        ..NsgaIII::default()
    }
    .optimize_within(&dtlz2, &Termination::budget(40_000));
    write_csv(dtlz2_out, &f2);
    eprintln!(
        "NSGA-III/DTLZ2: {} solutions, {} evals",
        f2.len(),
        f2.evaluations
    );
}