solverforge-solver 0.15.0

Solver engine for SolverForge
Documentation
use std::time::Instant;

use solverforge_core::domain::PlanningSolution;
use solverforge_core::score::Score;
use solverforge_scoring::Director;

use crate::heuristic::r#move::Move;
use crate::heuristic::selector::move_selector::MoveCandidateRef;
use crate::phase::hard_delta::{hard_score_delta, HardScoreDelta};
use crate::scope::{ProgressCallback, StepScope};

pub(crate) enum CandidateEvaluation<Sc> {
    Scored(Sc),
    NotDoable,
    RejectedByHardImprovement(Sc),
}

#[inline]
pub(crate) fn evaluate_candidate<S, D, ProgressCb, M>(
    mov: &MoveCandidateRef<'_, S, M>,
    step_scope: &mut StepScope<'_, '_, '_, S, D, ProgressCb>,
    reference_score: S::Score,
    selector_index: Option<usize>,
    evaluation_started: Instant,
) -> CandidateEvaluation<S::Score>
where
    S: PlanningSolution,
    D: Director<S>,
    ProgressCb: ProgressCallback<S>,
    M: Move<S>,
{
    if !mov.is_doable(step_scope.score_director()) {
        let move_label = mov.telemetry_label();
        record_evaluated_move(step_scope, selector_index, evaluation_started);
        step_scope
            .phase_scope_mut()
            .record_move_kind_evaluated_unscored(move_label);
        if let Some(selector_index) = selector_index {
            step_scope
                .phase_scope_mut()
                .record_selector_move_not_doable(selector_index);
        } else {
            step_scope.phase_scope_mut().record_move_not_doable();
        }
        step_scope
            .phase_scope_mut()
            .record_move_kind_not_doable(move_label);
        return CandidateEvaluation::NotDoable;
    }

    let score_state = step_scope.score_director().snapshot_score_state();
    let undo = mov.do_move(step_scope.score_director_mut());
    let move_score = step_scope.score_director_mut().calculate_score();
    mov.undo_move(step_scope.score_director_mut(), undo);
    step_scope
        .score_director_mut()
        .restore_score_state(score_state);

    step_scope.phase_scope_mut().record_score_calculation();

    let hard_delta = hard_score_delta(reference_score, move_score);
    match hard_delta {
        Some(HardScoreDelta::Improving) => {
            step_scope.phase_scope_mut().record_move_hard_improving();
        }
        Some(HardScoreDelta::Neutral) => {
            step_scope.phase_scope_mut().record_move_hard_neutral();
        }
        Some(HardScoreDelta::Worse) => {
            step_scope.phase_scope_mut().record_move_hard_worse();
        }
        None => {}
    }

    if mov.requires_hard_improvement() && hard_delta != Some(HardScoreDelta::Improving) {
        let move_label = mov.telemetry_label();
        record_evaluated_move(step_scope, selector_index, evaluation_started);
        step_scope
            .phase_scope_mut()
            .record_move_kind_evaluated(move_label, move_score.compare(&reference_score));
        if let Some(selector_index) = selector_index {
            step_scope
                .phase_scope_mut()
                .record_selector_move_acceptor_rejected(selector_index);
        } else {
            step_scope.phase_scope_mut().record_move_acceptor_rejected();
        }
        step_scope
            .phase_scope_mut()
            .record_move_kind_acceptor_rejected(move_label, move_score.compare(&reference_score));
        return CandidateEvaluation::RejectedByHardImprovement(move_score);
    }

    CandidateEvaluation::Scored(move_score)
}

#[inline]
pub(crate) fn record_evaluated_move<S, D, ProgressCb>(
    step_scope: &mut StepScope<'_, '_, '_, S, D, ProgressCb>,
    selector_index: Option<usize>,
    evaluation_started: Instant,
) where
    S: PlanningSolution,
    D: Director<S>,
    ProgressCb: ProgressCallback<S>,
{
    if let Some(selector_index) = selector_index {
        step_scope
            .phase_scope_mut()
            .record_selector_evaluated_move(selector_index, evaluation_started.elapsed());
    } else {
        step_scope
            .phase_scope_mut()
            .record_evaluated_move(evaluation_started.elapsed());
    }
}