Files
rust-optimizer/tests/visualization_tests.rs
Manuel Raimann 47b5f9cec8 feat: unify optimize and optimize_with via blanket Objective impl
- Add blanket `impl Objective<V> for Fn(&mut Trial) -> Result<V, E>`
  so closures work directly with `optimize`
- Rewrite optimize, optimize_async, optimize_parallel to accept
  `impl Objective<V>` with before_trial/after_trial hooks
- Remove optimize_with, optimize_with_async, optimize_with_parallel
- Remove max_retries and retry logic from Objective trait
- Add explicit closure type annotations for HRTB inference
- Convert FnMut test closures to Fn via RefCell/Cell
2026-02-12 13:09:14 +01:00

181 lines
5.9 KiB
Rust

use optimizer::parameter::{FloatParam, IntParam, Parameter};
use optimizer::sampler::random::RandomSampler;
use optimizer::{Direction, Study, generate_html_report};
#[test]
fn html_report_creates_file() {
let study: Study<f64> = Study::with_sampler(Direction::Minimize, RandomSampler::with_seed(42));
let x = FloatParam::new(0.0, 10.0).name("x");
let y = IntParam::new(1, 5).name("y");
study
.optimize(10, |trial: &mut optimizer::Trial| {
let xv = x.suggest(trial)?;
let yv = y.suggest(trial)?;
Ok::<_, optimizer::Error>(xv + yv as f64)
})
.unwrap();
let path = std::env::temp_dir().join("test_report_creates_file.html");
generate_html_report(&study, &path).unwrap();
let content = std::fs::read_to_string(&path).unwrap();
assert!(content.contains("<!DOCTYPE html>"));
assert!(content.contains("plotly"));
std::fs::remove_file(&path).ok();
}
#[test]
fn html_report_contains_all_chart_sections() {
let study: Study<f64> = Study::with_sampler(Direction::Minimize, RandomSampler::with_seed(42));
let x = FloatParam::new(0.0, 10.0).name("x");
let y = FloatParam::new(-5.0, 5.0).name("y");
study
.optimize(20, |trial: &mut optimizer::Trial| {
let xv = x.suggest(trial)?;
let yv = y.suggest(trial)?;
Ok::<_, optimizer::Error>(xv * xv + yv * yv)
})
.unwrap();
let path = std::env::temp_dir().join("test_report_all_charts.html");
generate_html_report(&study, &path).unwrap();
let content = std::fs::read_to_string(&path).unwrap();
// Should contain all chart divs.
assert!(content.contains("id=\"history\""));
assert!(content.contains("id=\"slices\""));
assert!(content.contains("id=\"parcoords\""));
assert!(content.contains("id=\"importance\""));
assert!(content.contains("id=\"timeline\""));
// Should contain chart titles.
assert!(content.contains("Optimization History"));
assert!(content.contains("Slice Plots"));
assert!(content.contains("Parallel Coordinates"));
assert!(content.contains("Parameter Importance"));
assert!(content.contains("Trial Timeline"));
// Should show direction and trial count.
assert!(content.contains("Minimize"));
assert!(content.contains("20 trials"));
std::fs::remove_file(&path).ok();
}
#[test]
fn html_report_empty_study() {
let study: Study<f64> = Study::new(Direction::Minimize);
let path = std::env::temp_dir().join("test_report_empty.html");
generate_html_report(&study, &path).unwrap();
let content = std::fs::read_to_string(&path).unwrap();
assert!(content.contains("<!DOCTYPE html>"));
assert!(content.contains("0 trials"));
std::fs::remove_file(&path).ok();
}
#[test]
fn html_report_single_param_no_parcoords() {
let study: Study<f64> = Study::with_sampler(Direction::Minimize, RandomSampler::with_seed(42));
let x = FloatParam::new(0.0, 10.0).name("x");
study
.optimize(5, |trial: &mut optimizer::Trial| {
let xv = x.suggest(trial)?;
Ok::<_, optimizer::Error>(xv * xv)
})
.unwrap();
let path = std::env::temp_dir().join("test_report_single_param.html");
generate_html_report(&study, &path).unwrap();
let content = std::fs::read_to_string(&path).unwrap();
// Should have slice plot but not parallel coordinates (needs >= 2 params).
assert!(content.contains("id=\"slices\""));
assert!(!content.contains("id=\"parcoords\""));
std::fs::remove_file(&path).ok();
}
#[test]
fn html_report_maximize_direction() {
let study: Study<f64> = Study::with_sampler(Direction::Maximize, RandomSampler::with_seed(42));
let x = FloatParam::new(0.0, 10.0).name("x");
study
.optimize(5, |trial: &mut optimizer::Trial| {
let xv = x.suggest(trial)?;
Ok::<_, optimizer::Error>(xv)
})
.unwrap();
let path = std::env::temp_dir().join("test_report_maximize.html");
generate_html_report(&study, &path).unwrap();
let content = std::fs::read_to_string(&path).unwrap();
assert!(content.contains("Maximize"));
std::fs::remove_file(&path).ok();
}
#[test]
fn export_html_convenience_method() {
let study: Study<f64> = Study::with_sampler(Direction::Minimize, RandomSampler::with_seed(42));
let x = FloatParam::new(0.0, 10.0).name("x");
study
.optimize(5, |trial: &mut optimizer::Trial| {
let xv = x.suggest(trial)?;
Ok::<_, optimizer::Error>(xv * xv)
})
.unwrap();
let path = std::env::temp_dir().join("test_export_html.html");
study.export_html(&path).unwrap();
let content = std::fs::read_to_string(&path).unwrap();
assert!(content.contains("<!DOCTYPE html>"));
assert!(content.contains("id=\"history\""));
std::fs::remove_file(&path).ok();
}
#[test]
fn html_report_with_intermediate_values() {
use optimizer::pruner::MedianPruner;
let mut study: Study<f64> =
Study::with_sampler(Direction::Minimize, RandomSampler::with_seed(42));
study.set_pruner(MedianPruner::new(Direction::Minimize));
let x = FloatParam::new(0.0, 10.0).name("x");
study
.optimize(10, |trial: &mut optimizer::Trial| {
let xv = x.suggest(trial)?;
for step in 0..5 {
let val = xv * xv + step as f64;
trial.report(step, val);
if trial.should_prune() {
return Err(optimizer::TrialPruned.into());
}
}
Ok::<_, optimizer::Error>(xv * xv)
})
.unwrap();
let path = std::env::temp_dir().join("test_report_intermediate.html");
generate_html_report(&study, &path).unwrap();
let content = std::fs::read_to_string(&path).unwrap();
assert!(content.contains("id=\"intermediate\""));
assert!(content.contains("Intermediate Values"));
std::fs::remove_file(&path).ok();
}