.. role:: raw-latex(raw) :format: latex .. Gates ===== In OpenQASM we refer to unitary quantum instructions as *gates*. Applying gates -------------- Every gate applies to a fixed number of qubits. Assuming ``g0``, ``g1``, ``g2``, ... are gates defined on 0, 1, 2, ... qubits then they can be applied as .. code-block:: g0; g1 q1; g2 q1, q2; g3 q1, q2, q3; Broadcasting ~~~~~~~~~~~~ If any arguments of a gate are quantum registers instead of qubits, then all such registers must be **of the same length** and this syntax is a convenient shorthand for broadcasting over the qubits of the register. For example, the circuit .. code-block:: qubit qr0[1]; qubit qr1[3]; qubit qr2[2]; qubit qr3[3]; g4 qr0[0], qr1, qr2[0], qr3; // ok g4 qr0[0], qr2, qr1[0], qr3; // error! qr2 and qr3 differ in size has a second-to-last line that is equivalent to .. code-block:: text g4 qr0[0], qr1[0], qr2[0], qr3[0]; g4 qr0[0], qr1[1], qr2[0], qr3[1]; g4 qr0[0], qr1[2], qr2[0], qr3[2]; Use of this syntax constitutes a promise to the compiler that the expanded set of broadcasted gates is mutually commuting and thus the compiler can take advantage of the opportunity to reorder the gates freely for optimization purposes. In certain cases a compiler might be able to detect a non-commutativity and raise a warning, but it is not required to do so in all cases. If the gates do not commute and a specific order is required by the programmer, than a ``for`` loop may be used to set that order. Parameterized gates ~~~~~~~~~~~~~~~~~~~ As well as gates that each represent a fixed unitary, OpenQASM also supports gates that represent *families* of unitaries, parameterized by angle variables. To distinguish the (optional) angle parameters from the (required) quantum arguments, if any angle parameters are present they must appear before any quantum arguments, and be delimited by parentheses. For example .. code-block:: text fsim(θ, ϕ) q[0], q[1]; Defining gates -------------- There are 3 mechanisms to construct new gates: 1. A new named gate can be introduced by a **hierarchical definition** from a sequence of existing gates; 2. Anonymous new gates may be defined by applying **gate modifiers** to existing gates; 3. The **built-in gates** comprising the one-qubit gate ``U(θ, ϕ, λ)`` and the zero-qubit gate ``gphase(γ)``. The definitions of these gates is part of the language specification. The next subsections go through these cases. .. _gate-statement: Hierarchical gates definitions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``gate`` statement associates an identifier with a corresponding unitary matrix (or parameterized family) transformation by a sequence of other (built-in or previously-defined) gates. For example the ``gate`` block .. code-block:: gate h q { U(π/2, 0, π) q; gphase -π/4; } defines a new gate called ``h`` and associates it to the unitary matrix of the Hadamard gate. Once we have defined ``h``, we can use it in later ``gate`` blocks. The definition does not imply that ``h`` is implemented by an instruction ``U(π/2, 0, π)`` on the quantum computer. The implementation is up to the user and/or compiler, given information about the instructions supported by a particular target. A minimal compiler implementation might simply expand ``gate`` definitions repeatedly until reaching definitions for which :ref:`defcal blocks ` are known. A more sophisticated implementation might use the `gate` definitions of the gates with associated ``defcal`` blocks to build a gate library, and use methods based on KAK decompositions to rewrite into this hardware library. The ``gate`` statement also allows defining parameterized families of unitaries. For example, a CPHASE operation is shown schematically in :numref:`fig_gate` and the corresponding OpenQASM code is .. code-block:: gate cphase(θ) a, b { U(0, 0, θ / 2) a; CX a, b; U(0, 0, -θ / 2) b; CX a, b; U(0, 0, θ / 2) b; } cphase(π / 2) q[0], q[1]; .. _fig_gate: .. figure:: ../qpics/gate.svg New gates are defined from previously defined gates. The gates are applied using the statement ``name(params) qargs;`` The parentheses are optional if there are no parameters. The gate :math:`{cphase}(\theta)` corresponds to the unitary matrix :math:`{diag}(1,1,1,e^{i\theta})` up to a global phase. Again, this definition does not imply that ``cphase`` must be implemented with this particular series of gates. Rather, we have specified the unitary transformation that corresponds to the symbol ``cphase``. The particular implementation is up to the compiler, given information about the basis gate set supported by a particular target. In general, new gates are defined by statements of the form .. code-block:: gate name(params) qargs { body } where the optional parameter list ``params`` is a comma-separated list of variable parameters, and the argument list ``qargs`` is a comma-separated list of qubit arguments. The parameters are identifiers that behave as ``angle`` type with unknown size. A compiler might recognize certain constructs and replace them with mathematically- equivalent versions that would be true for arbitrary precision, or it might do calculations at a fixed ``angle`` size, for example corresponding to the size of ``angle`` parameters in the corresponding ``defcal`` definitions. The qubit arguments are identifiers. If there are no variable parameters, the parentheses are optional. The arguments in ``qargs`` cannot be indexed within the body of the gate definition. .. code-block:: // this is ok: gate g a { U(0, 0, 0) a; } // this is invalid: gate g a { U(0, 0, 0) a[0]; } Only built-in gate statements and calls to previously defined gates can appear in ``body``. For example, it is not valid to declare a classical register in a gate body. Looping constructs over these quantum statements are valid. The statements in the body can only refer to the symbols given in the parameter or argument list, and these symbols are scoped only to the subroutine body. An empty body corresponds to the identity gate. To avoid infinite recursion, gates must be declared before use and cannot call themselves. The statement ``name(params) qargs;`` applies the gate, and the variable parameters ``params`` can have any type that can promote to ``angle`` type. Quantum gate modifiers ~~~~~~~~~~~~~~~~~~~~~~ A gate modifier is a keyword that applies to a gate. A modifier :math:`m` transforms a gate :math:`U` to a new gate :math:`m(U)` acting on the same or larger Hilbert space. We include modifiers in OpenQASM both for programming convenience and compiler analysis. Control modifiers +++++++++++++++++ The modifier ``ctrl @`` replaces its gate argument :math:`U` by a controlled-:math:`U` gate. If the control bit is 0, nothing happens to the target bit. If the control bit is 1, :math:`U` acts on the target bit. Mathematically, the controlled-:math:`U` gate is defined as :math:`C_U = I \otimes U^c`, where :math:`c` is the integer value of the control bit and :math:`C_U` is the controlled-:math:`U` gate. The new quantum argument is prepended to the argument list for the controlled-:math:`U` gate. The quantum argument can be a register, and in this case controlled gate broadcast over it (as for all gates). The modified gate does not use any additional scratch space and may require compilation to be executed. As a limiting case, the controlled *global* phase gate ``ctrl @ gphase(a)`` is equivalent to the single-qubit gate ``U(0, 0, a)``. .. code-block:: // Define a controlled Rz operation using the ctrl gate modifier. // q1 is control, q2 is target gate crz(θ) q1, q2 { ctrl @ rz(θ) q1, q2; } The modifier ``negctrl @`` generates controlled gates with negative polarity, ie conditioned on a controlled value of 0 rather than 1. Mathematically, the negative controlled-:math:`U` gate is given by :math:`N_U = I \otimes U^{1-c}`, where :math:`c` is the integer value of the control bit and :math:`N_U` is the negative controlled-:math:`U` gate. .. code-block:: // Define a negative controlled X operation using the negctrl gate modifier. // q1 is control, q2 is target gate neg_cx(θ) q1, q2 { negctrl @ x q1, q2; } ``ctrl`` and ``negctrl`` both accept an optional positive integer parameter ``n``, specifying the number of control arguments (omission means ``n=1``). ``n`` must be a compile-time constant. For an ``N`` qubit operation,these operations are mathematically defined as .. math:: C^n_U = I_1 \otimes I_2 ... \otimes I_n \otimes U^{c_1*c_2*...*c_n} N^n_U = I_1 \otimes I_2 ... \otimes I_n \otimes U^{1 - c_1*c_2*...*c_n} where :math:`c_1`, :math:`c_2`, ..., :math:`c_n` are the integer values of the control bits and :math:`C^n_U` are the n-bit controlled-:math:`U` and n-bit negative controlled-:math:`U` gates, respectively. .. code-block:: // A reversible boolean function // Demonstrates use of ``ctrl(n) @`` and ``negctrl(n) @`` qubit[3] a; qubit[2] b; qubit f; reset f; ctrl(3) @ x a[1], a[0], a[2], f; negctrl(3) @ ctrl @ x a[0], b[1], a[2], b[0], f; negctrl @ ctrl(2) @ negctrl @ x a[0], b[0], a[2], a[1], f; negctrl(2) @ ctrl @ x b[1], a, b[0], f; Inverse modifier ++++++++++++++++ The modifier ``inv @ U`` replaces its gate argument :math:`U` with its inverse :math:`U^\dagger`. This can be computed from gate :math:`U` via the following rules - The inverse of any gate :math:`U=U_m U_{m-1} ... U_1` can be defined recursively by reversing the order of the gates in its definition and replacing each of those with their inverse :math:`U^\dagger = U_1^\dagger U_2^\dagger ... U_m^\dagger`. - The inverse of a controlled operation is defined by inverting the control unitary. That is, ``inv @ ctrl @ U = ctrl @ inv @ U``. - The base case is given by replacing ``inv @ U(θ, ϕ, λ)`` by ``U(-θ, -λ, -ϕ)`` and ``inv @ gphase(a)`` by ``gphase(-a)``. .. code-block:: // Define a negative z rotation and the inverse of a positive z rotation gate rzm(θ) q1 { inv @ rzp(θ) q1; } // Equivalently, this can be written as gate rzm(θ) q1 { rzp(-θ) q1; } Power modifier ++++++++++++++ The modifier ``pow(k) @`` replaces its gate argument :math:`U` by its :math:`k`\ th power :math:`U^k` for some positive integer or floating point number :math:`k` (not necessarily constant). In the case that :math:`k` is an integer, the gate can be implemented (albeit inefficiently) by :math:`k` repetitions of :math:`U` for :math:`k > 0` and :math:`k` repetitions of ``inv @ U`` for :math:`k < 0`. .. code-block:: // define x as the square of sqrt(x) ``sx`` gate gate x q1 { pow(2) @ sx q1; } Built-in gates ~~~~~~~~~~~~~~ Built-in single-qubit gate ``U`` ++++++++++++++++++++++++++++++++ The built-in single-qubit gate ``U(θ, ϕ, λ)`` represents the unitary matrix .. math:: U(\theta,\phi,\lambda) := \frac{1}{2}\left(\begin{array}{cc} 1+e^{i\theta} & -ie^{i\lambda}(1-e^{i\theta}) \\ ie^{i\phi}(1-e^{i\theta}) & e^{i(\phi+\lambda)}(1+e^{i\theta}) \end{array}\right). This definition is :math:`2\pi`-periodic in each of the parameters θ, ϕ, λ and specifies any element of :math:`U(2)` up to a global phase [#uphase]_ . For example ``U(π/2, 0, π) q[0];``, applies a Hadamard gate to qubit ``q[0]`` (up to a non-standard global phase). Global phase gate ``gphase`` ++++++++++++++++++++++++++++ From a physical perspective, the unitaries :math:`e^{i\gamma}V` and :math:`V` are equivalent although they differ by a global phase :math:`e^{i\gamma}`. When we add a control to these gates, however, the global phase becomes a relative phase that is applied when the control qubit is one. A built-in global phase gate allows the inclusion of arbitrary global phases on circuits. The instruction ``gphase(γ);`` accumulates a global phase of :math:`e^{i\gamma}`. Just as every n-qubit gate can be thought of as generating a tensor product with the suitable identity matrix to cover all other qubits in the gate, subroutine, or global scope containing the instruction, similarly ``gphase`` behaves as a 0-qubit gate and when applied in a context with `m` qubits in scope, behaves as applying the unitary .. math:: \operatorname{gphase}(\gamma) := e^{i\gamma} I_m, where :math:`I_m` denotes the identity matrix with size :math:`2^m` For example .. code-block:: gate X q { U(π, 0, π) q; gphase -π/2; } gate CX c, t { ctrl @ X c, t; } defines ``CX`` as the standard CNOT gate. Relation of the built-in gates to hardware-native gates ---------------------------- For *non-parameterized gates*, the choice of ``U`` and ``gphase`` as the built-in gates, along with one two-qubit entangling gate CNOT as defined gives a universal gate set that can represent general n-qubit unitaries with an :math:`O(2^n)` size description :cite:`barenco95`. This basis is not an enforced compilation target but a mechanism to define other gates. For many gates of practical interest, there is a circuit representation with a polynomial number of one- and two-qubit gates, giving a more compact representation than requiring the programmer to express the full :math:`2^n \times 2^n` matrix. However, a general :math:`n`-qubit gate can be defined using an exponential number of these gates. Thus there is no particular privilege incurred by hardware implementations that natively support the built-in gates. For *parameterized gates*, the choice of built-in gates *does* constrain which hardware-native gates are well- supported, because conversion between parameterized basis sets in general can be involved, requiring careful selection of branch cuts and other logic that would not likely be feasible to specify as compact mathematical expressions, nor to evaluate at runtime for cases where the parameters depend on quantum measurements. For many current platforms the qubits are defined relative to a rotating frame and the rotating wave approximation (RWA) holds. This is the domain covered by the OpenPulse specification. For this case, the only supported form of run-time parameterization will likely be via a ``rz(ϕ)`` implemented by specialized frame-tracking hardware. This gate is covered by the built-in ``U`` as a special case ``U(0, 0, ϕ)`` However, if other forms of run-time parameterization become important, it may be necessary to revise OpenQASM, to give meaning to those gates, for example by adding new basis gates or additional ``gate`` definition syntax. .. [#uphase] This definition of ``U`` has a different global phase from previous versions of the OpenQASM spec. Unfortunately the original definitions were 4π rather than 2π periodic in the θ parameter. A gate ``U_old(0, ϕ, θ) q;`` under the previous definition corresponds to ``U(0, ϕ, θ) q; gphase(-0/2);`` with the present definition.