Source code for qrisp.algorithms.gqsp.gqsp

"""
********************************************************************************
* 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
********************************************************************************
"""

# from __future__ import annotations
# from jax.typing import ArrayLike
from qrisp import (
    QuantumArray,
    QuantumVariable,
    QuantumBool,
    control,
    invert,
    rx,
    rz,
)
from qrisp.algorithms.gqsp.gqsp_angles import gqsp_angles
from qrisp.jasp import jrange
from typing import Any, Callable, Dict, Optional, Tuple, TYPE_CHECKING

if TYPE_CHECKING:
    from jax.typing import ArrayLike


# https://journals.aps.org/prxquantum/pdf/10.1103/PRXQuantum.5.020368
[docs] def GQSP( anc: QuantumBool, *qargs: QuantumVariable, unitary: Callable[..., None], p: Optional["ArrayLike"] = None, angles: Optional[Tuple["ArrayLike", "ArrayLike", "ArrayLike"]] = None, k: int = 0, kwargs: Dict[str, Any] = {}, ) -> None: r""" Performs `Generalized Quantum Signal Processing <https://journals.aps.org/prxquantum/pdf/10.1103/PRXQuantum.5.020368>`_. Generalized Quantum Signal Processing was introduced by `Motlagh and Wiebe <https://journals.aps.org/prxquantum/pdf/10.1103/PRXQuantum.5.020368>`_. Its equivalence to the non-linear Fourier transform (NLFT) was subsequently established by `Laneve <https://arxiv.org/pdf/2503.03026>`_. By adopting the conventions from Laneve, we treat Generalized Quantum Signal Processing (GQSP) as the overarching framework for single-qubit signal processing. In this setting, previously known QSP variants—such as those constrained to $X$ or $Y$ rotations—emerge naturally as special cases where the underlying complex sequences of the NLFT are restricted to purely imaginary or real values, respectively. GQSP is described as follows: * Given a unitary $U$, * and a complex degree $d$ polynomial $p(z)\in\mathbb C[z]$ such that $|p(e^{ix})|^2\leq 1$ for all $x\in\mathbb R$, this algorithm implements the unitary .. math:: \begin{pmatrix} p(U) & * \\ * & * \end{pmatrix} &=R(\theta_0,\phi_0,\lambda)\left(\prod_{j=1}^{d}AR(\theta_j,\phi_j,0)\right)\\ &=R(\theta_0,\phi_0,\lambda)AR(\theta_1,\phi_1,0)A\dotsc AR(\theta_d,\phi_d,0) where .. math:: R(\theta,\phi,\lambda) = e^{i\lambda Z}e^{i\phi X}e^{i\theta Z} \in SU(2) and the angles $\theta,\phi\in\mathbb R^{d+1}$, $\lambda\in\mathbb R$ are calculated from the polynomial $p$, $A=\begin{pmatrix}U & 0\\ 0 & I\end{pmatrix}$ is the signal operator. Parameters ---------- anc : QuantumBool Auxiliary variable in state $\ket{0}$ for applying the GQSP protocol. Must be measured in state $\ket{0}$ for the GQSP protocol to be successful. *qargs : QuantumVariable QuantumVariables serving as operands for the unitary. unitary : Callable A function applying a unitary to the variables ``*qargs``. Typically, $U=e^{iH}$ for a Hermitian operator $H$ and GQSP applies a function of $H$. p : ArrayLike, optional 1-D array containing the polynomial coefficients, ordered from lowest order term to highest. Either the polynomial ``p`` or ``angles`` must be specified. angles : tuple(ArrayLike, ArrayLike, ArrayLike), optional A tuple of angles $(\theta,\phi,\lambda)$ where $\theta,\phi\in\mathbb R^{d+1}$ are 1-D arrays and $\lambda\in\mathbb R$ is a scalar. k : int If specified, the Laurent polynomial $\tilde p(x)=x^{-k}p(x)$ is applied. The default is 0. kwargs : dict A dictionary of keyword arguments to pass to ``unitary``. The default is {}. Notes ----- - The polynomial $p$ is rescaled automatically to satisfy $|p(e^{ix})|^2\leq 1$ for all $x\in\mathbb R$. Examples -------- **Applying a transformation in Fourier basis** We apply the operator .. math:: \cos(H) = \frac{e^{iH}+e^{-iH}}{2} for some :ref:`Hermitian operator <operators>` $H$ to the input state $\ket{\psi}=\ket{0}$. First, we define an operator $H$ and the unitary performing the Hamiltonian evolution $e^{iH}$. (In this case, Trotterization will perform Hamiltonian evolution exactly since the individual terms commute.) :: from qrisp import * from qrisp.gqsp import * from qrisp.operators import X,Y,Z import jax.numpy as jnp H = Z(0)*Z(1) + X(0)*X(1) def U(operand): H.trotterization(forward_evolution=False)(operand) Next, we define the ``operand_prep`` function that prepares a QuantumVariable is state $\ket{\psi}=\ket{0}$. :: def operand_prep(): operand = QuantumVariable(2) return operand The transformation $\cos(H)$ is achieved by applying $\tilde p(x)=0.5x^{-1} + 0.5x^1$ to the unitary $e^{iH}$. This corresponds to the polynomial $p(x)=0.5+0.5x^2$ (i.e., ``p=[0.5,0,0.5]``) and ``k=1``. Finally, we apply QSP within a :ref:`RUS` protocol. :: @RUS def inner(): p = jnp.array([0.5,0,0.5]) operand = operand_prep() anc = QuantumBool() GQSP(anc, operand, unitary=U, p=p, k=1) success_bool = measure(anc) == 0 reset(anc) anc.delete() return success_bool, operand @terminal_sampling def main(): qv = inner() return qv and simulate >>> main() {3: 0.85471756539818, 0: 0.14528243460182003} Let's compare to the classically calculated result: >>> A = H.to_array() >>> from scipy.linalg import cosm >>> print(cosm(A)) [[ 0.29192658+0.j 0. +0.j 0. +0.j -0.70807342+0.j] [ 0. +0.j 0.29192658+0.j 0.70807342+0.j 0. +0.j] [ 0. +0.j 0.70807342+0.j 0.29192658+0.j 0. +0.j] [-0.70807342+0.j 0. +0.j 0. +0.j 0.29192658+0.j]] That is, starting in state $\ket{\psi}=\ket{0}=(1,0,0,0)$, we obtain >>> result = cosm(A)@(np.array([1,0,0,0]).transpose()) >>> result = result/np.linalg.norm(result) # normalise >>> result = result**2 # compute measurement probabilities >>> print(result) [0.1452825+0.j 0. +0.j 0. +0.j 0.8547175-0.j] which are exactly the probabilities we observed in the quantum simulation. .. note:: While GQSP allows you to apply arbitrary polynomials to operators, applying abitrary polynomials to :ref:`BlockEncodings <BlockEncoding>` requires an additional step. This is because raising the operator .. math:: U = \begin{pmatrix} \frac{A}{\alpha} & * \\ * & * \end{pmatrix} to a given power $k$ does not necessarily give you .. math:: \tilde{U} = \begin{pmatrix} \left(\frac{A}{\alpha}\right)^k & * \\ * & * \end{pmatrix} In order to still apply polynomials also to them, we need to call the qubitization method and transform the polynomial into Chebychev basis. More to that in the GQSP :ref:`tutorial`. """ if angles is not None: theta, phi, lambda_ = angles d = len(theta) - 1 elif p is not None: d = len(p) - 1 (theta, phi, lambda_), _ = gqsp_angles(p) # Define R gate application function based on Theorem 9 in https://arxiv.org/abs/2503.03026 def R(theta, phi, qubit): rz(-2 * theta, qubit) rx(-2 * phi, qubit) theta = theta[::-1] phi = phi[::-1] for i in jrange(d - k): R(theta[i], phi[i], anc) with control(anc, ctrl_state=0): unitary(*qargs, **kwargs) for i in jrange(k): R(theta[d - k + i], phi[d - k + i], anc) with control(anc, ctrl_state=1): with invert(): unitary(*qargs, **kwargs) R(theta[d], phi[d], anc) rz(-2 * lambda_, anc)