From 5af7554c788394561f42553de8a4dd43c4a38bdf Mon Sep 17 00:00:00 2001 From: Kevin Hartman Date: Mon, 25 Sep 2023 16:41:52 -0400 Subject: [PATCH] Revert "Use singletons for standard library unparameterized, non-controlled gates (#10314)" This reverts commit e62c86bbc2720883270641f1c154743a657cfca8. --- qiskit/circuit/__init__.py | 2 - qiskit/circuit/add_control.py | 4 +- qiskit/circuit/gate.py | 12 +- qiskit/circuit/instruction.py | 33 +-- qiskit/circuit/instructionset.py | 2 +- qiskit/circuit/library/standard_gates/dcx.py | 13 +- qiskit/circuit/library/standard_gates/ecr.py | 12 +- qiskit/circuit/library/standard_gates/h.py | 30 +-- qiskit/circuit/library/standard_gates/i.py | 12 +- .../circuit/library/standard_gates/iswap.py | 12 +- qiskit/circuit/library/standard_gates/s.py | 22 +- qiskit/circuit/library/standard_gates/swap.py | 24 +- qiskit/circuit/library/standard_gates/sx.py | 40 +-- qiskit/circuit/library/standard_gates/t.py | 22 +- qiskit/circuit/library/standard_gates/x.py | 97 ++----- qiskit/circuit/library/standard_gates/y.py | 30 +-- qiskit/circuit/library/standard_gates/z.py | 30 +-- qiskit/circuit/random/utils.py | 2 +- qiskit/circuit/singleton_gate.py | 188 ------------- qiskit/converters/ast_to_dag.py | 12 +- qiskit/converters/circuit_to_instruction.py | 6 +- qiskit/dagcircuit/dagcircuit.py | 25 +- qiskit/qasm2/parse.py | 3 +- qiskit/qpy/binary_io/circuits.py | 28 +- .../reset_after_measure_simplification.py | 3 +- .../passes/scheduling/dynamical_decoupling.py | 5 +- .../padding/dynamical_decoupling.py | 4 +- .../scheduling/scheduling/base_scheduler.py | 1 - .../passes/scheduling/time_unit_conversion.py | 9 +- .../notes/singletons-83782de8bd062cbc.yaml | 122 --------- test/python/circuit/gate_utils.py | 2 +- .../circuit/test_circuit_load_from_qpy.py | 3 +- test/python/circuit/test_gate_definitions.py | 1 - test/python/circuit/test_instructions.py | 19 +- test/python/circuit/test_singleton_gate.py | 253 ------------------ test/python/dagcircuit/test_dagcircuit.py | 36 ++- 36 files changed, 169 insertions(+), 950 deletions(-) delete mode 100644 qiskit/circuit/singleton_gate.py delete mode 100644 releasenotes/notes/singletons-83782de8bd062cbc.yaml delete mode 100644 test/python/circuit/test_singleton_gate.py diff --git a/qiskit/circuit/__init__.py b/qiskit/circuit/__init__.py index e2f7936acbcc..b293fcdd12c2 100644 --- a/qiskit/circuit/__init__.py +++ b/qiskit/circuit/__init__.py @@ -279,7 +279,6 @@ InstructionSet Operation EquivalenceLibrary - SingletonGate Control Flow Operations ----------------------- @@ -367,7 +366,6 @@ # pylint: disable=cyclic-import from .controlledgate import ControlledGate -from .singleton_gate import SingletonGate from .instruction import Instruction from .instructionset import InstructionSet from .operation import Operation diff --git a/qiskit/circuit/add_control.py b/qiskit/circuit/add_control.py index 46fe2de0c68a..9a9837673d1a 100644 --- a/qiskit/circuit/add_control.py +++ b/qiskit/circuit/add_control.py @@ -55,9 +55,7 @@ def add_control( # attempt decomposition operation._define() cgate = control(operation, num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) - if operation.label is not None: - cgate.base_gate = cgate.base_gate.to_mutable() - cgate.base_gate.label = operation.label + cgate.base_gate.label = operation.label return cgate diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index 65ca210b5958..3bfd40f15999 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -24,15 +24,7 @@ class Gate(Instruction): """Unitary gate.""" - def __init__( - self, - name: str, - num_qubits: int, - params: list, - label: str | None = None, - duration=None, - unit="dt", - ) -> None: + def __init__(self, name: str, num_qubits: int, params: list, label: str | None = None) -> None: """Create a new gate. Args: @@ -42,7 +34,7 @@ def __init__( label: An optional label for the gate. """ self.definition = None - super().__init__(name, num_qubits, 0, params, label=label, duration=duration, unit=unit) + super().__init__(name, num_qubits, 0, params, label=label) # Set higher priority than Numpy array and matrix classes __array_priority__ = 20 diff --git a/qiskit/circuit/instruction.py b/qiskit/circuit/instruction.py index d2fe33d8d3b4..6b74dacfaab8 100644 --- a/qiskit/circuit/instruction.py +++ b/qiskit/circuit/instruction.py @@ -94,43 +94,16 @@ def __init__(self, name, num_qubits, num_clbits, params, duration=None, unit="dt self._label = label # tuple (ClassicalRegister, int), tuple (Clbit, bool) or tuple (Clbit, int) # when the instruction has a conditional ("if") - self._condition = None + self.condition = None # list of instructions (and their contexts) that this instruction is composed of # empty definition means opaque or fundamental instruction self._definition = None + self._duration = duration self._unit = unit self.params = params # must be at last (other properties may be required for validation) - @property - def mutable(self) -> bool: - """Is this instance is a mutable unique instance or not. - - If this attribute is ``False`` the gate instance is a shared singleton - and is not mutable. - """ - return True - - def to_mutable(self): - """Return a mutable copy of this gate. - - This method will return a new mutable copy of this gate instance. - If a singleton instance is being used this will be a new unique - instance that can be mutated. If the instance is already mutable it - will be a deepcopy of that instance. - """ - return self.copy() - - @property - def condition(self): - """The classical condition on the instruction.""" - return self._condition - - @condition.setter - def condition(self, condition): - self._condition = condition - def __eq__(self, other): """Two instructions are the same if they have the same name, same dimensions, and same params. @@ -436,7 +409,7 @@ def c_if(self, classical, val): # Casting the conditional value as Boolean when # the classical condition is on a classical bit. val = bool(val) - self._condition = (classical, val) + self.condition = (classical, val) return self def copy(self, name=None): diff --git a/qiskit/circuit/instructionset.py b/qiskit/circuit/instructionset.py index 2b1a3b756de6..8ea32f445a7d 100644 --- a/qiskit/circuit/instructionset.py +++ b/qiskit/circuit/instructionset.py @@ -132,7 +132,7 @@ def c_if(self, classical: Clbit | ClassicalRegister | int, val: int) -> "Instruc if self._requester is not None: classical = self._requester(classical) for instruction in self._instructions: - instruction.operation = instruction.operation.c_if(classical, val) + instruction.operation.c_if(classical, val) return self # Legacy support for properties. Added in Terra 0.21 to support the internal switch in diff --git a/qiskit/circuit/library/standard_gates/dcx.py b/qiskit/circuit/library/standard_gates/dcx.py index fed5ae3b442d..836a81ffa4cd 100644 --- a/qiskit/circuit/library/standard_gates/dcx.py +++ b/qiskit/circuit/library/standard_gates/dcx.py @@ -12,13 +12,13 @@ """Double-CNOT gate.""" -from qiskit.circuit.singleton_gate import SingletonGate +from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit._utils import with_gate_array @with_gate_array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 1, 0, 0], [0, 0, 1, 0]]) -class DCXGate(SingletonGate): +class DCXGate(Gate): r"""Double-CNOT gate. A 2-qubit Clifford gate consisting of two back-to-back @@ -48,14 +48,9 @@ class DCXGate(SingletonGate): \end{pmatrix} """ - def __init__(self, label=None, duration=None, unit=None, _condition=None): + def __init__(self): """Create new DCX gate.""" - if unit is None: - unit = "dt" - - super().__init__( - "dcx", 2, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("dcx", 2, []) def _define(self): """ diff --git a/qiskit/circuit/library/standard_gates/ecr.py b/qiskit/circuit/library/standard_gates/ecr.py index abff2072daa1..cc7b13ecc082 100644 --- a/qiskit/circuit/library/standard_gates/ecr.py +++ b/qiskit/circuit/library/standard_gates/ecr.py @@ -15,8 +15,8 @@ import numpy as np from qiskit.circuit._utils import with_gate_array +from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.circuit.singleton_gate import SingletonGate from .rzx import RZXGate from .x import XGate @@ -24,7 +24,7 @@ @with_gate_array( sqrt(0.5) * np.array([[0, 1, 0, 1.0j], [1, 0, -1.0j, 0], [0, 1.0j, 0, 1], [-1.0j, 0, 1, 0]]) ) -class ECRGate(SingletonGate): +class ECRGate(Gate): r"""An echoed cross-resonance gate. This gate is maximally entangling and is equivalent to a CNOT up to @@ -84,13 +84,9 @@ class ECRGate(SingletonGate): \end{pmatrix} """ - def __init__(self, label=None, _condition=None, duration=None, unit=None): + def __init__(self): """Create new ECR gate.""" - if unit is None: - unit = "dt" - super().__init__( - "ecr", 2, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("ecr", 2, []) def _define(self): """ diff --git a/qiskit/circuit/library/standard_gates/h.py b/qiskit/circuit/library/standard_gates/h.py index 7a75b6094010..539383436a11 100644 --- a/qiskit/circuit/library/standard_gates/h.py +++ b/qiskit/circuit/library/standard_gates/h.py @@ -15,7 +15,7 @@ from typing import Optional, Union import numpy from qiskit.circuit.controlledgate import ControlledGate -from qiskit.circuit.singleton_gate import SingletonGate +from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array from .t import TGate, TdgGate @@ -25,7 +25,7 @@ @with_gate_array(_H_ARRAY) -class HGate(SingletonGate): +class HGate(Gate): r"""Single-qubit Hadamard gate. This gate is a \pi rotation about the X+Z axis, and has the effect of @@ -54,13 +54,9 @@ class HGate(SingletonGate): \end{pmatrix} """ - def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None): + def __init__(self, label: Optional[str] = None): """Create new H gate.""" - if unit is None: - unit = "dt" - super().__init__( - "h", 1, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("h", 1, [], label=label) def _define(self): """ @@ -98,7 +94,8 @@ def control( ControlledGate: controlled version of this gate. """ if num_ctrl_qubits == 1: - gate = CHGate(label=label, ctrl_state=ctrl_state, _base_label=self.label) + gate = CHGate(label=label, ctrl_state=ctrl_state) + gate.base_gate.label = self.label return gate return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @@ -165,21 +162,10 @@ class CHGate(ControlledGate): \end{pmatrix} """ - def __init__( - self, - label: Optional[str] = None, - ctrl_state: Optional[Union[int, str]] = None, - _base_label=None, - ): + def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[int, str]] = None): """Create new CH gate.""" super().__init__( - "ch", - 2, - [], - num_ctrl_qubits=1, - label=label, - ctrl_state=ctrl_state, - base_gate=HGate(label=_base_label), + "ch", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=HGate() ) def _define(self): diff --git a/qiskit/circuit/library/standard_gates/i.py b/qiskit/circuit/library/standard_gates/i.py index b8742665f66c..b47571290653 100644 --- a/qiskit/circuit/library/standard_gates/i.py +++ b/qiskit/circuit/library/standard_gates/i.py @@ -13,12 +13,12 @@ """Identity gate.""" from typing import Optional -from qiskit.circuit.singleton_gate import SingletonGate +from qiskit.circuit.gate import Gate from qiskit.circuit._utils import with_gate_array @with_gate_array([[1, 0], [0, 1]]) -class IGate(SingletonGate): +class IGate(Gate): r"""Identity gate. Identity gate corresponds to a single-qubit gate wait cycle, @@ -45,13 +45,9 @@ class IGate(SingletonGate): └───┘ """ - def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None): + def __init__(self, label: Optional[str] = None): """Create new Identity gate.""" - if unit is None: - unit = "dt" - super().__init__( - "id", 1, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("id", 1, [], label=label) def inverse(self): """Invert this gate.""" diff --git a/qiskit/circuit/library/standard_gates/iswap.py b/qiskit/circuit/library/standard_gates/iswap.py index 018715ef3d68..a0cca1f484c7 100644 --- a/qiskit/circuit/library/standard_gates/iswap.py +++ b/qiskit/circuit/library/standard_gates/iswap.py @@ -16,7 +16,7 @@ import numpy as np -from qiskit.circuit.singleton_gate import SingletonGate +from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit._utils import with_gate_array @@ -24,7 +24,7 @@ @with_gate_array([[1, 0, 0, 0], [0, 0, 1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]]) -class iSwapGate(SingletonGate): +class iSwapGate(Gate): r"""iSWAP gate. A 2-qubit XX+YY interaction. @@ -85,13 +85,9 @@ class iSwapGate(SingletonGate): \end{pmatrix} """ - def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None): + def __init__(self, label: Optional[str] = None): """Create new iSwap gate.""" - if unit is None: - unit = "dt" - super().__init__( - "iswap", 2, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("iswap", 2, [], label=label) def _define(self): """ diff --git a/qiskit/circuit/library/standard_gates/s.py b/qiskit/circuit/library/standard_gates/s.py index 07349eb96350..67b3aafbe786 100644 --- a/qiskit/circuit/library/standard_gates/s.py +++ b/qiskit/circuit/library/standard_gates/s.py @@ -18,7 +18,7 @@ import numpy from qiskit.circuit.controlledgate import ControlledGate -from qiskit.circuit.singleton_gate import SingletonGate +from qiskit.circuit.gate import Gate from qiskit.circuit.library.standard_gates.p import CPhaseGate, PhaseGate from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array @@ -29,7 +29,7 @@ @with_gate_array(_S_ARRAY) -class SGate(SingletonGate): +class SGate(Gate): r"""Single qubit S gate (Z**0.5). It induces a :math:`\pi/2` phase, and is sometimes called the P gate (phase). @@ -59,13 +59,9 @@ class SGate(SingletonGate): Equivalent to a :math:`\pi/2` radian rotation about the Z axis. """ - def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None): + def __init__(self, label: Optional[str] = None): """Create new S gate.""" - if unit is None: - unit = "dt" - super().__init__( - "s", 1, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("s", 1, [], label=label) def _define(self): """ @@ -94,7 +90,7 @@ def power(self, exponent: float): @with_gate_array(_SDG_ARRAY) -class SdgGate(SingletonGate): +class SdgGate(Gate): r"""Single qubit S-adjoint gate (~Z**0.5). It induces a :math:`-\pi/2` phase. @@ -124,13 +120,9 @@ class SdgGate(SingletonGate): Equivalent to a :math:`-\pi/2` radian rotation about the Z axis. """ - def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None): + def __init__(self, label: Optional[str] = None): """Create new Sdg gate.""" - if unit is None: - unit = "dt" - super().__init__( - "sdg", 1, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("sdg", 1, [], label=label) def _define(self): """ diff --git a/qiskit/circuit/library/standard_gates/swap.py b/qiskit/circuit/library/standard_gates/swap.py index 5f4cc76a87e1..578efc3053fb 100644 --- a/qiskit/circuit/library/standard_gates/swap.py +++ b/qiskit/circuit/library/standard_gates/swap.py @@ -15,7 +15,7 @@ from typing import Optional, Union import numpy from qiskit.circuit.controlledgate import ControlledGate -from qiskit.circuit.singleton_gate import SingletonGate +from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array @@ -24,7 +24,7 @@ @with_gate_array(_SWAP_ARRAY) -class SwapGate(SingletonGate): +class SwapGate(Gate): r"""The SWAP gate. This is a symmetric and Clifford gate. @@ -59,13 +59,9 @@ class SwapGate(SingletonGate): |a, b\rangle \rightarrow |b, a\rangle """ - def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None): + def __init__(self, label: Optional[str] = None): """Create new SWAP gate.""" - if unit is None: - unit = "dt" - super().__init__( - "swap", 2, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("swap", 2, [], label=label) def _define(self): """ @@ -107,7 +103,8 @@ def control( ControlledGate: controlled version of this gate. """ if num_ctrl_qubits == 1: - gate = CSwapGate(label=label, ctrl_state=ctrl_state, _base_label=self.label) + gate = CSwapGate(label=label, ctrl_state=ctrl_state) + gate.base_gate.label = self.label return gate return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @@ -194,12 +191,7 @@ class CSwapGate(ControlledGate): |1, b, c\rangle \rightarrow |1, c, b\rangle """ - def __init__( - self, - label: Optional[str] = None, - ctrl_state: Optional[Union[str, int]] = None, - _base_label=None, - ): + def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None): """Create new CSWAP gate.""" super().__init__( "cswap", @@ -208,7 +200,7 @@ def __init__( num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, - base_gate=SwapGate(label=_base_label), + base_gate=SwapGate(), ) def _define(self): diff --git a/qiskit/circuit/library/standard_gates/sx.py b/qiskit/circuit/library/standard_gates/sx.py index 49fbb11c558c..a15b38787cc1 100644 --- a/qiskit/circuit/library/standard_gates/sx.py +++ b/qiskit/circuit/library/standard_gates/sx.py @@ -16,7 +16,7 @@ from typing import Optional, Union import numpy from qiskit.circuit.controlledgate import ControlledGate -from qiskit.circuit.singleton_gate import SingletonGate +from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array @@ -26,7 +26,7 @@ @with_gate_array(_SX_ARRAY) -class SXGate(SingletonGate): +class SXGate(Gate): r"""The single-qubit Sqrt(X) gate (:math:`\sqrt{X}`). Can be applied to a :class:`~qiskit.circuit.QuantumCircuit` @@ -64,13 +64,9 @@ class SXGate(SingletonGate): """ - def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None): + def __init__(self, label: Optional[str] = None): """Create new SX gate.""" - if unit is None: - unit = "dt" - super().__init__( - "sx", 1, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("sx", 1, [], label=label) def _define(self): """ @@ -112,13 +108,14 @@ def control( ControlledGate: controlled version of this gate. """ if num_ctrl_qubits == 1: - gate = CSXGate(label=label, ctrl_state=ctrl_state, _base_label=self.label) + gate = CSXGate(label=label, ctrl_state=ctrl_state) + gate.base_gate.label = self.label return gate return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @with_gate_array(_SXDG_ARRAY) -class SXdgGate(SingletonGate): +class SXdgGate(Gate): r"""The inverse single-qubit Sqrt(X) gate. Can be applied to a :class:`~qiskit.circuit.QuantumCircuit` @@ -151,13 +148,9 @@ class SXdgGate(SingletonGate): ) _ARRAY.setflags(write=False) - def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None): + def __init__(self, label: Optional[str] = None): """Create new SXdg gate.""" - if unit is None: - unit = "dt" - super().__init__( - "sxdg", 1, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("sxdg", 1, [], label=label) def _define(self): """ @@ -237,21 +230,10 @@ class CSXGate(ControlledGate): """ - def __init__( - self, - label: Optional[str] = None, - ctrl_state: Optional[Union[str, int]] = None, - _base_label=None, - ): + def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None): """Create new CSX gate.""" super().__init__( - "csx", - 2, - [], - num_ctrl_qubits=1, - label=label, - ctrl_state=ctrl_state, - base_gate=SXGate(label=_base_label), + "csx", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=SXGate() ) def _define(self): diff --git a/qiskit/circuit/library/standard_gates/t.py b/qiskit/circuit/library/standard_gates/t.py index e81d33773798..67a14a12e1c5 100644 --- a/qiskit/circuit/library/standard_gates/t.py +++ b/qiskit/circuit/library/standard_gates/t.py @@ -17,14 +17,14 @@ import numpy -from qiskit.circuit.singleton_gate import SingletonGate +from qiskit.circuit.gate import Gate from qiskit.circuit.library.standard_gates.p import PhaseGate from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit._utils import with_gate_array @with_gate_array([[1, 0], [0, (1 + 1j) / math.sqrt(2)]]) -class TGate(SingletonGate): +class TGate(Gate): r"""Single qubit T gate (Z**0.25). It induces a :math:`\pi/4` phase, and is sometimes called the pi/8 gate @@ -55,13 +55,9 @@ class TGate(SingletonGate): Equivalent to a :math:`\pi/4` radian rotation about the Z axis. """ - def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None): + def __init__(self, label: Optional[str] = None): """Create new T gate.""" - if unit is None: - unit = "dt" - super().__init__( - "t", 1, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("t", 1, [], label=label) def _define(self): """ @@ -90,7 +86,7 @@ def power(self, exponent: float): @with_gate_array([[1, 0], [0, (1 - 1j) / math.sqrt(2)]]) -class TdgGate(SingletonGate): +class TdgGate(Gate): r"""Single qubit T-adjoint gate (~Z**0.25). It induces a :math:`-\pi/4` phase. @@ -120,13 +116,9 @@ class TdgGate(SingletonGate): Equivalent to a :math:`-\pi/4` radian rotation about the Z axis. """ - def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None): + def __init__(self, label: Optional[str] = None): """Create new Tdg gate.""" - if unit is None: - unit = "dt" - super().__init__( - "tdg", 1, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("tdg", 1, [], label=label) def _define(self): """ diff --git a/qiskit/circuit/library/standard_gates/x.py b/qiskit/circuit/library/standard_gates/x.py index cc17b8060ebe..b48181be8062 100644 --- a/qiskit/circuit/library/standard_gates/x.py +++ b/qiskit/circuit/library/standard_gates/x.py @@ -17,7 +17,7 @@ import numpy from qiskit.utils.deprecation import deprecate_func from qiskit.circuit.controlledgate import ControlledGate -from qiskit.circuit.singleton_gate import SingletonGate +from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit._utils import _ctrl_state_to_int, with_gate_array, with_controlled_gate_array from .h import HGate @@ -30,7 +30,7 @@ @with_gate_array(_X_ARRAY) -class XGate(SingletonGate): +class XGate(Gate): r"""The single-qubit Pauli-X gate (:math:`\sigma_x`). Can be applied to a :class:`~qiskit.circuit.QuantumCircuit` @@ -76,13 +76,9 @@ class XGate(SingletonGate): |1\rangle \rightarrow |0\rangle """ - def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None): + def __init__(self, label: Optional[str] = None): """Create new X gate.""" - if unit is None: - unit = "dt" - super().__init__( - "x", 1, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("x", 1, [], label=label) def _define(self): """ @@ -119,12 +115,8 @@ def control( Returns: ControlledGate: controlled version of this gate. """ - gate = MCXGate( - num_ctrl_qubits=num_ctrl_qubits, - label=label, - ctrl_state=ctrl_state, - _base_label=self.label, - ) + gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) + gate.base_gate.label = self.label return gate def inverse(self): @@ -196,21 +188,10 @@ class CXGate(ControlledGate): `|a, b\rangle \rightarrow |a, a \oplus b\rangle` """ - def __init__( - self, - label: Optional[str] = None, - ctrl_state: Optional[Union[str, int]] = None, - _base_label=None, - ): + def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None): """Create new CX gate.""" super().__init__( - "cx", - 2, - [], - num_ctrl_qubits=1, - label=label, - ctrl_state=ctrl_state, - base_gate=XGate(label=_base_label), + "cx", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=XGate() ) def control( @@ -232,12 +213,8 @@ def control( """ ctrl_state = _ctrl_state_to_int(ctrl_state, num_ctrl_qubits) new_ctrl_state = (self.ctrl_state << num_ctrl_qubits) | ctrl_state - gate = MCXGate( - num_ctrl_qubits=num_ctrl_qubits + 1, - label=label, - ctrl_state=new_ctrl_state, - _base_label=self.label, - ) + gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits + 1, label=label, ctrl_state=new_ctrl_state) + gate.base_gate.label = self.label return gate def inverse(self): @@ -314,21 +291,10 @@ class CCXGate(ControlledGate): """ - def __init__( - self, - label: Optional[str] = None, - ctrl_state: Optional[Union[str, int]] = None, - _base_label=None, - ): + def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None): """Create new CCX gate.""" super().__init__( - "ccx", - 3, - [], - num_ctrl_qubits=2, - label=label, - ctrl_state=ctrl_state, - base_gate=XGate(label=_base_label), + "ccx", 3, [], num_ctrl_qubits=2, label=label, ctrl_state=ctrl_state, base_gate=XGate() ) def _define(self): @@ -393,12 +359,8 @@ def control( """ ctrl_state = _ctrl_state_to_int(ctrl_state, num_ctrl_qubits) new_ctrl_state = (self.ctrl_state << num_ctrl_qubits) | ctrl_state - gate = MCXGate( - num_ctrl_qubits=num_ctrl_qubits + 2, - label=label, - ctrl_state=new_ctrl_state, - _base_label=self.label, - ) + gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits + 2, label=label, ctrl_state=new_ctrl_state) + gate.base_gate.label = self.label return gate def inverse(self): @@ -418,7 +380,7 @@ def inverse(self): [0, 0, 0, 1j, 0, 0, 0, 0], ] ) -class RCCXGate(SingletonGate): +class RCCXGate(Gate): """The simplified Toffoli gate, also referred to as Margolus gate. The simplified Toffoli gate implements the Toffoli gate up to relative phases. @@ -434,13 +396,9 @@ class RCCXGate(SingletonGate): with the :meth:`~qiskit.circuit.QuantumCircuit.rccx` method. """ - def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None): + def __init__(self, label: Optional[str] = None): """Create a new simplified CCX gate.""" - if unit is None: - unit = "dt" - super().__init__( - "rccx", 3, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("rccx", 3, [], label=label) def _define(self): """ @@ -717,7 +675,7 @@ def inverse(self): [0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0], ] ) -class RC3XGate(SingletonGate): +class RC3XGate(Gate): """The simplified 3-controlled Toffoli gate. The simplified Toffoli gate implements the Toffoli gate up to relative phases. @@ -731,13 +689,9 @@ class RC3XGate(SingletonGate): with the :meth:`~qiskit.circuit.QuantumCircuit.rcccx` method. """ - def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None): + def __init__(self, label: Optional[str] = None): """Create a new RC3X gate.""" - if unit is None: - unit = "dt" - super().__init__( - "rcccx", 4, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("rcccx", 4, [], label=label) def _define(self): """ @@ -901,7 +855,6 @@ def __new__( num_ctrl_qubits: Optional[int] = None, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None, - _base_label=None, ): """Create a new MCX instance. @@ -913,11 +866,9 @@ def __new__( explicit: dict[int, Type[ControlledGate]] = {1: CXGate, 2: CCXGate} if num_ctrl_qubits in explicit: gate_class = explicit[num_ctrl_qubits] - gate = gate_class.__new__( - gate_class, label=label, ctrl_state=ctrl_state, _base_label=_base_label - ) + gate = gate_class.__new__(gate_class, label=label, ctrl_state=ctrl_state) # if __new__ does not return the same type as cls, init is not called - gate.__init__(label=label, ctrl_state=ctrl_state, _base_label=_base_label) + gate.__init__(label=label, ctrl_state=ctrl_state) return gate return super().__new__(cls) @@ -927,7 +878,6 @@ def __init__( label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None, _name="mcx", - _base_label=None, ): """Create new MCX gate.""" num_ancilla_qubits = self.__class__.get_num_ancilla_qubits(num_ctrl_qubits) @@ -938,7 +888,7 @@ def __init__( num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state, - base_gate=XGate(label=_base_label), + base_gate=XGate(), ) def inverse(self): @@ -1013,7 +963,6 @@ def __new__( num_ctrl_qubits: Optional[int] = None, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None, - _base_label=None, ): """Create a new MCXGrayCode instance""" # if 1 to 4 control qubits, create explicit gates diff --git a/qiskit/circuit/library/standard_gates/y.py b/qiskit/circuit/library/standard_gates/y.py index b4e9509903e5..65be3087c8bb 100644 --- a/qiskit/circuit/library/standard_gates/y.py +++ b/qiskit/circuit/library/standard_gates/y.py @@ -17,7 +17,7 @@ # pylint: disable=cyclic-import from qiskit.circuit.controlledgate import ControlledGate -from qiskit.circuit.singleton_gate import SingletonGate +from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array @@ -25,7 +25,7 @@ @with_gate_array(_Y_ARRAY) -class YGate(SingletonGate): +class YGate(Gate): r"""The single-qubit Pauli-Y gate (:math:`\sigma_y`). Can be applied to a :class:`~qiskit.circuit.QuantumCircuit` @@ -71,13 +71,9 @@ class YGate(SingletonGate): |1\rangle \rightarrow -i|0\rangle """ - def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None): + def __init__(self, label: Optional[str] = None): """Create new Y gate.""" - if unit is None: - unit = "dt" - super().__init__( - "y", 1, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("y", 1, [], label=label) def _define(self): # pylint: disable=cyclic-import @@ -112,7 +108,8 @@ def control( ControlledGate: controlled version of this gate. """ if num_ctrl_qubits == 1: - gate = CYGate(label=label, ctrl_state=ctrl_state, _base_label=self.label) + gate = CYGate(label=label, ctrl_state=ctrl_state) + gate.base_gate.label = self.label return gate return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @@ -178,21 +175,10 @@ class CYGate(ControlledGate): """ - def __init__( - self, - label: Optional[str] = None, - ctrl_state: Optional[Union[str, int]] = None, - _base_label=None, - ): + def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None): """Create new CY gate.""" super().__init__( - "cy", - 2, - [], - num_ctrl_qubits=1, - label=label, - ctrl_state=ctrl_state, - base_gate=YGate(label=_base_label), + "cy", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=YGate() ) def _define(self): diff --git a/qiskit/circuit/library/standard_gates/z.py b/qiskit/circuit/library/standard_gates/z.py index 9d8ba19d5fb1..978688d1908c 100644 --- a/qiskit/circuit/library/standard_gates/z.py +++ b/qiskit/circuit/library/standard_gates/z.py @@ -19,7 +19,7 @@ from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array from qiskit.circuit.controlledgate import ControlledGate -from qiskit.circuit.singleton_gate import SingletonGate +from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister from .p import PhaseGate @@ -28,7 +28,7 @@ @with_gate_array(_Z_ARRAY) -class ZGate(SingletonGate): +class ZGate(Gate): r"""The single-qubit Pauli-Z gate (:math:`\sigma_z`). Can be applied to a :class:`~qiskit.circuit.QuantumCircuit` @@ -74,13 +74,9 @@ class ZGate(SingletonGate): |1\rangle \rightarrow -|1\rangle """ - def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None): + def __init__(self, label: Optional[str] = None): """Create new Z gate.""" - if unit is None: - unit = "dt" - super().__init__( - "z", 1, [], label=label, _condition=_condition, duration=duration, unit=unit - ) + super().__init__("z", 1, [], label=label) def _define(self): # pylint: disable=cyclic-import @@ -116,7 +112,8 @@ def control( ControlledGate: controlled version of this gate. """ if num_ctrl_qubits == 1: - gate = CZGate(label=label, ctrl_state=ctrl_state, _base_label=self.label) + gate = CZGate(label=label, ctrl_state=ctrl_state) + gate.base_gate.label = self.label return gate return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @@ -163,21 +160,10 @@ class CZGate(ControlledGate): the target qubit if the control qubit is in the :math:`|1\rangle` state. """ - def __init__( - self, - label: Optional[str] = None, - ctrl_state: Optional[Union[str, int]] = None, - _base_label=None, - ): + def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None): """Create new CZ gate.""" super().__init__( - "cz", - 2, - [], - label=label, - num_ctrl_qubits=1, - ctrl_state=ctrl_state, - base_gate=ZGate(label=_base_label), + "cz", 2, [], label=label, num_ctrl_qubits=1, ctrl_state=ctrl_state, base_gate=ZGate() ) def _define(self): diff --git a/qiskit/circuit/random/utils.py b/qiskit/circuit/random/utils.py index d8c863a81d6f..b8ceb36496a7 100644 --- a/qiskit/circuit/random/utils.py +++ b/qiskit/circuit/random/utils.py @@ -193,7 +193,7 @@ def random_circuit( if is_cond: qc.measure(qc.qubits, cr) # The condition values are required to be bigints, not Numpy's fixed-width type. - operation = operation.c_if(cr, int(condition_values[c_ptr])) + operation.condition = (cr, int(condition_values[c_ptr])) c_ptr += 1 qc._append(CircuitInstruction(operation=operation, qubits=qubits[q_start:q_end])) else: diff --git a/qiskit/circuit/singleton_gate.py b/qiskit/circuit/singleton_gate.py deleted file mode 100644 index 3d24247e31ca..000000000000 --- a/qiskit/circuit/singleton_gate.py +++ /dev/null @@ -1,188 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -Singleton gate classes. -""" -import copy - -from qiskit.circuit.gate import Gate -from qiskit.circuit.classicalregister import ClassicalRegister, Clbit -from qiskit.circuit.exceptions import CircuitError - - -SINGLETONGATE_ATTR_SET = frozenset( - ( - "definition", - "unit", - "duration", - "condition", - "label", - "_label", - "_condition", - "_duration", - "_unit", - "_definition", - "_name", - "_num_qubits", - "_num_clbits", - "_params", - "params", - ) -) - - -class SingletonGate(Gate): - """A base class to use for Gate objects that by default are singleton instances - - This class should be used for gate classes that have fixed definitions and - do not contain any unique state. The canonical example of something like - this is :class:`~.HGate` which has an immutable definition and any - instance of :class:`~.HGate` is the same. Using singleton gates - as a base class for these types of gate classes provides a large - advantage in the memory footprint of multiple gates. - - The exception to be aware of with this class though are the :class:`~.Gate` - attributes :attr:`.label`, :attr:`.condition`, :attr:`.duration`, and - :attr:`.unit` which can be set differently for specific instances of gates. - For :class:`~.SingletonGate` usage to be sound setting these attributes - is not available and they can only be set at creation time. If any of these - attributes are used, then instead of using a single shared global instance - of the same gate a new separate instance will be created. - """ - - _instance = None - - def __new__(cls, *args, **kwargs): - if args or ( # pylint: disable=too-many-boolean-expressions - kwargs - and ( - "label" in kwargs - or "_condition" in kwargs - or "duration" in kwargs - or "unit" in kwargs - ) - ): - return super().__new__(cls) - if cls._instance is None: - cls._instance = super().__new__(cls) - return cls._instance - - def __init__(self, *args, _condition=None, **kwargs): - super().__init__(*args, **kwargs) - self._condition = _condition - - def c_if(self, classical, val): - if not isinstance(classical, (ClassicalRegister, Clbit)): - raise CircuitError("c_if must be used with a classical register or classical bit") - if val < 0: - raise CircuitError("condition value should be non-negative") - if isinstance(classical, Clbit): - # Casting the conditional value as Boolean when - # the classical condition is on a classical bit. - val = bool(val) - instance = type(self)( - label=self.label, _condition=(classical, val), duration=self.duration, unit=self.unit - ) - return instance - - @property - def mutable(self) -> bool: - return self is not self._instance - - def to_mutable(self): - if not self.mutable: - instance = super().__new__(type(self)) - # Coming from a shared singleton none of the arguments to - # __init__ can be set, so this is the correct behavior for - # initializing a new mutable instance - instance.__init__() - return instance - else: - return copy.deepcopy(self) - - @property - def label(self) -> str: - return self._label - - @label.setter - def label(self, label: str): - if self is self._instance: - raise NotImplementedError( - f"This gate class {type(self)} does not support manually setting a " - "label on an instance. Instead you must set the label when instantiating a new object." - ) - self._label = label - - @property - def condition(self): - return self._condition - - @condition.setter - def condition(self, condition): - if self is self._instance: - raise NotImplementedError( - f"This gate class {type(self)} does not support manually setting a " - "condition on an instance. Instead you must set the label when instantiating a new " - "object or via the .c_if() method" - ) - self._condition = condition - - @property - def duration(self): - return self._duration - - @duration.setter - def duration(self, duration): - if self is self._instance: - raise NotImplementedError( - f"This gate class {type(self)} does not support manually setting a " - "duration on an instance. Instead you must set the duration when instantiating a " - "new object." - ) - self._duration = duration - - @property - def unit(self): - return self._unit - - @unit.setter - def unit(self, unit): - if self is self._instance: - raise NotImplementedError( - f"This gate class {type(self)} does not support manually setting a " - "unit on an instance. Instead you must set the unit when instantiating a " - "new object." - ) - self._unit = unit - - def __deepcopy__(self, _memo=None): - if not self.mutable: - return self - else: - return type(self)( - label=self.label, _condition=self.condition, duration=self.duration, unit=self.unit - ) - - def __setattr__(self, name, value): - if self.mutable: - super().__setattr__(name, value) - else: - if name not in SINGLETONGATE_ATTR_SET: - raise NotImplementedError( - "Setting custom attributes is not allowed on a singleton gate" - ) - super().__setattr__(name, value) - - def copy(self, name=None): - if not self.mutable and name is None: - return self - return super().copy(name=name) diff --git a/qiskit/converters/ast_to_dag.py b/qiskit/converters/ast_to_dag.py index 9fb3361d9555..76541d8c89df 100644 --- a/qiskit/converters/ast_to_dag.py +++ b/qiskit/converters/ast_to_dag.py @@ -234,8 +234,7 @@ def _process_cnot(self, node): maxidx = max([len(id0), len(id1)]) for idx in range(maxidx): cx_gate = std.CXGate() - if self.condition: - cx_gate = cx_gate.c_if(*self.condition) + cx_gate.condition = self.condition if len(id0) > 1 and len(id1) > 1: self.dag.apply_operation_back(cx_gate, [id0[idx], id1[idx]], [], check=False) elif len(id0) > 1: @@ -253,8 +252,7 @@ def _process_measure(self, node): ) for idx, idy in zip(id0, id1): meas_gate = Measure() - if self.condition: - meas_gate = meas_gate.c_if(*self.condition) + meas_gate.condition = self.condition self.dag.apply_operation_back(meas_gate, [idx], [idy], check=False) def _process_if(self, node): @@ -343,8 +341,7 @@ def _process_node(self, node): id0 = self._process_bit_id(node.children[0]) for i, _ in enumerate(id0): reset = Reset() - if self.condition: - reset = reset.c_if(*self.condition) + reset.condition = self.condition self.dag.apply_operation_back(reset, [id0[i]], [], check=False) elif node.type == "if": @@ -401,8 +398,7 @@ def _create_dag_op(self, name, params, qargs): QiskitError: if encountering a non-basis opaque gate """ op = self._create_op(name, params) - if self.condition: - op = op.c_if(*self.condition) + op.condition = self.condition self.dag.apply_operation_back(op, qargs, [], check=False) def _create_op(self, name, params): diff --git a/qiskit/converters/circuit_to_instruction.py b/qiskit/converters/circuit_to_instruction.py index 793362281b5e..7965e3a474bc 100644 --- a/qiskit/converters/circuit_to_instruction.py +++ b/qiskit/converters/circuit_to_instruction.py @@ -81,7 +81,7 @@ def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None params=[*parameter_dict.values()], label=label, ) - out_instruction._condition = None + out_instruction.condition = None target = circuit.assign_parameters(parameter_dict, inplace=False) @@ -114,9 +114,9 @@ def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None if condition: reg, val = condition if isinstance(reg, Clbit): - rule.operation = rule.operation.c_if(clbit_map[reg], val) + rule.operation.condition = (clbit_map[reg], val) elif reg.size == c.size: - rule.operation = rule.operation.c_if(c, val) + rule.operation.condition = (c, val) else: raise QiskitError( "Cannot convert condition in circuit with " diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index e11b218efba7..f60223698e02 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -833,10 +833,7 @@ def _reject_new_register(reg): m_cargs = [edge_map.get(x, x) for x in nd.cargs] op = nd.op.copy() if (condition := getattr(op, "condition", None)) is not None: - if not isinstance(op, ControlFlowOp): - op = op.c_if(*variable_mapper.map_condition(condition, allow_reorder=True)) - else: - op.condition = variable_mapper.map_condition(condition, allow_reorder=True) + op.condition = variable_mapper.map_condition(condition, allow_reorder=True) elif isinstance(op, SwitchCaseOp): op.target = variable_mapper.map_target(op.target) dag.apply_operation_back(op, m_qargs, m_cargs, check=False) @@ -1275,11 +1272,7 @@ def substitute_node_with_dag(self, node, input_dag, wires=None, propagate_condit "cannot propagate a condition to an element that acts on those bits" ) new_op = copy.copy(in_node.op) - if new_condition: - if not isinstance(new_op, ControlFlowOp): - new_op = new_op.c_if(*new_condition) - else: - new_op.condition = new_condition + new_op.condition = new_condition in_dag.apply_operation_back(new_op, in_node.qargs, in_node.cargs, check=False) else: in_dag = input_dag @@ -1363,13 +1356,8 @@ def edge_weight_map(wire): label=old_node.op.label, ) elif getattr(old_node.op, "condition", None) is not None: - m_op = old_node.op - if not isinstance(old_node.op, ControlFlowOp): - new_condition = variable_mapper.map_condition(m_op.condition) - if new_condition is not None: - m_op = m_op.c_if(*new_condition) - else: - m_op.condition = variable_mapper.map_condition(m_op.condition) + m_op = copy.copy(old_node.op) + m_op.condition = variable_mapper.map_condition(m_op.condition) else: m_op = old_node.op m_qargs = [wire_map[x] for x in old_node.qargs] @@ -1442,10 +1430,7 @@ def substitute_node(self, node, op, inplace=False, propagate_condition=True): if (old_condition := getattr(node.op, "condition", None)) is not None: if not isinstance(op, Instruction): raise DAGCircuitError("Cannot add a condition on a generic Operation.") - if not isinstance(node.op, ControlFlowOp): - op = op.c_if(*old_condition) - else: - op.condition = old_condition + op.condition = old_condition new_wires.update(condition_resources(old_condition).clbits) if new_wires != current_wires: diff --git a/qiskit/qasm2/parse.py b/qiskit/qasm2/parse.py index 116c6b7c9aa0..6f56d31b0a02 100644 --- a/qiskit/qasm2/parse.py +++ b/qiskit/qasm2/parse.py @@ -227,7 +227,8 @@ def from_bytecode(bytecode, custom_instructions: Iterable[CustomInstruction]): ) elif opcode == OpCode.ConditionedGate: gate_id, parameters, op_qubits, creg, value = op.operands - gate = gates[gate_id](*parameters).c_if(qc.cregs[creg], value) + gate = gates[gate_id](*parameters) + gate.condition = (qc.cregs[creg], value) qc._append(CircuitInstruction(gate, [qubits[q] for q in op_qubits])) elif opcode == OpCode.Measure: qubit, clbit = op.operands diff --git a/qiskit/qpy/binary_io/circuits.py b/qiskit/qpy/binary_io/circuits.py index 91fe62f3e368..5266bbb0346b 100644 --- a/qiskit/qpy/binary_io/circuits.py +++ b/qiskit/qpy/binary_io/circuits.py @@ -25,11 +25,10 @@ from qiskit import circuit as circuit_mod from qiskit import extensions -from qiskit.circuit import library, controlflow, CircuitInstruction, ControlFlowOp +from qiskit.circuit import library, controlflow, CircuitInstruction from qiskit.circuit.classical import expr from qiskit.circuit.classicalregister import ClassicalRegister, Clbit from qiskit.circuit.gate import Gate -from qiskit.circuit.singleton_gate import SingletonGate from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.instruction import Instruction from qiskit.circuit.quantumcircuit import QuantumCircuit @@ -273,10 +272,8 @@ def _read_instruction(file_obj, circuit, registers, custom_operations, version, else: raise AttributeError("Invalid instruction type: %s" % gate_name) - if instruction.label_size <= 0: - label = None if gate_name in {"IfElseOp", "WhileLoopOp"}: - gate = gate_class(condition, *params, label=label) + gate = gate_class(condition, *params) elif version >= 5 and issubclass(gate_class, ControlledGate): if gate_name in { "MCPhaseGate", @@ -286,9 +283,9 @@ def _read_instruction(file_obj, circuit, registers, custom_operations, version, "MCXRecursive", "MCXVChain", }: - gate = gate_class(*params, instruction.num_ctrl_qubits, label=label) + gate = gate_class(*params, instruction.num_ctrl_qubits) else: - gate = gate_class(*params, label=label) + gate = gate_class(*params) gate.num_ctrl_qubits = instruction.num_ctrl_qubits gate.ctrl_state = instruction.ctrl_state gate.condition = condition @@ -307,19 +304,10 @@ def _read_instruction(file_obj, circuit, registers, custom_operations, version, params = [len(qargs)] elif gate_name in {"BreakLoopOp", "ContinueLoopOp"}: params = [len(qargs), len(cargs)] - if label is not None: - if issubclass(gate_class, SingletonGate): - gate = gate_class(*params, label=label) - else: - gate = gate_class(*params) - gate.label = label - else: - gate = gate_class(*params) - if condition: - if not isinstance(gate, ControlFlowOp): - gate = gate.c_if(*condition) - else: - gate.condition = condition + gate = gate_class(*params) + gate.condition = condition + if instruction.label_size > 0: + gate.label = label if circuit is None: return gate if not isinstance(gate, Instruction): diff --git a/qiskit/transpiler/passes/optimization/reset_after_measure_simplification.py b/qiskit/transpiler/passes/optimization/reset_after_measure_simplification.py index d49485784026..4445e878ce6b 100644 --- a/qiskit/transpiler/passes/optimization/reset_after_measure_simplification.py +++ b/qiskit/transpiler/passes/optimization/reset_after_measure_simplification.py @@ -36,7 +36,8 @@ def run(self, dag): for node in dag.op_nodes(Measure): succ = next(dag.quantum_successors(node)) if isinstance(succ, DAGOpNode) and isinstance(succ.op, Reset): - new_x = XGate().c_if(node.cargs[0], 1) + new_x = XGate() + new_x.condition = (node.cargs[0], 1) new_dag = DAGCircuit() new_dag.add_qubits(node.qargs) new_dag.add_clbits(node.cargs) diff --git a/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py b/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py index bc606a0f7161..7e5245b2e3fc 100644 --- a/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py +++ b/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py @@ -192,11 +192,8 @@ def run(self, dag): index_sequence_duration_map = {} for physical_qubit in self._qubits: dd_sequence_duration = 0 - for index, gate in enumerate(self._dd_sequence): - gate = gate.to_mutable() - self._dd_sequence[index] = gate + for gate in self._dd_sequence: gate.duration = self._durations.get(gate, physical_qubit) - dd_sequence_duration += gate.duration index_sequence_duration_map[physical_qubit] = dd_sequence_duration diff --git a/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py b/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py index 2d4114f3cfc7..fb33361a4959 100644 --- a/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +++ b/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py @@ -224,7 +224,7 @@ def _pre_runhook(self, dag: DAGCircuit): continue sequence_lengths = [] - for index, gate in enumerate(self._dd_sequence): + for gate in self._dd_sequence: try: # Check calibration. params = self._resolve_params(gate) @@ -246,8 +246,6 @@ def _pre_runhook(self, dag: DAGCircuit): gate_length = self._durations.get(gate, physical_index) sequence_lengths.append(gate_length) # Update gate duration. This is necessary for current timeline drawer, i.e. scheduled. - gate = gate.to_mutable() - self._dd_sequence[index] = gate gate.duration = gate_length self._dd_sequence_lengths[qubit] = sequence_lengths diff --git a/qiskit/transpiler/passes/scheduling/scheduling/base_scheduler.py b/qiskit/transpiler/passes/scheduling/scheduling/base_scheduler.py index 3792a149fd71..f83aed800009 100644 --- a/qiskit/transpiler/passes/scheduling/scheduling/base_scheduler.py +++ b/qiskit/transpiler/passes/scheduling/scheduling/base_scheduler.py @@ -70,7 +70,6 @@ def _get_node_duration( duration = dag.calibrations[node.op.name][cal_key].duration # Note that node duration is updated (but this is analysis pass) - node.op = node.op.to_mutable() node.op.duration = duration else: duration = node.op.duration diff --git a/qiskit/transpiler/passes/scheduling/time_unit_conversion.py b/qiskit/transpiler/passes/scheduling/time_unit_conversion.py index c75e22f285b8..a176f0aa5c9a 100644 --- a/qiskit/transpiler/passes/scheduling/time_unit_conversion.py +++ b/qiskit/transpiler/passes/scheduling/time_unit_conversion.py @@ -96,14 +96,13 @@ def run(self, dag: DAGCircuit): # Make units consistent for node in dag.op_nodes(): try: - duration = self.inst_durations.get( + node.op = node.op.copy() + node.op.duration = self.inst_durations.get( node.op, [dag.find_bit(qarg).index for qarg in node.qargs], unit=time_unit ) + node.op.unit = time_unit except TranspilerError: - continue - node.op = node.op.to_mutable() - node.op.duration = duration - node.op.unit = time_unit + pass self.property_set["time_unit"] = time_unit return dag diff --git a/releasenotes/notes/singletons-83782de8bd062cbc.yaml b/releasenotes/notes/singletons-83782de8bd062cbc.yaml deleted file mode 100644 index db6b761a392a..000000000000 --- a/releasenotes/notes/singletons-83782de8bd062cbc.yaml +++ /dev/null @@ -1,122 +0,0 @@ ---- -features: - - | - Introduced a new class :class:`~.SingletonGate` which is a subclass of - :class:`~.Gate` that uses a single instance for all objects of that type. - The intent behind this class is to minimize the memory and construction - overhead of using multiple gates in a circuit with the tradeoff of having - global shared state. For this reason this class is only applicable to - gates that do not have any unique and/or mutable state stored in an instance. - For example, the best example of this is :class:`.XGate` doesn't contain - any state and could leveerage :class:`~.SingletonGate` (and does starting in - this release), while :class:`~.RXGate` stores an angle parameter in an instance - and thus can not use :class:`~.SingletonGate` because a single shared global - instance can not represent the parameter values. - - The other potential issue to be aware of when using this class is around the - use of the :class:`~.SingletonGate` class is that the :class:`~.Gate` - data model supports some mutable state. Specifically, the - :attr:`~.Gate.label`, :attr:`~.Gate.duration`, :attr:`~.Gate.unit`, and - :attr:`~.Gate.condition` attributes are all accessible and mutable in the - :class:`~.Gate` and its direct subclasses. However, this is incompatible - with having a shared object via :class:`~.SingletonGate`. For instances of - :class:`~.SingletonGate` setting these attributes directly is not allowed - and it will raise an exception. If they are needed for a particular - instance you must set them on the constructor (or via - :meth:`~.SingletonGate.c_if` for :attr:`~.SingletonGate.condition`) when - creating a new object. When this is done the output from the constructor - will be a separate instance with the custom state instead of the globally - shared instance. You can also use the :meth:`.SingletonGate.to_mutable` - method to get a mutable copy of a gate object and then mutate the attributes - like you would on any other :class:`~.circuit.Instruction` object. - - | - The following standard library gates are now instances of - :class:`~.SingletonGate`: - - * :class:`~.DCXGate` - * :class:`~.ECRGate` - * :class:`~.HGate` - * :class:`~.IGate` - * :class:`~.iSwapGate` - * :class:`~.SGate` - * :class:`~.SdgGate` - * :class:`~.SwapGate` - * :class:`~.SXGate` - * :class:`~.SXdgGate` - * :class:`~.TGate` - * :class:`~.TdgGate` - * :class:`~.XGate` - * :class:`~.RCCXGate` - * :class:`~.RC3XGate` - * :class:`~.YGate` - * :class:`~.ZGate` - - This means that unless a ``label``, ``condition``, ``duration``, or ``unit`` - are set on the instance at creation time they will all share a single global - instance whenever a new gate object is created. This results in large reduction - in the memory overhead for > 1 object of these types and significantly faster - object construction time. - - | - Added a new method :meth`.Instruction.to_mutable` and attribute - :attr:`.Instruction.mutable` which is used to get a mutable copy and check whether - an :class:`~.circuit.Instruction` object is mutable. With the introduction - of :class:`~.SingletonGate` these methods can be used to have a unified interface - to deal with the mutablitiy of instruction objects. -upgrade: - - | - The following standard library gates: - - * :class:`~.DCXGate` - * :class:`~.ECRGate` - * :class:`~.HGate` - * :class:`~.IGate` - * :class:`~.iSwapGate` - * :class:`~.SGate` - * :class:`~.SdgGate` - * :class:`~.SwapGate` - * :class:`~.SXGate` - * :class:`~.SXdgGate` - * :class:`~.TGate` - * :class:`~.TdgGate` - * :class:`~.XGate` - * :class:`~.RCCXGate` - * :class:`~.RC3XGate` - * :class:`~.YGate` - * :class:`~.ZGate` - - no longer are able to set :attr:`~.Gate.label`, :attr:`~.Gate.condition`, - :attr:`~.Gate.duration`, or :attr:`~.Gate.unit` after instantiating an object - anymore. You will now only be able to set these attributes as arguments - when creating a new object or in the case of :attr:`~.Gate.condtion` through - the use :meth:`~.Gate.c_if`. Alternatively you can use :meth:`~.Gate.to_mutable` - to get a mutable copy of the instruction and then use the setter on that copy - instead of the original object. This change was necssary as part of converting - these classes to be :class:`~.SingletonGate` types which greatly reduces the - memory footprint of repeated instances of these gates. - - | - For anything that interacts with :class:`~.Gate`, :class:`~.Operation`, - or :class:`~.circuit.Instruction` objects or works with these as part of a - :class:`~.QuantumCircuit` or :class:`~.DAGCircuit` classes it is important - to note that the use of shared references for instances is much more common - now. Previously, it was possible to reuse and share an instance of a - a circuit operation it wasn't very commonly used and a copy would generate - a unique instance. This has changed starting in this release because of - :class:`~.SingletonGate` being made available (and a large number of standard - library gates now built off of it). If your usage of these objects is assuming - unique instances for every circuit operation there are potential issue because - of shared state that will be reused between operations of the same type (that - will persist through copy and deep copies). You can rely on the - :attr:`.Instruction.mutable` attribute to check the mutability of an object or - use :meth:`.Instruction.to_mutable` to get a mutable copy of any instruction. -fixes: - - | - Fixed an oversight in the :class:`~.ECRGate` that prevented setting an - :attr:`.ECRGate.label` attribute at object construction time. All other - :class:`~.Gate` classes and subclasses enable setting a ``label`` keyword - argument in the constructor. - - | - Fixed an oversight in the :class:`~.Gate` (and all its subclasses) constructor - where the :attr:`.Gate.duration` and :attr:`.Gate.unit` attributes could not - be set as keyword arguments during construction. The parent class - :class:`~.circuit.Instruction` supported setting this but :class:`~.Gate` was - previously not exposing this interface correctly. diff --git a/test/python/circuit/gate_utils.py b/test/python/circuit/gate_utils.py index 557059c32fb4..83e884bb724b 100644 --- a/test/python/circuit/gate_utils.py +++ b/test/python/circuit/gate_utils.py @@ -25,7 +25,7 @@ def _get_free_params(fun, ignore=None): Returns: list[str]: The name of the free parameters not listed in ``ignore``. """ - ignore = ignore or ["kwargs"] + ignore = ignore or [] free_params = [] for name, param in signature(fun).parameters.items(): if param.default == Parameter.empty and param.kind != Parameter.VAR_POSITIONAL: diff --git a/test/python/circuit/test_circuit_load_from_qpy.py b/test/python/circuit/test_circuit_load_from_qpy.py index 825e46a8ddbc..1e2f5263ce34 100644 --- a/test/python/circuit/test_circuit_load_from_qpy.py +++ b/test/python/circuit/test_circuit_load_from_qpy.py @@ -609,7 +609,8 @@ def test_custom_instruction_with_noop_definition(self): def test_standard_gate_with_label(self): """Test a standard gate with a label.""" qc = QuantumCircuit(1) - gate = XGate(label="My special X gate") + gate = XGate() + gate.label = "My special X gate" qc.append(gate, [0]) qpy_file = io.BytesIO() dump(qc, qpy_file) diff --git a/test/python/circuit/test_gate_definitions.py b/test/python/circuit/test_gate_definitions.py index 9589a68c7614..0c41189fc954 100644 --- a/test/python/circuit/test_gate_definitions.py +++ b/test/python/circuit/test_gate_definitions.py @@ -282,7 +282,6 @@ class TestGateEquivalenceEqual(QiskitTestCase): "PermutationGate", "Commuting2qBlock", "PauliEvolutionGate", - "SingletonGate", "_U0Gate", "_DefinedGate", } diff --git a/test/python/circuit/test_instructions.py b/test/python/circuit/test_instructions.py index f5f5e14d5130..8c8e9866050f 100644 --- a/test/python/circuit/test_instructions.py +++ b/test/python/circuit/test_instructions.py @@ -10,8 +10,6 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -# pylint: disable=unsubscriptable-object - """Test Qiskit's Instruction class.""" import unittest.mock @@ -24,7 +22,6 @@ from qiskit.circuit import QuantumCircuit from qiskit.circuit import QuantumRegister, ClassicalRegister, Qubit, Clbit from qiskit.circuit.library.standard_gates.h import HGate -from qiskit.circuit.library.standard_gates.rz import RZGate from qiskit.circuit.library.standard_gates.x import CXGate from qiskit.circuit.library.standard_gates.s import SGate from qiskit.circuit.library.standard_gates.t import TGate @@ -542,21 +539,21 @@ def test_instructionset_c_if_with_no_requester(self): arbitrary :obj:`.Clbit` and `:obj:`.ClassicalRegister` instances, but rejects integers.""" with self.subTest("accepts arbitrary register"): - instruction = RZGate(0) + instruction = HGate() instructions = InstructionSet() instructions.add(instruction, [Qubit()], []) register = ClassicalRegister(2) instructions.c_if(register, 0) self.assertIs(instruction.condition[0], register) with self.subTest("accepts arbitrary bit"): - instruction = RZGate(0) + instruction = HGate() instructions = InstructionSet() instructions.add(instruction, [Qubit()], []) bit = Clbit() instructions.c_if(bit, 0) self.assertIs(instruction.condition[0], bit) with self.subTest("rejects index"): - instruction = RZGate(0) + instruction = HGate() instructions = InstructionSet() instructions.add(instruction, [Qubit()], []) with self.assertRaisesRegex(CircuitError, r"Cannot pass an index as a condition .*"): @@ -581,7 +578,7 @@ def dummy_requester(specifier): with self.subTest("calls requester with bit"): dummy_requester.reset_mock() - instruction = RZGate(0) + instruction = HGate() instructions = InstructionSet(resource_requester=dummy_requester) instructions.add(instruction, [Qubit()], []) bit = Clbit() @@ -590,7 +587,7 @@ def dummy_requester(specifier): self.assertIs(instruction.condition[0], sentinel_bit) with self.subTest("calls requester with index"): dummy_requester.reset_mock() - instruction = RZGate(0) + instruction = HGate() instructions = InstructionSet(resource_requester=dummy_requester) instructions.add(instruction, [Qubit()], []) index = 0 @@ -599,7 +596,7 @@ def dummy_requester(specifier): self.assertIs(instruction.condition[0], sentinel_bit) with self.subTest("calls requester with register"): dummy_requester.reset_mock() - instruction = RZGate(0) + instruction = HGate() instructions = InstructionSet(resource_requester=dummy_requester) instructions.add(instruction, [Qubit()], []) register = ClassicalRegister(2) @@ -608,7 +605,7 @@ def dummy_requester(specifier): self.assertIs(instruction.condition[0], sentinel_register) with self.subTest("calls requester only once when broadcast"): dummy_requester.reset_mock() - instruction_list = [RZGate(0), RZGate(0), RZGate(0)] + instruction_list = [HGate(), HGate(), HGate()] instructions = InstructionSet(resource_requester=dummy_requester) for instruction in instruction_list: instructions.add(instruction, [Qubit()], []) @@ -628,7 +625,7 @@ def test_label_type_enforcement(self): Instruction("h", 1, 0, [], label=0) with self.subTest("raises when a non-string label is provided to setter"): with self.assertRaisesRegex(TypeError, r"label expects a string or None"): - instruction = RZGate(0) + instruction = HGate() instruction.label = 0 def test_deprecation_warnings_qasm_methods(self): diff --git a/test/python/circuit/test_singleton_gate.py b/test/python/circuit/test_singleton_gate.py deleted file mode 100644 index d8c80661d675..000000000000 --- a/test/python/circuit/test_singleton_gate.py +++ /dev/null @@ -1,253 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# pylint: disable=missing-function-docstring - - -""" -Tests for singleton gate behavior -""" - -import copy - -from qiskit.circuit.library import HGate, SXGate -from qiskit.circuit import Clbit, QuantumCircuit, QuantumRegister, ClassicalRegister -from qiskit.converters import dag_to_circuit, circuit_to_dag - -from qiskit.test.base import QiskitTestCase - - -class TestSingletonGate(QiskitTestCase): - """Qiskit SingletonGate tests.""" - - def test_default_singleton(self): - gate = HGate() - new_gate = HGate() - self.assertIs(gate, new_gate) - - def test_label_not_singleton(self): - gate = HGate() - label_gate = HGate(label="special") - self.assertIsNot(gate, label_gate) - - def test_condition_not_singleton(self): - gate = HGate() - condition_gate = HGate().c_if(Clbit(), 0) - self.assertIsNot(gate, condition_gate) - - def test_raise_on_state_mutation(self): - gate = HGate() - with self.assertRaises(NotImplementedError): - gate.label = "foo" - with self.assertRaises(NotImplementedError): - gate.condition = (Clbit(), 0) - - def test_labeled_condition(self): - singleton_gate = HGate() - clbit = Clbit() - gate = HGate(label="conditionally special").c_if(clbit, 0) - self.assertIsNot(singleton_gate, gate) - self.assertEqual(gate.label, "conditionally special") - self.assertEqual(gate.condition, (clbit, 0)) - - def test_default_singleton_copy(self): - gate = HGate() - copied = gate.copy() - self.assertIs(gate, copied) - - def test_label_copy(self): - gate = HGate(label="special") - copied = gate.copy() - self.assertIsNot(gate, copied) - self.assertEqual(gate, copied) - - def test_label_copy_new(self): - gate = HGate() - label_gate = HGate(label="special") - self.assertIsNot(gate, label_gate) - self.assertNotEqual(gate.label, label_gate.label) - copied = gate.copy() - copied_label = label_gate.copy() - self.assertIs(gate, copied) - self.assertIsNot(copied, label_gate) - self.assertIsNot(copied_label, gate) - self.assertIsNot(copied_label, label_gate) - self.assertNotEqual(copied.label, label_gate.label) - self.assertEqual(copied_label, label_gate) - self.assertNotEqual(copied.label, "special") - self.assertEqual(copied_label.label, "special") - - def test_condition_copy(self): - gate = HGate().c_if(Clbit(), 0) - copied = gate.copy() - self.assertIsNot(gate, copied) - self.assertEqual(gate, copied) - - def test_condition_label_copy(self): - clbit = Clbit() - gate = HGate(label="conditionally special").c_if(clbit, 0) - copied = gate.copy() - self.assertIsNot(gate, copied) - self.assertEqual(gate, copied) - self.assertEqual(copied.label, "conditionally special") - self.assertEqual(copied.condition, (clbit, 0)) - - def test_deepcopy(self): - gate = HGate() - copied = copy.deepcopy(gate) - self.assertIs(gate, copied) - - def test_deepcopy_with_label(self): - gate = HGate(label="special") - copied = copy.deepcopy(gate) - self.assertIsNot(gate, copied) - self.assertEqual(gate, copied) - self.assertEqual(copied.label, "special") - - def test_deepcopy_with_condition(self): - gate = HGate().c_if(Clbit(), 0) - copied = copy.deepcopy(gate) - self.assertIsNot(gate, copied) - self.assertEqual(gate, copied) - - def test_condition_label_deepcopy(self): - clbit = Clbit() - gate = HGate(label="conditionally special").c_if(clbit, 0) - copied = copy.deepcopy(gate) - self.assertIsNot(gate, copied) - self.assertEqual(gate, copied) - self.assertEqual(copied.label, "conditionally special") - self.assertEqual(copied.condition, (clbit, 0)) - - def test_label_deepcopy_new(self): - gate = HGate() - label_gate = HGate(label="special") - self.assertIsNot(gate, label_gate) - self.assertNotEqual(gate.label, label_gate.label) - copied = copy.deepcopy(gate) - copied_label = copy.deepcopy(label_gate) - self.assertIs(gate, copied) - self.assertIsNot(copied, label_gate) - self.assertIsNot(copied_label, gate) - self.assertIsNot(copied_label, label_gate) - self.assertNotEqual(copied.label, label_gate.label) - self.assertEqual(copied_label, label_gate) - self.assertNotEqual(copied.label, "special") - self.assertEqual(copied_label.label, "special") - - def test_control_a_singleton(self): - singleton_gate = HGate() - gate = HGate(label="special") - ch = gate.control(label="my_ch") - self.assertEqual(ch.base_gate.label, "special") - self.assertIsNot(ch.base_gate, singleton_gate) - - def test_round_trip_dag_conversion(self): - qc = QuantumCircuit(1) - gate = HGate() - qc.append(gate, [0]) - dag = circuit_to_dag(qc) - out = dag_to_circuit(dag) - self.assertIs(qc.data[0].operation, out.data[0].operation) - - def test_round_trip_dag_conversion_with_label(self): - gate = HGate(label="special") - qc = QuantumCircuit(1) - qc.append(gate, [0]) - dag = circuit_to_dag(qc) - out = dag_to_circuit(dag) - self.assertIsNot(qc.data[0].operation, out.data[0].operation) - self.assertEqual(qc.data[0].operation, out.data[0].operation) - self.assertEqual(out.data[0].operation.label, "special") - - def test_round_trip_dag_conversion_with_condition(self): - qc = QuantumCircuit(1, 1) - gate = HGate().c_if(qc.cregs[0], 0) - qc.append(gate, [0]) - dag = circuit_to_dag(qc) - out = dag_to_circuit(dag) - self.assertIsNot(qc.data[0].operation, out.data[0].operation) - self.assertEqual(qc.data[0].operation, out.data[0].operation) - self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) - - def test_round_trip_dag_conversion_condition_label(self): - qc = QuantumCircuit(1, 1) - gate = HGate(label="conditionally special").c_if(qc.cregs[0], 0) - qc.append(gate, [0]) - dag = circuit_to_dag(qc) - out = dag_to_circuit(dag) - self.assertIsNot(qc.data[0].operation, out.data[0].operation) - self.assertEqual(qc.data[0].operation, out.data[0].operation) - self.assertEqual(out.data[0].operation.condition, (qc.cregs[0], 0)) - self.assertEqual(out.data[0].operation.label, "conditionally special") - - def test_condition_via_instructionset(self): - gate = HGate() - qr = QuantumRegister(2, "qr") - cr = ClassicalRegister(1, "cr") - circuit = QuantumCircuit(qr, cr) - circuit.h(qr[0]).c_if(cr, 1) - self.assertIsNot(gate, circuit.data[0].operation) - self.assertEqual(circuit.data[0].operation.condition, (cr, 1)) - - def test_is_mutable(self): - gate = HGate() - self.assertFalse(gate.mutable) - label_gate = HGate(label="foo") - self.assertTrue(label_gate.mutable) - self.assertIsNot(gate, label_gate) - - def test_to_mutable(self): - gate = HGate() - self.assertFalse(gate.mutable) - new_gate = gate.to_mutable() - self.assertTrue(new_gate.mutable) - self.assertIsNot(gate, new_gate) - - def test_to_mutable_setter(self): - gate = HGate() - self.assertFalse(gate.mutable) - mutable_gate = gate.to_mutable() - mutable_gate.label = "foo" - mutable_gate.duration = 3 - mutable_gate.unit = "s" - clbit = Clbit() - mutable_gate.condition = (clbit, 0) - self.assertTrue(mutable_gate.mutable) - self.assertIsNot(gate, mutable_gate) - self.assertEqual(mutable_gate.label, "foo") - self.assertEqual(mutable_gate.duration, 3) - self.assertEqual(mutable_gate.unit, "s") - self.assertEqual(mutable_gate.condition, (clbit, 0)) - - def test_to_mutable_of_mutable_instance(self): - gate = HGate(label="foo") - mutable_copy = gate.to_mutable() - self.assertIsNot(gate, mutable_copy) - self.assertEqual(mutable_copy.label, gate.label) - mutable_copy.label = "not foo" - self.assertNotEqual(mutable_copy.label, gate.label) - - def test_set_custom_attr(self): - gate = SXGate() - with self.assertRaises(NotImplementedError): - gate.custom_foo = 12345 - mutable_gate = gate.to_mutable() - self.assertTrue(mutable_gate.mutable) - mutable_gate.custom_foo = 12345 - self.assertEqual(12345, mutable_gate.custom_foo) - - def test_positional_label(self): - gate = SXGate() - label_gate = SXGate("I am a little label") - self.assertIsNot(gate, label_gate) - self.assertEqual(label_gate.label, "I am a little label") diff --git a/test/python/dagcircuit/test_dagcircuit.py b/test/python/dagcircuit/test_dagcircuit.py index 35576d9b4361..7f7375178bb0 100644 --- a/test/python/dagcircuit/test_dagcircuit.py +++ b/test/python/dagcircuit/test_dagcircuit.py @@ -477,7 +477,8 @@ def setUp(self): def test_apply_operation_back(self): """The apply_operation_back() method.""" - x_gate = XGate().c_if(*self.condition) + x_gate = XGate() + x_gate.condition = self.condition self.dag.apply_operation_back(HGate(), [self.qubit0], []) self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Measure(), [self.qubit1], [self.clbit1]) @@ -489,7 +490,8 @@ def test_apply_operation_back(self): def test_edges(self): """Test that DAGCircuit.edges() behaves as expected with ops.""" - x_gate = XGate().c_if(*self.condition) + x_gate = XGate() + x_gate.condition = self.condition self.dag.apply_operation_back(HGate(), [self.qubit0], []) self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) self.dag.apply_operation_back(Measure(), [self.qubit1], [self.clbit1]) @@ -507,7 +509,8 @@ def test_apply_operation_back_conditional(self): # Single qubit gate conditional: qc.h(qr[2]).c_if(cr, 3) - h_gate = HGate().c_if(*self.condition) + h_gate = HGate() + h_gate.condition = self.condition h_node = self.dag.apply_operation_back(h_gate, [self.qubit2], []) self.assertEqual(h_node.qargs, (self.qubit2,)) @@ -547,7 +550,8 @@ def test_apply_operation_back_conditional_measure(self): new_creg = ClassicalRegister(1, "cr2") self.dag.add_creg(new_creg) - meas_gate = Measure().c_if(new_creg, 0) + meas_gate = Measure() + meas_gate.condition = (new_creg, 0) meas_node = self.dag.apply_operation_back(meas_gate, [self.qubit0], [self.clbit0]) self.assertEqual(meas_node.qargs, (self.qubit0,)) @@ -592,7 +596,8 @@ def test_apply_operation_back_conditional_measure_to_self(self): # Measure targeting a clbit which _is_ a member of the conditional # register. qc.measure(qr[0], cr[0]).c_if(cr, 3) - meas_gate = Measure().c_if(*self.condition) + meas_gate = Measure() + meas_gate.condition = self.condition meas_node = self.dag.apply_operation_back(meas_gate, [self.qubit1], [self.clbit1]) self.assertEqual(meas_node.qargs, (self.qubit1,)) @@ -1148,7 +1153,8 @@ def test_dag_collect_runs(self): def test_dag_collect_runs_start_with_conditional(self): """Test collect runs with a conditional at the start of the run.""" - h_gate = HGate().c_if(*self.condition) + h_gate = HGate() + h_gate.condition = self.condition self.dag.apply_operation_back(h_gate, [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) @@ -1161,7 +1167,8 @@ def test_dag_collect_runs_start_with_conditional(self): def test_dag_collect_runs_conditional_in_middle(self): """Test collect_runs with a conditional in the middle of a run.""" - h_gate = HGate().c_if(*self.condition) + h_gate = HGate() + h_gate.condition = self.condition self.dag.apply_operation_back(HGate(), [self.qubit0]) self.dag.apply_operation_back(h_gate, [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) @@ -1203,7 +1210,8 @@ def test_dag_collect_1q_runs_start_with_conditional(self): """Test collect 1q runs with a conditional at the start of the run.""" self.dag.apply_operation_back(Reset(), [self.qubit0]) self.dag.apply_operation_back(Delay(100), [self.qubit0]) - h_gate = HGate().c_if(*self.condition) + h_gate = HGate() + h_gate.condition = self.condition self.dag.apply_operation_back(h_gate, [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) @@ -1218,7 +1226,8 @@ def test_dag_collect_1q_runs_conditional_in_middle(self): """Test collect_1q_runs with a conditional in the middle of a run.""" self.dag.apply_operation_back(Reset(), [self.qubit0]) self.dag.apply_operation_back(Delay(100), [self.qubit0]) - h_gate = HGate().c_if(*self.condition) + h_gate = HGate() + h_gate.condition = self.condition self.dag.apply_operation_back(HGate(), [self.qubit0]) self.dag.apply_operation_back(h_gate, [self.qubit0]) self.dag.apply_operation_back(HGate(), [self.qubit0]) @@ -1296,7 +1305,8 @@ def test_layers_basic(self): qubit1 = qreg[1] clbit0 = creg[0] clbit1 = creg[1] - x_gate = XGate().c_if(creg, 3) + x_gate = XGate() + x_gate.condition = (creg, 3) dag = DAGCircuit() dag.add_qreg(qreg) dag.add_creg(creg) @@ -2125,8 +2135,10 @@ def test_substitute_with_provided_wire_map_propagate_condition(self): sub.cx(0, 1) sub.h(0) - conditioned_h = HGate().c_if(*conditioned_cz.condition) - conditioned_cx = CXGate().c_if(*conditioned_cz.condition) + conditioned_h = HGate() + conditioned_h.condition = conditioned_cz.condition + conditioned_cx = CXGate() + conditioned_cx.condition = conditioned_cz.condition expected = DAGCircuit() expected.add_qubits(base_qubits)