From 66840b00b9e4e5cb9bd789b105a9e33249d19207 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Mon, 20 May 2024 21:52:33 -0400 Subject: [PATCH 01/25] Updated LQ to work with runtime kwargs --- .../lightning_qubit/lightning_qubit.py | 4 ++- requirements-dev.txt | 2 +- tests/new_api/test_device.py | 25 +++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index fff9d8acaf..e2b0cb6fb4 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -570,7 +570,9 @@ def preprocess(self, execution_config: ExecutionConfig = DefaultExecutionConfig) program.add_transform(validate_measurements, name=self.name) program.add_transform(validate_observables, accepted_observables, name=self.name) program.add_transform(validate_device_wires, self.wires, name=self.name) - program.add_transform(mid_circuit_measurements, device=self) + program.add_transform( + mid_circuit_measurements, device=self, mcm_config=exec_config.mcm_config + ) program.add_transform( decompose, stopping_condition=stopping_condition, diff --git a/requirements-dev.txt b/requirements-dev.txt index b3fb6d76a5..90740a3cbf 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ pip~=22.0 -git+https://github.com/PennyLaneAI/pennylane.git@master +git+https://github.com/PennyLaneAI/pennylane.git@postselect-choice ninja flaky pybind11 diff --git a/tests/new_api/test_device.py b/tests/new_api/test_device.py index a1a5912160..620e4228a0 100644 --- a/tests/new_api/test_device.py +++ b/tests/new_api/test_device.py @@ -350,6 +350,31 @@ def test_preprocess_state_prep_middle_op_decomposition(self, op, decomp_depth): ) assert qml.equal(new_tape, expected_tape) + @pytest.mark.parametrize("postselect_shots", [True, False]) + def test_postselect_shots(self, postselect_shots): + """Test that results are as expected when a user requests to scale shots with postselection.""" + + shots = 10 + device = LightningDevice(wires=3, shots=shots) + + @qml.qnode(device, postselect_shots=postselect_shots) + def func(x): + qml.RX(x, 0) + qml.measure(0, postselect=1) + qml.CNOT([0, 1]) + qml.sample(qml.PauliZ(1)) + + # Choosing small rotation angle ensures that the number of valid shots is less than the + # total number of shots. This will allow us to avoid stochastic failures where the number + # of valid samples happens to be the same as the number of shots. + x = np.pi / 5 + res = func(x) + + if postselect_shots: + assert len(res) < shots + else: + assert len(res) == shots + @pytest.mark.usefixtures("use_legacy_and_new_opmath") @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) @pytest.mark.parametrize( From 4f446d320b68c3f037404d8aa90a892e3d2f9c32 Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Tue, 21 May 2024 01:57:39 +0000 Subject: [PATCH 02/25] Auto update version from '0.37.0-dev14' to '0.37.0-dev15' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index e81e04b214..cdfbc0899c 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.37.0-dev14" +__version__ = "0.37.0-dev15" From be8702dd492262bb28fa4bdde492f984b099ccc5 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Mon, 20 May 2024 21:58:37 -0400 Subject: [PATCH 03/25] Update changelog --- .github/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 5b421121dd..197de852f8 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -42,6 +42,9 @@ * Changed the name of `lightning.tensor` to `default.tensor` with the `quimb` backend. [(#719)](https://github.com/PennyLaneAI/pennylane-lightning/pull/719) +* `lightning.qubit` and `lightning.kokkos` adhere to user specified mid-circuit measurement configuration options. + [(#736)](https://github.com/PennyLaneAI/pennylane-lightning/pull/736) + ### Documentation ### Bug fixes From 0689e61aefaa9655ad48ec70dc0a01177ad1f622 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Mon, 20 May 2024 21:59:36 -0400 Subject: [PATCH 04/25] [skip ci] Skip CI From e21874e9478eb65c12824ac47457b23c0237e56d Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Wed, 22 May 2024 11:33:11 -0400 Subject: [PATCH 05/25] Added tests --- tests/new_api/test_device.py | 25 ----------------- tests/test_native_mcm.py | 53 ++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/tests/new_api/test_device.py b/tests/new_api/test_device.py index 620e4228a0..a1a5912160 100644 --- a/tests/new_api/test_device.py +++ b/tests/new_api/test_device.py @@ -350,31 +350,6 @@ def test_preprocess_state_prep_middle_op_decomposition(self, op, decomp_depth): ) assert qml.equal(new_tape, expected_tape) - @pytest.mark.parametrize("postselect_shots", [True, False]) - def test_postselect_shots(self, postselect_shots): - """Test that results are as expected when a user requests to scale shots with postselection.""" - - shots = 10 - device = LightningDevice(wires=3, shots=shots) - - @qml.qnode(device, postselect_shots=postselect_shots) - def func(x): - qml.RX(x, 0) - qml.measure(0, postselect=1) - qml.CNOT([0, 1]) - qml.sample(qml.PauliZ(1)) - - # Choosing small rotation angle ensures that the number of valid shots is less than the - # total number of shots. This will allow us to avoid stochastic failures where the number - # of valid samples happens to be the same as the number of shots. - x = np.pi / 5 - res = func(x) - - if postselect_shots: - assert len(res) < shots - else: - assert len(res) == shots - @pytest.mark.usefixtures("use_legacy_and_new_opmath") @pytest.mark.parametrize("theta, phi", list(zip(THETA, PHI))) @pytest.mark.parametrize( diff --git a/tests/test_native_mcm.py b/tests/test_native_mcm.py index 2e6dd41e7b..2844172c33 100644 --- a/tests/test_native_mcm.py +++ b/tests/test_native_mcm.py @@ -93,6 +93,59 @@ def func(x, y): func(*params) +@pytest.mark.parametrize("mcm_method", ["deferred", "one-shot"]) +def test_qnode_mcm_method(mcm_method, mocker): + """Test that user specified qnode arg for mid-circuit measurements transform are used correctly""" + spy = ( + mocker.spy(qml, "dynamic_one_shot") + if mcm_method == "one-shot" + else mocker.spy(qml, "defer_measurements") + ) + other_spy = ( + mocker.spy(qml, "defer_measurements") + if mcm_method == "one-shot" + else mocker.spy(qml, "dynamic_one_shot") + ) + + shots = 10 + device = qml.device(device_name, wires=3, shots=shots) + + @qml.qnode(device, mcm_method=mcm_method) + def f(x): + qml.RX(x, 0) + _ = qml.measure(0) + qml.CNOT([0, 1]) + return qml.sample(wires=[0, 1]) + + _ = f(np.pi / 8) + + spy.assert_called_once() + other_spy.assert_not_called() + + +@pytest.mark.parametrize("postselect_shots", [True, False]) +def test_qnode_postselect_shots(postselect_shots): + """Test that user specified qnode arg for discarding invalid shots is used correctly""" + shots = 10 + device = qml.device(device_name, wires=3, shots=100) + + @qml.qnode(device, postselect_shots=postselect_shots) + def f(x): + qml.RX(x, 0) + _ = qml.measure(0, postselect=1) + qml.CNOT([0, 1]) + return qml.sample(wires=[0, 1]) + + # Using small-ish rotation angle ensures the number of valid shots will be less than the + # original number of shots. This helps avoid stochastic failures for the assertion below + res = f(np.pi / 2) + + if postselect_shots: + assert len(res) < shots + else: + assert len(res) == shots + + @flaky(max_runs=5) @pytest.mark.parametrize("shots", [5000, [5000, 5001]]) @pytest.mark.parametrize("postselect", [None, 0, 1]) From 479f599e6dde5e0b3f0cc4aa9ddb4d9ae8aa157c Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Wed, 22 May 2024 15:33:36 +0000 Subject: [PATCH 06/25] Auto update version from '0.37.0-dev15' to '0.37.0-dev16' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index cdfbc0899c..eb05b84bd4 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.37.0-dev15" +__version__ = "0.37.0-dev16" From 39f08ca2112a1b95f612a36bdf325d304c5a441b Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Wed, 22 May 2024 13:21:03 -0400 Subject: [PATCH 07/25] Trigger CI From e434082101ee89fbe9d568a591373a3b4e304d1e Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Wed, 22 May 2024 15:16:44 -0400 Subject: [PATCH 08/25] Trigger CI From dc81cd3da6e9dbd670c040bfc6121c48fece763c Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Wed, 22 May 2024 15:36:50 -0400 Subject: [PATCH 09/25] Fixed test --- tests/test_native_mcm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_native_mcm.py b/tests/test_native_mcm.py index 2844172c33..09feb2edc0 100644 --- a/tests/test_native_mcm.py +++ b/tests/test_native_mcm.py @@ -126,8 +126,8 @@ def f(x): @pytest.mark.parametrize("postselect_shots", [True, False]) def test_qnode_postselect_shots(postselect_shots): """Test that user specified qnode arg for discarding invalid shots is used correctly""" - shots = 10 - device = qml.device(device_name, wires=3, shots=100) + shots = 100 + device = qml.device(device_name, wires=3, shots=shots) @qml.qnode(device, postselect_shots=postselect_shots) def f(x): From 93142789650bedf3c2a2b3c083c3fb2ae8af2b4b Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Wed, 22 May 2024 17:36:08 -0400 Subject: [PATCH 10/25] Fix preprocess test --- tests/new_api/test_device.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/new_api/test_device.py b/tests/new_api/test_device.py index a1a5912160..e188f178bb 100644 --- a/tests/new_api/test_device.py +++ b/tests/new_api/test_device.py @@ -259,7 +259,11 @@ def test_preprocess(self, adjoint): expected_program.add_transform(validate_measurements, name=device.name) expected_program.add_transform(validate_observables, accepted_observables, name=device.name) expected_program.add_transform(validate_device_wires, device.wires, name=device.name) - expected_program.add_transform(mid_circuit_measurements, device=device) + expected_program.add_transform( + mid_circuit_measurements, + device=device, + mcm_config={"postselect_shots": None, "mcm_method": None}, + ) expected_program.add_transform( decompose, stopping_condition=stopping_condition, From 4e2989936da190db4d89e9fc7d05ea92ba2fee4f Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Wed, 22 May 2024 21:39:20 +0000 Subject: [PATCH 11/25] Auto update version from '0.37.0-dev16' to '0.37.0-dev17' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index eb05b84bd4..7671600d2c 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.37.0-dev16" +__version__ = "0.37.0-dev17" From 36faf75ea3c026a6103fdae8f3f712ef3663e81d Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Wed, 22 May 2024 19:35:09 -0400 Subject: [PATCH 12/25] Updated tests --- tests/test_native_mcm.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_native_mcm.py b/tests/test_native_mcm.py index 09feb2edc0..fabc1964f8 100644 --- a/tests/test_native_mcm.py +++ b/tests/test_native_mcm.py @@ -97,14 +97,14 @@ def func(x, y): def test_qnode_mcm_method(mcm_method, mocker): """Test that user specified qnode arg for mid-circuit measurements transform are used correctly""" spy = ( - mocker.spy(qml, "dynamic_one_shot") + mocker.spy(qml.dynamic_one_shot, "_transform") if mcm_method == "one-shot" - else mocker.spy(qml, "defer_measurements") + else mocker.spy(qml.defer_measurements, "_transform") ) other_spy = ( - mocker.spy(qml, "defer_measurements") + mocker.spy(qml.defer_measurements, "_transform") if mcm_method == "one-shot" - else mocker.spy(qml, "dynamic_one_shot") + else mocker.spy(qml.dynamic_one_shot, "_transform") ) shots = 10 From 83cbd39d81c3da8eb4faa529e8dfe6a2661fabbf Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 23 May 2024 17:23:08 -0400 Subject: [PATCH 13/25] Changed postselect_shots to postselect_mode --- tests/new_api/test_device.py | 2 +- tests/test_native_mcm.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/new_api/test_device.py b/tests/new_api/test_device.py index e188f178bb..6ec882f7b5 100644 --- a/tests/new_api/test_device.py +++ b/tests/new_api/test_device.py @@ -262,7 +262,7 @@ def test_preprocess(self, adjoint): expected_program.add_transform( mid_circuit_measurements, device=device, - mcm_config={"postselect_shots": None, "mcm_method": None}, + mcm_config={"postselect_mode": None, "mcm_method": None}, ) expected_program.add_transform( decompose, diff --git a/tests/test_native_mcm.py b/tests/test_native_mcm.py index fabc1964f8..94c5e95640 100644 --- a/tests/test_native_mcm.py +++ b/tests/test_native_mcm.py @@ -123,13 +123,13 @@ def f(x): other_spy.assert_not_called() -@pytest.mark.parametrize("postselect_shots", [True, False]) -def test_qnode_postselect_shots(postselect_shots): +@pytest.mark.parametrize("postselect_mode", ["hw-like", "fill-shots"]) +def test_qnode_postselect_mode(postselect_mode): """Test that user specified qnode arg for discarding invalid shots is used correctly""" shots = 100 device = qml.device(device_name, wires=3, shots=shots) - @qml.qnode(device, postselect_shots=postselect_shots) + @qml.qnode(device, postselect_mode=postselect_mode) def f(x): qml.RX(x, 0) _ = qml.measure(0, postselect=1) @@ -140,7 +140,7 @@ def f(x): # original number of shots. This helps avoid stochastic failures for the assertion below res = f(np.pi / 2) - if postselect_shots: + if postselect_mode == "hw-like": assert len(res) < shots else: assert len(res) == shots From ac019ace79160808ed29d22def84a4cf5238c9e4 Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Thu, 23 May 2024 21:24:33 +0000 Subject: [PATCH 14/25] Auto update version from '0.37.0-dev17' to '0.37.0-dev18' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 7671600d2c..eb9ad44099 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.37.0-dev17" +__version__ = "0.37.0-dev18" From d4cfb4d5d5bed1e02fdd2def3d1af0362f8f8436 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 23 May 2024 17:26:34 -0400 Subject: [PATCH 15/25] Trigger CI From be64657f26c7483571af2acddf2ecbf95a3d069a Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Fri, 31 May 2024 18:35:35 +0000 Subject: [PATCH 16/25] Auto update version from '0.37.0-dev23' to '0.37.0-dev24' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 9d704fc017..9daa3347ec 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.37.0-dev23" +__version__ = "0.37.0-dev24" From 3b0615253b78be965f3b09ca48ac383eee2a343a Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Mon, 3 Jun 2024 11:25:48 -0400 Subject: [PATCH 17/25] Updated LQ and LK for mcm config support --- .../lightning_kokkos/lightning_kokkos.py | 25 ++++++--- .../lightning_qubit/_state_vector.py | 53 ++++++++++++++----- .../lightning_qubit/lightning_qubit.py | 25 +++++++-- 3 files changed, 79 insertions(+), 24 deletions(-) diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index f0d8bead12..0c5cc2c425 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -223,9 +223,7 @@ def accepts_obj(obj): @classmethod def capabilities(cls): capabilities = super().capabilities().copy() - capabilities.update( - supports_mid_measure=True, - ) + capabilities.update(supports_mid_measure=True) return capabilities @staticmethod @@ -382,22 +380,29 @@ def _apply_basis_state(self, state, wires): num = self._get_basis_state_index(state, wires) self._create_basis_state(num) - def _apply_lightning_midmeasure(self, operation: MidMeasureMP, mid_measurements: dict): + def _apply_lightning_midmeasure( + self, operation: MidMeasureMP, mid_measurements: dict, postselect_mode: str + ): """Execute a MidMeasureMP operation and return the sample in mid_measurements. + Args: operation (~pennylane.operation.Operation): mid-circuit measurement + Returns: None """ wires = self.wires.indices(operation.wires) wire = list(wires)[0] - sample = qml.math.reshape(self.generate_samples(shots=1), (-1,))[wire] + if postselect_mode == "fill-shots" and operation.postselect is not None: + sample = operation.postselect + else: + sample = qml.math.reshape(self.generate_samples(shots=1), (-1,))[wire] mid_measurements[operation] = sample getattr(self.state_vector, "collapse")(wire, bool(sample)) if operation.reset and bool(sample): self.apply([qml.PauliX(operation.wires)], mid_measurements=mid_measurements) - def apply_lightning(self, operations, mid_measurements=None): + def apply_lightning(self, operations, mid_measurements=None, postselect_mode="hw-like"): """Apply a list of operations to the state tensor. Args: @@ -428,7 +433,7 @@ def apply_lightning(self, operations, mid_measurements=None): if ops.meas_val.concretize(mid_measurements): self.apply_lightning([ops.then_op]) elif isinstance(ops, MidMeasureMP): - self._apply_lightning_midmeasure(ops, mid_measurements) + self._apply_lightning_midmeasure(ops, mid_measurements, postselect_mode) elif isinstance(ops, qml.ops.op_math.Controlled) and isinstance( ops.base, qml.GlobalPhase ): @@ -470,6 +475,8 @@ def apply(self, operations, rotations=None, mid_measurements=None, **kwargs): self._apply_basis_state(operations[0].parameters[0], operations[0].wires) operations = operations[1:] + postselect_mode = kwargs.get("postselect_mode", "hw-like") + for operation in operations: if isinstance(operation, (StatePrep, BasisState)): raise DeviceError( @@ -477,7 +484,9 @@ def apply(self, operations, rotations=None, mid_measurements=None, **kwargs): + f"Operations have already been applied on a {self.short_name} device." ) - self.apply_lightning(operations, mid_measurements=mid_measurements) + self.apply_lightning( + operations, mid_measurements=mid_measurements, postselect_mode=postselect_mode + ) # pylint: disable=protected-access def expval(self, observable, shot_range=None, bin_size=None): diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index 84ea60db95..b3e24addb4 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -29,6 +29,7 @@ import numpy as np import pennylane as qml from pennylane import BasisState, DeviceError, StatePrep +from pennylane.devices import MCMConfig from pennylane.measurements import MidMeasureMP from pennylane.ops import Conditional from pennylane.ops.op_math import Adjoint @@ -250,33 +251,45 @@ def _apply_lightning_controlled(self, operation): False, ) - def _apply_lightning_midmeasure(self, operation: MidMeasureMP, mid_measurements: dict): + def _apply_lightning_midmeasure( + self, operation: MidMeasureMP, mid_measurements: dict, postselect_mode: str + ): """Execute a MidMeasureMP operation and return the sample in mid_measurements. + Args: operation (~pennylane.operation.Operation): mid-circuit measurement mid_measurements (None, dict): Dictionary of mid-circuit measurements + postselect_mode (str): Configuration for handling shots with mid-circuit measurement + postselection. Use ``"hw-like"`` to discard invalid shots and ``"fill-shots"`` to + keep the same number of shots. + Returns: None """ wires = self.wires.indices(operation.wires) wire = list(wires)[0] circuit = QuantumScript([], [qml.sample(wires=operation.wires)], shots=1) - sample = LightningMeasurements(self).measure_final_state(circuit) - sample = np.squeeze(sample) - if operation.postselect is not None and sample != operation.postselect: - mid_measurements[operation] = -1 - return + if postselect_mode == "fill-shots" and operation.postselect is not None: + sample = operation.postselect + else: + sample = LightningMeasurements(self).measure_final_state(circuit) + sample = np.squeeze(sample) mid_measurements[operation] = sample getattr(self.state_vector, "collapse")(wire, bool(sample)) if operation.reset and bool(sample): self.apply_operations([qml.PauliX(operation.wires)], mid_measurements=mid_measurements) - def _apply_lightning(self, operations, mid_measurements: dict = None): + def _apply_lightning( + self, operations, mid_measurements: dict = None, postselect_mode: str = "hw-like" + ): """Apply a list of operations to the state tensor. Args: operations (list[~pennylane.operation.Operation]): operations to apply mid_measurements (None, dict): Dictionary of mid-circuit measurements + postselect_mode (str): Configuration for handling shots with mid-circuit measurement + postselection. Use ``"hw-like"`` to discard invalid shots and ``"fill-shots"`` to + keep the same number of shots. Default is ``"hw-like"``. Returns: None @@ -301,7 +314,9 @@ def _apply_lightning(self, operations, mid_measurements: dict = None): if operation.meas_val.concretize(mid_measurements): self._apply_lightning([operation.then_op]) elif isinstance(operation, MidMeasureMP): - self._apply_lightning_midmeasure(operation, mid_measurements) + self._apply_lightning_midmeasure( + operation, mid_measurements, postselect_mode=postselect_mode + ) elif method is not None: # apply specialized gate param = operation.parameters method(wires, invert_param, param) @@ -317,7 +332,9 @@ def _apply_lightning(self, operations, mid_measurements: dict = None): # To support older versions of PL method(operation.matrix, wires, False) - def apply_operations(self, operations, mid_measurements: dict = None): + def apply_operations( + self, operations, mid_measurements: dict = None, postselect_mode: str = "hw-like" + ): """Applies operations to the state vector.""" # State preparation is currently done in Python if operations: # make sure operations[0] exists @@ -328,9 +345,16 @@ def apply_operations(self, operations, mid_measurements: dict = None): self._apply_basis_state(operations[0].parameters[0], operations[0].wires) operations = operations[1:] - self._apply_lightning(operations, mid_measurements=mid_measurements) + self._apply_lightning( + operations, mid_measurements=mid_measurements, postselect_mode=postselect_mode + ) - def get_final_state(self, circuit: QuantumScript, mid_measurements: dict = None): + def get_final_state( + self, + circuit: QuantumScript, + mid_measurements: dict = None, + postselect_mode: str = "hw-like", + ): """ Get the final state that results from executing the given quantum script. @@ -339,11 +363,16 @@ def get_final_state(self, circuit: QuantumScript, mid_measurements: dict = None) Args: circuit (QuantumScript): The single circuit to simulate mid_measurements (None, dict): Dictionary of mid-circuit measurements + postselect_mode (str): Configuration for handling shots with mid-circuit measurement + postselection. Use ``"hw-like"`` to discard invalid shots and ``"fill-shots"`` to + keep the same number of shots. Default is ``"hw-like"``. Returns: LightningStateVector: Lightning final state class. """ - self.apply_operations(circuit.operations, mid_measurements=mid_measurements) + self.apply_operations( + circuit.operations, mid_measurements=mid_measurements, postselect_mode=postselect_mode + ) return self diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index e2b0cb6fb4..1fb21ad588 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -21,7 +21,7 @@ import numpy as np import pennylane as qml -from pennylane.devices import DefaultExecutionConfig, Device, ExecutionConfig +from pennylane.devices import DefaultExecutionConfig, Device, ExecutionConfig, MCMConfig from pennylane.devices.default_qubit import adjoint_ops from pennylane.devices.modifiers import simulator_tracking, single_tape_support from pennylane.devices.preprocess import ( @@ -58,7 +58,12 @@ PostprocessingFn = Callable[[ResultBatch], Result_or_ResultBatch] -def simulate(circuit: QuantumScript, state: LightningStateVector, mcmc: dict = None) -> Result: +def simulate( + circuit: QuantumScript, + state: LightningStateVector, + mcmc: dict = None, + postselect_mode: str = "hw-like", +) -> Result: """Simulate a single quantum script. Args: @@ -67,6 +72,9 @@ def simulate(circuit: QuantumScript, state: LightningStateVector, mcmc: dict = N mcmc (dict): Dictionary containing the Markov Chain Monte Carlo parameters: mcmc, kernel_name, num_burnin. Descriptions of these fields are found in :class:`~.LightningQubit`. + postselect_mode (str): Configuration for handling shots with mid-circuit measurement + postselection. Use ``"hw-like"`` to discard invalid shots and ``"fill-shots"`` to + keep the same number of shots. Default is ``"hw-like"``. Returns: Tuple[TensorLike]: The results of the simulation @@ -88,7 +96,9 @@ def simulate(circuit: QuantumScript, state: LightningStateVector, mcmc: dict = N for _ in range(circuit.shots.total_shots): state.reset_state() mid_measurements = {} - final_state = state.get_final_state(aux_circ, mid_measurements=mid_measurements) + final_state = state.get_final_state( + aux_circ, mid_measurements=mid_measurements, postselect_mode=postselect_mode + ) results.append( LightningMeasurements(final_state, **mcmc).measure_final_state( aux_circ, mid_measurements=mid_measurements @@ -610,7 +620,14 @@ def execute( for circuit in circuits: if self._wire_map is not None: [circuit], _ = qml.map_wires(circuit, self._wire_map) - results.append(simulate(circuit, self._statevector, mcmc=mcmc)) + results.append( + simulate( + circuit, + self._statevector, + mcmc=mcmc, + postselect_mode=execution_config.mcm_config.postselect_mode, + ) + ) return tuple(results) From 3c44bd4a3c90277b014670482918b911218172bd Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Mon, 3 Jun 2024 15:33:19 +0000 Subject: [PATCH 18/25] Auto update version from '0.37.0-dev24' to '0.37.0-dev25' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 9daa3347ec..3f4945295f 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.37.0-dev24" +__version__ = "0.37.0-dev25" From 97397cd0b365271131a03af7fe49664cd0a73745 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Mon, 3 Jun 2024 14:58:36 -0400 Subject: [PATCH 19/25] Update default postselect_mode to None --- .../lightning_kokkos/lightning_kokkos.py | 4 ++-- pennylane_lightning/lightning_qubit/_state_vector.py | 10 +++++----- pennylane_lightning/lightning_qubit/lightning_qubit.py | 4 ++-- tests/test_native_mcm.py | 6 ++++-- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 0c5cc2c425..93bc5a943d 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -402,7 +402,7 @@ def _apply_lightning_midmeasure( if operation.reset and bool(sample): self.apply([qml.PauliX(operation.wires)], mid_measurements=mid_measurements) - def apply_lightning(self, operations, mid_measurements=None, postselect_mode="hw-like"): + def apply_lightning(self, operations, mid_measurements=None, postselect_mode=None): """Apply a list of operations to the state tensor. Args: @@ -475,7 +475,7 @@ def apply(self, operations, rotations=None, mid_measurements=None, **kwargs): self._apply_basis_state(operations[0].parameters[0], operations[0].wires) operations = operations[1:] - postselect_mode = kwargs.get("postselect_mode", "hw-like") + postselect_mode = kwargs.get("postselect_mode", None) for operation in operations: if isinstance(operation, (StatePrep, BasisState)): diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index b3e24addb4..037b494db2 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -280,7 +280,7 @@ def _apply_lightning_midmeasure( self.apply_operations([qml.PauliX(operation.wires)], mid_measurements=mid_measurements) def _apply_lightning( - self, operations, mid_measurements: dict = None, postselect_mode: str = "hw-like" + self, operations, mid_measurements: dict = None, postselect_mode: str = None ): """Apply a list of operations to the state tensor. @@ -289,7 +289,7 @@ def _apply_lightning( mid_measurements (None, dict): Dictionary of mid-circuit measurements postselect_mode (str): Configuration for handling shots with mid-circuit measurement postselection. Use ``"hw-like"`` to discard invalid shots and ``"fill-shots"`` to - keep the same number of shots. Default is ``"hw-like"``. + keep the same number of shots. Default is ``None``. Returns: None @@ -333,7 +333,7 @@ def _apply_lightning( method(operation.matrix, wires, False) def apply_operations( - self, operations, mid_measurements: dict = None, postselect_mode: str = "hw-like" + self, operations, mid_measurements: dict = None, postselect_mode: str = None ): """Applies operations to the state vector.""" # State preparation is currently done in Python @@ -353,7 +353,7 @@ def get_final_state( self, circuit: QuantumScript, mid_measurements: dict = None, - postselect_mode: str = "hw-like", + postselect_mode: str = None, ): """ Get the final state that results from executing the given quantum script. @@ -365,7 +365,7 @@ def get_final_state( mid_measurements (None, dict): Dictionary of mid-circuit measurements postselect_mode (str): Configuration for handling shots with mid-circuit measurement postselection. Use ``"hw-like"`` to discard invalid shots and ``"fill-shots"`` to - keep the same number of shots. Default is ``"hw-like"``. + keep the same number of shots. Default is ``None``. Returns: LightningStateVector: Lightning final state class. diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index 1fb21ad588..1fb5279406 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -62,7 +62,7 @@ def simulate( circuit: QuantumScript, state: LightningStateVector, mcmc: dict = None, - postselect_mode: str = "hw-like", + postselect_mode: str = None, ) -> Result: """Simulate a single quantum script. @@ -74,7 +74,7 @@ def simulate( these fields are found in :class:`~.LightningQubit`. postselect_mode (str): Configuration for handling shots with mid-circuit measurement postselection. Use ``"hw-like"`` to discard invalid shots and ``"fill-shots"`` to - keep the same number of shots. Default is ``"hw-like"``. + keep the same number of shots. Default is ``None``. Returns: Tuple[TensorLike]: The results of the simulation diff --git a/tests/test_native_mcm.py b/tests/test_native_mcm.py index 94c5e95640..8a95d6e362 100644 --- a/tests/test_native_mcm.py +++ b/tests/test_native_mcm.py @@ -128,13 +128,14 @@ def test_qnode_postselect_mode(postselect_mode): """Test that user specified qnode arg for discarding invalid shots is used correctly""" shots = 100 device = qml.device(device_name, wires=3, shots=shots) + postselect = 1 @qml.qnode(device, postselect_mode=postselect_mode) def f(x): qml.RX(x, 0) - _ = qml.measure(0, postselect=1) + _ = qml.measure(0, postselect=postselect) qml.CNOT([0, 1]) - return qml.sample(wires=[0, 1]) + return qml.sample(wires=[1]) # Using small-ish rotation angle ensures the number of valid shots will be less than the # original number of shots. This helps avoid stochastic failures for the assertion below @@ -144,6 +145,7 @@ def f(x): assert len(res) < shots else: assert len(res) == shots + assert np.allclose(res, postselect) @flaky(max_runs=5) From b09734f394e6877a411c9efa57173375bef98d0d Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Mon, 3 Jun 2024 18:58:55 +0000 Subject: [PATCH 20/25] Auto update version from '0.37.0-dev25' to '0.37.0-dev26' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 3f4945295f..2a0c6b409f 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.37.0-dev25" +__version__ = "0.37.0-dev26" From aea378c55562366c48e789cdf5bf6dea8aaaa8d3 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Mon, 3 Jun 2024 15:39:55 -0400 Subject: [PATCH 21/25] Linting --- pennylane_lightning/lightning_qubit/_state_vector.py | 1 - pennylane_lightning/lightning_qubit/lightning_qubit.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pennylane_lightning/lightning_qubit/_state_vector.py b/pennylane_lightning/lightning_qubit/_state_vector.py index 037b494db2..4e61753f58 100644 --- a/pennylane_lightning/lightning_qubit/_state_vector.py +++ b/pennylane_lightning/lightning_qubit/_state_vector.py @@ -29,7 +29,6 @@ import numpy as np import pennylane as qml from pennylane import BasisState, DeviceError, StatePrep -from pennylane.devices import MCMConfig from pennylane.measurements import MidMeasureMP from pennylane.ops import Conditional from pennylane.ops.op_math import Adjoint diff --git a/pennylane_lightning/lightning_qubit/lightning_qubit.py b/pennylane_lightning/lightning_qubit/lightning_qubit.py index 5dc4ed7b35..90c1c69c0d 100644 --- a/pennylane_lightning/lightning_qubit/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit/lightning_qubit.py @@ -21,7 +21,7 @@ import numpy as np import pennylane as qml -from pennylane.devices import DefaultExecutionConfig, Device, ExecutionConfig, MCMConfig +from pennylane.devices import DefaultExecutionConfig, Device, ExecutionConfig from pennylane.devices.default_qubit import adjoint_ops from pennylane.devices.modifiers import simulator_tracking, single_tape_support from pennylane.devices.preprocess import ( From 9986c7acfb1b77f045e47589f576aac19467f382 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Tue, 4 Jun 2024 10:43:26 -0400 Subject: [PATCH 22/25] Fixed failing test --- tests/new_api/test_device.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/new_api/test_device.py b/tests/new_api/test_device.py index 6ec882f7b5..eff47472b1 100644 --- a/tests/new_api/test_device.py +++ b/tests/new_api/test_device.py @@ -20,7 +20,7 @@ import pennylane as qml import pytest from conftest import PHI, THETA, VARPHI, LightningDevice -from pennylane.devices import DefaultExecutionConfig, DefaultQubit, ExecutionConfig +from pennylane.devices import DefaultExecutionConfig, DefaultQubit, ExecutionConfig, MCMConfig from pennylane.devices.default_qubit import adjoint_ops from pennylane.tape import QuantumScript @@ -260,9 +260,7 @@ def test_preprocess(self, adjoint): expected_program.add_transform(validate_observables, accepted_observables, name=device.name) expected_program.add_transform(validate_device_wires, device.wires, name=device.name) expected_program.add_transform( - mid_circuit_measurements, - device=device, - mcm_config={"postselect_mode": None, "mcm_method": None}, + mid_circuit_measurements, device=device, mcm_config=MCMConfig() ) expected_program.add_transform( decompose, From 61bf035d29bc949df89f9375f470185d1e6cfd7a Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Tue, 4 Jun 2024 14:44:15 +0000 Subject: [PATCH 23/25] Auto update version from '0.37.0-dev26' to '0.37.0-dev27' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 2a0c6b409f..bf9078e9e7 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.37.0-dev26" +__version__ = "0.37.0-dev27" From c854b6a8927472c5463122c0c8ff2718c50cf6bc Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Tue, 4 Jun 2024 16:48:26 -0400 Subject: [PATCH 24/25] Update requirements-dev.txt --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 86a6d7a4b2..e5c6db9967 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ pip~=22.0 -git+https://github.com/PennyLaneAI/pennylane.git@postselect-choice +git+https://github.com/PennyLaneAI/pennylane.git@master ninja flaky pybind11 From 0459edebe9e209083ac3ad2b4165f240b495b9eb Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Wed, 5 Jun 2024 11:40:57 -0400 Subject: [PATCH 25/25] Trigger CI