variational
eigenkit.core.variational
Variational optimisation: minimise <H>(θ) over the parameters of a circuit.
This is the training engine, independent of any framework, that the Variational
Quantum Eigensolver (VQE) in chemistry, the quantum approximate optimisation
algorithm (QAOA) in optimization, and any workflow of the form "prepare a
parametrised state, measure <H>, optimise θ" share. It uses only NumPy and
SciPy. It runs the NumPy forward pass of the core Circuit (compiled.run) and
its analytic NumPy gradients (_gradient_jacobians: the backend's own gradient
when it has one, such as the adjoint method of the NumPy simulator, and the
parameter-shift rule otherwise). So it runs on any backend (NumPy,
PennyLane, Qiskit, Braket, CUDA Quantum) without PyTorch. Even on PennyLane it
uses the NumPy path, not the PyTorch QNode. SciPy is imported only at the first
call.
Backends that cannot differentiate (gradient_method="none", for example a
quantum processing unit (QPU) whose configuration leaves enable_gradients off)
must use an optimiser that needs no gradient, such as "COBYLA",
"Nelder-Mead", "Powell" or "spsa". These only need the forward evaluation.
VariationalResult
dataclass
Bases: ValueEquality
Outcome of minimize_expectation().
The result is read-only. Assigning to a field raises FrozenInstanceError, and
writing into an array field raises ValueError. This prevents a mistyped =
(instead of ==) or an in-place edit from leaving a result whose scalar fields
describe a different run from the array it holds. To get a writable copy of the
array, use np.array(result.field).
Results compare by value, including their arrays. They are not hashable, because a result is a collection of numbers and not a key, and a correct hash of a float array is expensive to compute.
Attributes:
| Name | Type | Description |
|---|---|---|
optimal_value |
float
|
The lowest expectation value reached. When the observable is a Hamiltonian, it is the estimated ground-state energy. |
optimal_parameters |
ndarray
|
The weights that produced this value, of shape
|
n_iterations |
int
|
The number of iterations reported by the SciPy optimiser.
When there is no such count (a custom callable, |
n_evaluations |
int
|
The number of circuit evaluations that the optimiser made.
With a SciPy method it is usually higher than |
success |
bool
|
Whether the run converged. A SciPy optimiser reports it. A
custom callable reports it only if it returns
|
message |
str
|
The termination message of the optimiser. For a custom callable that reported nothing, it gives the cost before and after the run. |
history |
tuple[float, ...]
|
The expectation value at every evaluation, in order. Plot it to see whether the run converged or stalled. It is a tuple, so nothing can be appended to it after the run. |
__post_init__()
Make the array field read-only.
Freezing the dataclass prevents a field from being reassigned, but it does not
prevent writing into the array that the field holds, and a result that has been
written into still reports the scalar values of the original run. The result
therefore keeps its own copy, so the array it was built from can still be
changed by the caller. To get a writable copy, use
np.array(result.optimal_parameters).
minimize_expectation(circuit, *, initial_parameters=None, optimizer='L-BFGS-B', max_iterations=None, tol=None, random_state=42, callback=None)
Minimise <H>(θ) for a circuit whose parameters are all variational.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circuit
|
Circuit
|
A core NumPy |
required |
initial_parameters
|
ndarray | None
|
The starting |
None
|
optimizer
|
Optimizer
|
The name of a |
'L-BFGS-B'
|
max_iterations
|
int | None
|
The maximum number of iterations. It is passed to SciPy as
|
None
|
tol
|
float | None
|
The tolerance, passed to |
None
|
random_state
|
int | None
|
The seed for the random starting point, which also seeds the
perturbations of |
42
|
callback
|
Callable[[ndarray], None] | None
|
An optional function called after every iteration with the
current parameters. It is passed to SciPy, and |
None
|
Returns:
| Type | Description |
|---|---|
VariationalResult
|
A |
Example
from eigenkit import Circuit, Variational
from eigenkit.core.ir import Observable
from eigenkit.core.variational import minimize_expectation
# One qubit, one trainable rotation, H = Z. The minimum is -1.
circuit = Circuit(
blocks=[Variational(depth=1)],
observables=Observable.from_string("Z0"),
provider="numpy",
)
result = minimize_expectation(circuit, random_state=42)
print(round(result.optimal_value, 6)) # -1.0
print(result.success, result.n_iterations) # True 4