This is the as-built map for the current benchmark repository. It is a
documentation contract: when source layout, benchmark ownership, run flow,
persistence, or CI changes, update this file with README.md and AGENTS.md.
pyproject.tomldeclares the Python 3.14 package, shared dependencies, and package discovery acrosssrc/,list-variable/cvrp/src/, andscalar-variable/employee-scheduling/src/, andscalar-variable/job-shop-scheduling/src/.Makefileis the root build and execution surface. It owns virtualenv setup, native adapter builds, benchmark smoke/full runs, database helpers, normalization, the SolverForge banner, and CPU-pinned harness execution.src/solverforge_bench/is the shared framework. It owns CLI parsing, TOML loading, benchmark registry, exact run matrix construction and hashing, runtime artifact provenance, timed execution, watchdog containment, row construction, CSV writing, logging, solver output capture, solver-version collection, secret-safe command metadata, ETL, and optional PostgreSQL writes.scripts/run_benchmark.py,scripts/verify_solverforge_py_guardrails.py, and benchmark-local validation scripts bootstrap throughscripts/_venv_bootstrap.pywhen they require the repository virtualenv.scripts/test_verify_solverforge_py_guardrails.pyandscripts/test_benchmark_contracts.pyandscripts/test_solver_output_contracts.pyrun through their root Make targets.list-variable/cvrp/is the list-variable benchmark package for CVRP.scalar-variable/employee-scheduling/is the scalar-variable benchmark package for INRC-II nurse scheduling.scalar-variable/job-shop-scheduling/is the scalar-variable benchmark package for classic JSPLIB job-shop scheduling.migrations/holds SQLx-compatible PostgreSQL warehouse migrations..github/workflows/ci.ymlholds the GitHub-hosted CI workflow..forgejo/workflows/ci.ymlholds the local Forgejo CI workflow.archive/holds historical reports and older standalone scripts only.
scripts/run_benchmark.pybootstraps into the root.venv, adds the four source roots tosys.path, and delegates tosolverforge_bench.cli.main.scripts/verify_model_parity.pyuses the same virtualenv bootstrap before importing the employee-scheduling package.cli.pyloads optional TOML configuration, selects the benchmark spec, applies CLI overrides, finalizes run catalog fields, validates solver names, and passes the selected spec torunner.py.registry.pyexposes the canonical benchmark specs:cvrp,employee-scheduling, andjob-shop-scheduling.runner.pymaterializes a nonempty, duplicate-free case list, positive unique time limits, unique solvers, and the complete expected Cartesian matrix. It resolves content-hashed runtime provenance before opening output, then iteratescase -> time_limit -> solverand refuses completion unless every expected key was emitted exactly once.execution.pyruns each solver in a child process. Only the instance payload and nominal time limit are passed to the solver callable; the nominal time limit is not the hard kill deadline. The watchdog only terminates runaway processes aftermax(time_limit * multiplier, time_limit + grace_seconds).- Each benchmark spec validates and evaluates returned solutions externally, then the shared runner writes an incremental CSV row and, when enabled, a PostgreSQL row. Reference solutions may be used here for scoring, not as solver starts.
- Solver exceptions, including
NoSolutionFoundError, become result rows withrun_error. CSV or PostgreSQL write failures remain fatal output-integrity failures. A SolverForge mandatory-construction stop at the requested time limit is ano_solutionrow with no objective and preserved native failure details; unrecognized SolverForge exceptions remainadapter_error. scripts/verify_solverforge_py_guardrails.pyis a release/local wrapper over the same root harness. It runs fixedsolverforge-pysmoke slices and paired native/Python comparison slices. Its one-second employee feasibility probe coversn030w4,n050w8, andn080w8and requires hard-feasible returned schedules. Before execution it resolves every requested dataset selector; afterward it requires the exact instance/time-limit/solver matrix, then writesbuild/solverforge-py-guardrails/summary.json. Database URL values are redacted from recorded commands and persisted run metadata.
| Spec | Category | Default solvers | Default time limits | Native columns |
|---|---|---|---|---|
cvrp |
list_variable |
pyvrp, ortools, vroom, timefold, rustvrp, pyhygese, solverforge, solverforge-py |
1, 10, 60 |
none |
employee-scheduling |
scalar_variable |
solverforge, solverforge-py, timefold, ortools |
1, 10, 60 |
nurses, weeks, validator_model_delta, score_drift |
job-shop-scheduling |
scalar_variable |
solverforge, solverforge-py, timefold, ortools |
1, 10, 60 |
num_jobs, num_machines, num_operations, source_family, known_best_makespan, lower_bound_makespan, upper_bound_makespan, makespan_gap_to_best |
- Data lives under
list-variable/cvrp/data/X/as CVRPLIB-X.vrpand.solpairs. spec.pyexposes datasetCVRPLIB-X, dataset setcanonical, and--num-instancesfor smoke selection.domain/models.pydefines Pydantic instance and solution contracts.domain/utils.pyvalidates route feasibility and cost.solver/solver.pyregisterspyvrp,ortools,vroom,timefold,rustvrp,pyhygese,solverforge, andsolverforge-py.- Native solver builds are rooted in
solver/ortools/,solver/rustvrp/,solver/vroom/,solver/timefold/, andsolver/solverforge/. - The SolverForge CVRP manifest and committed registry lockfile target the
published
0.19.4crates. - The CVRP model uses public SolverForge CVRP list-variable hook bundles:
VrpSolution, matrix distance meters, stock route hooks, stock savings depot/distance/metric-class hooks, and strict route feasibility for construction pruning. The benchmark budget is applied through the model config provider. - The SolverForge and Timefold CVRP list variables start from empty route lists; adapter-owned incumbents, route hints, and reference-solution reads are not part of solver input.
- The
solverforge-pyCVRP adapter builds a public Python-binding list-variable model from the same CVRPLIB instance. Its canonical0.6.6declaration uses independentListRouteHooksandListSavingsHooks, explicit row-scoped capacity/demand/distance metadata, and explicit cross/intra-position distance sources. It starts all route lists empty and reports the installedsolverforgePython distribution version. solverforge/solver.tomluses reproducible mode, seed42, a 60 second internal termination cap, list construction phases, and a local-search union of nearby list moves, reverse moves, k-opt, ruin, and limited-neighborhood sublist change moves.solverforge_py.tomlis the separate Python adapter copy of that complete policy. The adapter loads it directly and overlays only the requested termination seconds.- Native OR-Tools no-solution exits are normalized to
NoSolutionFoundErrorrather than benchmark-aborting runtime errors. - Every CVRP wrapper emits a fair-start witness before solving. The native OR-Tools and SolverForge adapters also include native witness checks in their JSON output.
- Data lives under
scalar-variable/employee-scheduling/data/inrc2/as bundled INRC-II scenario, history, week-data, and reference solution TXT files. manifest.jsondefines dataset groups:quickhas 1 group and 3 cases,test_with_solutionshas 3 groups and 9 cases,canonicalhas 14 groups and 42 cases, andlatehas 6 groups and 18 cases.loader.pyparses INRC-II TXT files and enumerates concrete cases.validation.pyis the shared Python referee. It checks hard constraints first and then computes the soft-cost breakdown.scripts/verify_model_parity.pyverifies that the Python validator, OR-Tools model, Timefold model, and SolverForge model encode the same model contract.spec.pyexposes--dataset-setand--datasets, writes solution JSON artifacts for hard-feasible runs, and reports validator/model deltas through native columns.solver/solver.pyregisterssolverforge,solverforge-py,timefold, andortools.- The SolverForge NRP manifest and committed registry lockfile target the
published
0.19.4crates withserdeenabled. - The SolverForge NRP model uses public scalar APIs: per-shift candidate
values, unassigned scalar variables for optional slots, nearby value/entity
candidates, and one
ScalarGroup::assignmentfor required minimum slots, one nurse per day capacity, adjacent forbidden-succession assignment rules, ordered shift positions, and nurse sequence keys. - SolverForge initializes each shift with
nurse_idx = None, Timefold leaves eachnurseplanning variable unset, and OR-Tools performs one CP-SAT solve without adapter hints, hard seeds, warm starts, or fallback schedules. OR-Tools uses a deterministic coverage-order, round-robin assignment strategy within that single solve and the shared time limit. - The
solverforge-pyemployee adapter builds a public Python-binding scalar model with unassignednurse_idxvariables. Immutable required, capacity, position, sequence, and same-nurse forbidden-succession conflict metadata stays in native row fields. The static conflict graph gives the assignment engine the same adjacency rule as the native Rust adapter without a Python callback on each candidate edge. Per-shift nurse candidates remain the native legality boundary for construction, swaps, and rematches, so an assignment cannot migrate onto a shift whose skill or initial-history domain excludes that nurse. Hard feasibility, indexed presence penalties, and shift-off request penalties remain in the constraint model; the shared validator remains the source of result feasibility and cost. This adapter is a first-class default performance row. - The Python wrappers emit a witness before solving. SolverForge Rust counts
preassigned scalar variables, Timefold Java counts preassigned
nurseplanning variables, and OR-Tools C++ inspects CP-SAT solution-hint fields in the native model proto. solverforge_nrp/solver.tomlsetsenvironment_mode = "non_reproducible"andrandom_seed = 1. It intentionally does not impose an independent termination cap; the shared harness passes the requested benchmark budget.solverforge_py.tomlis the separate Python adapter copy. Both files omit explicit phases so both bindings select the same model-aware default construction and local-search profile.
- Data lives under
scalar-variable/job-shop-scheduling/data/jsplib/as bundled classic JSPLIB instance files sourced fromtamy0612/JSPLIB. manifest.jsondefines dataset groups:quickhasft06andla01, andcanonicalhas all 162 bundled JSPLIB instances acrossabz,ft,la,orb,swv,ta, andyn.loader.pyparses standard JSPLIB text files with optional comments.validation.pyis the shared Python referee. It checks operation coverage, job precedence, machine non-overlap, and returned makespan.spec.pyexposes--dataset-setand--datasets, reports JSPLIB family, size, known optimum, lower/upper bounds, and makespan gap through native columns.solver/solver.pyregisterssolverforge,solverforge-py,timefold, andortools.- The SolverForge JSSP manifest and committed registry lockfile target the
published SolverForge facade, SolverForge Core, and SolverForge Scoring
0.19.4crates. - Its list model declares each operation's fixed machine owner with
element_owner_fn; SolverForge construction and list neighborhoods must not move an operation to a non-required machine. - The SolverForge JSSP score path uses the stock upstream
ListPrecedenceMakespanConstraint: job precedence is fixed precedence, each machine sequence contributes list precedence, missing/duplicate/wrong-owner assignments are hard penalties, and makespan is the soft objective. The adapter maps JSPLIB data into that generic constraint; it does not own a benchmark-local full-score search path. solverforge_jssp/solver.tomlremains a stock SolverForge selector configuration. It may choose upstream list neighborhoods, but it does not add benchmark-local solver/search helpers, config probes, warm starts, or reference-solution hints.- SolverForge and Timefold JSSP machine operation lists start empty. Known best bounds and validation data stay in specs and validators, not in solver-start incumbents.
- The
solverforge-pyJSSP adapter builds a public Python-binding list model with one empty machine sequence per machine and owner-constrained operation elements. The public first-class list precedence/makespan constraint scores operation ownership, assignment uniqueness, job precedence, machine order, and makespan; the shared validator remains the source of returned schedule feasibility and cost. This adapter is a first-class default performance row. - The JSSP wrappers emit witnesses before solving. SolverForge Rust and Timefold Java count prefilled machine lists, and OR-Tools C++ records CP-SAT solution-hint counts from the model proto.
solverforge_jssp/solver.tomland the separatesolverforge_py.tomlboth setrandom_seed = 1and omit explicit phases so native and Python use the same model-aware default construction and local-search profile.- SolverForge Rust, SolverForge Python, and Timefold reject incomplete, duplicated, wrong-owner, or cyclic JSSP structure. They never replace an unknown operation start with zero.
- Both SolverForge CVRP adapters require every customer exactly once. Both
employee-scheduling adapters require every callback-required shift while
preserving optional unassigned shifts. Native adapters accept a solution
only from
Completed, never from a prior best after cancellation.
make install-python-depscreates or refreshes the root.venvand is configured to install the exact publishedsolverforge==0.6.6wheel.make build-cvrpbuilds Python dependencies plus CVRP Timefold, SolverForge, OR-Tools, rustvrp, and VROOM integrations.make build-employee-schedulingbuilds Python dependencies plus employee Timefold, SolverForge, and OR-Tools integrations.make build-job-shop-schedulingbuilds Python dependencies plus job-shop Timefold, SolverForge, and OR-Tools integrations.make verify-fair-startenforces that active solver adapters start from unassigned scalar variables or empty list variables, emit runtime witnesses, and do not read reference solutions or inject adapter-owned incumbents.make verify-benchmark-contractsexercises exact matrix completion, runtime provenance, and all SolverForge output-completeness boundaries without running a full benchmark suite.make verify-fair-start-rows RUN_ID=<uuid>checks persisted PostgreSQL rows for valid fair-start witnesses after a DB smoke run.make verify-solverforge-config-parityparses each separate native/Python TOML pair, requires semantic equality, and pins the result to the qualified strongest-policy hash so jointly weakening both copies also fails.make verify-stock-solverforge-guardrailsbuilds the active native adapters, runs stock SolverForge guardrail benchmarks, and parses the resulting CSVs. JSSP quick plus the fixed canonical subset must produce hard-feasible SolverForge rows with valid fair-start witnesses; CVRP and employee SolverForge smoke rows must remain hard-feasible with valid fair-start witnesses. Pass--require-jssp-winthroughGUARDRAIL_ARGSwhen the run should enforce that SolverForge ties or beats the best feasible JSSP solver row.make verify-solverforge-py-smokebuilds the native SolverForge adapters, verifies fair-start source checks, runssolverforge-pysmoke rows through the shared harness, and parses the generated CSVs. Employee rows check fair-start, runtime, version, and score-reporting integrity while accepting an honest infeasible or no-incumbent result. Its production-scale one-second feasibility probe coversn030w4,n050w8, andn080w8and requires every row to return a hard-feasible schedule without a validation error.make verify-solverforge-py-guardrail-contractruns the focused exact-matrix and command-redaction regression suite without invoking solvers.make verify-solverforge-py-comparisonruns paired nativesolverforgeandsolverforge-pyrows through the shared harness and records parsable quality and wall-time summaries without PostgreSQL by default. Employee infeasible and no-incumbent rows remain honest outcomes, not gate failures.make verify-solverforge-py-releasecombines compileall, benchmark validators, fair-start checks, native adapter builds, smoke, and paired comparison into one release-mode guardrail invocation and summary.make bench-cvrp-quickruns three CVRP instances at 1 and 10 seconds with all registered CVRP solvers.make bench-cvrp-quick-dbruns the same CVRP smoke path after applying migrations and persists it to PostgreSQL.make bench-cvrp-solverforge-quickruns the same quick CVRP slice with only SolverForge.make bench-cvrp-solverforge-quick-dbpersists that same SolverForge-only CVRP smoke path after applying migrations.make bench-employee-scheduling-quickrunsn005w4at 1 and 10 seconds with all registered employee-scheduling solvers.make bench-employee-scheduling-quick-dbruns the same employee smoke path after applying migrations and persists it to PostgreSQL.make bench-employee-scheduling-solverforge-quickruns the same quick employee slice with only SolverForge.make bench-employee-scheduling-solverforge-quick-dbpersists that same SolverForge-only employee smoke path after applying migrations.make bench-job-shop-scheduling-quickrunsft06andla01at 1 and 10 seconds with all registered job-shop solvers.make bench-job-shop-scheduling-quick-dbruns the same job-shop smoke path after applying migrations and persists it to PostgreSQL.make bench-job-shop-scheduling-solverforge-quickruns the same quick job-shop slice with only SolverForge.make bench-job-shop-scheduling-solverforge-quick-dbpersists that same SolverForge-only job-shop smoke path after applying migrations.make bench-cvrp,make bench-employee-scheduling, andmake bench-job-shop-schedulingrun canonical benchmark paths at 1, 10, and 60 seconds.make bench-nightly-dbbuilds all benchmark stacks, applies migrations once, and invokes the root harness for CVRP, employee scheduling, and job-shop scheduling in parallel on their per-suite pinned cores.make db-check,make db-create,make db-migrate, andmake db-resetoperate onDATABASE_URL, thenBENCH_DATABASE_URL, thenpostgresql://postgres@localhost/solverforge_bench.make normalize-resultsconverts generated global CSV artifacts to normalized CSV or NDJSON throughscripts/normalize_results.py.- Benchmark run targets use per-suite pinned cores:
CVRP_BENCH_CPU ?= 0,EMPLOYEE_BENCH_CPU ?= 1, andJOBSHOP_BENCH_CPU ?= 2. They setOMP_NUM_THREADS=1,MKL_NUM_THREADS=1, and per-coreBENCH_LOCKvalues so different suites can run in parallel while accidental same-core runs serialize.BENCH_CPU=<n>intentionally forces all suites onto one core. - Override child harness arguments with
BENCH_ARGS, nightly child arguments withNIGHTLY_ARGS, config path withBENCH_CONFIG, and SQLx reset flags withDB_RESET_FLAGS.
- Root TOML keys are
benchmark,solver,time_limits,wall_time_tolerance,watchdog_multiplier,watchdog_grace_seconds,output,run_kind,nightly, andrelease_tag. [postgres]acceptssaveandurl.[logging]acceptslevel,dir,file,show_solver_output, andcapture_solver_output.[benchmarks.cvrp]acceptsnum_instances.[benchmarks.employee-scheduling]acceptsdataset_setanddatasets.[benchmarks.job-shop-scheduling]acceptsdataset_setanddatasets.run_kindis one ofquick,candidate, ortag;tagrequiresrelease_tag.- A CLI
--postgres-urlenables PostgreSQL persistence unless--no-save-postgresis also supplied. A TOML PostgreSQL URL alone does not.
- CSV output uses one global snake_case schema with stable optional native columns.
BenchmarkRow.as_dict()emits core fields and merges native fields.- CVRP output defaults to
list-variable/cvrp/data/benchmark_cvrp_<stamp>.csv. - Employee scheduling output defaults to
scalar-variable/employee-scheduling/data/benchmark_employee_scheduling_<stamp>.csv. - Job-shop scheduling output defaults to
scalar-variable/job-shop-scheduling/data/benchmark_job_shop_scheduling_<stamp>.csv. - SolverForge-Py guardrail CSVs, logs, and
summary.jsonare written underbuild/solverforge-py-guardrails/by the wrapper. Benchmark-local solution artifact writing remains shared harness behavior and uses the spec artifact directories. - Employee scheduling solution artifacts are written under
scalar-variable/employee-scheduling/data/artifacts/employee_scheduling_<stamp>/. - Job-shop scheduling solution artifacts are written under
scalar-variable/job-shop-scheduling/data/artifacts/job_shop_scheduling_<stamp>/. - PostgreSQL stores run catalog rows in
benchmark_runs, expected keys inbenchmark_run_matrix_entries, solver-version rows inbenchmark_solver_versions, and result rows inbenchmark_results. benchmark_result_facts,latest_benchmark_runs, andlatest_benchmark_result_factsare diagnostic warehouse views.benchmark_run_publication_auditexplains every rejected run.publishable_benchmark_runs,publishable_benchmark_result_facts, and thelatest_publishable_*views are the only public publication surface.- Completed result rows are written immediately. Interrupted runs keep partial rows but are excluded from latest-run views unless their run status is completed.
- Python CI runs on
ubuntu-latestin.github/workflows/ci.ymland onpythonrunner labels in.forgejo/workflows/ci.yml. - Rust CI runs on
ubuntu-latestin.github/workflows/ci.ymland onrustrunner labels in.forgejo/workflows/ci.yml. - Python CI uses Python 3.14, creates the root
.venvthroughmake install-python-deps HOST_PYTHON=..., compiles Python source, parses benchmark TOML examples, verifies separate SolverForge config parity, the shared matrix/provenance contract, and the Python guardrail regression contract, validates bundled CVRP instances, and validates employee model parity. GitHub usesactions/setup-python@v6; Forgejo uses a shell Python 3.14 bootstrap because the local Forgejo action mirror does not provide that interpreter version. - Rust CI preserves strict
--lockedresolution. Adapter manifests and committed locks target SolverForge0.19.4. CI checks formatting, runscargo clippy --locked --all-targets -- -D warnings, and runscargo build --lockedfor the CVRP SolverForge adapter, CVRP rustvrp adapter, employee SolverForge adapter, and job-shop SolverForge adapter.
- The ignored build and output surfaces are
.venv/,__pycache__/, Python package/build outputs, Rust/Javatarget/outputs,logs/*, generated benchmark CSVs,build/solverforge-py-guardrails/, and generated solution artifact directories. - Generated CSVs are evidence artifacts. Commit them only when the run output is intentionally part of the change.