Skip to content

Commit 8914a0b

Browse files
ShellyGarionElePT
authored andcommitted
Add some more gates to CollectCliffords pass (Qiskit#13214)
* add matrix_based option to CollectClifford pass Co-authored-by: Samantha Barron <samantha.v.barron@ibm.com> * add a test to collect matrix_based gates * add release notes * minor update following review
1 parent 529c45c commit 8914a0b

3 files changed

Lines changed: 115 additions & 11 deletions

File tree

qiskit/transpiler/passes/optimization/collect_cliffords.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
from functools import partial
1717

18+
from qiskit.exceptions import QiskitError
1819
from qiskit.transpiler.passes.optimization.collect_and_collapse import (
1920
CollectAndCollapse,
2021
collect_using_filter_function,
@@ -37,6 +38,7 @@ def __init__(
3738
min_block_size=2,
3839
split_layers=False,
3940
collect_from_back=False,
41+
matrix_based=False,
4042
):
4143
"""CollectCliffords initializer.
4244
@@ -51,11 +53,13 @@ def __init__(
5153
over disjoint qubit subsets.
5254
collect_from_back (bool): specifies if blocks should be collected started
5355
from the end of the circuit.
56+
matrix_based (bool): specifies whether to collect unitary gates
57+
which are Clifford gates only for certain parameters (based on their unitary matrix).
5458
"""
5559

5660
collect_function = partial(
5761
collect_using_filter_function,
58-
filter_function=_is_clifford_gate,
62+
filter_function=partial(_is_clifford_gate, matrix_based=matrix_based),
5963
split_blocks=split_blocks,
6064
min_block_size=min_block_size,
6165
split_layers=split_layers,
@@ -77,9 +81,21 @@ def __init__(
7781
)
7882

7983

80-
def _is_clifford_gate(node):
84+
def _is_clifford_gate(node, matrix_based=False):
8185
"""Specifies whether a node holds a clifford gate."""
82-
return node.op.name in clifford_gate_names and getattr(node.op, "condition", None) is None
86+
if getattr(node.op, "condition", None) is not None:
87+
return False
88+
if node.op.name in clifford_gate_names:
89+
return True
90+
91+
if not matrix_based:
92+
return False
93+
94+
try:
95+
Clifford(node.op)
96+
return True
97+
except QiskitError:
98+
return False
8399

84100

85101
def _collapse_to_clifford(circuit):
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
features_transpiler:
3+
- |
4+
Add an argument ``matrix_based`` to the :class:`.CollectCliffords()` transpiler pass.
5+
If the new parameter ``matrix_based=True``, the :class:`.CollectCliffords()` transpiler pass
6+
can collect :class:`.RZGate(np.pi/2)` gates and other unitary gates that are :class:`.Clifford()`
7+
gates for certain parameters.
8+

test/python/transpiler/test_clifford_passes.py

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,39 @@
2727
from qiskit.compiler.transpiler import transpile
2828
from test import QiskitTestCase # pylint: disable=wrong-import-order
2929

30+
from qiskit.circuit.library import (
31+
CPhaseGate,
32+
CRXGate,
33+
CRYGate,
34+
CRZGate,
35+
CXGate,
36+
CYGate,
37+
CZGate,
38+
DCXGate,
39+
ECRGate,
40+
HGate,
41+
IGate,
42+
iSwapGate,
43+
RXGate,
44+
RYGate,
45+
RZGate,
46+
RXXGate,
47+
RYYGate,
48+
RZZGate,
49+
RZXGate,
50+
SGate,
51+
SdgGate,
52+
SXGate,
53+
SXdgGate,
54+
SwapGate,
55+
UGate,
56+
XGate,
57+
XXMinusYYGate,
58+
XXPlusYYGate,
59+
YGate,
60+
ZGate,
61+
)
62+
3063

3164
class TestCliffordPasses(QiskitTestCase):
3265
"""Tests to verify correctness of transpiler passes and
@@ -381,15 +414,15 @@ def test_collect_cliffords_multiple_blocks(self):
381414
qc.cx(0, 1)
382415
qc.sdg(2)
383416
qc.swap(2, 1)
384-
qc.rx(np.pi / 2, 1)
417+
qc.t(1)
385418
qc.cz(0, 1)
386419
qc.z(0)
387420
qc.y(1)
388421

389422
# We should end up with two Cliffords and one "rx" gate
390423
qct = PassManager(CollectCliffords()).run(qc)
391424
self.assertEqual(qct.size(), 3)
392-
self.assertIn("rx", qct.count_ops().keys())
425+
self.assertIn("t", qct.count_ops().keys())
393426
self.assertEqual(qct.count_ops()["clifford"], 2)
394427

395428
self.assertIsInstance(qct.data[0].operation, Clifford)
@@ -479,9 +512,9 @@ def test_collect_cliffords_options_multiple_blocks(self):
479512
qc.x(2)
480513
qc.cx(2, 0)
481514

482-
qc.rx(np.pi / 2, 0)
483-
qc.rx(np.pi / 2, 1)
484-
qc.rx(np.pi / 2, 2)
515+
qc.rx(np.pi / 4, 0)
516+
qc.rx(np.pi / 4, 1)
517+
qc.rx(np.pi / 4, 2)
485518

486519
qc.cz(0, 1)
487520
qc.z(0)
@@ -524,7 +557,7 @@ def test_collect_from_back_corectness(self):
524557
qc.h(0)
525558
qc.x(1)
526559
qc.h(1)
527-
qc.rx(np.pi / 2, 0)
560+
qc.t(0)
528561
qc.y(0)
529562
qc.h(1)
530563

@@ -541,7 +574,7 @@ def test_collect_from_back_as_expected(self):
541574
qc.h(0)
542575
qc.x(1)
543576
qc.h(1)
544-
qc.rx(np.pi / 2, 0)
577+
qc.t(0)
545578
qc.y(0)
546579
qc.h(1)
547580

@@ -728,7 +761,7 @@ def test_collect_with_all_types(self):
728761
qc.cy(0, 1)
729762

730763
# not a clifford gate (separating the circuit)
731-
qc.rx(np.pi / 2, 0)
764+
qc.t(0)
732765

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

785+
def test_collect_all_clifford_gates(self):
786+
"""Assert that CollectClifford collects all basis gates
787+
(including certain rotation gates with pi/2 angles)"""
788+
gates_1q = [
789+
XGate(),
790+
YGate(),
791+
ZGate(),
792+
IGate(),
793+
HGate(),
794+
SGate(),
795+
SdgGate(),
796+
SXGate(),
797+
SXdgGate(),
798+
RXGate(theta=np.pi / 2),
799+
RYGate(theta=np.pi / 2),
800+
RZGate(phi=np.pi / 2),
801+
UGate(np.pi / 2, np.pi / 2, np.pi / 2),
802+
]
803+
gates_2q = [
804+
CXGate(),
805+
CYGate(),
806+
CZGate(),
807+
DCXGate(),
808+
ECRGate(),
809+
SwapGate(),
810+
iSwapGate(),
811+
CPhaseGate(theta=np.pi),
812+
CRXGate(theta=np.pi),
813+
CRYGate(theta=np.pi),
814+
CRZGate(theta=np.pi),
815+
RXXGate(theta=np.pi / 2),
816+
RYYGate(theta=np.pi / 2),
817+
RZZGate(theta=np.pi / 2),
818+
RZXGate(theta=np.pi / 2),
819+
XXMinusYYGate(theta=np.pi),
820+
XXPlusYYGate(theta=-np.pi),
821+
]
822+
823+
qc = QuantumCircuit(2)
824+
for gate in gates_1q:
825+
qc.append(gate, [0])
826+
for gate in gates_2q:
827+
qc.append(gate, [0, 1])
828+
829+
qct = PassManager(CollectCliffords(matrix_based=True)).run(qc)
830+
self.assertEqual(qct.count_ops()["clifford"], 1)
831+
752832

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

0 commit comments

Comments
 (0)