Dynamic Python bindings for the SolverForge constraint solver.
Project description
SolverForge Python
solverforge is the dynamic Python binding package for SolverForge. Python
users define models with Python classes, decorators, functions, and lambdas.
They do not write Rust and they do not pass JSON to a fixed backend.
The native extension owns the working solution state in Rust so SolverForge can clone, mutate, and snapshot solutions safely. Python callbacks are the single constraint authoring surface.
The package targets CPython 3.14 and Rust 1.95.0. The PyPI package starts at
solverforge 0.4.0 for this architecture and intentionally supersedes the
older incompatible 0.2.x and 0.3.0 artifacts in the same PyPI namespace.
Those older artifacts exposed SolverFactory, PlanningVariable, Java service
requirements, and other APIs that are not part of this package.
Installation
python3.14 -m pip install solverforge
The installable wheel contains the core solverforge package and native
extension. Source-checkout examples, including the hospital FastAPI app and its
static assets, are maintained in this repository rather than installed into the
runtime wheel.
Local development is driven by the root Makefile, which creates .venv,
installs maturin and developer tools, and builds the PyO3 extension against the
current checkout.
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
solution = Solver.solve(Schedule([Shift(), Shift()], [0, 1]))
Development
make develop # release native extension installed into .venv
make test # cargo test plus pytest
make lint # rustfmt check, ruff, mypy, and clippy
make ci-local # local CI simulation
make pre-release # ci-local plus release sdist/wheel checks
Run make help for focused targets such as make test-hospital,
make test-one TEST=pattern, make hospital-run, and make hospital-solve.
Boundaries
- No generated Rust.
- No expression-object DSL.
- No string-parsed constraints.
- No private upstream SolverForge modules; bindings use the public dynamic bridge contract.
- No fixed pre-modeled JSON-only backends.
Current Support
Solver.solve(..., config=None)andSolverManager(config=None)load a user-spacesolver.tomlfrom the current directory when present. ExplicitSolverConfigordictconfigs are normalized before Rust handoff, and top-level plus phase-level termination fields match upstream SolverForge:seconds_spent_limit,minutes_spent_limit,best_score_limit,step_count_limit,unimproved_step_count_limit, andunimproved_seconds_spent_limit.- Synchronous and retained scalar/list construction solves use upstream SolverForge.
SolverManageris backed by upstream retained jobs, statuses, events, and snapshots, including pause, resume, cancel, delete, and exact snapshot reads.- Supported score families are
SoftScore,HardSoftScore,HardSoftDecimalScore, andHardMediumSoftScore. - Python callback constraints are evaluated from Rust-owned dynamic state.
Supported stream shapes are unary
for_each(...).filter(...), binaryfor_each(...).join(...).filter(...), and grouped-countfor_each(...).group_by(...), plusfor_each(...).balance(...), with fixed or callback-computed score weights.joiner.equal(...)andjoiner.equal_bi(...)preserve Python equality semantics. - Dynamic scalar local search is available through upstream-style dynamic
change_move_selector,swap_move_selector,nearby_change_move_selector,nearby_swap_move_selector,pillar_change_move_selector,pillar_swap_move_selector,ruin_recreate_move_selector,grouped_scalar_move_selector,conflict_repair_move_selector, andcompound_conflict_repair_move_selectorphases. - Dynamic list local search is available through upstream-style
list_change_move_selector,nearby_list_change_move_selector,list_swap_move_selector,nearby_list_swap_move_selector,sublist_change_move_selector,sublist_swap_move_selector,list_reverse_move_selector,k_opt_move_selector, andlist_ruin_move_selectorphases. limited_neighborhood,union_move_selector, and two-childcartesian_product_move_selectorcompose supported dynamic scalar and list selectors.examples/solverforge_hospitalis a 1:1 Python model of the Rust hospital use case's public dataset/config surface:LARGEhas 50 employees, 688 shifts, retained jobs, snapshots, analysis, pause/resume/cancel, and a 30-second hard-feasible terminal solve when installed with the release native extension.- Top-level
ConstraintFactory.join,group_by,if_exists,if_not_exists, andflattenedremain explicit unsupported methods until the native callback stream planner has public bridge support for those top-level semantics. Use stream-levelfor_each(...).join(...)andfor_each(...).group_by(...)for the supported join and grouped-count surfaces.
Documentation Map
WIREFRAME.mdis the as-built public API, runtime, and hospital UI map.AGENTS.mdis the contributor guide and agent scope contract.docs/upstream-contract.mdrecords the public SolverForge bridge assumptions.docs/dynamic-move-parity-plan.mdtracks implemented dynamic selector parity.
Rust macro-generated SolverForge models remain the performance ceiling. The Python path preserves the Rust solver engine and Rust-owned state while paying the honest dynamic callback boundary cost.
License
SolverForge Python is licensed under the Apache License, Version 2.0. See
LICENSE for the full license text.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file solverforge-0.4.0.tar.gz.
File metadata
- Download URL: solverforge-0.4.0.tar.gz
- Upload date:
- Size: 756.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
88f19a1f85e01b1e460c44585d4124fc18d3aed58c2b5ed45104e0b6e2b2d737
|
|
| MD5 |
8285ff17f362c54c65fc624df8409987
|
|
| BLAKE2b-256 |
4d21beee47335295f7088c6bf463317ee61301524434fc2e825f78fd8a074c96
|
File details
Details for the file solverforge-0.4.0-cp314-cp314-win_amd64.whl.
File metadata
- Download URL: solverforge-0.4.0-cp314-cp314-win_amd64.whl
- Upload date:
- Size: 1.6 MB
- Tags: CPython 3.14, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
86a64d45ab2a25b07618b9aa27e769fe9acd6f7d3d529dcf8caf86c31ab68f02
|
|
| MD5 |
32c89b7b8d849b7c51c15eeb23d55451
|
|
| BLAKE2b-256 |
1f2dccb9585912ccd67877bde6a668bc2b9c86ea1612f3810698913be03b648c
|
File details
Details for the file solverforge-0.4.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: solverforge-0.4.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 1.7 MB
- Tags: CPython 3.14, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a5596e912ce68c531e0d6adc60a9d67a90f722b48c5bf61ded3f36f793e0682
|
|
| MD5 |
70d899406a1b4dd6ca970f9cbf27889c
|
|
| BLAKE2b-256 |
9cede5aad4ce781be2246b6192f67edb2b1dec1d94d66db1e9f62af51543225f
|
File details
Details for the file solverforge-0.4.0-cp314-cp314-macosx_11_0_arm64.whl.
File metadata
- Download URL: solverforge-0.4.0-cp314-cp314-macosx_11_0_arm64.whl
- Upload date:
- Size: 1.5 MB
- Tags: CPython 3.14, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ffcec30b788ea0ac5f39c75fbdd966ff1bce331893b1f511de51b6d539c0ef06
|
|
| MD5 |
4bdcecf9bcea82565a19cdde0ef7b6ae
|
|
| BLAKE2b-256 |
3edb9c164b7b550d6540ac3ef7871760a15c9a06d83ce45459111c79e176a1ae
|