diff --git a/crates/solverforge/tests/local_search_config.rs b/crates/solverforge/tests/local_search_config.rs index 7b6a21a1..cfb25f16 100644 --- a/crates/solverforge/tests/local_search_config.rs +++ b/crates/solverforge/tests/local_search_config.rs @@ -1,5 +1,5 @@ //! Local search settings that only show in a solve: the recreate heuristic of -//! ruin-and-recreate moves, and that each random part of a search +//! ruin-and-recreate moves, swap moves, and that each random part of a search //! (simulated annealing, random tie breaks, ruin choice) follows //! `random_seed`. @@ -65,6 +65,32 @@ fn first_fit_recreate_puts_ruined_tasks_back_on_the_first_worker() { assert_eq!(solved.workers_by_task(), [Some(0), Some(0)]); } +#[test] +fn swap_moves_exchange_two_tasks_workers() { + // Task 0 (weight 5) on worker 1 (cost 3) and task 1 (weight 1) on + // worker 0 (cost 1) cost 16; exchanging their workers costs 8. Only swap + // moves are configured, so only an exchange can improve. + let mut plan = Plan::new(&[5, 1], &[1, 3]); + plan.tasks[0].worker = Some(1); + plan.tasks[1].worker = Some(0); + let solved = solve( + plan, + r#"random_seed = 7 + +[[phases]] +type = "local_search" +[phases.termination] +step_count_limit = 10 +[phases.acceptor] +type = "hill_climbing" +[phases.move_selector] +type = "swap_move_selector" +"#, + ); + assert_eq!(solved.workers_by_task(), [Some(0), Some(1)]); + assert_eq!(solved.score, Some(SoftScore::of(-8))); +} + /// First fit construction, then local search configured by `local_search`, on /// twelve tasks weighing 0 to 8 and four workers costing 1 to 4: many local /// optima and equally scored moves, so a random part decides where a bounded diff --git a/crates/solverforge/tests/parallel_list_local_search.rs b/crates/solverforge/tests/parallel_list_local_search.rs index aead2513..e216d581 100644 --- a/crates/solverforge/tests/parallel_list_local_search.rs +++ b/crates/solverforge/tests/parallel_list_local_search.rs @@ -45,13 +45,18 @@ fn solve(manager: &'static SolverManager, move_threads: usize) -> Run .expect("job should start"); let mut completed = None; while let Some(event) = receiver.blocking_recv() { - if let SolverEvent::Completed { metadata, solution } = event { - assert_eq!( - metadata.terminal_reason, - Some(SolverTerminalReason::Completed) - ); - completed = Some((solution, metadata.telemetry)); - break; + match event { + SolverEvent::BestSolution { .. } | SolverEvent::Progress { .. } => {} + SolverEvent::Completed { metadata, solution } => { + assert_eq!( + metadata.terminal_reason, + Some(SolverTerminalReason::Completed) + ); + completed = Some((solution, metadata.telemetry)); + break; + } + SolverEvent::Failed { error, .. } => panic!("the solve failed: {error}"), + other => panic!("unexpected event: {other:?}"), } } let (plan, telemetry) = completed.expect("expected a completed solution"); diff --git a/crates/solverforge/tests/parallel_local_search.rs b/crates/solverforge/tests/parallel_local_search.rs index 8f30e4ef..7e4f8a3d 100644 --- a/crates/solverforge/tests/parallel_local_search.rs +++ b/crates/solverforge/tests/parallel_local_search.rs @@ -41,13 +41,18 @@ fn solve(manager: &'static SolverManager, move_threads: usize) -> Run { .expect("job should start"); let mut completed = None; while let Some(event) = receiver.blocking_recv() { - if let SolverEvent::Completed { metadata, solution } = event { - assert_eq!( - metadata.terminal_reason, - Some(SolverTerminalReason::Completed) - ); - completed = Some((solution, metadata.telemetry)); - break; + match event { + SolverEvent::BestSolution { .. } | SolverEvent::Progress { .. } => {} + SolverEvent::Completed { metadata, solution } => { + assert_eq!( + metadata.terminal_reason, + Some(SolverTerminalReason::Completed) + ); + completed = Some((solution, metadata.telemetry)); + break; + } + SolverEvent::Failed { error, .. } => panic!("the solve failed: {error}"), + other => panic!("unexpected event: {other:?}"), } } let (plan, telemetry) = completed.expect("expected a completed solution"); diff --git a/crates/solverforge/tests/scalar_runtime_publication.rs b/crates/solverforge/tests/scalar_runtime_publication.rs index ac691754..6b37c521 100644 --- a/crates/solverforge/tests/scalar_runtime_publication.rs +++ b/crates/solverforge/tests/scalar_runtime_publication.rs @@ -43,9 +43,9 @@ fn scalar_only_solution_runs_construction_in_retained_runtime() { completed_solution = Some(solution); break; } - other => { - eprintln!("event={other:?}"); - } + SolverEvent::Progress { .. } => {} + SolverEvent::Failed { error, .. } => panic!("the solve failed: {error}"), + other => panic!("unexpected event: {other:?}"), } }