ZoSolvers
Zeroth-order optimisation solvers with Gaussian and sphere random oracles.
ZoSolvers provides gradient-free solvers for minimisation and minimax problems. When the gradient of the objective is unavailable — because the function is non-differentiable, comes from a black-box simulator, or is too expensive to differentiate — ZoSolvers estimates it using random directional perturbations.
📖 Full documentation: ZoSolvers Manual (PDF)
Install
pip install ZoSolvers
Requirements: Python ≥ 3.10, NumPy ≥ 2.2, Matplotlib ≥ 3.7
Features
- Two oracle types — Gaussian (
u ~ N(0, B⁻¹)) and sphere (uniform on the unit sphere), both with optional preconditioning via a precision matrixB - Four solvers — ZOGD, ZOEGm (minimisation), ZOGDA, ZOEGmm (minimax)
- Three finite-difference methods — forward, backward, centered
- Flexible mini-batching — fixed number of oracle samples per step, or a growing schedule (
t="iteration") - Constrained problems — pass any projection function, and choose whether the initial guess is projected too
- Early stopping — based on relative function decrease or oracle norm
- Parallel mini-batches — the
toracle samples per step are evaluated acrossn_jobsworkers (thread or process backend) - Efficient sampling — Cholesky factorisation cached at construction; diagonal
Bhandled without matrix inversion
Quick Start
Minimisation
import numpy as np
from ZoSolvers.minimisation import ZO_gauss_min
def f(x):
return x[0]**2 + x[1]**2 + x[0]*x[1]
x0 = np.array([5.0, -5.0])
opt = ZO_gauss_min(f, x0, h=1e-2, mu=1e-5, N=2000, t=10)
# Zeroth-order gradient descent
x_traj = opt.ZOGD(method="center")
# Zeroth-order extra-gradient
x_traj = opt.ZOEGm(method="center", gamma=1.0)
Minimax
from ZoSolvers.minimax import ZO_gauss_minmax
def f(x, y):
return x[0]**2 + x[1]**2 - y[0]**2 - y[1]**2 + x[0]*y[1]
x0 = np.array([5.0, -5.0])
y0 = np.array([3.0, -3.0])
opt = ZO_gauss_minmax(f, x0, y0, h=1e-3, tau=1, mu=1e-8, N=10000, t=5)
# Gradient descent-ascent
x_traj, y_traj = opt.ZOGDA(method="center")
# Extra-gradient
x_traj, y_traj = opt.ZOEGmm(method="center", gamma=0.8)
Sphere Oracle and Precision Matrix
B = np.array([[10.0, 0.5],
[0.5, 2.0]])
opt = ZO_gauss_min(f, x0, h=1e-2, mu=1e-5, N=2000, t=10,
B=B, oracle_type="sphere")
x_traj = opt.ZOGD(method="center")
Parallel Function Evaluations
The t oracle samples that make up one step are independent, so they can be
evaluated concurrently. Pass n_jobs (-1 = all cores):
opt = ZO_gauss_min(f, x0, h=1e-2, mu=1e-5, N=2000, t=32, n_jobs=-1)
x_traj = opt.ZOGD(method="center")
opt.close() # or use the solver as a context manager
The worker pool is created once and reused across every iteration. Use the solver as a context manager to shut it down automatically:
with ZO_gauss_minmax(f, x0, y0, h=1e-3, mu=1e-8, N=10000, t=16, n_jobs=8) as opt:
x_traj, y_traj = opt.ZOGDA(method="center")
Two backends are available:
backend |
Use when | Notes |
|---|---|---|
"thread" (default) |
func releases the GIL — NumPy-heavy models, compiled extensions, subprocess or network-backed simulators |
Accepts any callable, including lambdas and closures |
"process" |
func is pure-Python and CPU-bound |
func must be picklable (module-level, not a lambda); np.random.seed no longer makes runs reproducible, since each worker seeds itself |
Parallelism pays off when the cost function is expensive relative to the scheduling overhead, the usual case for black-box simulators.
Box-Constrained Problem
proj = lambda x: np.clip(x, -3.0, 3.0)
opt = ZO_gauss_min(f, x0, h=1e-2, mu=1e-5, N=2000, t=10, proj=proj)
x_traj = opt.ZOGD(method="center")
Every iterate the solver computes is feasible, but the first row of the
returned trajectory is the x0 you passed in, returned untouched. If x0 lies
outside the feasible set, that first row is infeasible. Set project_init=True
to project it as well, so the whole trajectory is feasible:
opt = ZO_gauss_min(f, x0, h=1e-2, mu=1e-5, N=2000, t=10, proj=proj,
project_init=True)
x_traj = opt.ZOGD(method="center") # x_traj[0] == proj(x0)
The default is False, which keeps x0 visible exactly as given. For minimax,
the same flag covers both x0 and y0, each through its own projection.
Solvers
| Solver | Class | Problem | Description |
|---|---|---|---|
ZOGD |
ZO_gauss_min |
Minimisation | Zeroth-order gradient descent |
ZOEGm |
ZO_gauss_min |
Minimisation | Zeroth-order extra-gradient |
ZOGDA |
ZO_gauss_minmax |
Minimax | Zeroth-order gradient descent-ascent |
ZOEGmm |
ZO_gauss_minmax |
Minimax | Zeroth-order extra-gradient minimax |
Key Parameters
| Parameter | Description |
|---|---|
h |
Step size |
mu |
Smoothing parameter for finite differences |
N |
Maximum number of iterations |
t |
Oracle samples per step (int or "iteration" for growing schedule) |
B |
Precision matrix shaping the perturbation distribution (None = identity) |
oracle_type |
"gaussian" or "sphere" |
proj |
Projection onto the feasible set (None = unconstrained) |
project_init |
Project the initial guess onto the feasible set too (default False) |
n_jobs |
Workers used to evaluate the t samples per step (1 = sequential, -1 = all cores) |
backend |
"thread" (default) or "process" |
tau |
(Minimax) Step-size ratio: x-step = h/tau, y-step = h |
gamma |
(Extra-gradient) Second-stride scale factor |
Package Structure
ZoSolvers/
├── src/ZoSolvers/
│ ├── minimisation.py # ZO_gauss_min
│ ├── minimax.py # ZO_gauss_minmax
│ └── utils.py # shared utilities
├── tests/
│ ├── min_test.py # pytest suite for minimisation
│ ├── minimax_test.py # pytest suite for minimax
│ ├── demo_min.py # minimisation demo with plots
│ └── demo_minimax.py # minimax demo with plots
├── Docs/
│ └── ZoSolvers_Manual.pdf
└── pyproject.toml
License
MIT
Metadata
Release files for ZoSolvers 0.1.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| zosolvers-0.1.5.tar.gz | 11.4 kB | Without provenance | Details for zosolvers-0.1.5.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| zosolvers-0.1.5-py3-none-any.whl | Python 3 | none | any | Details for zosolvers-0.1.5-py3-none-any.whl |
Total release size: 24.1 kB
Release files / zosolvers-0.1.5.tar.gz
| Download URL | zosolvers-0.1.5.tar.gz |
|---|---|
| Size | 11.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dbd07e2c6e84af4411c4ac0ebcf49e0a1c19d07a4bf71d09c894b096d6a7aeca
|
|
BLAKE2b-256 checksum How to use checksums |
f5da4e70d56eef802b7003ae08a95604d28dd16b844f76539fec3837b471c62c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.1.2 CPython/3.10.12 Linux/6.8.0-136-generic
|
Release files / zosolvers-0.1.5-py3-none-any.whl
| Download URL | zosolvers-0.1.5-py3-none-any.whl |
|---|---|
| Size | 12.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
baa8b515b496c9c070e4b50aecd9da05781a196ac56310269e13febb44686891
|
|
BLAKE2b-256 checksum How to use checksums |
d7d0a69327ed593425e362ed1e8ace96510cbe7ae01389e22ad4f46d34684267
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.1.2 CPython/3.10.12 Linux/6.8.0-136-generic
|
Security for ZoSolvers 0.1.5
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.