Skip to content

Commit

Permalink
fix conditional and symbolic parameter in conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
cqc-melf committed Jan 19, 2024
1 parent 3ea0f5a commit 7417db4
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 23 deletions.
51 changes: 38 additions & 13 deletions pytket/extensions/qiskit/qiskit_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
Parameter,
ParameterExpression,
Reset,
Clbit,
)
from qiskit.circuit.library import (
CRYGate,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -653,22 +666,31 @@ 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"
)
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(
"""OpenQASM 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(
"""OpenQASM 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]
Expand Down Expand Up @@ -719,7 +741,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 += float(phase * sympy.pi)
else:
qcirc.global_phase += phase * sympy.pi
return qcirc.append(g, qargs=qargs)


Expand Down
2 changes: 0 additions & 2 deletions tests/qiskit_backend_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
63 changes: 55 additions & 8 deletions tests/qiskit_convert_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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])
Expand Down Expand Up @@ -398,11 +430,6 @@ 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)
Expand Down Expand Up @@ -857,6 +884,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
Expand Down

0 comments on commit 7417db4

Please sign in to comment.