Migration Guide
Migrate older integrations to the current generic `timetable-sa` solver API.
Migration Guide
This guide helps you migrate from older, more domain-specific usage patterns to the current generic solver API. The modern package surface is intentionally more abstract: the library owns the search engine, while you own the domain model, constraints, and move operators.
Migrate constructor usage
Older usage patterns commonly passed domain collections directly into the solver constructor.
Old pattern:
new SimulatedAnnealing(rooms, lecturers, classes, config)Current pattern:
new SimulatedAnnealing(initialState, constraints, moveGenerators, config)The conceptual shift is important. You now encode domain logic explicitly in
TState, Constraint<TState>, and MoveGenerator<TState>.
Migrate to async solving
solve() is asynchronous and returns Promise<Solution<TState>>.
Update call sites accordingly.
const result = await solver.solve();If you previously treated solving as synchronous, you may need to propagate
async through service, controller, or CLI layers.
Migrate the constraint score contract
The current solver expects Constraint.evaluate(state) to return a finite
satisfaction score in [0, 1].
1means satisfied,0means violated,- intermediate values mean partial satisfaction.
If older code used penalty-style semantics where larger numbers meant worse states, you must invert or normalize that logic.
Migrate move-generator assumptions
The current engine clones state before calling generate(...). That means move
generators may mutate the provided working state directly.
This differs from designs where move operators are expected to deep-clone their input on every call.
Migrate progress callbacks
If you use onProgress, make the callback mode explicit when behavior matters.
onProgressMode: 'await' | 'fire-and-forget'Also note that the callback receives state = null in the current
implementation for performance reasons.
Migrate Phase 1.5 start-temperature assumptions
The current branch no longer defaults to restarting Phase 1.5 from
initialTemperature.
Instead, the default behavior is:
intensificationStartTemperatureMode: 'phase1-end',- optional scaling through
intensificationStartTempMultiplier, - optional capping through
intensificationStartTempCapRatio.
If your older tuning assumed the legacy restart behavior, make it explicit.
intensificationStartTemperatureMode: 'initial-reset'This preserves the old mental model of each intensification attempt beginning
from the configured initialTemperature.
Migrate operator targeting assumptions
Older mental models often described Phase 1.5 as preferring operators whose names merely looked like repair operators. The branch API now exposes an explicit targeting surface.
intensificationTargetedOperatorNames: ['Repair hard conflict']Matching is case-insensitive, but it is still an exact name match. If you want deterministic targeting, rely on this field instead of assuming the solver will infer the right operators from substrings alone.
Migrate Phase 1.5 budget assumptions
The current implementation caps total Phase 1.5 work and can stop an attempt early when the global best hard-violation objective stalls.
intensificationBudgetFractionOfMaxIterations: 0.25,
intensificationEarlyStopNoBestImproveIterations: 800,This means intensificationIterations * maxIntensificationAttempts is no
longer the whole story. The true upper bound is the smaller of the per-attempt
settings and the global Phase 1.5 budget.
Migrate telemetry and benchmarking code
If you previously relied only on logs or onProgress to understand solver
behavior, use the new diagnostics surface for post-run analysis.
- Read
solution.diagnosticsfrom the solve result. - Call
solver.getDiagnostics()when you want a snapshot copy after the run.
This is the preferred branch-compatible way to inspect timing, first-feasible milestones, budget usage, and Phase 1.5 stop reasons.
Migrate logging setup
To log to files, set logging.output and logging.filePath explicitly.
logging: {
enabled: true,
output: 'file',
filePath: './logs/sa.log',
}The logger creates parent directories automatically.
Migrate error handling
If older integration code relied on generic catches, prefer typed catches where appropriate.
SAConfigErrorConstraintValidationErrorSolveConcurrencyError
You should also be aware that user-thrown exceptions from constraint evaluation can still propagate as plain errors.
Recommended migration sequence
Use this order to reduce migration risk.
- Define a typed
TStatethat contains the full mutable candidate solution. - Move all domain rules into
Constraintimplementations. - Implement a fast, deterministic
cloneState. - Port mutation logic into
MoveGeneratorimplementations. - Add progress callbacks and logging only after the base solve works.
- Run repeated solves and tune parameters empirically.
Next steps
Once the migration is complete:
- read API Reference to verify current contracts,
- read Configuration to tune the new runtime surface,
- read Troubleshooting if the migrated system behaves differently than expected.