# solverforge-solver WIREFRAME
Solver engine: phases, moves, selectors, acceptors, foragers, termination, and solver management.
**Location:** `crates/solverforge-solver/`
## Dependencies
- `solverforge-core` (path) — Score types, domain traits, descriptors
- `solverforge-scoring` (path) — Director trait, constraint scoring
- `solverforge-config` (path) — SolverConfig, PhaseConfig, AcceptorConfig
- `thiserror` (workspace) — Error derivation
- `tracing` (workspace) — Logging
- `rand` / `rand_chacha` (workspace) — RNG
- `rayon` (workspace) — Parallel computation
- `smallvec` (workspace) — Stack-allocated small vectors
- `serde` (workspace) — Serialization
- `tokio` (sync feature) — `mpsc` channels for solution streaming
## File Map
```
src/
├── lib.rs — Crate root; module declarations, re-exports
├── solver.rs — Solver struct, SolveResult, impl_solver! macro
├── basic.rs — BasicSpec struct
├── list_solver.rs — ListSpec struct
├── run.rs — AnyTermination, build_termination, unified run_solver()
├── problem_spec.rs — ProblemSpec trait
├── builder/
│ ├── mod.rs — Re-exports from all builder submodules
│ ├── acceptor.rs — AnyAcceptor<S> enum, AcceptorBuilder
│ ├── forager.rs — AnyForager<S> enum, ForagerBuilder
│ ├── context.rs — BasicContext<S>, ListContext<S, V, DM, IDM>
│ ├── basic_selector.rs — BasicLeafSelector<S> enum, BasicMoveSelectorBuilder
│ └── list_selector.rs — ListLeafSelector<S, V, DM, IDM> enum, ListMoveSelectorBuilder
├── stats.rs — SolverStats, PhaseStats
├── test_utils.rs — TestSolution, TestDirector, NQueens helpers
│
├── heuristic/
│ ├── mod.rs — Re-exports from move/ and selector/
│ │
│ ├── move/
│ │ ├── mod.rs — Module declarations, re-exports
│ │ ├── traits.rs — Move<S> trait definition
│ │ ├── arena.rs — MoveArena<M> allocator
│ │ ├── change.rs — ChangeMove<S, V>
│ │ ├── swap.rs — SwapMove<S, V>
│ │ ├── list_change.rs — ListChangeMove<S, V>
│ │ ├── list_swap.rs — ListSwapMove<S, V>
│ │ ├── list_reverse.rs — ListReverseMove<S, V>
│ │ ├── list_ruin.rs — ListRuinMove<S, V>
│ │ ├── sublist_change.rs — SubListChangeMove<S, V>
│ │ ├── sublist_swap.rs — SubListSwapMove<S, V>
│ │ ├── pillar_change.rs — PillarChangeMove<S, V>
│ │ ├── pillar_swap.rs — PillarSwapMove<S, V>
│ │ ├── ruin.rs — RuinMove<S, V>
│ │ ├── k_opt.rs — KOptMove<S, V>, CutPoint
│ │ ├── k_opt_reconnection.rs — KOptReconnection patterns
│ │ ├── composite.rs — CompositeMove<S, M1, M2>
│ │ ├── either.rs — EitherMove<S, V> enum
│ │ ├── list_either.rs — ListMoveImpl<S, V> enum
│ │ ├── list_change_tests.rs — Tests
│ │ ├── k_opt_tests.rs — Tests
│ │ ├── sublist_change_tests.rs — Tests
│ │ ├── sublist_swap_tests.rs — Tests
│ │ └── tests/ — Additional test modules
│ │ ├── mod.rs
│ │ ├── arena.rs
│ │ ├── change.rs
│ │ ├── swap.rs
│ │ ├── list_change.rs
│ │ ├── list_swap.rs
│ │ ├── list_reverse.rs
│ │ ├── list_ruin.rs
│ │ ├── pillar_change.rs
│ │ ├── pillar_swap.rs
│ │ ├── ruin.rs
│ │ ├── sublist_change.rs
│ │ ├── sublist_swap.rs
│ │ └── k_opt.rs
│ │
│ └── selector/
│ ├── mod.rs — Re-exports
│ ├── entity.rs — EntitySelector trait, FromSolutionEntitySelector, AllEntitiesSelector
│ ├── typed_value.rs — TypedValueSelector trait, StaticTypedValueSelector, FromSolutionTypedValueSelector
│ ├── typed_move_selector.rs — MoveSelector trait, ChangeMoveSelector, SwapMoveSelector, EitherChange/SwapMoveSelector, ListMove* wrappers
│ ├── typed_move_selector_tests.rs — Tests
│ ├── list_change.rs — ListChangeMoveSelector<S, V, ES>
│ ├── list_swap.rs — ListSwapMoveSelector<S, V, ES>, ListMoveListSwapSelector
│ ├── list_reverse.rs — ListReverseMoveSelector<S, V, ES>, ListMoveListReverseSelector
│ ├── list_ruin.rs — ListRuinMoveSelector<S, V>
│ ├── sublist_change.rs — SubListChangeMoveSelector<S, V, ES>, ListMoveSubListChangeSelector
│ ├── sublist_swap.rs — SubListSwapMoveSelector<S, V, ES>, ListMoveSubListSwapSelector
│ ├── pillar.rs — PillarSelector trait, DefaultPillarSelector, Pillar, SubPillarConfig
│ ├── pillar_tests.rs — Tests
│ ├── ruin.rs — RuinMoveSelector<S, V>
│ ├── mimic.rs — MimicRecorder, MimicRecordingEntitySelector, MimicReplayingEntitySelector
│ ├── selection_order.rs — SelectionOrder enum
│ ├── nearby.rs — NearbyDistanceMeter trait, DynDistanceMeter, NearbyEntitySelector, NearbySelectionConfig
│ ├── nearby_list_change.rs — CrossEntityDistanceMeter trait, NearbyListChangeMoveSelector, ListMoveNearbyListChangeSelector
│ ├── nearby_list_swap.rs — NearbyListSwapMoveSelector, ListMoveNearbyListSwapSelector
│ ├── decorator/
│ │ ├── mod.rs — Re-exports
│ │ ├── cartesian_product.rs — CartesianProductArena<S, M1, M2>
│ │ ├── count_limit.rs — SelectedCountLimitMoveSelector<S, M, Inner>
│ │ ├── filtering.rs — FilteringMoveSelector<S, M, Inner>
│ │ ├── probability.rs — ProbabilityMoveSelector<S, M, Inner>
│ │ ├── shuffling.rs — ShufflingMoveSelector<S, M, Inner>
│ │ ├── sorting.rs — SortingMoveSelector<S, M, Inner>
│ │ ├── union.rs — UnionMoveSelector<S, M, A, B>
│ │ ├── vec_union.rs — VecUnionSelector<S, M, Leaf> (Vec-backed union for config-driven composition)
│ │ └── test_utils.rs — Test helpers
│ ├── k_opt/
│ │ ├── mod.rs — Re-exports
│ │ ├── config.rs — KOptConfig
│ │ ├── cuts.rs — CutCombinationIterator (pub(crate))
│ │ ├── iterators.rs — CutCombinationIterator (pub), binomial(), count_cut_combinations()
│ │ ├── distance_meter.rs — ListPositionDistanceMeter trait, DefaultDistanceMeter
│ │ ├── distance.rs — (duplicate of distance_meter.rs)
│ │ ├── nearby.rs — NearbyKOptMoveSelector<S, V, D, ES>
│ │ ├── selector.rs — KOptMoveSelector<S, V, ES>
│ │ └── tests.rs — Tests
│ └── tests/
│ ├── mod.rs
│ ├── k_opt.rs
│ ├── list_change.rs
│ ├── list_ruin.rs
│ ├── mimic.rs
│ ├── nearby.rs
│ ├── pillar.rs
│ └── typed_move_selector.rs
│
├── phase/
│ ├── mod.rs — Phase<S, D> trait, tuple impls
│ ├── construction/
│ │ ├── mod.rs — ForagerType enum, ConstructionHeuristicConfig, re-exports
│ │ ├── phase.rs — ConstructionHeuristicPhase<S, M, P, Fo>
│ │ ├── forager.rs — ConstructionForager trait, FirstFit/BestFit/FirstFeasible/WeakestFit/StrongestFit foragers
│ │ ├── placer.rs — EntityPlacer trait, Placement, QueuedEntityPlacer, SortedEntityPlacer
│ │ ├── forager_tests.rs — Tests
│ │ └── placer_tests.rs — Tests
│ ├── localsearch/
│ │ ├── mod.rs — LocalSearchConfig, AcceptorType, re-exports
│ │ ├── phase.rs — LocalSearchPhase<S, M, MS, A, Fo>
│ │ ├── forager.rs — LocalSearchForager trait, AcceptedCountForager, FirstAcceptedForager, BestScoreForager, FirstBestScoreImprovingForager, FirstLastStepScoreImprovingForager
│ │ └── acceptor/
│ │ ├── mod.rs — Acceptor<S> trait, re-exports
│ │ ├── hill_climbing.rs — HillClimbingAcceptor
│ │ ├── late_acceptance.rs — LateAcceptanceAcceptor<S>
│ │ ├── simulated_annealing.rs — SimulatedAnnealingAcceptor
│ │ ├── tabu_search.rs — TabuSearchAcceptor<S>
│ │ ├── entity_tabu.rs — EntityTabuAcceptor
│ │ ├── value_tabu.rs — ValueTabuAcceptor
│ │ ├── move_tabu.rs — MoveTabuAcceptor
│ │ ├── great_deluge.rs — GreatDelugeAcceptor<S>
│ │ ├── step_counting.rs — StepCountingHillClimbingAcceptor<S>
│ │ ├── diversified_late_acceptance.rs — DiversifiedLateAcceptanceAcceptor<S>
│ │ └── tests.rs — Tests
│ ├── exhaustive/
│ │ ├── mod.rs — ExhaustiveSearchPhase, ExhaustiveSearchConfig, ExplorationType
│ │ ├── bounder.rs — ScoreBounder trait, SoftScoreBounder, FixedOffsetBounder
│ │ ├── decider.rs — ExhaustiveSearchDecider trait, SimpleDecider
│ │ └── node.rs — ExhaustiveSearchNode, MoveSequence
│ ├── partitioned/
│ │ ├── mod.rs — PartitionedSearchPhase, PartitionedSearchConfig, ChildPhases trait
│ │ └── partitioner.rs — SolutionPartitioner trait, FunctionalPartitioner, ThreadCount
│ └── vnd/
│ ├── mod.rs — Re-exports
│ └── phase.rs — VndPhase, impl_vnd_phase! macro (up to 8 neighborhoods)
│
├── manager/
│ ├── mod.rs — PhaseFactory trait, re-exports
│ ├── config.rs — LocalSearchType, ConstructionType, PhaseConfig enums
│ ├── builder.rs — SolverFactoryBuilder, SolverBuildError
│ ├── solver_factory.rs — SolverFactory, solver_factory_builder() free fn
│ ├── solver_manager.rs — SolverManager, Solvable trait, SolverStatus
│ ├── solution_manager.rs — analyze() free fn, Analyzable trait, ScoreAnalysis, ConstraintAnalysis
│ ├── phase_factory/
│ │ ├── mod.rs — Re-exports
│ │ ├── construction.rs — ConstructionPhaseFactory
│ │ ├── list_construction.rs — ListConstructionPhaseBuilder, ListConstructionPhase, ListCheapestInsertionPhase, ListRegretInsertionPhase
│ │ ├── local_search.rs — LocalSearchPhaseFactory
│ │ └── k_opt.rs — KOptPhaseBuilder, KOptPhase
│ ├── builder_tests.rs — Tests
│ ├── mod_tests.rs — Tests
│ ├── mod_tests_integration.rs — Integration tests
│ ├── phase_factory_tests.rs — Tests
│ └── phase_factory_tests_localsearch.rs — Tests
│
├── scope/
│ ├── mod.rs — Re-exports
│ ├── solver.rs — SolverScope<'t, S, D, BestCb = ()>, BestSolutionCallback trait
│ ├── phase.rs — PhaseScope<'t, 'a, S, D, BestCb = ()>
│ ├── step.rs — StepScope<'t, 'a, 'b, S, D, BestCb = ()>
│ └── tests.rs — Tests
│
├── termination/
│ ├── mod.rs — Termination<S, D, BestCb = ()> trait, re-exports
│ ├── time.rs — TimeTermination
│ ├── step_count.rs — StepCountTermination
│ ├── best_score.rs — BestScoreTermination<Sc>, BestScoreFeasibleTermination<S, F>
│ ├── unimproved.rs — UnimprovedStepCountTermination<S>, UnimprovedTimeTermination<S>
│ ├── composite.rs — OrTermination<T, S, D>, AndTermination<T, S, D> (tuple impls up to 8)
│ ├── move_count.rs — MoveCountTermination<S>
│ ├── score_calculation_count.rs — ScoreCalculationCountTermination<S>
│ ├── diminished_returns.rs — DiminishedReturnsTermination<S>
│ └── tests.rs — Tests
│
└── realtime/
├── mod.rs — Re-exports
├── problem_change.rs — ProblemChange trait, BoxedProblemChange, ClosureProblemChange
└── solver_handle.rs — SolverHandle<S>, ProblemChangeReceiver<S>, ProblemChangeResult
```
## Core Traits
### `Move<S: PlanningSolution>` — `traits.rs`
Requires: `Send + Sync + Debug`.
| `is_doable` | `fn<D: Director<S>>(&self, score_director: &D) -> bool` |
| `do_move` | `fn<D: Director<S>>(&self, score_director: &mut D)` |
| `descriptor_index` | `fn(&self) -> usize` |
| `entity_indices` | `fn(&self) -> &[usize]` |
| `variable_name` | `fn(&self) -> &str` |
Moves are **never cloned**. Ownership transfers via `MoveArena` indices.
### `Phase<S: PlanningSolution, D: Director<S>, BestCb: BestSolutionCallback<S> = ()>` — `phase/mod.rs`
Requires: `Send + Debug`.
| `solve` | `fn(&mut self, solver_scope: &mut SolverScope<'_, S, D, BestCb>)` |
| `phase_type_name` | `fn(&self) -> &'static str` |
All concrete phase types implement `Phase<S, D, BestCb>` for all `BestCb: BestSolutionCallback<S>`. Tuple implementations via `tuple_impl.rs`.
### `Termination<S: PlanningSolution, D: Director<S>, BestCb: BestSolutionCallback<S> = ()>` — `termination/mod.rs`
Requires: `Send + Debug`.
| `is_terminated` | `fn(&self, solver_scope: &SolverScope<S, D, BestCb>) -> bool` | — |
| `install_inphase_limits` | `fn(&self, solver_scope: &mut SolverScope<S, D, BestCb>)` | no-op |
All concrete termination types implement `Termination<S, D, BestCb>` for all `BestCb: BestSolutionCallback<S>`.
### `Acceptor<S: PlanningSolution>` — `acceptor/mod.rs`
Requires: `Send + Debug`.
| `is_accepted` | `fn(&mut self, last_step_score: &S::Score, move_score: &S::Score) -> bool` | — |
| `phase_started` | `fn(&mut self, initial_score: &S::Score)` | no-op |
| `phase_ended` | `fn(&mut self)` | no-op |
| `step_started` | `fn(&mut self)` | no-op |
| `step_ended` | `fn(&mut self, step_score: &S::Score)` | no-op |
### `EntitySelector<S: PlanningSolution>` — `entity.rs`
| `iter` | `fn<'a, D: Director<S>>(&'a self, score_director: &'a D) -> impl Iterator<Item = EntityReference> + 'a` |
| `size` | `fn<D: Director<S>>(&self, score_director: &D) -> usize` |
| `is_never_ending` | `fn(&self) -> bool` |
### `MoveSelector<S: PlanningSolution, M: Move<S>>` — `typed_move_selector.rs`
| `iter_moves` | `fn<'a, D: Director<S>>(&'a self, score_director: &'a D) -> impl Iterator<Item = M> + 'a` |
| `size` | `fn<D: Director<S>>(&self, score_director: &D) -> usize` |
| `is_never_ending` | `fn(&self) -> bool` |
### `TypedValueSelector<S: PlanningSolution, V>` — `typed_value.rs`
| `iter_typed` | `fn<'a, D: Director<S>>(&'a self, score_director: &'a D, descriptor_index: usize, entity_index: usize) -> impl Iterator<Item = V> + 'a` |
| `size` | `fn<D: Director<S>>(&self, score_director: &D, descriptor_index: usize, entity_index: usize) -> usize` |
| `is_never_ending` | `fn(&self) -> bool` |
### `PillarSelector<S: PlanningSolution>` — `pillar.rs`
| `iter` | `fn<'a, D: Director<S>>(&'a self, score_director: &'a D) -> impl Iterator<Item = Pillar> + 'a` |
| `size` | `fn<D: Director<S>>(&self, score_director: &D) -> usize` |
| `is_never_ending` | `fn(&self) -> bool` |
| `descriptor_index` | `fn(&self) -> usize` |
### `ConstructionForager<S, M>` — `construction/forager.rs`
Requires: `Send + Debug`. Bounds: `S: PlanningSolution, M: Move<S>`.
| `pick_move_index` | `fn<D: Director<S>>(&self, placement: &Placement<S, M>, score_director: &mut D) -> Option<usize>` |
### `LocalSearchForager<S, M>` — `localsearch/forager.rs`
Requires: `Send + Debug`. Bounds: `S: PlanningSolution, M: Move<S>`.
| `step_started` | `fn(&mut self, best_score: S::Score, last_step_score: S::Score)` |
| `add_move_index` | `fn(&mut self, index: usize, score: S::Score)` |
| `is_quit_early` | `fn(&self) -> bool` |
| `pick_move_index` | `fn(&mut self) -> Option<(usize, S::Score)>` |
### `EntityPlacer<S, M>` — `construction/placer.rs`
Requires: `Send + Debug`. Bounds: `S: PlanningSolution, M: Move<S>`.
| `get_placements` | `fn<D: Director<S>>(&self, score_director: &D) -> Vec<Placement<S, M>>` |
### `ScoreBounder<S, D>` — `exhaustive/bounder.rs`
Requires: `Send + Debug`. Bounds: `S: PlanningSolution, D: Director<S>`.
| `calculate_optimistic_bound` | `fn(&self, score_director: &D) -> Option<S::Score>` | — |
| `calculate_pessimistic_bound` | `fn(&self, score_director: &D) -> Option<S::Score>` | `None` |
### `ExhaustiveSearchDecider<S, D>` — `exhaustive/decider.rs`
Requires: `Send + Debug`. Bounds: `S: PlanningSolution, D: Director<S>`.
| `expand` | `fn(&self, parent_index: usize, parent: &ExhaustiveSearchNode<S>, score_director: &mut D) -> Vec<ExhaustiveSearchNode<S>>` |
| `total_entities` | `fn(&self, score_director: &D) -> usize` |
### `SolutionPartitioner<S>` — `partitioned/partitioner.rs`
Requires: `Send + Sync + Debug`. Bounds: `S: PlanningSolution`.
| `partition` | `fn(&self, solution: &S) -> Vec<S>` | — |
| `merge` | `fn(&self, original: &S, partitions: Vec<S>) -> S` | — |
| `recommended_partition_count` | `fn(&self) -> Option<usize>` | `None` |
### `NearbyDistanceMeter<Origin, Destination>` — `nearby.rs`
| `distance` | `fn(&self, origin: &Origin, destination: &Destination) -> f64` |
### `ListPositionDistanceMeter<S>` — `k_opt/distance_meter.rs`
Requires: `Send + Sync + Debug`.
| `distance` | `fn(&self, solution: &S, entity_idx: usize, pos_a: usize, pos_b: usize) -> f64` |
### `CrossEntityDistanceMeter<S>` — `nearby_list_change.rs`
| `distance` | `fn(&self, solution: &S, src_entity: usize, src_pos: usize, dst_entity: usize, dst_pos: usize) -> f64` |
### `PhaseFactory<S, D>` — `manager/mod.rs`
Requires: `Send + Sync`. Bounds: `S: PlanningSolution, D: Director<S>`.
| `Phase` | `Phase<S, D>` |
| `create` | `fn(&self) -> Self::Phase` |
### `Solvable` — `manager/solver_manager.rs`
Requires: `PlanningSolution + Send + 'static`.
| `solve` | `fn(self, terminate: Option<&AtomicBool>, sender: mpsc::UnboundedSender<(Self, Self::Score)>)` |
### `Analyzable` — `manager/solution_manager.rs`
Requires: `PlanningSolution + Clone + Send + 'static`.
| `analyze` | `fn(&self) -> ScoreAnalysis<Self::Score>` |
### `ProblemChange<S: PlanningSolution>` — `realtime/problem_change.rs`
Requires: `Send + Debug`.
| `apply` | `fn(&self, score_director: &mut dyn Director<S>)` |
## Move Types
All moves are generic over `S` (solution) and `V` (value). All use typed `fn` pointers for zero-erasure access.
| `ChangeMove` | `<S, V>` | entity_index, to_value, getter/setter fn ptrs | Yes (V: Clone) | Yes (V: Copy) |
| `SwapMove` | `<S, V>` | left/right entity indices, getter/setter fn ptrs | Yes | Yes |
| `ListChangeMove` | `<S, V>` | src/dst entity+position, list_len/remove/insert fn ptrs | Yes | Yes |
| `ListSwapMove` | `<S, V>` | first/second entity+position, list_len/get/set fn ptrs | Yes | Yes |
| `ListReverseMove` | `<S, V>` | entity_index, start/end, list_len/reverse fn ptrs | Yes | Yes |
| `ListRuinMove` | `<S, V>` | entity_index, SmallVec element_indices, fn ptrs | Yes (manual) | No |
| `SubListChangeMove` | `<S, V>` | src entity+start/end, dst entity+position, fn ptrs | Yes | Yes |
| `SubListSwapMove` | `<S, V>` | first/second entity+start/end, fn ptrs | Yes | Yes |
| `PillarChangeMove` | `<S, V>` | Vec entity_indices, to_value, getter/setter fn ptrs | Yes (manual) | No |
| `PillarSwapMove` | `<S, V>` | Vec left/right indices, getter/setter fn ptrs | Yes (manual) | No |
| `RuinMove` | `<S, V>` | SmallVec entity_indices, getter/setter fn ptrs | Yes (manual) | No |
| `KOptMove` | `<S, V>` | [CutPoint; 5], KOptReconnection, fn ptrs | Yes (manual) | No |
| `CompositeMove` | `<S, M1, M2>` | index_1, index_2, PhantomData | Yes | Yes |
### Move Union Enums
**`EitherMove<S, V>`** — Basic variable union:
- `Change(ChangeMove<S, V>)`, `Swap(SwapMove<S, V>)`
**`ListMoveImpl<S, V>`** — List variable union:
- `ListChange`, `ListSwap`, `SubListChange`, `SubListSwap`, `ListReverse`, `KOpt`, `ListRuin`
### Supporting Types
**`MoveArena<M>`** — O(1) arena allocator. `push()`, `take(index)`, `reset()`, `shuffle()`, `extend()`. Panics on double-take.
**`CutPoint`** — `{ entity_index: usize, position: usize }`. Derives: Clone, Copy, Debug, Default, PartialEq, Eq.
**`KOptReconnection`** — `{ segment_order: [u8; 6], reverse_mask: u8, len: u8 }`. Derives: Clone, Copy, Debug, PartialEq, Eq.
## Selector Types
### Entity Selectors
| `FromSolutionEntitySelector` | Iterates entities from descriptor. `with_skip_pinned()`, `with_is_pinned_fn()` |
| `AllEntitiesSelector` | Iterates all entities across all descriptors |
| `NearbyEntitySelector<S, M, ES>` | Distance-pruned entity selection |
| `MimicRecordingEntitySelector<S, ES>` | Records selections for replay |
| `MimicReplayingEntitySelector` | Replays recorded selections |
### Value Selectors
| `StaticTypedValueSelector<S, V>` | Fixed value list |
| `FromSolutionTypedValueSelector<S, V>` | Extracts values from solution via `fn(&S) -> Vec<V>` |
| `RangeValueSelector<S>` | Generates 0..count_fn(solution) |
### Move Selectors
| `ChangeMoveSelector<S, V, ES, VS>` | `ChangeMove<S, V>` | Basic variable change |
| `SwapMoveSelector<S, V, LES, RES>` | `SwapMove<S, V>` | Basic variable swap |
| `EitherChangeMoveSelector<S, V, ES, VS>` | `EitherMove<S, V>` | Wraps ChangeMoveSelector |
| `EitherSwapMoveSelector<S, V, LES, RES>` | `EitherMove<S, V>` | Wraps SwapMoveSelector |
| `ListChangeMoveSelector<S, V, ES>` | `ListChangeMove<S, V>` | List element relocation |
| `ListSwapMoveSelector<S, V, ES>` | `ListSwapMove<S, V>` | List element swap |
| `ListReverseMoveSelector<S, V, ES>` | `ListReverseMove<S, V>` | Segment reversal (2-opt) |
| `ListRuinMoveSelector<S, V>` | `ListRuinMove<S, V>` | LNS element removal |
| `SubListChangeMoveSelector<S, V, ES>` | `SubListChangeMove<S, V>` | Segment relocation (Or-opt) |
| `SubListSwapMoveSelector<S, V, ES>` | `SubListSwapMove<S, V>` | Segment swap |
| `KOptMoveSelector<S, V, ES>` | `KOptMove<S, V>` | K-opt tour optimization |
| `NearbyKOptMoveSelector<S, V, D, ES>` | `KOptMove<S, V>` | Distance-pruned k-opt |
| `NearbyListChangeMoveSelector<S, V, D, ES>` | `ListChangeMove<S, V>` | Distance-pruned relocation |
| `NearbyListSwapMoveSelector<S, V, D, ES>` | `ListSwapMove<S, V>` | Distance-pruned swap |
| `RuinMoveSelector<S, V>` | `RuinMove<S, V>` | Basic variable LNS |
**ListMove* wrappers** adapt specific move selectors to produce `ListMoveImpl<S, V>`:
`ListMoveListChangeSelector`, `ListMoveListSwapSelector`, `ListMoveListReverseSelector`, `ListMoveSubListChangeSelector`, `ListMoveSubListSwapSelector`, `ListMoveKOptSelector`, `ListMoveListRuinSelector`, `ListMoveNearbyListChangeSelector`, `ListMoveNearbyListSwapSelector`.
### Selector Decorators
| `UnionMoveSelector<S, M, A, B>` | Two selectors | Sequential combination |
| `CartesianProductArena<S, M1, M2>` | Two move types | Cross-product iteration arena |
| `FilteringMoveSelector<S, M, Inner>` | Predicate `fn(&M) -> bool` | Filters moves |
| `ShufflingMoveSelector<S, M, Inner>` | RNG | Randomizes order |
| `SortingMoveSelector<S, M, Inner>` | Comparator `fn(&M, &M) -> Ordering` | Sorts moves |
| `ProbabilityMoveSelector<S, M, Inner>` | Weight `fn(&M) -> f64` | Probabilistic filtering |
| `SelectedCountLimitMoveSelector<S, M, Inner>` | Limit `usize` | Caps yield count |
### Supporting Types
**`EntityReference`** — `{ descriptor_index: usize, entity_index: usize }`.
**`Pillar`** — `{ entities: Vec<EntityReference> }`. Methods: `size()`, `is_empty()`, `first()`, `iter()`.
**`SubPillarConfig`** — `{ enabled: bool, minimum_size: usize, maximum_size: usize }`. Methods: `none()`, `all()`, `with_minimum_size()`, `with_maximum_size()`.
**`SelectionOrder`** — Enum: `Inherit`, `Original`, `Random`, `Shuffled`, `Sorted`, `Probabilistic`. Methods: `resolve()`, `is_random()`, `requires_caching()`.
**`NearbySelectionConfig`** — Builder: `with_distribution_type()`, `with_max_nearby_size()`, `with_min_distance()`.
**`KOptConfig`** — `{ k: usize, min_segment_len: usize, limited_patterns: bool }`. Methods: `new(k)`, `with_min_segment_len()`, `with_limited_patterns()`.
**`MimicRecorder`** — Shared state for recording/replaying entity selections. Methods: `new(id)`, `get_has_next()`, `get_recorded_entity()`, `reset()`.
## Phase Types
### Construction Heuristic
**`ConstructionHeuristicPhase<S, M, P, Fo>`** — Bounds: `P: EntityPlacer<S, M>`, `Fo: ConstructionForager<S, M>`.
Construction foragers:
| `FirstFitForager<S, M>` | First doable move |
| `BestFitForager<S, M>` | Best scoring move |
| `FirstFeasibleForager<S, M>` | First feasible move |
| `WeakestFitForager<S, M>` | Lowest strength move |
| `StrongestFitForager<S, M>` | Highest strength move |
Entity placers:
| `QueuedEntityPlacer<S, V, ES, VS>` | Iterates entities, generates ChangeMove per value |
| `SortedEntityPlacer<S, M, Inner>` | Wraps placer, sorts entities by comparator |
**`Placement<S, M>`** — `{ entity_ref: EntityReference, moves: Vec<M> }`.
### Local Search
**`LocalSearchPhase<S, M, MS, A, Fo>`** — Bounds: `MS: MoveSelector<S, M>`, `A: Acceptor<S>`, `Fo: LocalSearchForager<S, M>`.
Local search foragers:
| `AcceptedCountForager<S>` | Best of N accepted moves |
| `FirstAcceptedForager<S>` | First accepted |
| `BestScoreForager<S>` | Best overall score |
| `FirstBestScoreImprovingForager<S>` | First improving best |
| `FirstLastStepScoreImprovingForager<S>` | First improving last step |
### Acceptors
| `HillClimbingAcceptor` | — | — |
| `LateAcceptanceAcceptor<S>` | `S: PlanningSolution` | `late_acceptance_size` |
| `SimulatedAnnealingAcceptor` | — | `starting_temperature`, `decay_rate` |
| `TabuSearchAcceptor<S>` | `S: PlanningSolution` | `tabu_size`, `aspiration_enabled` |
| `EntityTabuAcceptor` | — | `entity_tabu_size` |
| `ValueTabuAcceptor` | — | `value_tabu_size` |
| `MoveTabuAcceptor` | — | `move_tabu_size`, `aspiration_enabled` |
| `GreatDelugeAcceptor<S>` | `S: PlanningSolution` | `rain_speed` |
| `StepCountingHillClimbingAcceptor<S>` | `S: PlanningSolution` | `step_count_limit` |
| `DiversifiedLateAcceptanceAcceptor<S>` | `S: PlanningSolution` | `late_acceptance_size`, `tolerance` |
| `AnyAcceptor<S>` | `S: PlanningSolution` | Enum over all built-in acceptors; returned by `AcceptorBuilder::build()` |
### Exhaustive Search
**`ExhaustiveSearchPhase<Dec>`** — Bounds: `Dec: ExhaustiveSearchDecider<S, D>`.
**`ExplorationType`** — `DepthFirst`, `BreadthFirst`, `ScoreFirst`, `OptimisticBoundFirst`.
**`ExhaustiveSearchConfig`** — `{ exploration_type, node_limit, depth_limit, enable_pruning }`.
**`ExhaustiveSearchNode<S>`** — Tree node: depth, score, optimistic_bound, entity/value indices, parent_index.
**`MoveSequence<S, M>`** — Stack of moves for branch reconstruction.
**`SimpleDecider<S, V, B>`** — Generic decider with values and optional bounder.
Score bounders: `SoftScoreBounder`, `FixedOffsetBounder<S>`, `()` (no-op).
### Partitioned Search
**`PartitionedSearchPhase<S, D, PD, Part, SDF, PF, CP>`** — Generic over partitioner, score director factory, phase factory, child phases.
**`FunctionalPartitioner<S, PF, MF>`** — Closure-based partitioner.
**`ThreadCount`** — `Auto`, `Unlimited`, `Specific(usize)`.
### VND (Variable Neighborhood Descent)
**`VndPhase<T, M>`** — Wraps tuple of move selectors. `impl_vnd_phase!` macro generates Phase impls for tuples up to 8 neighborhoods.
## Scope Hierarchy
### `BestSolutionCallback<S>` — `scope/solver.rs`
Sealed trait for zero-allocation callback dispatch. Implemented for `()` (no-op) and any `F: Fn(&S) + Send + Sync`.
### `SolverScope<'t, S, D, BestCb = ()>`
Top-level scope for entire solve. Holds score director, best solution, RNG, stats, termination state.
Key methods: `new(score_director)`, `new_with_callback(score_director, callback, terminate)`, `with_best_solution_callback(F) -> SolverScope<.., F>`, `start_solving()`, `working_solution()`, `calculate_score()`, `update_best_solution()`, `is_terminate_early()`, `set_time_limit()`.
Public fields: `inphase_step_count_limit`, `inphase_move_count_limit`, `inphase_score_calc_count_limit`.
### `PhaseScope<'t, 'a, S, D, BestCb = ()>`
Borrows `&mut SolverScope`. Tracks per-phase state: phase_index, starting_score, step_count, PhaseStats.
### `StepScope<'t, 'a, 'b, S, D, BestCb = ()>`
Borrows `&mut PhaseScope`. Tracks per-step state: step_index, step_score. `complete()` records step in stats.
## Termination Types
| `TimeTermination` | `Duration` | `seconds()`, `millis()` helpers |
| `StepCountTermination` | `u64` | Total step limit |
| `BestScoreTermination<Sc>` | `Sc: Score` | Target score |
| `BestScoreFeasibleTermination<S, F>` | Closure | `score_at_least_zero()` convenience |
| `UnimprovedStepCountTermination<S>` | `u64` | Steps without improvement |
| `UnimprovedTimeTermination<S>` | `Duration` | Time without improvement |
| `MoveCountTermination<S>` | `u64` | Total moves evaluated |
| `ScoreCalculationCountTermination<S>` | `u64` | Total score calculations |
| `DiminishedReturnsTermination<S>` | `Duration, f64` | Window + min improvement rate |
| `OrTermination<T, S, D>` | Tuple | Any termination triggers |
| `AndTermination<T, S, D>` | Tuple | All must trigger |
Composite terminations use tuple impls (up to 8 elements) generated via `impl_composite_termination!` macro.
## Manager System
### `SolverFactory<S, D, C, P, T>`
Holds score calculator, phases, termination. Methods: `solve()`, `create_solver()`, `builder()`.
### `SolverFactoryBuilder<S, D, C, P, T>`
Fluent builder: `with_phase()`, `with_phase_factory()`, `with_config()`, `with_time_limit()`, `with_step_limit()`, `with_time_limit_or()`, `build()`.
### `SolverManager<S: Solvable>`
Static lifetime manager: `solve()` returns `(job_id, receiver)`. Methods: `get_status()`, `terminate_early()`, `free_slot()`. `MAX_JOBS = 16`.
### `SolverStatus`
Enum: `NotSolving`, `Solving`. Serde: `SCREAMING_SNAKE_CASE`.
### `analyze<S>(solution: &S) -> ScoreAnalysis<S::Score>`
Free function. Requires `S: Analyzable, S::Score: Score`. Delegates to `solution.analyze()`.
### `ScoreAnalysis<Sc>` / `ConstraintAnalysis<Sc>`
Serde-serializable. `ScoreAnalysis { score, constraints: Vec<ConstraintAnalysis> }`. `ConstraintAnalysis { name, weight, score, match_count }`.
### Phase Factories
| `ConstructionPhaseFactory<S, M, P, Fo>` | `ConstructionHeuristicPhase` |
| `LocalSearchPhaseFactory<S, M, MS, A, Fo>` | `LocalSearchPhase` |
| `ListConstructionPhaseBuilder<S, E>` | `ListConstructionPhase` |
| `ListCheapestInsertionPhase<S, E>` | Self (implements Phase directly) |
| `ListRegretInsertionPhase<S, E>` | Self (implements Phase directly) |
| `KOptPhaseBuilder<S, V, DM, ESF>` | `KOptPhase` |
## Real-Time Planning
**`SolverHandle<S>`** — Client-facing handle. `add_problem_change()`, `terminate_early()`, `is_solving()`.
**`ProblemChangeReceiver<S>`** — Server-side receiver. `try_recv()`, `drain_pending()`, `is_terminate_early_requested()`.
**`ProblemChangeResult`** — `Queued`, `SolverNotRunning`, `QueueFull`.
**`ClosureProblemChange<S, F>`** — Wraps `Fn(&mut dyn Director<S>)`.
**`BoxedProblemChange<S>`** — Type alias: `Box<dyn ProblemChange<S>>`.
## Solver & Convenience Functions
### `Solver<'t, P, T, S, D, BestCb = ()>`
Main solver struct. Drives phases and checks termination. `impl_solver!` macro generates `solve(self, score_director: D) -> SolveResult<S>` for phase tuples up to 8.
Builder methods: `new(phases)`, `with_termination(T)`, `with_terminate(&AtomicBool)`, `with_time_limit(Duration)`, `with_best_solution_callback<F>(F) -> Solver<.., F>`. The callback type transitions the `BestCb` parameter from `()` to the concrete closure type — no `Box<dyn Fn>` allocation.
### `SolveResult<S>`
`{ solution: S, stats: SolverStats }`. Methods: `solution()`, `into_solution()`, `stats()`, `step_count()`, `moves_evaluated()`, `moves_accepted()`.
### `SolverStats` / `PhaseStats`
Aggregate and per-phase metrics: step count, moves evaluated/accepted, score calculations, elapsed time, acceptance rate, moves per second.
### `ProblemSpec` — `problem_spec.rs`
Trait that abstracts over basic and list-variable problems for config-driven solver construction.
### `BasicSpec` — `basic.rs`
Concrete `ProblemSpec` implementation for basic (non-list) variable problems. Wires construction + local search with `SimulatedAnnealing` + `AcceptedCountForager` using `UnionMoveSelector` of `EitherChangeMoveSelector` + `EitherSwapMoveSelector`.
### `ListSpec` — `list_solver.rs`
Concrete `ProblemSpec` implementation for list-variable problems. Wires list construction + local search phases for ordered/routed planning problems.
### `AnyTermination` / `build_termination()` — `run.rs`
`AnyTermination` is an enum over all built-in termination types for config-driven dispatch. `build_termination()` constructs an `AnyTermination` from a `SolverConfig`.
### `run_solver()` — `run.rs`
Unified convenience function covering both basic and list-variable problems. Accepts `terminate: Option<&AtomicBool>` and `sender: mpsc::UnboundedSender<(S, S::Score)>` for external control and solution streaming. Replaces the former per-problem-type functions.
## Architectural Notes
- **Zero-erasure throughout.** All moves, selectors, phases, acceptors, foragers, and terminations are fully monomorphized via generics. No `Box<dyn Trait>` or `Arc` in hot paths.
- **Function pointer storage.** Moves and selectors store `fn` pointers (e.g., `fn(&S, usize) -> Option<V>`) instead of trait objects for solution access.
- **PhantomData<fn() -> T>** pattern used in all move types to avoid inheriting Clone/Send/Sync bounds from phantom type parameters.
- **SmallVec<[usize; 8]>** used in RuinMove and ListRuinMove for stack-allocated small ruin counts.
- **Tuple-based composition.** Phases, terminations, and VND neighborhoods compose via nested tuples with macro-generated impls, avoiding `Vec<Box<dyn Phase>>`.
- **Intentional `dyn` boundaries.** `DynDistanceMeter` in `nearby.rs` and `DefaultPillarSelector` value extractor closures are intentional type-erasure points to avoid monomorphization bloat.
- **`ProblemChange::apply` uses `&mut dyn Director<S>`** — intentional type erasure at the real-time planning boundary.
- **Arena-based move ownership.** Moves are pushed into `MoveArena`, evaluated by index, and taken (moved out) when selected. Never cloned.
- **Rayon for parallelism.** Partitioned search uses rayon for CPU-bound parallel solving. `tokio::sync::mpsc` for solution streaming.