Skip to content

Commit

Permalink
Add singleton lookup-key logic (#11040)
Browse files Browse the repository at this point in the history
* Add singleton lookup-key logic

This adds overridable logic for singleton classes to define a "key"
function, which takes the same arguments as its `__init__` and returns a
lookup key that is used as a comparison for returning singletons.  This
lets the standard singleton gates easily return the singleton instance
even when `label` is explicitly set to the default, such as happens
using the idiomatic `QuantumCircuit` methods like `x`.

This keying logic has a simple logical extension to allow more than one
singleton to exist for a single base class.  This functionality is not
yet used by the Qiskit standard library, but can easily be used to
make both closed- and open-controlled versions of gates singletons, or
to allow specific hard-coded parameters for (e.g.) `RZGate` singletons.

The decision on which gates to extend this to can be done in follow-up
commits later.

* Fix unnecessary statement

* 🇺🇸

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

* Update documentation comments

---------

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
  • Loading branch information
jakelishman and mtreinish committed Oct 18, 2023
1 parent 976d765 commit a881215
Show file tree
Hide file tree
Showing 15 changed files with 558 additions and 105 deletions.
4 changes: 3 additions & 1 deletion qiskit/circuit/library/standard_gates/dcx.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

"""Double-CNOT gate."""

from qiskit.circuit.singleton import SingletonGate
from qiskit.circuit.singleton import SingletonGate, stdlib_singleton_key
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array

Expand Down Expand Up @@ -52,6 +52,8 @@ def __init__(self, label=None, *, duration=None, unit="dt"):
"""Create new DCX gate."""
super().__init__("dcx", 2, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def _define(self):
"""
gate dcx a, b { cx a, b; cx b, a; }
Expand Down
4 changes: 3 additions & 1 deletion qiskit/circuit/library/standard_gates/ecr.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from qiskit.circuit._utils import with_gate_array
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit.singleton import SingletonGate
from qiskit.circuit.singleton import SingletonGate, stdlib_singleton_key
from .rzx import RZXGate
from .x import XGate

Expand Down Expand Up @@ -88,6 +88,8 @@ def __init__(self, label=None, *, duration=None, unit="dt"):
"""Create new ECR gate."""
super().__init__("ecr", 2, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def _define(self):
"""
gate ecr a, b { rzx(pi/4) a, b; x a; rzx(-pi/4) a, b;}
Expand Down
6 changes: 5 additions & 1 deletion qiskit/circuit/library/standard_gates/h.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from math import sqrt, pi
from typing import Optional, Union
import numpy
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate, stdlib_singleton_key
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array

Expand Down Expand Up @@ -55,6 +55,8 @@ def __init__(self, label: Optional[str] = None, *, duration=None, unit="dt"):
"""Create new H gate."""
super().__init__("h", 1, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def _define(self):
"""
gate h a { u2(0,pi) a; }
Expand Down Expand Up @@ -181,6 +183,8 @@ def __init__(
_base_label=_base_label,
)

_singleton_lookup_key = stdlib_singleton_key(num_ctrl_qubits=1)

def _define(self):
"""
gate ch a,b {
Expand Down
4 changes: 3 additions & 1 deletion qiskit/circuit/library/standard_gates/i.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"""Identity gate."""

from typing import Optional
from qiskit.circuit.singleton import SingletonGate
from qiskit.circuit.singleton import SingletonGate, stdlib_singleton_key
from qiskit.circuit._utils import with_gate_array


Expand Down Expand Up @@ -49,6 +49,8 @@ def __init__(self, label: Optional[str] = None, *, duration=None, unit="dt"):
"""Create new Identity gate."""
super().__init__("id", 1, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def inverse(self):
"""Invert this gate."""
return IGate() # self-inverse
Expand Down
4 changes: 3 additions & 1 deletion qiskit/circuit/library/standard_gates/iswap.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import numpy as np

from qiskit.circuit.singleton import SingletonGate
from qiskit.circuit.singleton import SingletonGate, stdlib_singleton_key
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array

Expand Down Expand Up @@ -89,6 +89,8 @@ def __init__(self, label: Optional[str] = None, *, duration=None, unit="dt"):
"""Create new iSwap gate."""
super().__init__("iswap", 2, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def _define(self):
"""
gate iswap a,b {
Expand Down
10 changes: 9 additions & 1 deletion qiskit/circuit/library/standard_gates/s.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import numpy

from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate, stdlib_singleton_key
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array

Expand Down Expand Up @@ -61,6 +61,8 @@ def __init__(self, label: Optional[str] = None, *, duration=None, unit="dt"):
"""Create new S gate."""
super().__init__("s", 1, [], label=label, duration=None, unit="dt")

_singleton_lookup_key = stdlib_singleton_key()

def _define(self):
"""
gate s a { u1(pi/2) a; }
Expand Down Expand Up @@ -124,6 +126,8 @@ def __init__(self, label: Optional[str] = None, *, duration=None, unit="dt"):
"""Create new Sdg gate."""
super().__init__("sdg", 1, [], label=label, duration=None, unit="dt")

_singleton_lookup_key = stdlib_singleton_key()

def _define(self):
"""
gate sdg a { u1(-pi/2) a; }
Expand Down Expand Up @@ -205,6 +209,8 @@ def __init__(
unit=unit,
)

_singleton_lookup_key = stdlib_singleton_key(num_ctrl_qubits=1)

def _define(self):
"""
gate cs a,b { h b; cp(pi/2) a,b; h b; }
Expand Down Expand Up @@ -276,6 +282,8 @@ def __init__(
unit=unit,
)

_singleton_lookup_key = stdlib_singleton_key(num_ctrl_qubits=1)

def _define(self):
"""
gate csdg a,b { h b; cp(-pi/2) a,b; h b; }
Expand Down
6 changes: 5 additions & 1 deletion qiskit/circuit/library/standard_gates/swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from typing import Optional, Union
import numpy
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate, stdlib_singleton_key
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array

Expand Down Expand Up @@ -62,6 +62,8 @@ def __init__(self, label: Optional[str] = None, *, duration=None, unit="dt"):
"""Create new SWAP gate."""
super().__init__("swap", 2, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def _define(self):
"""
gate swap a,b { cx a,b; cx b,a; cx a,b; }
Expand Down Expand Up @@ -213,6 +215,8 @@ def __init__(
unit=unit,
)

_singleton_lookup_key = stdlib_singleton_key(num_ctrl_qubits=1)

def _define(self):
"""
gate cswap a,b,c
Expand Down
8 changes: 7 additions & 1 deletion qiskit/circuit/library/standard_gates/sx.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from math import pi
from typing import Optional, Union
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate, stdlib_singleton_key
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array

Expand Down Expand Up @@ -66,6 +66,8 @@ def __init__(self, label: Optional[str] = None, *, duration=None, unit="dt"):
"""Create new SX gate."""
super().__init__("sx", 1, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def _define(self):
"""
gate sx a { rz(-pi/2) a; h a; rz(-pi/2); }
Expand Down Expand Up @@ -145,6 +147,8 @@ def __init__(self, label: Optional[str] = None, *, duration=None, unit="dt"):
"""Create new SXdg gate."""
super().__init__("sxdg", 1, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def _define(self):
"""
gate sxdg a { rz(pi/2) a; h a; rz(pi/2); }
Expand Down Expand Up @@ -245,6 +249,8 @@ def __init__(
unit=unit,
)

_singleton_lookup_key = stdlib_singleton_key(num_ctrl_qubits=1)

def _define(self):
"""
gate csx a,b { h b; cu1(pi/2) a,b; h b; }
Expand Down
6 changes: 5 additions & 1 deletion qiskit/circuit/library/standard_gates/t.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import numpy

from qiskit.circuit.singleton import SingletonGate
from qiskit.circuit.singleton import SingletonGate, stdlib_singleton_key
from qiskit.circuit.library.standard_gates.p import PhaseGate
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array
Expand Down Expand Up @@ -59,6 +59,8 @@ def __init__(self, label: Optional[str] = None, *, duration=None, unit="dt"):
"""Create new T gate."""
super().__init__("t", 1, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def _define(self):
"""
gate t a { u1(pi/4) a; }
Expand Down Expand Up @@ -120,6 +122,8 @@ def __init__(self, label: Optional[str] = None, *, duration=None, unit="dt"):
"""Create new Tdg gate."""
super().__init__("tdg", 1, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def _define(self):
"""
gate tdg a { u1(pi/4) a; }
Expand Down
45 changes: 34 additions & 11 deletions qiskit/circuit/library/standard_gates/x.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import numpy
from qiskit.utils.deprecation import deprecate_func
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate, stdlib_singleton_key
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import _ctrl_state_to_int, with_gate_array, with_controlled_gate_array

Expand Down Expand Up @@ -75,6 +75,8 @@ def __init__(self, label: Optional[str] = None, *, duration=None, unit="dt"):
"""Create new X gate."""
super().__init__("x", 1, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def _define(self):
"""
gate x a { u3(pi,0,pi) a; }
Expand Down Expand Up @@ -210,6 +212,8 @@ def __init__(
unit=unit,
)

_singleton_lookup_key = stdlib_singleton_key(num_ctrl_qubits=1)

def control(
self,
num_ctrl_qubits: int = 1,
Expand Down Expand Up @@ -333,6 +337,8 @@ def __init__(
unit=unit,
)

_singleton_lookup_key = stdlib_singleton_key(num_ctrl_qubits=2)

def _define(self):
"""
gate ccx a,b,c
Expand Down Expand Up @@ -442,6 +448,8 @@ def __init__(self, label: Optional[str] = None, *, duration=None, unit="dt"):
"""Create a new simplified CCX gate."""
super().__init__("rccx", 3, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def _define(self):
"""
gate rccx a,b,c
Expand Down Expand Up @@ -519,6 +527,8 @@ def __init__(
unit=unit,
)

_singleton_lookup_key = stdlib_singleton_key(num_ctrl_qubits=3)

def _define(self):
"""
gate c3sqrtx a,b,c,d
Expand Down Expand Up @@ -628,6 +638,8 @@ def __init__(
unit=unit,
)

_singleton_lookup_key = stdlib_singleton_key(num_ctrl_qubits=3)

# seems like open controls not hapening?
def _define(self):
"""
Expand Down Expand Up @@ -720,9 +732,12 @@ def control(
"""
ctrl_state = _ctrl_state_to_int(ctrl_state, num_ctrl_qubits)
new_ctrl_state = (self.ctrl_state << num_ctrl_qubits) | ctrl_state
gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits + 3, label=label, ctrl_state=new_ctrl_state)
gate.base_gate.label = self.label
return gate
return MCXGate(
num_ctrl_qubits=num_ctrl_qubits + 3,
label=label,
ctrl_state=new_ctrl_state,
_base_label=self.label,
)

def inverse(self):
"""Invert this gate. The C4X is its own inverse."""
Expand Down Expand Up @@ -767,6 +782,8 @@ def __init__(self, label: Optional[str] = None, *, duration=None, unit="dt"):
"""Create a new RC3X gate."""
super().__init__("rcccx", 4, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def _define(self):
"""
gate rc3x a,b,c,d
Expand Down Expand Up @@ -859,6 +876,8 @@ def __init__(
unit=unit,
)

_singleton_lookup_key = stdlib_singleton_key(num_ctrl_qubits=4)

# seems like open controls not hapening?
def _define(self):
"""
Expand Down Expand Up @@ -929,9 +948,12 @@ def control(
"""
ctrl_state = _ctrl_state_to_int(ctrl_state, num_ctrl_qubits)
new_ctrl_state = (self.ctrl_state << num_ctrl_qubits) | ctrl_state
gate = MCXGate(num_ctrl_qubits=num_ctrl_qubits + 4, label=label, ctrl_state=new_ctrl_state)
gate.base_gate.label = self.label
return gate
return MCXGate(
num_ctrl_qubits=num_ctrl_qubits + 4,
label=label,
ctrl_state=new_ctrl_state,
_base_label=self.label,
)

def inverse(self):
"""Invert this gate. The C4X is its own inverse."""
Expand Down Expand Up @@ -1052,11 +1074,12 @@ def control(
"""
if ctrl_state is None:
# use __class__ so this works for derived classes
gate = self.__class__(
self.num_ctrl_qubits + num_ctrl_qubits, label=label, ctrl_state=ctrl_state
return self.__class__(
self.num_ctrl_qubits + num_ctrl_qubits,
label=label,
ctrl_state=ctrl_state,
_base_label=self.label,
)
gate.base_gate.label = self.label
return gate
return super().control(num_ctrl_qubits, label=label, ctrl_state=ctrl_state)


Expand Down
6 changes: 5 additions & 1 deletion qiskit/circuit/library/standard_gates/y.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from typing import Optional, Union

# pylint: disable=cyclic-import
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate
from qiskit.circuit.singleton import SingletonGate, SingletonControlledGate, stdlib_singleton_key
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import with_gate_array, with_controlled_gate_array

Expand Down Expand Up @@ -74,6 +74,8 @@ def __init__(self, label: Optional[str] = None, *, duration=None, unit="dt"):
"""Create new Y gate."""
super().__init__("y", 1, [], label=label, duration=duration, unit=unit)

_singleton_lookup_key = stdlib_singleton_key()

def _define(self):
# pylint: disable=cyclic-import
from qiskit.circuit.quantumcircuit import QuantumCircuit
Expand Down Expand Up @@ -195,6 +197,8 @@ def __init__(
unit=unit,
)

_singleton_lookup_key = stdlib_singleton_key(num_ctrl_qubits=1)

def _define(self):
"""
gate cy a,b { sdg b; cx a,b; s b; }
Expand Down
Loading

0 comments on commit a881215

Please sign in to comment.