#![forbid(unsafe_code)]
use std::fs;
use std::path::PathBuf;
use std::time::{Instant, SystemTime, UNIX_EPOCH};
use fsci_opt::OptimizeMethod;
use fsci_opt::minimize::{OptCaspProblem, select_minimize_method};
use serde::Serialize;
const PACKET_ID: &str = "FSCI-P2C-007";
#[derive(Debug, Clone, Serialize)]
struct CaseDiff {
case_id: String,
expected: String,
actual: String,
pass: bool,
}
#[derive(Debug, Clone, Serialize)]
struct DiffLog {
test_id: String,
category: String,
case_count: usize,
pass: bool,
timestamp_ms: u128,
duration_ns: u128,
cases: Vec<CaseDiff>,
}
fn output_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(format!("fixtures/artifacts/{PACKET_ID}/diff"))
}
fn ensure_output_dir() {
fs::create_dir_all(output_dir()).expect("create casp_select diff dir");
}
fn timestamp_ms() -> u128 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_or(0, |d| d.as_millis())
}
fn emit_log(log: &DiffLog) {
ensure_output_dir();
let path = output_dir().join(format!("{}.json", log.test_id));
let json = serde_json::to_string_pretty(log).expect("serialize log");
fs::write(path, json).expect("write log");
}
#[test]
fn diff_opt_select_minimize_method() {
let start = Instant::now();
let mut diffs: Vec<CaseDiff> = Vec::new();
let p1 = OptCaspProblem {
dimension: 3,
variable_scale_ratio: 1.0,
has_box_bounds: false,
has_general_constraints: true,
gradient_available: true,
hessian_product_available: false,
};
let d1 = select_minimize_method(p1).expect("p1 ok");
diffs.push(CaseDiff {
case_id: "general_constr".into(),
expected: format!("{:?}", OptimizeMethod::Slsqp),
actual: format!("{:?}", d1.method),
pass: d1.method == OptimizeMethod::Slsqp,
});
let p2 = OptCaspProblem {
dimension: 5,
variable_scale_ratio: 1.0,
has_box_bounds: true,
has_general_constraints: false,
gradient_available: true,
hessian_product_available: false,
};
let d2 = select_minimize_method(p2).expect("p2 ok");
diffs.push(CaseDiff {
case_id: "box_bounds".into(),
expected: format!("{:?}", OptimizeMethod::LBfgsB),
actual: format!("{:?}", d2.method),
pass: d2.method == OptimizeMethod::LBfgsB,
});
let p3 = OptCaspProblem {
dimension: 3,
variable_scale_ratio: 1.0,
has_box_bounds: false,
has_general_constraints: false,
gradient_available: false,
hessian_product_available: false,
};
let d3 = select_minimize_method(p3).expect("p3 ok");
diffs.push(CaseDiff {
case_id: "no_grad".into(),
expected: format!("{:?}", OptimizeMethod::NelderMead),
actual: format!("{:?}", d3.method),
pass: d3.method == OptimizeMethod::NelderMead,
});
let p4 = OptCaspProblem {
dimension: 3,
variable_scale_ratio: 1.0e5,
has_box_bounds: false,
has_general_constraints: false,
gradient_available: true,
hessian_product_available: true,
};
let d4 = select_minimize_method(p4).expect("p4 ok");
diffs.push(CaseDiff {
case_id: "hessp_small_illscaled".into(),
expected: format!("{:?}", OptimizeMethod::TrustNcg),
actual: format!("{:?}", d4.method),
pass: d4.method == OptimizeMethod::TrustNcg,
});
let p5 = OptCaspProblem {
dimension: 8,
variable_scale_ratio: 1.0,
has_box_bounds: false,
has_general_constraints: false,
gradient_available: true,
hessian_product_available: true,
};
let d5 = select_minimize_method(p5).expect("p5 ok");
diffs.push(CaseDiff {
case_id: "hessp_well_scaled".into(),
expected: format!("{:?}", OptimizeMethod::NewtonCg),
actual: format!("{:?}", d5.method),
pass: d5.method == OptimizeMethod::NewtonCg,
});
let p6 = OptCaspProblem {
dimension: 50,
variable_scale_ratio: 1.0,
has_box_bounds: false,
has_general_constraints: false,
gradient_available: true,
hessian_product_available: false,
};
let d6 = select_minimize_method(p6).expect("p6 ok");
diffs.push(CaseDiff {
case_id: "large_dim_no_hessp".into(),
expected: format!("{:?}", OptimizeMethod::ConjugateGradient),
actual: format!("{:?}", d6.method),
pass: d6.method == OptimizeMethod::ConjugateGradient,
});
let p7 = OptCaspProblem {
dimension: 0,
variable_scale_ratio: 1.0,
has_box_bounds: false,
has_general_constraints: false,
gradient_available: true,
hessian_product_available: false,
};
let r7 = select_minimize_method(p7);
diffs.push(CaseDiff {
case_id: "dim_zero_err".into(),
expected: "Err".into(),
actual: if r7.is_err() {
"Err".into()
} else {
"Ok".into()
},
pass: r7.is_err(),
});
let all_pass = diffs.iter().all(|d| d.pass);
let log = DiffLog {
test_id: "diff_opt_select_minimize_method".into(),
category: "fsci_opt::select_minimize_method branch coverage".into(),
case_count: diffs.len(),
pass: all_pass,
timestamp_ms: timestamp_ms(),
duration_ns: start.elapsed().as_nanos(),
cases: diffs.clone(),
};
emit_log(&log);
for d in &diffs {
if !d.pass {
eprintln!(
"casp mismatch: {} got {} (expected {})",
d.case_id, d.actual, d.expected
);
}
}
assert!(
all_pass,
"casp branch coverage failed: {} cases",
diffs.len(),
);
}