Singleton parameterless controlled gates (#10898)

* Singleton parameterless controlled gates

This commit adds a new class SingletonControlledGate which is very
similar to the SingletonGate class, but instead can be used in place of
the ControlledGate class as a parent class. This enables a controlled
gate class to work as a singleton if there is no custom control state
specified. This new class has a large amount of duplication with the
SingletonGate class, but because of how the inheritance tree is built
up here there is no avoiding this as we need to specialize for adding
singleton support and SingletonGate is at the same level as
ControlledGate.

With the introduction of this new class all the standard library
ControlledGate instances are updated so they inherit from
SingletonControlledGate instead of ControlledGate. This should
significantly reduce the memory footprint of repeated instances of these
gates in circuits.

* Fix failing test cases

* Apply suggestions from code review

Co-authored-by: Kevin Hartman <kevin@hart.mn>

* Fixes from rebase

* Fix cyclic imports caused by eager definition population

* Fix tests

* Remove unused changes to ControlledGate

* Expand deepcopy test assertions

* Update release notes

---------

Co-authored-by: Kevin Hartman <kevin@hart.mn>
This commit is contained in:
Matthew Treinish 2023-10-16 15:50:01 -04:00 committed by GitHub
parent f87bb0132d
commit 111fd64d2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 635 additions and 74 deletions

View File

@ -111,6 +111,7 @@ def control(
else:
basis = ["p", "u", "x", "z", "rx", "ry", "rz", "cx"]
if isinstance(operation, controlledgate.ControlledGate):
operation = operation.to_mutable()
operation.ctrl_state = None
unrolled_gate = _unroll_gate(operation, basis_gates=basis)
if unrolled_gate.definition.global_phase:

View File

@ -38,6 +38,10 @@ class ControlledGate(Gate):
definition: Optional["QuantumCircuit"] = None,
ctrl_state: Optional[Union[int, str]] = None,
base_gate: Optional[Gate] = None,
duration=None,
unit=None,
*,
_base_label=None,
):
"""Create a new ControlledGate. In the new gate the first ``num_ctrl_qubits``
of the gate are the controls.
@ -95,7 +99,7 @@ class ControlledGate(Gate):
qc2.draw('mpl')
"""
self.base_gate = None if base_gate is None else base_gate.copy()
super().__init__(name, num_qubits, params, label=label)
super().__init__(name, num_qubits, params, label=label, duration=duration, unit=unit)
self._num_ctrl_qubits = 1
self.num_ctrl_qubits = num_ctrl_qubits
self.definition = copy.deepcopy(definition)
@ -111,7 +115,7 @@ class ControlledGate(Gate):
`_definition`.
"""
if self._open_ctrl:
closed_gate = self.copy()
closed_gate = self.to_mutable()
closed_gate.ctrl_state = None
bit_ctrl_state = bin(self.ctrl_state)[2:].zfill(self.num_ctrl_qubits)
qreg = QuantumRegister(self.num_qubits, "q")

View File

@ -14,12 +14,9 @@
from math import sqrt, pi
from typing import Optional, Union
import numpy
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.singleton import SingletonGate
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array
from .t import TGate, TdgGate
from .s import SGate, SdgGate
_H_ARRAY = 1 / sqrt(2) * numpy.array([[1, 1], [1, -1]], dtype=numpy.complex128)
@ -104,7 +101,7 @@ class HGate(SingletonGate):
@with_controlled_gate_array(_H_ARRAY, num_ctrl_qubits=1)
class CHGate(ControlledGate):
class CHGate(SingletonControlledGate):
r"""Controlled-Hadamard gate.
Applies a Hadamard on the target qubit if the control is
@ -165,6 +162,9 @@ class CHGate(ControlledGate):
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[int, str]] = None,
*,
duration=None,
unit="dt",
_base_label=None,
):
"""Create new CH gate."""
@ -176,6 +176,9 @@ class CHGate(ControlledGate):
label=label,
ctrl_state=ctrl_state,
base_gate=HGate(label=_base_label),
duration=duration,
unit=unit,
_base_label=_base_label,
)
def _define(self):
@ -193,6 +196,8 @@ class CHGate(ControlledGate):
# pylint: disable=cyclic-import
from qiskit.circuit.quantumcircuit import QuantumCircuit
from .x import CXGate # pylint: disable=cyclic-import
from .t import TGate, TdgGate
from .s import SGate, SdgGate
q = QuantumRegister(2, "q")
qc = QuantumCircuit(q, name=self.name)

View File

