solverforge-scoring 0.15.0

Incremental constraint scoring for SolverForge
Documentation
use solverforge_core::score::Score;
use solverforge_core::ConstraintRef;

use crate::api::analysis::ConstraintAnalysis;
use crate::api::constraint_set::{ConstraintMetadata, ConstraintResult};

use super::scorer::GroupedTerminalScorer;

pub trait ComplementedGroupedStateView<K, R> {
    fn for_each_complement_result<Visit>(&self, visit: Visit)
    where
        Visit: FnMut(&K, &R);

    fn for_each_complement_slot_result<Visit>(&self, visit: Visit)
    where
        Visit: FnMut(usize, Option<(&K, &R)>);

    fn for_each_changed_complement_slot_result<Visit>(&self, visit: Visit)
    where
        Visit: FnMut(usize, Option<(&K, &R)>);

    fn for_each_key_result<Visit>(&self, key: &K, visit: Visit)
    where
        Visit: FnMut(&R);

    fn complement_count(&self) -> usize;
}

pub trait ComplementedGroupedScorerSet<K, R, Sc: Score>: Send + Sync {
    fn evaluate<State>(&self, state: &State) -> Sc
    where
        State: ComplementedGroupedStateView<K, R>;

    fn initialize<State>(&mut self, state: &State) -> Sc
    where
        State: ComplementedGroupedStateView<K, R>;

    fn refresh_all<State>(&mut self, state: &State) -> Sc
    where
        State: ComplementedGroupedStateView<K, R>;

    fn refresh_changed<State>(&mut self, state: &State) -> Sc
    where
        State: ComplementedGroupedStateView<K, R>;

    fn constraint_count(&self) -> usize;

    fn primary_constraint_ref(&self) -> &ConstraintRef;

    fn constraint_metadata(&self) -> Vec<ConstraintMetadata<'_>>;

    fn evaluate_each<'a, State>(&'a self, state: &State) -> Vec<ConstraintResult<'a, Sc>>
    where
        State: ComplementedGroupedStateView<K, R>;

    fn evaluate_detailed<'a, State>(&'a self, state: &State) -> Vec<ConstraintAnalysis<'a, Sc>>
    where
        State: ComplementedGroupedStateView<K, R>;

    fn reset(&mut self);
}

impl<K, R, W, Sc> ComplementedGroupedScorerSet<K, R, Sc> for GroupedTerminalScorer<K, R, W, Sc>
where
    R: Send + Sync + 'static,
    W: Fn(&K, &R) -> Sc + Send + Sync,
    Sc: Score + 'static,
{
    fn evaluate<State>(&self, state: &State) -> Sc
    where
        State: ComplementedGroupedStateView<K, R>,
    {
        let mut total = Sc::zero();
        state.for_each_complement_result(|key, result| {
            total = total + self.compute_score(key, result);
        });
        total
    }

    fn initialize<State>(&mut self, state: &State) -> Sc
    where
        State: ComplementedGroupedStateView<K, R>,
    {
        self.reset_incremental_cache(state.complement_count());
        let mut total = Sc::zero();
        state.for_each_complement_slot_result(|slot, entry| {
            let score = self.score_entry(entry);
            self.replace_cached_score(slot, score);
            total = total + score;
        });
        total
    }

    fn refresh_all<State>(&mut self, state: &State) -> Sc
    where
        State: ComplementedGroupedStateView<K, R>,
    {
        self.mark_incremental_refresh(state.complement_count());
        self.clear_incremental_scores();
        let mut total = Sc::zero();
        state.for_each_complement_slot_result(|slot, entry| {
            let score = self.score_entry(entry);
            self.replace_cached_score(slot, score);
            total = total + score;
        });
        total
    }

    fn refresh_changed<State>(&mut self, state: &State) -> Sc
    where
        State: ComplementedGroupedStateView<K, R>,
    {
        self.mark_incremental_refresh(state.complement_count());
        let mut delta = Sc::zero();
        state.for_each_changed_complement_slot_result(|slot, entry| {
            let score = self.score_entry(entry);
            delta = delta + self.replace_cached_score(slot, score);
        });
        delta
    }

    fn constraint_count(&self) -> usize {
        1
    }

    fn primary_constraint_ref(&self) -> &ConstraintRef {
        self.constraint_ref()
    }

    fn constraint_metadata(&self) -> Vec<ConstraintMetadata<'_>> {
        vec![ConstraintMetadata::new(
            self.constraint_ref(),
            self.is_hard(),
        )]
    }

    fn evaluate_each<'a, State>(&'a self, state: &State) -> Vec<ConstraintResult<'a, Sc>>
    where
        State: ComplementedGroupedStateView<K, R>,
    {
        vec![ConstraintResult {
            name: self.name(),
            score: <Self as ComplementedGroupedScorerSet<K, R, Sc>>::evaluate(self, state),
            match_count: state.complement_count(),
            is_hard: self.is_hard(),
        }]
    }

    fn evaluate_detailed<'a, State>(&'a self, state: &State) -> Vec<ConstraintAnalysis<'a, Sc>>
    where
        State: ComplementedGroupedStateView<K, R>,
    {
        vec![ConstraintAnalysis::new(
            self.constraint_ref(),
            Sc::zero(),
            <Self as ComplementedGroupedScorerSet<K, R, Sc>>::evaluate(self, state),
            Vec::new(),
            self.is_hard(),
        )]
    }

    fn reset(&mut self) {
        self.reset();
    }
}

