Repository: PennyLaneAI/pennylane
Pull request #2260 (2022-03-01 21:38:50)

Circuit cutting: Remove circuit cuts that do not result in a disconnection
**Context:**

Not all place `WireCut` operations lead to a disconnection between a pair of subgraphs. Sometimes a `WireCut` will live _within_ a subgraph. For example, in this circuit:
![image](https://user-images.githubusercontent.com/49409390/156253115-353db445-ebb1-4a30-a35b-20465e8d78ca.png)

This PR removes the `MeasureNode` and `PrepareNode` pairs resulting from `WireCut`s that do not lead to a disconnection.

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.
codecov[bot]: # [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#2260](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (b0111c9) into [qcut_remove_unneeded_subgraphs](https://codecov.io/gh/PennyLaneAI/pennylane/commit/81c660dc5aa5925029083c1f686df18777985f68?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (81c660d) will **increase** coverage by `5.36%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260/graphs/tree.svg?width=650&height=150&src=pr&token=9S3YzlUgGk&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

```diff
@@                        Coverage Diff                         @@
##           qcut_remove_unneeded_subgraphs    #2260      +/-   ##
==================================================================
+ Coverage                           93.91%   99.27%   +5.36%     
==================================================================
  Files                                 231      226       -5     
  Lines                               18397    18003     -394     
==================================================================
+ Hits                                17277    17873     +596     
+ Misses                               1120      130     -990     
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/transforms/qcut.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3RyYW5zZm9ybXMvcWN1dC5weQ==) | `100.00% <100.00%> (+0.24%)` | :arrow_up: |
| [pennylane/\_\_init\_\_.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL19faW5pdF9fLnB5) | `97.64% <0.00%> (-2.36%)` | :arrow_down: |
| [qchem/pennylane\_qchem/\_version.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cWNoZW0vcGVubnlsYW5lX3FjaGVtL192ZXJzaW9uLnB5) | | |
| [qchem/pennylane\_qchem/qchem/\_\_init\_\_.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cWNoZW0vcGVubnlsYW5lX3FjaGVtL3FjaGVtL19faW5pdF9fLnB5) | | |
| [qchem/pennylane\_qchem/qchem/openfermion.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cWNoZW0vcGVubnlsYW5lX3FjaGVtL3FjaGVtL29wZW5mZXJtaW9uLnB5) | | |
| [qchem/pennylane\_qchem/qchem/obs.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cWNoZW0vcGVubnlsYW5lX3FjaGVtL3FjaGVtL29icy5weQ==) | | |
| [qchem/pennylane\_qchem/qchem/structure.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cWNoZW0vcGVubnlsYW5lX3FjaGVtL3FjaGVtL3N0cnVjdHVyZS5weQ==) | | |
| [pennylane/\_qubit\_device.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL19xdWJpdF9kZXZpY2UucHk=) | `98.72% <0.00%> (+0.31%)` | :arrow_up: |
| [pennylane/devices/default\_qubit.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL2RldmljZXMvZGVmYXVsdF9xdWJpdC5weQ==) | `100.00% <0.00%> (+0.40%)` | :arrow_up: |
| ... and [30 more](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Last update [81c660d...b0111c9](https://codecov.io/gh/PennyLaneAI/pennylane/pull/2260?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).


Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 09e5b982c..b9022bd69 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -208,6 +208,9 @@
 
   Circuit fragments that are disconnected from the terminal measurements are now removed.
   [(#2254)](https://github.com/PennyLaneAI/pennylane/pull/2254)
+  
+  `WireCut` operations that do not lead to a disconnection are now being removed.
+  [(#2260)](https://github.com/PennyLaneAI/pennylane/pull/2260)
 
 <h3>Improvements</h3>
 
diff --git a/pennylane/transforms/qcut.py b/pennylane/transforms/qcut.py
index 2d21ae174..1f1b40736 100644
--- a/pennylane/transforms/qcut.py
+++ b/pennylane/transforms/qcut.py
@@ -233,6 +233,7 @@ def tape_to_graph(tape: QuantumTape) -> MultiDiGraph:
     return graph
 
 
+# pylint: disable=too-many-branches
 def fragment_graph(graph: MultiDiGraph) -> Tuple[Tuple[MultiDiGraph], MultiDiGraph]:
     """
     Fragments a graph into a collection of subgraphs as well as returning
@@ -305,7 +306,16 @@ def fragment_graph(graph: MultiDiGraph) -> Tuple[Tuple[MultiDiGraph], MultiDiGra
             if subgraph.has_node(node2):
                 end_fragment = i
 
-        communication_graph.add_edge(start_fragment, end_fragment, pair=(node1, node2))
+        if start_fragment != end_fragment:
+            communication_graph.add_edge(start_fragment, end_fragment, pair=(node1, node2))
+        else:
+            # The MeasureNode and PrepareNode pair live in the same fragment and did not result
+            # in a disconnection. We can therefore remove these nodes. Note that we do not need
+            # to worry about adding back an edge between the predecessor to node1 and the successor
+            # to node2 because our next step is to convert the fragment circuit graphs to tapes,
+            # a process that does not depend on edge connections in the subgraph.
+            subgraphs[start_fragment].remove_node(node1)
+            subgraphs[end_fragment].remove_node(node2)
 
     terminal_indices = [i for i, s in enumerate(subgraphs) for n in measure_nodes if s.has_node(n)]
 


Test patch:
diff --git a/tests/transforms/test_qcut.py b/tests/transforms/test_qcut.py
index a33c989ee..782c16aee 100644
--- a/tests/transforms/test_qcut.py
+++ b/tests/transforms/test_qcut.py
@@ -21,7 +21,7 @@ import sys
 from itertools import product
 
 import pytest
-from networkx import MultiDiGraph
+from networkx import MultiDiGraph, number_of_selfloops
 from scipy.stats import unitary_group
 
 import pennylane as qml
@@ -900,6 +900,23 @@ class TestFragmentGraph:
         assert communication_graph_0.nodes == communication_graph_1.nodes
         assert communication_graph_0.edges == communication_graph_1.edges
 
+    def test_contained_cut(self):
+        """Tests that fragmentation ignores `MeasureNode` and `PrepareNode` pairs that do not
+        result in a disconnection"""
+        with qml.tape.QuantumTape() as tape:
+            qml.RX(0.4, wires=0)
+            qml.CNOT(wires=[0, 1])
+            qml.WireCut(wires=0)
+            qml.CNOT(wires=[0, 1])
+            qml.RX(0.4, wires=0)
+            qml.expval(qml.PauliZ(0))
+
+        g = qcut.tape_to_graph(tape)
+        qcut.replace_wire_cut_nodes(g)
+        fragments, communication_graph = qcut.fragment_graph(g)
+        assert len(fragments) == 1
+        assert number_of_selfloops(communication_graph) == 0
+
 
 class TestGraphToTape:
     """Tests that directed multigraphs are correctly converted to tapes"""
@@ -2095,7 +2112,7 @@ class TestCutCircuitTransform:
         res = circuit(x)
         assert np.allclose(res, np.cos(x))
         assert len(spy.call_args[0][0]) == 1  # there should be 2 tensors for wire 0
-        assert spy.call_args[0][0][0].shape == (4, 4)
+        assert spy.call_args[0][0][0].shape == ()
 
 
 class TestCutCircuitTransformValidation: