solverforge-solver 0.8.8

Solver engine for SolverForge
Documentation
use std::fmt::{self, Debug};
use std::ops::Range;

use solverforge_config::MoveSelectorConfig;
use solverforge_core::domain::PlanningSolution;
use solverforge_scoring::Director;

use crate::heuristic::r#move::{EitherMove, MoveArena};
use crate::heuristic::selector::decorator::VecUnionSelector;
use crate::heuristic::selector::{
    ChangeMoveSelector, FromSolutionEntitySelector, MoveSelector, PerEntitySliceValueSelector,
    RangeValueSelector, SwapMoveSelector, ValueSelector,
};

use super::context::{ScalarVariableContext, ValueSource};

pub type StandardSelector<S> = VecUnionSelector<S, EitherMove<S, usize>, StandardLeafSelector<S>>;

pub enum StandardValueSelector<S> {
    Empty,
    CountableRange { from: usize, to: usize },
    SolutionCount(RangeValueSelector<S>),
    EntitySlice(PerEntitySliceValueSelector<S, usize>),
}

impl<S> StandardValueSelector<S> {
    fn from_source(source: ValueSource<S>) -> Self {
        match source {
            ValueSource::Empty => Self::Empty,
            ValueSource::CountableRange { from, to } => Self::CountableRange { from, to },
            ValueSource::SolutionCount { count_fn } => {
                Self::SolutionCount(RangeValueSelector::new(count_fn))
            }
            ValueSource::EntitySlice { values_for_entity } => {
                Self::EntitySlice(PerEntitySliceValueSelector::new(values_for_entity))
            }
        }
    }
}

impl<S> Debug for StandardValueSelector<S> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Empty => write!(f, "StandardValueSelector::Empty"),
            Self::CountableRange { from, to } => {
                write!(f, "StandardValueSelector::CountableRange({from}..{to})")
            }
            Self::SolutionCount(_) => write!(f, "StandardValueSelector::SolutionCount(..)"),
            Self::EntitySlice(_) => write!(f, "StandardValueSelector::EntitySlice(..)"),
        }
    }
}

impl<S> ValueSelector<S, usize> for StandardValueSelector<S>
where
    S: PlanningSolution,
{
    fn iter_typed<'a, D: Director<S>>(
        &'a self,
        score_director: &D,
        descriptor_index: usize,
        entity_index: usize,
    ) -> impl Iterator<Item = usize> + 'a {
        match self {
            Self::Empty => StandardValueIter::Empty,
            Self::CountableRange { from, to } => StandardValueIter::CountableRange(*from..*to),
            Self::SolutionCount(selector) => StandardValueIter::SolutionCount(selector.iter_typed(
                score_director,
                descriptor_index,
                entity_index,
            )),
            Self::EntitySlice(selector) => StandardValueIter::EntitySlice(selector.iter_typed(
                score_director,
                descriptor_index,
                entity_index,
            )),
        }
    }

    fn size<D: Director<S>>(
        &self,
        score_director: &D,
        descriptor_index: usize,
        entity_index: usize,
    ) -> usize {
        match self {
            Self::Empty => 0,
            Self::CountableRange { from, to } => to.saturating_sub(*from),
            Self::SolutionCount(selector) => {
                selector.size(score_director, descriptor_index, entity_index)
            }
            Self::EntitySlice(selector) => {
                selector.size(score_director, descriptor_index, entity_index)
            }
        }
    }
}

enum StandardValueIter<A, B> {
    Empty,
    CountableRange(Range<usize>),
    SolutionCount(A),
    EntitySlice(B),
}

impl<A, B> Iterator for StandardValueIter<A, B>
where
    A: Iterator<Item = usize>,
    B: Iterator<Item = usize>,
{
    type Item = usize;

    fn next(&mut self) -> Option<Self::Item> {
        match self {
            Self::Empty => None,
            Self::CountableRange(iter) => iter.next(),
            Self::SolutionCount(iter) => iter.next(),
            Self::EntitySlice(iter) => iter.next(),
        }
    }
}

type StandardChangeSelector<S> =
    ChangeMoveSelector<S, usize, FromSolutionEntitySelector, StandardValueSelector<S>>;
type StandardSwapSelector<S> =
    SwapMoveSelector<S, usize, FromSolutionEntitySelector, FromSolutionEntitySelector>;

pub enum StandardLeafSelector<S> {
    Change(StandardChangeSelector<S>),
    Swap(StandardSwapSelector<S>),
}

impl<S> Debug for StandardLeafSelector<S> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Change(selector) => selector.fmt(f),
            Self::Swap(selector) => selector.fmt(f),
        }
    }
}

impl<S> MoveSelector<S, EitherMove<S, usize>> for StandardLeafSelector<S>
where
    S: PlanningSolution,
{
    fn open_cursor<'a, D: Director<S>>(
        &'a self,
        score_director: &D,
    ) -> impl Iterator<Item = EitherMove<S, usize>> + 'a {
        match self {
            Self::Change(selector) => StandardLeafIter::Change(
                selector.open_cursor(score_director).map(EitherMove::Change),
            ),
            Self::Swap(selector) => {
                StandardLeafIter::Swap(selector.open_cursor(score_director).map(EitherMove::Swap))
            }
        }
    }

    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
        match self {
            Self::Change(selector) => selector.size(score_director),
            Self::Swap(selector) => selector.size(score_director),
        }
    }

    fn append_moves<D: Director<S>>(
        &self,
        score_director: &D,
        arena: &mut MoveArena<EitherMove<S, usize>>,
    ) {
        match self {
            Self::Change(selector) => {
                arena.extend(selector.open_cursor(score_director).map(EitherMove::Change))
            }
            Self::Swap(selector) => {
                arena.extend(selector.open_cursor(score_director).map(EitherMove::Swap))
            }
        }
    }
}

