Fix handling of `Var` nodes in full-rebuild control-flow blocks (#11666)

In the control-flow builders, there are typically two ways the
individual blocks can be reconstructed to be unified over the qubit and
clbit resources.  We generally attempt to avoid completely rebuilding
the cirucits unless we have to.  In cases where the resources are
visited in an incompatible order, however, we have to construct new
circuit objects, and in these cases, we were failing to transfer the
`Var` use over completely.
This commit is contained in:
Jake Lishman 2024-04-11 22:28:27 +01:00 committed by GitHub
parent 3af3cf5880
commit 8c2afa965e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 51 additions and 1 deletions

View File

@ -174,8 +174,16 @@ def _unify_circuit_resources_rebuild( # pylint: disable=invalid-name # (it's t
out_circuits = []
for circuit in circuits:
out = QuantumCircuit(
qubits, clbits, *circuit.qregs, *circuit.cregs, global_phase=circuit.global_phase
qubits,
clbits,
*circuit.qregs,
*circuit.cregs,
global_phase=circuit.global_phase,
inputs=circuit.iter_input_vars(),
captures=circuit.iter_captured_vars(),
)
for var in circuit.iter_declared_vars():
out.add_uninitialized_var(var)
for instruction in circuit.data:
out._append(instruction)
out_circuits.append(out)

View File

@ -3245,6 +3245,48 @@ class TestControlFlowBuilders(QiskitTestCase):
self.assertEqual(base, expected)
def test_rebuild_captures_variables_in_blocks(self):
"""Test that when the separate blocks of a statement cause it to require a full rebuild of
the circuit objects during builder resolution, the variables are all moved over
correctly."""
a = expr.Var.new("🐍🐍🐍", types.Uint(8))
qc = QuantumCircuit(3, 1, inputs=[a])
qc.measure(0, 0)
b_outer = qc.add_var("b", False)
with qc.switch(a) as case:
with case(0):
qc.cx(1, 2)
qc.store(b_outer, True)
with case(1):
qc.store(qc.clbits[0], False)
with case(2):
# Explicit shadowing.
b_inner = qc.add_var("b", True)
with case(3):
qc.store(a, expr.lift(1, a.type))
with case(case.DEFAULT):
qc.cx(2, 1)
# (inputs, captures, declares) for each block of the `switch`.
expected = [
([], [b_outer], []),
([], [], []),
([], [], [b_inner]),
([], [a], []),
([], [], []),
]
actual = [
(
list(block.iter_input_vars()),
list(block.iter_captured_vars()),
list(block.iter_declared_vars()),
)
for block in qc.data[-1].operation.blocks
]
self.assertEqual(expected, actual)
@ddt.ddt
class TestControlFlowBuildersFailurePaths(QiskitTestCase):