solverforge-solver 0.18.0

Solver engine for SolverForge
Documentation
use solverforge_config::{LocalSearchConfig, LocalSearchType, SelectionOrder};
use solverforge_core::domain::{PlanningSolution, SolutionDescriptor};

use crate::builder::RuntimeModel;

use super::default_local_search::DefaultLocalSearchPlan;
use super::graph::{CompiledAcceptorForagerSelector, CompiledLocalSearch};
use super::selector_tree::compile_selector;
use super::types::{RuntimeCompileError, RuntimeCompileErrorKind};

pub(super) fn compile_local_search<S, V, DM, IDM>(
    config: &LocalSearchConfig,
    path: &str,
    descriptor: &SolutionDescriptor,
    model: &RuntimeModel<S, V, DM, IDM>,
    default_local_search: Option<&DefaultLocalSearchPlan>,
) -> Result<CompiledLocalSearch<S, V, DM, IDM>, RuntimeCompileError>
where
    S: PlanningSolution + 'static,
    V: Clone,
    DM: Clone,
    IDM: Clone,
{
    match config.local_search_type {
        LocalSearchType::AcceptorForager => {
            if !config.neighborhoods.is_empty() {
                return Err(local_search_shape(
                    path,
                    "acceptor_forager local_search uses move_selector; neighborhoods are only valid with local_search_type = \"variable_neighborhood_descent\"",
                ));
            }
            let selector = match config.move_selector.as_ref() {
                Some(selector) => CompiledAcceptorForagerSelector::Explicit(compile_selector(
                    selector,
                    SelectionOrder::Random,
                    &format!("{path}.move_selector"),
                    descriptor,
                    model,
                )?),
                None => {
                    default_local_search.ok_or_else(|| {
                        local_search_shape(
                            path,
                            "acceptor_forager omitted move_selector but no immutable default selector was compiled",
                        )
                    })?;
                    CompiledAcceptorForagerSelector::OmittedDefault
                }
            };
            Ok(CompiledLocalSearch::AcceptorForager {
                config: config.clone(),
                selector,
            })
        }
        LocalSearchType::VariableNeighborhoodDescent => {
            if config.acceptor.is_some()
                || config.forager.is_some()
                || config.move_selector.is_some()
            {
                return Err(local_search_shape(
                    path,
                    "variable_neighborhood_descent local_search uses neighborhoods; acceptor, forager, and move_selector are only valid with local_search_type = \"acceptor_forager\"",
                ));
            }
            if config.neighborhoods.is_empty() {
                return Err(local_search_shape(
                    path,
                    "variable_neighborhood_descent local_search requires at least one neighborhood selector",
                ));
            }
            let neighborhoods = config
                .neighborhoods
                .iter()
                .enumerate()
                .map(|(index, selector)| {
                    compile_selector(
                        selector,
                        SelectionOrder::Original,
                        &format!("{path}.neighborhoods[{index}]"),
                        descriptor,
                        model,
                    )
                })
                .collect::<Result<Vec<_>, _>>()?;
            Ok(CompiledLocalSearch::VariableNeighborhoodDescent {
                config: config.clone(),
                neighborhoods,
            })
        }
    }
}
pub(super) fn local_search_shape(path: &str, message: impl Into<String>) -> RuntimeCompileError {
    RuntimeCompileError {
        path: path.to_string(),
        kind: RuntimeCompileErrorKind::LocalSearchShape {
            message: message.into(),
        },
    }
}