use std::num::NonZeroUsize;
use std::time::{Duration, Instant};
use henad_core::explore::factor::{FactorSpec, LevelSpec};
use henad_core::explore::search::pse::{PatternAxis, PatternSpaceSettings};
use henad_core::explore::search::{SearchAlgorithm, SearchSpec};
use henad_core::explore::spec::SweepSpec;
use crate::exec::{Concurrency, SweepControl};
use crate::handle::{SweepEvent, SweepRun, SweepRunOptions};
use crate::output::manifest::ManifestStatus;
use crate::result_set::ResultSet;
use crate::sweep::{SweepEnd, SweepOptions};
use crate::tests::support::{CommitLimit, ScratchDir, entry, provenance, sweep_options, sweep_with};
const AXIS_MAX: f64 = 255.556_822_519_348_06;
const PATIENCE: Duration = Duration::from_secs(120);
fn search_spec() -> SweepSpec {
let mut spec = SweepSpec::new("sir");
spec.fixed = vec![
("grid_width".to_owned(), "16".to_owned()),
("grid_height".to_owned(), "16".to_owned()),
];
spec.run.steps = 20;
spec.measure.default_reducers = false;
spec.measure.reducers = ["Infected:max", "Infected:argmax"]
.map(|raw| raw.parse().expect("a valid reducer"))
.to_vec();
let range = |min, max| LevelSpec::Range { min, max, step: None };
spec.search = Some(SearchSpec {
algorithm: SearchAlgorithm::PatternSpaceExploration(PatternSpaceSettings {
initial_samples: 4,
..PatternSpaceSettings::new(
PatternAxis::bounded("Infected:max", 0.0, AXIS_MAX, 8),
PatternAxis::bounded("Infected:argmax", 0.0, 20.0, 8),
)
}),
max_evaluations: 12,
batch_size: 4,
objective: None,
space: vec![
FactorSpec::param("infection_rate", range(0.05, 0.9)),
FactorSpec::param("recovery_rate", range(0.02, 0.3)),
],
});
spec
}
#[test]
fn a_search_resumes_from_a_manifest_holding_a_seventeen_digit_axis_bound() {
let scratch = ScratchDir::new("manifest-floats");
let spec = search_spec();
let control = SweepControl::new();
let interrupted = SweepOptions {
concurrency: Concurrency::Fixed(NonZeroUsize::MIN),
control: control.clone(),
..sweep_options(false)
};
let report = sweep_with(
&entry("sir", None),
None,
&spec,
scratch.path(),
&interrupted,
&mut CommitLimit::new(control, 5),
)
.expect("an abort is not an error");
assert_eq!(report.end, SweepEnd::Aborted);
let set = ResultSet::open_dir(scratch.path(), usize::MAX).expect("the folder reads back");
let Some(SearchSpec {
algorithm: SearchAlgorithm::PatternSpaceExploration(settings),
..
}) = &set.spec().search
else {
panic!("the folder holds a Pattern Space Exploration");
};
assert_eq!(
settings.x_axis.max.map(f64::to_bits),
Some(AXIS_MAX.to_bits()),
"the bound reads back bit for bit"
);
let options = SweepRunOptions::new(provenance());
let mut run =
SweepRun::resume_directory(entry("sir", None), None, scratch.path(), options).expect("the resume starts");
let deadline = Instant::now() + PATIENCE;
let mut last = None;
while !run.is_ended() {
assert!(Instant::now() < deadline, "the resume did not end in time");
run.update(0.0);
match run.try_recv() {
Some(event) => last = Some(event),
None => std::thread::sleep(Duration::from_millis(2)),
}
}
match last {
Some(SweepEvent::Finished(record)) => assert_eq!(record.manifest.status, ManifestStatus::Complete),
Some(SweepEvent::Failed(message)) => panic!("the resume failed: {message}"),
_ => panic!("the resume sent no end"),
}
}