Documentation
Planning Solutions
The top-level container that holds entities, facts, and the current score.
A planning solution is the root struct that represents your entire problem and its current solution state. It owns your entity collections, problem facts, and score field.
The #[planning_solution] Macro
use solverforge::prelude::*;
#[planning_solution(
constraints = "crate::constraints::define_constraints",
scalar_groups = "crate::domain::scalar_groups"
)]
pub struct Schedule {
#[problem_fact_collection]
pub employees: Vec<Employee>,
#[problem_fact_collection]
pub availability: Vec<Availability>,
#[planning_entity_collection]
pub shifts: Vec<Shift>,
#[planning_score]
pub score: Option<HardSoftScore>,
}
The constraints parameter is optional, but it is what enables the generated
Solvable and Analyzable implementations used by the stock runtime.
scalar_groups is optional; use it when the model declares candidate-backed or
assignment-backed ScalarGroup values for grouped construction and grouped
scalar local search.
Field Attributes
#[planning_entity_collection]
Marks a Vec<T> field containing planning entities. The solver iterates these
collections to find variables to change.
#[planning_entity_collection]
pub shifts: Vec<Shift>,
#[problem_fact_collection]
Marks a Vec<T> field containing immutable problem facts. Used by constraints but never modified by the solver.
#[problem_fact_collection]
pub employees: Vec<Employee>,
#[planning_score]
Marks the field that holds the current solution quality. Must be Option<ScoreType>.
#[planning_score]
pub score: Option<HardSoftScore>,
Supported score types: SoftScore, HardSoftScore, HardMediumSoftScore, HardSoftDecimalScore, BendableScore.
Generated Helpers
When constraints = "..." is present, #[planning_solution] also generates:
- Collection source methods such as
Schedule::shifts()andSchedule::employees(), so constraints can start fromfactory.for_each(Schedule::shifts())without ad hoc slice closures Solvablefor retained-job solving throughSolverManager::solve(), including the typed search declaration consumed by the runtime compilerAnalyzablefor score breakdowns viaanalyze()
Before candidate work begins, generated solving resolves that declaration and the model’s descriptor/runtime slots into one immutable graph. Missing targets, capabilities, providers, stable list sources, or invalid selector composition surface as runtime-build errors instead of being discovered halfway through a phase.
Generated source methods are the default way to start constraints
over planning entity and problem fact collections. See
Constraint Factory Methods
for the full method contract and the lower-level for_each(...) escape hatch.
For advanced list-shadow workflows, the struct can also carry
#[shadow_variable_updates(...)]. When present, the macro generates
PlanningSolution::update_entity_shadows(...) and update_all_shadows()
overrides directly on the solution type, and the stock ScoreDirector calls
those hooks automatically during solving and score analysis. The update
configuration can maintain inverse_field, index_field, previous_field,
and next_field views of the canonical owner list before cascading,
aggregate, compute, and post-update listeners run.
Requirements
- Must be a named Rust struct
- Must have exactly one
#[planning_score]field - Must have at least one
#[planning_entity_collection]field for stock solving
Common value-range setup lives on the entity side, for example
#[planning_variable(value_range_provider = "employees")].
See Also
- Planning Entities — What the solver changes
- Score Types — Available score types