Source code for qrisp.circuit.standard_operations

# ********************************************************************************
# * Copyright (c) 2026 the Qrisp authors
# *
# * This program and the accompanying materials are made available under the
# * terms of the Eclipse Public License 2.0 which is available at
# * http://www.eclipse.org/legal/epl-2.0.
# *
# * This Source Code may also be made available under the following Secondary
# * Licenses when the conditions for such availability set forth in the Eclipse
# * Public License, v. 2.0 are satisfied: GNU General Public License, version 2
# * with the GNU Classpath Exception which is
# * available at https://www.gnu.org/software/classpath/license.html.
# *
# * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
# ********************************************************************************

"""Defines factory functions for standard gate Operations (X, H, CX, MCX, rotations, etc.)."""

from __future__ import annotations

import types
import warnings
from typing import TYPE_CHECKING

import numpy as np

from qrisp.circuit import PauliGate, U3Gate
from qrisp.circuit.operation import Operation
from qrisp.misc.exceptions import QrispDeprecationWarning

if TYPE_CHECKING:
    from qrisp.typing import FloatLike

# TODO: properly treat all gates
# from Aer.get_backend('qasm_simulator').configuration().basis_gates


def Measurement():
    """Return a measurement :class:`.Operation`.

    Returns
    -------
    :class:`.Operation`
        A measurement operation with 1 qubit and 1 classical bit.

    """
    res = Operation(name="measure", num_qubits=1, num_clbits=1)
    res.permeability = {0: True}
    return res


def XGate():
    """Return the Pauli-X (bit-flip / NOT) gate.

    Returns
    -------
    :class:`.Operation`
        The Pauli-X gate operation.

    """
    return PauliGate(name="x")


def YGate():
    """Return the Pauli-Y gate.

    Returns
    -------
    :class:`.Operation`
        The Pauli-Y gate operation.

    """
    return PauliGate(name="y")


def ZGate():
    """Return the Pauli-Z (phase-flip) gate.

    Returns
    -------
    :class:`.Operation`
        The Pauli-Z gate operation.

    """
    return PauliGate(name="z")


def CXGate():
    """Return the controlled-X (CNOT) gate.

    Returns
    -------
    :class:`.Operation`
        The controlled-X gate operation.

    """
    return XGate().control()


def CYGate():
    """Return the controlled-Y gate.

    Returns
    -------
    :class:`.Operation`
        The controlled-Y gate operation.

    """
    return YGate().control()


def CZGate():
    """Return the controlled-Z gate.

    Returns
    -------
    :class:`.Operation`
        The controlled-Z gate operation.

    """
    return ZGate().control()


def MCXGate(control_amount=1, ctrl_state=-1, method="gray"):
    """Return a multi-controlled X gate.

    Parameters
    ----------
    control_amount : int, optional
        Number of control qubits. The default is 1.
    ctrl_state : int, optional
        Integer that represents the control state in binary.
        The default is ``-1`` (all controls active when in state ``|1⟩``).
    method : str, optional
        Synthesis method. The default is ``"gray"``.

    Returns
    -------
    :class:`.Operation`
        The multi-controlled X gate operation.

    """
    return XGate().control(control_amount, method=method, ctrl_state=ctrl_state)


def PGate(phi: FloatLike = 0):
    """Return a phase gate.

    Parameters
    ----------
    phi : FloatLike, optional
        The phase angle in radians. The default is 0.

    Returns
    -------
    :class:`.Operation`
        The phase gate operation.

    """
    res = U3Gate(0, 0, phi, name="p")
    return res


def CPGate(phi: FloatLike = 0):
    """Return a controlled phase gate.

    Parameters
    ----------
    phi : FloatLike, optional
        The phase angle in radians. The default is 0.

    Returns
    -------
    :class:`.Operation`
        The controlled phase gate operation.

    """
    if isinstance(phi, (int, float)):
        phi = phi % (2 * np.pi)
        if np.abs(phi - np.pi) < 1e-8:
            return CZGate()
        if np.abs(phi) < 1e-8:
            from qrisp.circuit.quantum_circuit import QuantumCircuit

            temp_circ = QuantumCircuit(2)
            res = temp_circ.to_gate(name="cp")
            res.params = [0]
            return res
        return PGate(phi).control()
    return PGate(phi).control()


def HGate():
    """Return the Hadamard gate.

    Returns
    -------
    :class:`.Operation`
        The Hadamard gate operation.

    """
    res = U3Gate(np.pi / 2, 0, np.pi, name="h")
    return res


