solverforge-scoring 0.19.11

Incremental constraint scoring for SolverForge
Documentation
use std::hash::Hash;

use solverforge_core::score::Score;
use solverforge_core::ConstraintRef;

use crate::api::constraint_set::IncrementalConstraint;
use crate::stream::filter::{BiFilter, UniFilter};
use crate::stream::projected::Source;

use super::directed_bi::DirectedBi;

impl<S, Out, K, Src, F, KL, KR, PF, W, Sc> IncrementalConstraint<S, Sc>
    for DirectedBi<S, Out, K, Src, F, KL, KR, PF, W, Sc>
where
    S: Send + Sync + 'static,
    Out: Send + Sync + 'static,
    K: Eq + Hash + Send + Sync + 'static,
    Src: Source<S, Out>,
    F: UniFilter<S, Out>,
    KL: Fn(&Out) -> K + Send + Sync,
    KR: Fn(&Out) -> K + Send + Sync,
    PF: BiFilter<S, Out, Out>,
    W: Fn(&Out, &Out) -> Sc + Send + Sync,
    Sc: Score + 'static,
{
    fn evaluate(&self, solution: &S) -> Sc {
        let rows = self.evaluate_rows(solution);
        let right_index = self.right_index_for(&rows);

        let mut total = Sc::zero();
        for (left_index, left) in rows.iter().enumerate() {
            let key = (self.left_key_fn)(&left.output);
            let Some(right_indices) = right_index.get(&key) else {
                continue;
            };
            for &right_index in right_indices {
                if right_index == left_index {
                    continue;
                }
                total = total + self.score_evaluation_pair(solution, left, &rows[right_index]);
            }
        }
        total
    }

    fn match_count(&self, solution: &S) -> usize {
        let rows = self.evaluate_rows(solution);
        let right_index = self.right_index_for(&rows);

        let mut count = 0;
        for (left_index, left) in rows.iter().enumerate() {
            let key = (self.left_key_fn)(&left.output);
            let Some(right_indices) = right_index.get(&key) else {
                continue;
            };
            for &right_index in right_indices {
                if right_index != left_index
                    && self.evaluation_pair_matches(solution, left, &rows[right_index])
                {
                    count += 1;
                }
            }
        }
        count
    }

    fn initialize(&mut self, solution: &S) -> Sc {
        self.reset();
        let state = self.source.build_state(solution);
        let mut rows = Vec::new();
        self.source
            .collect_all(solution, &state, |coordinate, output| {
                if self.filter.test(solution, &output) {
                    rows.push((coordinate, output));
                }
            });
        self.source_state = Some(state);

        rows.into_iter()
            .fold(Sc::zero(), |total, (coordinate, output)| {
                total + self.insert_row(solution, coordinate, output)
            })
    }

    fn on_insert(&mut self, solution: &S, entity_index: usize, descriptor_index: usize) -> Sc {
        let owners = self.localized_owners(descriptor_index, entity_index);
        self.ensure_source_state(solution);
        {
            let state = self.source_state.as_mut().expect("projected source state");
            for owner in &owners {
                self.source.insert_entity_state(
                    solution,
                    state,
                    owner.source_slot,
                    owner.entity_index,
                );
            }
        }
        let mut rows = Vec::new();
        let state = self.source_state.as_ref().expect("projected source state");
        for owner in &owners {
            self.source.collect_entity(
                solution,
                state,
                owner.source_slot,
                owner.entity_index,
                |coordinate, output| {
                    if self.filter.test(solution, &output) {
                        rows.push((coordinate, output));
                    }
                },
            );
        }
        let mut total = Sc::zero();
        for (coordinate, output) in rows {
            total = total + self.insert_row(solution, coordinate, output);
        }
        total
    }

    fn on_retract(&mut self, solution: &S, entity_index: usize, descriptor_index: usize) -> Sc {
        let owners = self.localized_owners(descriptor_index, entity_index);
        let mut total = Sc::zero();
        for row_id in self.row_ids_for_owners(&owners) {
            total = total + self.retract_row(solution, row_id);
        }
        if let Some(state) = self.source_state.as_mut() {
            for owner in &owners {
                self.source.retract_entity_state(
                    solution,
                    state,
                    owner.source_slot,
                    owner.entity_index,
                );
            }
        }
        total
    }

    fn reset(&mut self) {
        self.source_state = None;
        self.rows.clear();
        self.free_row_ids.clear();
        self.rows_by_owner.clear();
        self.row_ids_by_coordinate.clear();
        self.rows_by_left_key.clear();
        self.rows_by_right_key.clear();
    }

    fn name(&self) -> &str {
        &self.constraint_ref.name
    }

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

    fn is_hard(&self) -> bool {
        self.is_hard
    }
}