Instructions from `QuantumCircuit.to_instruction` with control flow cannot be used in larger circuits
chriseclectic opened this issue · comments
Environment
- Qiskit version: 1.0.2
- Python version: 3.11
- Operating system: MacOS
What is happening?
Using to_instruction
on a circuit with control flow will flatten its registers. If this instruction definition is then composed into another circuit it will give errors like what are shown in previous linked issues. If the instruction is composed without using its definition, then it will appear to work until any unrolling happens.
How can we reproduce the issue?
Consider the following basic teleport circuit
qr = QuantumRegister(3, "q")
cr = ClassicalRegister(1, "c")
cr_aux = ClassicalRegister(2, "aux")
par = Parameter("a")
qc = QuantumCircuit(qr, cr, cr_aux)
qc.ry(par, 0)
qc.h(1)
qc.cx(1, 2)
qc.cx(0, 1)
qc.h(0)
qc.measure([0, 1], cr_aux)
with qc.switch(cr_aux) as case:
with case(1):
qc.z(2)
with case(2):
qc.x(2)
with case(3):
qc.z(2)
qc.x(2)
qc.measure(2, cr)
If I convert to an instruction and compose its definition
inst = qc.to_instruction()
tmp = QuantumCircuit(inst.num_qubits, inst.num_clbits)
tmp.compose(inst.definition, range(inst.num_qubits), range(inst.num_clbits), inplace=True)
I get the error
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
Cell In[45], line 23
20 qc.measure(2, cr)
22 qc = QuantumCircuit(inst.num_qubits, inst.num_clbits)
---> 23 qc.compose(inst.definition, range(inst.num_qubits), range(inst.num_clbits), inplace=True)
File ~/mambaforge/envs/qiskit1/lib/python3.11/site-packages/qiskit/circuit/quantumcircuit.py:1009, in QuantumCircuit.compose(self, other, qubits, clbits, front, inplace, wrap)
1007 mapped_instrs: CircuitData = other._data.copy()
1008 mapped_instrs.replace_bits(qubits=mapped_qubits, clbits=mapped_clbits)
-> 1009 mapped_instrs.map_ops(map_vars)
1011 append_existing = None
1012 if front:
File ~/mambaforge/envs/qiskit1/lib/python3.11/site-packages/qiskit/circuit/quantumcircuit.py:1004, in QuantumCircuit.compose.<locals>.map_vars(op)
1002 n_op.condition = variable_mapper.map_condition(condition)
1003 if isinstance(n_op, SwitchCaseOp):
-> 1004 n_op.target = variable_mapper.map_target(n_op.target)
1005 return n_op
File ~/mambaforge/envs/qiskit1/lib/python3.11/site-packages/qiskit/circuit/_classical_resource_map.py:118, in VariableMapper.map_target(self, target)
116 return self.bit_map[target]
117 if isinstance(target, ClassicalRegister):
--> 118 return self._map_register(target)
119 return self.map_expr(target)
File ~/mambaforge/envs/qiskit1/lib/python3.11/site-packages/qiskit/circuit/_classical_resource_map.py:58, in VariableMapper._map_register(self, theirs)
56 if (mapped_theirs := self.register_map.get(theirs.name)) is not None:
57 return mapped_theirs
---> 58 mapped_bits = [self.bit_map[bit] for bit in theirs]
59 for ours in self.target_cregs:
60 if mapped_bits == list(ours):
File ~/mambaforge/envs/qiskit1/lib/python3.11/site-packages/qiskit/circuit/_classical_resource_map.py:58, in <listcomp>(.0)
56 if (mapped_theirs := self.register_map.get(theirs.name)) is not None:
57 return mapped_theirs
---> 58 mapped_bits = [self.bit_map[bit] for bit in theirs]
59 for ours in self.target_cregs:
60 if mapped_bits == list(ours):
KeyError: Clbit(ClassicalRegister(2, 'aux'), 0)
If I compose as an instruction it appears to work until transpilation/decomposition happens and then a similar error to the above will be raised
inst = qc.to_instruction()
tmp = QuantumCircuit(inst.num_qubits, inst.num_clbits)
tmp.compose(inst, range(inst.num_qubits), range(inst.num_clbits), inplace=True)
tmp.decompose()
inst = qc.to_instruction()
qc = QuantumCircuit(inst.num_qubits, inst.num_clbits)
qc.append(inst, range(inst.num_qubits), range(inst.num_clbits))
qc.decompose()
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
Cell In[52], line 25
23 qc = QuantumCircuit(inst.num_qubits, inst.num_clbits)
24 qc.append(inst, range(inst.num_qubits), range(inst.num_clbits))
---> 25 qc.decompose()
File ~/mambaforge/envs/qiskit1/lib/python3.11/site-packages/qiskit/circuit/quantumcircuit.py:1651, in QuantumCircuit.decompose(self, gates_to_decompose, reps)
1649 pass_ = Decompose(gates_to_decompose)
1650 for _ in range(reps):
-> 1651 dag = pass_.run(dag)
1652 # do not copy operations, this is done in the conversion with circuit_to_dag
1653 return dag_to_circuit(dag, copy_operations=False)
File ~/mambaforge/envs/qiskit1/lib/python3.11/site-packages/qiskit/transpiler/passes/basis/decompose.py:64, in Decompose.run(self, dag)
62 dag.substitute_node(node, rule[0].operation, inplace=True)
63 else:
---> 64 decomposition = circuit_to_dag(node.op.definition)
65 dag.substitute_node_with_dag(node, decomposition)
67 return dag
File ~/mambaforge/envs/qiskit1/lib/python3.11/site-packages/qiskit/converters/circuit_to_dag.py:92, in circuit_to_dag(circuit, copy_operations, qubit_order, clbit_order)
90 if copy_operations:
91 op = copy.deepcopy(op)
---> 92 dagcircuit.apply_operation_back(op, instruction.qubits, instruction.clbits, check=False)
94 dagcircuit.duration = circuit.duration
95 dagcircuit.unit = circuit.unit
File ~/mambaforge/envs/qiskit1/lib/python3.11/site-packages/qiskit/dagcircuit/dagcircuit.py:682, in DAGCircuit.apply_operation_back(self, op, qargs, cargs, check)
675 self._increment_op(op)
677 # Add new in-edges from predecessors of the output nodes to the
678 # operation node while deleting the old in-edges of the output nodes
679 # and adding new edges from the operation node to each output node
680 self._multi_graph.insert_node_on_in_edges_multiple(
681 node._node_id,
--> 682 [self.output_map[bit]._node_id for bits in (qargs, all_cbits) for bit in bits],
683 )
684 return node
File ~/mambaforge/envs/qiskit1/lib/python3.11/site-packages/qiskit/dagcircuit/dagcircuit.py:682, in <listcomp>(.0)
675 self._increment_op(op)
677 # Add new in-edges from predecessors of the output nodes to the
678 # operation node while deleting the old in-edges of the output nodes
679 # and adding new edges from the operation node to each output node
680 self._multi_graph.insert_node_on_in_edges_multiple(
681 node._node_id,
--> 682 [self.output_map[bit]._node_id for bits in (qargs, all_cbits) for bit in bits],
683 )
684 return node
KeyError: Clbit(ClassicalRegister(2, 'aux'), 0)
What should happen?
Circuit with classical registers and control flow ops should be able to be converted to instructions and used as blocks in larger circuits.
Any suggestions?
This likely is the same underlying issue as #12084, since to_instruction
is essentially flattening the circuit for its definition in the same way I attempted to flatten the circuit manually in #12084.
Yeah, this is exactly the same underlying issue as #12084, because QuantumCircuit.append
just calls to_instruction
internally (imo it's not a great part of the interface that we do that implicitly - it makes it super hard to understand what's going on).
Let me close this one as duplicate, but I'll comment on the other one with the current state of things and try to chart a path forwards - sorry for the delay.
Duplicate of #12084