Repository: PennyLaneAI/pennylane
Pull request #5845 (2024-06-12 17:04:08)

Move `snapshots` and `breakpoint` functionality into a dedicated module
**Context:**
Move the contents of `~/pennylane/debugging.py` to a dedicated module and separated snapshots from PLDB and pldb.



github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [`doc/releases/changelog-dev.md`](/PennyLaneAI/pennylane/blob/master/doc/releases/changelog-dev.md) with:
* A one-to-two sentence description of the change. You may include a small working example for new features.
* A link back to this PR.
* Your name (or GitHub username) in the contributors section.

Patch:
diff --git a/pennylane/debugging/__init__.py b/pennylane/debugging/__init__.py
new file mode 100644
index 000000000..b6ea46e0f
--- /dev/null
+++ b/pennylane/debugging/__init__.py
@@ -0,0 +1,28 @@
+# Copyright 2018-2024 Xanadu Quantum Technologies Inc.
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+This module contains functionality for debugging quantum programs on simulator devices.
+"""
+
+from .snapshot import snapshots
+
+from .debugger import (
+    breakpoint,
+    expval,
+    PLDB,
+    pldb_device_manager,
+    probs,
+    state,
+    tape,
+)
diff --git a/pennylane/debugging.py b/pennylane/debugging/debugger.py
similarity index 66%
rename from pennylane/debugging.py
rename to pennylane/debugging/debugger.py
index 0d91bb827..6a2c34e8d 100644
--- a/pennylane/debugging.py
+++ b/pennylane/debugging/debugger.py
@@ -1,4 +1,4 @@
-# Copyright 2018-2022 Xanadu Quantum Technologies Inc.
+# Copyright 2018-2024 Xanadu Quantum Technologies Inc.
 
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,7 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 """
-This module contains functionality for debugging quantum programs on simulator devices.
+This module contains functionality for the PennyLane Debugger (PLDB) to support
+interactive debugging of quantum circuits.
 """
 import copy
 import pdb
@@ -20,96 +21,6 @@ import sys
 from contextlib import contextmanager
 
 import pennylane as qml
-from pennylane import DeviceError
-
-
-class _Debugger:
-    """A debugging context manager.
-
-    Without an active debugging context, devices will not save their internal state when
-    encoutering Snapshot operations. The debugger also serves as storage for the device states.
-
-    Args:
-        dev (Device): device to attach the debugger to
-    """
-
-    def __init__(self, dev):
-        # old device API: check if Snapshot is supported
-        if isinstance(dev, qml.devices.LegacyDevice) and "Snapshot" not in dev.operations:
-            raise DeviceError("Device does not support snapshots.")
-
-        # new device API: check if it's the simulator device
-        if isinstance(dev, qml.devices.Device) and not isinstance(
-            dev, (qml.devices.DefaultQubit, qml.devices.DefaultClifford)
-        ):
-            raise DeviceError("Device does not support snapshots.")
-
-        self.snapshots = {}
-        self.active = False
-        self.device = dev
-        dev._debugger = self
-
-    def __enter__(self):
-        self.active = True
-        return self
-
-    def __exit__(self, exc_type, exc_value, exc_traceback):
-        self.active = False
-        self.device._debugger = None
-
-
-def snapshots(qnode):
-    r"""Create a function that retrieves snapshot results from a QNode.
-
-    Args:
-        qnode (.QNode): the input QNode to be simulated
-
-    Returns:
-        A function that has the same argument signature as ``qnode`` and returns a dictionary.
-        When called, the function will execute the QNode on the registered device and retrieve
-        the saved snapshots obtained via the :class:`~.pennylane.Snapshot` operation. Additionally,
-        the snapshot dictionary always contains the execution results of the QNode, so the use of
-        the tag "execution_results" should be avoided to prevent conflicting key names.
-
-    **Example**
-
-    .. code-block:: python3
-
-        dev = qml.device("default.qubit", wires=2)
-
-        @qml.qnode(dev, interface=None)
-        def circuit():
-            qml.Snapshot(measurement=qml.expval(qml.Z(0))
-            qml.Hadamard(wires=0)
-            qml.Snapshot("very_important_state")
-            qml.CNOT(wires=[0, 1])
-            qml.Snapshot()
-            return qml.expval(qml.X(0))
-
-    >>> qml.snapshots(circuit)()
-    {0: 1.0,
-    'very_important_state': array([0.70710678+0.j, 0.        +0.j, 0.70710678+0.j, 0.        +0.j]),
-    2: array([0.70710678+0.j, 0.        +0.j, 0.        +0.j, 0.70710678+0.j]),
-    'execution_results': 0.0}
-    """
-
-    def get_snapshots(*args, **kwargs):
-        old_interface = qnode.interface
-        if old_interface == "auto":
-            qnode.interface = qml.math.get_interface(*args, *list(kwargs.values()))
-
-        with _Debugger(qnode.device) as dbg:
-            # pylint: disable=protected-access
-            if qnode._original_device:
-                qnode._original_device._debugger = qnode.device._debugger
-            results = qnode(*args, **kwargs)
-            # Reset interface
-            if old_interface == "auto":
-                qnode.interface = "auto"
-        dbg.snapshots["execution_results"] = results
-        return dbg.snapshots
-
-    return get_snapshots
 
 
 class PLDB(pdb.Pdb):
diff --git a/pennylane/debugging/snapshot.py b/pennylane/debugging/snapshot.py
new file mode 100644
index 000000000..f4424d215
--- /dev/null
+++ b/pennylane/debugging/snapshot.py
@@ -0,0 +1,107 @@
+# Copyright 2018-2024 Xanadu Quantum Technologies Inc.
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+This file contains the snapshots function which extracts measurements from the qnode.
+"""
+import pennylane as qml
+from pennylane import DeviceError
+
+
+class _Debugger:
+    """A debugging context manager.
+
+    Without an active debugging context, devices will not save their internal state when
+    encoutering Snapshot operations. The debugger also serves as storage for the device states.
+
+    Args:
+        dev (Device): device to attach the debugger to
+    """
+
+    def __init__(self, dev):
+        # old device API: check if Snapshot is supported
+        if isinstance(dev, qml.devices.LegacyDevice) and "Snapshot" not in dev.operations:
+            raise DeviceError("Device does not support snapshots.")
+
+        # new device API: check if it's the simulator device
+        if isinstance(dev, qml.devices.Device) and not isinstance(
+            dev, (qml.devices.DefaultQubit, qml.devices.DefaultClifford)
+        ):
+            raise DeviceError("Device does not support snapshots.")
+
+        self.snapshots = {}
+        self.active = False
+        self.device = dev
+        dev._debugger = self
+
+    def __enter__(self):
+        self.active = True
+        return self
+
+    def __exit__(self, exc_type, exc_value, exc_traceback):
+        self.active = False
+        self.device._debugger = None
+
+
+def snapshots(qnode):
+    r"""Create a function that retrieves snapshot results from a QNode.
+
+    Args:
+        qnode (.QNode): the input QNode to be simulated
+
+    Returns:
+        A function that has the same argument signature as ``qnode`` and returns a dictionary.
+        When called, the function will execute the QNode on the registered device and retrieve
+        the saved snapshots obtained via the :class:`~.pennylane.Snapshot` operation. Additionally,
+        the snapshot dictionary always contains the execution results of the QNode, so the use of
+        the tag "execution_results" should be avoided to prevent conflicting key names.
+
+    **Example**
+
+    .. code-block:: python3
+
+        dev = qml.device("default.qubit", wires=2)
+
+        @qml.qnode(dev, interface=None)
+        def circuit():
+            qml.Snapshot(measurement=qml.expval(qml.Z(0))
+            qml.Hadamard(wires=0)
+            qml.Snapshot("very_important_state")
+            qml.CNOT(wires=[0, 1])
+            qml.Snapshot()
+            return qml.expval(qml.X(0))
+
+    >>> qml.snapshots(circuit)()
+    {0: 1.0,
+    'very_important_state': array([0.70710678+0.j, 0.        +0.j, 0.70710678+0.j, 0.        +0.j]),
+    2: array([0.70710678+0.j, 0.        +0.j, 0.        +0.j, 0.70710678+0.j]),
+    'execution_results': 0.0}
+    """
+
+    def get_snapshots(*args, **kwargs):
+        old_interface = qnode.interface
+        if old_interface == "auto":
+            qnode.interface = qml.math.get_interface(*args, *list(kwargs.values()))
+
+        with _Debugger(qnode.device) as dbg:
+            # pylint: disable=protected-access
+            if qnode._original_device:
+                qnode._original_device._debugger = qnode.device._debugger
+            results = qnode(*args, **kwargs)
+            # Reset interface
+            if old_interface == "auto":
+                qnode.interface = "auto"
+        dbg.snapshots["execution_results"] = results
+        return dbg.snapshots
+
+    return get_snapshots


Test patch:
diff --git a/tests/test_debugging.py b/tests/test_debugging.py
index 30d49f6d3..1cc3e453f 100644
--- a/tests/test_debugging.py
+++ b/tests/test_debugging.py
@@ -228,7 +228,7 @@ class TestSnapshot:
         dev = DummyDevice()
 
         with pytest.raises(qml.DeviceError, match="Device does not support snapshots."):
-            with qml.debugging._Debugger(dev):
+            with qml.debugging.snapshot._Debugger(dev):
                 dev.execute([])
 
     def test_empty_snapshots(self):
@@ -572,7 +572,7 @@ def test_measure(mock_method, measurement_process):
     with qml.queuing.AnnotatedQueue() as queue:
         ops = [qml.X(0), qml.Y(1), qml.Z(0)] + [qml.Hadamard(i) for i in range(3)]
         measurements = [qml.expval(qml.X(2)), qml.state(), qml.probs(), qml.var(qml.Z(3))]
-        qml.debugging._measure(measurement_process)
+        qml.debugging.debugger._measure(measurement_process)
 
     executed_tape = qml.tape.QuantumScript.from_queue(queue)
     expected_tape = qml.tape.QuantumScript(ops, measurements)