solverforge-solver 0.11.1

Solver engine for SolverForge
Documentation
fn build_sub_pillar_config(minimum_size: usize, maximum_size: usize) -> SubPillarConfig {
    if minimum_size == 0 || maximum_size == 0 {
        SubPillarConfig::none()
    } else {
        SubPillarConfig {
            enabled: true,
            minimum_size: minimum_size.max(2),
            maximum_size: maximum_size.max(minimum_size.max(2)),
        }
    }
}

#[derive(Clone)]
pub struct DescriptorPillarChangeMoveSelector<S> {
    binding: VariableBinding,
    solution_descriptor: SolutionDescriptor,
    minimum_sub_pillar_size: usize,
    maximum_sub_pillar_size: usize,
    value_candidate_limit: Option<usize>,
    _phantom: PhantomData<fn() -> S>,
}

impl<S> Debug for DescriptorPillarChangeMoveSelector<S> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("DescriptorPillarChangeMoveSelector")
            .field("binding", &self.binding)
            .field("minimum_sub_pillar_size", &self.minimum_sub_pillar_size)
            .field("maximum_sub_pillar_size", &self.maximum_sub_pillar_size)
            .field("value_candidate_limit", &self.value_candidate_limit)
            .finish()
    }
}

impl<S> MoveSelector<S, DescriptorScalarMoveUnion<S>> for DescriptorPillarChangeMoveSelector<S>
where
    S: PlanningSolution + 'static,
    S::Score: Score,
{
    type Cursor<'a>
        = ArenaMoveCursor<S, DescriptorScalarMoveUnion<S>>
    where
        Self: 'a;

    fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
        let solution = score_director.working_solution() as &dyn Any;
        let binding = self.binding.clone();
        let descriptor = self.solution_descriptor.clone();
        let count = score_director
            .entity_count(binding.descriptor_index)
            .unwrap_or(0);
        let moves: Vec<_> = collect_pillar_groups(
            (0..count).map(|entity_index| {
                (
                    EntityReference::new(binding.descriptor_index, entity_index),
                    (binding.getter)(binding.entity_for_index(&descriptor, solution, entity_index)),
                )
            }),
            &build_sub_pillar_config(self.minimum_sub_pillar_size, self.maximum_sub_pillar_size),
        )
        .into_iter()
        .flat_map(move |group| {
            let entity_indices: Vec<usize> = group
                .pillar
                .iter()
                .map(|entity| entity.entity_index)
                .collect();
            intersect_legal_values_for_pillar(&group.pillar, |entity_index| {
                binding.candidate_values_for_entity_index(
                    &descriptor,
                    solution,
                    entity_index,
                    self.value_candidate_limit,
                )
            })
            .into_iter()
            .filter(move |&value| value != group.shared_value)
            .map({
                let binding = binding.clone();
                let descriptor = descriptor.clone();
                let entity_indices = entity_indices.clone();
                move |value| {
                    DescriptorScalarMoveUnion::PillarChange(DescriptorPillarChangeMove::new(
                        binding.clone(),
                        entity_indices.clone(),
                        Some(value),
                        descriptor.clone(),
                    ))
                }
            })
        })
        .collect();
        ArenaMoveCursor::from_moves(moves)
    }

    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
        self.open_cursor(score_director).count()
    }
}

#[derive(Clone)]
pub struct DescriptorPillarSwapMoveSelector<S> {
    binding: VariableBinding,
    solution_descriptor: SolutionDescriptor,
    minimum_sub_pillar_size: usize,
    maximum_sub_pillar_size: usize,
    _phantom: PhantomData<fn() -> S>,
}

impl<S> Debug for DescriptorPillarSwapMoveSelector<S> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("DescriptorPillarSwapMoveSelector")
            .field("binding", &self.binding)
            .field("minimum_sub_pillar_size", &self.minimum_sub_pillar_size)
            .field("maximum_sub_pillar_size", &self.maximum_sub_pillar_size)
            .finish()
    }
}

