Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some more gates to CollectCliffords pass #13214

Merged
merged 5 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
Loading