@ -17,9 +17,7 @@ from typing import Optional, Union
import numpy
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.singleton import SingletonGate
from qiskit.circuit.library.standard_gates.p import CPhaseGate, PhaseGate
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array
@ -86,6 +84,8 @@ class SGate(SingletonGate):
def power(self, exponent: float):
"""Raise gate to a power."""
from .p import PhaseGate
return PhaseGate(0.5 * numpy.pi * exponent)
@ -147,11 +147,13 @@ class SdgGate(SingletonGate):
def power(self, exponent: float):
"""Raise gate to a power."""
from .p import PhaseGate
return PhaseGate(-0.5 * numpy.pi * exponent)
@with_controlled_gate_array(_S_ARRAY, num_ctrl_qubits=1)
class CSGate(ControlledGate):
class CSGate(SingletonControlledGate):
r"""Controlled-S gate.
Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
@ -180,16 +182,35 @@ class CSGate(ControlledGate):
\end{pmatrix}
"""
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,
*,
duration=None,
unit="dt",
_base_label=None,
):
"""Create new CS gate."""
super().__init__(
"cs", 2, [], label=label, num_ctrl_qubits=1, ctrl_state=ctrl_state, base_gate=SGate()
"cs",
2,
[],
label=label,
num_ctrl_qubits=1,
ctrl_state=ctrl_state,
base_gate=SGate(label=_base_label),
duration=duration,
_base_label=_base_label,
unit=unit,
)
def _define(self):
"""
gate cs a,b { h b; cp(pi/2) a,b; h b; }
"""
from .p import CPhaseGate
self.definition = CPhaseGate(theta=pi / 2).definition
def inverse(self):
@ -198,11 +219,13 @@ class CSGate(ControlledGate):
def power(self, exponent: float):
"""Raise gate to a power."""
from .p import CPhaseGate
return CPhaseGate(0.5 * numpy.pi * exponent)
@with_controlled_gate_array(_SDG_ARRAY, num_ctrl_qubits=1)
class CSdgGate(ControlledGate):
class CSdgGate(SingletonControlledGate):
r"""Controlled-S^\dagger gate.
Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
@ -231,7 +254,15 @@ class CSdgGate(ControlledGate):
\end{pmatrix}
"""
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,
*,
duration=None,
unit="dt",
_base_label=None,
):
"""Create new CSdg gate."""
super().__init__(
"csdg",
@ -240,13 +271,17 @@ class CSdgGate(ControlledGate):
label=label,
num_ctrl_qubits=1,
ctrl_state=ctrl_state,
base_gate=SdgGate(),
base_gate=SdgGate(label=_base_label),
duration=duration,
unit=unit,
)
def _define(self):
"""
gate csdg a,b { h b; cp(-pi/2) a,b; h b; }
"""
from .p import CPhaseGate
self.definition = CPhaseGate(theta=-pi / 2).definition
def inverse(self):
@ -255,4 +290,6 @@ class CSdgGate(ControlledGate):
def power(self, exponent: float):
"""Raise gate to a power."""
from .p import CPhaseGate
return CPhaseGate(-0.5 * numpy.pi * exponent)

View File

