use solverforge_config::{SolverConfig, VariableTargetConfig};
use solverforge_core::domain::{PlanningSolution, SolutionDescriptor};
use solverforge_core::score::ParseableScore;
use crate::builder::{RuntimeModel, RuntimeScalarSlot, ScalarGroupBinding, VariableSlot};
use crate::phase::construction::ScalarConstructionSchedule;
use super::default_local_search::{
compile_default_local_search_components, compile_default_local_search_plan,
DefaultLocalSearchComponents, DefaultLocalSearchPlan,
};
use super::slots::{matching_list_slots, resolved_scalar_bindings};
use super::types::{CompiledListSlot, CompiledScalarSlot, RuntimeCompileError};
mod stages;
pub(crate) use stages::{
resolve_default_postconstruction_kopt, resolve_default_preconstruction_stage,
};
pub(crate) use stages::{
DefaultConstructionStage, DefaultConstructionStepKind, DefaultListPolicyProvenance,
DefaultPreconstructionStage, ResolvedDefaultConstructionPlan,
};
#[derive(Clone, Debug)]
pub(crate) struct DefaultScalarBinding<S> {
pub slot: CompiledScalarSlot<S>,
pub assignment_owned: bool,
pub construction_slot_index: usize,
pub schedule: ScalarConstructionSchedule,
}
#[derive(Clone, Debug)]
pub(crate) struct DefaultAssignmentBinding<S> {
pub group_index: usize,
pub group: ScalarGroupBinding<S>,
}
#[derive(Clone, Debug)]
pub(crate) struct DefaultRuntimeBindings<S, V, DM, IDM> {
pub list_slots: Vec<CompiledListSlot<S, V, DM, IDM>>,
pub scalar_slots: Vec<DefaultScalarBinding<S>>,
pub group_scalar_bindings: Vec<crate::descriptor::ResolvedVariableBinding<S>>,
pub assignment_groups: Vec<DefaultAssignmentBinding<S>>,
pub local_search_components: DefaultLocalSearchComponents,
pub local_search_plan: Option<DefaultLocalSearchPlan>,
pub local_search_nodes: Vec<super::graph::CompiledSelectorNode<S, V, DM, IDM>>,
pub local_search_policy: DefaultLocalSearchPolicy,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum DefaultLocalSearchPolicy {
RequireEffectiveSolverTermination,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum DefaultLocalSearchEligibility {
Eligible,
IneligibleWithoutEffectiveTermination,
}
impl DefaultLocalSearchPolicy {
pub(crate) fn eligibility<S>(self, config: &SolverConfig) -> DefaultLocalSearchEligibility
where
S: PlanningSolution,
S::Score: ParseableScore,
{
match self {
Self::RequireEffectiveSolverTermination => {
if crate::run::parse_configured_termination::<S>(config.termination.as_ref())
.has_effective_limit()
{
DefaultLocalSearchEligibility::Eligible
} else {
DefaultLocalSearchEligibility::IneligibleWithoutEffectiveTermination
}
}
}
}
}
pub(super) fn compile_default_runtime_bindings<S, V, DM, IDM>(
descriptor: &SolutionDescriptor,
model: &RuntimeModel<S, V, DM, IDM>,
compile_omitted_local_search: bool,
random_seed: Option<u64>,
) -> Result<DefaultRuntimeBindings<S, V, DM, IDM>, RuntimeCompileError>
where
S: PlanningSolution + 'static,
V: Clone,
DM: Clone,
IDM: Clone,
{
let target = VariableTargetConfig::default();
let list_slots = matching_list_slots(model, descriptor, &target, "default_runtime")?;
let scalar_slots: Vec<DefaultScalarBinding<S>> = model
.variables()
.iter()
.enumerate()
.filter_map(|(construction_slot_index, variable)| match variable {
VariableSlot::Scalar(slot) => Some(DefaultScalarBinding {
slot: RuntimeScalarSlot::Static(*slot),
assignment_owned: model.assignment_group_covers_scalar_variable(slot),
construction_slot_index,
schedule: ScalarConstructionSchedule::DescriptorPlacement,
}),
VariableSlot::DynamicScalar(slot) => Some(DefaultScalarBinding {
slot: RuntimeScalarSlot::Dynamic(slot.clone()),
assignment_owned: model.assignment_group_covers_dynamic_scalar_variable(slot),
construction_slot_index,
schedule: ScalarConstructionSchedule::DescriptorPlacement,
}),
VariableSlot::List(_) | VariableSlot::DynamicList(_) => None,
})
.collect();
let group_scalar_bindings = resolved_scalar_bindings(descriptor, model);
let assignment_groups = model
.assignment_scalar_groups()
.map(|(group_index, group)| DefaultAssignmentBinding {
group_index,
group: group.clone(),
})
.collect();
let local_search_components =
compile_default_local_search_components(model, &list_slots, &scalar_slots, random_seed);
let (local_search_plan, local_search_nodes) = if compile_omitted_local_search {
match compile_default_local_search_plan(
descriptor,
model,
&list_slots,
&scalar_slots,
local_search_components,
)? {
Some((plan, nodes)) => (Some(plan), nodes),
None => (None, Vec::new()),
}
} else {
(None, Vec::new())
};
Ok(DefaultRuntimeBindings {
list_slots,
scalar_slots,
group_scalar_bindings,
assignment_groups,
local_search_components,
local_search_plan,
local_search_nodes,
local_search_policy: DefaultLocalSearchPolicy::RequireEffectiveSolverTermination,
})
}