Repository: PennyLaneAI/pennylane
Pull request #7229 (2025-04-08 09:53:36)

[WIP] [Labs] parity matrix intermediate representation
[Parity matrix IR on compilation wiki](https://pennylane.ai/compilation/parity-matrix-intermediate-representation)
[notion doc on parity matrix prototype](https://www.notion.so/xanaduai/25-Q1-Intermediate-Representation-zoo-177bc6bd176481d6b67ad36143e61e84?pvs=4#177bc6bd1764808dacc5d0caf87880f3)
[notion doc on parity table prototype](https://www.notion.so/xanaduai/25-Q1-Intermediate-Representation-zoo-177bc6bd176481d6b67ad36143e61e84?pvs=4#177bc6bd176480c0a9d5ce8c4ef01a26)

TODO

- [x] basic test cases
- [x] docs
- [x] changelog
- [x] validation of input circuit
- [x] wire handling
- [x] wire handling tests
- [ ] 

Create parity matrix IR prototype [sc-88506]

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/7229?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 99.64%. Comparing base [(`31647e3`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/31647e335769691dc1b9cea356a0492d5027e6f5?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) to head [(`bc09fb7`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/bc09fb767626b58330b87824dd97844b0d567b46?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> Report is 1 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master    #7229   +/-   ##
=======================================
  Coverage   99.64%   99.64%           
=======================================
  Files         514      514           
  Lines       50088    50088           
=======================================
  Hits        49912    49912           
  Misses        176      176           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/7229?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>

Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 1df4c4769..8a3042272 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -186,6 +186,15 @@
 * Updated GitHub Actions workflows (`rtd.yml`, `readthedocs.yml`, and `docs.yml`) to use `ubuntu-24.04` runners.
  [(#7396)](https://github.com/PennyLaneAI/pennylane/pull/7396)
 
+
+<h3>Labs: a place for unified and rapid prototyping of research software 🧪</h3>
+
+* A :func:`parity_matrix <pennylane.labs.intermediate_reps.parity_matrix>` function is now available
+  in :mod:`pennylane.labs.intermediate_reps <pennylane.labs.intermediate_reps>`.
+  It allows computation of the parity matrix of a CNOT circuit; an efficient intermediate representation.
+  It is important for CNOT routing algorithms and other quantum compilation routines.
+  [(#7229)](https://github.com/PennyLaneAI/pennylane/pull/7229)
+
 <h3>Breaking changes 💔</h3>
 
 * The `return_type` property of `MeasurementProcess` has been removed. Please use `isinstance` for type checking instead.
diff --git a/pennylane/labs/__init__.py b/pennylane/labs/__init__.py
index 1ef3a9e89..8ea831903 100644
--- a/pennylane/labs/__init__.py
+++ b/pennylane/labs/__init__.py
@@ -28,6 +28,7 @@ Modules
     dla
     resource_estimation
     trotter_error
+    intermediate_reps
 
 """
 
@@ -35,5 +36,6 @@ from pennylane.labs import dla
 from pennylane.labs import resource_estimation
 from pennylane.labs import vibrational
 from pennylane.labs import trotter_error
+from pennylane.labs import intermediate_reps
 
 __all__ = []
diff --git a/pennylane/labs/intermediate_reps/__init__.py b/pennylane/labs/intermediate_reps/__init__.py
new file mode 100644
index 000000000..e7042ac31
--- /dev/null
+++ b/pennylane/labs/intermediate_reps/__init__.py
@@ -0,0 +1,30 @@
+# Copyright 2025 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.
+r"""
+Quantum circuit intermediate representations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Intermediate representations (IRs) are alternative representations of quantum circuits, typically offering a more efficient classical description for special classes of circuits.
+
+.. currentmodule:: pennylane.labs.intermediate_reps
+
+.. autosummary::
+    :toctree: api
+
+    ~parity_matrix
+
+
+"""
+
+from .parity_matrix import parity_matrix
diff --git a/pennylane/labs/intermediate_reps/parity_matrix.py b/pennylane/labs/intermediate_reps/parity_matrix.py
new file mode 100644
index 000000000..26ef4694c
--- /dev/null
+++ b/pennylane/labs/intermediate_reps/parity_matrix.py
@@ -0,0 +1,90 @@
+# Copyright 2025 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.
+"""Parity matrix representation"""
+
+from typing import Sequence
+
+import numpy as np
+
+import pennylane as qml
+
+
+def parity_matrix(circ: qml.tape.QuantumScript, wire_order: Sequence = None):
+    r"""Compute the `parity matrix intermediate representation <https://pennylane.ai/compilation/parity-matrix-intermediate-representation>`__ of a CNOT circuit.
+
+    Args:
+        circ (qml.tape.QuantumScript): Quantum circuit containing only CNOT gates.
+        wire_order (Sequence): ``wire_order`` indicating how rows and columns should be ordered. If ``None`` is provided, we take the wires of the input circuit (``circ.wires``).
+
+    Returns:
+        np.ndarray: :math:`n \times n` Parity matrix for :math:`n` qubits.
+
+    **Example**
+
+    .. code-block:: python
+
+        import pennylane as qml
+        from pennylane.labs.intermediate_reps import parity_matrix
+
+        circ = qml.tape.QuantumScript([
+            qml.CNOT((3, 2)),
+            qml.CNOT((0, 2)),
+            qml.CNOT((2, 1)),
+            qml.CNOT((3, 2)),
+            qml.CNOT((3, 0)),
+            qml.CNOT((0, 2)),
+        ], [])
+
+        P = parity_matrix(circ, wire_order=range(4))
+
+    >>> P
+    array([[1., 0., 0., 1.],
+           [1., 1., 1., 1.],
+           [0., 0., 1., 1.],
+           [0., 0., 0., 1.]])
+
+    The corresponding circuit is the following, with output values of the qubits denoted at the right end.
+
+    .. code-block::
+
+        x_0: ────╭●───────╭X─╭●─┤  x_0 ⊕ x_3
+        x_1: ────│──╭X────│──│──┤  x_0 ⊕ x_1 ⊕ x_2 ⊕ x_3
+        x_2: ─╭X─╰X─╰●─╭X─│──╰X─┤  x_2 ⊕ x_3
+        x_3: ─╰●───────╰●─╰●────┤  x_3
+
+    """
+    wires = circ.wires
+
+    if wire_order is None:
+        wire_order = wires
+
+    if not qml.wires.Wires(wire_order).contains_wires(wires):
+        raise qml.wires.WireError(
+            f"The provided wire_order {wire_order} does not contain all wires of the circuit {wires}"
+        )
+
+    if any(op.name != "CNOT" for op in circ.operations):
+        raise TypeError(
+            f"parity_matrix requires all input circuits to consist solely of CNOT gates. Received circuit with the following gates: {circ.operations}"
+        )
+
+    wire_map = {wire: idx for idx, wire in enumerate(wire_order)}
+
+    P = np.eye(len(wire_order), dtype=int)
+    for op in circ.operations:
+
+        control, target = op.wires
+        P[wire_map[target]] += P[wire_map[control]]
+
+    return P % 2


Test patch:
diff --git a/pennylane/labs/tests/intermediate_reps/test_parity_matrix.py b/pennylane/labs/tests/intermediate_reps/test_parity_matrix.py
new file mode 100644
index 000000000..4670c8d1e
--- /dev/null
+++ b/pennylane/labs/tests/intermediate_reps/test_parity_matrix.py
@@ -0,0 +1,78 @@
+# Copyright 2025 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.
+"""Tests for pennylane/labs/phase_polynomials/parity_matrix.py"""
+# pylint: disable = no-self-use
+from itertools import permutations
+
+import numpy as np
+import pytest
+
+import pennylane as qml
+from pennylane.labs.intermediate_reps import parity_matrix
+
+circ1 = qml.tape.QuantumScript(
+    [
+        qml.CNOT((3, 2)),
+        qml.CNOT((0, 2)),
+        qml.CNOT((2, 1)),
+        qml.CNOT((3, 2)),
+        qml.CNOT((3, 0)),
+        qml.CNOT((0, 2)),
+    ],
+)
+wire_order_abcd = ["a", "b", "c", "d"]
+(circ1_letters,), _ = qml.map_wires(circ1, dict(enumerate(wire_order_abcd)))
+
+P1 = np.array([[1, 0, 0, 1], [1, 1, 1, 1], [0, 0, 1, 1], [0, 0, 0, 1]])
+
+circ2 = qml.tape.QuantumScript(
+    [qml.SWAP((0, 1)), qml.SWAP((1, 2)), qml.SWAP((2, 3))], []
+).expand()  # expand into CNOTs
+P2 = np.array([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [1, 0, 0, 0]])
+
+
+class TestParityMatrix:
+    """Tests for parity_matrix function"""
+
+    @pytest.mark.parametrize("circ, P_true", ((circ1, P1), (circ2, P2)))
+    def test_parity_matrix(self, circ, P_true):
+        """Test parity matrix computation"""
+        P = parity_matrix(circ, wire_order=range(len(circ.wires)))
+
+        assert np.allclose(P, P_true)
+
+    def test_parity_matrix_with_string_wires(self):
+        """Test parity matrix computation with string valued wires"""
+        wire_order1 = ["a", "b", "c", "d"]
+        P_re = parity_matrix(circ1_letters, wire_order=wire_order1)
+
+        assert np.allclose(P_re, P1)
+
+    @pytest.mark.parametrize("wire_order", list(permutations(range(4), 4)))
+    def test_parity_matrix_wire_order(self, wire_order):
+        """Test wire_order works as expected"""
+        P1_re = parity_matrix(circ1, wire_order=wire_order)
+        assert np.allclose(P1_re[np.argsort(wire_order)][:, np.argsort(wire_order)], P1)
+
+    def test_WireError(self):
+        """Test that WireError is raised when wires in the provided wire_order dont match the circuit wires"""
+        with pytest.raises(qml.wires.WireError, match="The provided wire_order"):
+            _ = parity_matrix(circ1, wire_order=[1, 2, 3, 4])
+
+    def test_input_validation(self):
+        """Test that input circuits are correctly validated"""
+        circ = qml.tape.QuantumScript([qml.CNOT((0, 2)), qml.RZ(0.5, 0)])
+
+        with pytest.raises(TypeError, match="parity_matrix requires all input circuits"):
+            _ = parity_matrix(circ)