releases
solverforge 0.25.x: Literal Configuration, Framework Partitioned Search, and Automatic Parallel Evaluation
The 0.25.x line applies every configured termination criterion, adds no hidden time limit, validates configuration that used to be ignored, runs partitioned search in the framework, and turns on measured parallel evaluation by default.
solverforge 0.25.x follows solverforge 0.24.0; the line was published on
crates.io on 2026-11-09. The first release is
0.25.0, with a Rust 1.95
floor, and the line closes with
0.25.1. At publication
cargo add solverforge installed 0.25.0, and docs.rs served the 0.25.0 API.
solverforge-cli 3.9.0 scaffolds target 0.25.0 and solverforge-cli 4.0.0
and 4.0.1 scaffold target 0.25.1; existing applications keep their pins until they
upgrade.
What Changed
Termination means what the configuration says — breaking changes
- Every configured solver-level criterion applies, combined with OR: the first one reached stops the solve. A configuration with several criteria can stop earlier than before.
- Nothing adds a time limit the configuration does not set. A solve with only
best_score_limit,step_count_limit, or an unimproved limit is no longer capped at 30 or 60 seconds. seconds_spent_limit = 0andminutes_spent_limit = 0are a zero time limit, not “no limit”; time limits are literal and saturate instead of overflowing.- The
solverforge.execution_policycandidate-trace header carries one attribute per criterion, andtermination_compositionnames every installed criterion (for exampletime_or_step_count). Thecriterionandcriterion_targetattributes are removed. try_run_solver_with_config_and_search,try_run_weighted_solver_with_config_and_search, andtry_run_dynamic_solver_with_config_partslose theirdefault_time_limit_secsparameter.
See Termination.
Parallel evaluation is automatic — breaking change
The default move_thread_count = "auto" evaluates local-search steps on up to
four evaluators, choosing per step between sequential evaluation and parallel
windows from measured trial and coordination costs. A parallel window forks up
to three working-solution replicas. "none" keeps evaluation sequential.
{ count = N } needs N >= 1: MoveThreadCount::Count and
ThreadCount::Specific hold a NonZeroUsize, and { count = 0 } is a
configuration error. The thread policy is resolved when the local-search phase
solves, on the pool that runs it.
See Local Search.
Environment modes seed and check — breaking changes
reproducible,fast_assert, andfull_assertwithoutrandom_seedsolve with the fixed seedREPRODUCIBLE_RANDOM_SEED; onlynon_reproducibledraws an operating-system seed.fast_assertpanics on score drift at committed steps and best-solution updates.full_assertpanics on drift in any score-director calculation, including move evaluations and evaluator replicas.Directorgains the provided methodset_score_assertion.
Partitioned search runs in the framework — breaking changes
SearchBuilder::partitioned_phase is removed. Register a SolutionPartitioner
with SearchBuilder::partitioner; the framework builds and runs the phase from
the partitioned_search configuration, and the phase termination bounds every
partition. SolutionPartitioner::recommended_partition_count returns
Option<NonZeroUsize>, FunctionalPartitioner::with_recommended_count takes a
NonZeroUsize, ThreadCount::resolve takes the recommendation as its second
argument, and auto threads are bounded by the recommended count. A
partitioned_search termination with best_score_limit,
unimproved_step_count_limit, or unimproved_seconds_spent_limit is a
configuration error.
Configuration that used to be ignored is rejected — breaking changes
group_candidate_limitwithoutgroup_name.value_candidate_limit,group_candidate_limit, or a non-defaultconstruction_obligationon a list construction heuristic.simulated_annealingwith bothcalibrationandlevel_temperatures.step_counting_hill_climbing.step_count_limit = 0.- A non-positive or non-finite
great_deluge.water_level_increase_ratio. - A
score_director.constraint_providerthat names noconstraintsorconstraint_providerpath declared on the solution (RuntimeBuildError::UnknownConstraintProvider).
A fixed-weight union needs at least one positive weight; a zero weight disables its child instead of failing the configuration.
Score director and analysis — breaking changes
score_director.constraint_provider selects which declared constraint set a
solve uses, and score_director.constraint_match_enabled defaults to true.
Setting it to false disables SolverManager::analyze_snapshot for the job,
which returns the new SolverManagerError::SnapshotAnalysisDisabled.
SolverRuntime::bind_snapshot_analysis takes
Option<fn(&S) -> ScoreAnalysis<S::Score>>, and
try_run_dynamic_solver_with_config_parts takes
&[DynamicConstraintProvider].
Runtime overrides
SolverConfig::with_override applies a SolverConfigOverride to a
configuration, and SolverManager can run a retained job under an override.
See SolverManager.
Search
- VND phases honour
score_tie_break, whose default israndom; setscore_tie_break = "first"to apply the first of several equally scored best improving moves. list_k_optconstruction acceptskfrom 2 to 5, and polishes routes with k-opt forkfrom 3 to 5.
Scoring — breaking changes
SharedOperatorTerminalrequires its operator to implementGroupedOperator, andchanged_key_countsums changed group keys instead of counting row-changing notifications.- A
Projectionthat emits more rows for one input than itsMAX_EMITSpanics; raiseMAX_EMITSto the real maximum. Row lists are sized byMAX_EMITS. ConstraintWeightis documented and exported at the crate roots besideFixedWeightandHardWeight.- A score director can fork over another solution.
- Scoring production code forbids
unsafe.
Performance
The 0.25.1 patch keeps the nearest destinations of the nearby list change and swap cursors in one bounded buffer per cursor, described below. In 0.25.0, construction reuses its candidate and value buffers across placements, a solver runner keeps its declaration only when it runs partitioned search, and the global Rayon pool is no longer initialized while a solver compiles.
0.25.1: bounded nearby list candidates
The nearby list change and swap cursors keep only the max_nearby nearest
destinations of a source position in one buffer each cursor owns, ordered by
ascending distance with offer order between equal distances. That is the same
set and order the earlier sort-and-truncate produced, so candidate streams
are unchanged; the buffer is reused across source positions and moves read
from it directly, with no per-position allocation
(#228). The public
API is unchanged.
Published Version Boundaries
| Surface | Version | Notes |
|---|---|---|
| Core runtime | solverforge 0.25.0 |
Rust 1.95; literal termination, automatic parallel evaluation, framework partitioned search, stricter configuration |
| Core patch | solverforge 0.25.1 |
Bounded nearby list candidate buffer; API unchanged; scaffolded by solverforge-cli 4.0.0 and 4.0.1 |
| Previous core runtime | solverforge 0.24.0 |
Explicit configuration and constraint weights; see the 0.24.x release note |
| CLI | solverforge-cli 3.9.0 |
Fresh scaffolds target core 0.25.0 |
| Previous CLI | solverforge-cli 3.8.3 |
Scaffolds core 0.24.0; existing projects retain their pins |
| Python | solverforge-py 0.7.2 / PyPI solverforge 0.7.2 |
Still compiled onto core 0.23.0; upgrading the Rust core does not retarget these wheels |
| Worked use cases | November 9 app releases | All eight target core 0.25.0 and UI 0.9.0; see the use-case 3.0 release note |
| UI | solverforge-ui 0.9.1 |
Unchanged |
| Maps | solverforge-maps 2.3.3 |
Unchanged |
All nine workspace crates (solverforge, solverforge-core,
solverforge-config, solverforge-scoring, solverforge-solver,
solverforge-macros, solverforge-bridge, solverforge-console, and
solverforge-cvrp) publish 0.25.0.
The registry records the facade crate’s publication at 2026-11-09T03:06:41Z.
Its artifact SHA-256 is
4a3ecc7e869c19a21e06386736824f230ad106941188b861424389556456737c;
.cargo_vcs_info.json binds it to
3115b4dd6b40bc72a472b042f5e6b665cb9720c7, the v0.25.0 tag commit. The
GitHub Release
was published at 2026-11-09T03:06:58Z.
On that commit the CI run passed. The Release workflow re-ran the CI matrix on Linux, macOS, and Windows with the Miri, MSRV, docs, and package jobs, verified the release, published to crates.io, and created the GitHub Release; every job succeeded.
All nine workspace crates publish 0.25.1. The registry records the facade
crate’s publication at 2026-11-09T17:38:50Z; its artifact SHA-256 is
c899657c17ae3a41d67b583ec400e5d79fe253f0ed62a38a2efcb5d04d1171bb, and
.cargo_vcs_info.json binds it to
6069763dffdf57786b703f113e5d290f3695ed02, the v0.25.1 tag commit. On that
commit the CI run
passed, and the
Release workflow
re-ran the CI matrix on Linux, macOS, and Windows with the Miri, MSRV, docs,
and package jobs, verified the release, published to crates.io, and created
the GitHub Release
at 2026-11-09T17:39:07Z; every job succeeded.
Patch History
| Version | Date | Notes |
|---|---|---|
0.25.1 |
2026-11-09 | The nearby list change and swap cursors keep the nearest destinations in one bounded buffer per cursor; candidate streams and API unchanged. |
0.25.0 |
2026-11-09 | Every termination criterion applies with no hidden time limit; automatic measured parallel evaluation; fixed seeds and stronger checks in the assert modes; partitioned search run by the framework; ignored configuration rejected; snapshot analysis gated on constraint_match_enabled; configuration overrides. |
Upgrade Checklist
- Read every
[termination]table: all criteria now apply, a work or score criterion without a time limit can run indefinitely, and a zero time limit stops at once. - Replace
move_thread_count = { count = 0 }, and setmove_thread_count = "none"where a run must stay sequential. - Set
random_seedexplicitly if a reproducible or assert-mode run relied on a fresh seed each time. - Move partitioned search to
SearchBuilder::partitionerand thepartitioned_searchconfiguration; passNonZeroUsizepartition counts. - Remove the configuration combinations listed above, and give a union with fixed weights at least one positive weight.
- Handle
SolverManagerError::SnapshotAnalysisDisabledin exhaustive matches. - Raise a
Projection’sMAX_EMITSto its real maximum, and readchanged_key_countas a count of changed group keys. - Add
score_tie_break = "first"to a VND phase that must keep the earlier choice among equal best moves.
Where to read next
Read the core manual and the solver configuration guide. The full compare covers all 54 commits of 0.25.0, the 0.25.1 compare covers the patch, and the CHANGELOG lists every breaking change.