Optimizers¶
Guide to the classical optimizers available for VQE parameter optimization.
How It Works¶
The VQE optimization loop:
- The optimizer proposes parameters \(\theta\).
- The simulator prepares state \(|\psi(\theta)\rangle\) using the ansatz circuit.
- The energy expectation \(\langle \psi(\theta) | \hat{H} | \psi(\theta) \rangle\) is evaluated (exactly, or sampled with
--shots). - The optimizer updates \(\theta\) based on the energy.
- Repeat until convergence or the iteration limit.
By the variational principle, the computed energy is always an upper bound on the true ground state energy.
The pipeline offers the 10 optimizers listed in
settings.py.
Each one is configured in
optimizer_config.py.
Seven optimizers use a gradient (L-BFGS-B, SLSQP, BFGS, CG, TNC, Newton-CG, trust-constr) and need exact expectation values, which any run without --shots provides (see --exact). With --shots N they are refused with exit code 2, so use COBYLA, Nelder-Mead or Powell for sampled runs.
Configured Optimizers¶
Each configured optimizer has a class that controls the options dict and
tol parameter passed to scipy.optimize.minimize, plus parameter validation.
The
OptimizerConfigFactory.create_config()
method picks the right class. It accepts max_iterations and
convergence_threshold, which are mutually exclusive. At the CLI,
--convergence takes precedence over --max-iterations.
Specialized Configs¶
L-BFGS-B - Limited-memory BFGS with Bounds¶
Recommended default. A quasi-Newton method that approximates the inverse Hessian using a limited number of past gradient evaluations. Memory-efficient for high-dimensional problems.
- Type: Quasi-Newton (gradient-based)
- Default maxiter: 15,000
- Config class:
LBFGSBConfig
| Mode | Triggered by | Behavior |
|---|---|---|
| Strict iteration control | --max-iterations N |
maxfun=N, maxiter=N, ftol=1e-15, gtol=1e-15 |
| Convergence-based | --convergence --threshold T |
maxiter=15000, ftol=T, gtol=T |
| Defaults | Neither flag | maxiter=15000, scipy default tolerances |
The tight tolerances in strict mode prevent scipy from stopping early. The
compute_energy() callback enforces the iteration limit via
MaxFunctionEvalsReachedError.
COBYLA - Constrained Optimization by Linear Approximations¶
Derivative-free, trust-region method with linear models. Handles noisy cost functions well.
- Type: Derivative-free
- Default maxiter: 1,000
- Config class:
COBYLAConfig
Uses the global tol parameter for convergence (not options-level). Warns if
max_iterations < num_parameters + 2, which is the minimum COBYLA needs for
an initial simplex.
SLSQP - Sequential Least Squares Programming¶
Gradient-based constrained optimizer that solves a sequence of quadratic programming subproblems.
- Type: Sequential quadratic programming
- Default maxiter: 100
- Config class:
SLSQPConfig
Sets ftol in options when convergence threshold is provided. Also passes the
threshold as global tol.
trust-constr - Trust-Region Constrained¶
Gradient-based trust-region method. It needs no Hessian and keeps its own quasi-Newton approximation.
- Type: Trust-region
- Default maxiter: 1,000
- Config class:
TrustConstrConfig
With max_iterations set, xtol and gtol are 1e-15. Scipy then stops later
than with its defaults, but it may still stop before maxiter. Otherwise the convergence threshold is passed as global tol.
Generic Configs¶
The remaining 6 configured optimizers use
GenericConfig,
which passes maxiter (or maxfun for TNC) into the options dict and returns
convergence_threshold as the global tol.
| Optimizer | Type | Default maxiter | Notes |
|---|---|---|---|
Nelder-Mead |
Derivative-free simplex | 5,000 | Slow, needs high budget |
Powell |
Derivative-free direction-set | 10,000 | Each iter is roughly n line searches |
BFGS |
Quasi-Newton | 1,000 | Fast convergence, full Hessian approximation |
CG |
Conjugate gradient | 2,000 | Moderate convergence, low memory |
TNC |
Truncated Newton (bounded) | 500 | Uses maxfun instead of maxiter |
Newton-CG |
Newton conjugate gradient | 1,000 | Needs the analytic path: an exact run (no --shots) that is local and noiseless, without --gpu, with at most 16 qubits. Any other run exits with code 2. |
GenericConfig behavior:
max_iterationsset: uses it asmaxiter(ormaxfunfor TNC)convergence_thresholdset: uses optimizer's defaultmaxiter, passes threshold astol- Neither set: uses optimizer's default
maxiter
Other Optimizers¶
Only the ten optimizers above are offered by the CLI. Other scipy methods
can be added with OptimizerConfigFactory.register_optimizer() and a custom
OptimizerConfig subclass or a lambda wrapping GenericConfig, plus an entry
in settings.SUPPORTED_OPTIMIZERS.
Summary¶
| Optimizer | Type | Default maxiter |
|---|---|---|
L-BFGS-B |
Quasi-Newton (gradient) | 15,000 |
COBYLA |
Derivative-free (trust-region) | 1,000 |
SLSQP |
Sequential quadratic programming | 100 |
BFGS |
Quasi-Newton (gradient) | 1,000 |
CG |
Conjugate gradient | 2,000 |
TNC |
Truncated Newton (bounded) | 500 |
Powell |
Derivative-free (direction-set) | 10,000 |
Nelder-Mead |
Derivative-free (simplex) | 5,000 |
Newton-CG |
Newton conjugate gradient (needs gradient) | 1,000 |
trust-constr |
Trust-region (gradient) | 1,000 |
In practice, the thesis used L-BFGS-B and COBYLA exclusively. The v2.0.0 verification tested COBYLA, L-BFGS-B, SLSQP, Nelder-Mead, Powell, and BFGS on H2 and HeH+ (see Benchmarking Results). The main finding was that initialization strategy matters far more than optimizer choice - with HF init, all tested gradient-based optimizers reached similar energies.
Iteration Control¶
When you pass --max-iterations N, the limit is enforced in two ways:
- Optimizer-level: the config sets
maxiter(andmaxfunfor L-BFGS-B) in the options dict. - Callback-level:
compute_energy()inVQESolvertracks iteration count and raisesMaxFunctionEvalsReachedErrorwhen exceeded.
If --convergence is set, --max-iterations is ignored (with a warning if it
was set to a non-default value); the threshold decides when the run stops. (OptimizerConfig itself
raises a ValueError if constructed directly with both values.)
Performance Notes¶
On the analytic path (a local, exact, noiseless run with at most 16 qubits) a gradient optimizer gets the energy and its gradient from one statevector pass, so each iteration costs about one evaluation. On the Aer path (--gpu, --noise, or more than 16 qubits) it estimates the gradient by finite differences and needs about one extra evaluation per parameter, but faster convergence typically compensates. See --exact.
Convergence mode (--convergence) pairs well with gradient-based optimizers
that converge monotonically: