Skip to content

Commit 97ed168

Browse files
Ignoring unsupported gates for Solovay-Kitaev transpiler pass (Qiskit#13690)
* ignoring unsupported gates for SK * minor * making SK pass recurse over control-flow operations * Update releasenotes/notes/sk-ignore-unsupported-ops-8d7d5f6fca255ffb.yaml Co-authored-by: Julien Gacon <[email protected]> --------- Co-authored-by: Julien Gacon <[email protected]>
1 parent 2d389e0 commit 97ed168

File tree

3 files changed

+70
-27
lines changed

3 files changed

+70
-27
lines changed

qiskit/transpiler/passes/synthesis/solovay_kitaev_synthesis.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
generate_basic_approximations,
3434
)
3535
from qiskit.transpiler.basepasses import TransformationPass
36-
from qiskit.transpiler.exceptions import TranspilerError
36+
from qiskit.transpiler.passes.utils.control_flow import trivial_recurse
3737

3838
from .plugin import UnitarySynthesisPlugin
3939

@@ -155,6 +155,7 @@ def __init__(
155155
self.recursion_degree = recursion_degree
156156
self._sk = SolovayKitaevDecomposition(basic_approximations)
157157

158+
@trivial_recurse
158159
def run(self, dag: DAGCircuit) -> DAGCircuit:
159160
"""Run the ``SolovayKitaev`` pass on `dag`.
160161
@@ -168,18 +169,19 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:
168169
TranspilerError: if a gates does not have to_matrix
169170
"""
170171
for node in dag.op_nodes():
171-
if not node.op.num_qubits == 1:
172-
continue # ignore all non-single qubit gates
172+
173+
# ignore operations on which the algorithm cannot run
174+
if (
175+
(node.op.num_qubits != 1)
176+
or node.is_parameterized()
177+
or (not hasattr(node.op, "to_matrix"))
178+
):
179+
continue
173180

174181
# we do not check the input matrix as we know it comes from a Qiskit gate, as this
175182
# we know it will generate a valid SU(2) matrix
176183
check_input = not isinstance(node.op, Gate)
177184

178-
if not hasattr(node.op, "to_matrix"):
179-
raise TranspilerError(
180-
f"SolovayKitaev does not support gate without to_matrix method: {node.op.name}"
181-
)
182-
183185
matrix = node.op.to_matrix()
184186

185187
# call solovay kitaev
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
upgrade_transpiler:
3+
- |
4+
The :class:`.SolovayKitaev` transpiler pass no longer raises an exception on circuits
5+
that contain single-qubit operations without a ``to_matrix`` method (such as measures,
6+
barriers, control-flow operations) or parameterized single-qubit operations,
7+
but will leave them unchanged.

test/python/transpiler/test_solovay_kitaev.py

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
from ddt import ddt, data
2323

2424
from qiskit import transpile
25-
from qiskit.circuit import QuantumCircuit
25+
from qiskit.circuit import QuantumCircuit, Parameter
26+
from qiskit.circuit.classicalregister import ClassicalRegister
2627
from qiskit.circuit.library import TGate, TdgGate, HGate, SGate, SdgGate, IGate, QFT
28+
from qiskit.circuit.quantumregister import QuantumRegister
2729
from qiskit.converters import circuit_to_dag, dag_to_circuit
2830
from qiskit.quantum_info import Operator
2931
from qiskit.synthesis.discrete_basis.generate_basis_approximations import (
@@ -32,7 +34,6 @@
3234
from qiskit.synthesis.discrete_basis.commutator_decompose import commutator_decompose
3335
from qiskit.synthesis.discrete_basis.gate_sequence import GateSequence
3436
from qiskit.transpiler import PassManager
35-
from qiskit.transpiler.exceptions import TranspilerError
3637
from qiskit.transpiler.passes import UnitarySynthesis, Collect1qRuns, ConsolidateBlocks
3738
from qiskit.transpiler.passes.synthesis import SolovayKitaev, SolovayKitaevSynthesis
3839
from test import QiskitTestCase # pylint: disable=wrong-import-order
@@ -152,23 +153,6 @@ def test_exact_decomposition_acts_trivially(self):
152153
decomposed_circuit = dag_to_circuit(decomposed_dag)
153154
self.assertEqual(circuit, decomposed_circuit)
154155

155-
def test_fails_with_no_to_matrix(self):
156-
"""Test failer if gate does not have to_matrix."""
157-
circuit = QuantumCircuit(1)
158-
circuit.initialize("0")
159-
160-
synth = SolovayKitaev(3, self.basic_approx)
161-
162-
dag = circuit_to_dag(circuit)
163-
164-
with self.assertRaises(TranspilerError) as cm:
165-
_ = synth.run(dag)
166-
167-
self.assertEqual(
168-
"SolovayKitaev does not support gate without to_matrix method: initialize",
169-
cm.exception.message,
170-
)
171-
172156
def test_str_basis_gates(self):
173157
"""Test specifying the basis gates by string works."""
174158
circuit = QuantumCircuit(1)
@@ -261,6 +245,56 @@ def test_load_from_file(self):
261245

262246
self.assertEqual(discretized, reference)
263247

248+
def test_measure(self):
249+
"""Test the Solovay-Kitaev transpiler pass on circuits with measure operators."""
250+
qc = QuantumCircuit(1, 1)
251+
qc.x(0)
252+
qc.measure(0, 0)
253+
transpiled = SolovayKitaev()(qc)
254+
self.assertEqual(set(transpiled.count_ops()), {"h", "t", "measure"})
255+
256+
def test_barrier(self):
257+
"""Test the Solovay-Kitaev transpiler pass on circuits with barriers."""
258+
qc = QuantumCircuit(1)
259+
qc.x(0)
260+
qc.barrier(0)
261+
transpiled = SolovayKitaev()(qc)
262+
self.assertEqual(set(transpiled.count_ops()), {"h", "t", "barrier"})
263+
264+
def test_parameterized_gates(self):
265+
"""Test the Solovay-Kitaev transpiler pass on circuits with parameterized gates."""
266+
qc = QuantumCircuit(1)
267+
qc.x(0)
268+
qc.rz(Parameter("t"), 0)
269+
transpiled = SolovayKitaev()(qc)
270+
self.assertEqual(set(transpiled.count_ops()), {"h", "t", "rz"})
271+
272+
def test_control_flow_if(self):
273+
"""Test the Solovay-Kitaev transpiler pass on circuits with control flow ops"""
274+
qr = QuantumRegister(1)
275+
cr = ClassicalRegister(1)
276+
qc = QuantumCircuit(qr, cr)
277+
278+
with qc.if_test((cr[0], 0)) as else_:
279+
qc.y(0)
280+
with else_:
281+
qc.z(0)
282+
transpiled = SolovayKitaev()(qc)
283+
284+
# check that we still have an if-else block and all the operations within
285+
# have been recursively synthesized
286+
self.assertEqual(transpiled[0].name, "if_else")
287+
for block in transpiled[0].operation.blocks:
288+
self.assertLessEqual(set(block.count_ops()), {"h", "t", "tdg"})
289+
290+
def test_no_to_matrix(self):
291+
"""Test the Solovay-Kitaev transpiler pass ignores gates without to_matrix."""
292+
qc = QuantumCircuit(1)
293+
qc.initialize("0")
294+
295+
transpiled = SolovayKitaev()(qc)
296+
self.assertEqual(set(transpiled.count_ops()), {"initialize"})
297+
264298

265299
@ddt
266300
class TestGateSequence(QiskitTestCase):

0 commit comments

Comments
 (0)