From fc18d4926d9db74dac7ece4f434a282eb0d75f46 Mon Sep 17 00:00:00 2001 From: CalMacCQ <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 15 Nov 2023 15:52:54 +0000 Subject: [PATCH] Fix `RuntimeError` when converting qiskit instructions. (#203) * Don't use CustomGateDef in qiskit_to_tk * add test for RealAmplitudes handling * remove lines for debugging * remove a single line * fix assert for circuit naming * remove some unused imports * Add test of unitary equivalence * add ipynb to .gitignore * add isinstance check to keep mypy happy * add a check for equivalence with the qiskit operator * add comment with issue link * remove spaces --- .gitignore | 1 + pytket/extensions/qiskit/qiskit_convert.py | 19 ++++--------- tests/qiskit_convert_test.py | 33 ++++++++++++++++++++-- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 013424a3..225c97bf 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ obj docs/extensions .ipynb_checkpoints pytket/extensions/qiskit/_metadata.py +*.ipynb diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index 935e99e0..462857ee 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -154,7 +154,7 @@ # Special types: Barrier: OpType.Barrier, Instruction: OpType.CircBox, - Gate: OpType.CustomGate, + Gate: OpType.CircBox, Measure: OpType.Measure, Reset: OpType.Reset, Initialize: OpType.StatePreparationBox, @@ -476,7 +476,7 @@ def add_qiskit_data(self, data: "QuantumCircuitData") -> None: elif optype == OpType.Barrier: self.tkc.add_barrier(qubits) - elif optype in (OpType.CircBox, OpType.CustomGate): + elif optype == OpType.CircBox: qregs = ( [QuantumRegister(instr.num_qubits, "q")] if instr.num_qubits > 0 @@ -490,17 +490,9 @@ def add_qiskit_data(self, data: "QuantumCircuitData") -> None: builder = CircuitBuilder(qregs, cregs) builder.add_qiskit_data(instr.definition) subc = builder.circuit() - if optype == OpType.CircBox: - cbox = CircBox(subc) - self.tkc.add_circbox(cbox, qubits + bits, **condition_kwargs) # type: ignore - else: - # warning, this will catch all `Gate` instances - # that were not picked up as a subclass in _known_qiskit_gate - params = [param_to_tk(p) for p in instr.params] - gate_def = CustomGateDef.define( - instr.name, subc, list(subc.free_symbols()) - ) - self.tkc.add_custom_gate(gate_def, params, qubits + bits) # type: ignore + subc.name = instr.name + self.tkc.add_circbox(CircBox(subc), qubits + bits, **condition_kwargs) # type: ignore + elif optype == OpType.CU3 and type(instr) == qiskit_gates.CUGate: if instr.params[-1] == 0: self.tkc.add_gate( @@ -744,6 +736,7 @@ def append_tk_command_to_qiskit( _supported_tket_gates | _additional_multi_controlled_gates | {OpType.Unitary1qBox, OpType.Unitary2qBox, OpType.Unitary3qBox} + | {OpType.CustomGate} ) diff --git a/tests/qiskit_convert_test.py b/tests/qiskit_convert_test.py index 9ec5aa4f..1d31855e 100644 --- a/tests/qiskit_convert_test.py +++ b/tests/qiskit_convert_test.py @@ -24,14 +24,14 @@ ClassicalRegister, execute, ) -from qiskit.quantum_info import Pauli, SparsePauliOp # type: ignore +from qiskit.quantum_info import SparsePauliOp # type: ignore from qiskit.transpiler import PassManager # type: ignore -from qiskit.circuit.library import RYGate, MCMT, XXPlusYYGate, PauliEvolutionGate, UnitaryGate # type: ignore +from qiskit.circuit.library import RYGate, MCMT, XXPlusYYGate, PauliEvolutionGate, UnitaryGate, RealAmplitudes # type: ignore import qiskit.circuit.library.standard_gates as qiskit_gates # type: ignore from qiskit.circuit import Parameter from qiskit.synthesis import SuzukiTrotter # type: ignore from qiskit_aer import Aer # type: ignore -from qiskit.quantum_info import Statevector +from qiskit.quantum_info import Statevector, Operator from pytket.circuit import ( Circuit, @@ -1013,3 +1013,30 @@ def test_failed_conversion_error() -> None: NotImplementedError, match=r"Conversion of qiskit's xx_plus_yy instruction" ): qiskit_to_tk(qc) + + +# https://github.com/CQCL/pytket-qiskit/issues/200 +def test_RealAmplitudes_numeric_params() -> None: + qc = QuantumCircuit(3) + params = [np.pi / 2] * 9 + real_amps1 = RealAmplitudes(3, reps=2) + real_amps2 = real_amps1.assign_parameters(params) + qc.compose(real_amps2, qubits=[0, 1, 2], inplace=True) + # Unitary operator of the qiskit circuit. Order reversed from little -> big endian. + # The reversal means we can check it for equivalence with a tket unitary + qiskit_unitary = Operator(qc.reverse_bits()).data + converted_tkc = qiskit_to_tk(qc) + assert converted_tkc.n_gates == 1 + assert converted_tkc.n_gates_of_type(OpType.CircBox) == 1 + circbox_op = converted_tkc.get_commands()[0].op + assert isinstance(circbox_op, CircBox) + assert circbox_op.get_circuit().name == "RealAmplitudes" + DecomposeBoxes().apply(converted_tkc) + assert converted_tkc.n_gates_of_type(OpType.CX) == 4 + assert converted_tkc.n_gates_of_type(OpType.Ry) == 9 + unitary1 = converted_tkc.get_unitary() + qc2 = tk_to_qiskit(converted_tkc) + tkc2 = qiskit_to_tk(qc2) + unitary2 = tkc2.get_unitary() + assert compare_unitaries(qiskit_unitary, unitary1) + assert compare_unitaries(unitary1, unitary2)