Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 37 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
7087462
Use singletons for standard library unparameterized, non-controlled g…
mtreinish Apr 29, 2023
330beb8
Fix Python 3.8 compatibility
mtreinish Jun 20, 2023
c653304
Simplify qpy deserialization label handling
mtreinish Jun 20, 2023
7d54237
Remove unused classmethod decorator
mtreinish Jun 20, 2023
1743262
Fix lint
mtreinish Jun 20, 2023
ce6cf24
Merge remote-tracking branch 'origin/main' into singleton-gates-poc
mtreinish Jun 20, 2023
aadd8cb
Fix timeline drawer on output of legacy DD pass
mtreinish Jun 20, 2023
ffa051c
Fix doc build
mtreinish Jun 21, 2023
09dbcd7
Add methods to deal with mutability of singleton gates
mtreinish Jun 21, 2023
d1435cd
Merge branch 'main' into singleton-gates-poc
mtreinish Jun 21, 2023
7d91466
Merge remote-tracking branch 'origin/main' into singleton-gates-poc
mtreinish Jul 5, 2023
c29887a
Disallow custom attribute on singleton instances
mtreinish Jul 5, 2023
4d17c9f
Merge branch 'main' into singleton-gates-poc
mtreinish Jul 17, 2023
a788da4
Fix rebase error
mtreinish Jul 17, 2023
a03cce5
Merge branch 'main' into singleton-gates-poc
mtreinish Jul 26, 2023
9b49dc3
Fix rebase issues
mtreinish Aug 3, 2023
43f41d3
Merge remote-tracking branch 'origin/main' into singleton-gates-poc
mtreinish Aug 3, 2023
4e9911a
Merge branch 'main' into singleton-gates-poc
mtreinish Aug 23, 2023
95ecb6d
Merge remote-tracking branch 'origin/main' into singleton-gates-poc
mtreinish Sep 7, 2023
96cf467
Fix module docstring
mtreinish Sep 7, 2023
e1a0ef4
Add .mutable and .to_mutable() to Instruction
mtreinish Sep 7, 2023
3784da4
Unify handling of gates in scheduling passes
mtreinish Sep 7, 2023
b90eebb
Remove unnecessary deepcopy in substitute_node_wtih_dag
mtreinish Sep 7, 2023
4b242e1
Fix logic for SingletonGate.copy
mtreinish Sep 7, 2023
d9ff18a
Update Singleton Gate class docstring
mtreinish Sep 7, 2023
86ea2a0
Remove unused imports
mtreinish Sep 7, 2023
32d5395
Update release notes
mtreinish Sep 7, 2023
306fcc8
Merge branch 'main' into singleton-gates-poc
mtreinish Sep 7, 2023
75eb6e6
Fix release note typos
mtreinish Sep 18, 2023
efc7a12
Merge branch 'main' into singleton-gates-poc
mtreinish Sep 18, 2023
b95ac96
Improve setattr performance
mtreinish Sep 18, 2023
8deb2cb
Fix deepcopy logic
mtreinish Sep 18, 2023
2479b12
Add check for kwargs up front
mtreinish Sep 18, 2023
9c9bc09
Fix docs typos
mtreinish Sep 18, 2023
37a30cb
Add comment on to_mutable __init__ call
mtreinish Sep 18, 2023
ba04a8e
Fix lint
mtreinish Sep 18, 2023
7f99a09
Handle positional initialization arguments in SingletonGate
mtreinish Sep 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions qiskit/circuit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@
InstructionSet
Operation
EquivalenceLibrary
SingletonGate

Control Flow Operations
-----------------------
Expand Down Expand Up @@ -366,6 +367,7 @@

