qml.transforms.diagonalize_measurements¶
- diagonalize_measurements(tape, supported_base_obs=(<class 'pennylane.ops.qubit.non_parametric_ops.PauliZ'>, <class 'pennylane.ops.identity.Identity'>), to_eigvals=False)[source]¶
Diagonalize a set of measurements into the standard basis. Raises an error if the measurements do not commute.
See the usage details for more information on which measurements are supported.
- Parameters
tape (QNode or QuantumScript or Callable) – The quantum circuit to modify the measurements of.
supported_base_obs (Optional, Iterable(Observable)) – A list of supported base observable classes. Allowed observables are
qml.X
,qml.Y
,qml.Z
,qml.Hadamard
andqml.Identity
. Z and Identity are always treated as supported, regardless of input. If no list is provided, the transform will diagonalize everything into the Z basis. If a list is provided, only unsupported observables will be diagonalized to the Z basis.
- Returns
The transformed circuit as described in
qml.transform
.- Return type
qnode (QNode) or tuple[List[QuantumScript], function]
Note
An error will be raised if non-commuting terms are encountered. To avoid non-commuting terms in circuit measurements, the
split_non_commuting
transform can be applied.This transform will diagonalize what it can, i.e.,
qml.X
,qml.Y
,qml.Z
,qml.Hadamard
,qml.Identity
, or a linear combination of them. Any unrecognized observable will not raise an error, deferring to the device’s validation for supported measurements later on. Lastly, ifdiagonalize_measurements
produces additional gates that the device does not support, thedecompose()
transform should be applied to ensure that the additional gates are decomposed to those that the device supports.Examples:
This transform allows us to transform QNode measurements into the measurement basis by adding the relevant diagonalizing gates to the end of the tape operations.
from pennylane.transforms import diagonalize_measurements dev = qml.device("default.qubit") @diagonalize_measurements @qml.qnode(dev) def circuit(x): qml.RY(x[0], wires=0) qml.RX(x[1], wires=1) return qml.expval(qml.X(0) @ qml.Z(1)), qml.var(0.5 * qml.Y(2) + qml.X(0))
Applying the transform appends the relevant gates to the end of the circuit to allow measurements to be in the Z basis, so the original circuit
>>> print(qml.draw(circuit, level=0)([np.pi/4, np.pi/4])) 0: ──RY(0.79)─┤ ╭<X@Z> ╭Var[𝓗(0.50)] 1: ──RX(0.79)─┤ ╰<X@Z> │ 2: ───────────┤ ╰Var[𝓗(0.50)]
becomes
>>> print(qml.draw(circuit)([np.pi/4, np.pi/4])) 0: ──RY(0.79)──H────┤ ╭<Z@Z> ╭Var[𝓗(0.50)] 1: ──RX(0.79)───────┤ ╰<Z@Z> │ 2: ──Z─────────S──H─┤ ╰Var[𝓗(0.50)]
>>> circuit([np.pi/4, np.pi/4]) (0.5, 0.75)
Usage Details
The transform diagonalizes observables from the local Pauli basis only, i.e. it diagonalizes X, Y, Z, and Hadamard. Any other observable will be unaffected:
measurements = [ qml.expval(qml.X(0) + qml.Hermitian([[1, 0], [0, 1]], wires=[1])) ] tape = qml.tape.QuantumScript(measurements=measurements) tapes, processsing_fn = diagonalize_measurements(tape)
>>> tapes[0].operations [H(0)] >>> tapes[0].measurements [expval(Z(0) + Hermitian(array([[1, 0], [0, 1]]), wires=[1]))]
The transform can also diagonalize only a subset of these operators. By default, the only supported base observable is Z. What if a backend device can handle X, Y and Z, but doesn’t provide support for Hadamard? We can set this by passing
supported_base_obs
to the transform. Let’s create a tape with some measurements:measurements = [ qml.expval(qml.X(0) + qml.Hadamard(1)), qml.expval(qml.X(0) + 0.2 * qml.Hadamard(1)), qml.var(qml.Y(2) + qml.X(0)), ] tape = qml.tape.QuantumScript(measurements=measurements) tapes, processing_fn = diagonalize_measurements( tape, supported_base_obs=[qml.X, qml.Y, qml.Z] )
Now
tapes
is a tuple containing a single tape with the updated measurements, where only the Hadamard gate has been diagonalized:>>> tapes[0].measurements [expval(X(0) + Z(1)), expval(X(0) + 0.2 * Z(1)), var(Y(2) + X(0))]