Benchmarking Provers¶
A benchmark runs a set of provers across a set of proof tasks. This guide covers the dataset format, how to download datasets, and how to run a benchmark with both the Python API and the CLI.
Prerequisites¶
Dataset¶
A benchmark is just a directory of Lean tasks. OpenATP bundles several public proof-synthesis benchmarks (see Datasets). You can also create your own benchmarking datasets.
Dataset structure¶
A dataset is a directory containing proof tasks in one of three styles:
A single
.leanfile.A subdirectory containing multiple
.leanfiles (considered a single task).A complete lake project (contains
lean-toolchain,lakefile.toml, andlake-manifest.json).
A single dataset can contain a mix of these styles. The name of the task is derived from the file or subdirectory name. The example dataset below contains four tasks: single_1, single_2, multi_file, and full_project.
benchmark/
├── single_1.lean # single file → task "single_1"
├── single_2.lean # single file → task "single_2"
├── multi_file/ # multiple files → task "multi_file"
│ ├── Defs.lean
│ └── Problem.lean
└── full_project/ # complete lake project → task "full_project"
├── lean-toolchain
├── lakefile.toml
├── lake-manifest.json
└── FullProject/
└── Problem.lean
Loading a dataset¶
Datasets are loaded with tasks_from_dir(), which returns a mapping of task names to ProofTask objects. We can load the example dataset above with:
from open_atp.benchmark import tasks_from_dir
tasks = tasks_from_dir("benchmark")
list(tasks.keys())
>>> ['single_1', 'single_2', 'multi_file', 'full_project']
Warning
Single and multi-file tasks are loaded with create_project() which uses a lake project skeleton pinned to v4.28.0 of the Lean toolchain and Mathlib by default. If the dataset requires different versions, supply the skeleton argument to tasks_from_dir() which forwards to create_project().
Downloading a dataset¶
Common proof-synthesis benchmarks can be downloaded with download_dataset(). The available datasets are listed in Datasets. The following example downloads the FATE-M [4] dataset into datasets/fate-m:
from open_atp.benchmark import DATASET, download_dataset
src = download_dataset(DATASET.FATE_M, "datasets")
You can also use the CLI:
open-atp download fate-m datasets
The FATE-M dataset is a flat directory of single .lean files.
datasets/fate-m
├── 1.lean
├── 2.lean
├── 3.lean
├── ...
└── 100.lean
Benchmark with the Python API¶
Use run_benchmark() to run a set of provers across a set of tasks. It takes a mapping of task names to ProofTask objects, a mapping of prover names to AutomatedProver objects, and an output directory.
This example runs the Claude Code, Codex, and OpenCode provers across the FATE-M dataset.
from pathlib import Path
from open_atp.backends.docker import DockerBackend
from open_atp.benchmark import DATASET, download_dataset, run_benchmark, tasks_from_dir
from open_atp.config import standard_prover
# Download the FATE-M dataset and load the tasks
src = download_dataset(DATASET.FATE_M, "datasets")
tasks = tasks_from_dir("datasets/fate-m")
# Define the provers to benchmark
backend = DockerBackend()
provers = {
name: standard_prover(name, backend=backend)
for name in ("claude", "codex", "deepseek")
}
# Run the benchmark
result = run_benchmark(tasks, provers, Path("runs/fate-m"))
The returned BenchmarkResult collects a list of BenchmarkRun objects which combine each (task, prover) pair with its ProofResult. The output directory contains a subdirectory <task>/<prover> for each pair with the same structure as a single run (see Prover output).
Use only to restrict to a subset of the tasks and max_workers to control concurrency. See the API Reference for the full signature of run_benchmark().
result = run_benchmark(
tasks, provers, Path("runs/fate-m"),
only=["1", "2", "3"],
max_workers=10,
)
Benchmark with the CLI¶
The open-atp benchmark command is a thin shell over the same API. Provide a path to the dataset, an output destination for the result, and optionally a list of provers to run.
Benchmark standard provers¶
We can reproduce the above Python API example with the following CLI command. We provide a list of standard provers to --provers, set the compute backend to Docker with --compute, restrict to the first three tasks with --tasks, and finally set --workers to 10 to allow up to 10 concurrent runs.
$ open-atp benchmark datasets/fate-m runs/fate-m \
--provers claude,codex,deepseek \
--compute docker \
--tasks 1,2,3 \
--workers 10
╭──────┬──────────┬────────┬─────────┬──────╮
│ task │ prover │ status │ cost │ time │
├──────┼──────────┼────────┼─────────┼──────┤
│ 1 │ claude │ ✓ │ $0.4703 │ 150s │
│ 1 │ codex │ ✓ │ $0.6480 │ 140s │
│ 1 │ deepseek │ ✓ │ $0.2257 │ 129s │
│ 2 │ claude │ ✓ │ $0.5292 │ 141s │
│ 2 │ codex │ ✓ │ $0.7480 │ 139s │
│ 2 │ deepseek │ ✓ │ $0.1336 │ 135s │
│ 3 │ claude │ ✓ │ $0.2901 │ 153s │
│ 3 │ codex │ ✓ │ $0.4865 │ 147s │
│ 3 │ deepseek │ ✓ │ $0.2048 │ 139s │
╰──────┴──────────┴────────┴─────────┴──────╯
Using a YAML config¶
It is often useful to configure a benchmark with a YAML file. The YAML configuration supports the same keys as the CLI flags: provers, tasks, compute, and workers. The provers key accepts a list of standard prover names or a custom prover configuration can be supplied. The below example uses the OpenCode standard prover and two custom agent provers that override the default model and effort level.
# config.yaml
compute: docker
workers: 10
tasks: [1, 2, 3]
provers:
- type: agent
harness:
type: claude_code
model: claude-opus-4-8
effort: medium
- type: agent
harness:
type: codex
model: gpt-5.5
effort: medium
- deepseek
Supply the config to the benchmark with --config:
open-atp benchmark datasets/fate-m runs/fate-m --config config.yaml
The YAML configuration can be overridden by CLI flags.
open-atp benchmark datasets/fate-m runs/fate-m --config config.yaml --compute modal
Logging¶
The benchmark and prove CLI subcommands share a set of logging flags. --log-level {debug,info,warning,error} sets the console verbosity (default info); -v/--verbose and -q/--quiet are shortcuts for debug and warning. Both commands also write full-detail JSONL logs (one event per line) to <output>/logs/open-atp.jsonl.
open-atp benchmark datasets/fate-m runs/fate-m --provers claude -q
Notably, --log-level only controls the log level of the console output; the file sink is always DEBUG and captures all events. Furthermore, both the console and file sinks only capture logs from OpenATP itself (i.e. the open_atp logger).
See CLI Reference for the full reference.