diff --git a/qiskit/circuit/instruction.py b/qiskit/circuit/instruction.py index 6065cfecf804..f23f833f4666 100644 --- a/qiskit/circuit/instruction.py +++ b/qiskit/circuit/instruction.py @@ -41,7 +41,6 @@ import numpy from qiskit.circuit.exceptions import CircuitError -from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit.classicalregister import ClassicalRegister, Clbit from qiskit.qobj.qasm_qobj import QasmQobjInstruction from qiskit.circuit.parameter import ParameterExpression @@ -560,7 +559,13 @@ def _return_repeat(self, exponent): ) def repeat(self, n): - """Creates an instruction with `gate` repeated `n` amount of times. + """Creates an instruction with ``self`` repeated :math`n` times. + + If this operation has a conditional, the output instruction will have the same conditional + and the inner repeated operations will be unconditional; instructions within a compound + definition cannot be conditioned on registers within Qiskit's data model. This means that + it is not valid to apply a repeated instruction to a clbit that it both writes to and reads + from in its condition. Args: n (int): Number of times to repeat the instruction @@ -577,22 +582,24 @@ def repeat(self, n): n = int(n) instruction = self._return_repeat(n) - qargs = [] if self.num_qubits == 0 else QuantumRegister(self.num_qubits, "q") - cargs = [] if self.num_clbits == 0 else ClassicalRegister(self.num_clbits, "c") - if instruction.definition is None: # pylint: disable=cyclic-import from qiskit.circuit import QuantumCircuit, CircuitInstruction - qc = QuantumCircuit() - if qargs: - qc.add_register(qargs) - if cargs: - qc.add_register(cargs) - circuit_instruction = CircuitInstruction(self, qargs, cargs) + qc = QuantumCircuit(self.num_qubits, self.num_clbits) + qargs = tuple(qc.qubits) + cargs = tuple(qc.clbits) + base = self.copy() + if self.condition: + # Condition is handled on the outer instruction. + base = base.to_mutable() + base.condition = None for _ in [None] * n: - qc._append(circuit_instruction) - instruction.definition = qc + qc._append(CircuitInstruction(base, qargs, cargs)) + + instruction.definition = qc + if self.condition: + instruction = instruction.c_if(*self.condition) return instruction @property diff --git a/releasenotes/notes/fix-instruction-repeat-conditional-dfe4d7ced54a7bb6.yaml b/releasenotes/notes/fix-instruction-repeat-conditional-dfe4d7ced54a7bb6.yaml new file mode 100644 index 000000000000..061d58678429 --- /dev/null +++ b/releasenotes/notes/fix-instruction-repeat-conditional-dfe4d7ced54a7bb6.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + The method :meth:`.Instruction.repeat` now moves a set :attr:`~.Instruction.condition` to the + outer returned :class:`~.circuit.Instruction` and leave the inner gates of its definition + unconditional. Previously, the method would leave :class:`.ClassicalRegister` instances within + the inner definition, which was an invalid state, and would manifest itself as seemingly unrelated + bugs later, such as during transpilation or export. Fixed `#11935 `__. diff --git a/test/python/circuit/test_instruction_repeat.py b/test/python/circuit/test_instruction_repeat.py index 2fa26af311fc..778e6aad64c2 100644 --- a/test/python/circuit/test_instruction_repeat.py +++ b/test/python/circuit/test_instruction_repeat.py @@ -52,6 +52,18 @@ def test_standard_1Q_one(self): self.assertEqual(result.definition, expected.definition) self.assertIsInstance(result, Gate) + def test_conditional(self): + """Test that repetition works with a condition.""" + cr = ClassicalRegister(3, "cr") + gate = SGate().c_if(cr, 7).repeat(5) + self.assertEqual(gate.condition, (cr, 7)) + + defn = QuantumCircuit(1) + for _ in range(5): + # No conditions on the inner bit. + defn.s(0) + self.assertEqual(gate.definition, defn) + class TestRepeatInt2Q(QiskitTestCase): """Test gate_q2.repeat() with integer""" @@ -83,6 +95,18 @@ def test_standard_2Q_one(self): self.assertEqual(result.definition, expected.definition) self.assertIsInstance(result, Gate) + def test_conditional(self): + """Test that repetition works with a condition.""" + cr = ClassicalRegister(3, "cr") + gate = CXGate().c_if(cr, 7).repeat(5) + self.assertEqual(gate.condition, (cr, 7)) + + defn = QuantumCircuit(2) + for _ in range(5): + # No conditions on the inner bit. + defn.cx(0, 1) + self.assertEqual(gate.definition, defn) + class TestRepeatIntMeasure(QiskitTestCase): """Test Measure.repeat() with integer""" @@ -118,6 +142,18 @@ def test_measure_one(self): self.assertIsInstance(result, Instruction) self.assertNotIsInstance(result, Gate) + def test_measure_conditional(self): + """Test conditional measure moves condition to the outside.""" + cr = ClassicalRegister(3, "cr") + measure = Measure().c_if(cr, 7).repeat(5) + self.assertEqual(measure.condition, (cr, 7)) + + defn = QuantumCircuit(1, 1) + for _ in range(5): + # No conditions on the inner bit. + defn.measure(0, 0) + self.assertEqual(measure.definition, defn) + class TestRepeatErrors(QiskitTestCase): """Test when Gate.repeat() should raise."""