use super::*;
use crate::heuristic::r#move::ChangeMove;
use crate::heuristic::selector::{FromSolutionEntitySelector, StaticValueSelector};
use crate::phase::construction::{EntityPlacer, ForagerType, QueuedEntityPlacer};
use crate::scope::SolverScope;
use solverforge_core::domain::{EntityDescriptor, SolutionDescriptor, EntityCollectionExtractor};
use solverforge_core::score::SoftScore;
use solverforge_scoring::{Director, ScoreDirector};
use std::any::TypeId;
#[derive(Clone, Debug)]
struct Task {
id: usize,
priority: Option<i64>,
}
#[derive(Clone, Debug)]
struct TestSolution {
tasks: Vec<Task>,
score: Option<SoftScore>,
}
impl PlanningSolution for TestSolution {
type Score = SoftScore;
fn score(&self) -> Option<Self::Score> {
self.score
}
fn set_score(&mut self, score: Option<Self::Score>) {
self.score = score;
}
}
fn get_tasks(s: &TestSolution) -> &Vec<Task> {
&s.tasks
}
fn get_tasks_mut(s: &mut TestSolution) -> &mut Vec<Task> {
&mut s.tasks
}
fn get_task_priority(s: &TestSolution, idx: usize) -> Option<i64> {
s.tasks.get(idx).and_then(|t| t.priority)
}
fn set_task_priority(s: &mut TestSolution, idx: usize, v: Option<i64>) {
if let Some(task) = s.tasks.get_mut(idx) {
task.priority = v;
}
}
fn calculate_score(solution: &TestSolution) -> SoftScore {
let mut score = 0i64;
for task in &solution.tasks {
match task.priority {
Some(p) => score += p,
None => score -= 100, }
}
SoftScore::of(score)
}
fn create_test_director(
tasks: Vec<Task>,
) -> ScoreDirector<TestSolution, ()> {
let solution = TestSolution { tasks, score: None };
let extractor = Box::new(EntityCollectionExtractor::new(
"Task",
"tasks",
get_tasks,
get_tasks_mut,
));
let entity_desc =
EntityDescriptor::new("Task", TypeId::of::<Task>(), "tasks").with_extractor(extractor);
let descriptor = SolutionDescriptor::new("TestSolution", TypeId::of::<TestSolution>())
.with_entity(entity_desc);
ScoreDirector::with_calculator(solution, descriptor, calculate_score)
}
fn create_unassigned_solver_scope(
count: usize,
) -> SolverScope<TestSolution, impl Director<TestSolution>> {
let tasks: Vec<Task> = (0..count).map(|id| Task { id, priority: None }).collect();
let director = create_test_director(tasks);
SolverScope::new(director)
}
type TestMove = ChangeMove<TestSolution, i64>;
type TestPlacer = QueuedEntityPlacer<
TestSolution,
i64,
FromSolutionEntitySelector,
StaticValueSelector<TestSolution, i64>,
>;
fn create_placer_factory() -> impl Fn() -> TestPlacer + Send + Sync {
|| {
QueuedEntityPlacer::new(
FromSolutionEntitySelector::new(0),
StaticValueSelector::new(vec![1i64, 2, 3, 4, 5]),
get_task_priority,
set_task_priority,
0,
"priority",
)
}
}
#[test]
fn test_local_search_type_variants() {
let _hill = LocalSearchType::HillClimbing;
let _tabu = LocalSearchType::TabuSearch { tabu_size: 10 };
let _sa = LocalSearchType::SimulatedAnnealing {
starting_temp: 1.0,
decay_rate: 0.99,
};
let _late = LocalSearchType::LateAcceptance { size: 100 };
}
#[test]
fn test_forager_type_variants() {
let _first = ForagerType::FirstFit;
let _best = ForagerType::BestFit;
}
#[test]
fn test_construction_phase_factory_first_fit_creates_phase() {
let factory =
ConstructionPhaseFactory::<TestSolution, TestMove, _>::first_fit(create_placer_factory());
let phase = factory.create_phase();
assert_eq!(phase.phase_type_name(), "ConstructionHeuristic");
}
#[test]
fn test_construction_phase_factory_best_fit_creates_phase() {
let factory =
ConstructionPhaseFactory::<TestSolution, TestMove, _>::best_fit(create_placer_factory());
let phase = factory.create_phase();
assert_eq!(phase.phase_type_name(), "ConstructionHeuristic");
}
#[test]
fn test_construction_phase_factory_new_with_forager_type() {
let factory_first = ConstructionPhaseFactory::<TestSolution, TestMove, _>::new(
ForagerType::FirstFit,
create_placer_factory(),
);
let phase_first = factory_first.create_phase();
assert_eq!(phase_first.phase_type_name(), "ConstructionHeuristic");
let factory_best = ConstructionPhaseFactory::<TestSolution, TestMove, _>::new(
ForagerType::BestFit,
create_placer_factory(),
);
let phase_best = factory_best.create_phase();
assert_eq!(phase_best.phase_type_name(), "ConstructionHeuristic");
}
#[test]
fn test_construction_phase_factory_first_fit_solves() {
let factory =
ConstructionPhaseFactory::<TestSolution, TestMove, _>::first_fit(create_placer_factory());
let mut solver_scope = create_unassigned_solver_scope(3);
let initial_solution = solver_scope.working_solution();
for task in &initial_solution.tasks {
assert!(task.priority.is_none());
}
let mut phase = factory.create_phase();
phase.solve(&mut solver_scope);
let final_solution = solver_scope.working_solution();
for task in &final_solution.tasks {
assert!(
task.priority.is_some(),
"Task {} should have priority assigned",
task.id
);
}
}
#[test]
fn test_construction_phase_factory_best_fit_solves() {
let factory =
ConstructionPhaseFactory::<TestSolution, TestMove, _>::best_fit(create_placer_factory());
let mut solver_scope = create_unassigned_solver_scope(3);
let initial_solution = solver_scope.working_solution();
for task in &initial_solution.tasks {
assert!(task.priority.is_none());
}
let mut phase = factory.create_phase();
phase.solve(&mut solver_scope);
let final_solution = solver_scope.working_solution();
for task in &final_solution.tasks {
assert!(
task.priority.is_some(),
"Task {} should have priority assigned",
task.id
);
}
let total_priority: i64 = final_solution.tasks.iter().filter_map(|t| t.priority).sum();
assert_eq!(
total_priority, 15,
"BestFit should assign highest priorities"
);
}
#[test]
fn test_construction_phase_factory_creates_fresh_phases() {
let factory =
ConstructionPhaseFactory::<TestSolution, TestMove, _>::first_fit(create_placer_factory());
let phase1 = factory.create_phase();
let phase2 = factory.create_phase();
assert_eq!(phase1.phase_type_name(), "ConstructionHeuristic");
assert_eq!(phase2.phase_type_name(), "ConstructionHeuristic");
}
#[test]
fn test_construction_phase_factory_implements_solver_phase_factory() {
let factory =
ConstructionPhaseFactory::<TestSolution, TestMove, _>::first_fit(create_placer_factory());
let factory_ref: &dyn SolverPhaseFactory<TestSolution> = &factory;
let phase = factory_ref.create_phase();
assert_eq!(phase.phase_type_name(), "ConstructionHeuristic");
}