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 a Vec<usize> field on the entity for a per-entity legal domain
  • allows_unassigned = true: permits None for Option<T> variables
  • countable_range = "0..10": declares a half-open integer range of candidate values directly on the field. The field remains Option<usize> and stores the chosen candidate index, like every other scalar planning variable
  • candidate_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 it
  • nearby_value_candidates = "fn_name" and nearby_entity_candidates = "fn_name": supply bounded candidates for nearby scalar local-search selectors
  • nearby_value_distance_meter = "fn_name" and nearby_entity_distance_meter = "fn_name": rank or filter already bounded nearby candidates; they do not discover candidates by themselves
  • construction_entity_order_key = "fn_name" and construction_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.

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 elements
  • domain = "cvrp": uses the stock CVRP profile for route-list models, including the CVRP solution trait, distance meters, route hooks, savings hooks, and savings metric class
  • solution_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