# 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
Expand Down
4 changes: 3 additions & 1 deletion qiskit/circuit/add_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ def add_control(
# attempt decomposition
operation._define()
cgate = control(operation, num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)
cgate.base_gate.label = operation.label
if operation.label is not None:
cgate.base_gate = cgate.base_gate.to_mutable()
cgate.base_gate.label = operation.label
return cgate


Expand Down
12 changes: 10 additions & 2 deletions qiskit/circuit/gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@
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:
Expand All @@ -34,7 +42,7 @@ def __init__(self, name: str, num_qubits: int, params: list, label: str | None =
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
Expand Down
33 changes: 30 additions & 3 deletions qiskit/circuit/instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,43 @@ def __init__(self, name, num_qubits, num_clbits, params, duration=None, unit="dt
self._label = label
# tuple (ClassicalRegister, int), tuple (Clbit, bool) or tuple (Clbit, int)
# when the instruction has a conditional ("if")
self.condition = None
self._condition = None
# list of instructions (and their contexts) that this instruction is composed of
# empty definition means opaque or fundamental instruction
self._definition = None

self._duration = duration
self._unit = unit

self.params = params # must be at last (other properties may be required for validation)

@property
def mutable(self) -> bool:
"""Is this instance is a mutable unique instance or not.

If this attribute is ``False`` the gate instance is a shared singleton
and is not mutable.
"""
return True

def to_mutable(self):
"""Return a mutable copy of this gate.

This method will return a new mutable copy of this gate instance.
If a singleton instance is being used this will be a new unique
instance that can be mutated. If the instance is already mutable it
will be a deepcopy of that instance.
"""
return self.copy()

@property
def condition(self):
"""The classical condition on the instruction."""
return self._condition

@condition.setter
def condition(self, condition):
self._condition = condition

def __eq__(self, other):
"""Two instructions are the same if they have the same name,
same dimensions, and same params.
Expand Down Expand Up @@ -409,7 +436,7 @@ def c_if(self, classical, val):
# Casting the conditional value as Boolean when
# the classical condition is on a classical bit.
val = bool(val)
self.condition = (classical, val)
self._condition = (classical, val)
return self

def copy(self, name=None):
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/instructionset.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def c_if(self, classical: Clbit | ClassicalRegister | int, val: int) -> "Instruc
if self._requester is not None:
classical = self._requester(classical)
for instruction in self._instructions:
instruction.operation.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
Expand Down
13 changes: 9 additions & 4 deletions qiskit/circuit/library/standard_gates/dcx.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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):
"""
Expand Down
12 changes: 8 additions & 4 deletions qiskit/circuit/library/standard_gates/ecr.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@
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


@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
Expand Down Expand Up @@ -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"
Comment on lines +89 to +90
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here (and everywhere), is there a reason not to use "dt" directly as the default value in the signature?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason is there wasn't a way to differentiate between a user specified value and the default when __new__ is called. I guess we can change the __new__ check if kwargs["unit"] == "dt" and then do that. I'll give that a try locally and push it up if it works.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's a reason, it's already fine by me.

super().__init__(
"ecr", 2, [], label=label, _condition=_condition, duration=duration, unit=unit
)

def _define(self):
"""
Expand Down
30 changes: 22 additions & 8 deletions qiskit/circuit/library/standard_gates/h.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
from .t import TGate, TdgGate
Expand All @@ -25,7 +25,7 @@


@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
Expand Down Expand Up @@ -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):
"""
Expand Down Expand Up @@ -94,8 +98,7 @@ def control(
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)

Expand Down Expand Up @@ -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):
Expand Down
12 changes: 8 additions & 4 deletions qiskit/circuit/library/standard_gates/i.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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."""
Expand Down
12 changes: 8 additions & 4 deletions qiskit/circuit/library/standard_gates/iswap.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@

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

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.
Expand Down Expand Up @@ -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):
"""
Expand Down
22 changes: 15 additions & 7 deletions qiskit/circuit/library/standard_gates/s.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
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
Expand All @@ -29,7 +29,7 @@


@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).
Expand Down Expand Up @@ -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):
"""
Expand Down Expand Up @@ -90,7 +94,7 @@ def power(self, exponent: float):


@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.
Expand Down Expand Up @@ -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):
"""
Expand Down
Loading