From 817f3e0b47b99e795fe3544e25cb3e8bd7a5ea5e Mon Sep 17 00:00:00 2001 From: cqc-melf <70640934+cqc-melf@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:18:03 +0000 Subject: [PATCH] fix issue with conditional and symbolic parameter in conversion (#245) * fix conditional and symbolic parameter in conversion * remove linebreak from error message * use numpy.pi * remove qasm from error message --- docs/changelog.rst | 3 +- pytket/extensions/qiskit/qiskit_convert.py | 55 ++++++++++++----- tests/qiskit_backend_test.py | 2 - tests/qiskit_convert_test.py | 69 ++++++++++++++++++---- 4 files changed, 98 insertions(+), 31 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 7a983cd5..49cf8edc 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -8,7 +8,8 @@ General: * Python 3.12 support added, 3.9 dropped. * pytket dependency updated to 1.24. - +* fix conditional bit in pytket to qiskit conversion +* fix symbolic conversion of parameter in conversion 0.47.0 (January 2024) --------------------- diff --git a/pytket/extensions/qiskit/qiskit_convert.py b/pytket/extensions/qiskit/qiskit_convert.py index c0a695f7..7d04ba63 100644 --- a/pytket/extensions/qiskit/qiskit_convert.py +++ b/pytket/extensions/qiskit/qiskit_convert.py @@ -52,6 +52,7 @@ Parameter, ParameterExpression, Reset, + Clbit, ) from qiskit.circuit.library import ( CRYGate, @@ -342,11 +343,23 @@ def add_qiskit_data(self, data: "QuantumCircuitData") -> None: for instr, qargs, cargs in data: condition_kwargs = {} if instr.condition is not None: - cond_reg = self.cregmap[instr.condition[0]] - condition_kwargs = { - "condition_bits": [cond_reg[k] for k in range(len(cond_reg))], - "condition_value": instr.condition[1], - } + if type(instr.condition[0]) == ClassicalRegister: + cond_reg = self.cregmap[instr.condition[0]] + condition_kwargs = { + "condition_bits": [cond_reg[k] for k in range(len(cond_reg))], + "condition_value": instr.condition[1], + } + elif type(instr.condition[0]) == Clbit: + cond_reg = self.cregmap[instr.condition[0].register] + condition_kwargs = { + "condition_bits": [cond_reg[instr.condition[0].index]], + "condition_value": instr.condition[1], + } + else: + raise NotImplementedError( + "condition must contain classical bit or register" + ) + # Controlled operations may be controlled on values other than all-1. Handle # this by prepending and appending X gates on the control qubits. ctrl_state, num_ctrl_qubits = None, None @@ -653,22 +666,29 @@ def append_tk_command_to_qiskit( width = op.width # type: ignore value = op.value # type: ignore regname = args[0].reg_name - if len(cregmap[regname]) != width: - raise NotImplementedError("OpenQASM conditions must be an entire register") for i, a in enumerate(args[:width]): if a.reg_name != regname: - raise NotImplementedError( - "OpenQASM conditions can only use a single register" - ) - if a.index != [i]: - raise NotImplementedError( - "OpenQASM conditions must be an entire register in order" - ) + raise NotImplementedError("Conditions can only use a single register") instruction = append_tk_command_to_qiskit( op.op, args[width:], qcirc, qregmap, cregmap, symb_map, range_preds # type: ignore ) + if len(cregmap[regname]) == width: + for i, a in enumerate(args[:width]): + if a.index != [i]: + raise NotImplementedError( + """Conditions must be an entire register in\ + order or only one bit of one register""" + ) + + instruction.c_if(cregmap[regname], value) + elif width == 1: + instruction.c_if(cregmap[regname][args[0].index[0]], value) + else: + raise NotImplementedError( + """Conditions must be an entire register in\ +order or only one bit of one register""" + ) - instruction.c_if(cregmap[regname], value) return instruction # normal gates qargs = [qregmap[q.reg_name][q.index[0]] for q in args] @@ -719,7 +739,10 @@ def append_tk_command_to_qiskit( ) from error params = _get_params(op, symb_map) g = gatetype(*params) - qcirc.global_phase += phase * sympy.pi + if type(phase) == float: + qcirc.global_phase += phase * np.pi + else: + qcirc.global_phase += phase * sympy.pi return qcirc.append(g, qargs=qargs) diff --git a/tests/qiskit_backend_test.py b/tests/qiskit_backend_test.py index c275784a..26574934 100644 --- a/tests/qiskit_backend_test.py +++ b/tests/qiskit_backend_test.py @@ -22,8 +22,6 @@ from qiskit.primitives import BackendSampler # type: ignore from qiskit.providers import JobStatus # type: ignore from qiskit_algorithms import Grover, AmplificationProblem, AlgorithmError # type: ignore -from qiskit.transpiler import PassManager # type: ignore -from qiskit.transpiler.passes import Unroller # type: ignore from qiskit_aer import Aer # type: ignore from qiskit_ibm_provider import IBMProvider # type: ignore diff --git a/tests/qiskit_convert_test.py b/tests/qiskit_convert_test.py index 9d92d8ec..16c941fa 100644 --- a/tests/qiskit_convert_test.py +++ b/tests/qiskit_convert_test.py @@ -24,14 +24,17 @@ ClassicalRegister, execute, ) -from qiskit.quantum_info import SparsePauliOp # type: ignore +from qiskit.quantum_info import SparsePauliOp, Statevector, Operator # type: ignore from qiskit.transpiler import PassManager # 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, Operator +from qiskit.transpiler.passes import BasisTranslator # type: ignore +from qiskit.circuit.equivalence_library import StandardEquivalenceLibrary # type: ignore +from qiskit.providers.fake_provider import FakeGuadalupe # type: ignore +from qiskit.circuit.parameterexpression import ParameterExpression # type: ignore from pytket.circuit import ( Circuit, @@ -50,7 +53,14 @@ from pytket.extensions.qiskit.qiskit_convert import _gate_str_2_optype from pytket.extensions.qiskit.tket_pass import TketPass, TketAutoPass from pytket.extensions.qiskit.result_convert import qiskit_result_to_backendresult -from pytket.passes import RebaseTket, DecomposeBoxes, FullPeepholeOptimise, SequencePass +from pytket.passes import ( + RebaseTket, + DecomposeBoxes, + FullPeepholeOptimise, + SequencePass, + CliffordSimp, +) + from pytket.utils.results import ( compare_statevectors, permute_rows_cols_in_unitary, @@ -72,6 +82,28 @@ def _get_qiskit_statevector(qc: QuantumCircuit) -> np.ndarray: return np.array(job.result().data()["statevector"].reverse_qargs().data) +def test_parameterised_circuit_global_phase() -> None: + pass_1 = BasisTranslator( + StandardEquivalenceLibrary, + target_basis=FakeGuadalupe().configuration().basis_gates, + ) + pass_2 = CliffordSimp() + + qc = QuantumCircuit(2) + qc.ryy(Parameter("MyParam"), 0, 1) + + pm = PassManager(pass_1) + qc = pm.run(qc) + + tket_qc = qiskit_to_tk(qc) + + pass_2.apply(tket_qc) + + qc_2 = tk_to_qiskit(tket_qc) + + assert type(qc_2.global_phase) == ParameterExpression + + def test_classical_barrier_error() -> None: c = Circuit(1, 1) c.add_barrier([0], [0]) @@ -398,24 +430,17 @@ def test_conditions() -> None: def test_condition_errors() -> None: - with pytest.raises(Exception) as errorinfo: - c = Circuit(2, 2) - c.X(0, condition_bits=[0], condition_value=1) - tk_to_qiskit(c) - assert "OpenQASM conditions must be an entire register" in str(errorinfo.value) with pytest.raises(Exception) as errorinfo: c = Circuit(2, 2) b = c.add_c_register("b", 2) c.X(Qubit(0), condition_bits=[b[0], Bit(0)], condition_value=1) tk_to_qiskit(c) - assert "OpenQASM conditions can only use a single register" in str(errorinfo.value) + assert "Conditions can only use a single register" in str(errorinfo.value) with pytest.raises(Exception) as errorinfo: c = Circuit(2, 2) c.X(0, condition_bits=[1, 0], condition_value=1) tk_to_qiskit(c) - assert "OpenQASM conditions must be an entire register in order" in str( - errorinfo.value - ) + assert "Conditions must be an entire register in order" in str(errorinfo.value) def test_correction() -> None: @@ -857,6 +882,26 @@ def test_ccx_conversion() -> None: ) +def test_conditional_conversion() -> None: + c = Circuit(1, 2, "conditional_circ") + c.X(0, condition_bits=[0], condition_value=1) + + c_qiskit = tk_to_qiskit(c) + c_tket = qiskit_to_tk(c_qiskit) + + assert c_tket.to_dict() == c.to_dict() + + +def test_conditional_conversion_2() -> None: + c = Circuit(1, 2, "conditional_circ_2") + c.X(0, condition_bits=[1], condition_value=1) + + c_qiskit = tk_to_qiskit(c) + c_tket = qiskit_to_tk(c_qiskit) + + assert c_tket.to_dict() == c.to_dict() + + # https://github.com/CQCL/pytket-qiskit/issues/100 def test_state_prep_conversion_array_or_list() -> None: # State prep with list of real amplitudes