Use singletons for standard library unparameterized, non-controlled gates (#10314)

* Use singletons for standard library unparameterized, non-controlled gates

This commit adds a new class SingletonGate which is a Gate subclass that
reuses a single instance by default for all instances of a particular
class. This greatly reduces the memory overhead and significant improves
the construction speed for making multiple instances of the same gate.
The tradeoff is in the flexibility of use because it precludes having
any potentially mutable state in the shared instance. This is a large
change to the data model of qiskit because it previously could be
assumed that any gate instance was unique and there weren't any
unintended side effects from modifying it in isolation (for example
doing XGate().label = 'foo' wouldn't potentially break other code).
To limit the impact around this instances of SingletonGate do not allow
mutation of an existing instance. This can (and likely will) cause
unexpected issues as usage of the class is released. Specifically what
used to be valid will now raise an exception because it is a shared
instance. This is evident from the code modifications necessary to
most of the Qiskit code base to enable working with instances of
SingletonGates. The knock on effects of this downstream are likely
significant and managing how we roll this feature out is going to be
equally if not more important than the feature itself. This is why
I'm not personally convinced we want to do all this commit includes
in a single release. I've opened this as a pull request primarily to
start the conversation on how we want to do the roll out to try and
minimize and notify downstream users of the potential breakage to
avoid issues. The primary issue I have is this doesn't really follow
the Qiskit deprecation policy as there is no user facing notification
or documentation of this pending change and code that worked in the
previously release will not work in the release with this feature.
For some aspects of this change (primarily the setters on gate
attributes) this can easily be handled by deprecating it in planned
singleton standard library gates and waiting the necessary amount of
time. But the more fundamental data model changes are hard to announce
ahead of time. We can have a release note about it coming in the future
but it will end up being very abstract and users will not necessarily
be able to act on it ahead of time without concrete examples to test
with. This was an issue for me in developing this feature as I couldn't
anticipate where API breakages would occur until I switched over all the
standard library gates, and there still might be others.

Due to the large performance gains this offers and also in the
interest of testing the API implications of using singleton gates the
unparameterized and non-controlled gates available in
qiskit.circuit.library.standard_gates are all updated to be subclasses
of singleton gates. In aggregate this is causing construction to be
roughly 6x faster and building circuits comprised solely of these gates
consume 1/4th the memory as before. But it also exposed a large number
of internal changes we needed to make to the wider circuit, QPY, qasm2,
dagcircuit, transpiler, converters, and test modules to support working
with singleton gates.

Besides this there are a couple seemingly unrelated API changes in
this PR and it is caused by inconsistencies in the Instruction/Gate
API that were preventing this from working. The first which is the
ECRGate class was missing a label kwarg in the parent. Similarly
all Gate classes and subclasses were missing duration and unit
kwargs on their constructors. These are necessary to be able to use
these fields on singletons because we need an interface to construct
an instance that has the state set so we avoid the use of the global
shared instance. In the release notes I labeled these as bugfixes,
because in all the cases the parent clases were exposing these
interfaces and it primarily is an oversight that they were missing
in these places. But personally this does seem more like something
we'd normally document as a feature rather than a bugfix.

A follow up PR will add a SingletonControlledGate class which will
be similar to SingletonGate but will offer two singleton instance
based on the value of ctrl_state (and also handle nested labels
and other nested mutable state in the base gate). We can then update
the standard library gates like CXGate, and CHGate to also be
singletons. The ctrl state attribute is primarily why these gates
were not included in this commit.

* Fix Python 3.8 compatibility

There are some differences in how the inspect stdlib module behaves
between python 3.8 and newer versions of python. This was causing
divergence in the test and qpy behavior where inspect was used to
determine different aspects of a gate (either whether label was
supported as an arg or find the number of free parameters). This commit
fixes this by adjusting the code to handle both newer versions of
inspect as well as older ones.

* Simplify qpy deserialization label handling

* Remove unused classmethod decorator

* Fix lint

* Fix timeline drawer on output of legacy DD pass

* Fix doc build

* Add methods to deal with mutability of singleton gates

This commit adds two methods to the SingletonGate class, mutable and
to_mutable. The mutable() method is a property method that returns
whether a particular instance is mutable or a shared singleton instance.
The second method to_mutable() returns a mutable copy of the gate.

* Disallow custom attribute on singleton instances

This commit adds a __setattr__ method to the singleton gate class to
ensure that custom attributes are not settable for a shared singleton
instance. It prevents addign a custom attribute if the instance is in
singleton mode and will raise a NotImplementedError to avoid silently
sharing state in the single shared instance.

* Fix rebase error

* Fix rebase issues

* Fix module docstring

* Add .mutable and .to_mutable() to Instruction

To unify the access patterns between SingletonGates and other
instructions this commit adds a common property mutable and method
to_mutable() to check if any Instruction (not just singletons) are
mutable or not and to get a mutable copy. For things that don't inherit
from SingletonGate these are hard coded to `True` and to return a copy
as by default every Instruction is mutable, only `SingletonGate` objects
are different in this regard.

* Unify handling of gates in scheduling passes

Previously we were explicitly handling the SingletonGate class in the
scheduling passes. But with the introduction of mutable and to_mutable()
on Instruction we don't need to condition on gates being singleton or
not and we can just handle them in the same manner as other
instructions. This commit implements this change.

* Remove unnecessary deepcopy in substitute_node_wtih_dag

* Fix logic for SingletonGate.copy

Co-authored-by: Jake Lishman <jake.lishman@ibm.com>

* Update Singleton Gate class docstring

* Remove unused imports

* Update release notes

* Fix release note typos

Co-authored-by: Jake Lishman <jake@binhbar.com>

* Improve setattr performance

* Fix deepcopy logic

* Add check for kwargs up front

* Fix docs typos

* Add comment on to_mutable __init__ call

* Fix lint

* Handle positional initialization arguments in SingletonGate

If there are any positional arguments set when initializing a new
SingletonGate subclass those were not being handled correctly before. If
there is a positional argument being set that would indicate at least a
label is being set and indicates a mutable instance should be returned
instead of the immutable singleton. This commit adds the missing check
to the __new__ logic and also adds a test to verify the behavior is
correct.

---------

Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
Co-authored-by: Jake Lishman <jake@binhbar.com>
This commit is contained in:
Matthew Treinish 2023-09-19 15:41:30 -04:00 committed by GitHub
parent 2d3632fee6
commit e62c86bbc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 950 additions and 169 deletions

View File

@ -279,6 +279,7 @@ Gates and Instructions
InstructionSet
Operation
EquivalenceLibrary
SingletonGate
Control Flow Operations
-----------------------
@ -366,6 +367,7 @@ from .gate import Gate
# 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

View File

@ -55,6 +55,8 @@ 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
return cgate

View File

@ -24,7 +24,15 @@ from .instruction import Instruction
class Gate(Instruction):
"""Unitary gate."""
def __init__(self, name: str, num_qubits: int, params: list, label: str | None = None) -> None:
def __init__(
self,
name: str,
num_qubits: int,
params: list,
label: str | None = None,
duration=None,
unit="dt",
) -> None:
"""Create a new gate.
Args:
@ -34,7 +42,7 @@ class Gate(Instruction):
label: An optional label for the gate.
"""
self.definition = None
super().__init__(name, num_qubits, 0, params, label=label)
super().__init__(name, num_qubits, 0, params, label=label, duration=duration, unit=unit)
# Set higher priority than Numpy array and matrix classes
__array_priority__ = 20

View File

@ -94,16 +94,43 @@ class Instruction(Operation):
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.
@ -409,7 +436,7 @@ class Instruction(Operation):
# 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):

View File

@ -132,7 +132,7 @@ class InstructionSet:
if self._requester is not None:
classical = self._requester(classical)
for instruction in self._instructions:
instruction.operation.c_if(classical, val)
instruction.operation = instruction.operation.c_if(classical, val)
return self
# Legacy support for properties. Added in Terra 0.21 to support the internal switch in

View File

@ -12,13 +12,13 @@
"""Double-CNOT gate."""
from qiskit.circuit.gate import Gate
from qiskit.circuit.singleton_gate import SingletonGate
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(Gate):
class DCXGate(SingletonGate):
r"""Double-CNOT gate.
A 2-qubit Clifford gate consisting of two back-to-back
@ -48,9 +48,14 @@ class DCXGate(Gate):
\end{pmatrix}
"""
def __init__(self):
def __init__(self, label=None, duration=None, unit=None, _condition=None):
"""Create new DCX gate."""
super().__init__("dcx", 2, [])
if unit is None:
unit = "dt"
super().__init__(
"dcx", 2, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
"""

View File

@ -15,8 +15,8 @@ from math import sqrt
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 @@ from .x import XGate
@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(Gate):
class ECRGate(SingletonGate):
r"""An echoed cross-resonance gate.
This gate is maximally entangling and is equivalent to a CNOT up to
@ -84,9 +84,13 @@ class ECRGate(Gate):
\end{pmatrix}
"""
def __init__(self):
def __init__(self, label=None, _condition=None, duration=None, unit=None):
"""Create new ECR gate."""
super().__init__("ecr", 2, [])
if unit is None:
unit = "dt"
super().__init__(
"ecr", 2, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
"""

View File

@ -15,7 +15,7 @@ from math import sqrt, pi
from typing import Optional, Union
import numpy
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.gate import Gate
from qiskit.circuit.singleton_gate import SingletonGate
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 @@ _H_ARRAY = 1 / sqrt(2) * numpy.array([[1, 1], [1, -1]], dtype=numpy.complex128)
@with_gate_array(_H_ARRAY)
class HGate(Gate):
class HGate(SingletonGate):
r"""Single-qubit Hadamard gate.
This gate is a \pi rotation about the X+Z axis, and has the effect of
@ -54,9 +54,13 @@ class HGate(Gate):
\end{pmatrix}
"""
def __init__(self, label: Optional[str] = None):
def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None):
"""Create new H gate."""
super().__init__("h", 1, [], label=label)
if unit is None:
unit = "dt"
super().__init__(
"h", 1, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
"""
@ -94,8 +98,7 @@ class HGate(Gate):
ControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
gate = CHGate(label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
gate = CHGate(label=label, ctrl_state=ctrl_state, _base_label=self.label)
return gate
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)
@ -162,10 +165,21 @@ class CHGate(ControlledGate):
\end{pmatrix}
"""
def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[int, str]] = None):
def __init__(
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[int, str]] = None,
_base_label=None,
):
"""Create new CH gate."""
super().__init__(
"ch", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=HGate()
"ch",
2,
[],
num_ctrl_qubits=1,
label=label,
ctrl_state=ctrl_state,
base_gate=HGate(label=_base_label),
)
def _define(self):