@ -14,8 +14,7 @@
from typing import Optional, Union
import numpy
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.singleton import SingletonGate
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array
@ -113,7 +112,7 @@ class SwapGate(SingletonGate):
@with_controlled_gate_array(_SWAP_ARRAY, num_ctrl_qubits=1)
class CSwapGate(ControlledGate):
class CSwapGate(SingletonControlledGate):
r"""Controlled-SWAP gate, also known as the Fredkin gate.
Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
@ -194,9 +193,14 @@ class CSwapGate(ControlledGate):
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
*,
duration=None,
unit="dt",
_base_label=None,
):
"""Create new CSWAP gate."""
if unit is None:
unit = "dt"
super().__init__(
"cswap",
3,
@ -205,6 +209,8 @@ class CSwapGate(ControlledGate):
label=label,
ctrl_state=ctrl_state,
base_gate=SwapGate(label=_base_label),
duration=duration,
unit=unit,
)
def _define(self):

View File

@ -14,8 +14,7 @@
from math import pi
from typing import Optional, Union
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.singleton import SingletonGate
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array
@ -104,7 +103,7 @@ class SXGate(SingletonGate):
string (e.g. '110'), or None. If None, use all 1s.
Returns:
ControlledGate: controlled version of this gate.
SingletonControlledGate: controlled version of this gate.
"""
if num_ctrl_qubits == 1:
gate = CSXGate(label=label, ctrl_state=ctrl_state, _base_label=self.label)
@ -168,7 +167,7 @@ class SXdgGate(SingletonGate):
@with_controlled_gate_array(_SX_ARRAY, num_ctrl_qubits=1)
class CSXGate(ControlledGate):
class CSXGate(SingletonControlledGate):
r"""Controlled-√X gate.
Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
@ -228,6 +227,9 @@ class CSXGate(ControlledGate):
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
*,
duration=None,
unit="dt",
_base_label=None,
):
"""Create new CSX gate."""
@ -239,6 +241,8 @@ class CSXGate(ControlledGate):
label=label,
ctrl_state=ctrl_state,
base_gate=SXGate(label=_base_label),
duration=duration,
unit=unit,
)
def _define(self):

View File

@ -17,14 +17,9 @@ from math import ceil, pi
import numpy
from qiskit.utils.deprecation import deprecate_func
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.singleton import SingletonGate
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate
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
from .t import TGate, TdgGate
from .u1 import U1Gate
from .u2 import U2Gate
from .sx import SXGate
_X_ARRAY = [[0, 1], [1, 0]]
@ -129,7 +124,7 @@ class XGate(SingletonGate):
@with_controlled_gate_array(_X_ARRAY, num_ctrl_qubits=1)
class CXGate(ControlledGate):
class CXGate(SingletonControlledGate):
r"""Controlled-X gate.
Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
@ -196,6 +191,9 @@ class CXGate(ControlledGate):
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
*,
duration=None,
unit="dt",
_base_label=None,
):
"""Create new CX gate."""
@ -207,6 +205,9 @@ class CXGate(ControlledGate):
label=label,
ctrl_state=ctrl_state,
base_gate=XGate(label=_base_label),
_base_label=_base_label,
duration=duration,
unit=unit,
)
def control(
@ -242,7 +243,7 @@ class CXGate(ControlledGate):
@with_controlled_gate_array(_X_ARRAY, num_ctrl_qubits=2, cached_states=(3,))
class CCXGate(ControlledGate):
class CCXGate(SingletonControlledGate):
r"""CCX gate, also known as Toffoli gate.
Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
@ -314,6 +315,9 @@ class CCXGate(ControlledGate):
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
*,
duration=None,
unit="dt",
_base_label=None,
):
"""Create new CCX gate."""
@ -325,6 +329,8 @@ class CCXGate(ControlledGate):
label=label,
ctrl_state=ctrl_state,
base_gate=XGate(label=_base_label),
duration=duration,
unit=unit,
)
def _define(self):
@ -338,6 +344,8 @@ class CCXGate(ControlledGate):
"""
# pylint: disable=cyclic-import
from qiskit.circuit.quantumcircuit import QuantumCircuit
from .h import HGate
from .t import TGate, TdgGate
# ┌───┐
# q_0: ───────────────────■─────────────────────■────■───┤ T ├───■──
@ -450,6 +458,8 @@ class RCCXGate(SingletonGate):
"""
# pylint: disable=cyclic-import
from qiskit.circuit.quantumcircuit import QuantumCircuit
from .u1 import U1Gate
from .u2 import U2Gate
q = QuantumRegister(3, "q")
qc = QuantumCircuit(q, name=self.name)
@ -470,7 +480,7 @@ class RCCXGate(SingletonGate):
self.definition = qc
class C3SXGate(ControlledGate):
class C3SXGate(SingletonControlledGate):
"""The 3-qubit controlled sqrt-X gate.
This implementation is based on Page 17 of [1].
@ -483,6 +493,10 @@ class C3SXGate(ControlledGate):
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
*,
duration=None,
unit="dt",
_base_label=None,
):
"""Create a new 3-qubit controlled sqrt-X gate.
@ -491,8 +505,18 @@ class C3SXGate(ControlledGate):
ctrl_state (int or str or None): control state expressed as integer,
string (e.g. '110'), or None. If None, use all 1s.
"""
from .sx import SXGate
super().__init__(
"c3sx", 4, [], num_ctrl_qubits=3, label=label, ctrl_state=ctrl_state, base_gate=SXGate()
"c3sx",
4,
[],
num_ctrl_qubits=3,
label=label,
ctrl_state=ctrl_state,
base_gate=SXGate(label=_base_label),
duration=duration,
unit=unit,
)
def _define(self):
@ -517,6 +541,7 @@ class C3SXGate(ControlledGate):
# pylint: disable=cyclic-import
from qiskit.circuit.quantumcircuit import QuantumCircuit
from .u1 import CU1Gate
from .h import HGate
angle = numpy.pi / 8
q = QuantumRegister(4, name="q")
@ -564,7 +589,10 @@ class C3SXGate(ControlledGate):
# needlessly disruptive this late in OQ2's lifecycle. The current OQ2 exporter _always_
# outputs the `include 'qelib1.inc' line. ---Jake, 2022-11-21.
old_name = self.name
self.name = "c3sqrtx"
if not self.mutable:
copy_self = self.to_mutable()
copy_self.name = "c3sqrtx"
return copy_self.qasm()
try:
return super().qasm()
finally:
@ -572,7 +600,7 @@ class C3SXGate(ControlledGate):
@with_controlled_gate_array(_X_ARRAY, num_ctrl_qubits=3, cached_states=(7,))
class C3XGate(ControlledGate):
class C3XGate(SingletonControlledGate):
r"""The X gate controlled on 3 qubits.
This implementation uses :math:`\sqrt{T}` and 14 CNOT gates.
@ -582,10 +610,22 @@ class C3XGate(ControlledGate):
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
*,
_base_label=None,
duration=None,
unit="dt",
):
"""Create a new 3-qubit controlled X gate."""
super().__init__(
"mcx", 4, [], num_ctrl_qubits=3, label=label, ctrl_state=ctrl_state, base_gate=XGate()
"mcx",
4,
[],
num_ctrl_qubits=3,
label=label,
ctrl_state=ctrl_state,
base_gate=XGate(label=_base_label),
duration=duration,
unit=unit,
)
# seems like open controls not hapening?
@ -752,6 +792,8 @@ class RC3XGate(SingletonGate):
"""
# pylint: disable=cyclic-import
from qiskit.circuit.quantumcircuit import QuantumCircuit
from .u1 import U1Gate
from .u2 import U2Gate
q = QuantumRegister(4, "q")
qc = QuantumCircuit(q, name=self.name)
@ -782,7 +824,7 @@ class RC3XGate(SingletonGate):
@with_controlled_gate_array(_X_ARRAY, num_ctrl_qubits=4, cached_states=(15,))
class C4XGate(ControlledGate):
class C4XGate(SingletonControlledGate):
"""The 4-qubit controlled X gate.
This implementation is based on Page 21, Lemma 7.5, of [1], with the use
@ -793,10 +835,28 @@ class C4XGate(ControlledGate):
[2] Maslov, 2015. https://arxiv.org/abs/1508.03273
"""
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,
*,
duration=None,
unit="dt",
_base_label=None,
):
"""Create a new 4-qubit controlled X gate."""
if unit is None:
unit = "dt"
super().__init__(
"mcx", 5, [], num_ctrl_qubits=4, label=label, ctrl_state=ctrl_state, base_gate=XGate()
"mcx",
5,
[],
num_ctrl_qubits=4,
label=label,
ctrl_state=ctrl_state,
base_gate=XGate(label=_base_label),
duration=duration,
unit=unit,
)
# seems like open controls not hapening?
@ -830,6 +890,7 @@ class C4XGate(ControlledGate):
# pylint: disable=cyclic-import
from qiskit.circuit.quantumcircuit import QuantumCircuit
from .u1 import CU1Gate
from .h import HGate
q = QuantumRegister(5, name="q")
qc = QuantumCircuit(q, name=self.name)
@ -889,6 +950,9 @@ class MCXGate(ControlledGate):
num_ctrl_qubits: Optional[int] = None,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
*,
duration=None,
unit="dt",
_base_label=None,
):
"""Create a new MCX instance.
@ -899,13 +963,19 @@ class MCXGate(ControlledGate):
# The CXGate and CCXGate will be implemented for all modes of the MCX, and
# the C3XGate and C4XGate will be implemented in the MCXGrayCode class.
explicit: dict[int, Type[ControlledGate]] = {1: CXGate, 2: CCXGate}
if num_ctrl_qubits in explicit:
gate_class = explicit[num_ctrl_qubits]
gate_class = explicit.get(num_ctrl_qubits, None)
if gate_class is not None:
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, _base_label=_base_label)
gate.__init__(
label=label,
ctrl_state=ctrl_state,
_base_label=_base_label,
duration=duration,
unit=unit,
)
return gate
return super().__new__(cls)
@ -1001,16 +1071,31 @@ class MCXGrayCode(MCXGate):
num_ctrl_qubits: Optional[int] = None,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
*,
duration=None,
unit="dt",
_base_label=None,
):
"""Create a new MCXGrayCode instance"""
# if 1 to 4 control qubits, create explicit gates
explicit = {1: CXGate, 2: CCXGate, 3: C3XGate, 4: C4XGate}
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_class = explicit.get(num_ctrl_qubits, None)
if gate_class is not None:
gate = gate_class.__new__(
gate_class,
label=label,
ctrl_state=ctrl_state,
_base_label=_base_label,
duration=duration,
unit=unit,
)
# 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,
duration=duration,
unit=unit,
)
return gate
return super().__new__(cls)
@ -1031,6 +1116,7 @@ class MCXGrayCode(MCXGate):
# pylint: disable=cyclic-import
from qiskit.circuit.quantumcircuit import QuantumCircuit
from .u1 import MCU1Gate
from .h import HGate
q = QuantumRegister(self.num_qubits, name="q")
qc = QuantumCircuit(q, name=self.name)
@ -1116,12 +1202,24 @@ class MCXVChain(MCXGate):
dirty_ancillas: bool = False, # pylint: disable=unused-argument
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
*,
duration=None,
unit="dt",
_base_label=None,
):
"""Create a new MCX instance.
This must be defined anew to include the additional argument ``dirty_ancillas``.
"""
return super().__new__(cls, num_ctrl_qubits, label=label, ctrl_state=ctrl_state)
return super().__new__(
cls,
num_ctrl_qubits,
label=label,
ctrl_state=ctrl_state,
_base_label=_base_label,
duration=duration,
unit=unit,
)
def __init__(
self,
@ -1129,8 +1227,15 @@ class MCXVChain(MCXGate):
dirty_ancillas: bool = False,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
_base_label=None,
):
super().__init__(num_ctrl_qubits, label=label, ctrl_state=ctrl_state, _name="mcx_vchain")
super().__init__(
num_ctrl_qubits,
label=label,
ctrl_state=ctrl_state,
_name="mcx_vchain",
_base_label=_base_label,
)
self._dirty_ancillas = dirty_ancillas
def inverse(self):
@ -1150,6 +1255,8 @@ class MCXVChain(MCXGate):
"""Define the MCX gate using a V-chain of CX gates."""
# pylint: disable=cyclic-import
from qiskit.circuit.quantumcircuit import QuantumCircuit
from .u1 import U1Gate
from .u2 import U2Gate
q = QuantumRegister(self.num_qubits, name="q")
qc = QuantumCircuit(q, name=self.name)

View File

@ -16,8 +16,7 @@ from math import pi
from typing import Optional, Union
# pylint: disable=cyclic-import
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.singleton import SingletonGate
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array
@ -118,7 +117,7 @@ class YGate(SingletonGate):
@with_controlled_gate_array(_Y_ARRAY, num_ctrl_qubits=1)
class CYGate(ControlledGate):
class CYGate(SingletonControlledGate):
r"""Controlled-Y gate.
Can be applied to a :class:`~qiskit.circuit.QuantumCircuit`
@ -178,6 +177,9 @@ class CYGate(ControlledGate):
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
*,
duration=None,
unit="dt",
_base_label=None,
):
"""Create new CY gate."""
@ -189,6 +191,8 @@ class CYGate(ControlledGate):
label=label,
ctrl_state=ctrl_state,
base_gate=YGate(label=_base_label),
duration=duration,
unit=unit,
)
def _define(self):

