diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index a0017ec476d..78b02db38d0 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -99,6 +99,7 @@ A suite of integration tests has been added. [(#2231)](https://github.com/PennyLaneAI/pennylane/pull/2231) [(#2234)](https://github.com/PennyLaneAI/pennylane/pull/2234) + [(#2251)](https://github.com/PennyLaneAI/pennylane/pull/2251)

Improvements

diff --git a/pennylane/transforms/qcut.py b/pennylane/transforms/qcut.py index 701f59bdf98..c74138b5491 100644 --- a/pennylane/transforms/qcut.py +++ b/pennylane/transforms/qcut.py @@ -34,7 +34,6 @@ from pennylane.operation import Expectation, Operation, Operator, Tensor from pennylane.ops.qubit.non_parametric_ops import WireCut from pennylane.tape import QuantumTape -from pennylane.transforms import batch_transform from pennylane.wires import Wires from .batch_transform import batch_transform @@ -941,6 +940,31 @@ def circuit(x): >>> qml.grad(cut_circuit)(x) -0.506395895364911 """ + if len(tape.measurements) != 1: + raise ValueError( + "The circuit cutting workflow only supports circuits with a single output " + "measurement" + ) + + if not all(m.return_type is Expectation for m in tape.measurements): + raise ValueError( + "The circuit cutting workflow only supports circuits with expectation " + "value measurements" + ) + + if use_opt_einsum: + try: + import opt_einsum # pylint: disable=import-outside-toplevel,unused-import + except ImportError as e: + raise ImportError( + "The opt_einsum package is required when use_opt_einsum is set to " + "True in the cut_circuit function. This package can be " + "installed using:\npip install opt_einsum" + ) from e + + num_cut = len([op for op in tape.operations if isinstance(op, WireCut)]) + if num_cut == 0: + raise ValueError("Cannot apply the circuit cutting workflow to a circuit without any cuts") g = tape_to_graph(tape) replace_wire_cut_nodes(g) diff --git a/tests/transforms/test_qcut.py b/tests/transforms/test_qcut.py index d8f1422d447..5863af81bc6 100644 --- a/tests/transforms/test_qcut.py +++ b/tests/transforms/test_qcut.py @@ -2004,6 +2004,57 @@ def circuit(x): assert np.isclose(gradient, cut_gradient) +class TestCutCircuitTransformValidation: + """Tests of validation checks in the cut_circuit function""" + + def test_multiple_measurements_raises(self): + """Tests if a ValueError is raised when a tape with multiple measurements is requested + to be cut""" + + with qml.tape.QuantumTape() as tape: + qml.expval(qml.PauliZ(0)) + qml.expval(qml.PauliZ(1)) + + with pytest.raises(ValueError, match="The circuit cutting workflow only supports circuits"): + qcut.cut_circuit(tape) + + def test_no_measurements_raises(self): + """Tests if a ValueError is raised when a tape with no measurement is requested + to be cut""" + with pytest.raises(ValueError, match="The circuit cutting workflow only supports circuits"): + qcut.cut_circuit(qml.tape.QuantumTape()) + + def test_non_expectation_raises(self): + """Tests if a ValueError is raised when a tape with measurements that are not expectation + values is requested to be cut""" + + with qml.tape.QuantumTape() as tape: + qml.var(qml.PauliZ(0)) + + with pytest.raises(ValueError, match="workflow only supports circuits with expectation"): + qcut.cut_circuit(tape) + + def test_fail_import(self, monkeypatch): + """Test if an ImportError is raised when opt_einsum is requested but not installed""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.PauliZ(0)) + + with monkeypatch.context() as m: + m.setitem(sys.modules, "opt_einsum", None) + + with pytest.raises(ImportError, match="The opt_einsum package is required"): + qcut.cut_circuit(tape, use_opt_einsum=True) + + def test_no_cuts_raises(self): + """Tests if a ValueError is raised when circuit cutting is to be applied to a circuit + without cuts""" + with qml.tape.QuantumTape() as tape: + qml.expval(qml.PauliZ(0)) + + with pytest.raises(ValueError, match="to a circuit without any cuts"): + qcut.cut_circuit(tape) + + class TestCutStrategy: """Tests for class CutStrategy"""