Error when using qml.qsvt

Hi,

I am trying to apply the polynomial representation of the sign function to a block-encoded matrix using qml.qsvt — PennyLane 0.41.1 documentation.

I referred to how to use pyqsp (an external package) to approximate polynomial functions from the demo at QSVT in Practice | PennyLane Demos.

I generated the coefficients of the polynomials from the pyqsp function PolySign and used it in qml.qsvt by passing it in the poly argument.

However, qml.qsvt was throwing an error. Full error below. The problem seems to be coming from the qml.poly_to_angle function.

Code to reproduce it is as below:

import pennylane as qml
from pyqsp.poly import PolySign

DEGREE = 25

pg = PolySign()
pcoefs, scale = pg.generate(
    degree=DEGREE,
    delta=10,
    ensure_bounded=True,
    return_scale=True,
    chebyshev_basis=True,
    cheb_samples=250
)

angles = qml.poly_to_angles(pcoefs.tolist(), routine="QSVT")
print(angles)

Error:

ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (2,) + inhomogeneous part.

My Pennylane version is 0.41.1

I’m not sure why this error occurs. Looking forward to your kind help. Thank you.

Hi @jag , thanks for your question. I can replicate your error.

I’m not exactly sure why this is happening. Let me check with the team and get back to you.

Hi @jag ,

My colleague Guillermo had the answer :raising_hands:

The solver is based on root-finding by default and in very degenerate polynomials such as yours this is an unstable method.

We have a solution though!

Use the master version of PennyLane and set the method to "iterative"

angles = qml.poly_to_angles(pcoefs.tolist(), routine="QSVT", angle_solver = "iterative")

You can install the master version of PennyLane by using:

pip install git+https://github.com/PennyLaneAI/pennylane.git#egg=pennylane

Let us know if this works for you!

Hi @CatalinaAlbornoz ,

Thank you for your help. I have installed the master version of Pennylane as you have advised. Now, I can use qml.qsvt by using the angle_solver="iterative" argument to the function.

However, I would like to discuss an issue that I encountered. Let me walk through the code that I am experimenting with. It is based on the Pennylane demos Intro to QSVT | PennyLane Demos and QSVT in Practice | PennyLane Demos.

Firstly, I import the dependencies and generate the polynomial sign function’s coefficients using pyqsp

import pennylane as qml
from pyqsp.poly import PolySign
from pyqsp.angle_sequence import QuantumSignalProcessingPhases
import numpy as np
import matplotlib.pyplot as plt


DEGREE = 25

pg = PolySign()
pcoefs, scale = pg.generate(
    degree=DEGREE,
    delta=10,
    ensure_bounded=True,
    return_scale=True,
    chebyshev_basis=True,
    cheb_samples=250
)

Then I use qml.qsvt to apply the polynomial sign function to a list of points

a_vals = np.linspace(-1, 1, 50)
target = [scale * np.sign(x) for x in a_vals]
x_vals = np.linspace(-1, 1, 16)
A = np.diag(x_vals)
target_poly = pcoefs.tolist()
wire_order = list(range(5))

U_A = qml.matrix(qml.qsvt, wire_order=wire_order)(
    A, target_poly, encoding_wires=wire_order, angle_solver="iterative", block_encoding="embedding"
) # block-encoded in 5-qubit system

qsvt_A = np.real(np.diagonal(U_A))[:16]  # retrieve transformed eigenvalues

plt.title("QSVT with iterative angle solver")
plt.plot(a_vals, target, label="target")
plt.plot(x_vals, qsvt_A, label="qsvt")

plt.legend()
plt.show()
plt.close("all")

The output of the plot is as below:

As you can see, the qml.qsvt generated output doesn’t seem to map to the target.

Next, I used pyqsp QuantumSignalProcessingPhases function to generate the angles and used qml.QSVT instead. Note that the transformed values are in the imaginary part for this case.

(phi_pyqsp, reduced_phi, parity) = QuantumSignalProcessingPhases(pcoefs, signal_operator="Wx", chebyshev_basis=True, method="sym_qsp", tolerance=1e-12)
phi_qsvt = qml.transform_angles(phi_pyqsp, "QSP", "QSVT")  # convert pyqsp angles to be compatible with QSVT

block_encoding = qml.BlockEncode(A, wires=wire_order)
projectors = [qml.PCPhase(angle, dim=A.shape[0], wires=wire_order) for angle in phi_qsvt]
U_A = qml.matrix(qml.QSVT, wire_order=wire_order)(block_encoding, projectors)
qsvt_A = np.imag(np.diagonal(U_A))[:16] # Note that the transformed values are in the imaginary part for this case

plt.title("QSVT with angles from pyqsp")
plt.plot(a_vals, target, label="target")
plt.plot(x_vals, qsvt_A.real, label="qsvt")

plt.legend()
plt.show()
plt.close("all")

The output of the plot is as below:

As you can see, the qml.QSVT output resembles close to the target function except at values close to 0 which I think is expected for the sign function.

I’m not sure why there output differs. Also, I realized that the iterative angle solving in qml.qsvt gets slow when I increase the number of degrees, e.g. 161 degrees, compared to pyqsp angle generation function QuantumSignalProcessingPhases. Looking forward to your kind reply.

Hi @jag ! Very interesting application :grinning_face_with_smiling_eyes:
The issue you are experiencing is because pyqsp, represents polynomials in Chebyshev base while PennyLane does it in standard basis. Just change this line and you will see that it works correctly:

target_poly = np.polynomial.chebyshev.cheb2poly(pcoefs.tolist())

Regarding the performance question. The “iterative” solver is designed to take advantage of the jit compilation potential in jax. By simply passing the polynomial as a jax array instead of numpy array, you will notice significant improvements :rocket:

I hope that helps :blush:

2 Likes

Thank you @Guillermo_Alonso. Your answer fixed the issue I said. And thank you to @CatalinaAlbornoz too. :slight_smile:

1 Like