pennylane_backend
eigenkit.core.backends.pennylane_backend
The PennyLane backend.
It declares gradient_method = "native": the PyTorch interface of PennyLane
carries the gradients through the tape, so the parameter-shift rule is not needed
here.
compile() builds the device, both qnodes (one for NumPy and one for PyTorch)
and the Pauli operators once. Each run() only binds the parameters into the
cached qnodes. A finite number of shots is set on each qnode with
qml.set_shots, because passing shots= to qml.device was deprecated in
PennyLane 0.43.
Example
from eigenkit import Circuit, Encoding, Variational, all_z
blocks = [Encoding(in_features=4), Variational(depth=2)]
# by registered name
circuit = Circuit(blocks=blocks, observables=all_z(4), provider="pennylane")
# or with the config, when a device setting matters
from eigenkit.configs import PennyLaneConfig
circuit = Circuit(
blocks=blocks,
observables=all_z(4),
provider=PennyLaneConfig(device_name="lightning.qubit"),
)
This backend needs the pennylane extra.
PennyLaneBackend
The provider for PennyLane devices. It is registered as "pennylane".
It is internal: you select this backend with provider="pennylane" or by
passing a
PennyLaneConfig,
not by building this class.
capabilities
property
What this backend supports.
It runs every gate, evaluates a batch of inputs in one call, can sample a
finite number of shots, and has no limit on the number of qubits. It also
provides the two features that Iterative Quantum Amplitude Estimation
(IQAE) needs: the dense unitary of a callable oracle, through
qml.matrix, and sampling of the ancilla qubit by the provider, for
shot_mode="provider". See
Capabilities.
__init__(config=None, name='pennylane')
Build the backend from a device configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
PennyLaneConfig
|
The device settings. Default: |
None
|
name
|
str
|
The registry name, used in error messages. Default:
|
'pennylane'
|
compile(circuit, observables)
Prepare circuit for repeated evaluation of observables.
A Circuit calls it once and reuses the result for every call, so the
expensive work belongs here.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circuit
|
CircuitSpec
|
The circuit to prepare. |
required |
observables
|
Sequence[Observable]
|
The observables to measure. |
required |
Returns:
| Type | Description |
|---|---|
PennyLaneCompiled
|
The prepared circuit, as a |
PennyLaneCompiled
Bases: CompiledCircuit
A circuit prepared for PennyLane.
It is internal:
PennyLaneBackend.compile()
builds it and returns it, and it is never built directly. It has a
run_torch() method, so the PyTorch interface differentiates through
PennyLane itself instead of falling back to the parameter-shift rule.
__init__(circuit, observables, config)
Prepare the circuit for repeated evaluation.
PennyLaneBackend.compile() builds this object, so there is no reason to
build one directly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
circuit
|
CircuitSpec
|
The circuit to prepare. |
required |
observables
|
Sequence[Observable]
|
The observables to measure. |
required |
config
|
PennyLaneConfig
|
The device settings. |
required |
jacobian(weights, inputs, *, need_input_grad=False)
Compute the gradient with PennyLane, using the configured diff_method.
The gradient code of the core package looks for a jacobian method, and
prefers it to its own parameter-shift rule, which works on any backend.
Here PennyLane differentiates the gate angles with its own engine (adjoint,
backpropagation or parameter shift, as diff_method selects). These
gate-angle gradients are then mapped onto the weights and inputs with the
shared symbolic chain-rule factors, so angles built with Scale,
Multiply, Sum or Negate are handled. It takes a single sample (1-D)
only, and the caller loops over a batch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weights
|
ndarray
|
The parameter vector (1-D). |
required |
inputs
|
ndarray
|
The input features of one sample (1-D). |
required |
need_input_grad
|
bool
|
If True, also compute the jacobian with
respect to the inputs. Default: |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
weight_jacobian |
ndarray
|
The jacobian with respect to the weights. |
input_jacobian |
ndarray | None
|
The jacobian with respect to the inputs, or |
probabilities(weights, inputs)
Return the full measurement distribution over the 2**n_qubits basis states.
Qubit 0 is the most significant bit. The distribution comes from
qml.probs, through a qnode that is built on first use and then cached.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weights
|
ndarray
|
The parameter vector (1-D). |
required |
inputs
|
ndarray
|
The input features of one sample (1-D). |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The probabilities, of shape |
run(weights, inputs)
Evaluate the circuit and return one expectation value per observable.
It follows the CompiledCircuit contract: the result has shape (n_obs,)
when weights and inputs are both 1-D, and (B, n_obs) otherwise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weights
|
ndarray
|
The parameter vector: 1-D for one sample, or 2-D with a leading batch axis. |
required |
inputs
|
ndarray
|
The input features, with the same 1-D or 2-D
shape convention as |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The expectation values, one for each observable. |
run_torch(weights, inputs)
Evaluate the circuit on PyTorch tensors, keeping the result on the tape.
This is the path of native automatic differentiation: the gradients flow through the simulator itself instead of through the parameter-shift fallback.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weights
|
Tensor
|
The parameter vector, 1-D or batched. |
required |
inputs
|
Tensor
|
The input features, 1-D or batched. |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
A PyTorch tensor of expectation values, which can still be differentiated. |
statevector(weights, inputs, initial_state=None)
Return the full complex statevector, computed with qml.state().
Qubit 0 is the most significant bit. initial_state is added at the
start of the circuit as a qml.StatePrep, so the evolution starts from
that state instead of |0…0⟩. A 2-D inputs batch returns an array of
shape (B, 2**n_qubits). The batch is evaluated in a loop, because
reading the state does not use the fast path of parameter broadcasting.
This method needs exact mode, because qml.state is not defined with a
finite number of shots.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weights
|
ndarray
|
The parameter vector (1-D). |
required |
inputs
|
ndarray
|
The input features: 1-D for one sample, or 2-D with a leading batch axis. |
required |
initial_state
|
ndarray | None
|
The state to start from, instead
of |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
The statevector, of shape |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If the configuration sets a finite number of
|
zero_state_probability(weights, inputs)
Return the probability of measuring |0…0⟩ after the circuit has run.
On first use, it builds a separate qnode that returns qml.probs(...),
and the method returns entry 0 of that distribution. The qnode uses the
same device as the expectation-value qnodes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
weights
|
ndarray
|
The parameter vector (1-D). |
required |
inputs
|
ndarray
|
The input features of one sample (1-D). |
required |
Returns:
| Type | Description |
|---|---|
float
|
The probability, a float in |
PennyLaneConfig
dataclass
Device settings, used for every qml.device() call.
Pass it directly to Circuit(provider=PennyLaneConfig(...)), or pass it to
register() under a name to reuse it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
device_name
|
str
|
The identifier of the PennyLane simulator plugin:
|
'default.qubit'
|
shots
|
int | None
|
|
None
|
diff_method
|
str
|
The PennyLane differentiation method. With |
'best'
|
make_backend()
Build the backend that this configuration describes.
The provider layer calls it when a configuration is passed as provider=.
There is no reason to call it directly.
Returns:
| Type | Description |
|---|---|
'PennyLaneBackend'
|
The |