use std::fmt;
use solverforge_core::domain::PlanningSolution;
use crate::builder::RuntimeExtensionRegistry;
use crate::heuristic::selector::nearby_list_change::CrossEntityDistanceMeter;
use super::super::graph::{
CompiledConstruction, CompiledRuntimeExtension, CompiledRuntimeGraph, CompiledRuntimePhase,
ListConstructionKind,
};
use super::super::types::{CompiledListSlot, RuntimeSlotId};
use crate::builder::context::ListConstructionKernelError;
mod types;
use types::prepare_construction;
pub(crate) use types::{
PreparedConstruction, PreparedDefaultRuntime, PreparedListSlot, PreparedListSourceCatalog,
PreparedRuntimeExecution, PreparedRuntimePhase,
};
pub(crate) struct CompiledRuntimeExecutor<S, V, DM, IDM, E>
where
S: PlanningSolution,
{
graph: CompiledRuntimeGraph<S, V, DM, IDM, E>,
}
impl<S, V, DM, IDM, E> CompiledRuntimeExecutor<S, V, DM, IDM, E>
where
S: PlanningSolution,
{
pub(crate) fn new(graph: CompiledRuntimeGraph<S, V, DM, IDM, E>) -> Self {
Self { graph }
}
pub(crate) fn graph(&self) -> &CompiledRuntimeGraph<S, V, DM, IDM, E> {
&self.graph
}
}
impl<S, V, DM, IDM, E> CompiledRuntimeExecutor<S, V, DM, IDM, E>
where
S: PlanningSolution + Clone + Send + Sync + 'static,
V: Clone + PartialEq + Send + Sync + fmt::Debug + 'static,
DM: Clone + Send + Sync + fmt::Debug + CrossEntityDistanceMeter<S>,
IDM: Clone + Send + Sync + fmt::Debug + CrossEntityDistanceMeter<S>,
E: RuntimeExtensionRegistry<S, V, DM, IDM>,
{
pub(crate) fn instantiate(
&self,
) -> Result<PreparedRuntimeExecution<S, V, DM, IDM, E::Phase>, RuntimeInstantiationError> {
let mut sources = PreparedListSourceRegistry::default();
for (phase_index, phase) in self.graph.phases().iter().enumerate() {
self.register_phase_sources(phase_index, phase, &mut sources)?;
}
let mut phases = Vec::with_capacity(self.graph.phases().len());
for (phase_index, phase) in self.graph.phases().iter().enumerate() {
phases.push(self.instantiate_phase(phase_index, phase, &sources)?);
}
let list_source_catalog = sources.into_catalog();
let list_source_indices = (0..list_source_catalog.binding_slots())
.map(|_| None)
.collect();
Ok(PreparedRuntimeExecution {
phases,
list_source_catalog,
list_source_indices,
})
}
fn instantiate_phase(
&self,
phase_index: usize,
phase: &CompiledRuntimePhase<S, V, DM, IDM>,
sources: &PreparedListSourceRegistry<S, V, DM, IDM>,
) -> Result<PreparedRuntimePhase<S, V, DM, IDM, E::Phase>, RuntimeInstantiationError> {
match phase {
CompiledRuntimePhase::Construction(construction) => {
Ok(PreparedRuntimePhase::Construction(
self.instantiate_construction(phase_index, construction, sources)?,
))
}
CompiledRuntimePhase::LocalSearch(local_search) => {
Ok(PreparedRuntimePhase::LocalSearch(local_search.clone()))
}
CompiledRuntimePhase::Extension(extension) => Ok(PreparedRuntimePhase::Extension(
self.instantiate_extension(phase_index, extension)?,
)),
CompiledRuntimePhase::DefaultRuntime => Ok(PreparedRuntimePhase::DefaultRuntime(
PreparedDefaultRuntime {
bindings: self.graph.default_bindings().clone(),
phase_index,
},
)),
}
}
fn register_phase_sources(
&self,
phase_index: usize,
phase: &CompiledRuntimePhase<S, V, DM, IDM>,
sources: &mut PreparedListSourceRegistry<S, V, DM, IDM>,
) -> Result<(), RuntimeInstantiationError> {
match phase {
CompiledRuntimePhase::Construction(construction) => {
self.register_construction_sources(phase_index, construction, sources)
}
CompiledRuntimePhase::DefaultRuntime => self.register_list_slots(
phase_index,
&self.graph.default_bindings().list_slots,
sources,
),
CompiledRuntimePhase::LocalSearch(_) | CompiledRuntimePhase::Extension(_) => Ok(()),
}
}
fn register_construction_sources(
&self,
phase_index: usize,
construction: &CompiledConstruction<S, V, DM, IDM>,
sources: &mut PreparedListSourceRegistry<S, V, DM, IDM>,
) -> Result<(), RuntimeInstantiationError> {
match construction {
CompiledConstruction::ScalarOrMixed { list_slots, .. } => {
self.register_list_slots(phase_index, list_slots, sources)
}
CompiledConstruction::List { kind, slots, .. }
if !matches!(kind, ListConstructionKind::KOpt) =>
{
self.register_list_slots(phase_index, slots, sources)
}
CompiledConstruction::List { .. } | CompiledConstruction::GroupedScalar { .. } => {
Ok(())
}
}
}
fn register_list_slots(
&self,
_phase_index: usize,
slots: &[CompiledListSlot<S, V, DM, IDM>],
sources: &mut PreparedListSourceRegistry<S, V, DM, IDM>,
) -> Result<(), RuntimeInstantiationError> {
slots.iter().for_each(|slot| {
sources.register(slot);
});
Ok(())
}
fn instantiate_construction(
&self,
phase_index: usize,
construction: &CompiledConstruction<S, V, DM, IDM>,
sources: &PreparedListSourceRegistry<S, V, DM, IDM>,
) -> Result<PreparedConstruction<S, V, DM, IDM>, RuntimeInstantiationError> {
prepare_construction(construction, |slots| {
self.prepared_list_slots(phase_index, slots, sources)
})
}
fn prepared_list_slots(
&self,
phase_index: usize,
slots: &[CompiledListSlot<S, V, DM, IDM>],
sources: &PreparedListSourceRegistry<S, V, DM, IDM>,
) -> Result<Vec<PreparedListSlot>, RuntimeInstantiationError> {
slots
.iter()
.map(|slot| {
sources
.index_for(phase_index, slot)
.map(|catalog_index| PreparedListSlot::new(phase_index, catalog_index))
})
.collect()
}
fn instantiate_extension(
&self,
phase_index: usize,
extension: &CompiledRuntimeExtension,
) -> Result<E::Phase, RuntimeInstantiationError> {
match extension {
CompiledRuntimeExtension::Custom { name } => self
.graph
.extensions()
.instantiate_custom(name, self.graph.context())
.ok_or_else(|| RuntimeInstantiationError {
phase_index,
kind: RuntimeInstantiationErrorKind::MissingInstantiatedCustomExtension {
name: name.clone(),
},
}),
CompiledRuntimeExtension::Partitioned { name, config } => self
.graph
.extensions()
.instantiate_partitioned(name, config, self.graph.context())
.ok_or_else(|| RuntimeInstantiationError {
phase_index,
kind: RuntimeInstantiationErrorKind::MissingInstantiatedPartitioner {
name: name.clone(),
},
}),
}
}
}
struct PreparedListSourceRegistry<S, V, DM, IDM> {
catalog: PreparedListSourceCatalog<S, V, DM, IDM>,
}
impl<S, V, DM, IDM> Default for PreparedListSourceRegistry<S, V, DM, IDM> {
fn default() -> Self {
Self {
catalog: PreparedListSourceCatalog::default(),
}
}
}
impl<S, V, DM, IDM> PreparedListSourceRegistry<S, V, DM, IDM>
where
S: PlanningSolution + Clone + Send + Sync + 'static,
V: Clone + PartialEq + Send + Sync + fmt::Debug + 'static,
DM: Clone + Send + Sync + fmt::Debug + CrossEntityDistanceMeter<S>,
IDM: Clone + Send + Sync + fmt::Debug + CrossEntityDistanceMeter<S>,
{
fn register(&mut self, slot: &CompiledListSlot<S, V, DM, IDM>) -> usize {
self.catalog.register(slot)
}
fn index_for(
&self,
phase_index: usize,
slot: &CompiledListSlot<S, V, DM, IDM>,
) -> Result<usize, RuntimeInstantiationError> {
self.catalog
.index_for(slot)
.ok_or(RuntimeInstantiationError {
phase_index,
kind: RuntimeInstantiationErrorKind::MissingRegisteredSource {
target: slot.identity(),
},
})
}
fn into_catalog(self) -> PreparedListSourceCatalog<S, V, DM, IDM> {
self.catalog
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct RuntimeInstantiationError {
pub(crate) phase_index: usize,
pub(crate) kind: RuntimeInstantiationErrorKind,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum RuntimeInstantiationErrorKind {
SourceBinding {
target: RuntimeSlotId,
error: ListConstructionKernelError,
},
SourceRefresh {
target: RuntimeSlotId,
error: ListConstructionKernelError,
},
MissingRegisteredSource {
target: RuntimeSlotId,
},
MissingInstantiatedCustomExtension {
name: String,
},
MissingInstantiatedPartitioner {
name: String,
},
}
impl fmt::Display for RuntimeInstantiationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"runtime graph instantiation at phase {}: ",
self.phase_index
)?;
match &self.kind {
RuntimeInstantiationErrorKind::SourceBinding { target, error } => {
write!(f, "{target} source binding failed: {error:?}")
}
RuntimeInstantiationErrorKind::SourceRefresh { target, error } => {
write!(f, "{target} source assignment refresh failed: {error:?}")
}
RuntimeInstantiationErrorKind::MissingRegisteredSource { target } => {
write!(f, "{target} had no registered source declaration")
}
RuntimeInstantiationErrorKind::MissingInstantiatedCustomExtension { name } => {
write!(
f,
"compiled custom extension `{name}` was no longer instantiable"
)
}
RuntimeInstantiationErrorKind::MissingInstantiatedPartitioner { name } => {
write!(
f,
"compiled partitioned extension `{name}` was no longer instantiable"
)
}
}
}
}
impl std::error::Error for RuntimeInstantiationError {}
#[cfg(test)]
mod tests;