solverforge-scoring 0.15.0

Incremental constraint scoring for SolverForge
Documentation
use std::collections::VecDeque;

use solverforge_core::score::Score;

use super::incremental::{ConstraintMetadata, ConstraintResult, ConstraintSet};
use crate::api::analysis::ConstraintAnalysis;

pub struct ConstraintSetChain<Left, Right> {
    left: Left,
    right: Right,
}

pub enum ConstraintSetSource {
    Left,
    Right {
        constraint_count: usize,
        metadata_entry_count: usize,
    },
}

pub struct OrderedConstraintSetChain<Left, Right> {
    left: Left,
    right: Right,
    order: Vec<ConstraintSetSource>,
}

impl<Left, Right> ConstraintSetChain<Left, Right> {
    pub fn new(left: Left, right: Right) -> Self {
        Self { left, right }
    }
}

impl<Left, Right> OrderedConstraintSetChain<Left, Right> {
    pub fn new(left: Left, right: Right, order: Vec<ConstraintSetSource>) -> Self {
        Self { left, right, order }
    }
}

impl<S, Sc, Left, Right> ConstraintSet<S, Sc> for ConstraintSetChain<Left, Right>
where
    S: Send + Sync,
    Sc: Score,
    Left: ConstraintSet<S, Sc>,
    Right: ConstraintSet<S, Sc>,
{
    #[inline]
    fn evaluate_all(&self, solution: &S) -> Sc {
        self.left.evaluate_all(solution) + self.right.evaluate_all(solution)
    }

    #[inline]
    fn constraint_count(&self) -> usize {
        self.left.constraint_count() + self.right.constraint_count()
    }

    fn constraint_metadata_entries(&self) -> Vec<ConstraintMetadata<'_>> {
        let mut metadata = self.left.constraint_metadata_entries();
        metadata.extend(self.right.constraint_metadata_entries());
        metadata
    }

    fn evaluate_each<'a>(&'a self, solution: &S) -> Vec<ConstraintResult<'a, Sc>> {
        let mut results = self.left.evaluate_each(solution);
        results.extend(self.right.evaluate_each(solution));
        results
    }

    fn evaluate_detailed<'a>(&'a self, solution: &S) -> Vec<ConstraintAnalysis<'a, Sc>> {
        let mut analyses = self.left.evaluate_detailed(solution);
        analyses.extend(self.right.evaluate_detailed(solution));
        analyses
    }

    #[inline]
    fn initialize_all(&mut self, solution: &S) -> Sc {
        self.left.initialize_all(solution) + self.right.initialize_all(solution)
    }

    #[inline]
    fn on_insert_all(&mut self, solution: &S, entity_index: usize, descriptor_index: usize) -> Sc {
        self.left
            .on_insert_all(solution, entity_index, descriptor_index)
            + self
                .right
                .on_insert_all(solution, entity_index, descriptor_index)
    }

    #[inline]
    fn on_retract_all(&mut self, solution: &S, entity_index: usize, descriptor_index: usize) -> Sc {
        self.left
            .on_retract_all(solution, entity_index, descriptor_index)
            + self
                .right
                .on_retract_all(solution, entity_index, descriptor_index)
    }

    #[inline]
    fn reset_all(&mut self) {
        self.left.reset_all();
        self.right.reset_all();
    }
}

