From 258b4b5f9020e8cf4bd8b7c67993775be8879a46 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 3 Mar 2023 01:09:54 -0500 Subject: [PATCH] Fix UnitarySynthesis with target that contains custom gates (#9677) * Fix UnitarySynthesis with target that contains custom gates Since #9175 merged the UnitarySynthesis pass has been updated to consider multiple different potential target bases and select the best performing synthesis output found. When UnitarySynthesis is provided a Target object this involves querying the target for the error rates and duration of the instructions used and determining which bases are valid. As part of this lookup the Operator for the 2q gates in the target is needed to determine the characteristics of the 2q gates. However, the code for doing this wasn't considering that some operations in the target might not have sufficient information to build a unitary representation of the operation. This could come up with the use of custom opaque gates in the target that don't have a definition defined or a matrix defined. This commit fixes this oversight and handles the case where building an Operator for the gate fails. Related to #9675 (the underlying issue is in the qiskit-ibm-runtime package for not mapping the "ecr" string to the "ECRGate" object, so this won't close that issue). * Fix lint * Only run Operator construction under try block The error we're trying to catch and handle is if a gate object doesn't have sufficient information to get it's matrix. This commit updates the try block to only run on the Operator construction so we don't potentially mas errors in the TwoQubitWeylDecomposition construction. --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../passes/synthesis/unitary_synthesis.py | 13 +++++++++-- .../transpiler/test_unitary_synthesis.py | 23 ++++++++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py index a9c850b4f11c..898998d08c63 100644 --- a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py @@ -47,6 +47,7 @@ ) from qiskit.providers.models import BackendProperties from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping +from qiskit.exceptions import QiskitError KAK_GATE_NAMES = { @@ -635,11 +636,19 @@ def _decomposer_2q_from_target(self, target, qubits, approximation_degree): decomposers = [] def is_supercontrolled(gate): - kak = TwoQubitWeylDecomposition(Operator(gate).data) + try: + operator = Operator(gate) + except QiskitError: + return False + kak = TwoQubitWeylDecomposition(operator.data) return isclose(kak.a, pi / 4) and isclose(kak.c, 0.0) def is_controlled(gate): - kak = TwoQubitWeylDecomposition(Operator(gate).data) + try: + operator = Operator(gate) + except QiskitError: + return False + kak = TwoQubitWeylDecomposition(operator.data) return isclose(kak.b, 0.0) and isclose(kak.c, 0.0) # possible supercontrolled decomposers (i.e. TwoQubitBasisDecomposer) diff --git a/test/python/transpiler/test_unitary_synthesis.py b/test/python/transpiler/test_unitary_synthesis.py index 9daefbe8c0c4..a51f7a420734 100644 --- a/test/python/transpiler/test_unitary_synthesis.py +++ b/test/python/transpiler/test_unitary_synthesis.py @@ -56,7 +56,7 @@ RXXGate, ) from qiskit.circuit.controlflow import IfElseOp -from qiskit.circuit import Parameter +from qiskit.circuit import Parameter, Gate @ddt @@ -836,6 +836,27 @@ def test_unitary_synthesis_with_ideal_and_variable_width_ops(self): result_qc = dag_to_circuit(result_dag) self.assertEqual(result_qc, QuantumCircuit(2)) + def test_unitary_synthesis_custom_gate_target(self): + qc = QuantumCircuit(2) + qc.unitary(np.eye(4), [0, 1]) + dag = circuit_to_dag(qc) + + class CustomGate(Gate): + """Custom Opaque Gate""" + + def __init__(self): + super().__init__("custom", 2, []) + + target = Target(num_qubits=2) + target.add_instruction( + UGate(Parameter("t"), Parameter("p"), Parameter("l")), {(0,): None, (1,): None} + ) + target.add_instruction(CustomGate(), {(0, 1): None, (1, 0): None}) + unitary_synth_pass = UnitarySynthesis(target=target) + result_dag = unitary_synth_pass.run(dag) + result_qc = dag_to_circuit(result_dag) + self.assertEqual(result_qc, QuantumCircuit(2)) + if __name__ == "__main__": unittest.main()