qml.tape.QubitParamShiftTape

class QubitParamShiftTape(name=None)[source]

Bases: pennylane.tape.tapes.jacobian_tape.JacobianTape

Quantum tape for qubit parameter-shift analytic differentiation method.

This class extends the jacobian method of the quantum tape to support analytic gradients of qubit operations using the parameter-shift rule. This gradient method returns exact gradients, and can be computed directly on quantum hardware. Simply pass method=analytic when computing the Jacobian:

>>> tape.jacobian(dev, method="analytic")

For more details on the quantum tape, please see JacobianTape.

Gradients of expectation values

For a variational evolution \(U(mathbf{p})\vert 0\rangle\) with \(N\) parameters \(mathbf{p}\),

consider the expectation value of an observable \(O\):

\[f(mathbf{p}) = \langle hat{O} \rangle(mathbf{p}) = \langle 0 \vert U(mathbf{p})^\dagger hat{O} U(mathbf{p}) \vert 0\rangle.\]

The gradient of this expectation value can be calculated using \(2N\) expectation values using the parameter-shift rule:

\[\frac{\partial f}{\partial mathbf{p}} = \frac{1}{2\sin s} \left[ f(mathbf{p} + s) - f(mathbf{p} -s) \right].\]

Gradients of variances

We can extend this to the variance, \(g(mathbf{p})=\langle hat{O}^2 \rangle (mathbf{p}) - [\langle hat{O} \rangle(mathbf{p})]^2\), by noting that:

\[\frac{\partial g}{\partial mathbf{p}}= \frac{\partial}{\partial mathbf{p}} \langle hat{O}^2 \rangle (mathbf{p}) - 2 f(mathbf{p}) \frac{\partial f}{\partial mathbf{p}}.\]

This results in \(4N + 1\) evaluations.

In the case where \(O\) is involutory (\(hat{O}^2 = I\)), the first term in the above expression vanishes, and we are simply left with

\[\frac{\partial g}{\partial mathbf{p}} = - 2 f(mathbf{p}) \frac{\partial f}{\partial mathbf{p}},\]

allowing us to compute the gradient using \(2N + 1\) evaluations.

data

Alias to get_parameters() and set_parameters() for backwards compatibilities with operations.

diagonalizing_gates

Returns the gates that diagonalize the measured wires such that they are in the eigenbasis of the circuit observables.

graph

Returns a directed acyclic graph representation of the recorded quantum circuit:

interface

automatic differentiation interface used by the quantum tape (if any)

measurements

Returns the measurements on the quantum tape.

num_params

Returns the number of trainable parameters on the quantum tape.

observables

Returns the observables on the quantum tape.

operations

Returns the operations on the quantum tape.

output_dim

The (inferred) output dimension of the quantum tape.

queue

Returns a list of objects in the annotated queue

trainable_params

Store or return a set containing the indices of parameters that support differentiability.

data

Alias to get_parameters() and set_parameters() for backwards compatibilities with operations.

diagonalizing_gates

Returns the gates that diagonalize the measured wires such that they are in the eigenbasis of the circuit observables.

Returns

the operations that diagonalize the observables

Return type

List[Operation]

graph

Returns a directed acyclic graph representation of the recorded quantum circuit:

>>> tape.graph
<pennylane.tape.circuit_graph.TapeCircuitGraph object at 0x7fcc0433a690>

Note that the circuit graph is only constructed once, on first call to this property, and cached for future use.

Returns

the circuit graph object

Return type

TapeCircuitGraph

interface

automatic differentiation interface used by the quantum tape (if any)

Type

str, None

measurements

Returns the measurements on the quantum tape.

Returns

list of recorded measurement processess

Return type

list[MeasurementProcess]

Example

with JacobianTape() as tape:
    qml.RX(0.432, wires=0)
    qml.RY(0.543, wires=0)
    qml.CNOT(wires=[0, 'a'])
    qml.RX(0.133, wires='a')
    expval(qml.PauliZ(wires=[0]))
>>> tape.measurements
[<pennylane.tape.measure.MeasurementProcess object at 0x7f10b2150c10>]
num_params

Returns the number of trainable parameters on the quantum tape.

observables

Returns the observables on the quantum tape.

