solverforge-solver 0.15.0

Solver engine for SolverForge
Documentation
use std::collections::HashMap;

use solverforge_core::domain::PlanningSolution;

use super::assignment_candidate::ScalarAssignmentMoveOptions;
use super::assignment_path::move_from_edits;
use super::assignment_state::ScalarAssignmentState;
use super::assignment_value_index::assigned_value_sequence_index;
use crate::builder::ScalarAssignmentBinding;
use crate::heuristic::r#move::CompoundScalarMove;

pub(super) struct ValueRunReleaseCursor {
    values: Vec<usize>,
    sequence_keys: Vec<usize>,
    by_value_sequence: HashMap<(usize, usize), Vec<usize>>,
    value_pos: usize,
    start_pos: usize,
    len: usize,
    max_len: usize,
}

impl ValueRunReleaseCursor {
    pub(super) fn new<S>(
        group: &ScalarAssignmentBinding<S>,
        solution: &S,
        state: &ScalarAssignmentState,
        options: ScalarAssignmentMoveOptions,
    ) -> Self
    where
        S: PlanningSolution,
    {
        let index = assigned_value_sequence_index(group, solution, state, options);
        let max_len = options
            .max_rematch_size
            .min(index.sequence_keys.len())
            .max(options.max_depth.max(2));
        Self {
            values: index.values,
            sequence_keys: index.sequence_keys,
            by_value_sequence: index.by_value_sequence,
            value_pos: 0,
            start_pos: 0,
            len: 2,
            max_len,
        }
    }

    pub(super) fn next<S>(
        &mut self,
        group: &ScalarAssignmentBinding<S>,
        solution: &S,
        state: &mut ScalarAssignmentState,
    ) -> Option<CompoundScalarMove<S>>
    where
        S: PlanningSolution,
    {
        while self.value_pos < self.values.len() {
            while self.start_pos < self.sequence_keys.len() {
                while self.len <= self.max_len {
                    if self.start_pos + self.len > self.sequence_keys.len() {
                        break;
                    }
                    let value = self.values[self.value_pos];
                    let sequence_window =
                        &self.sequence_keys[self.start_pos..self.start_pos + self.len];
                    self.len += 1;
                    let edits = self.window_release_edits(state, value, sequence_window);
                    if edits.len() < 2
                        || !state.assignment_feasible_after_edits(group, solution, &edits)
                    {
                        continue;
                    }
                    let scalar_edits = edits
                        .iter()
                        .map(|(entity_index, value)| group.edit(*entity_index, *value))
                        .collect::<Vec<_>>();
                    if let Some(mov) = move_from_edits(
                        group,
                        solution,
                        &scalar_edits,
                        "scalar_assignment_value_run_release",
                    ) {
                        return Some(mov);
                    }
                }
                self.start_pos += 1;
                self.len = 2;
            }
            self.value_pos += 1;
            self.start_pos = 0;
            self.len = 2;
        }
        None
    }

    fn window_release_edits(
        &self,
        state: &ScalarAssignmentState,
        value: usize,
        sequence_window: &[usize],
    ) -> Vec<(usize, Option<usize>)> {
        let mut edits = Vec::new();
        for sequence_key in sequence_window {
            let Some(entities) = self.by_value_sequence.get(&(value, *sequence_key)) else {
                return Vec::new();
            };
            for entity_index in entities {
                if !state.is_required(*entity_index) {
                    edits.push((*entity_index, None));
                }
            }
        }
        edits
    }
}