View File

@ -18,8 +18,7 @@ from typing import Optional, Union
import numpy
from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.singleton import SingletonGate
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate
from qiskit.circuit.quantumregister import QuantumRegister
from .p import PhaseGate
@ -126,7 +125,7 @@ class ZGate(SingletonGate):
@with_controlled_gate_array(_Z_ARRAY, num_ctrl_qubits=1)
class CZGate(ControlledGate):
class CZGate(SingletonControlledGate):
r"""Controlled-Z gate.
This is a Clifford and symmetric gate.
@ -163,6 +162,9 @@ class CZGate(ControlledGate):
self,
label: Optional[str] = None,
ctrl_state: Optional[Union[str, int]] = None,
*,
duration=None,
unit="dt",
_base_label=None,
):
"""Create new CZ gate."""
@ -174,6 +176,8 @@ class CZGate(ControlledGate):
num_ctrl_qubits=1,
ctrl_state=ctrl_state,
base_gate=ZGate(label=_base_label),
duration=duration,
unit=unit,
)
def _define(self):
@ -200,7 +204,7 @@ class CZGate(ControlledGate):
@with_controlled_gate_array(_Z_ARRAY, num_ctrl_qubits=2, cached_states=(3,))
class CCZGate(ControlledGate):
class CCZGate(SingletonControlledGate):
r"""CCZ gate.
This is a symmetric gate.
@ -239,10 +243,26 @@ class CCZGate(ControlledGate):
the target qubit if the control qubits are in the :math:`|11\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,
*,
duration=None,
unit="dt",
_base_label=None,
):
"""Create new CCZ gate."""
super().__init__(
"ccz", 3, [], label=label, num_ctrl_qubits=2, ctrl_state=ctrl_state, base_gate=ZGate()
"ccz",
3,
[],
label=label,
num_ctrl_qubits=2,
ctrl_state=ctrl_state,
base_gate=ZGate(label=_base_label),
duration=duration,
unit=unit,
)
def _define(self):

View File

@ -200,6 +200,7 @@ import operator
from .instruction import Instruction
from .gate import Gate
from .controlledgate import ControlledGate
def _impl_new(cls, *_args, **_kwargs):
@ -353,3 +354,28 @@ class SingletonGate(Gate, metaclass=_SingletonMeta, overrides=_SingletonGateOver
apply here as well."""
__slots__ = ()
class _SingletonControlledGateOverrides(_SingletonInstructionOverrides, ControlledGate):
"""Overrides for all the mutable methods and properties of `ControlledGate` to make it immutable.
This class just exists for the principle; there's no additional overrides required compared
to :class:`~.circuit.Instruction`.
"""
__slots__ = ()
class SingletonControlledGate(
ControlledGate,
metaclass=_SingletonMeta,
overrides=_SingletonControlledGateOverrides,
):
"""A base class to use for :class:`.ControlledGate` objects that by default are singleton instances
This class is very similar to :class:`SingletonInstruction`, except implies unitary
:class:`.ControlledGate` semantics as well. The same caveats around setting attributes in
that class apply here as well.
"""
__slots__ = ()

View File

@ -309,9 +309,15 @@ def _read_instruction(
gate = gate_class(*params, instruction.num_ctrl_qubits, label=label)
else:
gate = gate_class(*params, label=label)
gate.num_ctrl_qubits = instruction.num_ctrl_qubits
gate.ctrl_state = instruction.ctrl_state
gate.condition = condition
if (
gate.num_ctrl_qubits != instruction.num_ctrl_qubits
or gate.ctrl_state != instruction.ctrl_state
):
gate = gate.to_mutable()
gate.num_ctrl_qubits = instruction.num_ctrl_qubits
gate.ctrl_state = instruction.ctrl_state
if condition:
gate = gate.c_if(*condition)
else:
if gate_name in {
"Initialize",

View File

@ -52,6 +52,52 @@ features:
all the constructor defaults, they will all share a single global
instance. This results in large reduction in the memory overhead for > 1
object of these types and significantly faster object construction time.
- |
Introduced a new class :class:`~.SingletonControlledGate` which is a subclass of
:class:`~.ControlledGate` 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, a :class:`.CXGate` doesn't contain any state and thus can
leverage :class:`~.SingletonControlledGate` (and does starting in
this release). In contrast, :class:`~.CRXGate` stores an angle parameter as
part of its instance data and thus can not use :class:`~.SingletonControlledGate`.
The other potential issue to be aware of when using
:class:`~.SingletonControlledGate` is that the original data model
of :class:`~.ControlledGate` supports mutation. Specifically, the
:attr:`~.ControlledGate.label`, :attr:`~.ControlledGate.duration`,
:attr:`~.ControlledGate.unit`, :attr:`~.ControlledGate.condition`, and
:attr:`~.ControlledGate.ctrl_state` attributes are all accessible and mutable in the
:class:`~.ControlledGate`, but mutation of these attributes on :class:`~.SingletonControlledGate` subclasses
is not allowed, and will raise an exception. These attributes can be customized but only at creation time
(i.e. via the constructor). In that case, the newly constructed gate will be a separate instance with the
custom state instead of the globally shared instance. You can also use the
:meth:`.SingletonControlledGate.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:`~.SingletonControlledGate`:
* :class:`~.CHGate`
* :class:`~.CSGate`
* :class:`~.CSdgGate`
* :class:`~.CSwapGate`
* :class:`~.CSXGate`
* :class:`~.CXGate`
* :class:`~.CCXGate`
* :class:`~.C3SXGate`
* :class:`~.C3XGate`
* :class:`~.C4XGate`
* :class:`~.CYGate`
* :class:`~.CZGate`
This means that unless a ``label``, ``condition``, ``duration``, ``unit``, or
``ctrl_state`` 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.
- |
Added a new method :meth`.Instruction.to_mutable` and attribute
:attr:`.Instruction.mutable` which is used to get a mutable copy and check whether
@ -86,18 +132,32 @@ upgrade:
* :class:`~.RC3XGate`
* :class:`~.YGate`
* :class:`~.ZGate`
* :class:`~.CHGate`
* :class:`~.CSGate`
* :class:`~.CSdgGate`
* :class:`~.CSwapGate`
* :class:`~.CSXGate`
* :class:`~.CXGate`
* :class:`~.CCXGate`
* :class:`~.C3SXGate`
* :class:`~.C3XGate`
* :class:`~.C4XGate`
* :class:`~.CYGate`
* :class:`~.CZGate`
no longer are able to set :attr:`~.Gate.label`, :attr:`~.Gate.condition`,
:attr:`~.Gate.duration`, or :attr:`~.Gate.unit` after instantiating an object
anymore. :attr:`~.Gate.condition` can be set through
:attr:`~.Gate.duration`, or :attr:`~.Gate.unit` (and :attr:`~.ControlledGate.ctrl_state`
for :class:`~.ControlledGate` subclasses) after instantiating an object
anymore. :attr:`~.Gate.condition` can be set through
the use :meth:`~.Gate.c_if`. 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. ``label``, ``duration`` and ``unit`` can
be given as keyword arguments to these gates at construction time, and a
mutable instance will be returned automatically.
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.
these classes to be :class:`~.SingletonGate` and :class:`~.SingletonControlledGate`
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

