use solverforge_config::{
ConstructionHeuristicConfig, ConstructionHeuristicType, LocalSearchConfig, MoveSelectorConfig,
PartitionedSearchConfig, SelectionOrder, SolverConfig, UnionSelectionOrder, UnionWeighting,
};
use crate::builder::{
RuntimeCandidateMetricBinding, RuntimeProviderHandle, RuntimeScalarSlotId, ScalarGroupBinding,
SearchContext,
};
use crate::descriptor::ResolvedVariableBinding;
use crate::phase::construction::{ScalarConstructionSchedule, ScalarOrMixedSlotOrder};
use super::defaults::DefaultRuntimeBindings;
use super::types::{CompiledListSlot, CompiledScalarSlot};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ScalarLeafKind {
Change,
Swap,
NearbyChange,
NearbySwap,
PillarChange,
PillarSwap,
RuinRecreate,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ListLeafKind {
Change,
NearbyChange,
Swap,
Permute,
Precedence,
NearbySwap,
SublistChange,
SublistSwap,
Reverse,
KOpt,
Ruin,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ListConstructionKind {
RoundRobin,
CheapestInsertion,
RegretInsertion,
ClarkeWright,
KOpt,
}
impl ListConstructionKind {
pub(super) fn from_heuristic(heuristic: ConstructionHeuristicType) -> Option<Self> {
match heuristic {
ConstructionHeuristicType::ListRoundRobin => Some(Self::RoundRobin),
ConstructionHeuristicType::ListCheapestInsertion => Some(Self::CheapestInsertion),
ConstructionHeuristicType::ListRegretInsertion => Some(Self::RegretInsertion),
ConstructionHeuristicType::ListClarkeWright => Some(Self::ClarkeWright),
ConstructionHeuristicType::ListKOpt => Some(Self::KOpt),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct ProviderBindingPlan {
pub handle: RuntimeProviderHandle,
pub declared_schema_index: usize,
pub allowed_slots: Vec<RuntimeScalarSlotId>,
pub policy: ProviderBindingPolicy,
pub candidate_contract: ProviderCandidateContract,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ProviderBindingPolicy {
CallbackGroup {
rotation_seed_salt: u64,
pull_timing: ProviderPullTiming,
},
StaticGroup {
rotation_seed_salt: u64,
declared_max_moves_per_step: Option<usize>,
pull_timing: ProviderPullTiming,
},
CallbackRepair {
rotation_seed_salt: u64,
pull_timing: ProviderPullTiming,
},
StaticRepair {
constraint_rotation_seed_salt: u64,
provider_rotation_seed_salt: u64,
spec_rotation_seed_salt: u64,
pull_timing: ProviderPullTiming,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ProviderPullTiming {
FirstReachableNext,
OpenCursor,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum ProviderSchedule {
Group {
value_candidate_limit: Option<usize>,
requested_max_moves_per_step: Option<usize>,
},
Repair {
constraints: Vec<String>,
max_matches_per_step: usize,
max_repairs_per_match: usize,
max_moves_per_step: usize,
include_soft_matches: bool,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct CompiledProviderPlan {
pub move_kind: ProviderMoveKind,
pub schedule: ProviderSchedule,
pub bindings: Vec<ProviderBindingPlan>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(u8)]
pub(crate) enum ProviderMoveKind {
Grouped = 1,
ConflictRepair = 2,
CompoundConflictRepair = 3,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct ProviderCandidateContract {
pub reason_storage: ProviderReasonStorage,
pub deduplication: ProviderCandidateDeduplication,
pub tabu_identity: ProviderTabuIdentity,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ProviderReasonStorage {
PerRunInternedId,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ProviderCandidateDeduplication {
PerProviderReasonAndOrderedEdits,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ProviderTabuIdentity {
ProviderKindAndOrderedEdits,
}
pub(super) const GROUP_PROVIDER_ROTATION_SALT: u64 = 0xC0A1_E5CE_DA7A_0001;
pub(super) const REPAIR_PROVIDER_ROTATION_SALT: u64 = 0xC0AF_11C7_DA7A_0001;
pub(super) const STATIC_GROUP_PROVIDER_ROTATION_SALT: u64 = 0xC0A1_E5CE_AAA0_0001;
pub(super) const STATIC_REPAIR_CONSTRAINT_ROTATION_SALT: u64 = 0xC0AF_11C7_0000_0001;
pub(super) const STATIC_REPAIR_PROVIDER_ROTATION_SALT: u64 = 0xC0AF_11C7_0000_0002;
pub(super) const STATIC_REPAIR_SPEC_ROTATION_SALT: u64 = 0xC0AF_11C7_0000_0003;
pub(super) const PYTHON_PROVIDER_CANDIDATE_CONTRACT: ProviderCandidateContract =
ProviderCandidateContract {
reason_storage: ProviderReasonStorage::PerRunInternedId,
deduplication: ProviderCandidateDeduplication::PerProviderReasonAndOrderedEdits,
tabu_identity: ProviderTabuIdentity::ProviderKindAndOrderedEdits,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct CompiledSelectorNodeId(usize);
impl CompiledSelectorNodeId {
pub(super) fn new(index: usize) -> Self {
Self(index)
}
pub(super) fn index(self) -> usize {
self.0
}
}
#[derive(Clone, Debug)]
#[allow(
clippy::large_enum_variant,
reason = "leaf declarations carry their frozen slot vectors"
)]
pub(crate) enum CompiledSelectorNode<S, V, DM, IDM> {
Scalar {
kind: ScalarLeafKind,
config: MoveSelectorConfig,
candidate_order: SelectionOrder,
candidate_metric: Option<RuntimeCandidateMetricBinding<S>>,
slots: Vec<CompiledScalarSlot<S>>,
},
List {
kind: ListLeafKind,
config: MoveSelectorConfig,
candidate_order: SelectionOrder,
candidate_metric: Option<RuntimeCandidateMetricBinding<S>>,
slots: Vec<CompiledListSlot<S, V, DM, IDM>>,
},
GroupedScalar {
config: MoveSelectorConfig,
candidate_order: SelectionOrder,
candidate_metric: Option<RuntimeCandidateMetricBinding<S>>,
group: ScalarGroupBinding<S>,
},
Provider {
config: MoveSelectorConfig,
candidate_order: SelectionOrder,
candidate_metric: Option<RuntimeCandidateMetricBinding<S>>,
plan: CompiledProviderPlan,
},
Limited {
selected_count_limit: usize,
selector: CompiledSelectorNodeId,
},
Union {
selection_order: UnionSelectionOrder,
weighting: UnionWeighting,
weights: Vec<u64>,
children: Vec<CompiledSelectorNodeId>,
},
Cartesian {
require_hard_improvement: bool,
left: CompiledSelectorNodeId,
right: CompiledSelectorNodeId,
},
}
#[derive(Clone, Debug)]
pub(crate) struct CompiledSelectorGraph<S, V, DM, IDM> {
nodes: Vec<CompiledSelectorNode<S, V, DM, IDM>>,
root: CompiledSelectorNodeId,
}
impl<S, V, DM, IDM> CompiledSelectorGraph<S, V, DM, IDM> {
pub(super) fn from_nodes_and_root(
nodes: Vec<CompiledSelectorNode<S, V, DM, IDM>>,
root: CompiledSelectorNodeId,
) -> Self {
assert!(
root.index() == nodes.len() - 1,
"compiled selector graph root must be the last pushed node"
);
Self { nodes, root }
}
pub(super) fn into_parts(
self,
) -> (
Vec<Option<CompiledSelectorNode<S, V, DM, IDM>>>,
CompiledSelectorNodeId,
) {
(self.nodes.into_iter().map(Some).collect(), self.root)
}
pub(super) fn node(&self, id: CompiledSelectorNodeId) -> &CompiledSelectorNode<S, V, DM, IDM> {
&self.nodes[id.index()]
}
pub(super) fn requires_score_during_move_root(&self) -> bool {
matches!(
self.node(self.root),
CompiledSelectorNode::Scalar {
kind: ScalarLeafKind::RuinRecreate,
..
} | CompiledSelectorNode::List {
kind: ListLeafKind::Ruin,
..
}
)
}
pub(super) fn contains_provider_root(&self) -> bool {
matches!(self.node(self.root), CompiledSelectorNode::Provider { .. })
}
}
pub(super) struct SelectorGraphBuilder<S, V, DM, IDM> {
nodes: Vec<CompiledSelectorNode<S, V, DM, IDM>>,
}
impl<S, V, DM, IDM> Default for SelectorGraphBuilder<S, V, DM, IDM> {
fn default() -> Self {
Self { nodes: Vec::new() }
}
}
impl<S, V, DM, IDM> SelectorGraphBuilder<S, V, DM, IDM> {
pub(super) fn push(
&mut self,
node: CompiledSelectorNode<S, V, DM, IDM>,
) -> CompiledSelectorNodeId {
let id = CompiledSelectorNodeId::new(self.nodes.len());
self.nodes.push(node);
id
}
pub(super) fn assemble_graph(
self,
root: CompiledSelectorNodeId,
) -> CompiledSelectorGraph<S, V, DM, IDM> {
assert!(
root.index() == self.nodes.len() - 1,
"compiled selector graph root must be the last pushed node"
);
CompiledSelectorGraph::from_nodes_and_root(self.nodes, root)
}
}
pub(super) fn rebase_graph<S, V, DM, IDM>(
builder: &mut SelectorGraphBuilder<S, V, DM, IDM>,
graph: CompiledSelectorGraph<S, V, DM, IDM>,
) -> CompiledSelectorNodeId {
let offset = builder.nodes.len();
for node in graph.nodes {
builder.nodes.push(node);
}
CompiledSelectorNodeId::new(graph.root.index() + offset)
}
#[derive(Clone, Debug)]
#[allow(
clippy::large_enum_variant,
reason = "compiled construction stays value-owned through preparation"
)]
pub(crate) enum CompiledConstruction<S, V, DM, IDM> {
ScalarOrMixed {
config: ConstructionHeuristicConfig,
schedule: ScalarConstructionSchedule,
scalar_slots: Vec<CompiledScalarSlot<S>>,
list_slots: Vec<CompiledListSlot<S, V, DM, IDM>>,
slot_order: Vec<ScalarOrMixedSlotOrder>,
},
List {
kind: ListConstructionKind,
config: ConstructionHeuristicConfig,
slots: Vec<CompiledListSlot<S, V, DM, IDM>>,
},
GroupedScalar {
config: ConstructionHeuristicConfig,
group_index: usize,
group: ScalarGroupBinding<S>,
scalar_bindings: Vec<ResolvedVariableBinding<S>>,
},
}
#[derive(Clone, Debug)]
pub(crate) enum CompiledAcceptorForagerSelector<S, V, DM, IDM> {
Explicit(CompiledSelectorGraph<S, V, DM, IDM>),
OmittedDefault,
}
#[derive(Clone, Debug)]
pub(crate) enum CompiledLocalSearch<S, V, DM, IDM> {
AcceptorForager {
config: LocalSearchConfig,
selector: CompiledAcceptorForagerSelector<S, V, DM, IDM>,
},
VariableNeighborhoodDescent {
config: LocalSearchConfig,
neighborhoods: Vec<CompiledSelectorGraph<S, V, DM, IDM>>,
},
}
#[derive(Clone, Debug)]
#[allow(
clippy::large_enum_variant,
reason = "compiled phases remain one value-owned execution graph"
)]
pub(crate) enum CompiledRuntimePhase<S, V, DM, IDM> {
Construction(CompiledConstruction<S, V, DM, IDM>),
LocalSearch(CompiledLocalSearch<S, V, DM, IDM>),
Extension(CompiledRuntimeExtension),
DefaultRuntime,
}
#[derive(Clone, Debug)]
pub(crate) enum CompiledRuntimeExtension {
Custom {
name: String,
},
Partitioned {
name: String,
config: PartitionedSearchConfig,
},
}
pub(crate) struct CompiledRuntimeGraph<S, V, DM, IDM, E>
where
S: solverforge_core::domain::PlanningSolution,
{
pub(super) context: SearchContext<S, V, DM, IDM>,
pub(super) extensions: E,
pub(super) config: SolverConfig,
pub(super) default_bindings: DefaultRuntimeBindings<S, V, DM, IDM>,
pub(super) phases: Vec<CompiledRuntimePhase<S, V, DM, IDM>>,
}
impl<S, V, DM, IDM, E> std::fmt::Debug for CompiledRuntimeGraph<S, V, DM, IDM, E>
where
S: solverforge_core::domain::PlanningSolution,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CompiledRuntimeGraph")
.field("config", &self.config)
.finish_non_exhaustive()
}
}
impl<S, V, DM, IDM, E> CompiledRuntimeGraph<S, V, DM, IDM, E>
where
S: solverforge_core::domain::PlanningSolution,
{
pub(crate) fn context(&self) -> &SearchContext<S, V, DM, IDM> {
&self.context
}
pub(crate) fn config(&self) -> &SolverConfig {
&self.config
}
pub(crate) fn phases(&self) -> &[CompiledRuntimePhase<S, V, DM, IDM>] {
&self.phases
}
pub(crate) fn extensions(&self) -> &E {
&self.extensions
}
pub(crate) fn default_bindings(&self) -> &DefaultRuntimeBindings<S, V, DM, IDM> {
&self.default_bindings
}
}