Documentation
What SolverForge is, how it differs from mathematical solvers, and the project roadmap.
What is SolverForge?
SolverForge is a constraint satisfaction solver for real-world planning and scheduling problems. It helps you assign resources to tasks while respecting business rules and optimizing for your goals.
What Problems Does It Solve?
SolverForge excels at combinatorial planning problems — problems where a brute-force search is impossible (millions to billions of possibilities), but a good solution dramatically improves efficiency.
Hospital Scheduling
Assign hospital staff to shifts based on skills, availability, and labor regulations.
Vehicle Routing
Plan delivery routes that minimize travel time while meeting time windows.
School Timetabling
Schedule lessons to rooms and timeslots without conflicts.
Task Assignment
Allocate jobs to workers or machines optimally.
Meeting Scheduling
Find times and rooms that work for all attendees.
Bin Packing
Fit items into containers efficiently.
How Is This Different from Gurobi or CVXPY?
This is a common question. SolverForge and mathematical programming solvers (Gurobi, CPLEX, OR-Tools, CVXPY) solve different kinds of problems using different approaches.
| SolverForge | Mathematical Solvers (Gurobi, CVXPY) | |
|---|---|---|
| Problem type | Constraint satisfaction & scheduling | Linear/mixed-integer programming |
| Modeling approach | Business objects with rules | Mathematical equations & matrices |
| Constraints | Natural language-like rules on objects | Linear inequalities (Ax ≤ b) |
| Best for | Scheduling, routing, assignment | Resource allocation, network flow, portfolio optimization |
| Developer experience | Write rules about “Shifts” and “Employees” | Formulate objective functions and constraint matrices |
A Concrete Example
use solverforge::prelude::*;
use solverforge::stream::{joiner::*, ConstraintFactory};
fn define_constraints() -> impl ConstraintSet<Plan, HardSoftDecimalScore> {
use PlanConstraintStreams;
use ShiftUnassignedFilter;
let factory = ConstraintFactory::<Plan, HardSoftDecimalScore>::new();
let unassigned = factory.clone()
.shifts()
.unassigned()
.penalize(HardSoftDecimalScore::of_hard_scaled(100_000))
.named("Unassigned shift");
let missing_skill = factory
.shifts()
.filter(|shift: &Shift| shift.employee_idx.is_some())
.join((
Plan::employees_slice,
equal_bi(
|shift: &Shift| shift.employee_idx,
|employee: &Employee| Some(employee.index),
),
))
.filter(|shift: &Shift, employee: &Employee| {
!employee.skills.contains(&shift.required_skill)
})
.penalize(HardSoftDecimalScore::of_hard_scaled(1_000_000))
.named("Missing skill");
(unassigned, missing_skill)
}
# You must translate your problem into mathematical form
x = model.addVars(employees, shifts, vtype=GRB.BINARY)
model.addConstrs(sum(x[e,s] for e in employees) == 1 for s in shifts)
model.addConstrs(sum(x[e,s] for s in shifts) <= max_shifts for e in employees)
The key difference: With SolverForge, you work with domain objects (Shift,
Employee) and express constraints as natural business rules. You don’t need to
reformulate your problem as a system of linear equations.
When to Use Each
Use SolverForge when:
- Your problem involves scheduling, routing, or assignment
- Constraints are naturally expressed as business rules
- The problem structure doesn’t fit neatly into linear programming
- You want readable, maintainable constraint definitions
Use Gurobi/CVXPY when:
- Your problem is naturally linear or convex
- You need provably optimal solutions with bounds
- The problem fits the mathematical programming paradigm (LP, MIP, QP)
The Developer Experience
SolverForge provides a Rust derive-macro API for ergonomic domain modeling:
use solverforge::prelude::*;
#[planning_entity]
pub struct Shift {
#[planning_id]
pub id: String,
pub required_skill: String,
#[planning_variable(value_range = "employees", allows_unassigned = true)]
pub employee_idx: Option<usize>,
}
#[planning_solution(constraints = "crate::constraints::define_constraints")]
pub struct Plan {
#[problem_fact_collection]
pub employees: Vec<Employee>,
#[planning_entity_collection]
pub shifts: Vec<Shift>,
#[planning_score]
pub score: Option<HardSoftDecimalScore>,
}
You define your domain model with derive macros and attribute annotations. The solver figures out how to assign employees to shifts while respecting your constraints.
Project Status & Roadmap
Current Status
| Component | Status | Description |
|---|---|---|
| Rust Core | ✅ Production-ready | Native Rust constraint solver with the current runtime surface |
Want to try it today?
- Start with solverforge-cli Getting Started for the generic app shell, then continue with the SolverForge Hospital Use Case
What’s Complete
SolverForge Rust is feature-complete as a production constraint solver:
- Constraint Streams API: Declarative constraint definition with
for_each, generated collection accessors,filter, unifiedjoin(...),flatten_last,group_by,balance,if_exists(...),if_not_exists(...),penalize,reward, and.named(...) - Score Types: SoftScore, HardSoftScore, HardMediumSoftScore, HardSoftDecimalScore, BendableScore
- Score Analysis:
ScoreAnalysis,ConstraintAnalysis,ScoreExplanation,IndictmentMap - SERIO Engine: Scoring Engine for Real-time Incremental Optimization
- Solver Phases:
- Construction Heuristics for scalar and list-variable models
- Local Search with Hill Climbing, Simulated Annealing, Tabu Search, Late Acceptance, and Great Deluge in the stock config surface
- Exhaustive Search (
branch_and_bound,brute_force) - Partitioned Search (multi-threaded)
- VND (Variable Neighborhood Descent)
- Move System: Zero-allocation move types with arena allocation — Change, Swap, Composite, ListChange, ListSwap, ListReverse, SubListChange, SubListSwap, KOpt, ListRuin, Ruin, PillarChange, PillarSwap
- List Variables: Full support for sequencing/routing problems
- Nearby Selection: Distance-based move selection for large problems
- Balance stream: Load-balancing constraint support without manual grouped unfairness scoring
- SolverManager API: Retained job lifecycle with
SolverEvent::{Progress, BestSolution, PauseRequested, Paused, Resumed, Completed, Cancelled, Failed},SolverStatus, exact in-process pause/resume checkpoints, retained snapshots, snapshot-bound analysis, terminal-job deletion, and exact retained telemetry - Configuration: stock
solver.tomlloading plusSolverConfig::load(),from_toml_str(),from_yaml_str(), and#[planning_solution(config = "...")]overlays that decorate the loaded runtime config
Runtime Notes
-
Shape-aware startup telemetry: startup logging now labels scalar solve scale as average
candidatesinstead of genericvalues. List-heavy solves report element counts, and console output labels those solve shapes ascandidatesorelements. - Optional
FirstFitkeepsNoneas a real baseline: optional scalar construction now leaves a value unassigned unless a concrete assignment is strictly better, matching the currentCheapestInsertionsemantics while preserving eagerFirstFitsearch order. - Accepted-count local search retains the best accepted moves:
accepted_count_limitcaps the retained accepted candidates for final selection instead of acting as an implicit early-exit threshold. - Canonical construction engine: generic runtime construction now centers on
the shared engine under
phase/construction/engine.rs; pure scalar matches reuse the descriptor-scalar path, while round-robin list construction uses one shared implementation for runtime and builder assembly. - Tighter neighborhood iteration:
limited_neighborhoodcarries move caps at the neighborhood level, andChangeMoveSelectorkeeps change-value iteration lazy so cursor caps and early-stop paths avoid unnecessary candidate generation. - Exact retained telemetry remains authoritative: generated, evaluated, and
accepted counts plus generation/evaluation
Durations are retained exactly through the solver pipeline. Any displayedmoves/smetric is still derived at the edge.
Roadmap
Phase 1: Native Solver ✅ Complete
Built a complete constraint solver in Rust from the ground up:
- Full metaheuristic algorithm suite
- Incremental scoring engine (SERIO)
- Zero-cost abstractions with inline move types
- Derive macros for ergonomic domain modeling
Phase 2: Rust API Refinement & Production Enhancements (H1 2026)
- Multi-threaded move evaluation
- Constraint strength system
- Performance tuning guides
- Enterprise features
Phase 3: Python Bindings (H2 2026)
Bringing the Rust solver to Python developers via PyO3:
- Native extension:
pip install solverforge - Pythonic API backed by the Rust core
- Native performance without JVM overhead
How You Can Help
- Get started — Follow the getting started guides and share feedback
- Report issues — Found a bug or have a suggestion? Open an issue
- Contribute — PRs welcome! Check the issue tracker for good first issues
- Spread the word — Star the GitHub repo and share with colleagues