Documentation
Install SolverForge Python, define a minimal planning model, solve it, and pass runtime config.
Python Getting Started
This guide installs SolverForge Python, defines a small shift-assignment model, solves it, and shows the equivalent config forms.
Prerequisites
- CPython 3.14
- Rust 1.95.0 only if pip needs to build from source
Create an isolated environment:
python3.14 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install solverforge
python - <<'PY'
import solverforge
print(solverforge.__version__)
PY
The version printed for the current package should be 0.4.0.
A Minimal Model
The model below assigns nurses to required shifts. It uses one scalar planning
variable, one hard constraint, and the built-in HardSoftScore family.
from solverforge import (
ConstraintFactory,
HardSoftScore,
Solver,
constraint_provider,
planning_entity,
planning_solution,
planning_variable,
)
@planning_entity
class Shift:
nurse = planning_variable(
value_range_provider="nurses",
allows_unassigned=True,
)
def __init__(self, required: bool = True, nurse: int | None = None) -> None:
self.required = required
self.nurse = nurse
@constraint_provider
def constraints(factory: ConstraintFactory):
return [
factory.for_each(Shift)
.filter(lambda shift: shift.required and shift.nurse is None)
.penalize(HardSoftScore.ONE_HARD)
.named("required shift is unassigned")
]
@planning_solution(score=HardSoftScore, constraints=constraints)
class Schedule:
shifts: list[Shift]
def __init__(self, shifts: list[Shift], nurses: list[int]) -> None:
self.shifts = shifts
self.nurses = nurses
self.score = None
schedule = Schedule([Shift(), Shift()], [0, 1])
solved = Solver.solve(schedule)
print(solved.score)
print([shift.nurse for shift in solved.shifts])
Solver.solve(...) reads the Python object graph, runs SolverForge, then writes
the solved variables and score back to a Python solution object.
Analyze A Solution
Use Solver.analyze(...) when you want to calculate the score for an existing
solution without running a search.
schedule = Schedule([Shift(nurse=None)], [0])
analyzed = Solver.analyze(schedule)
print(analyzed.score)
Pass Runtime Config
You can pass config as a SolverConfig, as a dictionary, or through a
solver.toml file in the current directory.
from solverforge import SolverConfig
config = SolverConfig(seconds_spent_limit=2, random_seed=7)
solved = Solver.solve(schedule, config=config)
The equivalent dictionary shape is:
solved = Solver.solve(
schedule,
config={
"random_seed": 7,
"termination": {"seconds_spent_limit": 2},
},
)
The equivalent solver.toml is:
random_seed = 7
[termination]
seconds_spent_limit = 2
When the config argument is None, Solver.solve(...) and SolverManager
load solver.toml from the current directory if it exists.
Next Steps
- Use Modeling for decorators, scalar variables, list variables, and score family selection.
- Use Constraints for joins, grouping, balance scoring, and callback weights.
- Use Solving & Runtime for retained jobs and dynamic selector configuration.
- Use Hospital Example for the larger FastAPI demo.