Optimization benchmark functions and pytest-oriented evaluation helpers.
Project description
optfunc
optfunc provides differentiable PyTorch optfuncs and
pytest-oriented helpers for evaluating first-order and second-order optimizers.
The package is published as optfuncs and imported as optfunc.
Install
For CPU-only use:
pip install "optfuncs[torch-cpu]"
With uv in this repository:
uv sync --extra torch-cpu
Supported Torch extras:
| Extra | Backend | Typical command |
|---|---|---|
torch-cpu |
CPU, Linux/macOS/Windows | uv sync --extra torch-cpu |
torch-cu118 |
CUDA 11.8, Linux/Windows, PyTorch 2.7.x | uv sync --extra torch-cu118 |
torch-cu126 |
CUDA 12.6, Linux/Windows | uv sync --extra torch-cu126 |
torch-cu128 |
CUDA 12.8, Linux/Windows | uv sync --extra torch-cu128 |
torch-cu130 |
CUDA 13.0, Linux/Windows | uv sync --extra torch-cu130 |
torch-rocm |
ROCm 7.1, Linux | uv sync --extra torch-rocm |
torch-xpu |
Intel XPU, Linux/Windows | uv sync --extra torch-xpu |
Only choose one Torch extra at a time. The extras are configured as mutually
exclusive in pyproject.toml.
Helper scripts:
# bash/zsh on Linux or macOS, and Git Bash/MSYS/Cygwin on Windows
./scripts/sync_torch_variant.sh cpu
./scripts/sync_torch_variant.sh cu130
./scripts/sync_torch_variant.sh rocm
./scripts/sync_torch_variant.sh xpu
# Windows PowerShell or PowerShell 7 on Windows/Linux/macOS
.\scripts\sync_torch_variant.ps1 cpu
.\scripts\sync_torch_variant.ps1 cu130
.\scripts\sync_torch_variant.ps1 rocm
.\scripts\sync_torch_variant.ps1 xpu
If Windows blocks local PowerShell scripts, run:
powershell -ExecutionPolicy Bypass -File .\scripts\sync_torch_variant.ps1 cu130
Platform notes:
- CPU works on Linux, macOS, and Windows.
- CUDA extras work on Linux and Windows.
- ROCm works on Linux.
- XPU works on Linux and Windows.
- macOS should use
torch-cpuin this project configuration.
The backend indexes follow the official uv PyTorch guide and PyTorch install selector:
Optfunc Usage
The repository provides these built-in optfuncs:
| Registry name | Class | Known minimizer |
|---|---|---|
ackley |
Ackley |
all zeros |
dixonprice |
DixonPrice |
recursive Dixon-Price optimum |
griewank |
Griewank |
all zeros |
levy |
Levy |
all ones |
rastrigin |
Rastrigin |
all zeros |
rosenbrock |
Rosenbrock |
all ones |
rotatedhyperellipsoid |
RotatedHyperEllipsoid |
all zeros |
schwefel |
Schwefel |
near 420.968746 in every coordinate |
sphere |
Sphere |
all zeros |
styblinskitang |
StyblinskiTang |
near -2.903534 in every coordinate |
sumsquares |
SumSquares |
all zeros |
trid |
Trid |
x_i = i * (d + 1 - i) with 1-based indexing |
zakharov |
Zakharov |
all zeros |
Use a class directly when you know which function you want:
import torch
from optfunc import Sphere
opt_func = Sphere(dim=8, dtype=torch.float64)
x = torch.zeros(8, dtype=torch.float64)
value = opt_func(x)
grad = opt_func.grad(x)
hessian = opt_func.hessian(x)
hvp = opt_func.hvp(x, torch.ones_like(x))
x_star = opt_func.global_minimizer()
distance = opt_func.distance_to_optimum(x)
Use OptFuncRegistry when a test should select an optfunc by name:
from optfunc import OptFuncRegistry
opt_func = OptFuncRegistry.create("rosenbrock", dim=4)
print(OptFuncRegistry.available())
Each optfunc uses the same conventions:
- input
xis a 1-D PyTorch tensor with shape(dim,); - output is a scalar tensor;
grad,hessian, andhvpuse PyTorch autograd unless a subclass provides a better implementation;global_minimizer()returns a known theoretical minimizer when available;distance_to_optimum(x)defaults to Euclidean distance toglobal_minimizer();project_to_bounds(x)clamps a point into the optfunc's documented search box.
Pytest Optimizer Evaluation
The optimizer evaluation API lives in optfunc.testing. A standard test file
has three parts:
- Define or import an optimizer function.
- Configure one or more
OptimizerCaseobjects. - Assign
make_optimizer_tests(...)to a pytest-visible name such astest_my_optimizer.
Run the file with:
uv run pytest tests/test_my_optimizer.py -q --tb=short --optfunc-report
Each OptimizerCase becomes an independent pytest item. If one case fails or
raises an exception, pytest continues running the remaining cases.
--optfunc-report prints a final serial summary with function gap, distance to
the theoretical optimum, gradient norm, Hessian information, step count, and
error messages. Do not combine --optfunc-report with pytest-xdist -n in v1.
OptimizerBudget
OptimizerBudget describes the budget passed to the optimizer.
from optfunc.testing import OptimizerBudget
budget = OptimizerBudget(max_steps=500, lr=0.05)
Fields:
max_steps: positive integer iteration limit.lr: positive learning-rate-like scalar. The test harness does not enforce how the optimizer uses it; your optimizer reads it fromproblem.budget.lr.
For torch.optim.Adam, a typical use is:
optimizer = torch.optim.Adam([x], lr=problem.budget.lr)
for step in range(problem.budget.max_steps):
...
ConvergenceTolerances
ConvergenceTolerances decides whether a finished optimizer run passes.
from optfunc.testing import ConvergenceTolerances
tolerances = ConvergenceTolerances(
value_gap=1e-8,
x_distance=1e-4,
grad_norm=1e-4,
hessian_min_eig=0.0,
)
Fields:
value_gap: maximum allowed absolute gap between final value and the known theoretical optimum value. Set toNoneto skip this check.x_distance: maximum allowed distance from finalxto the known theoretical minimizer. Set toNoneto skip this check.grad_norm: maximum allowed Euclidean norm of the final gradient. Set toNoneto skip this check.hessian_min_eig: optional lower bound on the final Hessian's smallest eigenvalue. Set toNoneto skip this check.
The report still computes available metrics even when a tolerance is None;
None only disables that pass/fail check.
OptimizerCase
OptimizerCase describes one pytest item.
from optfunc.testing import OptimizerCase
from optfunc.testing import OptimizerBudget, ConvergenceTolerances
case = OptimizerCase(
opt_func="sphere",
dim=8,
budget=OptimizerBudget(max_steps=350, lr=0.05),
tolerances=ConvergenceTolerances(value_gap=1e-8, x_distance=1e-4, grad_norm=1e-4),
start="near_minimizer",
start_radius=0.5,
seed=0,
hessian_max_dim=32,
)
Important fields:
opt_func: registry name such as"sphere"or an already-createdTorchOptFunctioninstance.dim: required whenbenchmarkis a string.budget:OptimizerBudgetpassed to the optimizer throughproblem.budget.tolerances:ConvergenceTolerancesused after the optimizer returns.case_id: optional pytest id; by default this is likesphere[8].x0: optional explicit initial point. If omitted, the harness builds one fromstart.start:"near_minimizer","random", or"zeros".start_radius: offset size used by"near_minimizer".seed: random seed used by"random".device: optional torch device string, for example"cuda"or"cpu".dtype: torch dtype, defaulttorch.float64.hessian_max_dim: largest dimension for dense Hessian report metrics. Larger cases skip dense Hessian metrics to avoid slow tests.
make_optimizer_tests
make_optimizer_tests converts an optimizer plus cases into a pytest test
function.
from optfunc.testing import make_optimizer_tests
test_my_optimizer = make_optimizer_tests(
optimizer=my_optimizer,
cases=[case1, case2],
name="test_my_optimizer",
)
Rules:
- Assign the returned function to a module-level variable whose name starts with
test_, otherwise pytest will not collect it. optimizermust accept oneOptimizationProblemargument.- The optimizer may return either a final
torch.Tensoror anOptimizerResult. - Every case becomes an independent parametrized pytest item.
Standard Adam Test Example
This example shows the recommended shape for a user-owned optimizer wrapper.
The test harness does not hide torch.optim.Adam; the user function decides how
to initialize Adam, how to use the budget, and what history to expose.
Create tests/test_torch_adam.py:
import torch
from optfunc.testing import (
ConvergenceTolerances,
OptimizerBudget,
OptimizerCase,
OptimizerResult,
make_optimizer_tests,
optimizer_adapter,
)
def scalar_item(value):
return float(value.detach().cpu().item())
@optimizer_adapter(name="torch_adam")
def torch_adam(problem):
x = problem.x0.detach().clone().requires_grad_(True)
optimizer = torch.optim.Adam([x], lr=problem.budget.lr)
history = []
for step in range(1, problem.budget.max_steps + 1):
optimizer.zero_grad(set_to_none=True)
loss = problem.value(x)
loss.backward()
optimizer.step()
with torch.no_grad():
x.copy_(problem.project_to_bounds(x.detach()))
if step % 25 == 0 or step == problem.budget.max_steps:
x_now = x.detach()
grad = problem.grad(x_now)
history.append(
{
"step": step,
"value": scalar_item(problem.value(x_now)),
"grad_norm": scalar_item(torch.linalg.vector_norm(grad)),
"x_norm": scalar_item(torch.linalg.vector_norm(x_now)),
}
)
return OptimizerResult(
final_x=x.detach().clone(),
steps=problem.budget.max_steps,
history=history,
)
test_torch_adam = make_optimizer_tests(
optimizer=torch_adam,
cases=[
OptimizerCase(
opt_func="sphere",
dim=8,
budget=OptimizerBudget(max_steps=350, lr=0.05),
tolerances=ConvergenceTolerances(
value_gap=1e-8,
x_distance=1e-4,
grad_norm=1e-4,
),
start="near_minimizer",
start_radius=0.5,
),
OptimizerCase(
opt_func="rosenbrock",
dim=4,
budget=OptimizerBudget(max_steps=1200, lr=0.02),
tolerances=ConvergenceTolerances(
value_gap=5e-4,
x_distance=5e-2,
grad_norm=1e-2,
),
start="near_minimizer",
start_radius=0.25,
),
],
)
Run:
uv run pytest tests/test_torch_adam.py -q --tb=short --optfunc-report
The OptimizationProblem object passed into torch_adam exposes:
problem.opt_func: the selectedTorchOptFunction;problem.x0: the initial point for this case;problem.budget: theOptimizerBudget;problem.value(x): scalar objective value;problem.grad(x): gradient;problem.value_and_grad(x): objective and gradient;problem.hessian(x): dense Hessian;problem.hvp(x, v): Hessian-vector product;problem.project_to_bounds(x): clamp into the optfunc's bounds.
For second-order methods, call problem.hessian(x) or problem.hvp(x, v)
inside the same optimizer wrapper and return the same OptimizerResult shape.
Built-In Adam Helper
For smoke tests or examples, optfunc.testing.make_torch_adam() provides the
same Adam loop as a convenience:
from optfunc.testing import make_torch_adam, make_optimizer_tests
test_adam = make_optimizer_tests(
optimizer=make_torch_adam(),
cases=[...],
)
For production optimizer tests, prefer writing the small wrapper yourself so the learning rate, projection, stopping rule, and history format are explicit in your test file.
Local Development
uv sync --extra torch-cpu
uv run pytest -q --optfunc-report
On this repository's CUDA development path:
uv sync --extra torch-cu130
uv run pytest -q --optfunc-report
Release Flow
- Update the package version in
pyproject.toml, or runuv version <version> --frozen. - Create and push a Git tag named
v<version>. - CNB will publish that tag to PyPI through the
tag_pushpipeline.
uv version z.x.y --frozen
rm -rf dist build src/*.egg-info
uv lock
git tag -a vz,x,y -m "Release vX.Y.Z"
git push origin vz,x,y
Optfunc definitions are adapted from SFU's optimization benchmark collection:
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file optfuncs-0.0.4.tar.gz.
File metadata
- Download URL: optfuncs-0.0.4.tar.gz
- Upload date:
- Size: 23.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cb207af62394b2f53f21f8af4726c7311a481b62b7d93fc620c35bc7b45673ea
|
|
| MD5 |
e83d726bc876747394bc71a8c6f886eb
|
|
| BLAKE2b-256 |
35ee85c666b7b9ee03b819ca22845c88bd5a510702940e281c9d0494d74c2dc5
|
File details
Details for the file optfuncs-0.0.4-py3-none-any.whl.
File metadata
- Download URL: optfuncs-0.0.4-py3-none-any.whl
- Upload date:
- Size: 24.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2a7ffc82de4d77bd81733c60e4169b7c3744fbd3770e206013311a9f7b1daf9
|
|
| MD5 |
0592040e2cf4020516ea157959093a45
|
|
| BLAKE2b-256 |
133e458afe2f09c63e45f77e538e4bb612fc1d721efcabb417a409c4fb55358e
|