qml.defer_measurements¶
- defer_measurements(tape, reduce_postselected=True, **kwargs)[source]¶
Quantum function transform that substitutes operations conditioned on measurement outcomes to controlled operations.
This transform uses the deferred measurement principle and applies to qubit-based quantum functions.
Support for mid-circuit measurements is device-dependent. If a device doesn’t support mid-circuit measurements natively, then the QNode will apply this transform.
Note
The transform uses the
ctrl()
transform to implement operations controlled on mid-circuit measurement outcomes. The set of operations that can be controlled as such depends on the set of operations supported by the chosen device.Note
Devices that inherit from
QubitDevice
must be initialized with an additional wire for each mid-circuit measurement after which the measured wire is reused or reset fordefer_measurements
to transform the quantum tape correctly.Note
This transform does not change the list of terminal measurements returned by the quantum function.
Note
When applying the transform on a quantum function that contains the
Snapshot
instruction, state information corresponding to simulating the transformed circuit will be obtained. No post-measurement states are considered.Warning
state()
is not supported with thedefer_measurements
transform. Additionally,probs()
,sample()
andcounts()
can only be used withdefer_measurements
if wires or an observable are explicitly specified.Warning
defer_measurements
does not support using custom wire labels if any measured wires are reused or reset.- Parameters
tape (QNode or QuantumTape or Callable) – a quantum circuit.
reduce_postselected (bool) – Whether or not to use postselection information to reduce the number of operations and control wires in the output tape. Active by default.
- Returns
The transformed circuit as described in
qml.transform
.- Return type
qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]
- Raises
ValueError – If custom wire labels are used with qubit reuse or reset
ValueError – If any measurements with no wires or observable are present
ValueError – If continuous variable operations or measurements are present
ValueError – If using the transform with any device other than
default.qubit
and postselection is used
Example
Suppose we have a quantum function with mid-circuit measurements and conditional operations:
def qfunc(par): qml.RY(0.123, wires=0) qml.Hadamard(wires=1) m_0 = qml.measure(1) qml.cond(m_0, qml.RY)(par, wires=0) return qml.expval(qml.Z(0))
The
defer_measurements
transform allows executing such quantum functions without having to perform mid-circuit measurements:>>> dev = qml.device('default.qubit', wires=2) >>> transformed_qfunc = qml.defer_measurements(qfunc) >>> qnode = qml.QNode(transformed_qfunc, dev) >>> par = np.array(np.pi/2, requires_grad=True) >>> qnode(par) tensor(0.43487747, requires_grad=True)
We can also differentiate parameters passed to conditional operations:
>>> qml.grad(qnode)(par) tensor(-0.49622252, requires_grad=True)
Reusing and resetting measured wires will work as expected with the
defer_measurements
transform:dev = qml.device("default.qubit", wires=3) @qml.qnode(dev) def func(x, y): qml.RY(x, wires=0) qml.CNOT(wires=[0, 1]) m_0 = qml.measure(1, reset=True) qml.cond(m_0, qml.RY)(y, wires=0) qml.RX(np.pi/4, wires=1) return qml.probs(wires=[0, 1])
Executing this QNode:
>>> pars = np.array([0.643, 0.246], requires_grad=True) >>> func(*pars) tensor([0.76960924, 0.13204407, 0.08394415, 0.01440254], requires_grad=True)
Usage Details
By default,
defer_measurements
makes use of postselection information of mid-circuit measurements in the circuit in order to reduce the number of controlled operations and control wires. We can explicitly switch this feature off and compare the created circuits with and without this optimization. Consider the following circuit:@qml.qnode(qml.device("default.qubit")) def node(x): qml.RX(x, 0) qml.RX(x, 1) qml.RX(x, 2) mcm0 = qml.measure(0, postselect=0, reset=False) mcm1 = qml.measure(1, postselect=None, reset=True) mcm2 = qml.measure(2, postselect=1, reset=False) qml.cond(mcm0+mcm1+mcm2==1, qml.RX)(0.5, 3) return qml.expval(qml.Z(0) @ qml.Z(3))
Without the optimization, we find three gates controlled on the three measured qubits. They correspond to the combinations of controls that satisfy the condition
mcm0+mcm1+mcm2==1
.>>> print(qml.draw(qml.defer_measurements(node, reduce_postselected=False))(0.6)) 0: ──RX(0.60)──|0⟩⟨0|─╭●─────────────────────────────────────────────┤ ╭<Z@Z> 1: ──RX(0.60)─────────│──╭●─╭X───────────────────────────────────────┤ │ 2: ──RX(0.60)─────────│──│──│───|1⟩⟨1|─╭○────────╭○────────╭●────────┤ │ 3: ───────────────────│──│──│──────────├RX(0.50)─├RX(0.50)─├RX(0.50)─┤ ╰<Z@Z> 4: ───────────────────╰X─│──│──────────├○────────├●────────├○────────┤ 5: ──────────────────────╰X─╰●─────────╰●────────╰○────────╰○────────┤
If we do not explicitly deactivate the optimization, we obtain a much simpler circuit:
>>> print(qml.draw(qml.defer_measurements(node))(0.6)) 0: ──RX(0.60)──|0⟩⟨0|─╭●─────────────────┤ ╭<Z@Z> 1: ──RX(0.60)─────────│──╭●─╭X───────────┤ │ 2: ──RX(0.60)─────────│──│──│───|1⟩⟨1|───┤ │ 3: ───────────────────│──│──│──╭RX(0.50)─┤ ╰<Z@Z> 4: ───────────────────╰X─│──│──│─────────┤ 5: ──────────────────────╰X─╰●─╰○────────┤
There is only one controlled gate with only one control wire.