use std::time::Instant;
#[derive(Debug, Clone)]
pub struct Stage1State {
reference_set_size: usize,
best_objective: f64,
best_point: Option<ndarray::Array1<f64>>,
current_substage: String,
substage_time: Option<f64>,
substage_start: Option<Instant>,
function_evaluations: usize,
trial_points_generated: usize,
total_time: Option<f64>,
stage_start: Option<Instant>,
}
impl Stage1State {
pub fn new() -> Self {
Self {
reference_set_size: 0,
best_objective: f64::NAN,
best_point: None,
current_substage: "not_started".to_string(),
substage_time: None,
substage_start: None,
function_evaluations: 0,
trial_points_generated: 0,
total_time: None,
stage_start: None,
}
}
pub fn start(&mut self) {
self.stage_start = Some(Instant::now());
}
pub fn end(&mut self) {
if let Some(start) = self.stage_start {
self.total_time = Some(start.elapsed().as_secs_f64());
}
}
pub fn set_reference_set_size(&mut self, size: usize) {
self.reference_set_size = size;
}
pub fn set_best_objective(&mut self, objective: f64) {
if self.best_objective.is_nan() || objective < self.best_objective {
self.best_objective = objective;
}
}
pub fn set_best_solution(&mut self, objective: f64, point: &ndarray::Array1<f64>) {
if self.best_objective.is_nan() || objective < self.best_objective {
self.best_objective = objective;
self.best_point = Some(point.clone());
}
}
pub fn enter_substage(&mut self, name: &str) {
if let Some(start) = self.substage_start {
self.substage_time = Some(start.elapsed().as_secs_f64());
}
self.current_substage = name.to_string();
self.substage_start = Some(Instant::now());
}
pub fn add_function_evaluations(&mut self, count: usize) {
self.function_evaluations += count;
}
pub fn add_trial_points(&mut self, count: usize) {
self.trial_points_generated += count;
}
pub fn reference_set_size(&self) -> usize {
self.reference_set_size
}
pub fn best_objective(&self) -> f64 {
self.best_objective
}
pub fn current_substage(&self) -> &str {
&self.current_substage
}
pub fn total_time(&self) -> Option<f64> {
if let Some(start) = self.stage_start {
Some(start.elapsed().as_secs_f64())
} else {
self.total_time
}
}
pub fn function_evaluations(&self) -> usize {
self.function_evaluations
}
pub fn trial_points_generated(&self) -> usize {
self.trial_points_generated
}
pub fn best_point(&self) -> Option<&ndarray::Array1<f64>> {
self.best_point.as_ref()
}
}
impl Default for Stage1State {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests_observers_stage1 {
use super::*;
#[test]
fn test_stage1_state_creation() {
let state = Stage1State::new();
assert_eq!(state.reference_set_size(), 0);
assert!(state.best_objective().is_nan());
assert_eq!(state.current_substage(), "not_started");
assert_eq!(state.function_evaluations(), 0);
assert_eq!(state.trial_points_generated(), 0);
}
#[test]
fn test_stage1_state_updates() {
let mut state = Stage1State::new();
state.set_reference_set_size(10);
assert_eq!(state.reference_set_size(), 10);
state.set_best_objective(1.5);
assert_eq!(state.best_objective(), 1.5);
state.set_best_objective(2.0);
assert_eq!(state.best_objective(), 1.5);
state.add_function_evaluations(5);
assert_eq!(state.function_evaluations(), 5);
state.add_trial_points(20);
assert_eq!(state.trial_points_generated(), 20);
}
#[test]
fn test_stage1_substages() {
let mut state = Stage1State::new();
state.start();
state.enter_substage("initialization");
assert_eq!(state.current_substage(), "initialization");
std::thread::sleep(std::time::Duration::from_millis(10));
state.enter_substage("diversification");
assert_eq!(state.current_substage(), "diversification");
assert!(state.total_time().is_some());
}
#[test]
fn test_stage1_timing() {
let mut state = Stage1State::new();
state.start();
std::thread::sleep(std::time::Duration::from_millis(10));
state.end();
let total_time = state.total_time();
assert!(total_time.is_some());
assert!(total_time.unwrap() > 0.0);
}
#[test]
fn test_stage1_best_objective_edge_cases() {
let mut state = Stage1State::new();
assert!(state.best_objective().is_nan());
state.set_best_objective(5.0);
assert_eq!(state.best_objective(), 5.0);
state.set_best_objective(3.0);
assert_eq!(state.best_objective(), 3.0);
state.set_best_objective(4.0);
assert_eq!(state.best_objective(), 3.0);
state.set_best_objective(3.0);
assert_eq!(state.best_objective(), 3.0);
state.set_best_objective(-1.0);
assert_eq!(state.best_objective(), -1.0);
state.set_best_objective(0.0);
assert_eq!(state.best_objective(), -1.0);
}
#[test]
fn test_stage1_function_evaluations_accumulation() {
let mut state = Stage1State::new();
assert_eq!(state.function_evaluations(), 0);
state.add_function_evaluations(10);
assert_eq!(state.function_evaluations(), 10);
state.add_function_evaluations(5);
assert_eq!(state.function_evaluations(), 15);
state.add_function_evaluations(0);
assert_eq!(state.function_evaluations(), 15);
state.add_function_evaluations(1000);
assert_eq!(state.function_evaluations(), 1015);
}
#[test]
fn test_stage1_trial_points_accumulation() {
let mut state = Stage1State::new();
assert_eq!(state.trial_points_generated(), 0);
state.add_trial_points(25);
assert_eq!(state.trial_points_generated(), 25);
state.add_trial_points(10);
assert_eq!(state.trial_points_generated(), 35);
state.add_trial_points(0);
assert_eq!(state.trial_points_generated(), 35);
}
#[test]
fn test_stage1_multiple_substage_transitions() {
let mut state = Stage1State::new();
state.start();
let substages = vec![
"scatter_search_running",
"initialization_complete",
"diversification_complete",
"intensification_complete",
"scatter_search_complete",
"local_optimization_complete",
"stage1_complete",
];
for substage in substages {
state.enter_substage(substage);
assert_eq!(state.current_substage(), substage);
}
state.end();
assert!(state.total_time().is_some());
}
#[test]
fn test_stage1_timing_without_start() {
let mut state = Stage1State::new();
assert!(state.total_time().is_none());
state.end();
assert!(state.total_time().is_none());
}
#[test]
fn test_stage1_reference_set_size_updates() {
let mut state = Stage1State::new();
assert_eq!(state.reference_set_size(), 0);
state.set_reference_set_size(5);
assert_eq!(state.reference_set_size(), 5);
state.set_reference_set_size(0);
assert_eq!(state.reference_set_size(), 0);
state.set_reference_set_size(100);
assert_eq!(state.reference_set_size(), 100);
}
#[test]
fn test_stage1_substage_timing_precision() {
let mut state = Stage1State::new();
state.start();
state.enter_substage("test_stage");
std::thread::sleep(std::time::Duration::from_millis(50));
let time1 = state.total_time().unwrap();
assert!(time1 >= 0.05);
std::thread::sleep(std::time::Duration::from_millis(25));
let time2 = state.total_time().unwrap();
assert!(time2 >= time1 + 0.02); }
#[test]
fn test_stage1_clone_behavior() {
let mut state = Stage1State::new();
state.set_reference_set_size(10);
state.set_best_objective(2.5);
state.add_function_evaluations(100);
state.add_trial_points(50);
state.enter_substage("test");
let cloned = state.clone();
assert_eq!(cloned.reference_set_size(), 10);
assert_eq!(cloned.best_objective(), 2.5);
assert_eq!(cloned.function_evaluations(), 100);
assert_eq!(cloned.trial_points_generated(), 50);
assert_eq!(cloned.current_substage(), "test");
}
#[test]
fn test_stage1_default_implementation() {
let state = Stage1State::default();
assert_eq!(state.reference_set_size(), 0);
assert!(state.best_objective().is_nan());
assert_eq!(state.current_substage(), "not_started");
}
#[test]
fn test_stage1_best_point_tracking() {
use ndarray::array;
let mut state = Stage1State::new();
assert!(state.best_point().is_none());
assert!(state.best_objective().is_nan());
state.set_best_solution(10.0, &array![1.0, 2.0, 3.0]);
assert!(state.best_point().is_some());
assert_eq!(state.best_objective(), 10.0);
assert_eq!(state.best_point().unwrap(), &array![1.0, 2.0, 3.0]);
state.set_best_solution(5.0, &array![4.0, 5.0, 6.0]);
assert_eq!(state.best_objective(), 5.0);
assert_eq!(state.best_point().unwrap(), &array![4.0, 5.0, 6.0]);
state.set_best_solution(8.0, &array![7.0, 8.0, 9.0]);
assert_eq!(state.best_objective(), 5.0);
assert_eq!(state.best_point().unwrap(), &array![4.0, 5.0, 6.0]);
}
#[test]
fn test_stage1_best_objective_independent() {
use ndarray::array;
let mut state = Stage1State::new();
state.set_best_solution(10.0, &array![1.0, 2.0]);
assert_eq!(state.best_objective(), 10.0);
assert_eq!(state.best_point().unwrap(), &array![1.0, 2.0]);
state.set_best_objective(5.0);
assert_eq!(state.best_objective(), 5.0);
assert_eq!(state.best_point().unwrap(), &array![1.0, 2.0]); }
}