View File

@ -13,12 +13,12 @@
"""Identity gate."""
from typing import Optional
from qiskit.circuit.gate import Gate
from qiskit.circuit.singleton_gate import SingletonGate
from qiskit.circuit._utils import with_gate_array
@with_gate_array([[1, 0], [0, 1]])
class IGate(Gate):
class IGate(SingletonGate):
r"""Identity gate.
Identity gate corresponds to a single-qubit gate wait cycle,
@ -45,9 +45,13 @@ class IGate(Gate):
"""
def __init__(self, label: Optional[str] = None):
def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None):
"""Create new Identity gate."""
super().__init__("id", 1, [], label=label)
if unit is None:
unit = "dt"
super().__init__(
"id", 1, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def inverse(self):
"""Invert this gate."""

View File

@ -16,7 +16,7 @@ from typing import Optional
import numpy as np
from qiskit.circuit.gate import Gate
from qiskit.circuit.singleton_gate import SingletonGate
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array
@ -24,7 +24,7 @@ from .xx_plus_yy import XXPlusYYGate
@with_gate_array([[1, 0, 0, 0], [0, 0, 1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]])
class iSwapGate(Gate):
class iSwapGate(SingletonGate):
r"""iSWAP gate.
A 2-qubit XX+YY interaction.
@ -85,9 +85,13 @@ class iSwapGate(Gate):
\end{pmatrix}
"""
def __init__(self, label: Optional[str] = None):
def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None):
"""Create new iSwap gate."""
super().__init__("iswap", 2, [], label=label)
if unit is None:
unit = "dt"
super().__init__(
"iswap", 2, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
"""

View File

@ -18,7 +18,7 @@ from typing import Optional, Union
import numpy
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.gate import Gate
from qiskit.circuit.singleton_gate import SingletonGate
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 @@ _SDG_ARRAY = numpy.array([[1, 0], [0, -1j]])
@with_gate_array(_S_ARRAY)
class SGate(Gate):
class SGate(SingletonGate):
r"""Single qubit S gate (Z**0.5).
It induces a :math:`\pi/2` phase, and is sometimes called the P gate (phase).
@ -59,9 +59,13 @@ class SGate(Gate):
Equivalent to a :math:`\pi/2` radian rotation about the Z axis.
"""
def __init__(self, label: Optional[str] = None):
def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None):
"""Create new S gate."""
super().__init__("s", 1, [], label=label)
if unit is None:
unit = "dt"
super().__init__(
"s", 1, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
"""
@ -90,7 +94,7 @@ class SGate(Gate):
@with_gate_array(_SDG_ARRAY)
class SdgGate(Gate):
class SdgGate(SingletonGate):
r"""Single qubit S-adjoint gate (~Z**0.5).
It induces a :math:`-\pi/2` phase.
@ -120,9 +124,13 @@ class SdgGate(Gate):
Equivalent to a :math:`-\pi/2` radian rotation about the Z axis.
"""
def __init__(self, label: Optional[str] = None):
def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None):
"""Create new Sdg gate."""
super().__init__("sdg", 1, [], label=label)
if unit is None:
unit = "dt"
super().__init__(
"sdg", 1, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
"""

View File

@ -15,7 +15,7 @@
from typing import Optional, Union
import numpy
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.gate import Gate
from qiskit.circuit.singleton_gate import SingletonGate
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array
@ -24,7 +24,7 @@ _SWAP_ARRAY = numpy.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1
@with_gate_array(_SWAP_ARRAY)
class SwapGate(Gate):
class SwapGate(SingletonGate):
r"""The SWAP gate.
This is a symmetric and Clifford gate.
@ -59,9 +59,13 @@ class SwapGate(Gate):
|a, b\rangle \rightarrow |b, a\rangle
"""
def __init__(self, label: Optional[str] = None):
def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None):
"""Create new SWAP gate."""
super().__init__("swap", 2, [], label=label)
if unit is None:
unit = "dt"
super().__init__(
"swap", 2, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
"""
@ -103,8 +107,7 @@ class SwapGate(Gate):
ControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
gate = CSwapGate(label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
gate = CSwapGate(label=label, ctrl_state=ctrl_state, _base_label=self.label)
return gate
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)
@ -191,7 +194,12 @@ 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):
def __init__(
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
_base_label=None,
):
"""Create new CSWAP gate."""
super().__init__(
"cswap",
@ -200,7 +208,7 @@ class CSwapGate(ControlledGate):
num_ctrl_qubits=1,
label=label,
ctrl_state=ctrl_state,
base_gate=SwapGate(),
base_gate=SwapGate(label=_base_label),
)
def _define(self):