impl<S> MoveSelector<S, DescriptorScalarMoveUnion<S>> for DescriptorPillarSwapMoveSelector<S>
where
    S: PlanningSolution + 'static,
    S::Score: Score,
{
    type Cursor<'a>
        = ArenaMoveCursor<S, DescriptorScalarMoveUnion<S>>
    where
        Self: 'a;

    fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
        let binding = self.binding.clone();
        let descriptor = self.solution_descriptor.clone();
        let solution = score_director.working_solution() as &dyn Any;
        let count = score_director
            .entity_count(binding.descriptor_index)
            .unwrap_or(0);
        let pillars = collect_pillar_groups(
            (0..count).map(|entity_index| {
                (
                    EntityReference::new(binding.descriptor_index, entity_index),
                    (binding.getter)(binding.entity_for_index(&descriptor, solution, entity_index)),
                )
            }),
            &build_sub_pillar_config(self.minimum_sub_pillar_size, self.maximum_sub_pillar_size),
        );
        let mut moves = Vec::new();
        for left_index in 0..pillars.len() {
            for right_index in (left_index + 1)..pillars.len() {
                if !binding.has_unspecified_value_range()
                    && !pillars_are_swap_compatible(
                        &pillars[left_index],
                        &pillars[right_index],
                        |entity_index| {
                            binding.values_for_entity_index(&descriptor, solution, entity_index)
                        },
                    )
                {
                    continue;
                }
                moves.push(DescriptorScalarMoveUnion::PillarSwap(
                    DescriptorPillarSwapMove::new(
                        binding.clone(),
                        pillars[left_index]
                            .pillar
                            .iter()
                            .map(|entity| entity.entity_index)
                            .collect(),
                        pillars[right_index]
                            .pillar
                            .iter()
                            .map(|entity| entity.entity_index)
                            .collect(),
                        descriptor.clone(),
                    ),
                ));
            }
        }
        ArenaMoveCursor::from_moves(moves)
    }

    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
        self.open_cursor(score_director).count()
    }
}

#[derive(Clone)]
pub struct DescriptorRuinRecreateMoveSelector<S> {
    binding: VariableBinding,
    solution_descriptor: SolutionDescriptor,
    min_ruin_count: usize,
    max_ruin_count: usize,
    moves_per_step: usize,
    value_candidate_limit: Option<usize>,
    recreate_heuristic_type: solverforge_config::RecreateHeuristicType,
    rng: RefCell<SmallRng>,
    _phantom: PhantomData<fn() -> S>,
}

impl<S> Debug for DescriptorRuinRecreateMoveSelector<S> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("DescriptorRuinRecreateMoveSelector")
            .field("binding", &self.binding)
            .field("min_ruin_count", &self.min_ruin_count)
            .field("max_ruin_count", &self.max_ruin_count)
            .field("moves_per_step", &self.moves_per_step)
            .field("value_candidate_limit", &self.value_candidate_limit)
            .field("recreate_heuristic_type", &self.recreate_heuristic_type)
            .finish()
    }
}

impl<S> MoveSelector<S, DescriptorScalarMoveUnion<S>> for DescriptorRuinRecreateMoveSelector<S>
where
    S: PlanningSolution + 'static,
    S::Score: Score,
{
    type Cursor<'a>
        = ArenaMoveCursor<S, DescriptorScalarMoveUnion<S>>
    where
        Self: 'a;

    fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
        let solution = score_director.working_solution() as &dyn Any;
        let count = score_director
            .entity_count(self.binding.descriptor_index)
            .unwrap_or(0);
        let descriptor = self.solution_descriptor.clone();
        let binding = self.binding.clone();
        let assigned_indices: Vec<usize> = (0..count)
            .filter(|&entity_index| {
                let entity = descriptor
                    .get_entity(solution, binding.descriptor_index, entity_index)
                    .expect("entity lookup failed for descriptor ruin_recreate selector");
                (binding.getter)(entity).is_some()
            })
            .collect();
        let total = assigned_indices.len();
        let min = self.min_ruin_count.min(total);
        let max = self.max_ruin_count.min(total);
        let moves_per_step = self.moves_per_step;
        let recreate_heuristic_type = self.recreate_heuristic_type;
        let value_candidate_limit = self.value_candidate_limit;
        let mut rng = self.rng.borrow_mut();
        let subsets: Vec<SmallVec<[usize; 8]>> = (0..moves_per_step)
            .map(|_| {
                if total == 0 || min == 0 {
                    return SmallVec::new();
                }
                let ruin_count = if min == max {
                    min
                } else {
                    rng.random_range(min..=max)
                };
                let mut indices = assigned_indices.clone();
                for swap_index in 0..ruin_count {
                    let other = rng.random_range(swap_index..total);
                    indices.swap(swap_index, other);
                }
                indices.truncate(ruin_count);
                SmallVec::from_vec(indices)
            })
            .collect();

        let moves: Vec<_> = subsets
            .into_iter()
            .filter_map({
                let binding = binding.clone();
                let descriptor = descriptor.clone();
                move |indices| {
                    if indices.is_empty() {
                        return None;
                    }
                    let mov = DescriptorRuinRecreateMove::new(
                        binding.clone(),
                        &indices,
                        descriptor.clone(),
                        recreate_heuristic_type,
                        value_candidate_limit,
                    );
                    mov.is_doable(score_director)
                        .then_some(DescriptorScalarMoveUnion::RuinRecreate(mov))
                }
            })
            .collect();
        ArenaMoveCursor::from_moves(moves)
    }

    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
        let count = score_director
            .entity_count(self.binding.descriptor_index)
            .unwrap_or(0);
        if count == 0 {
            0
        } else {
            self.moves_per_step
        }
    }
}