Skip to content

Commit

Permalink
Add some more gates to CollectCliffords pass (Qiskit#13214)
Browse files Browse the repository at this point in the history
* add matrix_based option to CollectClifford pass
Co-authored-by: Samantha Barron <[email protected]>

* add a test to collect matrix_based gates

* add release notes

* minor update following review
  • Loading branch information
ShellyGarion authored and ElePT committed Oct 9, 2024
1 parent 529c45c commit 8914a0b
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 11 deletions.
22 changes: 19 additions & 3 deletions qiskit/transpiler/passes/optimization/collect_cliffords.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from functools import partial

from qiskit.exceptions import QiskitError
from qiskit.transpiler.passes.optimization.collect_and_collapse import (
CollectAndCollapse,
collect_using_filter_function,
Expand All @@ -37,6 +38,7 @@ def __init__(
min_block_size=2,
split_layers=False,
collect_from_back=False,
matrix_based=False,
):
"""CollectCliffords initializer.
Expand All @@ -51,11 +53,13 @@ def __init__(
over disjoint qubit subsets.
collect_from_back (bool): specifies if blocks should be collected started
from the end of the circuit.
matrix_based (bool): specifies whether to collect unitary gates
which are Clifford gates only for certain parameters (based on their unitary matrix).
"""

collect_function = partial(
collect_using_filter_function,
filter_function=_is_clifford_gate,
filter_function=partial(_is_clifford_gate, matrix_based=matrix_based),
split_blocks=split_blocks,
min_block_size=min_block_size,
split_layers=split_layers,
Expand All @@ -77,9 +81,21 @@ def __init__(
)


def _is_clifford_gate(node):
def _is_clifford_gate(node, matrix_based=False):
"""Specifies whether a node holds a clifford gate."""
return node.op.name in clifford_gate_names and getattr(node.op, "condition", None) is None
if getattr(node.op, "condition", None) is not None:
return False
if node.op.name in clifford_gate_names:
return True

if not matrix_based:
return False

try:
Clifford(node.op)
return True
except QiskitError:
return False


def _collapse_to_clifford(circuit):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
features_transpiler:
- |
Add an argument ``matrix_based`` to the :class:`.CollectCliffords()` transpiler pass.
If the new parameter ``matrix_based=True``, the :class:`.CollectCliffords()` transpiler pass
can collect :class:`.RZGate(np.pi/2)` gates and other unitary gates that are :class:`.Clifford()`
gates for certain parameters.
96 changes: 88 additions & 8 deletions test/python/transpiler/test_clifford_passes.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,39 @@
from qiskit.compiler.transpiler import transpile
from test import QiskitTestCase # pylint: disable=wrong-import-order

from qiskit.circuit.library import (
CPhaseGate,
CRXGate,
CRYGate,
CRZGate,
CXGate,
CYGate,
CZGate,
DCXGate,
ECRGate,
HGate,
IGate,
iSwapGate,
RXGate,
RYGate,
RZGate,
RXXGate,
RYYGate,
RZZGate,
RZXGate,
SGate,
SdgGate,
SXGate,
SXdgGate,
SwapGate,
UGate,
XGate,
XXMinusYYGate,
XXPlusYYGate,
YGate,
ZGate,
)


class TestCliffordPasses(QiskitTestCase):
"""Tests to verify correctness of transpiler passes and
Expand Down Expand Up @@ -381,15 +414,15 @@ def test_collect_cliffords_multiple_blocks(self):
qc.cx(0, 1)
qc.sdg(2)
qc.swap(2, 1)
qc.rx(np.pi / 2, 1)
qc.t(1)
qc.cz(0, 1)
qc.z(0)
qc.y(1)

# We should end up with two Cliffords and one "rx" gate
qct = PassManager(CollectCliffords()).run(qc)
self.assertEqual(qct.size(), 3)
self.assertIn("rx", qct.count_ops().keys())
self.assertIn("t", qct.count_ops().keys())
self.assertEqual(qct.count_ops()["clifford"], 2)

self.assertIsInstance(qct.data[0].operation, Clifford)
Expand Down Expand Up @@ -479,9 +512,9 @@ def test_collect_cliffords_options_multiple_blocks(self):
qc.x(2)
qc.cx(2, 0)

qc.rx(np.pi / 2, 0)
qc.rx(np.pi / 2, 1)
qc.rx(np.pi / 2, 2)
qc.rx(np.pi / 4, 0)
qc.rx(np.pi / 4, 1)
qc.rx(np.pi / 4, 2)

qc.cz(0, 1)
qc.z(0)
Expand Down Expand Up @@ -524,7 +557,7 @@ def test_collect_from_back_corectness(self):
qc.h(0)
qc.x(1)
qc.h(1)
qc.rx(np.pi / 2, 0)
qc.t(0)
qc.y(0)
qc.h(1)

Expand All @@ -541,7 +574,7 @@ def test_collect_from_back_as_expected(self):
qc.h(0)
qc.x(1)
qc.h(1)
qc.rx(np.pi / 2, 0)
qc.t(0)
qc.y(0)
qc.h(1)

Expand Down Expand Up @@ -728,7 +761,7 @@ def test_collect_with_all_types(self):
qc.cy(0, 1)

# not a clifford gate (separating the circuit)
qc.rx(np.pi / 2, 0)
qc.t(0)

qc.append(pauli_gate2, [0, 2, 1])
qc.append(lf2, [2, 1, 0])
Expand All @@ -749,6 +782,53 @@ def test_collect_with_all_types(self):
op2 = Operator(qct)
self.assertTrue(op1.equiv(op2))

def test_collect_all_clifford_gates(self):
"""Assert that CollectClifford collects all basis gates
(including certain rotation gates with pi/2 angles)"""
gates_1q = [
XGate(),
YGate(),
ZGate(),
IGate(),
HGate(),
SGate(),
SdgGate(),
SXGate(),
SXdgGate(),
RXGate(theta=np.pi / 2),
RYGate(theta=np.pi / 2),
RZGate(phi=np.pi / 2),
UGate(np.pi / 2, np.pi / 2, np.pi / 2),
]
gates_2q = [
CXGate(),
CYGate(),
CZGate(),
DCXGate(),
ECRGate(),
SwapGate(),
iSwapGate(),
CPhaseGate(theta=np.pi),
CRXGate(theta=np.pi),
CRYGate(theta=np.pi),
CRZGate(theta=np.pi),
RXXGate(theta=np.pi / 2),
RYYGate(theta=np.pi / 2),
RZZGate(theta=np.pi / 2),
RZXGate(theta=np.pi / 2),
XXMinusYYGate(theta=np.pi),
XXPlusYYGate(theta=-np.pi),
]

qc = QuantumCircuit(2)
for gate in gates_1q:
qc.append(gate, [0])
for gate in gates_2q:
qc.append(gate, [0, 1])

qct = PassManager(CollectCliffords(matrix_based=True)).run(qc)
self.assertEqual(qct.count_ops()["clifford"], 1)


if __name__ == "__main__":
unittest.main()

0 comments on commit 8914a0b

Please sign in to comment.