Repository navigation
Expand file tree
/
Copy pathpyproject.toml
More file actions
416 lines (393 loc) · 19.9 KB
/
Copy pathpyproject.toml
File metadata and controls
416 lines (393 loc) · 19.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# The ONLY dependency list, with agent/pyproject.toml (the harness venvs' groups): package metadata, extras,
# groups and the dace pin, all resolved into uv.lock. The images `uv sync --frozen` from this file and the lock
# without the package source (`--no-install-project` installs the dependencies, never the project), so it
# holds nothing hidden.
[build-system]
# 77: the first setuptools that reads the PEP 639 `license` expression and `license-files`.
requires = ["setuptools>=77"]
build-backend = "setuptools.build_meta"
[project]
name = "hpcagent-bench"
version = "0.2.1"
description = "A benchmark for AI agents that optimize numerical code: NumPy kernels, a judge, and speedup scoring."
readme = "README.md"
requires-python = ">=3.12"
license = "GPL-3.0-or-later"
license-files = ["LICENSE", "NOTICE"]
authors = [{ name = "SPCL @ ETH Zurich", email = "yakupkoray.budanaz@inf.ethz.ch" }]
keywords = ["benchmark", "hpc", "llm-agents", "code-optimization", "numpy", "compilers"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Science/Research",
"Intended Audience :: Developers",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: C",
"Programming Language :: C++",
"Programming Language :: Fortran",
"Topic :: Scientific/Engineering",
"Topic :: Software Development :: Compilers",
"Topic :: System :: Benchmark",
]
# What the library imports at module level (hpcagent_bench/ outside benchmarks/ and tests/). The
# framework matrix (torch/jax/tvm/triton/...) is per platform and lives in the extras; numba is core
# because it is the default LLR/SciComp baseline.
# dace and relayout resolve to the commits [tool.uv.sources] pins; hpcagent-agent is the agent-side
# runtime (agent/), a workspace member.
dependencies = [
# numpy 2: the references are graded under its NEP 50 scalar promotion; 2.3.2 is the first
# release with Python 3.14 wheels.
"numpy>=2.3.2",
"scipy",
"pandas",
"matplotlib",
"ml_dtypes",
"pyyaml",
# Validated models: config, request allowances, manifests, the Harbor grade (2.12: Field(exclude_if=...)).
"pydantic>=2.12",
"jinja2",
"cffi",
"sympy",
"numba",
"dace",
# The request schema (requests: startup + operations) and its lowering: relayout.
"relayout",
"hpcagent-agent",
# The grading stack, judge, HF export and agent clients every framework set shares.
"pygount",
"jsonschema",
"setuptools",
"tree-sitter-language-pack",
"psutil",
"py-cpuinfo",
"GPUtil",
"pythran<0.19", # 0.19.0 turns subset_sum into a >600 s hang, then a SIGSEGV
"xgboost",
# Python bindings for the libhdf5 / libnetcdf the images install; the image smoke imports both.
"h5py",
"netCDF4",
# dace's analyses fail closed without them (sequential loops, no diagnostic).
"islpy",
"z3-solver",
# The upstream KernelBench models the machine_learning ports are compared against import it.
"einops",
# tvm.s_tir and tvm.tirx (frameworks/tvm_build.py) first ship in 0.25.
"apache-tvm>=0.25",
# Built from source against the system MPI (MPICC).
"mpi4py",
# `hpcagent-bench export-hf`: parquet writer, load-back check, Hub push.
"pyarrow",
# load_back reads the dataset card's `configs:` metadata.
"datasets>=2.17",
"huggingface_hub",
# The judge: hpcagent_bench/cluster/judge_service.py's router (starlette's TestClient needs httpx2), the bound
# judge tools' crawl/browser stack. The proxy the setups talk through is the judge-proxy group below.
"fastapi",
"httpx",
"httpx2",
"crawl4ai",
"playwright",
"uvicorn",
"orjson",
"tenacity",
"rich",
"typer",
# Agent clients: the Anthropic API and a locally hosted transformers model.
"anthropic",
"transformers",
"accelerate",
]
[project.urls]
Homepage = "https://github.com/spcl/HPCAgent-Bench"
Repository = "https://github.com/spcl/HPCAgent-Bench"
Documentation = "https://github.com/spcl/HPCAgent-Bench/tree/main/docs"
Issues = "https://github.com/spcl/HPCAgent-Bench/issues"
Changelog = "https://github.com/spcl/HPCAgent-Bench/blob/main/CHANGELOG.md"
[project.scripts]
hpcagent-bench = "hpcagent_bench.cli:main"
hpcagent-bench-install-apptainer = "hpcagent_bench.containers:install_apptainer_main"
numpyto = "hpcagent_bench.translators.numpyto_common.cli:main"
numpyto_c = "hpcagent_bench.translators.numpyto_c.cli:main"
numpyto_fortran = "hpcagent_bench.translators.numpyto_fortran.cli:main"
numpyto_cupy = "hpcagent_bench.translators.numpyto_cupy.cli:main"
numpyto_numba = "hpcagent_bench.translators.numpyto_numba.cli:main"
numpyto_pythran = "hpcagent_bench.translators.numpyto_pythran.cli:main"
# Exactly one of cpu / amdgpu / nvgpu is a COMPLETE install for that hardware: the package dependencies (the
# grading stack, judge, HF export and agent clients) plus that hardware's framework set (torch, jax, triton,
# cupy) and the developer tools ([dev]). Harbor is the `harbor` dependency group below (the adapter in
# adapters/hpcagent_bench depends only on this package): its rich>=14.1 cannot share an environment with the
# judge-proxy group. The images install one of them (`uv sync --frozen --extra amdgpu`).
[project.optional-dependencies]
# The framework sets differ in torch and jax, triton and cupy; [tool.uv.sources] picks each one's wheel index.
# cpu: PyTorch's CPU index, jax's CPU jaxlib, triton for its CPU interpreter.
cpu = ["hpcagent_bench[dev]", "torch", "jax", "triton"]
# amdgpu: torch, triton, jaxlib and the jax ROCm plugin from AMD's repo.radeon.com page for ROCm 7.2. Its torch is
# the lightweight build that links the image's /opt/rocm: PyTorch's ROCm wheel bundles its own libamdhip64 and
# librccl, and a grading rank that also loads an agent's HIP/RCCL library then maps two HIP runtimes and two RCCLs
# (every ML grade crashed); AMD builds it for CPython 3.10 to 3.13, the images run 3.12. jax 0.8.0+rocm7.2.0 is the
# newest there and holds jax itself at 0.8.0; cupy built from the PyPI sdist for HIP (no ROCm wheel exists; CUPY_INSTALL_USE_HIP=1
# and --no-binary-package cupy). x86_64 only, and ROCm jax on CPython 3.12 only: AMD publishes no aarch64 wheels
# and a cp311 and cp312 plugin.
amdgpu = [
"hpcagent_bench[dev]",
"torch ; platform_machine == 'x86_64' and python_version < '3.14'",
"triton ; platform_machine == 'x86_64' and python_version < '3.14'",
"jax ; platform_machine == 'x86_64' and python_version == '3.12'",
"jaxlib ; platform_machine == 'x86_64' and python_version == '3.12'",
"jax-rocm7-plugin ; platform_machine == 'x86_64' and python_version == '3.12'",
"jax-rocm7-pjrt ; platform_machine == 'x86_64' and python_version == '3.12'",
"cupy ; platform_machine == 'x86_64'",
]
# nvgpu: torch for CUDA 13 from PyTorch's cu132 index, jax and cupy wheels for CUDA 13, PyPI's triton, and cuTile
# (cuda-tile; compiles with the image toolkit's tileiras, runs on sm_80 and newer).
nvgpu = ["hpcagent_bench[dev]", "torch", "jax[cuda13]", "cupy-cuda13x", "triton", "cuda-tile"]
# The Sphinx build of docs/ (`sphinx-build -W -b html docs docs/_build`).
docs = ["sphinx", "myst-parser", "furo"]
# Alloy, the autotuner behind the `dace_<arch>_<pipeline>_alloy_<mode>` flavors. It pins the
# same dace commit, so it joins any of the framework sets.
alloy = ["alloy"]
# Tests and formatters, also part of every framework set so the suite runs inside each image.
dev = [
"pytest",
"pytest-timeout",
"pytest-xdist",
# tests/ports/ runs each test in its own process (a bad ctypes index aborts the interpreter).
"pytest-forked",
"pytest-cov",
"pytest-mpi",
"coverage",
"ruff",
"mypy",
"pyright",
"fprettify",
"clang-format",
"pre-commit",
]
# PEP 735 groups: never published with the distribution, and installed without the project's own
# dependencies. Each is a stack that cannot share one resolution with the hardware extras.
[dependency-groups]
# The judge-agent images' LLM proxy the setups talk through, exactly as the recorded setups ran it; the
# images install it after their hardware extra. litellm 1.94 needs rich<14, so harbor>=0.23 never
# shares its environment.
judge-proxy = [
"litellm[proxy]==1.94.0", # the proxy every recorded setup talked through
"httpx2==2.12.0", # the proxy stack the recorded setups ran
"anyio==4.14.2", # the proxy stack the recorded setups ran
]
# What the judge and agent images build from source against their own toolchain, the only Python they carry:
# numpy and scipy on the image's OpenBLAS (numpy_on_openblas.sh), mpi4py on its MPICH. Every other locked
# package is installed when a job starts (containers/images/lib/launch_venv.sh).
image-python = ["numpy", "scipy", "mpi4py"]
# The AMD image's cupy, built for HIP from the sdist (no ROCm wheel exists).
image-cupy-rocm = ["cupy ; platform_machine == 'x86_64'"]
# numpy_on_openblas.sh rebuilds the locked numpy and scipy from source on the image's OpenBLAS: scipy builds
# without isolation, so its build tools are installed into the environment first.
openblas-build = ["meson-python", "Cython", "pybind11", "pythran<0.19", "patchelf"]
# `hpcagent-bench harbor run`: the Harbor CLI and the compose frontend its podman backend drives,
# in their own venv (harbor>=0.23 needs rich>=14, which litellm's judge-proxy stack refuses).
harbor = ["harbor>=0.23.0", "podman-compose>=1.6"]
[tool.uv.workspace]
members = ["agent", "adapters/hpcagent_bench"]
[tool.uv.sources]
# The spcl/dace@extended and relayout@main commits this release is tested with, and the ONE place each is
# written: uv.lock records them, and the image builds and jobs read them through helpers/scripts/pins.sh.
dace = { git = "https://github.com/spcl/dace.git", rev = "baac24733f204d0e992f8cf73ce3d7c77fce7b5d" }
relayout = { git = "https://github.com/ThrudPrimrose/relayout.git", rev = "2718b2fee48a0a60b0ed1b65f281d9636e75f0f8" }
hpcagent-agent = { workspace = true }
# Moves with the dace pin above: Alloy's own dace pin must be the same commit.
alloy = { git = "https://github.com/ThrudPrimrose/alloy.git", rev = "d6517881201abb26e169e7e2a5bd29fae30b1e39" }
torch = [
{ index = "pytorch-cpu", extra = "cpu" },
{ index = "rocm-radeon", extra = "amdgpu" },
{ index = "pytorch-cuda", extra = "nvgpu" },
]
triton = [{ index = "rocm-radeon", extra = "amdgpu" }]
jaxlib = [{ index = "rocm-radeon", extra = "amdgpu" }]
jax-rocm7-plugin = [{ index = "rocm-radeon", extra = "amdgpu" }]
jax-rocm7-pjrt = [{ index = "rocm-radeon", extra = "amdgpu" }]
# PyPI's torch is the CUDA 12 build: every framework set takes it from PyTorch's index for its hardware.
[[tool.uv.index]]
name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true
# cu132 is the newest CUDA 13 index that carries torch (cu134 carries none yet); a CUDA 13.4 driver runs it.
[[tool.uv.index]]
name = "pytorch-cuda"
url = "https://download.pytorch.org/whl/cu132"
explicit = true
# AMD's flat page of ROCm 7.2 wheels (links, no index pages): torch and triton built on the system ROCm, and the
# only source of jaxlib and its plugin for ROCm.
[[tool.uv.index]]
name = "rocm-radeon"
url = "https://repo.radeon.com/rocm/manylinux/rocm-rel-7.2/"
format = "flat"
explicit = true
[tool.uv]
# Linux only: the toolchain, the seal and the images are. CPython 3.15 is out until the GPU extras ship wheels
# for it (cupy-cuda13x has none), so the lock resolves 3.12 to 3.14; the images run 3.12.
environments = ["sys_platform == 'linux' and python_full_version < '3.15'"]
# The bench's dace pin wins over the one Alloy's own pyproject names, so the [alloy] extra grades on the same dace.
override-dependencies = ["dace @ git+https://github.com/spcl/dace.git@baac24733f204d0e992f8cf73ce3d7c77fce7b5d"]
# One environment per framework set, and each group is its own venv (litellm's judge-proxy stack needs
# rich<14; harbor and the OpenHands tools need rich>=14).
conflicts = [
[{ extra = "cpu" }, { extra = "amdgpu" }, { extra = "nvgpu" }],
[
{ group = "judge-proxy" },
{ group = "harbor" },
{ package = "hpcagent-agent", group = "harness-miniswe" },
{ package = "hpcagent-agent", group = "harness-openhands" },
{ package = "hpcagent-agent", group = "harness-anthropic" },
{ package = "hpcagent-agent", group = "harness-openai" },
{ package = "hpcagent-agent", group = "harness-optimas" },
],
]
[tool.setuptools]
include-package-data = true
[tool.setuptools.packages.find]
include = ["hpcagent_bench*"]
# Listed explicitly: the judge image builds the wheel from hpcagent_bench/ and pyproject.toml alone
# (no MANIFEST.in, no VCS file finder).
[tool.setuptools.package-data]
hpcagent_bench = [
# Every setting's default (hpcagent_bench.config reads it at first use).
"config.yaml",
"container_backends.txt",
# The experiment launchers and the sample sbatch a job's steps re-enter (hpcagent_bench.cluster).
"cluster/*.sh",
"cluster/*.sbatch",
"cluster/systems.yaml",
"envs/*.yaml",
# -include'd on every gcc/g++ compile (CPU_BASELINE_GCC).
"envs/vecmath.h",
"envs/papi_ranges.h",
# Kernel manifests, references in every language, and the numpy reference read as text. Kernel
# dirs are not packages, so their .py files ship only through this list.
"benchmarks/**/*.yaml",
"benchmarks/**/*.c",
"benchmarks/**/*.cpp",
"benchmarks/**/*.cu",
"benchmarks/**/*.f90",
"benchmarks/**/*.hip",
"benchmarks/**/*.hu",
"benchmarks/**/*.j2",
"benchmarks/**/*.md",
"benchmarks/**/*.npz",
"benchmarks/**/*.py",
# Prompt templates, skill pages and tool fragments (harness/prompts.py).
"harness/**/*.j2",
"harness/**/*.md",
"harness/kernelbench_map.yaml",
"harness/baseline_leaders.yaml",
# Kernels whose grading changed after final grades were recorded (grade_under.stale_final).
"harness/grading_cuts.yaml",
# The results database schema v1.
"harness/schema.sql",
"skills/*/SKILL.md",
"skills/*/*.py",
"tools/*.md",
# Study tags: tags/<tag>.txt lists the kernels of study <tag> (hpcagent_bench.tags).
"tags/*.txt",
]
# Third-party warnings only (zero-warning policy for our own code).
[tool.pytest.ini_options]
markers = [
"smoke: the release smoke suite (`pytest -m smoke`): an end-to-end path on a CPU host without a GPU or SLURM; the whole selection runs in under 5 minutes.",
]
filterwarnings = [
# starlette's TestClient portal teardown, inside anyio/starlette.
"ignore:.*BlockingPortal.*:DeprecationWarning:anyio",
"ignore:.*BlockingPortal.*:DeprecationWarning:starlette",
# A forked child (run_forked, the oracle's and the seal's raw forks) runs its task and exits without
# re-entering a thread pool the parent held, which is what both fork warnings are about; the oracle
# and the seal already silence the same warning at their fork sites. The suite's workers carry
# numpy/BLAS threads, and JAX's once a jax test has run in them; no forked child runs JAX.
"ignore:This process .* is multi-threaded, use of fork\\(\\) may lead to deadlocks in the child:DeprecationWarning",
"ignore:os.fork\\(\\) was called. os.fork\\(\\) is incompatible with multithreaded code, and JAX is multithreaded:RuntimeWarning",
]
# CI sets PYTEST_ADDOPTS per job. relative_files: jobs run in different filesystem layouts;
# parallel + concurrency: the suite runs under xdist and forks (hpcagent_bench/numerical_oracle.py).
[tool.coverage.run]
source = ["hpcagent_bench"]
branch = true
parallel = true
relative_files = true
concurrency = ["multiprocessing", "thread"]
omit = ["hpcagent_bench/benchmarks/*"]
[tool.coverage.report]
show_missing = true
skip_covered = true
exclude_also = [
"if TYPE_CHECKING:",
"raise NotImplementedError",
"if __name__ == .__main__.:",
]
# helpers/scripts/checks/check_format.py routes every .py to ruff (pre-commit and CI call it).
[tool.ruff]
line-length = 120
target-version = "py312"
extend-exclude = ["*_generated.py", "third_party"]
# Explicit paths (pre-commit's file list) honour the excludes too.
force-exclude = true
[tool.ruff.lint]
select = ["ALL"]
# The benchmark kernels are task text the translators parse and agents read, not package code (as for the
# type checkers below): a lint rewrite would change what a task says or what a frontend accepts.
exclude = ["hpcagent_bench/benchmarks/**"]
ignore = [
"D", # docstrings: the reviewers own these
"ARG", # unused arguments: callbacks and protocol signatures
"C408", # dict(a=..., b=...) reads better than a literal here
"PLR2004", # magic values: tolerances, sizes and exit codes are named where it helps
"EM101", "EM102", "TRY003", # the message is built where it is raised
"S101", # assert is the test idiom
"COM812", "ISC001", "E501", # the formatter owns commas, string joins and line length
"N802", "N803", "N806", "N812", "N816", # mathematical names (A, N, M, LEN_1D) follow the kernels
"N815", # visit_While = visit_For: the names ast.NodeVisitor dispatches on
"C901", "PLR0911", "PLR0912", "PLR0913", "PLR0915", "PLR0917", # size limits
"PLC0415", # imports inside functions keep heavy modules (dace, torch, cupy) off the import path
"INP001", # tests/ and helpers/ are not packages
"CPY001", # the header hook owns the SPDX block
"T201", # CLIs and jobs report on stdout
"FBT001", "FBT002", "FBT003", # boolean flags in signatures and calls
"SLF001", # tests reach into module internals on purpose
"S603", "S607", # subprocess calls are the harness's job
# HTTP to its own judge, SQL over its own result DBs, temp dirs, test tokens and seeded RNG are the harness's
# job too; isolation is enforced by the seal, not by lint.
"S105", "S106", "S108", "S110", "S112", "S310", "S311", "S608",
"N818", "TRY004", # exception class names and types are API callers catch
"PLW0603", # module state a process sets once (caches, the configured device)
"ERA001", # code-shaped comments are worked examples
"TD002", "TD003", "FIX002", # TODO format
"RUF059", "B007", # their fix binds a _-prefixed name, which check_names.py forbids
"E731", # a named one-line lambda; its fix is an unannotated def
"PTH100", # os.path.abspath keeps a symlink where Path.resolve follows it (mount points, the seal's paths)
"TRY301", # a raise its own except clause reports is the point of that try
]
[tool.ruff.lint.per-file-ignores]
"tests/**" = ["E402"] # imports after pytest.importorskip, which gates the optional extra they need
# The ports keep their kernel's own names (SRAD's image I, a vexx O); E741 reads them as ambiguous.
"tests/ports/**" = ["E402", "E741"]
"tests/test_seissol_init_vectorized.py" = ["E402", "E741"]
"containers/images/lib/spack-overlay/**" = ["F403", "F405"] # a spack package takes the package DSL by star import
"hpcagent_bench/harbor.py" = ["PT028"]
# running_min branches on purpose: simplification lifts the comparison onto the interstate edge under test.
"tests/test_dace_gpu_residency.py" = ["PLR1730"] # test_sh writes a task's tests/test.sh; it is not a test
# Type-check against the requires-python floor. The benchmark references are task text agents read (original
# sources and NumPy references), not package code, so neither checker edits them into another task.
[tool.pyright]
pythonVersion = "3.12"
# agent/ is a workspace member installed editable, which pyright cannot follow through the import hook.
extraPaths = ["agent"]
exclude = ["hpcagent_bench/benchmarks", "third_party", "**/node_modules", "**/__pycache__", "**/.*", "**/*_generated.py"]
[tool.mypy]
python_version = "3.12"
follow_imports = "silent"
# The agent package (hpcagent_agent) is a source root of its own: the judge imports its wire types.
mypy_path = "agent"
ignore_missing_imports = true
exclude = ["^hpcagent_bench/benchmarks/", "^third_party/", "_generated\\.py$"]