Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions qiskit/circuit/measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,19 @@
Quantum measurement in the computational basis.
"""

from qiskit.circuit.instruction import Instruction
from qiskit.circuit.singleton import SingletonInstruction, stdlib_singleton_key
from qiskit.circuit.exceptions import CircuitError


class Measure(Instruction):
class Measure(SingletonInstruction):
"""Quantum measurement in the computational basis."""

def __init__(self, label=None, *, duration=None, unit="dt"):
"""Create new measurement instruction."""
super().__init__("measure", 1, 1, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def broadcast_arguments(self, qargs, cargs):
qarg = qargs[0]
carg = cargs[0]
Expand Down
6 changes: 4 additions & 2 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@
from .bit import Bit
from .quantumcircuitdata import QuantumCircuitData, CircuitInstruction
from .delay import Delay
from .measure import Measure
from .reset import Reset

if typing.TYPE_CHECKING:
import qiskit # pylint: disable=cyclic-import
Expand Down Expand Up @@ -2180,6 +2178,8 @@ def reset(self, qubit: QubitSpecifier) -> InstructionSet:
Returns:
qiskit.circuit.InstructionSet: handle to the added instruction.
"""
from .reset import Reset

return self.append(Reset(), [qubit], [])

def measure(self, qubit: QubitSpecifier, cbit: ClbitSpecifier) -> InstructionSet:
Expand Down Expand Up @@ -2255,6 +2255,8 @@ def measure(self, qubit: QubitSpecifier, cbit: ClbitSpecifier) -> InstructionSet
circuit.measure(qreg[1], creg[1])

"""
from .measure import Measure

return self.append(Measure(), [qubit], [cbit])

def measure_active(self, inplace: bool = True) -> Optional["QuantumCircuit"]:
Expand Down
6 changes: 4 additions & 2 deletions qiskit/circuit/reset.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@
Qubit reset to computational zero.
"""

from qiskit.circuit.instruction import Instruction
from qiskit.circuit.singleton import SingletonInstruction, stdlib_singleton_key


class Reset(Instruction):
class Reset(SingletonInstruction):
"""Qubit reset."""

def __init__(self, label=None, *, duration=None, unit="dt"):
"""Create new reset instruction."""
super().__init__("reset", 1, 0, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def broadcast_arguments(self, qargs, cargs):
for qarg in qargs[0]:
yield [qarg], []
6 changes: 2 additions & 4 deletions qiskit/qasm2/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,15 +234,13 @@ def from_bytecode(bytecode, custom_instructions: Iterable[CustomInstruction]):
qc._append(CircuitInstruction(Measure(), (qubits[qubit],), (clbits[clbit],)))
elif opcode == OpCode.ConditionedMeasure:
qubit, clbit, creg, value = op.operands
measure = Measure()
measure.condition = (qc.cregs[creg], value)
measure = Measure().c_if(qc.cregs[creg], value)
qc._append(CircuitInstruction(measure, (qubits[qubit],), (clbits[clbit],)))
elif opcode == OpCode.Reset:
qc._append(CircuitInstruction(Reset(), (qubits[op.operands[0]],)))
elif opcode == OpCode.ConditionedReset:
qubit, creg, value = op.operands
reset = Reset()
reset.condition = (qc.cregs[creg], value)
reset = Reset().c_if(qc.cregs[creg], value)
qc._append(CircuitInstruction(reset, (qubits[qubit],)))
elif opcode == OpCode.Barrier:
op_qubits = op.operands[0]
Expand Down
32 changes: 32 additions & 0 deletions releasenotes/notes/singletonize-instructions-78723f68cd0ac03f.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
features:
- |
The following standard library instructions are now instances of
:class:`~.SingletonInstruction`:

* :class:`~.Measure`
* :class:`~.Reset`

This means that if these classes are instantiated as (e.g.) ``Measure()`` using
all the constructor defaults, they will all share a single global
instance. This results in large reduction in the memory overhead for > 1
object of these types and significantly faster object construction time.
upgrade:
- |
The following standard library instructions:

* :class:`~.Measure`
* :class:`~.Reset`

are immutable, unless the attributes ``label``, ``duration`` and ``unit`` are given as keyword
arguments during class construction.
The attributes :attr:`~.Instruction.label`, :attr:`~.Instruction.duration`, :attr:`~.Instruction.unit`,
and :attr:`~.Instruction.condition` attributes are all not publicly accessible and setting these attributes
directly is not allowed and it will raise an exception. If they are needed for a particular
instance you must ensure you have a mutable instance using :meth:`.Instruction.to_mutable`
and use :meth:`.Instruction.c_if` for :attr:`~.Instruction.condition`
For the singleton variant of these instructions, there is a special attribute
:attr:`~.SingletonInstruction._singleton_lookup_key`, that when called generates a key based on the input
arguments, which can be used for identifying and indexing these instructions within the framework.


47 changes: 33 additions & 14 deletions test/python/circuit/test_singleton.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


"""
Tests for singleton gate behavior
Tests for singleton gate and instruction behavior
"""

import copy
Expand All @@ -36,15 +36,16 @@
XGate,
C4XGate,
)
from qiskit.circuit import Measure, Reset
from qiskit.circuit import Clbit, QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.singleton import SingletonGate, SingletonInstruction
from qiskit.circuit.singleton import SingletonGate
from qiskit.converters import dag_to_circuit, circuit_to_dag

from qiskit.test.base import QiskitTestCase


class TestSingletonGate(QiskitTestCase):
"""Qiskit SingletonGate tests."""
class TestSingleton(QiskitTestCase):
"""Qiskit SingletonGate and SingletonInstruction tests."""

def test_default_singleton(self):
gate = HGate()
Expand Down Expand Up @@ -325,20 +326,38 @@ def __init__(self, x):
self.assertEqual(gate.x, 1)
self.assertIsNot(MyAbstractGate(1), MyAbstractGate(1))

def test_inherit_singleton(self):
class Measure(SingletonInstruction):
def __init__(self):
super().__init__("measure", 1, 1, [])
def test_return_type_singleton_instructions(self):
measure = Measure()
new_measure = Measure()
self.assertIs(measure, new_measure)
self.assertIs(measure.base_class, Measure)
self.assertIsInstance(measure, Measure)

reset = Reset()
new_reset = Reset()
self.assertIs(reset, new_reset)
self.assertIs(reset.base_class, Reset)
self.assertIsInstance(reset, Reset)

def test_singleton_instruction_integration(self):
measure = Measure()
reset = Reset()
qc = QuantumCircuit(1, 1)
qc.measure(0, 0)
qc.reset(0)
self.assertIs(qc.data[0].operation, measure)
self.assertIs(qc.data[1].operation, reset)

def test_inherit_singleton_instructions(self):
class ESPMeasure(Measure):
pass

base = Measure()
esp = ESPMeasure()
self.assertIs(esp, ESPMeasure())
self.assertIsNot(esp, base)
self.assertIs(base.base_class, Measure)
self.assertIs(esp.base_class, ESPMeasure)
measure_base = Measure()
esp_measure = ESPMeasure()
self.assertIs(esp_measure, ESPMeasure())
self.assertIsNot(esp_measure, measure_base)
self.assertIs(measure_base.base_class, Measure)
self.assertIs(esp_measure.base_class, ESPMeasure)

def test_singleton_with_default(self):
# Explicitly setting the label to its default.
Expand Down