View File

@ -22,6 +22,7 @@ from ddt import ddt, data, unpack
from qiskit import QuantumRegister, QuantumCircuit, execute, BasicAer, QiskitError
from qiskit.test import QiskitTestCase
from qiskit.circuit import ControlledGate, Parameter, Gate
from qiskit.circuit.singleton import SingletonControlledGate, _SingletonControlledGateOverrides
from qiskit.circuit.exceptions import CircuitError
from qiskit.quantum_info.operators.predicates import matrix_equal, is_unitary_matrix
from qiskit.quantum_info.random import random_unitary
@ -1014,6 +1015,8 @@ class TestControlledGate(QiskitTestCase):
"""Test all gates in standard extensions which are of type ControlledGate
and have a base gate setting.
"""
if gate_class in {SingletonControlledGate, _SingletonControlledGateOverrides}:
self.skipTest("SingletonControlledGate isn't directly instantiated.")
num_free_params = len(_get_free_params(gate_class.__init__, ignore=["self"]))
free_params = [0.1 * i for i in range(num_free_params)]
if gate_class in [MCU1Gate, MCPhaseGate]:
@ -1144,6 +1147,8 @@ class TestControlledGate(QiskitTestCase):
num_ctrl_qubits = 1
for gate_class in ControlledGate.__subclasses__():
with self.subTest(i=repr(gate_class)):
if gate_class in {SingletonControlledGate, _SingletonControlledGateOverrides}:
self.skipTest("Singleton class isn't intended to be created directly.")
num_free_params = len(_get_free_params(gate_class.__init__, ignore=["self"]))
free_params = [0.1 * (i + 1) for i in range(num_free_params)]
if gate_class in [MCU1Gate, MCPhaseGate]:
@ -1311,6 +1316,8 @@ class TestOpenControlledToMatrix(QiskitTestCase):
@combine(gate_class=ControlledGate.__subclasses__(), ctrl_state=[0, None])
def test_open_controlled_to_matrix(self, gate_class, ctrl_state):
"""Test open controlled to_matrix."""
if gate_class in {SingletonControlledGate, _SingletonControlledGateOverrides}:
self.skipTest("SingletonGateClass isn't intended for direct initalization")
num_free_params = len(_get_free_params(gate_class.__init__, ignore=["self"]))
free_params = [0.1 * i for i in range(1, num_free_params + 1)]
if gate_class in [MCU1Gate, MCPhaseGate]:

View File

@ -283,9 +283,11 @@ class TestGateEquivalenceEqual(QiskitTestCase):
"Commuting2qBlock",
"PauliEvolutionGate",
"SingletonGate",
"SingletonControlledGate",
"_U0Gate",
"_DefinedGate",
"_SingletonGateOverrides",
"_SingletonControlledGateOverrides",
}
# Amazingly, Python's scoping rules for class bodies means that this is the closest we can get
# to a "natural" comprehension or functional iterable definition:

View File

@ -21,7 +21,7 @@ import copy
import io
import pickle
from qiskit.circuit.library import HGate, SXGate
from qiskit.circuit.library import HGate, SXGate, CXGate, CZGate, CSwapGate, CHGate, CCXGate, XGate
from qiskit.circuit import Clbit, QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.singleton import SingletonGate, SingletonInstruction
from qiskit.converters import dag_to_circuit, circuit_to_dag
@ -325,3 +325,275 @@ class TestSingletonGate(QiskitTestCase):
self.assertIsNot(esp, base)
self.assertIs(base.base_class, Measure)
self.assertIs(esp.base_class, ESPMeasure)
class TestSingletonControlledGate(QiskitTestCase):
"""Qiskit SingletonGate tests."""
def test_default_singleton(self):
gate = CXGate()
new_gate = CXGate()
self.assertIs(gate, new_gate)
def test_label_not_singleton(self):
gate = CXGate()
label_gate = CXGate(label="special")
self.assertIsNot(gate, label_gate)
def test_condition_not_singleton(self):
gate = CZGate()
condition_gate = CZGate().c_if(Clbit(), 0)
self.assertIsNot(gate, condition_gate)
def test_raise_on_state_mutation(self):
gate = CSwapGate()
with self.assertRaises(TypeError):
gate.label = "foo"
with self.assertRaises(TypeError):
gate.condition = (Clbit(), 0)
def test_labeled_condition(self):
singleton_gate = CSwapGate()
clbit = Clbit()
gate = CSwapGate(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 = CXGate()
copied = gate.copy()
self.assertIs(gate, copied)
def test_label_copy(self):
gate = CZGate(label="special")
copied = gate.copy()
self.assertIsNot(gate, copied)
self.assertEqual(gate, copied)
def test_label_copy_new(self):
gate = CZGate()
label_gate = CZGate(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 = CZGate().c_if(Clbit(), 0)
copied = gate.copy()
self.assertIsNot(gate, copied)
self.assertEqual(gate, copied)
def test_condition_label_copy(self):
clbit = Clbit()
gate = CZGate(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 = CXGate()
copied = copy.deepcopy(gate)
self.assertIs(gate, copied)
def test_deepcopy_with_label(self):
singleton_gate = CXGate()
gate = CXGate(label="special")
copied = copy.deepcopy(gate)
self.assertIsNot(gate, copied)
self.assertEqual(gate, copied)
self.assertEqual(copied.label, "special")
self.assertTrue(copied.mutable)
self.assertIsNot(gate.base_gate, copied.base_gate)
self.assertIsNot(copied, singleton_gate)
self.assertEqual(singleton_gate, copied)
self.assertNotEqual(singleton_gate.label, copied.label)
def test_deepcopy_with_condition(self):
gate = CCXGate().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 = CHGate(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 = CHGate()
label_gate = CHGate(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 = CHGate()
gate = CHGate(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(2)
gate = CHGate()
qc.append(gate, [0, 1])
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 = CHGate(label="special")
qc = QuantumCircuit(2)
qc.append(gate, [0, 1])
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(2, 1)
gate = CHGate().c_if(qc.cregs[0], 0)
qc.append(gate, [0, 1])
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(2, 1)
gate = CHGate(label="conditionally special").c_if(qc.cregs[0], 0)
qc.append(gate, [0, 1])
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 = CHGate()
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 = CXGate()
self.assertFalse(gate.mutable)
label_gate = CXGate(label="foo")
self.assertTrue(label_gate.mutable)
self.assertIsNot(gate, label_gate)
def test_to_mutable(self):
gate = CXGate()
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 = CZGate()
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 = CZGate(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_inner_gate_label(self):
inner_gate = HGate(label="my h gate")
controlled_gate = inner_gate.control()
self.assertTrue(controlled_gate.mutable)
self.assertEqual("my h gate", controlled_gate.base_gate.label)
def test_inner_gate_label_outer_label_too(self):
inner_gate = HGate(label="my h gate")
controlled_gate = inner_gate.control(label="foo")
self.assertTrue(controlled_gate.mutable)
self.assertEqual("my h gate", controlled_gate.base_gate.label)
self.assertEqual("foo", controlled_gate.label)
def test_inner_outer_label_with_c_if(self):
inner_gate = HGate(label="my h gate")
controlled_gate = inner_gate.control(label="foo")
clbit = Clbit()
conditonal_controlled_gate = controlled_gate.c_if(clbit, 0)
self.assertTrue(conditonal_controlled_gate.mutable)
self.assertEqual("my h gate", conditonal_controlled_gate.base_gate.label)
self.assertEqual("foo", conditonal_controlled_gate.label)
self.assertEqual((clbit, 0), conditonal_controlled_gate.condition)
def test_inner_outer_label_with_c_if_deepcopy(self):
inner_gate = XGate(label="my h gate")
controlled_gate = inner_gate.control(label="foo")
clbit = Clbit()
conditonal_controlled_gate = controlled_gate.c_if(clbit, 0)
self.assertTrue(conditonal_controlled_gate.mutable)
self.assertEqual("my h gate", conditonal_controlled_gate.base_gate.label)
self.assertEqual("foo", conditonal_controlled_gate.label)
self.assertEqual((clbit, 0), conditonal_controlled_gate.condition)
copied = copy.deepcopy(conditonal_controlled_gate)
self.assertIsNot(conditonal_controlled_gate, copied)
self.assertTrue(copied.mutable)
self.assertEqual("my h gate", copied.base_gate.label)
self.assertEqual("foo", copied.label)
self.assertEqual((clbit, 0), copied.condition)
def test_inner_outer_label_pickle(self):
inner_gate = XGate(label="my h gate")
controlled_gate = inner_gate.control(label="foo")
self.assertTrue(controlled_gate.mutable)
self.assertEqual("my h gate", controlled_gate.base_gate.label)
self.assertEqual("foo", controlled_gate.label)
with io.BytesIO() as fd:
pickle.dump(controlled_gate, fd)
fd.seek(0)
copied = pickle.load(fd)
self.assertIsNot(controlled_gate, copied)
self.assertTrue(copied.mutable)
self.assertEqual("my h gate", copied.base_gate.label)
self.assertEqual("foo", copied.label)

View File

@ -2048,7 +2048,7 @@ class TestDagSubstitute(QiskitTestCase):
base.add_qubits(base_qubits)
base.add_clbits(base_clbits)
base.apply_operation_back(HGate(), [base_qubits[0]], [])
conditioned = CZGate()
conditioned = CZGate().to_mutable()
conditioned.condition = (base_clbits[0], True)
target = base.apply_operation_back(conditioned, base_qubits, [])
base.apply_operation_back(XGate(), [base_qubits[1]], [])
@ -2063,7 +2063,7 @@ class TestDagSubstitute(QiskitTestCase):
expected.add_clbits(base_clbits)
expected.apply_operation_back(HGate(), [base_qubits[0]], [])
expected.apply_operation_back(HGate(), [base_qubits[0]], [])
cx = CXGate()
cx = CXGate().to_mutable()
cx.condition = (base_clbits[0], True)
expected.apply_operation_back(cx, base_qubits, [])
expected.apply_operation_back(HGate(), [base_qubits[0]], [])
@ -2081,7 +2081,7 @@ class TestDagSubstitute(QiskitTestCase):
base.add_qubits(base_qubits)
base.add_creg(base_creg)
base.apply_operation_back(HGate(), [base_qubits[0]], [])
conditioned = CZGate()
conditioned = CZGate().to_mutable()
conditioned.condition = (base_creg, 3)
target = base.apply_operation_back(conditioned, base_qubits, [])
base.apply_operation_back(XGate(), [base_qubits[1]], [])
@ -2096,7 +2096,7 @@ class TestDagSubstitute(QiskitTestCase):
expected.add_creg(base_creg)
expected.apply_operation_back(HGate(), [base_qubits[0]], [])
expected.apply_operation_back(HGate(), [base_qubits[0]], [])
cx = CXGate()
cx = CXGate().to_mutable()
cx.condition = (base_creg, 3)
expected.apply_operation_back(cx, base_qubits, [])
expected.apply_operation_back(HGate(), [base_qubits[0]], [])
@ -2114,7 +2114,7 @@ class TestDagSubstitute(QiskitTestCase):
base.add_qubits(base_qubits)
base.add_clbits(base_clbits)
base.apply_operation_back(HGate(), [base_qubits[0]], [])
conditioned_cz = CZGate()
conditioned_cz = CZGate().to_mutable()
conditioned_cz.condition = (base_clbits[0], True)
target = base.apply_operation_back(conditioned_cz, base_qubits, [])
base.apply_operation_back(XGate(), [base_qubits[1]], [])
@ -2152,7 +2152,7 @@ class TestDagSubstitute(QiskitTestCase):
base.add_qubits(base_qubits)
base.add_clbits(base_clbits)
base.apply_operation_back(HGate(), [base_qubits[0]], [])
conditioned_cz = CZGate()
conditioned_cz = CZGate().to_mutable()
conditioned_cz.condition = (base_clbits[0], True)
target = base.apply_operation_back(conditioned_cz, base_qubits, [])
base.apply_operation_back(XGate(), [base_qubits[1]], [])
@ -2162,7 +2162,7 @@ class TestDagSubstitute(QiskitTestCase):
sub.cx(0, 1).c_if(0, True)
sub.h(0)
conditioned_cx = CXGate()
conditioned_cx = CXGate().to_mutable()
conditioned_cx.condition = conditioned_cz.condition
expected = DAGCircuit()
@ -2207,7 +2207,7 @@ class TestDagSubstitute(QiskitTestCase):
expected.add_qreg(base_qreg)
expected.add_creg(base_creg)
expected.add_creg(added_creg)
cx = CXGate()
cx = CXGate().to_mutable()
cx.condition = (added_creg, 3)
expected.apply_operation_back(cx, base_qreg, [])
self.assertEqual(base, expected)
@ -2248,7 +2248,7 @@ class TestDagSubstituteNode(QiskitTestCase):
dag.add_qreg(qr)
dag.add_creg(cr)
dag.apply_operation_back(HGate(), [qr[1]])
cx_gate = CXGate()
cx_gate = CXGate().to_mutable()
cx_gate.condition = (cr, 1)
node_to_be_replaced = dag.apply_operation_back(cx_gate, [qr[1], qr[0]])