mirror of https://github.com/Qiskit/qiskit.git
Fix `Instruction.repeat` with conditionals (#11940)
* Fix `Instruction.repeat` with conditionals We can't put register conditionals within an `Instruction.definition` field; the data model of `QuantumCircuit` doesn't permit closing over registers from within definitions. This commit moves a condition to the outer `Instruction` that's returned. * Avoid 'to_mutable' if not needed * Add proviso on repeated conditionals in documentation * Update wording in release note Co-authored-by: Luciano Bello <bel@zurich.ibm.com> --------- Co-authored-by: Luciano Bello <bel@zurich.ibm.com>
This commit is contained in:
parent
3e7f8b8018
commit
6ebb7aa577
|
@ -41,7 +41,6 @@ from typing import List, Type
|
|||
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 @@ class Instruction(Operation):
|
|||
)
|
||||
|
||||
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 @@ class Instruction(Operation):
|
|||
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
|
||||
|
|
|
@ -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 <https://github.com/Qiskit/qiskit/issues/11935>`__.
|
|
@ -52,6 +52,18 @@ class TestRepeatInt1Q(QiskitTestCase):
|
|||
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 @@ class TestRepeatInt2Q(QiskitTestCase):
|
|||
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 @@ class TestRepeatIntMeasure(QiskitTestCase):
|
|||
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."""
|
||||
|
|
Loading…
Reference in New Issue