View File

@ -16,7 +16,7 @@ from math import pi
from typing import Optional, Union
import numpy
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.gate import Gate
from qiskit.circuit.singleton_gate import SingletonGate
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array
@ -26,7 +26,7 @@ _SXDG_ARRAY = [[0.5 - 0.5j, 0.5 + 0.5j], [0.5 + 0.5j, 0.5 - 0.5j]]
@with_gate_array(_SX_ARRAY)
class SXGate(Gate):
class SXGate(SingletonGate):
r"""The single-qubit Sqrt(X) gate (:math:`\sqrt{X}`).
Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
@ -64,9 +64,13 @@ class SXGate(Gate):
"""
def __init__(self, label: Optional[str] = None):
def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None):
"""Create new SX gate."""
super().__init__("sx", 1, [], label=label)
if unit is None:
unit = "dt"
super().__init__(
"sx", 1, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
"""
@ -108,14 +112,13 @@ class SXGate(Gate):
ControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
gate = CSXGate(label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
gate = CSXGate(label=label, ctrl_state=ctrl_state, _base_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(Gate):
class SXdgGate(SingletonGate):
r"""The inverse single-qubit Sqrt(X) gate.
Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
@ -148,9 +151,13 @@ class SXdgGate(Gate):
)
_ARRAY.setflags(write=False)
def __init__(self, label: Optional[str] = None):
def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None):
"""Create new SXdg gate."""
super().__init__("sxdg", 1, [], label=label)
if unit is None:
unit = "dt"
super().__init__(
"sxdg", 1, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
"""
@ -230,10 +237,21 @@ class CSXGate(ControlledGate):
"""
def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None):
def __init__(
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
_base_label=None,
):
"""Create new CSX gate."""
super().__init__(
"csx", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=SXGate()
"csx",
2,
[],
num_ctrl_qubits=1,
label=label,
ctrl_state=ctrl_state,
base_gate=SXGate(label=_base_label),
)
def _define(self):

