qrisp.gqsp.GQSVT#

GQSVT(A: BlockEncoding | FermionicOperator | QubitOperator, p: ArrayLike, kind: Literal['Polynomial', 'Chebyshev'] = 'Polynomial', parity: Literal['odd', 'even'] = 'odd', rescale: bool = True) BlockEncoding[source]#

Returns a BlockEncoding representing a polynomial transformation of the operator via Generalized Quantum Singular Value Transform.

For a block-encoded operator \(A\) with Singular Value Decomposition \(A = U \Sigma V^{\dagger}\) for unitaries \(U, V\), and a (complex) polynomial \(p(z)\), this method returns a BlockEncoding of either operator:

  • \(p_{odd}(A)=V p_{odd}(\Sigma) U^{\dagger}\)

  • \(p_{even}(A)=V p_{even}(\Sigma) V^{\dagger}\)

where \(p=p_{odd}+p_{even}\) is decomposed into odd and even parity parts.

Warning

If the parity is odd, this deviates from qrisp.algorithms.gqsp.qsvt.QSVT(), which returns a BlockEncoding of \(p_{odd}(A)=U p_{odd}(\Sigma) V^{\dagger}\), i.e., the Hermitian conjugate.

Parameters:
ABlockEncoding | FermionicOperator | QubitOperator

The operator to be transformed. Unlike in (G)QET, this operator does not need to be Hermitian.

pArrayLike

1-D array containing the polynomial coefficients, ordered from lowest order term to highest.

kind{“Polynomial”, “Chebyshev”}

The basis in which the coefficients are defined.

  • "Polynomial": \(p(z) = \sum c_i z^i\)

  • "Chebyshev": \(p(z) = \sum c_i T_i(z)\), where \(T_i\) are Chebyshev polynomials of the first kind.

Default is "Polynomial".

parity{“odd”, “even”}

The parity part of \(p=p_{odd}+p_{even}\) to be applied.

  • "odd": The odd part \(p_{odd}(A)\) is applied.

  • "even": The even part \(p_{even}(A)\) is applied.

Default is "odd".

rescalebool

If True (default), the method returns a block-encoding of \(p(A)\). If False, the method returns a block-encoding of \(p(A/\alpha)\) where \(\alpha\) is the normalization factor for the block-encoding of the operator \(A\).

Returns:
BlockEncoding

A new BlockEncoding instance representing the transformed operator \(p(A)\).

Examples

Define a non-Hermitian matrix \(A\) and a vector \(\vec{b}\). The matrix \(A\) has singular value decomposition \(A = U \Sigma V^{\dagger}\) for unitary matrices \(U, V\).

import numpy as np

N = 4
A = np.eye(N, k=1) + 3 * np.eye(N)
A[N-1,0] = 1

b = np.array([1,0,0,0])

print(A)
# [[3. 1. 0. 0.]
# [0. 3. 1. 0.]
# [0. 0. 3. 1.]
# [1. 0. 0. 3.]]

Generate a BlockEncoding of \(A\) and use GQSVT to obtain a BlockEncoding of \(p(A)=V p(\Sigma) U^{\dagger}\) for an odd parity polynomial.

from qrisp import *
from qrisp.block_encodings import BlockEncoding
from qrisp.gqsp import GQSVT

def U0(qv): pass
def U1(qv): qv-=1
BE = BlockEncoding.from_lcu(np.array([3,1]), [U0,U1])

BE_poly = GQSVT(BE, np.array([0.,1.,0.,1.]), parity="odd")

# Prepare initial system state |b>
def operand_prep():
    qv = QuantumFloat(2)
    prepare(qv, b)
    return qv

@terminal_sampling
def main():
    operand = BE_poly.apply_rus(operand_prep)()
    return operand

res_dict = main()
amps = np.sqrt([res_dict.get(i, 0) for i in range(len(b))])
print(amps)
# [0.85184734 0.47324852 0.07098728 0.21296184]

Finally, compare the quantum simulation result with the classical solution:

# Compute the SVD
U, S, Vh = np.linalg.svd(A)

# Apply polynomial z + z^3 to singular values
S_poly = S + S ** 3

# Reconstruct transformed matrix
A_poly = (U @ np.diag(S_poly) @ Vh).conj().T

res = A_poly @ b / np.linalg.norm(A_poly @ b)
print(res)
# [0.85184734, 0.47324852, 0.07098728, 0.21296184]

Warning

For non-Hermitian matrices performing Singular Value Transform is not the same as applying a matrix polynomial.

A_poly = A + A @ A @ A
res = A_poly @ b / np.linalg.norm(A_poly @ b)
print(res)
# [0.71388113 0.02379604 0.21416434 0.66628906]