Skip to content

Commit 5410faa

Browse files
KetpuntoGobliviateandsurrendersoranjh
authored
GQSP template and angle solver (#6565)
- [x] Add thumbnail - [x] Add changelog This PR introduces the [Generalized QSP](https://arxiv.org/abs/2308.01501) template and some functionality to calculate the desire polynomials. [sc-76734] --------- Co-authored-by: Utkarsh <utkarshazad98@gmail.com> Co-authored-by: Soran Jahangiri <40344468+soranjh@users.noreply.github.com>
1 parent 798ab5b commit 5410faa

File tree

8 files changed

+499
-0
lines changed

8 files changed

+499
-0
lines changed
43.5 KB
Loading

‎doc/introduction/templates.rst‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,10 @@ Other useful templates which do not belong to the previous categories can be fou
326326
:description: :doc:`QSVT<../code/api/pennylane.QSVT>`
327327
:figure: _static/templates/subroutines/qsvt.png
328328

329+
.. gallery-item::
330+
:description: :doc:`GQSP<../code/api/pennylane.GQSP>`
331+
:figure: _static/templates/subroutines/gqsp.png
332+
329333
.. gallery-item::
330334
:description: :doc:`Select<../code/api/pennylane.Select>`
331335
:figure: _static/templates/subroutines/select.png

‎doc/releases/changelog-dev.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@
5757
[(#6426)](https://github.com/PennyLaneAI/pennylane/pull/6426)
5858
[(#6645)](https://github.com/PennyLaneAI/pennylane/pull/6645)
5959

60+
* New `qml.GQSP` template has been added to perform Generalized Quantum Signal Processing (GQSP).
61+
The functionality `qml.poly_to_angles` has been also extended to support GQSP.
62+
[(#6565)](https://github.com/PennyLaneAI/pennylane/pull/6565)
63+
64+
6065
<h4>New `labs` module `dla` for handling dynamical Lie algebras (DLAs)</h4>
6166

6267
* Added a dense implementation of computing the Lie closure in a new function

‎pennylane/templates/subroutines/__init__.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,4 @@
5252
from .out_adder import OutAdder
5353
from .mod_exp import ModExp
5454
from .out_poly import OutPoly
55+
from .gqsp import GQSP
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# Copyright 2024 Xanadu Quantum Technologies Inc.
2+
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""
15+
Contains the GQSP template.
16+
"""
17+
# pylint: disable=too-many-arguments
18+
19+
import copy
20+
21+
import pennylane as qml
22+
from pennylane.operation import Operation
23+
from pennylane.queuing import QueuingManager
24+
from pennylane.wires import Wires
25+
26+
27+
class GQSP(Operation):
28+
r"""
29+
Implements the generalized quantum signal processing (GQSP) circuit.
30+
31+
This operation encodes a polynomial transformation of an input unitary operator following the algorithm
32+
described in `arXiv:2308.01501 <https://arxiv.org/abs/2308.01501>`__ as:
33+
34+
.. math::
35+
U
36+
\xrightarrow{GQSP}
37+
\begin{pmatrix}
38+
\text{poly}(U) & * \\
39+
* & * \\
40+
\end{pmatrix}
41+
42+
The implementation requires one auxiliary qubit.
43+
44+
Args:
45+
46+
unitary (Operator): the operator to be encoded by the GQSP circuit
47+
angles (tensor[float]): array of angles defining the polynomial transformation. The shape of the array must be `(3, d+1)`, where `d` is the degree of the polynomial.
48+
control (Union[Wires, int, str]): control qubit used to encode the polynomial transformation
49+
50+
.. note::
51+
52+
The :func:`~.poly_to_angles` function can be used to calculate the angles for a given polynomial.
53+
54+
Example:
55+
56+
.. code-block:: python
57+
58+
# P(x) = 0.1 + 0.2j x + 0.3 x^2
59+
poly = [0.1, 0.2j, 0.3]
60+
61+
angles = qml.poly_to_angles(poly, "GQSP")
62+
63+
@qml.prod # transforms the qfunc into an Operator
64+
def unitary(wires):
65+
qml.RX(0.3, wires)
66+
67+
dev = qml.device("default.qubit")
68+
69+
@qml.qnode(dev)
70+
def circuit(angles):
71+
qml.GQSP(unitary(wires = 1), angles, control = 0)
72+
return qml.state()
73+
74+
matrix = qml.matrix(circuit, wire_order=[0, 1])(angles)
75+
76+
.. code-block:: pycon
77+
78+
>>> print(np.round(matrix,3)[:2, :2])
79+
[[0.387+0.198j 0.03 -0.089j]
80+
[0.03 -0.089j 0.387+0.198j]]
81+
"""
82+
83+
grad_method = None
84+
85+
def __init__(self, unitary, angles, control, id=None):
86+
total_wires = qml.wires.Wires(control) + unitary.wires
87+
88+
self._hyperparameters = {"unitary": unitary, "control": qml.wires.Wires(control)}
89+
90+
super().__init__(angles, *unitary.data, wires=total_wires, id=id)
91+
92+
def _flatten(self):
93+
data = self.parameters
94+
return data, (
95+
self.hyperparameters["unitary"],
96+
self.hyperparameters["control"],
97+
)
98+
99+
@classmethod
100+
def _unflatten(cls, data, metadata):
101+
return cls(unitary=metadata[0], angles=data[0], control=metadata[1])
102+
103+
@classmethod
104+
def _primitive_bind_call(cls, *args, **kwargs):
105+
return cls._primitive.bind(*args, **kwargs)
106+
107+
def map_wires(self, wire_map: dict):
108+
# pylint: disable=protected-access
109+
new_op = copy.deepcopy(self)
110+
new_op._wires = Wires([wire_map.get(wire, wire) for wire in self.wires])
111+
new_op._hyperparameters["unitary"] = qml.map_wires(
112+
new_op._hyperparameters["unitary"], wire_map
113+
)
114+
new_op._hyperparameters["control"] = tuple(
115+
wire_map.get(w, w) for w in new_op._hyperparameters["control"]
116+
)
117+
118+
return new_op
119+
120+
@staticmethod
121+
def compute_decomposition(*parameters, **hyperparameters): # pylint: disable=arguments-differ
122+
r"""
123+
Representation of the operator as a product of other operators (static method).
124+
125+
.. math:: O = O_1 O_2 \dots O_n.
126+
127+
.. seealso:: :meth:`~.Operator.decomposition`.
128+
129+
Args:
130+
*parameters (list): trainable parameters of the operator, as stored in the ``parameters`` attribute
131+
wires (Iterable[Any], Wires): wires that the operator acts on
132+
**hyperparams (dict): non-trainable hyperparameters of the operator, as stored in the ``hyperparameters`` attribute
133+
134+
Returns:
135+
list[Operator]: decomposition of the operator
136+
"""
137+
138+
unitary = hyperparameters["unitary"]
139+
control = hyperparameters["control"]
140+
141+
angles = parameters[0]
142+
143+
thetas, phis, lambds = angles[0], angles[1], angles[2]
144+
145+
op_list = []
146+
147+
# These four gates adapt PennyLane's qml.U3 to the chosen U3 format in the GQSP paper.
148+
op_list.append(qml.X(control))
149+
op_list.append(qml.U3(2 * thetas[0], phis[0], lambds[0], wires=control))
150+
op_list.append(qml.X(control))
151+
op_list.append(qml.Z(control))
152+
153+
for theta, phi, lamb in zip(thetas[1:], phis[1:], lambds[1:]):
154+
155+
op_list.append(qml.ctrl(unitary, control=control, control_values=0))
156+
157+
op_list.append(qml.X(control))
158+
op_list.append(qml.U3(2 * theta, phi, lamb, wires=control))
159+
op_list.append(qml.X(control))
160+
op_list.append(qml.Z(control))
161+
162+
return op_list
163+
164+
def queue(self, context=QueuingManager):
165+
context.remove(self.hyperparameters["unitary"])
166+
context.append(self)
167+
return self

‎pennylane/templates/subroutines/qsvt.py‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,70 @@ def _compute_qsp_angle(poly_coeffs):
571571
return rotation_angles
572572

573573

574+
def _gqsp_u3_gate(theta, phi, lambd):
575+
r"""
576+
Computes the U3 gate matrix for Generalized Quantum Signal Processing (GQSP) as described
577+
in [`arXiv:2406.04246 <https://arxiv.org/abs/2406.04246>`_]
578+
"""
579+
580+
exp_phi = qml.math.exp(1j * phi)
581+
exp_lambda = qml.math.exp(1j * lambd)
582+
exp_lambda_phi = qml.math.exp(1j * (lambd + phi))
583+
584+
matrix = np.array(
585+
[
586+
[exp_lambda_phi * qml.math.cos(theta), exp_phi * qml.math.sin(theta)],
587+
[exp_lambda * qml.math.sin(theta), -qml.math.cos(theta)],
588+
],
589+
dtype=complex,
590+
)
591+
592+
return matrix
593+
594+
595+
def _compute_gqsp_angles(poly_coeffs):
596+
r"""
597+
Computes the Generalized Quantum Signal Processing (GQSP) angles given the coefficients of a polynomial P.
598+
599+
The method for computing the GQSP angles is based on the algorithm described in [`arXiv:2406.04246 <https://arxiv.org/abs/2406.04246>`_].
600+
The complementary polynomial is calculated using root-finding methods.
601+
602+
Args:
603+
poly_coeffs (tensor-like): Coefficients of the input polynomial P.
604+
605+
Returns:
606+
(tensor-like): QSP angles corresponding to the input polynomial P. The shape is (3, P-degree)
607+
"""
608+
609+
complementary = _complementary_poly(poly_coeffs)
610+
611+
# Algorithm 1 in [arXiv:2308.01501]
612+
input_data = qml.math.array([poly_coeffs, complementary])
613+
num_elements = input_data.shape[1]
614+
615+
angles_theta, angles_phi, angles_lambda = qml.math.zeros([3, num_elements])
616+
617+
for idx in range(num_elements - 1, -1, -1):
618+
619+
component_a, component_b = input_data[:, idx]
620+
angles_theta[idx] = qml.math.arctan2(np.abs(component_b), qml.math.abs(component_a))
621+
angles_phi[idx] = (
622+
0
623+
if qml.math.isclose(qml.math.abs(component_b), 0, atol=1e-10)
624+
else qml.math.angle(component_a * qml.math.conj(component_b))
625+
)
626+
627+
if idx == 0:
628+
angles_lambda[0] = qml.math.angle(component_b)
629+
else:
630+
updated_matrix = (
631+
_gqsp_u3_gate(angles_theta[idx], angles_phi[idx], 0).conj().T @ input_data
632+
)
633+
input_data = qml.math.array([updated_matrix[0][1 : idx + 1], updated_matrix[1][0:idx]])
634+
635+
return angles_theta, angles_phi, angles_lambda
636+
637+
574638
def transform_angles(angles, routine1, routine2):
575639
r"""
576640
Converts angles for quantum signal processing (QSP) and quantum singular value transformation (QSVT) routines.
@@ -766,4 +830,7 @@ def circuit_qsvt():
766830
if angle_solver == "root-finding":
767831
return _compute_qsp_angle(poly)
768832
raise AssertionError("Invalid angle solver method. Valid value is 'root-finding'")
833+
834+
if routine == "GQSP":
835+
return _compute_gqsp_angles(poly)
769836
raise AssertionError("Invalid routine. Valid values are 'QSP' and 'QSVT'")

‎tests/capture/test_templates.py‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,9 +253,11 @@ def fn(*args):
253253
qml.OutAdder,
254254
qml.ModExp,
255255
qml.OutPoly,
256+
qml.GQSP,
256257
]
257258

258259

260+
# pylint: disable=too-many-public-methods
259261
class TestModifiedTemplates:
260262
"""Test that templates with custom primitive binds are captured as expected."""
261263

@@ -927,6 +929,39 @@ def qfunc():
927929
assert len(q) == 1
928930
qml.assert_equal(q.queue[0], qml.OutPoly(**kwargs))
929931

932+
def test_gqsp(self):
933+
"""Test the primitive bind call of GQSP."""
934+
935+
kwargs = {
936+
"unitary": qml.RX(1, wires=1),
937+
"control": 0,
938+
"angles": np.ones([3, 3]),
939+
}
940+
941+
def qfunc():
942+
qml.GQSP(**kwargs)
943+
944+
# Validate inputs
945+
qfunc()
946+
947+
# Actually test primitive bind
948+
jaxpr = jax.make_jaxpr(qfunc)()
949+
950+
assert len(jaxpr.eqns) == 1
951+
952+
eqn = jaxpr.eqns[0]
953+
assert eqn.primitive == qml.GQSP._primitive
954+
assert eqn.invars == jaxpr.jaxpr.invars
955+
assert eqn.params == kwargs
956+
assert len(eqn.outvars) == 1
957+
assert isinstance(eqn.outvars[0], jax.core.DropVar)
958+
959+
with qml.queuing.AnnotatedQueue() as q:
960+
jax.core.eval_jaxpr(jaxpr.jaxpr, jaxpr.consts)
961+
962+
assert len(q) == 1
963+
qml.assert_equal(q.queue[0], qml.GQSP(**kwargs))
964+
930965
@pytest.mark.parametrize(
931966
"template, kwargs",
932967
[

0 commit comments

Comments
 (0)