impl<S, Sc, Left, Right> ConstraintSet<S, Sc> for OrderedConstraintSetChain<Left, Right>
where
    S: Send + Sync,
    Sc: Score,
    Left: ConstraintSet<S, Sc>,
    Right: ConstraintSet<S, Sc>,
{
    #[inline]
    fn evaluate_all(&self, solution: &S) -> Sc {
        self.left.evaluate_all(solution) + self.right.evaluate_all(solution)
    }

    #[inline]
    fn constraint_count(&self) -> usize {
        self.left.constraint_count() + self.right.constraint_count()
    }

    fn constraint_metadata_entries(&self) -> Vec<ConstraintMetadata<'_>> {
        let mut left = VecDeque::from(self.left.constraint_metadata_entries());
        let mut right = VecDeque::from(self.right.constraint_metadata_entries());
        let mut metadata = Vec::new();
        for source in &self.order {
            match source {
                ConstraintSetSource::Left => {
                    let Some(candidate) = left.pop_front() else {
                        panic!("ordered constraint set source order does not match metadata");
                    };
                    metadata.push(candidate);
                }
                ConstraintSetSource::Right {
                    metadata_entry_count,
                    ..
                } => {
                    for _ in 0..*metadata_entry_count {
                        let Some(candidate) = right.pop_front() else {
                            panic!("ordered constraint set source order does not match metadata");
                        };
                        metadata.push(candidate);
                    }
                }
            }
        }
        assert!(
            left.is_empty() && right.is_empty(),
            "ordered constraint set source order does not consume every metadata entry"
        );
        metadata
    }

    fn evaluate_each<'a>(&'a self, solution: &S) -> Vec<ConstraintResult<'a, Sc>> {
        let mut left = VecDeque::from(self.left.evaluate_each(solution));
        let mut right = VecDeque::from(self.right.evaluate_each(solution));
        let mut results = Vec::with_capacity(self.constraint_count());
        for source in &self.order {
            match source {
                ConstraintSetSource::Left => {
                    let Some(result) = left.pop_front() else {
                        panic!(
                            "ordered constraint set source order does not match constraint results"
                        );
                    };
                    results.push(result);
                }
                ConstraintSetSource::Right {
                    constraint_count, ..
                } => {
                    for _ in 0..*constraint_count {
                        let Some(result) = right.pop_front() else {
                            panic!(
                                "ordered constraint set source order does not match constraint results"
                            );
                        };
                        results.push(result);
                    }
                }
            }
        }
        assert!(
            left.is_empty() && right.is_empty(),
            "ordered constraint set source order does not consume every result"
        );
        results
    }

    fn evaluate_detailed<'a>(&'a self, solution: &S) -> Vec<ConstraintAnalysis<'a, Sc>> {
        let mut left = VecDeque::from(self.left.evaluate_detailed(solution));
        let mut right = VecDeque::from(self.right.evaluate_detailed(solution));
        let mut analyses = Vec::with_capacity(self.constraint_count());
        for source in &self.order {
            match source {
                ConstraintSetSource::Left => {
                    let Some(analysis) = left.pop_front() else {
                        panic!("ordered constraint set source order does not match constraint analyses");
                    };
                    analyses.push(analysis);
                }
                ConstraintSetSource::Right {
                    constraint_count, ..
                } => {
                    for _ in 0..*constraint_count {
                        let Some(analysis) = right.pop_front() else {
                            panic!(
                                "ordered constraint set source order does not match constraint analyses"
                            );
                        };
                        analyses.push(analysis);
                    }
                }
            }
        }
        assert!(
            left.is_empty() && right.is_empty(),
            "ordered constraint set source order does not consume every analysis"
        );
        analyses
    }

    #[inline]
    fn initialize_all(&mut self, solution: &S) -> Sc {
        self.left.initialize_all(solution) + self.right.initialize_all(solution)
    }

    #[inline]
    fn on_insert_all(&mut self, solution: &S, entity_index: usize, descriptor_index: usize) -> Sc {
        self.left
            .on_insert_all(solution, entity_index, descriptor_index)
            + self
                .right
                .on_insert_all(solution, entity_index, descriptor_index)
    }

    #[inline]
    fn on_retract_all(&mut self, solution: &S, entity_index: usize, descriptor_index: usize) -> Sc {
        self.left
            .on_retract_all(solution, entity_index, descriptor_index)
            + self
                .right
                .on_retract_all(solution, entity_index, descriptor_index)
    }

    #[inline]
    fn reset_all(&mut self) {
        self.left.reset_all();
        self.right.reset_all();
    }
}