solverforge-solver 0.15.0

Solver engine for SolverForge
Documentation
use std::collections::{HashMap, HashSet};

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 ValueWindowCursor {
    pairs: Vec<(usize, usize)>,
    sequence_keys: Vec<usize>,
    by_value_sequence: HashMap<(usize, usize), Vec<usize>>,
    pair_pos: usize,
    start_pos: usize,
    len: usize,
    max_len: usize,
}

pub(super) struct ValueLongWindowCursor {
    pairs: Vec<(usize, usize)>,
    sequence_keys: Vec<usize>,
    by_value_sequence: HashMap<(usize, usize), Vec<usize>>,
    pair_pos: usize,
    length_pos: usize,
    start_pos: usize,
    lengths: Vec<usize>,
}

pub(super) struct ValueBlockReassignmentCursor {
    pairs: Vec<(usize, usize)>,
    sequence_keys: Vec<usize>,
    by_value_sequence: HashMap<(usize, usize), Vec<usize>>,
    pair_pos: usize,
    start_pos: usize,
    len: usize,
    max_len: usize,
}

impl ValueLongWindowCursor {
    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 mut pairs = Vec::new();
        for left_pos in 0..index.values.len() {
            for right_pos in left_pos + 1..index.values.len() {
                pairs.push((index.values[left_pos], index.values[right_pos]));
            }
        }
        if !pairs.is_empty() {
            let pair_count = pairs.len();
            pairs.rotate_left(options.entity_offset % pair_count);
        }

        let base_len = options
            .max_rematch_size
            .min(index.sequence_keys.len())
            .max(options.max_depth.max(2));
        let lengths = vec![base_len];
        Self {
            pairs,
            sequence_keys: index.sequence_keys,
            by_value_sequence: index.by_value_sequence,
            pair_pos: 0,
            length_pos: 0,
            start_pos: 0,
            lengths,
        }
    }

    pub(super) fn next<S>(
        &mut self,
        group: &ScalarAssignmentBinding<S>,
        solution: &S,
        state: &mut ScalarAssignmentState,
    ) -> Option<CompoundScalarMove<S>>
    where
        S: PlanningSolution,
    {
        while self.pair_pos < self.pairs.len() {
            while self.length_pos < self.lengths.len() {
                let len = self.lengths[self.length_pos];
                while self.start_pos + len <= self.sequence_keys.len() {
                    let (left_value, right_value) = self.pairs[self.pair_pos];
                    let sequence_window = &self.sequence_keys[self.start_pos..self.start_pos + len];
                    self.start_pos += 1;
                    let edits = value_window_edits(
                        &self.by_value_sequence,
                        group,
                        solution,
                        state,
                        left_value,
                        right_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_long_window_swap",
                    ) {
                        return Some(mov);
                    }
                }
                self.length_pos += 1;
                self.start_pos = 0;
            }
            self.pair_pos += 1;
            self.length_pos = 0;
            self.start_pos = 0;
        }
        None
    }
}

impl ValueWindowCursor {
    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 mut pairs = Vec::new();
        for left_pos in 0..index.values.len() {
            for right_pos in left_pos + 1..index.values.len() {
                pairs.push((index.values[left_pos], index.values[right_pos]));
            }
        }
        if !pairs.is_empty() {
            let pair_count = pairs.len();
            pairs.rotate_left(options.entity_offset % pair_count);
        }

        let max_len = options
            .max_depth
            .max(3)
            .min(index.sequence_keys.len())
            .max(2);
        Self {
            pairs,
            sequence_keys: index.sequence_keys,
            by_value_sequence: index.by_value_sequence,
            pair_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.pair_pos < self.pairs.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 (left_value, right_value) = self.pairs[self.pair_pos];
                    let sequence_window =
                        &self.sequence_keys[self.start_pos..self.start_pos + self.len];
                    self.len += 1;
                    let edits = value_window_edits(
                        &self.by_value_sequence,
                        group,
                        solution,
                        state,
                        left_value,
                        right_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_window_swap",
                    ) {
                        return Some(mov);
                    }
                }
                self.start_pos += 1;
                self.len = 2;
            }
            self.pair_pos += 1;
            self.start_pos = 0;
            self.len = 2;
        }
        None
    }
}

impl ValueBlockReassignmentCursor {
    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 mut pairs = Vec::new();
        for source in &index.values {
            for target in &index.values {
                if source != target {
                    pairs.push((*source, *target));
                }
            }
        }
        if !pairs.is_empty() {
            let pair_count = pairs.len();
            pairs.rotate_left(options.entity_offset % pair_count);
        }

        let max_len = options
            .max_depth
            .max(2)
            .min(index.sequence_keys.len())
            .max(2);
        Self {
            pairs,
            sequence_keys: index.sequence_keys,
            by_value_sequence: index.by_value_sequence,
            pair_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.pair_pos < self.pairs.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 (source_value, target_value) = self.pairs[self.pair_pos];
                    let sequence_window =
                        &self.sequence_keys[self.start_pos..self.start_pos + self.len];
                    self.len += 1;
                    let edits = self.window_edits(
                        group,
                        solution,
                        state,
                        source_value,
                        target_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_block_reassignment",
                    ) {
                        return Some(mov);
                    }
                }
                self.start_pos += 1;
                self.len = 2;
            }
            self.pair_pos += 1;
            self.start_pos = 0;
            self.len = 2;
        }
        None
    }

    fn window_edits<S>(
        &self,
        group: &ScalarAssignmentBinding<S>,
        solution: &S,
        state: &ScalarAssignmentState,
        source_value: usize,
        target_value: usize,
        sequence_window: &[usize],
    ) -> Vec<(usize, Option<usize>)>
    where
        S: PlanningSolution,
    {
        let mut edits = Vec::new();
        for sequence_key in sequence_window {
            if let Some(source_entities) =
                self.by_value_sequence.get(&(source_value, *sequence_key))
            {
                for entity_index in source_entities {
                    if state.current_value(*entity_index) != Some(target_value)
                        && group.value_is_legal(solution, *entity_index, Some(target_value))
                    {
                        edits.push((*entity_index, Some(target_value)));
                    }
                }
            }
        }
        edits
    }
}

fn value_window_edits<S>(
    by_value_sequence: &HashMap<(usize, usize), Vec<usize>>,
    group: &ScalarAssignmentBinding<S>,
    solution: &S,
    state: &ScalarAssignmentState,
    left_value: usize,
    right_value: usize,
    sequence_window: &[usize],
) -> Vec<(usize, Option<usize>)>
where
    S: PlanningSolution,
{
    let mut edits = Vec::new();
    let mut touched = HashSet::new();
    for sequence_key in sequence_window {
        if let Some(left_entities) = by_value_sequence.get(&(left_value, *sequence_key)) {
            for entity_index in left_entities {
                if touched.insert(*entity_index)
                    && state.current_value(*entity_index) != Some(right_value)
                    && group.value_is_legal(solution, *entity_index, Some(right_value))
                {
                    edits.push((*entity_index, Some(right_value)));
                }
            }
        }
        if let Some(right_entities) = by_value_sequence.get(&(right_value, *sequence_key)) {
            for entity_index in right_entities {
                if touched.insert(*entity_index)
                    && state.current_value(*entity_index) != Some(left_value)
                    && group.value_is_legal(solution, *entity_index, Some(left_value))
                {
                    edits.push((*entity_index, Some(left_value)));
                }
            }
        }
    }
    edits
}