View File

@ -17,14 +17,14 @@ from typing import Optional
import numpy
from qiskit.circuit.gate import Gate
from qiskit.circuit.singleton_gate import SingletonGate
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(Gate):
class TGate(SingletonGate):
r"""Single qubit T gate (Z**0.25).
It induces a :math:`\pi/4` phase, and is sometimes called the pi/8 gate
@ -55,9 +55,13 @@ class TGate(Gate):
Equivalent to a :math:`\pi/4` radian rotation about the Z axis.
"""
def __init__(self, label: Optional[str] = None):
def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None):
"""Create new T gate."""
super().__init__("t", 1, [], label=label)
if unit is None:
unit = "dt"
super().__init__(
"t", 1, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
"""
@ -86,7 +90,7 @@ class TGate(Gate):
@with_gate_array([[1, 0], [0, (1 - 1j) / math.sqrt(2)]])
class TdgGate(Gate):
class TdgGate(SingletonGate):
r"""Single qubit T-adjoint gate (~Z**0.25).
It induces a :math:`-\pi/4` phase.
@ -116,9 +120,13 @@ class TdgGate(Gate):
Equivalent to a :math:`-\pi/4` radian rotation about the Z axis.
"""
def __init__(self, label: Optional[str] = None):
def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None):
"""Create new Tdg gate."""
super().__init__("tdg", 1, [], label=label)
if unit is None:
unit = "dt"
super().__init__(
"tdg", 1, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
"""

View File

@ -17,7 +17,7 @@ from math import ceil, pi
import numpy
from qiskit.utils.deprecation import deprecate_func
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.gate import Gate
from qiskit.circuit.singleton_gate import SingletonGate
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 @@ _X_ARRAY = [[0, 1], [1, 0]]
@with_gate_array(_X_ARRAY)
class XGate(Gate):
class XGate(SingletonGate):
r"""The single-qubit Pauli-X gate (:math:`\sigma_x`).
Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
@ -76,9 +76,13 @@ class XGate(Gate):
|1\rangle \rightarrow |0\rangle
"""
def __init__(self, label: Optional[str] = None):
def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None):
"""Create new X gate."""
super().__init__("x", 1, [], label=label)
if unit is None:
unit = "dt"
super().__init__(
"x", 1, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
"""
@ -115,8 +119,12 @@ class XGate(Gate):
Returns:
ControlledGate: controlled version of this gate.
"""
gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
gate = MCXGate(
num_ctrl_qubits=num_ctrl_qubits,
label=label,
ctrl_state=ctrl_state,
_base_label=self.label,
)
return gate
def inverse(self):
@ -188,10 +196,21 @@ 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):
def __init__(
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
_base_label=None,
):
"""Create new CX gate."""
super().__init__(
"cx", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=XGate()
"cx",
2,
[],
num_ctrl_qubits=1,
label=label,
ctrl_state=ctrl_state,
base_gate=XGate(label=_base_label),
)
def control(
@ -213,8 +232,12 @@ class CXGate(ControlledGate):
"""
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)
gate.base_gate.label = self.label
gate = MCXGate(
num_ctrl_qubits=num_ctrl_qubits + 1,
label=label,
ctrl_state=new_ctrl_state,
_base_label=self.label,
)
return gate
def inverse(self):
@ -291,10 +314,21 @@ class CCXGate(ControlledGate):
"""
def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None):
def __init__(
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
_base_label=None,
):
"""Create new CCX gate."""
super().__init__(
"ccx", 3, [], num_ctrl_qubits=2, label=label, ctrl_state=ctrl_state, base_gate=XGate()
"ccx",
3,
[],
num_ctrl_qubits=2,
label=label,
ctrl_state=ctrl_state,
base_gate=XGate(label=_base_label),
)
def _define(self):
@ -359,8 +393,12 @@ class CCXGate(ControlledGate):
"""
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)
gate.base_gate.label = self.label
gate = MCXGate(
num_ctrl_qubits=num_ctrl_qubits + 2,
label=label,
ctrl_state=new_ctrl_state,
_base_label=self.label,
)
return gate
def inverse(self):
@ -380,7 +418,7 @@ class CCXGate(ControlledGate):
[0, 0, 0, 1j, 0, 0, 0, 0],
]
)
class RCCXGate(Gate):
class RCCXGate(SingletonGate):
"""The simplified Toffoli gate, also referred to as Margolus gate.
The simplified Toffoli gate implements the Toffoli gate up to relative phases.
@ -396,9 +434,13 @@ class RCCXGate(Gate):
with the :meth:`~qiskit.circuit.QuantumCircuit.rccx` method.
"""
def __init__(self, label: Optional[str] = None):
def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None):
"""Create a new simplified CCX gate."""
super().__init__("rccx", 3, [], label=label)
if unit is None:
unit = "dt"
super().__init__(
"rccx", 3, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
"""
@ -675,7 +717,7 @@ class C3XGate(ControlledGate):
[0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0],
]
)
class RC3XGate(Gate):
class RC3XGate(SingletonGate):
"""The simplified 3-controlled Toffoli gate.
The simplified Toffoli gate implements the Toffoli gate up to relative phases.
@ -689,9 +731,13 @@ class RC3XGate(Gate):
with the :meth:`~qiskit.circuit.QuantumCircuit.rcccx` method.
"""
def __init__(self, label: Optional[str] = None):
def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None):
"""Create a new RC3X gate."""
super().__init__("rcccx", 4, [], label=label)
if unit is None:
unit = "dt"
super().__init__(
"rcccx", 4, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
"""
@ -855,6 +901,7 @@ class MCXGate(ControlledGate):
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.
@ -866,9 +913,11 @@ class MCXGate(ControlledGate):
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)
gate = gate_class.__new__(
gate_class, label=label, ctrl_state=ctrl_state, _base_label=_base_label
)
# if __new__ does not return the same type as cls, init is not called
gate.__init__(label=label, ctrl_state=ctrl_state)
gate.__init__(label=label, ctrl_state=ctrl_state, _base_label=_base_label)
return gate
return super().__new__(cls)
@ -878,6 +927,7 @@ class MCXGate(ControlledGate):
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)
@ -888,7 +938,7 @@ class MCXGate(ControlledGate):
num_ctrl_qubits=num_ctrl_qubits,
label=label,
ctrl_state=ctrl_state,
base_gate=XGate(),
base_gate=XGate(label=_base_label),
)
def inverse(self):
@ -963,6 +1013,7 @@ class MCXGrayCode(MCXGate):
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

View File

@ -17,7 +17,7 @@ from typing import Optional, Union
# pylint: disable=cyclic-import
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.gate import Gate
from qiskit.circuit.singleton_gate import SingletonGate
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array
@ -25,7 +25,7 @@ _Y_ARRAY = [[0, -1j], [1j, 0]]
@with_gate_array(_Y_ARRAY)
class YGate(Gate):
class YGate(SingletonGate):
r"""The single-qubit Pauli-Y gate (:math:`\sigma_y`).
Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
@ -71,9 +71,13 @@ class YGate(Gate):
|1\rangle \rightarrow -i|0\rangle
"""
def __init__(self, label: Optional[str] = None):
def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None):
"""Create new Y gate."""
super().__init__("y", 1, [], label=label)
if unit is None:
unit = "dt"
super().__init__(
"y", 1, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
# pylint: disable=cyclic-import
@ -108,8 +112,7 @@ class YGate(Gate):
ControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
gate = CYGate(label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
gate = CYGate(label=label, ctrl_state=ctrl_state, _base_label=self.label)
return gate
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)
@ -175,10 +178,21 @@ class CYGate(ControlledGate):
"""
def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None):
def __init__(
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
_base_label=None,
):
"""Create new CY gate."""
super().__init__(
"cy", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=YGate()
"cy",
2,
[],
num_ctrl_qubits=1,
label=label,
ctrl_state=ctrl_state,
base_gate=YGate(label=_base_label),
)
def _define(self):