Returns

list of recorded quantum operations

Return type

list[Observable]

Example

with JacobianTape() as tape:
    qml.RX(0.432, wires=0)
    qml.RY(0.543, wires=0)
    qml.CNOT(wires=[0, 'a'])
    qml.RX(0.133, wires='a')
    expval(qml.PauliZ(wires=[0]))
>>> tape.observables
[expval(PauliZ(wires=[0]))]
operations

Returns the operations on the quantum tape.

Returns

recorded quantum operations

Return type

list[Operation]

Example

with JacobianTape() as tape:
    qml.RX(0.432, wires=0)
    qml.RY(0.543, wires=0)
    qml.CNOT(wires=[0, 'a'])
    qml.RX(0.133, wires='a')
    expval(qml.PauliZ(wires=[0]))
>>> tape.operations
[RX(0.432, wires=[0]), RY(0.543, wires=[0]), CNOT(wires=[0, 'a']), RX(0.133, wires=['a'])]
output_dim

The (inferred) output dimension of the quantum tape.

queue

Returns a list of objects in the annotated queue

trainable_params

Store or return a set containing the indices of parameters that support differentiability. The indices provided match the order of appearence in the quantum circuit.

Setting this property can help reduce the number of quantum evaluations needed to compute the Jacobian; parameters not marked as trainable will be automatically excluded from the Jacobian computation.

The number of trainable parameters determines the number of parameters passed to set_parameters(), execute(), and jacobian(), and changes the default output size of methods jacobian() and get_parameters().

Note

Since the jacobian() method is not called for devices that support native backpropagation (such as default.qubit.tf and default.qubit.autograd), this property contains no relevant information when using backpropagation to compute gradients.

Example

with JacobianTape() as tape:
    qml.RX(0.432, wires=0)
    qml.RY(0.543, wires=0)
    qml.CNOT(wires=[0, 'a'])
    qml.RX(0.133, wires='a')
    expval(qml.PauliZ(wires=[0]))
>>> tape.trainable_params
{0, 1, 2}
>>> tape.trainable_params = {0} # set only the first parameter as free
>>> tape.get_parameters()
[0.432]
Parameters

param_indices (set[int]) – parameter indices

active_context()

Returns the currently active queuing context.

analytic_pd(idx, params, **options)

Generate the quantum tapes and classical post-processing function required to compute the gradient of the tape with respect to a single trainable tape parameter using an analytic method.

append(obj, **kwargs)

Append an object to the queue(s).

copy([copy_operations, tape_cls])

Returns a shallow copy of the quantum tape.

device_pd(device[, params])

Evaluate the gradient of the tape with respect to all trainable tape parameters by querying the provided device.

draw([charset])

Draw the quantum tape as a circuit diagram.

execute(device[, params])

Execute the tape on a quantum device.

execute_device(params, device)

Execute the tape on a quantum device.

expand([depth, stop_at, expand_measurements])

Expand all operations in the processed queue to a specific depth.

get_depth()

Depth of the quantum circuit.

get_info(obj)

Retrieves information of an object in the active queue.

get_parameters([trainable_only])

Return the parameters incident on the tape operations.

get_resources()

Resource requirements of a quantum circuit.

inv()

Inverts the processed operations.

jacobian(device[, params])

Compute the Jacobian of the parametrized quantum circuit recorded by the quantum tape.

numeric_pd(idx[, params])

Generate the tapes and postprocessing methods required to compute the gradient of a parameter using the finite-difference method.

parameter_shift(idx, params, **options)

Generate the tapes and postprocessing methods required to compute the gradient of a parameter using the parameter-shift method.

parameter_shift_var(idx, params, **options)

Generate the tapes and postprocessing methods required to compute the gradient of a parameter and its variance using the parameter-shift method.

recording()

Whether a queuing context is active and recording operations

remove(obj)

Remove an object from the queue(s) if it is in the queue(s).

set_parameters(params[, trainable_only])

Set the parameters incident on the tape operations.

update_info(obj, **kwargs)

Updates information of an object in the active queue.

classmethod active_context()

Returns the currently active queuing context.

analytic_pd(idx, params, **options)

Generate the quantum tapes and classical post-processing function required to compute the gradient of the tape with respect to a single trainable tape parameter using an analytic method.

