diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst
index 9b8b68f04d2..a61b8338222 100644
--- a/doc/development/deprecations.rst
+++ b/doc/development/deprecations.rst
@@ -9,6 +9,12 @@ deprecations are listed below.
Pending deprecations
--------------------
+* The ``mcm_config`` argument to ``qml.execute`` has been deprecated.
+ Instead, use the ``mcm_method`` and ``postselect_mode`` arguments.
+
+ - Deprecated in v0.41
+ - Will be removed in v0.42
+
* Specifying gradient keyword arguments as any additional keyword argument to the qnode is deprecated
and will be removed in v0.42. The gradient keyword arguments should be passed to the new
keyword argument ``gradient_kwargs`` via an explicit dictionary, like ``gradient_kwargs={"h": 1e-4}``.
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 9d7e3a50427..88280392227 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -84,6 +84,9 @@
Deprecations 👋
+* The `mcm_method` keyword in `qml.execute` is deprecated. Instead, use the ``mcm_method`` and ``postselect_mode`` arguments.
+ [(#6807)](https://github.com/PennyLaneAI/pennylane/pull/6807)
+
* Specifying gradient keyword arguments as any additional keyword argument to the qnode is deprecated
and will be removed in v0.42. The gradient keyword arguments should be passed to the new
keyword argument `gradient_kwargs` via an explicit dictionary. This change will improve qnode argument
diff --git a/pennylane/workflow/_capture_qnode.py b/pennylane/workflow/_capture_qnode.py
index d4b23f2cb6a..2552770a159 100644
--- a/pennylane/workflow/_capture_qnode.py
+++ b/pennylane/workflow/_capture_qnode.py
@@ -107,7 +107,6 @@
"""
from copy import copy
-from dataclasses import asdict
from functools import partial
from numbers import Number
from warnings import warn
@@ -404,8 +403,7 @@ def f(x):
qfunc_jaxpr = jax.make_jaxpr(flat_fn)(*args)
execute_kwargs = copy(qnode.execute_kwargs)
- mcm_config = asdict(execute_kwargs.pop("mcm_config"))
- qnode_kwargs = {"diff_method": qnode.diff_method, **execute_kwargs, **mcm_config}
+ qnode_kwargs = {"diff_method": qnode.diff_method, **execute_kwargs}
flat_args = jax.tree_util.tree_leaves(args)
diff --git a/pennylane/workflow/execution.py b/pennylane/workflow/execution.py
index cfe93f322d2..fb9ac5ef708 100644
--- a/pennylane/workflow/execution.py
+++ b/pennylane/workflow/execution.py
@@ -47,14 +47,16 @@ def execute(
diff_method: Optional[Union[Callable, SupportedDiffMethods, TransformDispatcher]] = None,
interface: Optional[InterfaceLike] = Interface.AUTO,
*,
+ transform_program: TransformProgram = None,
grad_on_execution: Literal[bool, "best"] = "best",
cache: Union[None, bool, dict, Cache] = True,
cachesize: int = 10000,
max_diff: int = 1,
device_vjp: Union[bool, None] = False,
+ postselect_mode=None,
+ mcm_method=None,
gradient_kwargs: dict = None,
- transform_program: TransformProgram = None,
- mcm_config: "qml.devices.MCMConfig" = None,
+ mcm_config: "qml.devices.MCMConfig" = "unset",
config="unset",
inner_transform="unset",
) -> ResultBatch:
@@ -86,10 +88,18 @@ def execute(
(classical) computational overhead during the backward pass.
device_vjp=False (Optional[bool]): whether or not to use the device-provided Jacobian
product if it is available.
- mcm_config (dict): Dictionary containing configuration options for handling
- 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 ``None``.
+ mcm_method (str): Strategy to use when executing circuits with mid-circuit measurements.
+ ``"deferred"`` is ignored. If mid-circuit measurements are found in the circuit,
+ the device will use ``"tree-traversal"`` if specified and the ``"one-shot"`` method
+ otherwise. For usage details, please refer to the
+ :doc:`dynamic quantum circuits page `.
gradient_kwargs (dict): dictionary of keyword arguments to pass when
determining the gradients of tapes.
+ mcm_config="unset": **DEPRECATED**. This keyword argument has been replaced by ``postselect_mode``
+ and ``mcm_method`` and will be removed in v0.42.
config="unset": **DEPRECATED**. This keyword argument has been deprecated and
will be removed in v0.42.
inner_transform="unset": **DEPRECATED**. This keyword argument has been deprecated
@@ -173,6 +183,14 @@ def cost_fn(params, x):
qml.PennyLaneDeprecationWarning,
)
+ if mcm_config != "unset":
+ warn(
+ "The mcm_config argument is deprecated and will be removed in v0.42, use mcm_method and postselect_mode instead.",
+ qml.PennyLaneDeprecationWarning,
+ )
+ mcm_method = mcm_config.mcm_method
+ postselect_mode = mcm_config.postselect_mode
+
if logger.isEnabledFor(logging.DEBUG):
logger.debug(
(
@@ -209,7 +227,7 @@ def cost_fn(params, x):
gradient_method=diff_method,
grad_on_execution=None if grad_on_execution == "best" else grad_on_execution,
use_device_jacobian_product=device_vjp,
- mcm_config=mcm_config or {},
+ mcm_config=qml.devices.MCMConfig(postselect_mode=postselect_mode, mcm_method=mcm_method),
gradient_keyword_arguments=gradient_kwargs or {},
derivative_order=max_diff,
)
diff --git a/pennylane/workflow/qnode.py b/pennylane/workflow/qnode.py
index 39659dbc95e..ea1a9e33900 100644
--- a/pennylane/workflow/qnode.py
+++ b/pennylane/workflow/qnode.py
@@ -107,6 +107,10 @@ def _to_qfunc_output_type(
return qml.pytrees.unflatten(results, qfunc_output_structure)
+def _validate_mcm_config(postselect_mode: str, mcm_method: str) -> None:
+ qml.devices.MCMConfig(postselect_mode=postselect_mode, mcm_method=mcm_method)
+
+
def _validate_gradient_kwargs(gradient_kwargs: dict) -> None:
for kwarg in gradient_kwargs:
if kwarg == "expansion_strategy":
@@ -563,17 +567,18 @@ def __init__(
self.device = device
self._interface = get_canonical_interface_name(interface)
self.diff_method = diff_method
- mcm_config = qml.devices.MCMConfig(mcm_method=mcm_method, postselect_mode=postselect_mode)
cache = (max_diff > 1) if cache == "auto" else cache
# execution keyword arguments
+ _validate_mcm_config(postselect_mode, mcm_method)
self.execute_kwargs = {
"grad_on_execution": grad_on_execution,
"cache": cache,
"cachesize": cachesize,
"max_diff": max_diff,
"device_vjp": device_vjp,
- "mcm_config": mcm_config,
+ "postselect_mode": postselect_mode,
+ "mcm_method": mcm_method,
}
# internal data attributes
diff --git a/tests/capture/test_capture_qnode.py b/tests/capture/test_capture_qnode.py
index 0f2f7ea3abe..680d11f083e 100644
--- a/tests/capture/test_capture_qnode.py
+++ b/tests/capture/test_capture_qnode.py
@@ -14,7 +14,6 @@
"""
Tests for capturing a qnode into jaxpr.
"""
-from dataclasses import asdict
from functools import partial
# pylint: disable=protected-access
@@ -130,7 +129,6 @@ def circuit(x):
assert eqn0.params["shots"] == qml.measurements.Shots(None)
expected_kwargs = {"diff_method": "best"}
expected_kwargs.update(circuit.execute_kwargs)
- expected_kwargs.update(asdict(expected_kwargs.pop("mcm_config")))
assert eqn0.params["qnode_kwargs"] == expected_kwargs
qfunc_jaxpr = eqn0.params["qfunc_jaxpr"]
diff --git a/tests/ops/functions/test_matrix.py b/tests/ops/functions/test_matrix.py
index 5adc8912cd2..a1f82e6077a 100644
--- a/tests/ops/functions/test_matrix.py
+++ b/tests/ops/functions/test_matrix.py
@@ -683,6 +683,7 @@ def circuit(theta):
assert np.allclose(matrix, expected_matrix)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
@pytest.mark.catalyst
@pytest.mark.external
def test_catalyst(self):
diff --git a/tests/ops/op_math/test_exp.py b/tests/ops/op_math/test_exp.py
index 5abead84595..6ca4c68091b 100644
--- a/tests/ops/op_math/test_exp.py
+++ b/tests/ops/op_math/test_exp.py
@@ -768,6 +768,7 @@ def circ(phi):
grad = jax.grad(circ)(phi)
assert qml.math.allclose(grad, -jnp.sin(phi))
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
@pytest.mark.catalyst
@pytest.mark.external
def test_catalyst_qnode(self):
diff --git a/tests/test_compiler.py b/tests/test_compiler.py
index 38d61cd86cf..6eb3fa851a4 100644
--- a/tests/test_compiler.py
+++ b/tests/test_compiler.py
@@ -76,6 +76,7 @@ def test_compiler(self):
assert qml.compiler.available("catalyst")
assert qml.compiler.available_compilers() == ["catalyst", "cuda_quantum"]
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_active_compiler(self):
"""Test `qml.compiler.active_compiler` inside a simple circuit"""
dev = qml.device("lightning.qubit", wires=2)
@@ -91,6 +92,7 @@ def circuit(phi, theta):
assert jnp.allclose(circuit(jnp.pi, jnp.pi / 2), 1.0)
assert jnp.allclose(qml.qjit(circuit)(jnp.pi, jnp.pi / 2), -1.0)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_active(self):
"""Test `qml.compiler.active` inside a simple circuit"""
dev = qml.device("lightning.qubit", wires=2)
@@ -114,6 +116,7 @@ def test_jax_enable_x64(self, jax_enable_x64):
qml.compiler.active()
assert jax.config.jax_enable_x64 is jax_enable_x64
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_qjit_circuit(self):
"""Test JIT compilation of a circuit with 2-qubit"""
dev = qml.device("lightning.qubit", wires=2)
@@ -128,6 +131,7 @@ def circuit(theta):
assert jnp.allclose(circuit(0.5), 0.0)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_qjit_aot(self):
"""Test AOT compilation of a circuit with 2-qubit"""
@@ -152,6 +156,7 @@ def circuit(x: complex, z: ShapedArray(shape=(3,), dtype=jnp.float64)):
)
assert jnp.allclose(result, expected)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
@pytest.mark.parametrize(
"_in,_out",
[
@@ -196,6 +201,7 @@ def workflow1(params1, params2):
result = workflow1(params1, params2)
assert jnp.allclose(result, expected)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_return_value_dict(self):
"""Test pytree return values."""
dev = qml.device("lightning.qubit", wires=2)
@@ -218,6 +224,7 @@ def circuit1(params):
assert jnp.allclose(result["w0"], expected["w0"])
assert jnp.allclose(result["w1"], expected["w1"])
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_qjit_python_if(self):
"""Test JIT compilation with the autograph support"""
dev = qml.device("lightning.qubit", wires=2)
@@ -235,6 +242,7 @@ def circuit(x: int):
assert jnp.allclose(circuit(3), 0.0)
assert jnp.allclose(circuit(5), 1.0)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_compilation_opt(self):
"""Test user-configurable compilation options"""
dev = qml.device("lightning.qubit", wires=2)
@@ -250,6 +258,7 @@ def circuit(x: float):
result_header = "func.func public @circuit(%arg0: tensor) -> tensor"
assert result_header in mlir_str
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_qjit_adjoint(self):
"""Test JIT compilation with adjoint support"""
dev = qml.device("lightning.qubit", wires=2)
@@ -273,6 +282,7 @@ def func():
assert jnp.allclose(workflow_cl(0.1, [1]), workflow_pl(0.1, [1]))
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_qjit_adjoint_lazy(self):
"""Test that the lazy kwarg is supported."""
dev = qml.device("lightning.qubit", wires=2)
@@ -287,6 +297,7 @@ def workflow_pl(theta, wires):
assert jnp.allclose(workflow_cl(0.1, [1]), workflow_pl(0.1, [1]))
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_control(self):
"""Test that control works with qjit."""
dev = qml.device("lightning.qubit", wires=2)
@@ -317,6 +328,7 @@ def cond_fn():
class TestCatalystControlFlow:
"""Test ``qml.qjit`` with Catalyst's control-flow operations"""
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_alternating_while_loop(self):
"""Test simple while loop."""
dev = qml.device("lightning.qubit", wires=1)
@@ -334,6 +346,7 @@ def loop(v):
assert jnp.allclose(circuit(1), -1.0)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_nested_while_loops(self):
"""Test nested while loops."""
dev = qml.device("lightning.qubit", wires=1)
@@ -393,6 +406,7 @@ def loop(v):
expected = [qml.PauliX(0) for i in range(4)]
_ = [qml.assert_equal(i, j) for i, j in zip(tape.operations, expected)]
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_dynamic_wires_for_loops(self):
"""Test for loops with iteration index-dependant wires."""
dev = qml.device("lightning.qubit", wires=6)
@@ -414,6 +428,7 @@ def loop_fn(i):
assert jnp.allclose(circuit(6), expected)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_nested_for_loops(self):
"""Test nested for loops."""
dev = qml.device("lightning.qubit", wires=4)
@@ -445,6 +460,7 @@ def inner(j):
assert jnp.allclose(circuit(4), jnp.eye(2**4)[0])
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_for_loop_python_fallback(self):
"""Test that qml.for_loop fallsback to Python
interpretation if Catalyst is not available"""
@@ -496,6 +512,7 @@ def inner(j):
_ = [qml.assert_equal(i, j) for i, j in zip(res, expected)]
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_cond(self):
"""Test condition with simple true_fn"""
dev = qml.device("lightning.qubit", wires=1)
@@ -514,6 +531,7 @@ def ansatz_true():
assert jnp.allclose(circuit(1.4), 1.0)
assert jnp.allclose(circuit(1.6), 0.0)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_cond_with_else(self):
"""Test condition with simple true_fn and false_fn"""
dev = qml.device("lightning.qubit", wires=1)
@@ -535,6 +553,7 @@ def ansatz_false():
assert jnp.allclose(circuit(1.4), 0.16996714)
assert jnp.allclose(circuit(1.6), 0.0)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_cond_with_elif(self):
"""Test condition with a simple elif branch"""
dev = qml.device("lightning.qubit", wires=1)
@@ -558,6 +577,7 @@ def false_fn():
assert jnp.allclose(circuit(1.2), 0.13042371)
assert jnp.allclose(circuit(jnp.pi), -1.0)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_cond_with_elifs(self):
"""Test condition with multiple elif branches"""
dev = qml.device("lightning.qubit", wires=1)
@@ -630,6 +650,7 @@ def conditional_false_fn(): # pylint: disable=unused-variable
class TestCatalystGrad:
"""Test ``qml.qjit`` with Catalyst's grad operations"""
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_grad_classical_preprocessing(self):
"""Test the grad transformation with classical preprocessing."""
@@ -647,6 +668,7 @@ def circuit(x):
assert jnp.allclose(workflow(2.0), -jnp.pi)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_grad_with_postprocessing(self):
"""Test the grad transformation with classical preprocessing and postprocessing."""
dev = qml.device("lightning.qubit", wires=1)
@@ -665,6 +687,7 @@ def loss(theta):
assert jnp.allclose(workflow(1.0), 5.04324559)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_grad_with_multiple_qnodes(self):
"""Test the grad transformation with multiple QNodes with their own differentiation methods."""
dev = qml.device("lightning.qubit", wires=1)
@@ -703,6 +726,7 @@ def dsquare(x: float):
assert jnp.allclose(dsquare(2.3), 4.6)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_jacobian_diff_method(self):
"""Test the Jacobian transformation with the device diff_method."""
dev = qml.device("lightning.qubit", wires=1)
@@ -721,6 +745,7 @@ def workflow(p: float):
assert jnp.allclose(result, reference)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_jacobian_auto(self):
"""Test the Jacobian transformation with 'auto'."""
dev = qml.device("lightning.qubit", wires=1)
@@ -740,6 +765,7 @@ def circuit(x):
assert jnp.allclose(result, reference)
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
def test_jacobian_fd(self):
"""Test the Jacobian transformation with 'fd'."""
dev = qml.device("lightning.qubit", wires=1)
@@ -838,6 +864,7 @@ def f(x):
vjp(x, dy)
+@pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
class TestCatalystSample:
"""Test qml.sample with Catalyst."""
@@ -858,6 +885,7 @@ def circuit(x):
assert circuit(jnp.pi) == 1
+@pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
class TestCatalystMCMs:
"""Test dynamic_one_shot with Catalyst."""
diff --git a/tests/test_qnode.py b/tests/test_qnode.py
index ec141af64e7..5d270d1f9b8 100644
--- a/tests/test_qnode.py
+++ b/tests/test_qnode.py
@@ -1926,14 +1926,17 @@ def circuit(x, mp):
return mp(qml.PauliZ(0))
_ = circuit(1.8, qml.expval, shots=10)
- assert circuit.execute_kwargs["mcm_config"] == original_config
+ assert circuit.execute_kwargs["postselect_mode"] == original_config.postselect_mode
+ assert circuit.execute_kwargs["mcm_method"] == original_config.mcm_method
if mcm_method != "one-shot":
_ = circuit(1.8, qml.expval)
- assert circuit.execute_kwargs["mcm_config"] == original_config
+ assert circuit.execute_kwargs["postselect_mode"] == original_config.postselect_mode
+ assert circuit.execute_kwargs["mcm_method"] == original_config.mcm_method
_ = circuit(1.8, qml.expval, shots=10)
- assert circuit.execute_kwargs["mcm_config"] == original_config
+ assert circuit.execute_kwargs["postselect_mode"] == original_config.postselect_mode
+ assert circuit.execute_kwargs["mcm_method"] == original_config.mcm_method
class TestTapeExpansion:
diff --git a/tests/transforms/core/test_transform_dispatcher.py b/tests/transforms/core/test_transform_dispatcher.py
index 4a24627c739..6784c3b0a6e 100644
--- a/tests/transforms/core/test_transform_dispatcher.py
+++ b/tests/transforms/core/test_transform_dispatcher.py
@@ -216,6 +216,7 @@ def test_the_transform_container_attributes(self):
class TestTransformDispatcher: # pylint: disable=too-many-public-methods
"""Test the transform function (validate and dispatch)."""
+ @pytest.mark.xfail(reason="https://github.com/PennyLaneAI/catalyst/pull/1452")
@pytest.mark.catalyst
@pytest.mark.external
def test_error_on_qjit(self):
diff --git a/tests/workflow/interfaces/execute/test_execute.py b/tests/workflow/interfaces/execute/test_execute.py
index 8bea335ff7e..4a253727261 100644
--- a/tests/workflow/interfaces/execute/test_execute.py
+++ b/tests/workflow/interfaces/execute/test_execute.py
@@ -57,6 +57,28 @@ def test_execute_legacy_device():
assert qml.math.allclose(res[0], np.cos(0.1))
+def test_mcm_config_deprecation(mocker):
+ """Test that mcm_config argument has been deprecated."""
+
+ tape = qml.tape.QuantumScript(
+ [qml.RX(qml.numpy.array(1.0), 0)], [qml.expval(qml.Z(0))], shots=10
+ )
+ dev = qml.device("default.qubit")
+
+ with dev.tracker:
+ with pytest.warns(
+ qml.PennyLaneDeprecationWarning,
+ match="The mcm_config argument is deprecated and will be removed in v0.42, use mcm_method and postselect_mode instead.",
+ ):
+ spy = mocker.spy(qml.dynamic_one_shot, "_transform")
+ qml.execute(
+ (tape,),
+ dev,
+ mcm_config=qml.devices.MCMConfig(mcm_method="one-shot", postselect_mode=None),
+ )
+ spy.assert_called_once()
+
+
def test_config_deprecation():
"""Test that the config argument has been deprecated."""