Skip to content

Commit

Permalink
Fix broadcasting of QuantumCircuit.delay (Qiskit#11447)
Browse files Browse the repository at this point in the history
The object is supposed to "broadcast" like a gate; one instruction per
qubit.  This silently did the wrong thing when given a `set` as its
argument, previously.
  • Loading branch information
jakelishman authored Jan 9, 2024
1 parent d113a6a commit 4c6eed9
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 29 deletions.
6 changes: 3 additions & 3 deletions qiskit/circuit/delay.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import numpy as np
from qiskit.circuit.exceptions import CircuitError
from qiskit.circuit.instruction import Instruction
from qiskit.circuit.gate import Gate
from qiskit.circuit.parameterexpression import ParameterExpression


Expand All @@ -29,13 +30,12 @@ def __init__(self, duration, unit="dt"):

super().__init__("delay", 1, 0, params=[duration], unit=unit)

broadcast_arguments = Gate.broadcast_arguments

def inverse(self):
"""Special case. Return self."""
return self

def broadcast_arguments(self, qargs, cargs):
yield [qarg for sublist in qargs for qarg in sublist], []

def c_if(self, classical, val):
raise CircuitError("Conditional Delay is not yet implemented.")

Expand Down
29 changes: 3 additions & 26 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -3351,32 +3351,9 @@ def delay(
Raises:
CircuitError: if arguments have bad format.
"""
qubits: list[QubitSpecifier] = []
if qarg is None: # -> apply delays to all qubits
for q in self.qubits:
qubits.append(q)
else:
if isinstance(qarg, QuantumRegister):
qubits.extend([qarg[j] for j in range(qarg.size)])
elif isinstance(qarg, list):
qubits.extend(qarg)
elif isinstance(qarg, (range, tuple)):
qubits.extend(list(qarg))
elif isinstance(qarg, slice):
qubits.extend(self.qubits[qarg])
else:
qubits.append(qarg)

instructions = InstructionSet(
resource_requester=self._current_scope().resolve_classical_resource
)
for q in qubits:
inst: tuple[
Instruction, Sequence[QubitSpecifier] | None, Sequence[ClbitSpecifier] | None
] = (Delay(duration, unit), [q], [])
self.append(*inst)
instructions.add(*inst)
return instructions
if qarg is None:
qarg = self.qubits
return self.append(Delay(duration, unit=unit), [qarg], [])

def h(self, qubit: QubitSpecifier) -> InstructionSet:
"""Apply :class:`~qiskit.circuit.library.HGate`.
Expand Down
7 changes: 7 additions & 0 deletions releasenotes/notes/fix-delay-broadcast-e8762b01dfd7e94f.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
fixes:
- |
The qubit-argument broadcasting of :meth:`.QuantumCircuit.delay` now correctly produces
individual :class:`~.circuit.Delay` instructions for each qubit, as intended. Previously, when
given certain iterables (such as :class:`set`\ s), it would instead silently produce an invalid
circuit that might fail in unusual locations.
11 changes: 11 additions & 0 deletions test/python/circuit/test_delay.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@ def test_add_delay_on_multiple_qubits_to_circuit(self):
expected.delay(300, 2)
self.assertEqual(qc, expected)

def test_delay_set(self):
"""Test that a set argument to `delay` works."""
qc = QuantumCircuit(5)
qc.delay(8, {0, 1, 3, 4})
expected = QuantumCircuit(5)
expected.delay(8, 0)
expected.delay(8, 1)
expected.delay(8, 3)
expected.delay(8, 4)
self.assertEqual(qc, expected)

def test_to_matrix_return_identity_matrix(self):
actual = np.array(Delay(100))
expected = np.array([[1, 0], [0, 1]], dtype=complex)
Expand Down

0 comments on commit 4c6eed9

Please sign in to comment.