solverforge-solver 0.18.0

Solver engine for SolverForge
Documentation
use solverforge_core::domain::PlanningSolution;
use solverforge_scoring::Director;

use crate::heuristic::r#move::Move;
use crate::heuristic::selector::move_selector::MoveCandidateRef;
use crate::scope::{ProgressCallback, StepScope};
const VND_PROGRESS_POLL_INTERVAL: u64 = 0xFF;

pub(crate) struct VndProgress {
    moves_generated: u64,
    moves_evaluated: u64,
}

impl VndProgress {
    pub(crate) fn new() -> Self {
        Self {
            moves_generated: 0,
            moves_evaluated: 0,
        }
    }

    pub(crate) fn moves_evaluated(&self) -> u64 {
        self.moves_evaluated
    }

    pub(crate) fn record_generated(&mut self) {
        self.moves_generated += 1;
    }

    pub(crate) fn record_evaluated(&mut self) {
        self.moves_evaluated += 1;
    }

    pub(crate) fn maybe_report<S, D, ProgressCb>(
        &mut self,
        step_scope: &mut StepScope<'_, '_, '_, S, D, ProgressCb>,
    ) where
        S: PlanningSolution,
        D: Director<S>,
        ProgressCb: ProgressCallback<S>,
    {
        if self.moves_evaluated & VND_PROGRESS_POLL_INTERVAL != 0 {
            return;
        }
        step_scope.phase_scope_mut().report_progress_if_due();
    }
}

pub(crate) fn candidate_selector_label<S, M>(mov: &MoveCandidateRef<'_, S, M>) -> String
where
    S: PlanningSolution,
    M: Move<S>,
{
    let move_label = mov.telemetry_label();
    if mov.variable_name() == "compound_scalar" || mov.variable_name() == "conflict_repair" {
        return format!("{}:{move_label}", mov.variable_name());
    }
    let mut label = None;
    mov.for_each_affected_entity(&mut |affected| {
        if label.is_none() {
            label = Some(affected.variable_name.to_string());
        }
    });
    label
        .map(|variable| format!("{variable}:{move_label}"))
        .unwrap_or_else(|| format!("move:{move_label}"))
}