View File

@ -19,7 +19,7 @@ import numpy
from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.gate import Gate
from qiskit.circuit.singleton_gate import SingletonGate
from qiskit.circuit.quantumregister import QuantumRegister
from .p import PhaseGate
@ -28,7 +28,7 @@ _Z_ARRAY = [[1, 0], [0, -1]]
@with_gate_array(_Z_ARRAY)
class ZGate(Gate):
class ZGate(SingletonGate):
r"""The single-qubit Pauli-Z gate (:math:`\sigma_z`).
Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
@ -74,9 +74,13 @@ class ZGate(Gate):
|1\rangle \rightarrow -|1\rangle
"""
def __init__(self, label: Optional[str] = None):
def __init__(self, label: Optional[str] = None, duration=None, unit=None, _condition=None):
"""Create new Z gate."""
super().__init__("z", 1, [], label=label)
if unit is None:
unit = "dt"
super().__init__(
"z", 1, [], label=label, _condition=_condition, duration=duration, unit=unit
)
def _define(self):
# pylint: disable=cyclic-import
@ -112,8 +116,7 @@ class ZGate(Gate):
ControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
gate = CZGate(label=label, ctrl_state=ctrl_state)
gate.base_gate.label = self.label
gate = CZGate(label=label, ctrl_state=ctrl_state, _base_label=self.label)
return gate
return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)
@ -160,10 +163,21 @@ 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):
def __init__(
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
_base_label=None,
):
"""Create new CZ gate."""
super().__init__(
"cz", 2, [], label=label, num_ctrl_qubits=1, ctrl_state=ctrl_state, base_gate=ZGate()
"cz",
2,
[],
label=label,
num_ctrl_qubits=1,
ctrl_state=ctrl_state,
base_gate=ZGate(label=_base_label),
)
def _define(self):

