Skip to content

Commit

Permalink
Merge pull request #525 from qiboteam/newgates
Browse files Browse the repository at this point in the history
Add T, S gates
  • Loading branch information
scarrazza committed Dec 22, 2021
2 parents d2c7e1a + 1394fd9 commit 20fb08d
Show file tree
Hide file tree
Showing 12 changed files with 326 additions and 9 deletions.
14 changes: 14 additions & 0 deletions doc/source/api-reference/qibo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,20 @@ Pauli Z (Z)
:members:
:member-order: bysource

S gate (S)
"""""""""""

.. autoclass:: qibo.abstractions.gates.S
:members:
:member-order: bysource

T gate (T)
"""""""""""

.. autoclass:: qibo.abstractions.gates.T
:members:
:member-order: bysource

Identity (I)
""""""""""""

Expand Down
91 changes: 90 additions & 1 deletion src/qibo/abstractions/gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"cx": "CNOT", "swap": "SWAP", "cz": "CZ",
"crx": "CRX", "cry": "CRY", "crz": "CRZ",
"cu1": "CU1", "cu3": "CU3",
"ccx": "TOFFOLI", "id": "I"}
"ccx": "TOFFOLI", "id": "I", "s": "S",
"sdg": "SDG", "t": "T", "tdg": "TDG"}
PARAMETRIZED_GATES = {"rx", "ry", "rz", "u1", "u2", "u3",
"crx", "cry", "crz", "cu1", "cu3"}

Expand Down Expand Up @@ -162,6 +163,94 @@ def controlled_by(self, *q):
return gate


class S(Gate):
"""The S gate.
Corresponds to the following unitary matrix
.. math::
\\begin{pmatrix}
1 & 0 \\\\
0 & i \\\\
\\end{pmatrix}
Args:
q (int): the qubit id number.
"""

def __init__(self, q):
super().__init__()
self.name = "s"
self.target_qubits = (q,)
self.init_args = [q]


class SDG(Gate):
"""The conjugate transpose of the S gate.
Corresponds to the following unitary matrix
.. math::
\\begin{pmatrix}
1 & 0 \\\\
0 & -i \\\\
\\end{pmatrix}
Args:
q (int): the qubit id number.
"""

def __init__(self, q):
super().__init__()
self.name = "sdg"
self.target_qubits = (q,)
self.init_args = [q]


class T(Gate):
"""The T gate.
Corresponds to the following unitary matrix
.. math::
\\begin{pmatrix}
1 & 0 \\\\
0 & e^{i \\pi / 4} \\\\
\\end{pmatrix}
Args:
q (int): the qubit id number.
"""

def __init__(self, q):
super().__init__()
self.name = "t"
self.target_qubits = (q,)
self.init_args = [q]


class TDG(Gate):
"""The conjugate transpose of the T gate.
Corresponds to the following unitary matrix
.. math::
\\begin{pmatrix}
1 & 0 \\\\
0 & e^{-i \\pi / 4} \\\\
\\end{pmatrix}
Args:
q (int): the qubit id number.
"""

def __init__(self, q):
super().__init__()
self.name = "tdg"
self.target_qubits = (q,)
self.init_args = [q]


