use std::time::Instant;
#[derive(Debug, Clone)]
pub struct Stage2State {
best_objective: f64,
best_point: Option<ndarray::Array1<f64>>,
last_added_point: Option<ndarray::Array1<f64>>,
solution_set_size: usize,
current_iteration: usize,
threshold_value: f64,
local_solver_calls: usize,
improved_local_calls: usize,
function_evaluations: usize,
unchanged_cycles: usize,
total_time: Option<f64>,
stage_start: Option<Instant>,
}
impl Stage2State {
pub fn new() -> Self {
Self {
best_objective: f64::NAN,
best_point: None,
last_added_point: None,
solution_set_size: 0,
current_iteration: 0,
threshold_value: f64::INFINITY,
local_solver_calls: 0,
improved_local_calls: 0,
function_evaluations: 0,
unchanged_cycles: 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_iteration(&mut self, iteration: usize) {
self.current_iteration = iteration;
}
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 set_solution_set_size(&mut self, size: usize) {
self.solution_set_size = size;
}
pub fn set_threshold_value(&mut self, threshold: f64) {
self.threshold_value = threshold;
}
pub fn add_local_solver_call(&mut self, improved: bool) {
self.local_solver_calls += 1;
if improved {
self.improved_local_calls += 1;
}
}
pub fn add_function_evaluations(&mut self, count: usize) {
self.function_evaluations += count;
}
pub fn set_unchanged_cycles(&mut self, count: usize) {
self.unchanged_cycles = count;
}
pub fn set_last_added_solution(&mut self, point: &ndarray::Array1<f64>) {
self.last_added_point = Some(point.clone());
}
pub fn best_objective(&self) -> f64 {
self.best_objective
}
pub fn solution_set_size(&self) -> usize {
self.solution_set_size
}
pub fn current_iteration(&self) -> usize {
self.current_iteration
}
pub fn threshold_value(&self) -> f64 {
self.threshold_value
}
pub fn local_solver_calls(&self) -> usize {
self.local_solver_calls
}
pub fn improved_local_calls(&self) -> usize {
self.improved_local_calls
}
pub fn function_evaluations(&self) -> usize {
self.function_evaluations
}
pub fn unchanged_cycles(&self) -> usize {
self.unchanged_cycles
}
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 best_point(&self) -> Option<&ndarray::Array1<f64>> {
self.best_point.as_ref()
}
pub fn last_added_point(&self) -> Option<&ndarray::Array1<f64>> {
self.last_added_point.as_ref()
}
}
impl Default for Stage2State {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests_observers_stage2 {
use super::*;
#[test]
fn test_stage2_state_creation() {
let state = Stage2State::new();
assert!(state.best_objective().is_nan());
assert_eq!(state.solution_set_size(), 0);
assert_eq!(state.current_iteration(), 0);
assert!(state.best_objective().is_nan());
assert_eq!(state.local_solver_calls(), 0);
assert_eq!(state.improved_local_calls(), 0);
assert_eq!(state.function_evaluations(), 0);
assert_eq!(state.unchanged_cycles(), 0);
}
#[test]
fn test_stage2_state_updates() {
let mut state = Stage2State::new();
state.set_iteration(5);
assert_eq!(state.current_iteration(), 5);
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.set_solution_set_size(3);
assert_eq!(state.solution_set_size(), 3);
state.set_threshold_value(2.5);
assert_eq!(state.threshold_value(), 2.5);
state.add_local_solver_call(true);
state.add_local_solver_call(false);
assert_eq!(state.local_solver_calls(), 2);
assert_eq!(state.improved_local_calls(), 1);
state.add_function_evaluations(10);
state.add_function_evaluations(50);
assert_eq!(state.function_evaluations(), 60);
state.set_unchanged_cycles(3);
assert_eq!(state.unchanged_cycles(), 3);
}
#[test]
fn test_stage2_timing() {
let mut state = Stage2State::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_stage2_improved_local_calls() {
let mut state = Stage2State::new();
assert_eq!(state.improved_local_calls(), 0);
state.add_local_solver_call(true);
state.add_local_solver_call(true);
assert_eq!(state.local_solver_calls(), 2);
assert_eq!(state.improved_local_calls(), 2);
state.add_local_solver_call(false);
state.add_local_solver_call(false);
assert_eq!(state.local_solver_calls(), 4);
assert_eq!(state.improved_local_calls(), 2);
}
#[test]
fn test_stage2_best_objective_edge_cases() {
let mut state = Stage2State::new();
assert!(state.best_objective().is_nan());
state.set_best_objective(10.0);
assert_eq!(state.best_objective(), 10.0);
state.set_best_objective(5.0);
assert_eq!(state.best_objective(), 5.0);
state.set_best_objective(7.0);
assert_eq!(state.best_objective(), 5.0);
state.set_best_objective(5.0);
assert_eq!(state.best_objective(), 5.0);
state.set_best_objective(-2.0);
assert_eq!(state.best_objective(), -2.0);
state.set_best_objective(1.0);
assert_eq!(state.best_objective(), -2.0);
state.set_best_objective(-2.000001);
assert_eq!(state.best_objective(), -2.000001);
}
#[test]
fn test_stage2_iteration_progression() {
let mut state = Stage2State::new();
assert_eq!(state.current_iteration(), 0);
for i in 1..=10 {
state.set_iteration(i);
assert_eq!(state.current_iteration(), i);
}
state.set_iteration(50);
assert_eq!(state.current_iteration(), 50);
state.set_iteration(25);
assert_eq!(state.current_iteration(), 25);
}
#[test]
fn test_stage2_solution_set_size_variations() {
let mut state = Stage2State::new();
assert_eq!(state.solution_set_size(), 0);
let sizes = vec![1, 5, 10, 0, 100, 50];
for &size in &sizes {
state.set_solution_set_size(size);
assert_eq!(state.solution_set_size(), size);
}
}
#[test]
fn test_stage2_threshold_value_updates() {
let mut state = Stage2State::new();
assert_eq!(state.threshold_value(), f64::INFINITY);
let thresholds = vec![10.0, 5.5, 1.0, 0.1, 0.0, -1.0];
for &threshold in &thresholds {
state.set_threshold_value(threshold);
assert_eq!(state.threshold_value(), threshold);
}
state.set_threshold_value(f64::INFINITY);
assert_eq!(state.threshold_value(), f64::INFINITY);
}
#[test]
fn test_stage2_local_solver_call_patterns() {
let mut state = Stage2State::new();
let pattern = vec![true, false, true, true, false, false, true];
let mut expected_improved = 0;
for &improved in &pattern {
state.add_local_solver_call(improved);
if improved {
expected_improved += 1;
}
}
assert_eq!(state.local_solver_calls(), pattern.len());
assert_eq!(state.improved_local_calls(), expected_improved);
let mut state2 = Stage2State::new();
for _ in 0..5 {
state2.add_local_solver_call(false);
}
assert_eq!(state2.local_solver_calls(), 5);
assert_eq!(state2.improved_local_calls(), 0);
let mut state3 = Stage2State::new();
for _ in 0..3 {
state3.add_local_solver_call(true);
}
assert_eq!(state3.local_solver_calls(), 3);
assert_eq!(state3.improved_local_calls(), 3);
}
#[test]
fn test_stage2_function_evaluations_accumulation() {
let mut state = Stage2State::new();
assert_eq!(state.function_evaluations(), 0);
state.add_function_evaluations(100);
assert_eq!(state.function_evaluations(), 100);
state.add_function_evaluations(250);
assert_eq!(state.function_evaluations(), 350);
state.add_function_evaluations(0);
assert_eq!(state.function_evaluations(), 350);
state.add_function_evaluations(10000);
assert_eq!(state.function_evaluations(), 10350);
}
#[test]
fn test_stage2_unchanged_cycles_tracking() {
let mut state = Stage2State::new();
assert_eq!(state.unchanged_cycles(), 0);
for i in 1..=5 {
state.set_unchanged_cycles(i);
assert_eq!(state.unchanged_cycles(), i);
}
state.set_unchanged_cycles(0);
assert_eq!(state.unchanged_cycles(), 0);
state.set_unchanged_cycles(1000);
assert_eq!(state.unchanged_cycles(), 1000);
}
#[test]
fn test_stage2_timing_edge_cases() {
let mut state = Stage2State::new();
assert!(state.total_time().is_none());
state.end();
assert!(state.total_time().is_none());
state.start();
let time1 = state.total_time().unwrap();
assert!(time1 >= 0.0);
std::thread::sleep(std::time::Duration::from_millis(5));
let time2 = state.total_time().unwrap();
assert!(time2 > time1);
state.end();
let final_time = state.total_time().unwrap();
assert!(final_time >= time2);
}
#[test]
fn test_stage2_clone_behavior() {
let mut state = Stage2State::new();
state.set_iteration(10);
state.set_best_objective(3.5);
state.set_solution_set_size(8);
state.set_threshold_value(2.0);
state.add_local_solver_call(true);
state.add_local_solver_call(false);
state.add_function_evaluations(500);
state.set_unchanged_cycles(2);
let cloned = state.clone();
assert_eq!(cloned.current_iteration(), 10);
assert_eq!(cloned.best_objective(), 3.5);
assert_eq!(cloned.solution_set_size(), 8);
assert_eq!(cloned.threshold_value(), 2.0);
assert_eq!(cloned.local_solver_calls(), 2);
assert_eq!(cloned.improved_local_calls(), 1);
assert_eq!(cloned.function_evaluations(), 500);
assert_eq!(cloned.unchanged_cycles(), 2);
}
#[test]
fn test_stage2_default_implementation() {
let state = Stage2State::default();
assert!(state.best_objective().is_nan());
assert_eq!(state.solution_set_size(), 0);
assert_eq!(state.current_iteration(), 0);
assert_eq!(state.threshold_value(), f64::INFINITY);
assert_eq!(state.local_solver_calls(), 0);
assert_eq!(state.improved_local_calls(), 0);
assert_eq!(state.function_evaluations(), 0);
assert_eq!(state.unchanged_cycles(), 0);
}
#[test]
fn test_stage2_convergence_scenario() {
let mut state = Stage2State::new();
state.set_best_objective(10.0);
state.set_solution_set_size(5);
state.set_threshold_value(8.0);
for iter in 1..=5 {
state.set_iteration(iter);
state.add_local_solver_call(true);
state.add_function_evaluations(20);
}
assert_eq!(state.current_iteration(), 5);
assert_eq!(state.local_solver_calls(), 5);
assert_eq!(state.improved_local_calls(), 5);
assert_eq!(state.function_evaluations(), 100);
for iter in 6..=10 {
state.set_iteration(iter);
state.add_local_solver_call(false);
state.add_function_evaluations(15);
state.set_unchanged_cycles(iter - 5); }
assert_eq!(state.current_iteration(), 10);
assert_eq!(state.local_solver_calls(), 10);
assert_eq!(state.improved_local_calls(), 5); assert_eq!(state.function_evaluations(), 100 + 5 * 15); assert_eq!(state.unchanged_cycles(), 5);
}
#[test]
fn test_stage2_best_point_tracking() {
use ndarray::array;
let mut state = Stage2State::new();
assert!(state.best_point().is_none());
assert!(state.best_objective().is_nan());
state.set_best_solution(10.0, &array![1.0, 2.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]);
state.set_best_solution(5.0, &array![3.0, 4.0]);
assert_eq!(state.best_objective(), 5.0);
assert_eq!(state.best_point().unwrap(), &array![3.0, 4.0]);
state.set_best_solution(8.0, &array![5.0, 6.0]);
assert_eq!(state.best_objective(), 5.0);
assert_eq!(state.best_point().unwrap(), &array![3.0, 4.0]);
}
#[test]
fn test_stage2_best_objective_independent() {
use ndarray::array;
let mut state = Stage2State::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]); }
}