-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: handle IfElseOp
in qiskit_to_tk
#437
base: main
Are you sure you want to change the base?
Changes from all commits
c424b24
3b9189a
692506f
9be4d45
80dddbe
e38ca3e
3843234
69f9e0a
09a0ca2
89ac299
96d9efd
6518628
f87d814
816e2bd
2ff1b6f
ea62c0b
73dc681
d65a347
3281184
52458ab
8e1edce
4b382b0
4729114
f69aa04
8573082
4d311f3
eec8fe5
9d66595
4b4b40a
7950e77
25bfcd5
ae6cbfe
e479c3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,6 +76,7 @@ | |
Clbit, | ||
ControlledGate, | ||
Gate, | ||
IfElseOp, | ||
Instruction, | ||
InstructionSet, | ||
Measure, | ||
|
@@ -479,6 +480,70 @@ def _build_circbox(instr: Instruction, circuit: QuantumCircuit) -> CircBox: | |
return CircBox(subc) | ||
|
||
|
||
# Used for handling of IfElseOp | ||
# docs -> https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.IfElseOp | ||
# Examples -> https://docs.quantum.ibm.com/guides/classical-feedforward-and-control-flow | ||
# pytket-qiskit issue -> https://github.com/CQCL/pytket-qiskit/issues/415 | ||
def _pytket_boxes_from_ifelseop( | ||
if_else_op: IfElseOp, qregs: list[QuantumRegister], cregs: list[ClassicalRegister] | ||
) -> tuple[CircBox, Optional[CircBox]]: | ||
# Extract the QuantumCircuit implementing true_body | ||
if_qc: QuantumCircuit = if_else_op.blocks[0] | ||
|
||
if_builder = CircuitBuilder(qregs, cregs) | ||
if_builder.add_qiskit_data(if_qc) | ||
CalMacCQ marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if_circuit = if_builder.circuit() | ||
if_circuit.name = "If" | ||
# Remove blank wires to ensure CircBox is the correct size. | ||
if_circuit.remove_blank_wires() | ||
|
||
# The false_body arg is optional | ||
if len(if_else_op.blocks) == 2: | ||
else_qc: QuantumCircuit = if_else_op.blocks[1] | ||
else_builder = CircuitBuilder(qregs, cregs) | ||
else_builder.add_qiskit_data(else_qc) | ||
else_circuit = else_builder.circuit() | ||
else_circuit.name = "Else" | ||
else_circuit.remove_blank_wires() | ||
return CircBox(if_circuit), CircBox(else_circuit) | ||
|
||
# If no false_body is specified IfElseOp.blocks is of length 1. | ||
# In this case we return a CircBox implementing true_body and None. | ||
return CircBox(if_circuit), None | ||
|
||
|
||
def _build_if_else_circuit( | ||
if_else_op: IfElseOp, | ||
qregs: list[QuantumRegister], | ||
cregs: list[ClassicalRegister], | ||
qubits: list[Qubit], | ||
bits: list[Bit], | ||
) -> Circuit: | ||
# Get two CircBox objects which implement the true_body and false_body. | ||
if_box, else_box = _pytket_boxes_from_ifelseop(if_else_op, qregs, cregs) | ||
# else_box can be None if no false_body is specified. | ||
|
||
circ_builder = CircuitBuilder(qregs, cregs) | ||
circ = circ_builder.circuit() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
prints
So the box has 2 qubits and 2 bits, but in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. I figured out what I was doing wrong. The thing that was confusing me was that the two circuits which define the Cleaning up the blank wires with |
||
|
||
circ.add_circbox( | ||
circbox=if_box, | ||
args=qubits, | ||
condition_bits=bits, | ||
condition_value=if_else_op.condition[1], | ||
) | ||
# If we have an else_box defined, add it to the circuit | ||
if else_box is not None: | ||
circ.add_circbox( | ||
circbox=else_box, | ||
args=qubits, | ||
condition_bits=bits, | ||
# TODO: handle conditions over multiple bits/registers? | ||
condition_value=not bool(if_else_op.condition[1]), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still need to decide if its worth handling more complex conditionals here or just restrict to conditions over a single bit for now. |
||
) | ||
return circ | ||
|
||
|
||
class CircuitBuilder: | ||
def __init__( | ||
self, | ||
|
@@ -522,16 +587,16 @@ def add_qiskit_data( | |
bits: list[Bit] = [self.cbmap[bit] for bit in cargs] | ||
|
||
condition_kwargs = {} | ||
if instr.condition is not None: | ||
if instr.condition is not None and type(instr) is not IfElseOp: | ||
condition_kwargs = _get_pytket_condition_kwargs( | ||
instruction=instr, | ||
cregmap=self.cregmap, | ||
circuit=circuit, | ||
) | ||
|
||
optype = None | ||
if type(instr) not in (PauliEvolutionGate, UnitaryGate): | ||
# Handling of PauliEvolutionGate and UnitaryGate below | ||
if type(instr) not in (PauliEvolutionGate, UnitaryGate, IfElseOp): | ||
# Handling of PauliEvolutionGate, UnitaryGate and IfElseOp below | ||
optype = _optype_from_qiskit_instruction(instruction=instr) | ||
|
||
if optype == OpType.QControlBox: | ||
|
@@ -543,6 +608,16 @@ def add_qiskit_data( | |
# Append OpType found by stateprep helpers | ||
_add_state_preparation(self.tkc, qubits, instr) | ||
|
||
elif type(instr) is IfElseOp: | ||
if_else_circ = _build_if_else_circuit( | ||
if_else_op=instr, | ||
qregs=self.qregs, | ||
cregs=self.cregs, | ||
qubits=qubits, | ||
bits=bits, | ||
) | ||
self.tkc.append(if_else_circ) | ||
|
||
elif type(instr) is PauliEvolutionGate: | ||
qpo = _qpo_from_peg(instr, qubits) | ||
empty_circ = Circuit(len(qargs)) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1193,6 +1193,65 @@ def test_nonregister_bits() -> None: | |
tk_to_qiskit(c) | ||
|
||
|
||
# https://github.com/CQCL/pytket-qiskit/issues/415 | ||
def test_ifelseop_two_branches() -> None: | ||
qubits = QuantumRegister(2) | ||
clbits = ClassicalRegister(2) | ||
circuit = QuantumCircuit(qubits, clbits) | ||
(q0, q1) = qubits | ||
(c0, c1) = clbits | ||
|
||
circuit.h(q0) | ||
circuit.measure(q0, c0) | ||
|
||
with circuit.if_test((c0, 1)) as else_: | ||
circuit.h(q1) | ||
with else_: | ||
circuit.x(q1) | ||
circuit.measure(q1, c1) | ||
|
||
tkc = qiskit_to_tk(circuit) | ||
assert tkc.n_gates_of_type(OpType.Conditional) == 2 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should test this more thoroughly. I ran into this bug -> CQCL/tket#1726 when trying to validate the contents of the two |
||
|
||
|
||
# https://github.com/CQCL/pytket-qiskit/issues/415 | ||
def test_ifelseop_one_branch() -> None: | ||
qubits = QuantumRegister(1, "q1") | ||
clbits = ClassicalRegister(1, "c1") | ||
circuit = QuantumCircuit(qubits, clbits) | ||
(q0,) = qubits | ||
(c0,) = clbits | ||
|
||
circuit.h(q0) | ||
circuit.measure(q0, c0) | ||
with circuit.if_test((c0, 1)): | ||
circuit.x(q0) | ||
circuit.measure(q0, c0) | ||
|
||
tket_circ_if_else = qiskit_to_tk(circuit) | ||
tket_circ_if_else.name = "test_circ" | ||
|
||
# Manually build the expected pytket Circuit. | ||
# Validate against tket_circ_if_else. | ||
expected_circ = Circuit() | ||
expected_circ.name = "test_circ" | ||
q1_tk = expected_circ.add_q_register("q1", 1) | ||
c1_tk = expected_circ.add_c_register("c1", 1) | ||
expected_circ.H(q1_tk[0]) | ||
expected_circ.Measure(q1_tk[0], c1_tk[0]) | ||
x_circ = Circuit() | ||
x_circ.name = "If" | ||
xq1 = x_circ.add_q_register("q1", 1) | ||
x_circ.X(xq1[0]) | ||
expected_circ.add_circbox( | ||
CircBox(x_circ), [q1_tk[0]], condition_bits=[c1_tk[0]], condition_value=1 | ||
) | ||
|
||
expected_circ.Measure(q1_tk[0], c1_tk[0]) | ||
|
||
assert tket_circ_if_else == expected_circ | ||
|
||
|
||
def test_range_preds_with_conditionals() -> None: | ||
# https://github.com/CQCL/pytket-qiskit/issues/375 | ||
c = Circuit(1, 1) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not so pleased with this
Optional
return type in the function signature. This is to deal with the case when theIfElseOp
only has atrue_body
rather than atrue_body
and afalse_body
.There's probably a cleaner way to deal with this.