class I(Gate):
"""The identity gate.
Expand Down
22 changes: 21 additions & 1 deletion src/qibo/backends/matrices.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

class Matrices:

_NAMES = ["I", "H", "X", "Y", "Z", "CNOT", "CZ", "SWAP", "TOFFOLI"]
_NAMES = ["I", "H", "X", "Y", "Z", "S", "T", "CNOT", "CZ", "SWAP", "TOFFOLI"]

def __init__(self, backend):
self.backend = backend
Expand All @@ -12,6 +12,8 @@ def __init__(self, backend):
self._X = None
self._Y = None
self._Z = None
self._S = None
self._T = None
self._CNOT = None
self._CZ = None
self._SWAP = None
Expand Down Expand Up @@ -46,6 +48,14 @@ def Y(self):
def Z(self):
return self._Z

@property
def S(self):
return self._S

@property
def T(self):
return self._T

@property
def CNOT(self):
return self._CNOT
Expand Down Expand Up @@ -85,6 +95,16 @@ def _setZ(self):
m[1, 1] = -1
self._Z = self.backend.cast(m)

def _setS(self):
m = np.eye(2, dtype=self.dtype)
m[1, 1] = 1j
self._S = self.backend.cast(m)

def _setT(self):
m = np.eye(2, dtype=self.dtype)
m[1, 1] = np.exp(1j * np.pi / 4.0)
self._T = self.backend.cast(m)

def _setCNOT(self):
m = np.eye(4, dtype=self.dtype)
m[2, 2], m[2, 3] = 0, 1
Expand Down
54 changes: 53 additions & 1 deletion src/qibo/core/gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,58 @@ def _construct_unitary(self):
return K.matrices.Z


class S(MatrixGate, abstract_gates.S):

def __init__(self, q):
MatrixGate.__init__(self)
abstract_gates.S.__init__(self, q)

def _construct_unitary(self):
return K.matrices.S

def _dagger(self) -> "SDG":
return SDG(*self.init_args)


class SDG(MatrixGate, abstract_gates.SDG):

def __init__(self, q):
MatrixGate.__init__(self)
abstract_gates.SDG.__init__(self, q)

def _construct_unitary(self):
return K.conj(K.matrices.S) # no need to transpose because it's diagonal

def _dagger(self) -> "S":
return S(*self.init_args)


class T(MatrixGate, abstract_gates.T):

def __init__(self, q):
MatrixGate.__init__(self)
abstract_gates.T.__init__(self, q)

def _construct_unitary(self):
return K.matrices.T

def _dagger(self) -> "TDG":
return TDG(*self.init_args)


class TDG(MatrixGate, abstract_gates.TDG):

def __init__(self, q):
MatrixGate.__init__(self)
abstract_gates.TDG.__init__(self, q)

def _construct_unitary(self):
return K.conj(K.matrices.T) # no need to transpose because it's diagonal

def _dagger(self) -> "T":
return T(*self.init_args)


class I(BackendGate, abstract_gates.I):

def __init__(self, *q):
Expand Down Expand Up @@ -563,7 +615,7 @@ def _construct_unitary(self):
matrix[3, 3] = p.exp(-1j * phi)
return K.cast(matrix)

def _dagger(self) -> "GenerelizedfSim":
def _dagger(self) -> "GeneralizedfSim":
unitary, phi = self.parameters
if isinstance(unitary, K.native_types):
ud = K.conj(K.transpose(unitary))
Expand Down
64 changes: 64 additions & 0 deletions src/qibo/tests/test_abstract_circuit_qasm.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,32 @@ def test_simple():
h q[1];"""
assert_strings_equal(c.to_qasm(), target)

def test_singlequbit_gates():
c = Circuit(2)
c.add(gates.H(0))
c.add(gates.X(1))
c.add(gates.Y(0))
c.add(gates.Z(1))
c.add(gates.S(0))
c.add(gates.SDG(1))
c.add(gates.T(0))
c.add(gates.TDG(1))
c.add(gates.I(0))
target = f"""// Generated by QIBO {__version__}
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q[0];
x q[1];
y q[0];
z q[1];
s q[0];
sdg q[1];
t q[0];
tdg q[1];
id q[0];"""
assert_strings_equal(c.to_qasm(), target)


def test_unknown_gate_error():
""""Check that using `to_qasm` with not supported gates raises error."""
Expand Down Expand Up @@ -236,6 +262,44 @@ def test_from_qasm_multiqubit_gates():
assert c.queue[4].qubits == (1, 2, 0)


def test_from_qasm_singlequbit_gates():
target = f"""// Generated by QIBO {__version__}
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q[0];
x q[1];
y q[0];
z q[1];
s q[0];
sdg q[1];
t q[0];
tdg q[1];
id q[0];"""
c = Circuit.from_qasm(target)
assert c.nqubits == 2
assert c.depth == 5
assert c.ngates == 9
assert isinstance(c.queue[0], gates.H)
assert c.queue[0].qubits == (0,)
assert isinstance(c.queue[1], gates.X)
assert c.queue[1].qubits == (1,)
assert isinstance(c.queue[2], gates.Y)
assert c.queue[2].qubits == (0,)
assert isinstance(c.queue[3], gates.Z)
assert c.queue[3].qubits == (1,)
assert isinstance(c.queue[4], gates.S)
assert c.queue[4].qubits == (0,)
assert isinstance(c.queue[5], gates.SDG)
assert c.queue[5].qubits == (1,)
assert isinstance(c.queue[6], gates.T)
assert c.queue[6].qubits == (0,)
assert isinstance(c.queue[7], gates.TDG)
assert c.queue[7].qubits == (1,)
assert isinstance(c.queue[8], gates.I)
assert c.queue[8].qubits == (0,)