enum StandardLeafIter<A, B> {
    Change(A),
    Swap(B),
}

impl<T, A, B> Iterator for StandardLeafIter<A, B>
where
    A: Iterator<Item = T>,
    B: Iterator<Item = T>,
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        match self {
            Self::Change(iter) => iter.next(),
            Self::Swap(iter) => iter.next(),
        }
    }
}

pub fn build_standard_move_selector<S>(
    config: Option<&MoveSelectorConfig>,
    scalar_variables: &[ScalarVariableContext<S>],
) -> StandardSelector<S>
where
    S: PlanningSolution + 'static,
{
    let mut leaves = Vec::new();
    collect_standard_leaf_selectors(config, scalar_variables, &mut leaves);
    assert!(
        !leaves.is_empty(),
        "move selector configuration produced no standard neighborhoods"
    );
    VecUnionSelector::new(leaves)
}

fn collect_standard_leaf_selectors<S>(
    config: Option<&MoveSelectorConfig>,
    scalar_variables: &[ScalarVariableContext<S>],
    leaves: &mut Vec<StandardLeafSelector<S>>,
) where
    S: PlanningSolution + 'static,
{
    fn push_change<S: PlanningSolution + 'static>(
        ctx: &ScalarVariableContext<S>,
        leaves: &mut Vec<StandardLeafSelector<S>>,
    ) {
        leaves.push(StandardLeafSelector::Change(ChangeMoveSelector::new(
            FromSolutionEntitySelector::new(ctx.descriptor_index),
            StandardValueSelector::from_source(ctx.value_source),
            ctx.getter,
            ctx.setter,
            ctx.descriptor_index,
            ctx.variable_name,
        )));
    }

    fn push_swap<S: PlanningSolution + 'static>(
        ctx: &ScalarVariableContext<S>,
        leaves: &mut Vec<StandardLeafSelector<S>>,
    ) {
        leaves.push(StandardLeafSelector::Swap(SwapMoveSelector::new(
            FromSolutionEntitySelector::new(ctx.descriptor_index),
            FromSolutionEntitySelector::new(ctx.descriptor_index),
            ctx.getter,
            ctx.setter,
            ctx.descriptor_index,
            ctx.variable_name,
        )));
    }

    fn collect<S: PlanningSolution + 'static>(
        cfg: &MoveSelectorConfig,
        scalar_variables: &[ScalarVariableContext<S>],
        leaves: &mut Vec<StandardLeafSelector<S>>,
    ) {
        match cfg {
            MoveSelectorConfig::ChangeMoveSelector(change) => {
                let matched: Vec<_> = scalar_variables
                    .iter()
                    .cloned()
                    .filter(|ctx| {
                        ctx.matches_target(
                            change.target.entity_class.as_deref(),
                            change.target.variable_name.as_deref(),
                        )
                    })
                    .collect();
                assert!(
                    !matched.is_empty(),
                    "change_move selector matched no standard planning variables for entity_class={:?} variable_name={:?}",
                    change.target.entity_class,
                    change.target.variable_name
                );
                for ctx in matched {
                    push_change(&ctx, leaves);
                }
            }
            MoveSelectorConfig::SwapMoveSelector(swap) => {
                let matched: Vec<_> = scalar_variables
                    .iter()
                    .cloned()
                    .filter(|ctx| {
                        ctx.matches_target(
                            swap.target.entity_class.as_deref(),
                            swap.target.variable_name.as_deref(),
                        )
                    })
                    .collect();
                assert!(
                    !matched.is_empty(),
                    "swap_move selector matched no standard planning variables for entity_class={:?} variable_name={:?}",
                    swap.target.entity_class,
                    swap.target.variable_name
                );
                for ctx in matched {
                    push_swap(&ctx, leaves);
                }
            }
            MoveSelectorConfig::UnionMoveSelector(union) => {
                for child in &union.selectors {
                    collect(child, scalar_variables, leaves);
                }
            }
            MoveSelectorConfig::SelectedCountLimitMoveSelector(_) => {
                panic!(
                    "selected_count_limit_move_selector must be handled by the canonical runtime"
                );
            }
            MoveSelectorConfig::ListChangeMoveSelector(_)
            | MoveSelectorConfig::NearbyListChangeMoveSelector(_)
            | MoveSelectorConfig::ListSwapMoveSelector(_)
            | MoveSelectorConfig::NearbyListSwapMoveSelector(_)
            | MoveSelectorConfig::SubListChangeMoveSelector(_)
            | MoveSelectorConfig::SubListSwapMoveSelector(_)
            | MoveSelectorConfig::ListReverseMoveSelector(_)
            | MoveSelectorConfig::KOptMoveSelector(_)
            | MoveSelectorConfig::ListRuinMoveSelector(_) => {
                panic!("list move selector configured against a standard-variable context");
            }
            MoveSelectorConfig::CartesianProductMoveSelector(_) => {
                panic!("cartesian_product move selectors are not supported in the canonical solver path");
            }
        }
    }

    match config {
        Some(cfg) => collect(cfg, scalar_variables, leaves),
        None => {
            for ctx in scalar_variables {
                push_change(ctx, leaves);
                push_swap(ctx, leaves);
            }
        }
    }
}

#[cfg(test)]
#[path = "standard_selector_tests.rs"]
mod tests;