Documentation
Planning Entities
Structs with planning variables that the solver assigns during optimization.
A planning entity is a struct that contains one or more planning variables. These are the fields the solver changes while searching for a good solution.
The #[planning_entity] Macro
use solverforge::prelude::*;
#[planning_entity]
pub struct Shift {
#[planning_id]
pub id: usize,
pub required_skill: String,
#[planning_variable(value_range_provider = "employees", allows_unassigned = true)]
pub employee_id: Option<usize>,
}
Field Attributes
#[planning_id]
Provides stable identity for the entity. This is strongly recommended for joins, analysis, and readable telemetry.
#[planning_id]
pub id: usize,
#[planning_variable]
Marks a field as a planning variable — the solver assigns values to this field.
#[planning_variable(value_range_provider = "employees", allows_unassigned = true)]
pub employee_id: Option<usize>,
#[planning_variable(countable_range = "0..10")]
pub priority_bucket: Option<usize>,
Common parameters:
value_range_provider = "employees": references a solution collection for a shared legal index range, or aVec<usize>field on the entity for a per-entity legal domainallows_unassigned = true: permitsNoneforOption<T>variablescountable_range = "0..10": declares a half-open integer range of candidate values directly on the field. The field remainsOption<usize>and stores the chosen candidate index, like every other scalar planning variablecandidate_values = "fn_name": supplies an ordered bounded scalar value neighborhood for construction, change, pillar-change, and ruin-recreate. It is a search hint, not a legal-domain restriction; swap families ignore itnearby_value_candidates = "fn_name"andnearby_entity_candidates = "fn_name": supply bounded candidates for nearby scalar local-search selectorsnearby_value_distance_meter = "fn_name"andnearby_entity_distance_meter = "fn_name": rank or filter already bounded nearby candidates; they do not discover candidates by themselvesconstruction_entity_order_key = "fn_name"andconstruction_value_order_key = "fn_name": provide live construction-order keys for scalar-only construction heuristics
For a per-entity domain, store legal indexes on the entity and name that field:
use solverforge::prelude::*;
#[planning_entity]
pub struct Shift {
#[planning_id]
pub id: usize,
pub allowed_employees: Vec<usize>,
#[planning_variable(value_range_provider = "allowed_employees", allows_unassigned = true)]
pub employee_idx: Option<usize>,
}
Construction and all scalar move families, including swap, nearby swap, and
pillar swap, enforce the receiving entity’s legal domain. A swap can receive a
value outside candidate_values if it is inside that domain. Use
#[planning_pin], not a one-value domain, to preserve an input assignment.
Nullability is not the same as required coverage. An assignment-backed
ScalarGroup::assignment(...) can mark nullable rows required with
with_required_entity, and uses named grouped construction and
grouped_scalar_move_selector. Optional kept-unassigned decisions complete
only their anchor group slot and allow construction to continue to later rows;
completed optional roots are skipped before reading candidate domains.
See Construction for group declaration,
capacity hooks, assignment limits, and completion semantics.
The macro also generates an .unassigned() stream helper when the entity has
exactly one Option<_> planning variable.
Construction order keys are construction-only. Local-search ranking uses
separate search_entity_order_key and search_value_order_key hooks.
Guided Scalar Search
Declare search_entity_order_key = "fn_name" to rank ordinary scalar change
and swap entities, and search_value_order_key = "fn_name" to rank ordinary
change values. The signatures are fn(&S, &Entity) -> i64 and
fn(&S, &Entity, value: usize) -> i64. Lower keys are tried first; equal keys
retain the seeded candidate order. Variables without these hooks retain their
unguided ordering. These hooks rank enumeration, not acceptance: the score,
acceptor, and forager still decide which candidate is committed.
Callbacks may read the whole solution. A change to another entity can change a candidate domain, order key, legality verdict, or ownership policy; do not treat a callback as row-local merely because it receives one entity.
Scalar variables remain direct one-slot assignments. When planning owns membership or order as well as assignment, use a list variable instead.
#[planning_list_variable]
Declares the canonical ordered-sequence variable for routing or sequencing. In
the current runtime this is represented as Vec<usize> plus an
element_collection name.
#[planning_list_variable(element_collection = "visits")]
pub visits: Vec<usize>,
Common parameters:
element_collection = "visits": names the solution collection that contains all list elementsdomain = "cvrp": uses the stock CVRP profile for route-list models, including the CVRP solution trait, distance meters, route hooks, savings hooks, and savings metric classsolution_trait = "path::Trait": adds an explicit solution-side trait bound for generated stock list-variable helpers when meters or route utilities need richer solution context
#[planning_pin]
Prevents the solver from changing an entity’s variables. Useful for pre-assigned or locked entities.
#[planning_pin]
pub pinned: bool,
When pinned is true, the solver treats the entity as immovable.
Shadow Variables
Shadow variables are derived fields maintained from genuine planning variables. They are advanced modeling features; you do not assign them directly.
#[inverse_relation_shadow_variable]
Tracks an inverse relationship from a source variable.
#[inverse_relation_shadow_variable(source_variable_name = "visits")]
pub vehicle: Option<usize>,
#[index_shadow_variable]
For list variables, tracks the element’s current zero-based position. Declare
#[index_shadow_variable(source_variable_name = "visits")] on an
Option<usize> field and name that field with index_field on the solution’s
#[shadow_variable_updates(...)] configuration.
#[previous_element_shadow_variable]
For list variables — automatically tracks the previous element in the list.
#[previous_element_shadow_variable(source_variable_name = "visits")]
pub previous_stop: Option<usize>,
#[next_element_shadow_variable]
For list variables — automatically tracks the next element in the list.
#[next_element_shadow_variable(source_variable_name = "visits")]
pub next_stop: Option<usize>,
Requirements
- Must be a named Rust struct
- Must have at least one
#[planning_variable]or#[planning_list_variable]field
The macro adds the standard derives and trait implementations for you; you do
not need to duplicate Clone or Debug manually in the common case.
See Also
- Planning Solutions — The container that holds entities
- Problem Facts — Immutable input data
- List Variables — Sequencing and routing variables