View File

@ -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.condition = (cr, int(condition_values[c_ptr]))
operation = operation.c_if(cr, int(condition_values[c_ptr]))
c_ptr += 1
qc._append(CircuitInstruction(operation=operation, qubits=qubits[q_start:q_end]))
else:

View File

@ -0,0 +1,188 @@
# 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)

View File

@ -234,7 +234,8 @@ class AstInterpreter:
maxidx = max([len(id0), len(id1)])
for idx in range(maxidx):
cx_gate = std.CXGate()
cx_gate.condition = self.condition
if self.condition:
cx_gate = cx_gate.c_if(*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:
@ -252,7 +253,8 @@ class AstInterpreter:
)
for idx, idy in zip(id0, id1):
meas_gate = Measure()
meas_gate.condition = self.condition
if self.condition:
meas_gate = meas_gate.c_if(*self.condition)
self.dag.apply_operation_back(meas_gate, [idx], [idy], check=False)
def _process_if(self, node):
@ -341,7 +343,8 @@ class AstInterpreter:
id0 = self._process_bit_id(node.children[0])
for i, _ in enumerate(id0):
reset = Reset()
reset.condition = self.condition
if self.condition:
reset = reset.c_if(*self.condition)
self.dag.apply_operation_back(reset, [id0[i]], [], check=False)
elif node.type == "if":
@ -398,7 +401,8 @@ class AstInterpreter:
QiskitError: if encountering a non-basis opaque gate
"""
op = self._create_op(name, params)
op.condition = self.condition
if self.condition:
op = op.c_if(*self.condition)
self.dag.apply_operation_back(op, qargs, [], check=False)
def _create_op(self, name, params):

View File

@ -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.condition = (clbit_map[reg], val)
rule.operation = rule.operation.c_if(clbit_map[reg], val)
elif reg.size == c.size:
rule.operation.condition = (c, val)
rule.operation = rule.operation.c_if(c, val)
else:
raise QiskitError(
"Cannot convert condition in circuit with "

View File

@ -833,6 +833,9 @@ class DAGCircuit:
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)
elif isinstance(op, SwitchCaseOp):
op.target = variable_mapper.map_target(op.target)
@ -1272,6 +1275,10 @@ class DAGCircuit:
"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
in_dag.apply_operation_back(new_op, in_node.qargs, in_node.cargs, check=False)
else:
@ -1356,7 +1363,12 @@ class DAGCircuit:
label=old_node.op.label,
)
elif getattr(old_node.op, "condition", None) is not None:
m_op = copy.copy(old_node.op)
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)
else:
m_op = old_node.op
@ -1430,6 +1442,9 @@ class DAGCircuit:
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
new_wires.update(condition_resources(old_condition).clbits)

View File

@ -227,8 +227,7 @@ 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)
gate.condition = (qc.cregs[creg], value)
gate = gates[gate_id](*parameters).c_if(qc.cregs[creg], value)
qc._append(CircuitInstruction(gate, [qubits[q] for q in op_qubits]))
elif opcode == OpCode.Measure:
qubit, clbit = op.operands

View File

@ -25,10 +25,11 @@ import numpy as np
from qiskit import circuit as circuit_mod
from qiskit import extensions
from qiskit.circuit import library, controlflow, CircuitInstruction
from qiskit.circuit import library, controlflow, CircuitInstruction, ControlFlowOp
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
@ -272,8 +273,10 @@ 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)
gate = gate_class(condition, *params, label=label)
elif version >= 5 and issubclass(gate_class, ControlledGate):
if gate_name in {
"MCPhaseGate",
@ -283,9 +286,9 @@ def _read_instruction(file_obj, circuit, registers, custom_operations, version,
"MCXRecursive",
"MCXVChain",
}:
gate = gate_class(*params, instruction.num_ctrl_qubits)
gate = gate_class(*params, instruction.num_ctrl_qubits, label=label)
else:
gate = gate_class(*params)
gate = gate_class(*params, label=label)
gate.num_ctrl_qubits = instruction.num_ctrl_qubits
gate.ctrl_state = instruction.ctrl_state
gate.condition = condition
@ -304,10 +307,19 @@ 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.condition = condition
if instruction.label_size > 0:
gate.label = label
else:
gate = gate_class(*params)
if condition:
if not isinstance(gate, ControlFlowOp):
gate = gate.c_if(*condition)
else:
gate.condition = condition
if circuit is None:
return gate
if not isinstance(gate, Instruction):

View File

@ -36,8 +36,7 @@ class ResetAfterMeasureSimplification(TransformationPass):
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()
new_x.condition = (node.cargs[0], 1)
new_x = XGate().c_if(node.cargs[0], 1)
new_dag = DAGCircuit()
new_dag.add_qubits(node.qargs)
new_dag.add_clbits(node.cargs)

View File

@ -192,8 +192,11 @@ class DynamicalDecoupling(TransformationPass):
index_sequence_duration_map = {}
for physical_qubit in self._qubits:
dd_sequence_duration = 0
for gate in self._dd_sequence:
for index, gate in enumerate(self._dd_sequence):
gate = gate.to_mutable()
self._dd_sequence[index] = gate
gate.duration = self._durations.get(gate, physical_qubit)
dd_sequence_duration += gate.duration
index_sequence_duration_map[physical_qubit] = dd_sequence_duration

View File

@ -224,7 +224,7 @@ class PadDynamicalDecoupling(BasePadding):
continue
sequence_lengths = []
for gate in self._dd_sequence:
for index, gate in enumerate(self._dd_sequence):
try:
# Check calibration.
params = self._resolve_params(gate)
@ -246,6 +246,8 @@ class PadDynamicalDecoupling(BasePadding):
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

View File

@ -70,6 +70,7 @@ class BaseScheduler(AnalysisPass):
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

View File

@ -96,13 +96,14 @@ class TimeUnitConversion(TransformationPass):
# Make units consistent
for node in dag.op_nodes():
try:
node.op = node.op.copy()
node.op.duration = self.inst_durations.get(
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:
pass
continue
node.op = node.op.to_mutable()
node.op.duration = duration
node.op.unit = time_unit
self.property_set["time_unit"] = time_unit
return dag

View File

@ -0,0 +1,122 @@
---
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.

View File

@ -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 []
ignore = ignore or ["kwargs"]
free_params = []
for name, param in signature(fun).parameters.items():
if param.default == Parameter.empty and param.kind != Parameter.VAR_POSITIONAL:

View File

@ -609,8 +609,7 @@ class TestLoadFromQPY(QiskitTestCase):
def test_standard_gate_with_label(self):
"""Test a standard gate with a label."""
qc = QuantumCircuit(1)
gate = XGate()
gate.label = "My special X gate"
gate = XGate(label="My special X gate")
qc.append(gate, [0])
qpy_file = io.BytesIO()
dump(qc, qpy_file)

View File

@ -282,6 +282,7 @@ class TestGateEquivalenceEqual(QiskitTestCase):
"PermutationGate",
"Commuting2qBlock",
"PauliEvolutionGate",
"SingletonGate",
"_U0Gate",
"_DefinedGate",
}

View File

@ -10,6 +10,8 @@
# 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
@ -22,6 +24,7 @@ from qiskit.circuit import Instruction, InstructionSet
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
@ -539,21 +542,21 @@ class TestInstructions(QiskitTestCase):
arbitrary :obj:`.Clbit` and `:obj:`.ClassicalRegister` instances, but rejects integers."""
with self.subTest("accepts arbitrary register"):
instruction = HGate()
instruction = RZGate(0)
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 = HGate()
instruction = RZGate(0)
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 = HGate()
instruction = RZGate(0)
instructions = InstructionSet()
instructions.add(instruction, [Qubit()], [])
with self.assertRaisesRegex(CircuitError, r"Cannot pass an index as a condition .*"):
@ -578,7 +581,7 @@ class TestInstructions(QiskitTestCase):
with self.subTest("calls requester with bit"):
dummy_requester.reset_mock()
instruction = HGate()
instruction = RZGate(0)
instructions = InstructionSet(resource_requester=dummy_requester)
instructions.add(instruction, [Qubit()], [])
bit = Clbit()
@ -587,7 +590,7 @@ class TestInstructions(QiskitTestCase):
self.assertIs(instruction.condition[0], sentinel_bit)
with self.subTest("calls requester with index"):
dummy_requester.reset_mock()
instruction = HGate()
instruction = RZGate(0)
instructions = InstructionSet(resource_requester=dummy_requester)
instructions.add(instruction, [Qubit()], [])
index = 0
@ -596,7 +599,7 @@ class TestInstructions(QiskitTestCase):
self.assertIs(instruction.condition[0], sentinel_bit)
with self.subTest("calls requester with register"):
dummy_requester.reset_mock()
instruction = HGate()
instruction = RZGate(0)
instructions = InstructionSet(resource_requester=dummy_requester)
instructions.add(instruction, [Qubit()], [])
register = ClassicalRegister(2)
@ -605,7 +608,7 @@ class TestInstructions(QiskitTestCase):
self.assertIs(instruction.condition[0], sentinel_register)
with self.subTest("calls requester only once when broadcast"):
dummy_requester.reset_mock()
instruction_list = [HGate(), HGate(), HGate()]
instruction_list = [RZGate(0), RZGate(0), RZGate(0)]
instructions = InstructionSet(resource_requester=dummy_requester)
for instruction in instruction_list:
instructions.add(instruction, [Qubit()], [])
@ -625,7 +628,7 @@ class TestInstructions(QiskitTestCase):
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 = HGate()
instruction = RZGate(0)
instruction.label = 0
def test_deprecation_warnings_qasm_methods(self):

