Documentation
Composite Move Selectors
Candidate ownership, selector composition, limits, and lower-level building blocks.
Composite selectors combine or constrain other selectors. Use them when one selector family is not enough, or when a broad selector needs an explicit cap.
Candidate Ownership
Selectors expose cursor-scoped candidates with stable candidate IDs. Search
phases evaluate borrowed candidates, release losers promptly, and transfer only
the selected winner by value. MoveArena remains a reusable-capacity owner for
the concrete APIs and composite storage that need it; it is not the universal
runtime owner for every selector step. This keeps cartesian and union execution
preview-safe without cloning large move payloads.
Lending Cursor Contract
A public MoveCursor<S, M> pulls with next_candidate(solution: &S).
next_candidate_with_control, next_owned_candidate,
next_owned_candidate_matching, and next_owned_candidate_inspected likewise
receive the caller’s solution. The cursor retains declaration/state and move
ownership, not a solution borrow across evaluation. candidate(id) lends a
MoveCandidateRef; take_candidate(id) transfers that candidate exactly once,
and release_candidate(id) drops a loser promptly. Dropping a cursor does not
require exhausting its unrequested tail. CandidateStore::reset() drops live
candidates while retaining its backing capacities.
The hidden resource-aware runtime boundary lends the mutable score director:
next_candidate_with_resources(director: &mut D, resources: &mut Resources).
The public director-aware pull next_candidate_with_director(&mut D) defaults
to the ordinary solution pull; preview-aware cursors and decorators forward it.
Do not retain the lent solution/director between pulls or leave speculative
writes behind when returning a candidate.
union_move_selector
union_move_selector combines multiple selectors and yields candidates from
each child selector according to selection_order.
[phases.move_selector]
type = "union_move_selector"
selection_order = "stratified_random"
weighting = "equal"
[[phases.move_selector.selectors]]
type = "change_move_selector"
variable_name = "employee_idx"
[[phases.move_selector.selectors]]
type = "swap_move_selector"
variable_name = "employee_idx"
Use union when the solver should search several independent neighborhoods in
one local-search phase. Available union orders are sequential, round_robin,
rotating_round_robin, random, and the default stratified_random. Child
weights can be equal, fixed through a parallel weights vector, or derived
from candidate_count.
Union scheduling and leaf candidate ordering are separate policies. Each leaf
can preserve original order, use seeded random or shuffled order, or use a
registered selection_metric for sorted / probabilistic ordering. The
runtime compiler resolves and freezes both layers before the phase starts.
cartesian_product_move_selector
cartesian_product_move_selector composes one move from each child selector
into a sequential composite candidate.
[phases.move_selector]
type = "cartesian_product_move_selector"
[[phases.move_selector.selectors]]
type = "change_move_selector"
variable_name = "employee_idx"
[[phases.move_selector.selectors]]
type = "change_move_selector"
variable_name = "room_idx"
Use cartesian composition only when the real move is “do A and B together”. Do not use it as a substitute for grouped scalar search; grouped scalar carries model-owned coupled-decision semantics.
Compiled Cartesian Preview
Configured Limited/Union/Cartesian declarations form an immutable indexed arena with stable node IDs. Retained leaf state moves into the cursor and returns to the solve-owned state on drop. Union nodes share the canonical scheduler; flat extensions remain concrete generic types rather than boxed erased leaves.
A Cartesian right child opens only after the left candidate is applied on the live director. Every right pull reinstates that row’s left preview, generates and checks the right candidate, then undoes the preview and restores the snapshotted score state before returning. Nested Cartesian rows compose the same transaction. Compiled Cartesian discovery does not clone a preview solution. The ordinary public Cartesian selector uses its separately owned preview director; it is not the compiled implementation.
Sequential candidate evaluation uses
Move::is_doable_with_working_preview(&mut D), so later legs see earlier legs’
effects. A rejected preview leaves the working solution and committed score
state unchanged.
limited_neighborhood
Use limited_neighborhood when a selector is useful but too broad before
scoring.
[phases.move_selector]
type = "limited_neighborhood"
selected_count_limit = 100
[phases.move_selector.selector]
type = "nearby_change_move_selector"
variable_name = "employee_idx"
max_nearby = 20
selected_count_limit caps emitted candidates before scoring. It is different
from the accepted-count forager limit, which stops a selector step after that
many accepted candidates and then picks the best candidate inside that finite
horizon.
Lower-Level Building Blocks
Entity Selectors
Entity selectors decide which entities are visited by a move selector. Most app
configs target entities through optional entity_class and variable_name
fields instead of constructing entity selectors directly.
Value Selectors
Value selectors provide candidate scalar values. In generated apps, scalar
value neighborhoods come from #[planning_variable] hooks such as
candidate_values, nearby_value_candidates, or a bounded range selected by
config.
Descriptor Scalar Selectors
Descriptor scalar selectors target variables through model descriptors rather than hand-written field access. They remain a standalone opt-in selector API. Generated and dynamic configured solves use the immutable runtime compiler; descriptor selectors do not create a second construction or configured-search engine.
Selector Decorators
| Decorator | Purpose |
|---|---|
FilteringMoveSelector |
filter borrowable candidates without reopening cartesian children |
Leaf ordering is not a public decorator type. Configure random, shuffled, sorted,
or probabilistic order through selection_order on a leaf selector, and register
a metric with selection_metric for the sorted and probabilistic modes.
Move Unions
| Move union | Purpose |
|---|---|
ScalarMoveUnion |
change, swap, pillar, ruin, and related scalar moves |
ListMoveUnion |
list change, list swap, reverse, K-opt, and list ruin moves |
DescriptorMoveUnion |
descriptor-targeted moves |
See Also
- Scalar Move Selectors - scalar assignment and repair selectors
- List Move Selectors - route and sequence selectors