Parameters
  • idx (int) – trainable parameter index to differentiate with respect to

  • params (list[Any]) – the quantum tape operation parameters

Returns

A tuple containing the list of generated tapes, in addition to a post-processing function to be applied to the evaluated tapes.

Return type

tuple[list[QuantumTape], function]

classmethod append(obj, **kwargs)

Append an object to the queue(s).

Parameters

obj – the object to be appended

copy(copy_operations=False, tape_cls=None)

Returns a shallow copy of the quantum tape.

Parameters
  • copy_operations (bool) – If True, the tape operations are also shallow copied. Otherwise, if False, the copied tape operations will simply be references to the original tape operations; changing the parameters of one tape will likewise change the parameters of all copies.

  • tape_cls (QuantumTape) – Cast the copied tape to a specific quantum tape subclass. If not provided, the same subclass is used as the original tape.

Returns

a shallow copy of the tape

Return type

QuantumTape

device_pd(device, params=None, **options)

Evaluate the gradient of the tape with respect to all trainable tape parameters by querying the provided device.

Parameters
  • device (Device, QubitDevice) – a PennyLane device that can execute quantum operations and return measurement statistics

  • params (list[Any]) – The quantum tape operation parameters. If not provided, the current tape parameter values are used (via get_parameters()).

draw(charset='unicode')

Draw the quantum tape as a circuit diagram.

Consider the following circuit as an example:

with QuantumTape() as tape:
    qml.Hadamard(0)
    qml.CRX(2.3, wires=[0, 1])
    qml.Rot(1.2, 3.2, 0.7, wires=[1])
    qml.CRX(-2.3, wires=[0, 1])
    qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))

We can draw the tape after construction:

>>> print(tape.draw())
0: ──H──╭C────────────────────────────╭C─────────╭┤ ⟨Z ⊗ Z⟩
1: ─────╰RX(2.3)──Rot(1.2, 3.2, 0.7)──╰RX(-2.3)──╰┤ ⟨Z ⊗ Z⟩
>>> print(tape.draw(charset="ascii"))
0: --H--+C----------------------------+C---------+| <Z @ Z>
1: -----+RX(2.3)--Rot(1.2, 3.2, 0.7)--+RX(-2.3)--+| <Z @ Z>
Parameters

charset (str, optional) – The charset that should be used. Currently, “unicode” and “ascii” are supported.

Raises

ValueError – if the given charset is not supported

Returns

the circuit representation of the tape

Return type

str

execute(device, params=None)

Execute the tape on a quantum device.

Parameters
  • device (Device) – a PennyLane device that can execute quantum operations and return measurement statistics

  • params (list[Any]) – The quantum tape operation parameters. If not provided, the current tape parameters are used (via get_parameters()).

Example

with JacobianTape() as tape:
    qml.RX(0.432, wires=0)
    qml.RY(0.543, wires=0)
    qml.CNOT(wires=[0, 'a'])
    qml.RX(0.133, wires='a')
    probs(wires=[0, 'a'])

If parameters are not provided, the existing tape parameters are used:

>>> dev = qml.device("default.qubit", wires=[0, 'a'])
>>> tape.execute(dev)
array([[8.84828969e-01, 3.92449987e-03, 4.91235209e-04, 1.10755296e-01]])

Parameters can be optionally passed during execution:

>>> tape.execute(dev, params=[1.0, 0.0, 1.0])
array([[0.5931328 , 0.17701835, 0.05283049, 0.17701835]])

Parameters provided for execution are temporary, and do not affect the tapes’ parameters in-place:

>>> tape.get_parameters()
[0.432, 0.543, 0.133]
execute_device(params, device)

Execute the tape on a quantum device.

This is a low-level method, intended to be called by an interface, and does not support autodifferentiation.

For more details on differentiable tape execution, see execute().

Parameters
  • device (Device) – a PennyLane device that can execute quantum operations and return measurement statistics

  • params (list[Any]) – The quantum tape operation parameters. If not provided, the current tape parameter values are used (via get_parameters()).

expand(depth=1, stop_at=None, expand_measurements=False)

Expand all operations in the processed queue to a specific depth.

