Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

angle solver qsp/qsvt #6483

Merged
merged 57 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
64612a3
main functions
KetpuntoG Oct 25, 2024
94752b4
Update angle_solver.py
KetpuntoG Oct 25, 2024
df00544
some tests
KetpuntoG Oct 25, 2024
7d3a320
some changes
KetpuntoG Oct 29, 2024
2fac287
clarifications
KetpuntoG Oct 29, 2024
ca185e2
black
KetpuntoG Oct 30, 2024
6c00f5b
codefactor
KetpuntoG Oct 30, 2024
0522f11
Update angle_solver.py
KetpuntoG Oct 30, 2024
970cc76
Merge branch 'master' into angle_solver_qsp
KetpuntoG Oct 30, 2024
8d72102
codefactor
KetpuntoG Oct 30, 2024
bef2be2
Merge branch 'angle_solver_qsp' of https://github.com/PennyLaneAI/pen…
KetpuntoG Oct 30, 2024
1c8e6fc
stop recording
KetpuntoG Oct 31, 2024
b57312a
Merge branch 'master' into angle_solver_qsp
KetpuntoG Nov 4, 2024
6178b23
Merge branch 'master' into angle_solver_qsp
KetpuntoG Nov 5, 2024
e63fad7
Merge branch 'master' into angle_solver_qsp
KetpuntoG Nov 6, 2024
a9959a1
update function
KetpuntoG Nov 8, 2024
bfe3b87
[skip-ci]
KetpuntoG Nov 8, 2024
d99f884
[skip ci]
KetpuntoG Nov 8, 2024
137e8f3
Merge branch 'master' into angle_solver_qsp
KetpuntoG Nov 11, 2024
f3baeda
Update angle_solver.py
KetpuntoG Nov 11, 2024
0474821
Merge branch 'master' into angle_solver_qsp
KetpuntoG Nov 11, 2024
1dff36f
Update pennylane/math/angle_solver.py
KetpuntoG Nov 13, 2024
50c63f5
Merge branch 'master' into angle_solver_qsp
KetpuntoG Nov 13, 2024
06b1a4c
move angle solver to qsvt.py
KetpuntoG Nov 19, 2024
f1ea3a3
remove elses
KetpuntoG Nov 19, 2024
20b3de9
Merge branch 'master' into angle_solver_qsp
KetpuntoG Nov 19, 2024
926f2da
codefactor
KetpuntoG Nov 19, 2024
e96ac74
Merge branch 'angle_solver_qsp' of https://github.com/PennyLaneAI/pen…
KetpuntoG Nov 19, 2024
28a1d31
Update qsvt.py
KetpuntoG Nov 19, 2024
3a49e36
changelog
KetpuntoG Nov 19, 2024
4a02e8b
extra test for codecovs
KetpuntoG Nov 19, 2024
f734804
improve docs
KetpuntoG Nov 20, 2024
7f1c342
more tests and adding assertionErrores
KetpuntoG Nov 20, 2024
77a9358
Merge branch 'master' into angle_solver_qsp
KetpuntoG Nov 20, 2024
457f320
Some Jay's comments
KetpuntoG Nov 20, 2024
3e07bfd
Adding implementation details section
KetpuntoG Nov 21, 2024
8fe75a0
codecov
KetpuntoG Nov 21, 2024
128e229
Merge branch 'master' into angle_solver_qsp
KetpuntoG Nov 21, 2024
c0c4cdb
cleaning docs
KetpuntoG Nov 21, 2024
f2fc99e
Some Moran suggestions [skip ci]
KetpuntoG Nov 22, 2024
cbe8244
suggestions + theory
KetpuntoG Nov 22, 2024
c8ab5b2
Update qsvt.py
KetpuntoG Nov 22, 2024
91eb3a8
Merge branch 'master' into angle_solver_qsp
KetpuntoG Nov 22, 2024
62d47bb
Apply suggestions from code review
KetpuntoG Nov 29, 2024
0f92439
Update qsvt.py
KetpuntoG Nov 29, 2024
3a4721d
Merge branch 'master' into angle_solver_qsp
KetpuntoG Nov 29, 2024
994ee50
code review suggestions [skip-ci]
KetpuntoG Nov 29, 2024
c628607
Update qsvt.py
KetpuntoG Nov 29, 2024
a953b83
Update qsvt.py
KetpuntoG Nov 29, 2024
4fc50ca
Merge branch 'master' into angle_solver_qsp
KetpuntoG Nov 29, 2024
ba38539
Apply suggestions from code review
KetpuntoG Nov 29, 2024
16bad9d
Update qsvt.py
KetpuntoG Nov 29, 2024
f97eaff
Merge branch 'master' into angle_solver_qsp
KetpuntoG Nov 29, 2024
ee6ab2a
Apply suggestions from code review
KetpuntoG Nov 29, 2024
476ef81
Merge branch 'master' into angle_solver_qsp
KetpuntoG Nov 29, 2024
1a55c91
clarification in docs
KetpuntoG Nov 29, 2024
5546c5d
Update pennylane/templates/subroutines/qsvt.py
KetpuntoG Nov 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
`lie_closure_dense` in `pennylane.labs.dla`.
[(#6371)](https://github.com/PennyLaneAI/pennylane/pull/6371)

* New functionality to calculate angles for QSP and QSVT has been added. This includes the function `qml.poly_to_angles`
to obtain angles directly and the function `qml.transform_angles` to convert angles from one subroutine to another.
[(#6483)](https://github.com/PennyLaneAI/pennylane/pull/6483)

<h4>New API for Qubit Mixed</h4>

* Added `qml.devices.qubit_mixed` module for mixed-state qubit device support [(#6379)](https://github.com/PennyLaneAI/pennylane/pull/6379). This module introduces an `apply_operation` helper function that features:
Expand Down Expand Up @@ -184,6 +188,7 @@ same information.

This release contains contributions from (in alphabetical order):

Guillermo Alonso,
Shiwen An,
Astral Cai,
Yushao Chen,
Expand Down
2 changes: 1 addition & 1 deletion pennylane/templates/subroutines/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from .hilbert_schmidt import HilbertSchmidt, LocalHilbertSchmidt
from .flip_sign import FlipSign
from .basis_rotation import BasisRotation
from .qsvt import QSVT, qsvt
from .qsvt import poly_to_angles, QSVT, qsvt, transform_angles
from .select import Select
from .qdrift import QDrift
from .controlled_sequence import ControlledSequence
Expand Down
216 changes: 216 additions & 0 deletions pennylane/templates/subroutines/qsvt.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import copy

import numpy as np
from numpy.polynomial import Polynomial, chebyshev

import pennylane as qml
from pennylane.operation import AnyWires, Operation
Expand Down Expand Up @@ -123,6 +124,7 @@ def qsvt(A, angles, wires, convention=None):
A = qml.math.reshape(A, [1, 1])

c, r = qml.math.shape(A)
global_phase, global_phase_op = None, None
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

with qml.QueuingManager.stop_recording():
UA = BlockEncode(A, wires=wires)
Expand Down Expand Up @@ -464,3 +466,217 @@ def _qsp_to_qsvt(angles):
update_vals = qml.math.convert_like(update_vals, angles)

return angles + update_vals


def _complementary_poly(P):
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
r"""
Computes the complementary polynomial Q given a polynomial P.

The polynomial Q is complementary of P if satisfies the following equation:
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

.. math:

|P(e^{i\theta})|^2 + |Q(e^{i\theta})|^2 = 1, \quad \forall \theta \in \left[0, 2\pi\right]
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

The method is based on computing an auxiliary polynomial R, finding its roots,
and reconstructing Q by using information extracted of those roots.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
For more details see reference `arXiv:2308.01501 <https://arxiv.org/abs/2308.01501>`_.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

Args:
P (array-like): Coefficients of the complex polynomial P.

Returns:
array-like: Coefficients of the complementary polynomial Q.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
"""
poly_degree = len(P) - 1

# Build the polynomial R(z) = z^degree * (1 - conj(P(1/z)) * P(z)), deduced from (eq.33) and (eq.34)
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
R = Polynomial.basis(poly_degree) - Polynomial(P) * Polynomial(np.conj(P[::-1]))
Jaybsoni marked this conversation as resolved.
Show resolved Hide resolved
r_roots = R.roots()

# Categorize the roots based on their magnitude: outside the unit circle or inside the closed unit circle
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
inside_circle = [root for root in r_roots if np.abs(root) <= 1]
outside_circle = [root for root in r_roots if np.abs(root) > 1]

# Compute the scaling factor for Q, which depends on the leading coefficient of R
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
scale_factor = np.sqrt(np.abs(R.coef[-1] * np.prod(outside_circle)))

# Complementary polynomial Q is built using th roots inside the closed unit circle
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
Q_poly = scale_factor * Polynomial.fromroots(inside_circle)

return Q_poly.coef


def _QSP_angles_root_finding(F):
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
r"""
Computes the Quantum Signal Processing (QSP) angles given a polynomial F.

Args:
F (array-like): Coefficients of the input polynomial F.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

Returns:
theta (array-like): QSP angles corresponding to the input polynomial F.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
"""

parity = (len(F) - 1) % 2

# Construct the auxiliary polynomial P and its complementary Q based on appendix A in [arXiv:2406.04246]
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
primary_poly = np.concatenate([np.zeros(len(F) // 2), chebyshev.poly2cheb(F)[parity::2]]) * (
1 - 1e-12
)
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
secondary_poly = _complementary_poly(primary_poly)

polynomial_matrix = np.array([primary_poly, secondary_poly])
num_terms = polynomial_matrix.shape[1]
rotation_angles = np.zeros(num_terms)

# This subroutine is an adaptation of Algorithm 1 in [arXiv:2308.01501]
# in order to work in the context of QSP.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
with qml.QueuingManager.stop_recording():
for idx in range(num_terms - 1, -1, -1):

poly_a, poly_b = polynomial_matrix[:, idx]
rotation_angles[idx] = np.arctan2(poly_b.real, poly_a.real)

rotation_op = qml.matrix(qml.RY(-2 * rotation_angles[idx], wires=0))

updated_poly_matrix = rotation_op @ polynomial_matrix
polynomial_matrix = np.array(
[updated_poly_matrix[0][1 : idx + 1], updated_poly_matrix[1][0:idx]]
)
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

return rotation_angles


def transform_angles(angles, routine1, routine2):
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
r"""
Transforms a set of angles from one routine to another.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

This function adjusts the angles according to the specified transformation
between two routines, either from "Quantum Signal Processing" (QSP) to
"Quantum Singular Value Transformation" (QSVT), or vice versa.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

Args:
angles (array-like): A list or array of angles to be transformed.
routine1 (str): The current routine of the angles. Must be either "QSP" or "QSVT".
routine2 (str): The target routine to which the angles should be transformed.
Must be either "QSP" or "QSVT".

Returns:
array-like: The transformed angles as an array.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

"""
if routine1 == "QSP" and routine2 == "QSVT":
num_angles = len(angles)
update_vals = np.empty(num_angles)

update_vals[0] = 3 * np.pi / 4 - (3 + len(angles) % 4) * np.pi / 2
update_vals[1:-1] = np.pi / 2
update_vals[-1] = -np.pi / 4
update_vals = qml.math.convert_like(update_vals, angles)
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

return angles + update_vals

if routine1 == "QSVT" and routine2 == "QSP":
num_angles = len(angles)
update_vals = np.empty(num_angles)

update_vals[0] = 3 * np.pi / 4 - (3 + len(angles) % 4) * np.pi / 2
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
update_vals[1:-1] = np.pi / 2
update_vals[-1] = -np.pi / 4
update_vals = qml.math.convert_like(update_vals, angles)

return angles - update_vals

raise AssertionError("Invalid conversion")
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved


def poly_to_angles(poly, routine, angle_solver="root-finding"):
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
r"""
Converts a given polynomial's coefficients into angles for specific quantum signal processing (QSP)
or quantum singular value transformation (QSVT) routines.
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

Args:
poly (array-like): Coefficients of the polynomial, ordered from lowest to higher degree.
The polynomial must have defined parity and real coefficients.

routine (str): Specifies the type of angle transformation required. Must be either: "QSP" or "QSVT".

angle_solver (str): Specifies the method used to calculate the angles. Must be "root-finding".

Returns:
(array-like): Angles corresponding to the specified transformation routine.

Raises:
AssertionError: if poly is not valid in the chosen subroutine
AssertionError: if routine or angle_solver specified does not exist
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved


**Example**
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

This example applies the polynomial :math:`P(x) = x - \frac{x^3}{2} + \frac{x^5}{3}` to a block-encoding
of :math:`x = 0.2`.

.. code-block::

poly = [0, 1.0, 0, -1/2, 0, 1/3]

qsvt_angles = qml.poly_to_angles(poly, "QSVT")

x = 0.2
block_encoding = qml.RX(2 * np.arccos(x), wires=0) # Encodes x in the top left of the matrix
projectors = [qml.PCPhase(angle, dim=1, wires=0) for angle in qsvt_angles]

@qml.qnode(qml.device("default.qubit"))
def circuit_qsvt():
qml.QSVT(block_encoding, projectors)
return qml.state()

output = qml.matrix(circuit_qsvt, wire_order=[0])()[0, 0]
expected = sum(coef * (x**i) for i, coef in enumerate(poly))

print("output qsvt: ", output.real)
print("P(x) = ", expected)

.. code-block:: pycon

output qsvt: 0.19610666666647059
P(x) = 0.19610666666666668
"""

# Leading zeros are removed from the array
poly = poly[
: len(poly)
- next((i for i, x in enumerate(reversed(poly)) if not np.isclose(x, 0.0)), len(poly))
]
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

if len(poly) == 1:
raise AssertionError("The polynomial must have at least degree 1.")

for x in [-1, 0, 1]:
if qml.math.abs(qml.math.sum(coeff * x**i for i, coeff in enumerate(poly))) > 1:
# It is not a property that we can check globally but checking these three points is useful
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
raise AssertionError("The polynomial must satisfy that |P(x)| < 1 for all x in [0, 1]")
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved

if routine in ["QSVT", "QSP"]:
if not (
np.isclose(qml.math.sum(qml.math.abs(poly[::2])), 0.0)
or np.isclose(qml.math.sum(qml.math.abs(poly[1::2])), 0.0)
):
raise AssertionError(
"The polynomial has no definite parity. All odd or even entries in the array must take a value of zero."
)
assert np.allclose(
np.array(poly, dtype=np.complex128).imag, 0
), "Array must not have an imaginary part"

if routine == "QSVT":
if angle_solver == "root-finding":
return transform_angles(_QSP_angles_root_finding(poly), "QSP", "QSVT")
raise AssertionError("Invalid angle solver method. Valid value is 'root-finding'")

if routine == "QSP":
if angle_solver == "root-finding":
return _QSP_angles_root_finding(poly)
raise AssertionError("Invalid angle solver method")
raise AssertionError("Invalid routine. Valid values are 'QSP' and 'QSVT'")
KetpuntoG marked this conversation as resolved.
Show resolved Hide resolved
Loading