From cdc36bc6b8e8ea55b2ff8664fd14da5a3078f3d9 Mon Sep 17 00:00:00 2001 From: Mudit Pandey Date: Thu, 13 Jun 2024 15:23:03 -0400 Subject: [PATCH] Update `QNode` to not mutate original MCM config (#5850) As name says. Mutating the config in place was causing unexpected results such as below. Now we create a copy of the original config before execution begins, which can be mutated as needed. ```python import pennylane as qml dev = qml.device("default.qubit") @qml.qnode(dev, postselect_mode="fill-shots") def f(x, mp): qml.RX(x, 0) qml.measure(0, postselect=1) return mp(qml.PauliZ(0)) ``` ```pycon >>> print(f(1.8, qml.sample, shots=10)) [-1. -1. -1. -1. -1. -1. -1. -1. -1. -1.] >>> print(f(1.8, qml.expval)) -0.9999999999999998 >>> print(f(1.8, qml.sample, shots=10)) [-1. -1. -1. -1. -1. -1.] ``` --- doc/releases/changelog-dev.md | 31 ++++++++++++++++--------------- pennylane/workflow/qnode.py | 2 +- tests/test_qnode.py | 28 +++++++++++++++++++++++++++- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 5b289648612..298a8fc83e7 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -34,21 +34,6 @@ [1 1 0] ``` -* `qml.QNode` and `qml.qnode` now accept two new keyword arguments: `postselect_mode` and `mcm_method`. - These keyword arguments can be used to configure how the device should behave when running circuits with - mid-circuit measurements. - [(#5679)](https://github.com/PennyLaneAI/pennylane/pull/5679) - [(#5833)](https://github.com/PennyLaneAI/pennylane/pull/5833) - - * `postselect_mode="hw-like"` will indicate to devices to discard invalid shots when postselecting - mid-circuit measurements. Use `postselect_mode="fill-shots"` to unconditionally sample the postselected - value, thus making all samples valid. This is equivalent to sampling until the number of valid samples - matches the total number of shots. - * `mcm_method` will indicate which strategy to use for running circuits with mid-circuit measurements. - Use `mcm_method="deferred"` to use the deferred measurements principle, or `mcm_method="one-shot"` - to execute once for each shot. If using `qml.jit` with the Catalyst compiler, `mcm_method="single-branch-statistics"` - is also available. Using this method, a single branch of the execution tree will be randomly explored. - * The `default.tensor` device is introduced to perform tensor network simulations of quantum circuits using the `mps` (Matrix Product State) method. [(#5699)](https://github.com/PennyLaneAI/pennylane/pull/5699) @@ -110,6 +95,22 @@

Mid-circuit measurements and dynamic circuits

+* `qml.QNode` and `qml.qnode` now accept two new keyword arguments: `postselect_mode` and `mcm_method`. + These keyword arguments can be used to configure how the device should behave when running circuits with + mid-circuit measurements. + [(#5679)](https://github.com/PennyLaneAI/pennylane/pull/5679) + [(#5833)](https://github.com/PennyLaneAI/pennylane/pull/5833) + [(#5850)](https://github.com/PennyLaneAI/pennylane/pull/5850) + + * `postselect_mode="hw-like"` will indicate to devices to discard invalid shots when postselecting + mid-circuit measurements. Use `postselect_mode="fill-shots"` to unconditionally sample the postselected + value, thus making all samples valid. This is equivalent to sampling until the number of valid samples + matches the total number of shots. + * `mcm_method` will indicate which strategy to use for running circuits with mid-circuit measurements. + Use `mcm_method="deferred"` to use the deferred measurements principle, or `mcm_method="one-shot"` + to execute once for each shot. If using `qml.jit` with the Catalyst compiler, `mcm_method="single-branch-statistics"` + is also available. Using this method, a single branch of the execution tree will be randomly explored. + * The `dynamic_one_shot` transform is made compatible with the Catalyst compiler. [(#5766)](https://github.com/PennyLaneAI/pennylane/pull/5766) diff --git a/pennylane/workflow/qnode.py b/pennylane/workflow/qnode.py index f07dbdb0810..985eb24cba8 100644 --- a/pennylane/workflow/qnode.py +++ b/pennylane/workflow/qnode.py @@ -1042,7 +1042,7 @@ def _execution_component(self, args: tuple, kwargs: dict, override_shots) -> qml ) self._tape_cached = using_custom_cache and self.tape.hash in cache - mcm_config = self.execute_kwargs["mcm_config"] + mcm_config = copy.copy(self.execute_kwargs["mcm_config"]) finite_shots = _get_device_shots if override_shots is False else override_shots if not finite_shots: mcm_config.postselect_mode = None diff --git a/tests/test_qnode.py b/tests/test_qnode.py index b11ca5db1e5..f4700935b19 100644 --- a/tests/test_qnode.py +++ b/tests/test_qnode.py @@ -16,7 +16,7 @@ # pylint: disable=import-outside-toplevel, protected-access, no-member import warnings -from dataclasses import replace +from dataclasses import asdict, replace from functools import partial from typing import Callable, Tuple @@ -1839,6 +1839,32 @@ def circuit(x): with pytest.raises(ValueError, match="Cannot use mcm_method='single-branch-statistics'"): _ = circuit(param) + @pytest.mark.parametrize("postselect_mode", [None, "fill-shots", "hw-like"]) + @pytest.mark.parametrize("mcm_method", [None, "one-shot", "deferred"]) + def test_execution_does_not_mutate_config(self, mcm_method, postselect_mode): + """Test that executing a QNode does not mutate its mid-circuit measurement config options""" + dev = qml.device("default.qubit", wires=2) + + original_config = qml.devices.MCMConfig( + postselect_mode=postselect_mode, mcm_method=mcm_method + ) + + @qml.qnode(dev, **asdict(original_config)) + def circuit(x, mp): + qml.RX(x, 0) + qml.measure(0, postselect=1) + return mp(qml.PauliZ(0)) + + _ = circuit(1.8, qml.expval, shots=10) + assert circuit.execute_kwargs["mcm_config"] == original_config + + if mcm_method != "one-shot": + _ = circuit(1.8, qml.expval) + assert circuit.execute_kwargs["mcm_config"] == original_config + + _ = circuit(1.8, qml.expval, shots=10) + assert circuit.execute_kwargs["mcm_config"] == original_config + class TestTapeExpansion: """Test that tape expansion within the QNode works correctly"""