Parameters
  • depth (int) – the depth the tape should be expanded

  • stop_at (Callable) – A function which accepts a queue object, and returns True if this object should not be expanded. If not provided, all objects that support expansion will be expanded.

  • expand_measurements (bool) – If True, measurements will be expanded to basis rotations and computational basis measurements.

Example

Consider the following nested tape:

with JacobianTape() as tape:
    qml.BasisState(np.array([1, 1]), wires=[0, 'a'])

    with JacobianTape() as tape2:
        qml.Rot(0.543, 0.1, 0.4, wires=0)

    qml.CNOT(wires=[0, 'a'])
    qml.RY(0.2, wires='a')
    probs(wires=0), probs(wires='a')

The nested structure is preserved:

>>> tape.operations
[BasisState(array([1, 1]), wires=[0, 'a']),
 <JacobianTape: wires=[0], params=3>,
 CNOT(wires=[0, 'a']),
 RY(0.2, wires=['a'])]

Calling .expand will return a tape with all nested tapes expanded, resulting in a single tape of quantum operations:

>>> new_tape = tape.expand()
>>> new_tape.operations
[PauliX(wires=[0]),
 PauliX(wires=['a']),
 Rot(0.543, 0.1, 0.4, wires=[0]),
 CNOT(wires=[0, 'a']),
 RY(0.2, wires=['a'])]
get_depth()

Depth of the quantum circuit.

Returns

Circuit depth, computed as the longest path in the circuit’s directed acyclic graph representation.

Return type

int

Example

with QuantumTape() as tape:
    qml.Hadamard(wires=0)
    qml.PauliX(wires=1)
    qml.CRX(2.3, wires=[0, 1])
    qml.Rot(1.2, 3.2, 0.7, wires=[1])
    qml.CRX(-2.3, wires=[0, 1])
    qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))

The depth can be obtained like so:

>>> tape.get_depth()
4
classmethod get_info(obj)

Retrieves information of an object in the active queue.

Parameters

obj – the object with metadata to be retrieved

Returns

object metadata

get_parameters(trainable_only=True)

Return the parameters incident on the tape operations.

The returned parameters are provided in order of appearance on the tape.

Parameters

trainable_only (bool) – if True, returns only trainable parameters

Example

with JacobianTape() as tape:
    qml.RX(0.432, wires=0)
    qml.RY(0.543, wires=0)
    qml.CNOT(wires=[0, 'a'])
    qml.RX(0.133, wires='a')
    expval(qml.PauliZ(wires=[0]))

By default, all parameters are trainable and will be returned:

>>> tape.get_parameters()
[0.432, 0.543, 0.133]

Setting the trainable parameter indices will result in only the specified parameters being returned:

>>> tape.trainable_params = {1} # set the second parameter as free
>>> tape.get_parameters()
[0.543]

The trainable_only argument can be set to False to instead return all parameters:

>>> tape.get_parameters(trainable_only=False)
[0.432, 0.543, 0.133]
get_resources()

Resource requirements of a quantum circuit.

Returns

how many times constituent operations are applied

Return type

dict[str, int]

Example

with qml.tape.QuantumTape() as tape:
    qml.Hadamard(wires=0)
    qml.RZ(0.26, wires=1)
    qml.CNOT(wires=[1, 0])
    qml.Rot(1.8, -2.7, 0.2, wires=0)
    qml.Hadamard(wires=1)
    qml.CNOT(wires=[0, 1])
    qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))

Asking for the resources produces a dictionary as shown below:

>>> tape.get_resources()
{'Hadamard': 2, 'RZ': 1, 'CNOT': 2, 'Rot': 1}
inv()

Inverts the processed operations.

Inversion is performed in-place.

Note

This method only inverts the quantum operations/unitary recorded by the quantum tape; state preparations and measurements are left unchanged.

Example

with JacobianTape() as tape:
    qml.BasisState(np.array([1, 1]), wires=[0, 'a'])
    qml.RX(0.432, wires=0)
    qml.Rot(0.543, 0.1, 0.4, wires=0).inv()
    qml.CNOT(wires=[0, 'a'])
    probs(wires=0), probs(wires='a')

This tape has the following properties:

>>> tape.operations
[BasisState(array([1, 1]), wires=[0, 'a']),
 RX(0.432, wires=[0]),
 Rot.inv(0.543, 0.1, 0.4, wires=[0]),
 CNOT(wires=[0, 'a'])]
>>> tape.get_parameters()
[array([1, 1]), 0.432, 0.543, 0.1, 0.4]

Here, let’s set some trainable parameters:

>>> tape.trainable_params = {1, 2}
>>> tape.get_parameters()
[0.432, 0.543]

Inverting the tape:

>>> tape.inv()
>>> tape.operations
[BasisState(array([1, 1]), wires=[0, 'a']),
 CNOT.inv(wires=[0, 'a']),
 Rot(0.543, 0.1, 0.4, wires=[0]),
 RX.inv(0.432, wires=[0])]

Tape inversion also modifies the order of tape parameters:

>>> tape.get_parameters(trainable_only=False)
[array([1, 1]), 0.543, 0.1, 0.4, 0.432]
>>> tape.get_parameters(trainable_only=True)
[0.543, 0.432]
>>> tape.trainable_params
{1, 4}
jacobian(device, params=None, **options)[source]

Compute the Jacobian of the parametrized quantum circuit recorded by the quantum tape.

The quantum tape can be interpreted as a simple \(\mathbb{R}^m \to \mathbb{R}^n\) function, mapping \(m\) (trainable) gate parameters to \(n\) measurement statistics, such as expectation values or probabilities.

By default, the Jacobian will be computed with respect to all parameters on the quantum tape. This can be modified by setting the trainable_params attribute of the tape.

The Jacobian can be computed using several methods:

  • Finite differences ('numeric'). The first-order method evaluates the circuit at \(n+1\) points of the parameter space, the second-order method at \(2n\) points, where n = tape.num_params.

  • Analytic method ('analytic'). Analytic, if implemented by the inheriting quantum tape.

  • Best known method for each parameter ('best'): uses the analytic method if possible, otherwise finite difference.

  • Device method ('device'): Delegates the computation of the Jacobian to the device executing the circuit. Only supported by devices that provide their own method for computing derivatives; support can be checked by querying the device capabilities: dev.capabilities()['provides_jacobian'] must return True. Examples of supported devices include the experimental "default.tensor.tf" device.

Note

The finite difference method is sensitive to statistical noise in the circuit output, since it compares the output at two points infinitesimally close to each other. Hence the 'F' method works best with exact expectation values when using simulator devices.

Parameters
  • device (Device, QubitDevice) – a PennyLane device that can execute quantum operations and return measurement statistics

  • params (list[Any]) – The quantum tape operation parameters. If not provided, the current tape parameter values are used (via get_parameters()).

Keyword Arguments
  • method="best" (str) – The differentiation method. Must be one of "numeric", "analytic", "best", or "device".

  • h=1e-7 (float) – finite difference method step size

  • order=1 (int) – The order of the finite difference method to use. 1 corresponds to forward finite differences, 2 to centered finite differences.

shift=pi/2 (float): the size of the shift for two-term parameter-shift gradient computations

Returns

2-dimensional array of shape (tape.num_params, tape.output_dim)

Return type

array[float]

Example

with JacobianTape() as tape:
    qml.RX(0.432, wires=0)
    qml.RY(0.543, wires=0)
    qml.CNOT(wires=[0, 'a'])
    qml.RX(0.133, wires='a')
    probs(wires=[0, 'a'])

If parameters are not provided, the existing tape parameters are used:

>>> dev = qml.device("default.qubit", wires=[0, 'a'])
>>> tape.jacobian(dev)
array([[-0.178441  , -0.23358253, -0.05892804],
       [-0.00079144, -0.00103601,  0.05892804],
       [ 0.00079144,  0.00103601,  0.00737611],
       [ 0.178441  ,  0.23358253, -0.00737611]])

Parameters can be optionally passed during execution:

>>> tape.jacobian(dev, params=[1.0, 0.0, 1.0])
array([[-3.24029934e-01, -9.99200722e-09, -3.24029934e-01],
       [-9.67055711e-02, -2.77555756e-09,  3.24029935e-01],
       [ 9.67055709e-02,  3.05311332e-09,  9.67055709e-02],
       [ 3.24029935e-01,  1.08246745e-08, -9.67055711e-02]])

