qml.workflow.jacobian_products.DeviceDerivatives¶
- class DeviceDerivatives(device, execution_config=None)[source]¶
Bases:
pennylane.workflow.jacobian_products.JacobianProductCalculator
Calculate jacobian products via a device provided jacobian. This class relies on
qml.devices.Device.compute_derivatives
.- Parameters
device (Union[pennylane.Device, pennylane.devices.Device]) – the device for execution and derivatives. Must support first order gradients with the requested configuration.
execution_config (pennylane.devices.ExecutionConfig) – a datastructure containing the options needed to fully describe the execution. Only used with
pennylane.devices.Device
from the new device interface.gradient_kwargs (dict) – a dictionary of keyword arguments for the gradients. Only used with a
Device
from the old device interface.
Examples:
>>> device = qml.device('default.qubit') >>> config = qml.devices.ExecutionConfig(gradient_method="adjoint") >>> jpc = DeviceDerivatives(device, config, {})
This same class can also be used with the old device interface.
>>> device = qml.device('lightning.qubit', wires=5) >>> gradient_kwargs = {"method": "adjoint_jacobian"} >>> jpc_lightning = DeviceDerivatives(device, gradient_kwargs=gradient_kwargs)
Technical comments on caching and calculating the gradients on execution:
In order to store results and Jacobians for the backward pass during the forward pass, the
_jacs_cache
and_results_cache
properties areLRUCache
objects with a maximum size of 10. In the current execution pipeline, only one batch will be used per instance, but a size of 10 adds some extra flexibility for future uses.Note that batches of identically looking
QuantumScript
s that are different instances will be cached separately. This is because thehash
ofQuantumScript
is expensive, as it requires inspecting all its constituents, which is not worth the effort in this case.When a forward pass with
execute_and_cache_jacobian()
is called, both the results and the jacobian for the object are stored.>>> tape = qml.tape.QuantumScript([qml.RX(1.0, wires=0)], [qml.expval(qml.Z(0))]) >>> batch = (tape, ) >>> with device.tracker: ... results = jpc.execute_and_cache_jacobian(batch ) >>> results (0.5403023058681398,) >>> device.tracker.totals {'execute_and_derivative_batches': 1, 'executions': 1, 'derivatives': 1} >>> jpc._jacs_cache LRUCache({5660934048: (array(-0.84147098),)}, maxsize=10, currsize=1)
Then when the vjp, jvp, or jacobian is requested, that cached value is used instead of requesting from the device again.
>>> with device.tracker: ... vjp = jpc.compute_vjp(batch , (0.5, ) ) >>> vjp (array([-0.42073549]),) >>> device.tracker.totals {}
Methods
compute_jacobian
(tapes)Compute the full Jacobian for a batch of tapes.
compute_vjp
(tapes, dy)Compute the vjp for a given batch of tapes.
execute_and_cache_jacobian
(tapes)Forward pass used to cache the results and jacobians.
execute_and_compute_jacobian
(tapes)Compute the results and the full Jacobian for a batch of tapes.
execute_and_compute_jvp
(tapes, tangents)Calculate both the results for a batch of tapes and the jvp.
- compute_jacobian(tapes)[source]¶
Compute the full Jacobian for a batch of tapes.
This method is required to compute Jacobians in the
jax-jit
interface- Parameters
tapes – the batch of tapes to take the Jacobian of
- Returns
the full jacobian
- Return type
TensorLike
- Side Effects:
caches the newly computed jacobian if it wasn’t already present in the cache.
Examples:
For an instance of
DeviceDerivatives
jpc
, we have:>>> tape0 = qml.tape.QuantumScript([qml.RX(0.1, wires=0)], [qml.expval(qml.Z(0))]) >>> tape1 = qml.tape.QuantumScript([qml.RY(0.2, wires=0)], [qml.expval(qml.Z(0)), qml.expval(qml.X(0))]) >>> batch = (tape0, tape1) >>> jpc.compute_jacobian(batch) (array(-0.09983342), (array(-0.19866933), array(0.98006658)))
While this method could support non-scalar parameters in theory, no implementation currently supports jacobians with non-scalar parameters.
- compute_vjp(tapes, dy)[source]¶
Compute the vjp for a given batch of tapes.
This method is used by autograd, torch, and tensorflow to compute VJPs.
- Parameters
tapes (tuple[~.QuantumScript]) – the batch of tapes to take the derivatives of
dy (tuple[tuple[TensorLike]]) – the derivatives of the results of an execution. The
i
th entry (cotangent) corresponds to thei
th tape, and thej
th entry of thei
th cotangent corresponds to thej
th return value of thei
th tape.
- Returns
the vector jacobian product.
- Return type
TensorLike
- Side Effects:
caches the newly computed jacobian if it wasn’t already present in the cache.
Examples:
For an instance of
DeviceDerivatives
jpc
, we have:>>> tape0 = qml.tape.QuantumScript([qml.RX(0.1, wires=0)], [qml.expval(qml.Z(0))]) >>> tape1 = qml.tape.QuantumScript([qml.RY(0.2, wires=0)], [qml.expval(qml.Z(0)), qml.expval(qml.X(0))]) >>> batch = (tape0, tape1) >>> dy0 = (0.5, ) >>> dy1 = (2.0, 3.0) >>> dys = (dy0, dy1) >>> vjps = jpc.compute_vjp(batch, dys) >>> vjps (array([-0.04991671]), array([2.54286107])) >>> expected_vjp0 = 0.5 * -np.sin(0.1) >>> qml.math.allclose(vjps[0], expected_vjp0) True >>> expected_jvp1 = 2.0 * -np.sin(0.2) + 3.0 * np.cos(0.2) >>> qml.math.allclose(vjps[1], expected_vjp1) True
While this method could support non-scalar parameters in theory, no implementation currently supports jacobians with non-scalar parameters.
- execute_and_cache_jacobian(tapes)[source]¶
Forward pass used to cache the results and jacobians.
- Parameters
tapes (tuple[~.QuantumScript]) – the batch of tapes to execute and take derivatives of
- Returns
the results of the execution.
- Return type
ResultBatch
- Side Effects:
Caches both the results and jacobian into
_results_cache
and_jacs_cache
.
- execute_and_compute_jacobian(tapes)[source]¶
Compute the results and the full Jacobian for a batch of tapes.
This method is required to compute Jacobians in the
jax-jit
interface- Parameters
tapes (tuple[QuantumScript]) – the batch of tapes to take the derivatives of
Examples:
For an instance of
JacobianProductCalculator
jpc
, we have:>>> tape0 = qml.tape.QuantumScript([qml.RX(0.1, wires=0)], [qml.expval(qml.Z(0))]) >>> tape1 = qml.tape.QuantumScript([qml.RY(0.2, wires=0)], [qml.expval(qml.Z(0)), qml.expval(qml.X(0))]) >>> batch = (tape0, tape1) >>> results, jacs = jpc.execute_and_compute_jacobian(batch) >>> results (0.9950041652780258, (0.9800665778412417, 0.19866933079506116)) >>> jacs (array(-0.09983342), (array(-0.19866933), array(0.98006658)))
While this method could support non-scalar parameters in theory, no implementation currently supports jacobians with non-scalar parameters.
- execute_and_compute_jvp(tapes, tangents)[source]¶
Calculate both the results for a batch of tapes and the jvp.
This method is required to compute JVPs in the JAX interface.
- Parameters
tapes (tuple[~.QuantumScript]) – The batch of tapes to take the derivatives of
tangents (Sequence[Sequence[TensorLike]]) – the tangents for the parameters of the tape. The
i
th tangent corresponds to thei
th tape, and thej
th entry into a tangent entry corresponds to thej
th trainable parameter of the tape.
- Returns
the results of the execution and the jacobian vector product
- Return type
ResultBatch, TensorLike
- Side Effects:
caches newly computed results or jacobians if they were not already cached.
Examples:
For an instance of
DeviceDerivatives
jpc
, we have:>>> tape0 = qml.tape.QuantumScript([qml.RX(0.1, wires=0)], [qml.expval(qml.Z(0))]) >>> tape1 = qml.tape.QuantumScript([qml.RY(0.2, wires=0)], [qml.expval(qml.Z(0))]) >>> batch = (tape0, tape1) >>> tangents0 = (1.5, ) >>> tangents1 = (2.0, ) >>> tangents = (tangents0, tangents1) >>> results, jvps = jpc.execute_and_compute_jvp(batch, tangents) >>> expected_results = (np.cos(0.1), np.cos(0.2)) >>> qml.math.allclose(results, expected_results) True >>> jvps (array(-0.14975012), array(-0.39733866)) >>> expected_jvps = 1.5 * -np.sin(0.1), 2.0 * -np.sin(0.2) >>> qml.math.allclose(jvps, expected_jvps) True
While this method could support non-scalar parameters in theory, no implementation currently supports jacobians with non-scalar parameters.