def RXGate(phi: FloatLike = 0):
    """Return a rotation-around-X gate.

    Parameters
    ----------
    phi : FloatLike, optional
        The rotation angle in radians. The default is 0.

    Returns
    -------
    :class:`.Operation`
        The :math:`R_X` gate operation.

    """
    res = U3Gate(phi, -np.pi / 2, np.pi / 2, name="rx")
    return res


def RYGate(phi: FloatLike = 0):
    """Return a rotation-around-Y gate.

    Parameters
    ----------
    phi : FloatLike, optional
        The rotation angle in radians. The default is 0.

    Returns
    -------
    :class:`.Operation`
        The :math:`R_Y` gate operation.

    """
    res = U3Gate(phi, 0, 0, name="ry")
    return res


def RZGate(phi: FloatLike = 0):
    """Return a rotation-around-Z gate.

    Parameters
    ----------
    phi : FloatLike, optional
        The rotation angle in radians. The default is 0.

    Returns
    -------
    :class:`.Operation`
        The :math:`R_Z` gate operation.

    """
    res = U3Gate(0, phi, 0, name="rz", global_phase=-phi / 2)  # type: ignore[operator]
    return res


[docs] class PRXGate(U3Gate): r"""PRX (Phased-RX) gate. The PRX gate is a single-qubit gate of the form: .. math:: \text{PRX}(\alpha, \beta) = R_Z(\beta) \cdot R_X(\alpha) \cdot R_Z(-\beta) Parameters ---------- alpha : FloatLike, optional The rotation angle. The default is 0. beta : FloatLike, optional The phase parameter. The default is 0. """ def __init__(self, alpha: FloatLike = 0, beta: FloatLike = 0) -> None: """Initialize the PRX gate as ``U3(alpha, beta - pi/2, pi/2 - beta)``.""" self.alpha = alpha self.beta = beta # PRX(alpha, beta) = RZ(beta) * RX(alpha) * RZ(-beta) # # Using RX(alpha) = RZ(-pi/2) * RY(alpha) * RZ(pi/2) and the U3 # decomposition U3(theta, phi, lam) = RZ(phi) * RY(theta) * RZ(lam): # # PRX(alpha, beta) = RZ(beta) * RZ(-pi/2) * RY(alpha) * RZ(pi/2) * RZ(-beta) # = RZ(beta - pi/2) * RY(alpha) * RZ(pi/2 - beta) # = U3(alpha, beta - pi/2, pi/2 - beta) # super().__init__(alpha, beta - np.pi / 2, np.pi / 2 - beta, name="prx") # type: ignore[operator] def inverse(self) -> PRXGate: r"""Returns the inverse of the PRX gate. The inverse of :math:`R_Z(\beta) R_X(\alpha) R_Z(-\beta)` is :math:`R_Z(\beta) R_X(-\alpha) R_Z(-\beta)`. Returns ------- PRXGate The inverted PRX gate with parameters ``(-alpha, beta)``. """ return PRXGate(-self.alpha, self.beta) # type: ignore[arg-type] def bind_parameters(self, subs_dic: dict) -> PRXGate: """Return the PRX gate with the abstract parameters in ``subs_dic`` bound to their values.""" # U3Gate.bind_parameters would return a plain U3Gate named "prx" bound = U3Gate.bind_parameters(self, subs_dic) return PRXGate(bound.theta, bound.phi + np.pi / 2) # type: ignore[operator]
class RGate(PRXGate): """R gate. .. deprecated:: 0.10 ``RGate`` is deprecated, use :class:`PRXGate` instead. ``RGate(theta, phi)`` is identical to ``PRXGate(theta, phi)``. Parameters ---------- theta : FloatLike, optional The rotation angle in radians. The default is 0. phi : FloatLike, optional The axis angle in radians. The default is 0. """ def __init__(self, theta: FloatLike = 0, phi: FloatLike = 0) -> None: """Initialize the deprecated R gate as ``PRXGate(theta, phi)``.""" warnings.warn( "DeprecationWarning: RGate is deprecated and will be removed in a later " "release of Qrisp. Use PRXGate instead, RGate(theta, phi) is identical " "to PRXGate(theta, phi).", QrispDeprecationWarning, stacklevel=2, ) super().__init__(theta, phi) def GPhaseGate(phi: FloatLike = 0): """Return a global phase gate. Parameters ---------- phi : FloatLike, optional The global phase angle in radians. The default is 0. Returns ------- :class:`.Operation` The global phase gate operation. """ res = U3Gate(0, 0, 0, name="gphase", global_phase=phi) return res def MCRXGate(phi: FloatLike = 0, control_amount: int = 0): """Return a multi-controlled :func:`RXGate`. Parameters ---------- phi : FloatLike, optional The rotation angle in radians. The default is 0. control_amount : int, optional Number of control qubits. The default is 0. Returns ------- :class:`.Operation` The multi-controlled :math:`R_X` gate operation. """ res = RXGate(phi).control(control_amount) res.name = "mcrx" return res def SGate(): """Return the S gate. Returns ------- :class:`.Operation` The S gate operation. """ res = PGate(np.pi / 2) res.params = [] res.name = "s" return res def TGate(): """Return the T gate. Returns ------- :class:`.Operation` The T gate operation. """ res = PGate(np.pi / 4) res.params = [] res.name = "t" return res def IDGate(): """Return the single-qubit identity gate. Returns ------- :class:`.Operation` The identity gate operation. """ res = U3Gate(0, 0, 0, name="id") return res def RXXGate(phi: FloatLike = 0): """Return an Ising XX-coupling gate. Parameters ---------- phi : FloatLike, optional The coupling angle in radians. The default is 0. Returns ------- :class:`.Operation` The :math:`R_{XX}` gate operation. """ from qrisp.circuit.quantum_circuit import QuantumCircuit qc = QuantumCircuit(2) qc.gphase(-phi / 2, qc.qubits[0]) qc.h(qc.qubits[0]) qc.h(qc.qubits[1]) qc.cx(qc.qubits[0], qc.qubits[1]) qc.p(phi, qc.qubits[1]) qc.cx(qc.qubits[0], qc.qubits[1]) qc.h(qc.qubits[0]) qc.h(qc.qubits[1]) res = Operation(name="rxx", num_qubits=2, num_clbits=0, params=[phi], definition=qc) res.permeability = {0: False, 1: False} res.is_qfree = False return res def RYYGate(phi: FloatLike = 0): """Return an Ising YY-coupling gate. Parameters ---------- phi : FloatLike, optional The coupling angle in radians. The default is 0. Returns ------- :class:`.Operation` The :math:`R_{YY}` gate operation. """ from qrisp.circuit.quantum_circuit import QuantumCircuit qc = QuantumCircuit(2) qc.gphase(-phi / 2, qc.qubits[0]) qc.sx_dg(qc.qubits[0]) qc.sx_dg(qc.qubits[1]) qc.cx(qc.qubits[0], qc.qubits[1]) qc.p(phi, qc.qubits[1]) qc.cx(qc.qubits[0], qc.qubits[1]) qc.sx(qc.qubits[0]) qc.sx(qc.qubits[1]) res = Operation(name="ryy", num_qubits=2, num_clbits=0, params=[phi], definition=qc) res.permeability = {0: False, 1: False} res.is_qfree = False return res def RZZGate(phi: FloatLike = 0): """Return an Ising ZZ-coupling gate. Parameters ---------- phi : FloatLike, optional The coupling angle in radians. The default is 0. Returns ------- :class:`.Operation` The :math:`R_{ZZ}` gate operation. """ from qrisp.circuit.quantum_circuit import QuantumCircuit qc = QuantumCircuit(2) qc.cx(qc.qubits[0], qc.qubits[1]) qc.gphase(-phi / 2, qc.qubits[0]) qc.p(phi, qc.qubits[1]) qc.cx(qc.qubits[0], qc.qubits[1]) res = Operation(name="rzz", num_qubits=2, num_clbits=0, params=[phi], definition=qc) res.permeability = {0: True, 1: True} res.is_qfree = True return res def XXYYGate(phi: FloatLike = 0, beta: FloatLike = 0): """Return an XX+YY interaction gate. Parameters ---------- phi : FloatLike, optional The rotation angle in radians. The default is 0. beta : FloatLike, optional The phase angle in radians. The default is 0. Returns ------- :class:`.Operation` The XX+YY gate operation. """ from qrisp.circuit.quantum_circuit import QuantumCircuit qc = QuantumCircuit(2) qc.rz(beta, qc.qubits[0]) qc.rz(-np.pi / 2, qc.qubits[1]) qc.sx(qc.qubits[1]) qc.rz(np.pi / 2, qc.qubits[1]) qc.s(qc.qubits[0]) qc.cx(qc.qubits[1], qc.qubits[0]) qc.ry(-phi / 2, qc.qubits[1]) qc.ry(-phi / 2, qc.qubits[0]) qc.cx(qc.qubits[1], qc.qubits[0]) qc.s_dg(qc.qubits[0]) qc.rz(-np.pi / 2, qc.qubits[1]) qc.sx_dg(qc.qubits[1]) qc.rz(np.pi / 2, qc.qubits[1]) qc.rz(-beta, qc.qubits[0]) return Operation(name="xxyy", num_qubits=2, num_clbits=0, params=[phi, beta], definition=qc) def Barrier(num_qubits=1): """Return a barrier. Parameters ---------- num_qubits : int, optional The number of qubits the barrier spans. The default is 1. Returns ------- :class:`.Operation` The barrier operation spanning ``num_qubits`` qubits. """ res = Operation(num_qubits=num_qubits, name="barrier") res.permeability = {i: False for i in range(num_qubits)} res.is_qfree = True return res def u3Gate(theta: FloatLike = 0, phi: FloatLike = 0, lam: FloatLike = 0): """Return a U3 gate with Euler angles ``theta``, ``phi``, ``lam``. This is a thin alias for :class:`.U3Gate`. Parameters ---------- theta : FloatLike, optional The theta Euler angle in radians. The default is 0. phi : FloatLike, optional The phi Euler angle in radians. The default is 0. lam : FloatLike, optional The lambda Euler angle in radians. The default is 0. Returns ------- :class:`.Operation` The U3 gate operation. """ return U3Gate(theta, phi, lam) def U1Gate(phi: FloatLike = 0): """Return a U1 gate, identical to :func:`RZGate`. Unlike Qiskit's ``u1``, which is the phase gate :func:`PGate`, this gate includes the global phase of RZ. Parameters ---------- phi : FloatLike, optional The phase angle in radians. The default is 0. Returns ------- :class:`.Operation` The U1 gate operation. """ res = RZGate(phi) res.name = "u1" res.params = [phi] res.permeability = {0: True} res.is_qfree = True return res def Reset(): """Return a reset gate. Returns ------- :class:`.Operation` The reset operation. """ res = Operation("reset", 1) res.permeability = {0: False} return res def SXGate(): """Return the square-root-of-X gate. Returns ------- :class:`.Operation` The :math:`\\sqrt{X}` gate operation. """ res = RXGate(np.pi / 2) res.global_phase = np.pi / 4 res.name = "sx" res.params = [] res.permeability = {0: False} res.is_qfree = False return res def SXDGGate(): """Return the adjoint of the square-root-of-X gate. Returns ------- :class:`.Operation` The adjoint of the square-root-of-X gate operation. """ res = RXGate(-np.pi / 2) res.global_phase = -np.pi / 4 res.name = "sx_dg" res.params = [] res.permeability = {0: False} res.is_qfree = False return res def SwapGate(): """Return a SWAP gate. Returns ------- :class:`.Operation` The SWAP gate operation. """ from qrisp.circuit.quantum_circuit import QuantumCircuit temp_qc = QuantumCircuit(2) temp_qc.cx(temp_qc.qubits[0], temp_qc.qubits[1]) temp_qc.cx(temp_qc.qubits[1], temp_qc.qubits[0]) temp_qc.cx(temp_qc.qubits[0], temp_qc.qubits[1]) res = temp_qc.to_gate(name="swap") res.inverse = types.MethodType(lambda self: self.copy(), res) res.permeability = {0: False, 1: False} res.is_qfree = True return res def QubitAlloc(): """Return an internal qubit-allocation marker. Returns ------- :class:`.Operation` The qubit-allocation marker operation. """ res = Operation("qb_alloc", 1) res.unitary = np.eye(2, dtype=np.complex64) res.qfree = True res.permeability[0] = False return res def QubitDealloc(): """Return an internal qubit-deallocation marker. Returns ------- :class:`.Operation` The qubit-deallocation marker operation. """ res = Operation("qb_dealloc", 1) res.unitary = np.eye(2, dtype=np.complex64) res.qfree = True res.permeability[0] = False return res op_list = [ XGate, YGate, ZGate, CXGate, CYGate, CZGate, MCXGate, PGate, CPGate, u3Gate, HGate, RXGate, RYGate, RZGate, MCRXGate, SGate, TGate, RXXGate, RZZGate, SXGate, SXDGGate, XXYYGate, Barrier, Measurement, Reset, QubitAlloc, QubitDealloc, GPhaseGate, SwapGate, U1Gate, IDGate, PRXGate, ]