Skip to content

Commit

Permalink
Ignoring unsupported gates for Solovay-Kitaev transpiler pass (#13690)
Browse files Browse the repository at this point in the history
* 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 <gaconju@gmail.com>

---------

Co-authored-by: Julien Gacon <gaconju@gmail.com>
  • Loading branch information
alexanderivrii and Cryoris authored Feb 18, 2025
1 parent 2d389e0 commit 97ed168
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 27 deletions.
18 changes: 10 additions & 8 deletions qiskit/transpiler/passes/synthesis/solovay_kitaev_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
generate_basic_approximations,
)
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.passes.utils.control_flow import trivial_recurse

from .plugin import UnitarySynthesisPlugin

Expand Down Expand Up @@ -155,6 +155,7 @@ def __init__(
self.recursion_degree = recursion_degree
self._sk = SolovayKitaevDecomposition(basic_approximations)

@trivial_recurse
def run(self, dag: DAGCircuit) -> DAGCircuit:
"""Run the ``SolovayKitaev`` pass on `dag`.
Expand All @@ -168,18 +169,19 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:
TranspilerError: if a gates does not have to_matrix
"""
for node in dag.op_nodes():
if not node.op.num_qubits == 1:
continue # ignore all non-single qubit gates

# ignore operations on which the algorithm cannot run
if (
(node.op.num_qubits != 1)
or node.is_parameterized()
or (not hasattr(node.op, "to_matrix"))
):
continue

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

if not hasattr(node.op, "to_matrix"):
raise TranspilerError(
f"SolovayKitaev does not support gate without to_matrix method: {node.op.name}"
)

matrix = node.op.to_matrix()

# call solovay kitaev
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
upgrade_transpiler:
- |
The :class:`.SolovayKitaev` transpiler pass no longer raises an exception on circuits
that contain single-qubit operations without a ``to_matrix`` method (such as measures,
barriers, control-flow operations) or parameterized single-qubit operations,
but will leave them unchanged.
72 changes: 53 additions & 19 deletions test/python/transpiler/test_solovay_kitaev.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
from ddt import ddt, data

from qiskit import transpile
from qiskit.circuit import QuantumCircuit
from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.circuit.classicalregister import ClassicalRegister
from qiskit.circuit.library import TGate, TdgGate, HGate, SGate, SdgGate, IGate, QFT
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.converters import circuit_to_dag, dag_to_circuit
from qiskit.quantum_info import Operator
from qiskit.synthesis.discrete_basis.generate_basis_approximations import (
Expand All @@ -32,7 +34,6 @@
from qiskit.synthesis.discrete_basis.commutator_decompose import commutator_decompose
from qiskit.synthesis.discrete_basis.gate_sequence import GateSequence
from qiskit.transpiler import PassManager
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.passes import UnitarySynthesis, Collect1qRuns, ConsolidateBlocks
from qiskit.transpiler.passes.synthesis import SolovayKitaev, SolovayKitaevSynthesis
from test import QiskitTestCase # pylint: disable=wrong-import-order
Expand Down Expand Up @@ -152,23 +153,6 @@ def test_exact_decomposition_acts_trivially(self):
decomposed_circuit = dag_to_circuit(decomposed_dag)
self.assertEqual(circuit, decomposed_circuit)

def test_fails_with_no_to_matrix(self):
"""Test failer if gate does not have to_matrix."""
circuit = QuantumCircuit(1)
circuit.initialize("0")

synth = SolovayKitaev(3, self.basic_approx)

dag = circuit_to_dag(circuit)

with self.assertRaises(TranspilerError) as cm:
_ = synth.run(dag)

self.assertEqual(
"SolovayKitaev does not support gate without to_matrix method: initialize",
cm.exception.message,
)

def test_str_basis_gates(self):
"""Test specifying the basis gates by string works."""
circuit = QuantumCircuit(1)
Expand Down Expand Up @@ -261,6 +245,56 @@ def test_load_from_file(self):

self.assertEqual(discretized, reference)

def test_measure(self):
"""Test the Solovay-Kitaev transpiler pass on circuits with measure operators."""
qc = QuantumCircuit(1, 1)
qc.x(0)
qc.measure(0, 0)
transpiled = SolovayKitaev()(qc)
self.assertEqual(set(transpiled.count_ops()), {"h", "t", "measure"})

def test_barrier(self):
"""Test the Solovay-Kitaev transpiler pass on circuits with barriers."""
qc = QuantumCircuit(1)
qc.x(0)
qc.barrier(0)
transpiled = SolovayKitaev()(qc)
self.assertEqual(set(transpiled.count_ops()), {"h", "t", "barrier"})

def test_parameterized_gates(self):
"""Test the Solovay-Kitaev transpiler pass on circuits with parameterized gates."""
qc = QuantumCircuit(1)
qc.x(0)
qc.rz(Parameter("t"), 0)
transpiled = SolovayKitaev()(qc)
self.assertEqual(set(transpiled.count_ops()), {"h", "t", "rz"})

def test_control_flow_if(self):
"""Test the Solovay-Kitaev transpiler pass on circuits with control flow ops"""
qr = QuantumRegister(1)
cr = ClassicalRegister(1)
qc = QuantumCircuit(qr, cr)

with qc.if_test((cr[0], 0)) as else_:
qc.y(0)
with else_:
qc.z(0)
transpiled = SolovayKitaev()(qc)

# check that we still have an if-else block and all the operations within
# have been recursively synthesized
self.assertEqual(transpiled[0].name, "if_else")
for block in transpiled[0].operation.blocks:
self.assertLessEqual(set(block.count_ops()), {"h", "t", "tdg"})

def test_no_to_matrix(self):
"""Test the Solovay-Kitaev transpiler pass ignores gates without to_matrix."""
qc = QuantumCircuit(1)
qc.initialize("0")

transpiled = SolovayKitaev()(qc)
self.assertEqual(set(transpiled.count_ops()), {"initialize"})


@ddt
class TestGateSequence(QiskitTestCase):
Expand Down

0 comments on commit 97ed168

Please # to comment.