solverforge-solver 0.12.0

Solver engine for SolverForge
Documentation
fn solve_scalar_first_fit<S, V, D, ProgressCb>(
    variable_index: usize,
    ctx: ScalarVariableSlot<S>,
    value_candidate_limit: Option<usize>,
    construction_obligation: ConstructionObligation,
    phase_scope: &mut PhaseScope<'_, '_, S, D, ProgressCb>,
) -> IterationProgress<S, V>
where
    S: PlanningSolution,
    S::Score: Score + Copy,
    D: Director<S>,
    ProgressCb: ProgressCallback<S>,
{
    let entity_count = (ctx.entity_count)(phase_scope.score_director().working_solution());

    for entity_index in 0..entity_count {
        let slot_id = ConstructionSlotId::new(variable_index, entity_index);
        if phase_scope.solver_scope().is_scalar_slot_completed(slot_id) {
            continue;
        }

        let current = ctx.current_value(
            phase_scope.score_director().working_solution(),
            entity_index,
        );
        if current.is_some() {
            continue;
        }

        let values = ctx.candidate_values_for_entity(
            phase_scope.score_director().working_solution(),
            entity_index,
            value_candidate_limit,
        );
        if values.is_empty() {
            if ctx.allows_unassigned {
                complete_scalar_slot(
                    slot_id,
                    ScalarSlotCompletion::NoDoableCandidate,
                    phase_scope,
                );
                return IterationProgress::CompletedOnly;
            }
            continue;
        }

        let mut first_doable = None;
        let baseline_score =
            keep_current_allowed(ctx.allows_unassigned, construction_obligation)
                .then(|| phase_scope.calculate_score());

        for (value_index, value) in values.into_iter().enumerate() {
            let mov = ChangeMove::new(
                entity_index,
                Some(value),
                ctx.getter,
                ctx.setter,
                ctx.variable_index,
                ctx.variable_name,
                ctx.descriptor_index,
            );
            if !mov.is_doable(phase_scope.score_director()) {
                continue;
            }

            let score = candidate_score(phase_scope, &mov);

            if let Some(baseline_score) = baseline_score {
                if is_first_fit_improvement(baseline_score, score) {
                    first_doable = Some((value_index, value, score));
                    break;
                }
            } else {
                first_doable = Some((value_index, value, score));
                break;
            }
        }

        let selection = select_first_fit(
            first_doable
                .as_ref()
                .map(|(value_index, _, _)| *value_index),
        );

        match selection {
            crate::phase::construction::ConstructionChoice::Select(selected_index) => {
                let Some((value_index, value, score)) =
                    first_doable.filter(|(value_index, _, _)| *value_index == selected_index)
                else {
                    unreachable!("selected scalar construction candidate should exist");
                };
                return IterationProgress::Committed(Candidate::Scalar {
                    getter: ctx.getter,
                    setter: ctx.setter,
                    variable_name: ctx.variable_name,
                    descriptor_index: ctx.descriptor_index,
                    variable_index: ctx.variable_index,
                    entity_index,
                    value,
                    order_key: [variable_index, entity_index, value_index, 0],
                    score,
                });
            }
            crate::phase::construction::ConstructionChoice::KeepCurrent
                if ctx.allows_unassigned =>
            {
                let completion =
                    if keep_current_allowed(ctx.allows_unassigned, construction_obligation) {
                        ScalarSlotCompletion::Kept
                    } else {
                        ScalarSlotCompletion::NoDoableCandidate
                    };
                complete_scalar_slot(slot_id, completion, phase_scope);
                return IterationProgress::CompletedOnly;
            }
            crate::phase::construction::ConstructionChoice::KeepCurrent => {}
        }
    }

    IterationProgress::None
}

fn solve_list_first_fit<S, V, DM, IDM, D, ProgressCb>(
    list_index: usize,
    ctx: ListVariableSlot<S, V, DM, IDM>,
    phase_scope: &mut PhaseScope<'_, '_, S, D, ProgressCb>,
) -> IterationProgress<S, V>
where
    S: PlanningSolution,
    S::Score: Score + Copy,
    V: Clone + Copy + PartialEq + Eq + Hash + Send + Sync + Debug + 'static,
    DM: Clone + Debug + 'static,
    IDM: Clone + Debug + 'static,
    D: Director<S>,
    ProgressCb: ProgressCallback<S>,
{
    let entity_count = (ctx.entity_count)(phase_scope.score_director().working_solution());
    if entity_count == 0 {
        return IterationProgress::None;
    }

    let assigned = (ctx.assigned_elements)(phase_scope.score_director().working_solution());
    let assigned_set: std::collections::HashSet<V> = assigned.into_iter().collect();
    let element_count = (ctx.element_count)(phase_scope.score_director().working_solution());

    for element_index in 0..element_count {
        let element_id = ConstructionListElementId::new(list_index, element_index);
        if phase_scope
            .solver_scope()
            .is_list_element_completed(element_id)
        {
            continue;
        }

        let element = (ctx.index_to_element)(
            phase_scope.score_director().working_solution(),
            element_index,
        );
        if assigned_set.contains(&element) {
            continue;
        }

        let entity_index = 0;
        let position = 0;
        let score = evaluate_list_insertion(phase_scope, &ctx, element, entity_index, position);
        return IterationProgress::Committed(Candidate::List {
            list_insert: ctx.list_insert,
            descriptor_index: ctx.descriptor_index,
            element,
            entity_index,
            position,
            order_key: [list_index, element_index, entity_index, position],
            score,
        });
    }

    IterationProgress::None
}