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)
}

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?

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, unified join(...), 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.toml loading plus SolverConfig::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 candidates instead of generic values. List-heavy solves report element counts, and console output labels those solve shapes as candidates or elements.

  • Optional FirstFit keeps None as a real baseline: optional scalar construction now leaves a value unassigned unless a concrete assignment is strictly better, matching the current CheapestInsertion semantics while preserving eager FirstFit search order.
  • Accepted-count local search retains the best accepted moves: accepted_count_limit caps 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_neighborhood carries move caps at the neighborhood level, and ChangeMoveSelector keeps 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 displayed moves/s metric 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


Technical Details

Architecture (for the curious) SolverForge is a **native Rust constraint solver** that delivers both developer ergonomics and high performance: ``` ┌─────────────────────────────────────────────────────────────────┐ │ solverforge │ │ (facade + re-exports) │ └─────────────────────────────────────────────────────────────────┘ │ │ │ ▼ ▼ ▼ ┌──────────────┬──────────────┬──────────────┐ │solverforge- │solverforge- │solverforge- │ │ solver │ scoring │ config │ │ │ │ │ │ • Phases │ • Constraint │ • TOML │ │ • Moves │ Streams │ • YAML │ │ • Selectors │ • Score │ • Builders │ │ • Foragers │ Directors │ │ │ • Acceptors │ • SERIO │ │ │ • Termination│ Engine │ │ │ • Manager │ │ │ └──────────────┴──────────────┴──────────────┘ │ │ └──────┬───────┘ ▼ ┌──────────────────────────────┐ │ solverforge-core │ │ │ │ • Score types │ │ • Domain traits │ │ • Descriptors │ │ • Variable system │ └──────────────────────────────┘ │ ▼ ┌──────────────────────────────┐ │ solverforge-macros │ │ │ │ • #[planning_solution] │ │ • #[planning_entity] │ │ • #[problem_fact] │ └──────────────────────────────┘ ``` **Why this design?** 1. **Zero-cost abstractions** — Rust's type system eliminates runtime overhead. Constraint streams compile to efficient machine code with no dynamic dispatch. 2. **Incremental scoring (SERIO)** — The Scoring Engine for Real-time Incremental Optimization only recalculates affected constraints when moves are evaluated, delivering 10-100x speedups. 3. **Type-safe moves** — `ChangeMove<S, V>` and `SwapMove<S, V>` store values inline without boxing or heap allocation. Arena allocation provides O(1) per-step cleanup. 4. **No garbage collection** — Predictable, low-latency performance without GC pauses. 5. **Modular architecture** — Each crate has a single responsibility, making the codebase maintainable and testable. **The result:** You write declarative constraint logic that compiles to highly optimized native code.
What's implemented **Repository**: [solverforge/solverforge](https://github.com/solverforge/solverforge) **Core solver features:** - **Score types**: SoftScore, HardSoftScore, HardMediumSoftScore, HardSoftDecimalScore, BendableScore - **Domain model**: Derive macros for `#[planning_solution]`, `#[planning_entity]`, `#[problem_fact]` - **Variable types**: Genuine, shadow, list variables - **Shadow variables**: `#[inverse_relation_shadow_variable]`, `#[previous_element_shadow_variable]`, `#[next_element_shadow_variable]` - **Constraint Streams API**: `for_each`, generated collection accessors, unified `join`, `flatten_last`, `group_by`, `balance`, `if_exists(...)`, `if_not_exists(...)`, `penalize`, `reward`, and `.named()` - **Grouped helpers**: `count`, `sum`, and `load_balance` - **Score analysis**: `ScoreAnalysis`, `ConstraintAnalysis`, `ScoreExplanation`, `IndictmentMap` **Solver phases and runtime:** - **Construction heuristics**: first fit, weakest fit, strongest fit, queue allocators, cheapest insertion, and list-specific constructors - **Local search**: hill climbing, simulated annealing, tabu search, late acceptance, great deluge - **Exhaustive search**: branch and bound and brute force - **Partitioned search**: Multi-threaded parallel solving - **VND**: Variable Neighborhood Descent **Move system:** - Basic: ChangeMove, SwapMove, CompositeMove - List: ListChangeMove, ListSwapMove, ListReverseMove, SubListChangeMove, SubListSwapMove - Advanced: KOptMove, RuinMove, PillarChangeMove, PillarSwapMove - MoveArena: Zero-allocation move storage **Infrastructure:** - **SERIO**: Scoring Engine for Real-time Incremental Optimization - **SolverManager**: Retained job lifecycle API with event streaming, snapshots, and pause/resume control - **Configuration**: stock `solver.toml` loading plus TOML/YAML parsing APIs - **Termination**: Time limits, step counts, score targets, unimproved step detection, composites (And/Or) - **Nearby selection**: Distance-based move selection **Performance:** - Zero-allocation move system with arena allocation - Type-safe moves without boxing (`ChangeMove<S, V>`, `SwapMove<S, V>`) - No garbage collection pauses - Incremental score calculation (10-100x faster than full recalculation)