def test_from_qasm_multiple_qregs():
target = f"""// Generated by QIBO {__version__}
OPENQASM 2.0;
Expand Down
4 changes: 3 additions & 1 deletion src/qibo/tests/test_abstract_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
from qibo.abstractions import gates


@pytest.mark.parametrize("gatename", ["H", "X", "Y", "Z", "I", "Align"])
@pytest.mark.parametrize("gatename", ["H", "X", "Y", "Z",
"S", "SDG", "T", "TDG",
"I", "Align"])
def test_one_qubit_gates_init(gatename):
gate = getattr(gates, gatename)(0)
assert gate.target_qubits == (0,)
Expand Down
2 changes: 2 additions & 0 deletions src/qibo/tests/test_backends_matrices.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ def test_matrices(backend, dtype):
"X": np.array([[0, 1], [1, 0]]),
"Y": np.array([[0, -1j], [1j, 0]]),
"Z": np.array([[1, 0], [0, -1]]),
"S": np.array([[1, 0], [0, 1j]]),
"T": np.array([[1, 0], [0, np.exp(1j * np.pi / 4.0)]]),
"CNOT": np.array([[1, 0, 0, 0], [0, 1, 0, 0],
[0, 0, 0, 1], [0, 0, 1, 0]]),
"CZ": np.array([[1, 0, 0, 0], [0, 1, 0, 0],
Expand Down
5 changes: 3 additions & 2 deletions src/qibo/tests/test_cirq.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ def test_x_decompose_with_cirq(target, controls, free):
@pytest.mark.parametrize(("gate_name", "nqubits", "ndevices"),
[("H", 3, None), ("H", 3, 2),
("X", 2, None), ("X", 2, 2),
("Y", 1, None), ("Z", 1, None)])
("Y", 1, None), ("Z", 1, None),
("S", 4, None), ("T", 4, None)])
def test_one_qubit_gates(backend, gate_name, nqubits, ndevices):
"""Check simple one-qubit gates."""
targets = random_active_qubits(nqubits, nactive=1)
Expand Down Expand Up @@ -211,7 +212,7 @@ def test_unitary_matrix_gate(backend, nqubits, ndevices):
@pytest.mark.parametrize(("gate_name", "nqubits", "ndevices"),
[("H", 3, None), ("Z", 4, None), ("Y", 5, 4),
("X", 6, None), ("H", 7, 2), ("Z", 8, 8),
("Y", 12, 16)])
("Y", 12, 16), ("S", 13, 4), ("T", 9, 2)])
def test_one_qubit_gates_controlled_by(backend, gate_name, nqubits, ndevices):
"""Check one-qubit gates controlled on arbitrary number of qubits."""
all_qubits = np.arange(nqubits)
Expand Down
25 changes: 25 additions & 0 deletions src/qibo/tests/test_core_circuit_qasm.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,31 @@ def test_simple_cirq(backend):
np.testing.assert_allclose(final_state_c3, final_state_c2, atol=_atol)


def test_singlequbit_gates_cirq(backend):
c1 = Circuit(2)
c1.add(gates.H(0))
c1.add(gates.X(1))
c1.add(gates.Y(0))
c1.add(gates.Z(1))
c1.add(gates.S(0))
c1.add(gates.SDG(1))
c1.add(gates.T(0))
c1.add(gates.TDG(1))
c1.add(gates.I(0))
final_state_c1 = c1()

c2 = circuit_from_qasm(c1.to_qasm())
c2depth = len(cirq.Circuit(c2.all_operations()))
assert c1.depth == c2depth
final_state_c2 = cirq.Simulator().simulate(c2).final_state_vector # pylint: disable=no-member
np.testing.assert_allclose(final_state_c1, final_state_c2, atol=_atol)

c3 = Circuit.from_qasm(c2.to_qasm())
assert c3.depth == c2depth
final_state_c3 = c3()
np.testing.assert_allclose(final_state_c3, final_state_c2, atol=_atol)


def test_multiqubit_gates_cirq(backend):
c1 = Circuit(2)
c1.add(gates.H(0))
Expand Down
Loading

0 comments on commit 20fb08d

Please sign in to comment.