View File

@ -0,0 +1,253 @@
# 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")

View File

@ -477,8 +477,7 @@ class TestDagApplyOperation(QiskitTestCase):
def test_apply_operation_back(self):
"""The apply_operation_back() method."""
x_gate = XGate()
x_gate.condition = self.condition
x_gate = XGate().c_if(*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])
@ -490,8 +489,7 @@ class TestDagApplyOperation(QiskitTestCase):
def test_edges(self):
"""Test that DAGCircuit.edges() behaves as expected with ops."""
x_gate = XGate()
x_gate.condition = self.condition
x_gate = XGate().c_if(*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])
@ -509,8 +507,7 @@ class TestDagApplyOperation(QiskitTestCase):
# Single qubit gate conditional: qc.h(qr[2]).c_if(cr, 3)
h_gate = HGate()
h_gate.condition = self.condition
h_gate = HGate().c_if(*self.condition)
h_node = self.dag.apply_operation_back(h_gate, [self.qubit2], [])
self.assertEqual(h_node.qargs, (self.qubit2,))
@ -550,8 +547,7 @@ class TestDagApplyOperation(QiskitTestCase):
new_creg = ClassicalRegister(1, "cr2")
self.dag.add_creg(new_creg)
meas_gate = Measure()
meas_gate.condition = (new_creg, 0)
meas_gate = Measure().c_if(new_creg, 0)
meas_node = self.dag.apply_operation_back(meas_gate, [self.qubit0], [self.clbit0])
self.assertEqual(meas_node.qargs, (self.qubit0,))
@ -596,8 +592,7 @@ class TestDagApplyOperation(QiskitTestCase):
# 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()
meas_gate.condition = self.condition
meas_gate = Measure().c_if(*self.condition)
meas_node = self.dag.apply_operation_back(meas_gate, [self.qubit1], [self.clbit1])
self.assertEqual(meas_node.qargs, (self.qubit1,))
@ -1153,8 +1148,7 @@ class TestDagNodeSelection(QiskitTestCase):
def test_dag_collect_runs_start_with_conditional(self):
"""Test collect runs with a conditional at the start of the run."""
h_gate = HGate()
h_gate.condition = self.condition
h_gate = HGate().c_if(*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])
@ -1167,8 +1161,7 @@ class TestDagNodeSelection(QiskitTestCase):
def test_dag_collect_runs_conditional_in_middle(self):
"""Test collect_runs with a conditional in the middle of a run."""
h_gate = HGate()
h_gate.condition = self.condition
h_gate = HGate().c_if(*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])
@ -1210,8 +1203,7 @@ class TestDagNodeSelection(QiskitTestCase):
"""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()
h_gate.condition = self.condition
h_gate = HGate().c_if(*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])
@ -1226,8 +1218,7 @@ class TestDagNodeSelection(QiskitTestCase):
"""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()
h_gate.condition = self.condition
h_gate = HGate().c_if(*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])
@ -1305,8 +1296,7 @@ class TestDagLayers(QiskitTestCase):
qubit1 = qreg[1]
clbit0 = creg[0]
clbit1 = creg[1]
x_gate = XGate()
x_gate.condition = (creg, 3)
x_gate = XGate().c_if(creg, 3)
dag = DAGCircuit()
dag.add_qreg(qreg)
dag.add_creg(creg)
@ -2135,10 +2125,8 @@ class TestDagSubstitute(QiskitTestCase):
sub.cx(0, 1)
sub.h(0)
conditioned_h = HGate()
conditioned_h.condition = conditioned_cz.condition
conditioned_cx = CXGate()
conditioned_cx.condition = conditioned_cz.condition
conditioned_h = HGate().c_if(*conditioned_cz.condition)
conditioned_cx = CXGate().c_if(*conditioned_cz.condition)
expected = DAGCircuit()
expected.add_qubits(base_qubits)