diff --git a/doc/source/api-reference/qibo.rst b/doc/source/api-reference/qibo.rst index 60d852db5f..33947014cb 100644 --- a/doc/source/api-reference/qibo.rst +++ b/doc/source/api-reference/qibo.rst @@ -744,6 +744,23 @@ Mølmer–Sørensen (MS) :members: :member-order: bysource +Quantinuum native gates +^^^^^^^^^^^^^^^^^^^^^^^ + +U1q +""" + +.. autoclass:: qibo.gates.U1q + :members: + :member-order: bysource + +.. note:: + The other Quantinuum single-qubit and two-qubit native gates are + implemented in Qibo as: + - Pauli-:math:`Z` rotation: :class:`qibo.gates.RZ` + - Arbitrary :math:`ZZ` rotation: :class:`qibo.gates.RZZ` + - Fully-entangling :math:`ZZ`-interaction: :math:`R_{ZZ}(\\pi/2)` + _______________________ diff --git a/src/qibo/backends/npmatrices.py b/src/qibo/backends/npmatrices.py index de0f7103f0..217697f006 100644 --- a/src/qibo/backends/npmatrices.py +++ b/src/qibo/backends/npmatrices.py @@ -112,6 +112,9 @@ def U3(self, theta, phi, lam): dtype=self.dtype, ) + def U1q(self, theta, phi): + return self.U3(theta, phi - self.np.pi / 2, self.np.pi / 2 - phi) + @cached_property def CNOT(self): return self.np.array( diff --git a/src/qibo/gates/gates.py b/src/qibo/gates/gates.py index 7c3a9f08fb..792bd13ae7 100644 --- a/src/qibo/gates/gates.py +++ b/src/qibo/gates/gates.py @@ -892,6 +892,48 @@ def decompose(self) -> List[Gate]: ] +class U1q(_Un_): + """Native single-qubit gate in the Quantinuum platform. + + Corresponds to the following unitary matrix: + + .. math:: + \\begin{pmatrix} + \\cos\\left(\\frac{\\theta}{2}\\right) & + -i \\, e^{-i \\, \\phi} \\, \\sin\\left(\\frac{\\theta}{2}\\right) \\\\ + -i \\, e^{i \\, \\phi} \\, \\sin\\left(\\frac{\\theta}{2}\\right) & + \\cos\\left(\\frac{\\theta}{2}\\right) \\\\ + \\end{pmatrix} + + Note that + :math:`U_{1q}(\\theta, \\phi) = U_{3}(\\theta, \\phi - \\frac{\\pi}{2}, \\frac{\\pi}{2} - \\phi)`, + where :math:`U_{3}` is :class:`qibo.gates.U3`. + + Args: + q (int): the qubit id number. + theta (float): first rotation angle. + phi (float): second rotation angle. + trainable (bool): whether gate parameters can be updated using + :meth:`qibo.models.circuit.Circuit.set_parameters`. + Defaults to ``True``. + """ + + def __init__(self, q, theta, phi, trainable=True): + super().__init__(q, trainable=trainable) + self.name = "u1q" + self.draw_label = "U1q" + self.nparams = 2 + self._theta, self._phi = None, None + self.init_kwargs = {"theta": theta, "phi": phi, "trainable": trainable} + self.parameter_names = ["theta", "phi"] + self.parameters = theta, phi + + def _dagger(self) -> "Gate": + """""" + theta, phi = self.init_kwargs["theta"], self.init_kwargs["phi"] + return self.__class__(self.init_args[0], -theta, phi) + + class CNOT(Gate): """The Controlled-NOT gate. diff --git a/tests/test_gates_gates.py b/tests/test_gates_gates.py index c441fea22e..4020abc97c 100644 --- a/tests/test_gates_gates.py +++ b/tests/test_gates_gates.py @@ -437,6 +437,30 @@ def test_u3(backend, seed_state, seed_observable): assert gates.U3(0, theta, phi, lam).unitary +@pytest.mark.parametrize("seed_state", list(range(1, 10 + 1))) +def test_u1q(backend, seed_state): + nqubits = 1 + theta, phi = np.random.rand(2) + + initial_state = random_statevector(2**nqubits, seed=seed_state, backend=backend) + final_state = apply_gates( + backend, [gates.U1q(0, theta, phi)], initial_state=initial_state + ) + cost, sint = np.cos(theta / 2), np.sin(theta / 2) + + matrix = np.array( + [[cost, -1j * np.exp(-1j * phi) * sint], [-1j * np.exp(1j * phi) * sint, cost]] + ) + matrix = backend.cast(matrix, dtype=matrix.dtype) + + target_state = np.dot(matrix, initial_state) + + backend.assert_allclose(final_state, target_state) + + assert not gates.U1q(0, theta, phi).clifford + assert gates.U1q(0, theta, phi).unitary + + @pytest.mark.parametrize("applyx", [False, True]) def test_cnot(backend, applyx): if applyx: @@ -1408,6 +1432,7 @@ def test_controlled_unitary_matrix(backend): ("U1", (0, 0.1)), ("U2", (0, 0.2, 0.3)), ("U3", (0, 0.1, 0.2, 0.3)), + ("U1q", (0, 0.1, 0.2)), ("CNOT", (0, 1)), ("CZ", (0, 1)), ("CSX", (0, 1)),