Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow postselecting on mid-circuit measurement results #4442

Closed
wants to merge 179 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
179 commits
Select commit Hold shift + click to select a range
5ba3407
Added qubit reuse and reset
mudit2812 Jul 27, 2023
310d69b
Merge branch 'master' into qubit-reuse
mudit2812 Jul 27, 2023
7259e12
Fixed `defer_measurements`
mudit2812 Jul 27, 2023
8c17c50
Removed `MeasurementValue`
mudit2812 Jul 28, 2023
60c9a6e
Update type hint
mudit2812 Aug 2, 2023
1355956
Merge branch 'master' into mv-merge
mudit2812 Aug 2, 2023
1e15bba
Removed `MeasurementValue`
mudit2812 Jul 28, 2023
d72c2e2
Add VJP/JVP capabilities to `DefaultQubit2` (#4374)
eddddddy Jul 28, 2023
8265e8b
Initialize `DefaultQubit2` local rng from global rng if no seed provi…
albi3ro Jul 28, 2023
970508e
Changes to remove use of `Operator.__eq__` (#4398)
mudit2812 Jul 28, 2023
c11a12e
Make autoray changes backwards compatible (#4397)
eddddddy Jul 28, 2023
478a289
Raise warning in Operator `__eq__` and `__hash__` about change in beh…
mudit2812 Jul 28, 2023
dfc9b4b
Tensorflow qnode integration with DefaultQubit2 (#4393)
timmysilv Jul 28, 2023
12a6399
remove turning warning into error (#4409)
albi3ro Jul 28, 2023
d5291b5
Fix jax.ad deprecation. (#4403)
vincentmr Jul 31, 2023
a52ded0
fix `has_decomposition` for ControlledQubitUnitary (#4407)
timmysilv Aug 1, 2023
014c125
[sc-36527]: Add new robots.txt to doc build to hide latest build from…
rashidnhm Aug 1, 2023
dbd337e
Adding a `wire_order` kwarg to `Tensor.sparse_matrix()` (#4424)
BorjaRequena Aug 2, 2023
879ab47
Updated defer_measurements post-merge
mudit2812 Aug 2, 2023
91926fe
Fixed defer_measurements
mudit2812 Aug 2, 2023
28bf0bc
Updated test
mudit2812 Aug 2, 2023
35bbb59
Updated class signature
mudit2812 Aug 2, 2023
ad0dbce
Updating keys
mudit2812 Aug 2, 2023
db91b7b
Updated id process
mudit2812 Aug 2, 2023
e8c18e9
Merge branch 'master' into mv-merge
mudit2812 Aug 2, 2023
5ed0f1b
Merge branch 'mv-merge' into qubit-reuse
mudit2812 Aug 2, 2023
a5b7f9c
Updated changelog
mudit2812 Aug 2, 2023
2648ee3
Merge branch 'master' into mv-merge
mudit2812 Aug 2, 2023
f1ad394
Updated changelog
mudit2812 Aug 2, 2023
5dac8fe
Adds shots to experimental device interface and integrate with QNode …
albi3ro Aug 2, 2023
a44c3f8
QNSPSA bugfix (#4421)
albi3ro Aug 2, 2023
be4d1cd
Update changelog
mudit2812 Aug 2, 2023
5fe9e84
Updated defer_measurements for device wires
mudit2812 Aug 2, 2023
66aad3a
Merge branch 'mv-merge' into qubit-reuse
mudit2812 Aug 2, 2023
ddf1af9
Removed error from drawable layers
mudit2812 Aug 2, 2023
47bd0f2
Update changelog
mudit2812 Aug 3, 2023
22a0b51
Updated drawable layers test
mudit2812 Aug 3, 2023
4163b9d
Updated signatures
mudit2812 Aug 3, 2023
a24091b
Merge branch 'master' into mv-merge
mudit2812 Aug 3, 2023
50640b4
Updated signatures
mudit2812 Aug 3, 2023
a143a3b
Merge `v0.31.1-rc0` branch into master (#4428)
eddddddy Aug 2, 2023
6a8c9b1
Integrate `TransformProgram` with `QNode` (#4404)
albi3ro Aug 3, 2023
ca9db70
move multiprocessing pre-processing to preprocess (#4425)
timmysilv Aug 3, 2023
14f868e
Remove `parametric_ops.py` (#4434)
mudit2812 Aug 3, 2023
a9bee54
Updated signature
mudit2812 Aug 3, 2023
7baceb1
Merge branch 'master' into mv-merge
mudit2812 Aug 4, 2023
743d35e
Merge branch 'mv-merge' into qubit-reuse
mudit2812 Aug 4, 2023
d4daee2
Removed `dev_wires` arg
mudit2812 Aug 4, 2023
929158c
Fixed defer_measurements
mudit2812 Aug 4, 2023
e269b64
Fixed tests
mudit2812 Aug 4, 2023
83bd0fe
Fixed tests
mudit2812 Aug 4, 2023
d4febfa
Adding postselect functionality
mudit2812 Aug 4, 2023
f5cd749
Update mid_measure tests
mudit2812 Aug 4, 2023
e8702e3
Updated how queuing for `MidMeasureMP` works
mudit2812 Aug 4, 2023
a7303df
Updated tests
mudit2812 Aug 4, 2023
bbe3340
Merge branch 'mv-merge' into qubit-reuse
mudit2812 Aug 4, 2023
6c860a6
Added tests
mudit2812 Aug 4, 2023
20995f0
Merge branch 'master' into mv-merge
mudit2812 Aug 4, 2023
3cbe253
Add converter function for PySCF's CISD to PennyLane statevector (#4427)
Chiffafox Aug 4, 2023
f5e2ed2
Update mid_measure tests
mudit2812 Aug 4, 2023
cb25548
Updated how queuing for `MidMeasureMP` works
mudit2812 Aug 4, 2023
2e10b34
Updated tests
mudit2812 Aug 4, 2023
b125fd4
Added tests
mudit2812 Aug 4, 2023
93a7ded
Add converter function for PySCF's CISD to PennyLane statevector (#4427)
Chiffafox Aug 4, 2023
5f3af10
Fixed merge conflicts
mudit2812 Aug 4, 2023
a31201f
Updated init
mudit2812 Aug 4, 2023
a42244a
Merge branch 'mv-merge' into qubit-reuse
mudit2812 Aug 4, 2023
dc0c71b
Merge branch 'qubit-reuse' into mcm-postselect
mudit2812 Aug 4, 2023
4a24576
Reverted queuing change
mudit2812 Aug 8, 2023
3758bc0
Trigger CI
mudit2812 Aug 8, 2023
4e8ef88
Added documentation
mudit2812 Aug 8, 2023
8ec73e9
Trigger CI
mudit2812 Aug 8, 2023
9b64d99
Trigger CI
mudit2812 Aug 8, 2023
b200335
Update docs
mudit2812 Aug 8, 2023
0256fd4
Reverted queuing change
mudit2812 Aug 8, 2023
1ac527c
Trigger CI
mudit2812 Aug 8, 2023
6927c91
Added documentation
mudit2812 Aug 8, 2023
eddd775
Trigger CI
mudit2812 Aug 8, 2023
1369a54
Trigger CI
mudit2812 Aug 8, 2023
2b19a58
Update docs
mudit2812 Aug 8, 2023
43b3a5f
[skip ci] Add projector dispatch to default.qubit
mudit2812 Aug 8, 2023
6e990a1
Updated measurement value
mudit2812 Aug 8, 2023
8e48582
Updated changelog
mudit2812 Aug 8, 2023
c7727a7
Update doc/releases/changelog-dev.md
mudit2812 Aug 8, 2023
4abe41b
Updated tests
mudit2812 Aug 8, 2023
fe6ea76
Fix transmon Hamiltonians (#4418)
Qottmann Aug 8, 2023
939bf26
Updated measurement value
mudit2812 Aug 8, 2023
7f17f0e
Updated changelog
mudit2812 Aug 8, 2023
94f4cbf
Update doc/releases/changelog-dev.md
mudit2812 Aug 8, 2023
0754ceb
Updated tests
mudit2812 Aug 8, 2023
34c92ab
Added qubit reuse and reset
mudit2812 Jul 27, 2023
693e4c2
Removed `MeasurementValue`
mudit2812 Jul 28, 2023
4868853
Integrate `TransformProgram` with `QNode` (#4404)
albi3ro Aug 3, 2023
274007f
Removed `dev_wires` arg
mudit2812 Aug 4, 2023
27d0840
Fixed tests
mudit2812 Aug 4, 2023
63a6c91
Update mid_measure tests
mudit2812 Aug 4, 2023
574c796
Trigger CI
mudit2812 Aug 8, 2023
45cc194
Trigger CI
mudit2812 Aug 8, 2023
986c04f
Trigger CI
mudit2812 Aug 8, 2023
a61af40
Merge branch 'mv-update' into qubit-reuse
mudit2812 Aug 8, 2023
1fefc2c
Trigger CI
mudit2812 Aug 8, 2023
6ad41db
Trigger CI
mudit2812 Aug 8, 2023
ef6bd22
Trigger CI
mudit2812 Aug 8, 2023
d0cbc58
Fix transmon Hamiltonians (#4418)
Qottmann Aug 8, 2023
2e928bc
Updated measurement value
mudit2812 Aug 8, 2023
fff7305
Updated changelog
mudit2812 Aug 8, 2023
ebbd5fe
Update doc/releases/changelog-dev.md
mudit2812 Aug 8, 2023
03d7450
Updated tests
mudit2812 Aug 8, 2023
07818a9
Added qubit reuse and reset
mudit2812 Jul 27, 2023
2a07c91
Removed `MeasurementValue`
mudit2812 Jul 28, 2023
572bfbc
Integrate `TransformProgram` with `QNode` (#4404)
albi3ro Aug 3, 2023
cb3e32f
Removed `dev_wires` arg
mudit2812 Aug 4, 2023
edca3b7
Fixed tests
mudit2812 Aug 4, 2023
8623720
Update mid_measure tests
mudit2812 Aug 4, 2023
bbd5976
Trigger CI
mudit2812 Aug 8, 2023
0f6f41c
Trigger CI
mudit2812 Aug 8, 2023
9c0910c
Trigger CI
mudit2812 Aug 8, 2023
9119143
Add postselection
mudit2812 Aug 8, 2023
5957c6b
Merge branch 'qubit-reuse' into mcm-postselect
mudit2812 Aug 8, 2023
5473ae9
Updated tests
mudit2812 Aug 8, 2023
b3c5739
Merge branch 'master' into mv-update
mudit2812 Aug 8, 2023
70b3c47
Updated tests
mudit2812 Aug 8, 2023
11337ef
Remove references to measurement_ids
mudit2812 Aug 8, 2023
a936d8d
Removed weird code
mudit2812 Aug 8, 2023
7fc2c0c
Add converter function for PySCF's RCISD and CCSD/UCCSD to PennyLane …
Chiffafox Aug 8, 2023
730567e
Remove references to measurement_ids
mudit2812 Aug 8, 2023
72fe080
Merge branch 'mv-update' into qubit-reuse
mudit2812 Aug 8, 2023
4a1c2df
Updated init
mudit2812 Aug 8, 2023
682b753
linting
mudit2812 Aug 8, 2023
18f14a4
Removed merge artifacts
mudit2812 Aug 9, 2023
785b10c
Fixing tests
mudit2812 Aug 9, 2023
9a1f7ab
Fixed tests
mudit2812 Aug 9, 2023
15c3ecd
Linting
mudit2812 Aug 9, 2023
1b5789d
Testing to see if doc build fails
mudit2812 Aug 9, 2023
9be3145
Check doc build
mudit2812 Aug 9, 2023
08d2823
Merge branch 'master' into qubit-reuse
mudit2812 Aug 9, 2023
49d0f15
Progressing docs
mudit2812 Aug 9, 2023
da42925
Updated doc
mudit2812 Aug 9, 2023
4643354
doc test
mudit2812 Aug 9, 2023
49290a2
Testing doc
mudit2812 Aug 9, 2023
e84471d
Updated docstring
mudit2812 Aug 9, 2023
d931285
Changed docstring (hopefully the last time)
mudit2812 Aug 9, 2023
261afda
Merge branch 'master' into qubit-reuse
mudit2812 Aug 9, 2023
e2e3ced
Apply suggestions from code review
mudit2812 Aug 9, 2023
1e91efe
Updating docs
mudit2812 Aug 9, 2023
5e3fc09
linting
mudit2812 Aug 9, 2023
1d89285
Updated measurement value
mudit2812 Aug 8, 2023
2093985
Updated tests
mudit2812 Aug 8, 2023
91e52f5
Updated tests
mudit2812 Aug 8, 2023
f9bd2ce
Removed weird code
mudit2812 Aug 8, 2023
e7e6a7f
Add converter function for PySCF's RCISD and CCSD/UCCSD to PennyLane …
Chiffafox Aug 8, 2023
54f17e5
Remove references to measurement_ids
mudit2812 Aug 8, 2023
6bcd5c1
Add converter function for PySCF's RCISD and CCSD/UCCSD to PennyLane …
Chiffafox Aug 8, 2023
81eb88c
Updated init
mudit2812 Aug 8, 2023
6966eed
linting
mudit2812 Aug 8, 2023
cf842a3
Removed merge artifacts
mudit2812 Aug 9, 2023
300ff01
Fixing tests
mudit2812 Aug 9, 2023
858957e
Fixed tests
mudit2812 Aug 9, 2023
dce1461
Linting
mudit2812 Aug 9, 2023
eddb675
Testing to see if doc build fails
mudit2812 Aug 9, 2023
cd550f8
Check doc build
mudit2812 Aug 9, 2023
6a77242
Support `StatePrep` mid-circuit for `default.qubit` device (#4437)
Jaybsoni Aug 8, 2023
96560ad
Allow shots reuse on commuting measurements (#4429)
eddddddy Aug 8, 2023
3347b23
Progressing docs
mudit2812 Aug 9, 2023
bf61434
Updated doc
mudit2812 Aug 9, 2023
2a3e0d6
doc test
mudit2812 Aug 9, 2023
f85b22b
Testing doc
mudit2812 Aug 9, 2023
c94c6f7
Updated docstring
mudit2812 Aug 9, 2023
447c65f
Changed docstring (hopefully the last time)
mudit2812 Aug 9, 2023
f94b178
bugfix: substraction works with new opmath (#4441)
timmysilv Aug 9, 2023
d18172f
Apply suggestions from code review
mudit2812 Aug 9, 2023
8fb4363
Updating docs
mudit2812 Aug 9, 2023
5a78662
linting
mudit2812 Aug 9, 2023
0ef8d15
Merge branch 'qubit-reuse' into mcm-postselect
mudit2812 Aug 10, 2023
1523a92
Removed old comments
mudit2812 Aug 10, 2023
090c981
Removed old comment
mudit2812 Aug 10, 2023
78a4105
Added projector support to state vector devices
mudit2812 Aug 10, 2023
627e1e9
Merge branch 'qubit-reuse' into mcm-postselect
mudit2812 Aug 10, 2023
a995d83
Fixed dq1 bug
mudit2812 Aug 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

<h3>New features since last release</h3>

* `qml.measure` now includes a boolean keyword argument `reset` to reset a wire to the
$|0\rangle$ computational basis state after measurement.
[(#4402)](https://github.com/PennyLaneAI/pennylane/pull/4402/)

* `DefaultQubit2` accepts a `max_workers` argument which controls multiprocessing.
A `ProcessPoolExecutor` executes tapes asynchronously
using a pool of at most `max_workers` processes. If `max_workers` is `None`
Expand Down Expand Up @@ -63,6 +67,9 @@ array([False, False])

<h3>Improvements 🛠</h3>

* Wires can now be reused after making a mid-circuit measurement on them.
[(#4402)](https://github.com/PennyLaneAI/pennylane/pull/4402/)

* Transform Programs, `qml.transforms.core.TransformProgram`, can now be called on a batch of circuits
and return a new batch of circuits and a single post processing function.
[(#4364)](https://github.com/PennyLaneAI/pennylane/pull/4364)
Expand Down
1 change: 1 addition & 0 deletions pennylane/_qubit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ class QubitDevice(Device):
_real = staticmethod(np.real)
_size = staticmethod(np.size)
_ndim = staticmethod(np.ndim)
_norm = staticmethod(np.linalg.norm)

@staticmethod
def _scatter(indices, array, new_dimensions):
Expand Down
14 changes: 10 additions & 4 deletions pennylane/devices/default_qubit.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ def apply(self, operations, rotations=None, **kwargs):

# apply the circuit operations
for i, operation in enumerate(operations):
print(self._state.shape)
if i > 0 and isinstance(operation, (QubitStateVector, BasisState)):
raise DeviceError(
f"Operation {operation.name} cannot be used after other Operations have already been applied "
Expand Down Expand Up @@ -333,12 +334,17 @@ def _apply_operation(self, state, operation):
matrix = self._asarray(self._get_unitary_matrix(operation), dtype=self.C_DTYPE)

if operation in diagonal_in_z_basis:
return self._apply_diagonal_unitary(state, matrix, wires)
if len(wires) <= 2:
new_state = self._apply_diagonal_unitary(state, matrix, wires)
elif len(wires) <= 2:
# Einsum is faster for small gates
return self._apply_unitary_einsum(state, matrix, wires)
new_state = self._apply_unitary_einsum(state, matrix, wires)
else:
new_state = self._apply_unitary(state, matrix, wires)

if operation.__class__.__name__ in {"Projector", "_BasisStateProjector"}:
new_state = new_state / self._norm(new_state)

return self._apply_unitary(state, matrix, wires)
return new_state

def _apply_x(self, state, axes, **kwargs):
"""Applies a PauliX gate by rolling 1 unit along the axis specified in ``axes``.
Expand Down
1 change: 1 addition & 0 deletions pennylane/devices/default_qubit_jax.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ def circuit():
_const_mul = staticmethod(jnp.multiply)
_size = staticmethod(jnp.size)
_ndim = staticmethod(jnp.ndim)
_norm = staticmethod(jnp.linalg.norm)

operations = DefaultQubit.operations.union({"ParametrizedEvolution"})

Expand Down
1 change: 1 addition & 0 deletions pennylane/devices/default_qubit_tf.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ class DefaultQubitTF(DefaultQubit):
_stack = staticmethod(tf.stack)
_size = staticmethod(tf.size)
_ndim = staticmethod(_ndim_tf)
_norm = staticmethod(tf.norm)

@staticmethod
def _const_mul(constant, array):
Expand Down
8 changes: 6 additions & 2 deletions pennylane/devices/qubit/apply_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,12 @@ def _(op: type_op, state):
len(op.wires) < EINSUM_OP_WIRECOUNT_PERF_THRESHOLD
and math.ndim(state) < EINSUM_STATE_WIRECOUNT_PERF_THRESHOLD
) or (op.batch_size and is_state_batched):
return apply_operation_einsum(op, state, is_state_batched=is_state_batched)
return apply_operation_tensordot(op, state, is_state_batched=is_state_batched)
new_state = apply_operation_einsum(op, state, is_state_batched=is_state_batched)
else:
new_state = apply_operation_tensordot(op, state, is_state_batched=is_state_batched)

if op.__class__.__name__ in {"Projector", "_BasisStateProjector"}:
new_state = new_state / math.norm(new_state)


@apply_operation.register
Expand Down
5 changes: 0 additions & 5 deletions pennylane/drawer/drawable_layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,6 @@ def drawable_layers(ops, wire_map=None):
# loop over operations
for op in ops:
is_mid_measure = is_conditional = False
if set(measured_wires.values()).intersection({wire_map[w] for w in op.wires}):
raise ValueError(
f"Cannot apply operations on {op.wires} as some wires have been measured already."
)

if isinstance(op, MidMeasureMP):
if len(op.wires) > 1:
raise ValueError("Cannot draw mid-circuit measurements with more than one wire.")
Expand Down
63 changes: 52 additions & 11 deletions pennylane/measurements/mid_measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
from .measurements import MeasurementProcess, MidMeasure


def measure(wires): # TODO: Change name to mid_measure
"""Perform a mid-circuit measurement in the computational basis on the
def measure(
wires: Wires, reset: Optional[bool] = False, postselect: Optional[int] = None
): # TODO: Change name to mid_measure
r"""Perform a mid-circuit measurement in the computational basis on the
supplied qubit.

Measurement outcomes can be obtained and used to conditionally apply
Expand All @@ -38,7 +40,7 @@ def measure(wires): # TODO: Change name to mid_measure

.. code-block:: python3

dev = qml.device("default.qubit", wires=2)
dev = qml.device("default.qubit", wires=3)

@qml.qnode(dev)
def func(x, y):
Expand All @@ -55,23 +57,48 @@ def func(x, y):
>>> func(*pars)
tensor([0.90165331, 0.09834669], requires_grad=True)

Wires can be reused after measurement. Moreover, measured wires can be reset
to the :math:`|0 \rangle` by setting ``reset=True``.

.. code-block:: python3

dev = qml.device("default.qubit", wires=3)

@qml.qnode(dev)
def func():
qml.PauliX(1)
m_0 = qml.measure(1, reset=True)
return qml.probs(wires=[1])

Executing this QNode:

>>> func()
tensor([1., 0.], requires_grad=True)

Mid circuit measurements can be manipulated using the following dunder methods
``+``, ``-``, ``*``, ``/``, ``~`` (not), ``&`` (and), ``|`` (or), ``==``, ``<=``,
``>=``, ``<``, ``>`` with other mid-circuit measurements or scalars.

Note:
python ``not``, ``and``, ``or``, do not work since these do not have dunder
methods. Instead use ``~``, ``&``, ``|``.
.. Note ::

Python ``not``, ``and``, ``or``, do not work since these do not have dunder methods.
Instead use ``~``, ``&``, ``|``.

Args:
wires (Wires): The wire of the qubit the measurement process applies to.
reset (Optional[bool]): Whether to reset the wire to the :math:`|0 \rangle`
state after measurement.
postselect (Optional[int]): The measured computational basis state on which to
optionally postselect the circuit. Must be ``0`` or ``1`` if postselection
is requested.

Returns:
MidMeasureMP: measurement process instance

Raises:
QuantumFunctionError: if multiple wires were specified
"""

wire = Wires(wires)
if len(wire) > 1:
raise qml.QuantumFunctionError(
Expand All @@ -80,7 +107,7 @@ def func(x, y):

# Create a UUID and a map between MP and MV to support serialization
measurement_id = str(uuid.uuid4())[:8]
mp = MidMeasureMP(wires=wire, id=measurement_id)
mp = MidMeasureMP(wires=wire, reset=reset, postselect=postselect, id=measurement_id)
return MeasurementValue([mp], processing_fn=lambda v: v)


Expand All @@ -90,17 +117,31 @@ def func(x, y):
class MidMeasureMP(MeasurementProcess):
"""Mid-circuit measurement.

This class additionally stores information about unknown measurement outcomes in the qubit model.
Measurements on a single qubit in the computational basis are assumed.

Please refer to :func:`measure` for detailed documentation.

Args:
wires (.Wires): The wires the measurement process applies to.
This can only be specified if an observable was not provided.
id (str): custom label given to a measurement instance, can be useful for some applications
where the instance has to be identified
reset (bool): Whether to reset the wire after measurement.
postselect (Optional[int]): The measured computational basis state on which to
optionally postselect the circuit. Must be ``0`` or ``1`` if postselection
is requested.
id (str): Custom label given to a measurement instance.
"""

def __init__(self, wires: Optional[Wires] = None, id: Optional[str] = None):
super().__init__(wires=wires, id=id)
def __init__(
self,
wires: Optional[Wires] = None,
reset: Optional[bool] = False,
postselect: Optional[int] = None,
id: Optional[str] = None,
):
super().__init__(wires=Wires(wires), id=id)
self.reset = reset
self.postselect = postselect

@property
def return_type(self):
Expand Down
4 changes: 2 additions & 2 deletions pennylane/ops/qubit/observables.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from scipy.sparse import csr_matrix

import pennylane as qml
from pennylane.operation import AnyWires, Observable
from pennylane.operation import AnyWires, Observable, Operation
from pennylane.wires import Wires

from .matrix_ops import QubitUnitary
Expand Down Expand Up @@ -428,7 +428,7 @@ def __copy__(self):
return copied_op


class _BasisStateProjector(Observable):
class _BasisStateProjector(Observable, Operation):
# The call signature should be the same as Projector.__new__ for the positional
# arguments, but with free key word arguments.
def __init__(self, state, wires, id=None):
Expand Down
4 changes: 3 additions & 1 deletion pennylane/transforms/condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def cond(condition, true_fn, false_fn=None):
:func:`defer_measurements` transform.

Args:
condition (.MeasurementValue[bool]): a conditional expression involving a mid-circuit
condition (.MeasurementValue): a conditional expression involving a mid-circuit
measurement value (see :func:`.pennylane.measure`)
true_fn (callable): The quantum function of PennyLane operation to
apply if ``condition`` is ``True``
Expand Down Expand Up @@ -114,6 +114,8 @@ def qnode(x, y):
Expressions with boolean logic flow using operators like ``and``,
``or`` and ``not`` are not supported as the ``condition`` argument.

While such statements may not result in errors, they may result in
incorrect behaviour.

.. details::
:title: Usage Details
Expand Down
65 changes: 52 additions & 13 deletions pennylane/transforms/defer_measurements.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
from pennylane.measurements import MidMeasureMP
from pennylane.ops.op_math import ctrl
from pennylane.queuing import apply
from pennylane.tape import QuantumTape
from pennylane.transforms import qfunc_transform
from pennylane.wires import Wires


@qfunc_transform
def defer_measurements(tape):
def defer_measurements(tape: QuantumTape):
"""Quantum function transform that substitutes operations conditioned on
measurement outcomes to controlled operations.

Expand All @@ -40,6 +41,13 @@ def defer_measurements(tape):
that can be controlled as such depends on the set of operations
supported by the chosen device.

.. note::

Devices that inherit `QubitDevice` **must** be initialized with an additional
wire for each mid-circuit measurement for `defer_measurements` to transform
the quantum tape correctly. Such devices should also be initialized without
custom wire labels for correct behaviour.

.. note::

This transform does not change the list of terminal measurements returned by
Expand All @@ -54,7 +62,7 @@ def defer_measurements(tape):
post-measurement states are considered.

Args:
qfunc (function): a quantum function
tape (.QuantumTape): a quantum tape

**Example**

Expand Down Expand Up @@ -84,34 +92,65 @@ def qfunc(par):

>>> qml.grad(qnode)(par)
tensor(-0.49622252, requires_grad=True)

Reusing and reseting measured wires will work as expected with the
``defer_measurements`` transform:

.. code-block:: python3

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)
"""
measured_wires = {}

cv_types = (qml.operation.CVOperation, qml.operation.CVObservable)
ops_cv = any(isinstance(op, cv_types) for op in tape.operations)
obs_cv = any(isinstance(getattr(op, "obs", None), cv_types) for op in tape.measurements)
if ops_cv or obs_cv:
raise ValueError("Continuous variable operations and observables are not supported.")

for op in tape:
op_wires_measured = set(wire for wire in op.wires if wire in measured_wires.values())
if len(op_wires_measured) > 0:
raise ValueError(
f"Cannot apply operations on {op.wires} as the following wires have been measured already: {op_wires_measured}."
)
# Current wire in which pre-measurement state will be saved if dev_wires not specified
cur_wire = max(tape.wires) + 1
new_wires = {}

for op in tape:
if isinstance(op, MidMeasureMP):
measured_wires[op.id] = op.wires[0]
new_wires[op.id] = cur_wire
qml.CNOT([op.wires[0], cur_wire])

if op.postselect is not None:
qml.Projector([op.postselect], wires=op.wires[0])

if op.reset:
qml.CNOT([cur_wire, op.wires[0]])

cur_wire += 1

elif op.__class__.__name__ == "Conditional":
_add_control_gate(op, measured_wires)
_add_control_gate(op, new_wires)
else:
apply(op)

return tape._qfunc_output # pylint: disable=protected-access


def _add_control_gate(op, measured_wires):
def _add_control_gate(op, control_wires):
"""Helper function to add control gates"""
control = [measured_wires[m.id] for m in op.meas_val.measurements]
control = [control_wires[m.id] for m in op.meas_val.measurements]
for branch, value in op.meas_val._items(): # pylint: disable=protected-access
if value:
ctrl(
Expand Down
3 changes: 2 additions & 1 deletion tests/devices/qubit/test_preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ def test_expand_fn_defer_measurement(self):
expanded_tape = expand_fn(tape)
expected = [
qml.Hadamard(0),
qml.ops.Controlled(qml.RX(0.123, wires=1), 0),
qml.CNOT([0, 2]),
qml.ops.Controlled(qml.RX(0.123, wires=1), 2),
]

for op, exp in zip(expanded_tape, expected + measurements):
Expand Down
Loading