Parameters provided for execution are temporary, and do not affect the tapes’ parameters in-place:

>>> tape.get_parameters()
[0.432, 0.543, 0.133]

Explicitly setting the trainable parameters can significantly reduce computational resources, as non-trainable parameters are ignored during the computation:

>>> tape.trainable_params = {0} # set only the first parameter as trainable
>>> tape.jacobian(dev)
array([[-0.178441  ],
       [-0.00079144],
       [ 0.00079144],
       [ 0.178441  ]])

If a tape has no trainable parameters, the Jacobian will be empty:

>>> tape.trainable_params = {}
>>> tape.jacobian(dev)
array([], shape=(4, 0), dtype=float64)
numeric_pd(idx, params=None, **options)

Generate the tapes and postprocessing methods required to compute the gradient of a parameter using the finite-difference method.

Parameters
  • idx (int) – trainable parameter index to differentiate with respect to

  • params (list[Any]) – The quantum tape operation parameters. If not provided, the current tape parameter values are used (via get_parameters()).

Keyword Arguments
  • h=1e-7 (float) – finite difference method step size

  • order=1 (int) – The order of the finite difference method to use. 1 corresponds to forward finite differences, 2 to centered finite differences.

Returns

A tuple containing the list of generated tapes, in addition to a post-processing function to be applied to the evaluated tapes.

Return type

tuple[list[QuantumTape], function]

parameter_shift(idx, params, **options)[source]

Generate the tapes and postprocessing methods required to compute the gradient of a parameter using the parameter-shift method.

Parameters
  • idx (int) – trainable parameter index to differentiate with respect to

  • params (list[Any]) – the quantum tape operation parameters

Keyword Arguments

shift=pi/2 (float) – the size of the shift for two-term parameter-shift gradient computations

Returns

A tuple containing the list of generated tapes, in addition to a post-processing function to be applied to the evaluated tapes.

Return type

tuple[list[QuantumTape], function]

parameter_shift_var(idx, params, **options)[source]

Generate the tapes and postprocessing methods required to compute the gradient of a parameter and its variance using the parameter-shift method.

Parameters
  • idx (int) – trainable parameter index to differentiate with respect to

  • params (list[Any]) – the quantum tape operation parameters

Keyword Arguments

shift=pi/2 (float) – the size of the shift for two-term parameter-shift gradient computations

Returns

A tuple containing the list of generated tapes, in addition to a post-processing function to be applied to the evaluated tapes.

Return type

tuple[list[QuantumTape], function]

classmethod recording()

Whether a queuing context is active and recording operations

classmethod remove(obj)

Remove an object from the queue(s) if it is in the queue(s).

Parameters

obj – the object to be removed

set_parameters(params, trainable_only=True)

Set the parameters incident on the tape operations.

Parameters
  • params (list[float]) – A list of real numbers representing the parameters of the quantum operations. The parameters should be provided in order of appearance in the quantum tape.

  • trainable_only (bool) – if True, set only trainable parameters

Example

with JacobianTape() as tape:
    qml.RX(0.432, wires=0)
    qml.RY(0.543, wires=0)
    qml.CNOT(wires=[0, 'a'])
    qml.RX(0.133, wires='a')
    expval(qml.PauliZ(wires=[0]))

By default, all parameters are trainable and can be modified:

>>> tape.set_parameters([0.1, 0.2, 0.3])
>>> tape.get_parameters()
[0.1, 0.2, 0.3]

Setting the trainable parameter indices will result in only the specified parameters being modifiable. Note that this only modifies the number of parameters that must be passed.

>>> tape.trainable_params = {0, 2} # set the first and third parameter as free
>>> tape.set_parameters([-0.1, 0.5])
>>> tape.get_parameters(trainable_only=False)
[-0.1, 0.2, 0.5]

The trainable_only argument can be set to False to instead set all parameters:

>>> tape.set_parameters([4, 1, 6], trainable_only=False)
>>> tape.get_parameters(trainable_only=False)
[4, 1, 6]
classmethod update_info(obj, **kwargs)

Updates information of an object in the active queue.

Parameters

obj – the object with metadata to be updated