macro_rules! impl_complemented_grouped_scorer_set_for_tuple {
    ($($idx:tt: $T:ident),+) => {
        impl<K, R, Sc, $($T),+> ComplementedGroupedScorerSet<K, R, Sc> for ($($T,)+)
        where
            R: Send + Sync + 'static,
            Sc: Score + 'static,
            $($T: ComplementedGroupedScorerSet<K, R, Sc>,)+
        {
            fn evaluate<State>(&self, state: &State) -> Sc
            where
                State: ComplementedGroupedStateView<K, R>,
            {
                let mut total = Sc::zero();
                $(total = total + ComplementedGroupedScorerSet::evaluate(&self.$idx, state);)+
                total
            }

            fn initialize<State>(&mut self, state: &State) -> Sc
            where
                State: ComplementedGroupedStateView<K, R>,
            {
                let mut total = Sc::zero();
                $(total = total + ComplementedGroupedScorerSet::initialize(&mut self.$idx, state);)+
                total
            }

            fn refresh_all<State>(&mut self, state: &State) -> Sc
            where
                State: ComplementedGroupedStateView<K, R>,
            {
                let mut total = Sc::zero();
                $(total = total + ComplementedGroupedScorerSet::refresh_all(&mut self.$idx, state);)+
                total
            }

            fn refresh_changed<State>(&mut self, state: &State) -> Sc
            where
                State: ComplementedGroupedStateView<K, R>,
            {
                let mut total = Sc::zero();
                $(total = total + ComplementedGroupedScorerSet::refresh_changed(&mut self.$idx, state);)+
                total
            }

            fn constraint_count(&self) -> usize {
                let mut count = 0;
                $(let _ = &self.$idx; count += ComplementedGroupedScorerSet::constraint_count(&self.$idx);)+
                count
            }

            fn primary_constraint_ref(&self) -> &ConstraintRef {
                self.0.primary_constraint_ref()
            }

            fn constraint_metadata(&self) -> Vec<ConstraintMetadata<'_>> {
                let mut metadata = Vec::new();
                $(
                    metadata.extend(ComplementedGroupedScorerSet::constraint_metadata(&self.$idx));
                )+
                metadata
            }

            fn evaluate_each<'a, State>(&'a self, state: &State) -> Vec<ConstraintResult<'a, Sc>>
            where
                State: ComplementedGroupedStateView<K, R>,
            {
                let mut results = Vec::new();
                $(results.extend(ComplementedGroupedScorerSet::evaluate_each(&self.$idx, state));)+
                results
            }

            fn evaluate_detailed<'a, State>(&'a self, state: &State) -> Vec<ConstraintAnalysis<'a, Sc>>
            where
                State: ComplementedGroupedStateView<K, R>,
            {
                let mut analyses = Vec::new();
                $(analyses.extend(ComplementedGroupedScorerSet::evaluate_detailed(&self.$idx, state));)+
                analyses
            }

            fn reset(&mut self) {
                $(ComplementedGroupedScorerSet::reset(&mut self.$idx);)+
            }
        }
    };
}

impl_complemented_grouped_scorer_set_for_tuple!(0: C0);
impl_complemented_grouped_scorer_set_for_tuple!(0: C0, 1: C1);
impl_complemented_grouped_scorer_set_for_tuple!(0: C0, 1: C1, 2: C2);
impl_complemented_grouped_scorer_set_for_tuple!(0: C0, 1: C1, 2: C2, 3: C3);
impl_complemented_grouped_scorer_set_for_tuple!(0: C0, 1: C1, 2: C2, 3: C3, 4: C4);
impl_complemented_grouped_scorer_set_for_tuple!(0: C0, 1: C1, 2: C2, 3: C3, 4: C4, 5: C5);
impl_complemented_grouped_scorer_set_for_tuple!(0: C0, 1: C1, 2: C2, 3: C3, 4: C4, 5: C5, 6: C6);
impl_complemented_grouped_scorer_set_for_tuple!(0: C0, 1: C1, 2: C2, 3: C3, 4: C4, 5: C5, 6: C6, 7: C7);