use std::fmt::Debug;
use std::marker::PhantomData;
use solverforge_core::domain::PlanningSolution;
use solverforge_scoring::Director;
use crate::heuristic::r#move::k_opt_reconnection::KOptReconnection;
use crate::heuristic::r#move::k_opt_reconnection::{
enumerate_reconnections, THREE_OPT_RECONNECTIONS,
};
use crate::heuristic::r#move::KOptMove;
use super::super::entity::EntitySelector;
use super::super::move_selector::MoveSelector;
use super::config::KOptConfig;
use super::iterators::count_cut_combinations;
use super::iterators::CutCombinationIterator;
pub struct KOptMoveSelector<S, V, ES> {
entity_selector: ES,
config: KOptConfig,
owned_patterns: Vec<KOptReconnection>,
list_len: fn(&S, usize) -> usize,
sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
sublist_insert: fn(&mut S, usize, usize, Vec<V>),
variable_name: &'static str,
descriptor_index: usize,
_phantom: PhantomData<(fn() -> S, fn() -> V)>,
}
impl<S, V: Debug, ES: Debug> Debug for KOptMoveSelector<S, V, ES> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("KOptMoveSelector")
.field("entity_selector", &self.entity_selector)
.field("config", &self.config)
.field("pattern_count", &self.owned_patterns.len())
.field("variable_name", &self.variable_name)
.finish()
}
}
impl<S: PlanningSolution, V, ES> KOptMoveSelector<S, V, ES> {
#[allow(clippy::too_many_arguments)]
pub fn new(
entity_selector: ES,
config: KOptConfig,
list_len: fn(&S, usize) -> usize,
sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
sublist_insert: fn(&mut S, usize, usize, Vec<V>),
variable_name: &'static str,
descriptor_index: usize,
) -> Self {
let owned_patterns: Vec<KOptReconnection> = if config.k == 3 {
THREE_OPT_RECONNECTIONS.to_vec()
} else {
enumerate_reconnections(config.k)
};
Self {
entity_selector,
config,
owned_patterns,
list_len,
sublist_remove,
sublist_insert,
variable_name,
descriptor_index,
_phantom: PhantomData,
}
}
}
impl<S, V, ES> MoveSelector<S, KOptMove<S, V>> for KOptMoveSelector<S, V, ES>
where
S: PlanningSolution,
ES: EntitySelector<S>,
V: Clone + Send + Sync + Debug + 'static,
{
fn iter_moves<'a, D: Director<S>>(
&'a self,
score_director: &'a D,
) -> impl Iterator<Item = KOptMove<S, V>> + 'a {
let k = self.config.k;
let min_seg = self.config.min_segment_len;
let patterns = &self.owned_patterns;
let list_len = self.list_len;
let sublist_remove = self.sublist_remove;
let sublist_insert = self.sublist_insert;
let variable_name = self.variable_name;
let descriptor_index = self.descriptor_index;
self.entity_selector
.iter(score_director)
.flat_map(move |entity_ref| {
let entity_idx = entity_ref.entity_index;
let solution = score_director.working_solution();
let len = list_len(solution, entity_idx);
let cuts_iter = CutCombinationIterator::new(k, len, min_seg, entity_idx);
cuts_iter.flat_map(move |cuts| {
patterns.iter().map(move |pattern| {
KOptMove::new(
&cuts,
pattern,
list_len,
sublist_remove,
sublist_insert,
variable_name,
descriptor_index,
)
})
})
})
}
fn size<D: Director<S>>(&self, score_director: &D) -> usize {
let k = self.config.k;
let min_seg = self.config.min_segment_len;
let pattern_count = self.owned_patterns.len();
self.entity_selector
.iter(score_director)
.map(|entity_ref| {
let solution = score_director.working_solution();
let len = (self.list_len)(solution, entity_ref.entity_index);
count_cut_combinations(k, len, min_seg) * pattern_count
})
.sum()
}
}